1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
pragma solidity ^0.5.11;
// This contract mocks the Tangerine governance contract ONLY FOR TESTING PURPOSE,
// and its behavior is not exaclty same as the Tangerine governance contract.
contract GovernanceMock {
struct Node {
address owner;
bytes publicKey;
uint256 staked;
uint256 fined;
string name;
string email;
string location;
string url;
uint256 unstaked;
uint256 unstakedAt;
}
Node[] public nodes;
mapping(address => int256) public nodesOffsetByAddress;
mapping(address => bool) public registered;
uint256 public staked = 0;
uint256 public unstaked = 0;
uint256 public minStake = 1000000000000000000;
event Register(
address owner,
bytes publicKey,
string name,
string email,
string location,
string url,
uint256 value
);
event Staked(address owner, uint256 amount);
event Unstaked(address owner, uint256 amount);
event Withdrawn(address owner, uint256 amount);
event NodeOwnershipTransfered(address indexed NodeAddress, address indexed NewOwnerAddress);
function register(
bytes memory publicKey,
string memory name,
string memory email,
string memory location,
string memory url
) public payable {
require(!registered[msg.sender], "sender address is registered");
registered[msg.sender] = true;
staked += msg.value;
Node memory node = Node(
msg.sender,
publicKey,
msg.value,
0,
name,
email,
location,
url,
0,
0
);
nodesOffsetByAddress[msg.sender] = int(nodes.length);
nodes.push(node);
emit Register(msg.sender, publicKey, name, email, location, url, msg.value);
}
function stake() public payable {
staked += msg.value;
uint256 offset = uint(nodesOffsetByAddress[msg.sender]);
Node storage node = nodes[offset];
node.staked += msg.value;
emit Staked(msg.sender, msg.value);
}
function unstake(uint256 amount) public {
staked -= amount;
unstaked += amount;
uint256 offset = uint(nodesOffsetByAddress[msg.sender]);
Node storage node = nodes[offset];
require(amount <= node.staked, "unstake amount larger than staked");
node.staked -= amount;
node.unstaked += amount;
emit Unstaked(msg.sender, amount);
}
function withdraw() public {
uint256 offset = uint(nodesOffsetByAddress[msg.sender]);
Node storage node = nodes[offset];
uint256 amount = node.unstaked;
require(amount >= 0, "no unstaked balance");
require(address(this).balance >= amount, "invalid balance");
unstaked -= amount;
node.unstaked = 0;
msg.sender.transfer(amount);
emit Withdrawn(msg.sender, amount);
}
function withdrawable() public view returns (bool) {
uint256 offset = uint(nodesOffsetByAddress[msg.sender]);
Node storage node = nodes[offset];
uint256 amount = node.unstaked;
return amount >= 0 && address(this).balance >= amount;
}
function replaceNodePublicKey(bytes memory key) public {
uint256 offset = uint(nodesOffsetByAddress[msg.sender]);
Node storage node = nodes[offset];
node.publicKey = key;
}
function transferNodeOwnership(address NewOwner) public {
emit NodeOwnershipTransfered(msg.sender, NewOwner);
}
}
|