/
node.go
300 lines (262 loc) · 9.35 KB
/
node.go
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package topology
import (
"time"
"github.com/ava-labs/avalanchego-kurtosis/kurtosis/avalanche/libs/avalanchegoclient"
"github.com/ava-labs/avalanchego-kurtosis/kurtosis/avalanche/libs/builder/chainhelper"
"github.com/ava-labs/avalanchego-kurtosis/kurtosis/avalanche/libs/constants"
"github.com/ava-labs/avalanchego/api"
"github.com/ava-labs/avalanchego/ids"
"github.com/palantir/stacktrace"
"github.com/sirupsen/logrus"
)
// Node defines the Node in the Topology
type Node struct {
id string
UserPass api.UserPass
PAddress string
XAddress string
client *avalanchegoclient.Client
NodeID string
ipAddress string
}
func newNode(id string, userName string, password string, ipAddress string, client *avalanchegoclient.Client) *Node {
nodeID, err := client.InfoAPI().GetNodeID()
if err != nil {
panic(stacktrace.Propagate(err, "Could not get node ID."))
}
return &Node{
id: id,
UserPass: api.UserPass{
Username: userName,
Password: password,
},
NodeID: nodeID,
client: client,
ipAddress: ipAddress,
}
}
// CreateAddress creates user and both XChain and PChain addresses for the Node
func (n *Node) CreateAddress() *Node {
keystore := n.client.KeystoreAPI()
if _, err := keystore.CreateUser(n.UserPass); err != nil {
panic(stacktrace.Propagate(err, "Could not create user for node."))
}
xAddress, err := n.client.XChainAPI().CreateAddress(n.UserPass)
if err != nil {
panic(stacktrace.Propagate(err, "Could not create user address in the XChainAPI."))
}
n.XAddress = xAddress
pAddress, err := n.client.PChainAPI().CreateAddress(n.UserPass)
if err != nil {
panic(stacktrace.Propagate(err, "Could not create user address in the PChainAPI."))
}
n.PAddress = pAddress
return n
}
// GetClient returns the RPC API client to access the nodes VMS
func (n *Node) GetClient() *avalanchegoclient.Client {
return n.client
}
// BecomeValidator is a multi step methods that does the following
// - exports AVAX from the XChain + waits for acceptance in the XChain
// - imports the amount to the PChain + waits for acceptance in the PChain
// - verifies the PChain balance + verifies the XChain balance
// - adds nodeID as a validator - waits Tx acceptance in the PChain
// - waits until the validation period begins
//
func (n *Node) BecomeValidator(genesisAmount uint64, seedAmount uint64, stakeAmount uint64, txFee uint64) *Node {
// exports AVAX from the X Chain
exportTxID, err := n.client.XChainAPI().ExportAVAX(
n.UserPass,
nil, // from addrs
"", // change addr
seedAmount+txFee, // deducted (seedAmmount + txFee(ExportAVAX) ) - 1xFee deducted from XChain + 1xFee to be deducted from PChain Tx
n.PAddress,
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to export AVAX to pchainAddress %s", n.PAddress))
return n
}
// waits Tx acceptance in the XChain
err = chainhelper.XChain().AwaitTransactionAcceptance(n.client, exportTxID, 120*time.Second)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to export AVAX from XChain Address %s", n.XAddress))
return n
}
// imports the amount to the P Chain
importTxID, err := n.client.PChainAPI().ImportAVAX( // receivedAmount = (sent - txFee)
n.UserPass,
nil, // from addrs
"", // change addr
n.PAddress,
constants.XChainID.String(),
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed import AVAX to PChain Address %s", n.PAddress))
return n
}
// waits Tx acceptance in the PChain
err = chainhelper.PChain().AwaitTransactionAcceptance(n.client, importTxID, constants.TimeoutDuration)
if err != nil {
panic(err)
return n
}
// verify the PChain balance of seedAmount on the PChain (which should have been at 0)
err = chainhelper.PChain().CheckBalance(n.client, n.PAddress, seedAmount) // balance = seedAmount = transferred + txFee
if err != nil {
panic(stacktrace.Propagate(err, "expected balance of seedAmount the stakeAmount was moved to XChain"))
return n
}
// verify the XChain balance of (seedAmount - stakeAmount - 2*txFee) the stake was moved to PChain
err = chainhelper.XChain().CheckBalance(n.client, n.XAddress, "AVAX", genesisAmount-seedAmount-2*txFee)
if err != nil {
panic(stacktrace.Propagate(err, "expected balance of (seedAmount - stakeAmount - 2*txFee) the stake was moved to XChain"))
return n
}
// add nodeID as a validator
stakingStartTime := time.Now().Add(20 * time.Second)
startTime := uint64(stakingStartTime.Unix())
endTime := uint64(stakingStartTime.Add(72 * time.Hour).Unix())
addStakerTxID, err := n.client.PChainAPI().AddValidator(
n.UserPass,
nil,
"",
n.PAddress,
n.NodeID,
stakeAmount,
startTime,
endTime,
float32(2),
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to add validator to primary network %s", n.id))
return n
}
// waits Tx acceptance in the PChain
err = chainhelper.PChain().AwaitTransactionAcceptance(n.client, addStakerTxID, constants.TimeoutDuration)
if err != nil {
panic(stacktrace.Propagate(err, "transaction not accepted"))
return n
}
// waits until the validation period begins
time.Sleep(time.Until(stakingStartTime) + 3*time.Second)
// verifies if the node is a current validator
currentStakers, err := n.client.PChainAPI().GetCurrentValidators(ids.Empty)
if err != nil {
panic(stacktrace.Propagate(err, "Could not get current stakers."))
return n
}
found := false
for _, stakerIntf := range currentStakers {
staker := stakerIntf.(map[string]interface{})
if staker["nodeID"] == n.NodeID {
found = true
break
}
}
if !found {
panic(stacktrace.NewError("Node: %s not found in the stakers %v", n.NodeID, currentStakers))
return n
}
// verifies the balance of the staker in the PChain - should be the seedAmmount - stakedAmount
err = chainhelper.PChain().CheckBalance(n.client, n.PAddress, seedAmount-stakeAmount)
if err != nil {
panic(stacktrace.Propagate(err, "Error checking the PChain balance."))
}
logrus.Infof("Verified the staker was added to current validators and has the expected P Chain balance.")
return n
}
// BecomeDelegator is a multi step methods that does the following
// - exports AVAX from the XChain + waits for acceptance in the XChain
// - imports the amount to the PChain + waits for acceptance in the PChain
// - verifies the PChain balance + verifies the XChain balance
// - adds nodeID as a delegator - waits Tx acceptance in the PChain
// - waits until the validation period begins
//
func (n *Node) BecomeDelegator(genesisAmount uint64, seedAmount uint64, delegatorAmount uint64, txFee uint64, stakerNodeID string) *Node {
// exports AVAX from the X Chain
exportTxID, err := n.client.XChainAPI().ExportAVAX(
n.UserPass,
nil, // from addrs
"", // change addr
seedAmount+txFee,
n.PAddress,
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to export AVAX to pchainAddress %s", n.PAddress))
return n
}
// waits Tx acceptance in the XChain
err = chainhelper.XChain().AwaitTransactionAcceptance(n.client, exportTxID, constants.TimeoutDuration)
if err != nil {
panic(err)
return n
}
// imports the amount to the P Chain
importTxID, err := n.client.PChainAPI().ImportAVAX(
n.UserPass,
nil, // from addrs
"", // change addr
n.PAddress,
constants.XChainID.String(),
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed import AVAX to pchainAddress %s", n.PAddress))
return n
}
// waits Tx acceptance in the PChain
err = chainhelper.PChain().AwaitTransactionAcceptance(n.client, importTxID, constants.TimeoutDuration)
if err != nil {
panic(err)
return n
}
// verify the PChain balance (seedAmount+txFee-txFee)
err = chainhelper.PChain().CheckBalance(n.client, n.PAddress, seedAmount)
if err != nil {
panic(stacktrace.Propagate(err, "expected balance of seedAmount exists in the PChain"))
return n
}
// verify the XChain balance of genesisAmount - seedAmount - txFee - txFee (import PChain)
err = chainhelper.XChain().CheckBalance(n.client, n.XAddress, "AVAX", genesisAmount-seedAmount-2*txFee)
if err != nil {
panic(stacktrace.Propagate(err, "expected balance XChain balance of genesisAmount-seedAmount-txFee"))
return n
}
delegatorStartTime := time.Now().Add(20 * time.Second)
startTime := uint64(delegatorStartTime.Unix())
endTime := uint64(delegatorStartTime.Add(36 * time.Hour).Unix())
addDelegatorTxID, err := n.client.PChainAPI().AddDelegator(
n.UserPass,
nil, // from addrs
"", // change addr
n.PAddress,
stakerNodeID,
delegatorAmount,
startTime,
endTime,
)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to add delegator %s", n.PAddress))
return n
}
err = chainhelper.PChain().AwaitTransactionAcceptance(n.client, addDelegatorTxID, constants.TimeoutDuration)
if err != nil {
panic(stacktrace.Propagate(err, "Failed to accept AddDelegator tx: %s", addDelegatorTxID))
return n
}
// Sleep until delegator starts validating
time.Sleep(time.Until(delegatorStartTime) + 3*time.Second)
expectedDelegatorBalance := seedAmount - delegatorAmount
err = chainhelper.PChain().CheckBalance(n.client, n.PAddress, expectedDelegatorBalance)
if err != nil {
panic(stacktrace.Propagate(err, "Unexpected P Chain Balance after adding a new delegator to the network."))
return n
}
logrus.Infof("Added delegator to subnet and verified the expected P Chain balance.")
return n
}
func (n *Node) GetIPAddress() string {
return n.ipAddress
}