-
Notifications
You must be signed in to change notification settings - Fork 666
/
static_service.go
405 lines (360 loc) · 12 KB
/
static_service.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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package platformvm
import (
"bytes"
"errors"
"fmt"
"net/http"
"sort"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/formatting/address"
"github.com/ava-labs/avalanchego/utils/json"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
safemath "github.com/ava-labs/avalanchego/utils/math"
pChainValidator "github.com/ava-labs/avalanchego/vms/platformvm/validator"
)
// Note that since an Avalanche network has exactly one Platform Chain,
// and the Platform Chain defines the genesis state of the network
// (who is staking, which chains exist, etc.), defining the genesis
// state of the Platform Chain is the same as defining the genesis
// state of the network.
var (
errUTXOHasNoValue = errors.New("genesis UTXO has no value")
errValidatorAddsNoValue = errors.New("validator would have already unstaked")
errStakeOverflow = errors.New("too many funds staked on single validator")
)
// StaticService defines the static API methods exposed by the platform VM
type StaticService struct{}
// APIUTXO is a UTXO on the Platform Chain that exists at the chain's genesis.
type APIUTXO struct {
Locktime json.Uint64 `json:"locktime"`
Amount json.Uint64 `json:"amount"`
Address string `json:"address"`
Message string `json:"message"`
}
// TODO: refactor APIStaker, APIValidators and merge them together for SubnetValidators + PrimaryValidators
// APIStaker is the representation of a staker sent via APIs.
// [TxID] is the txID of the transaction that added this staker.
// [Amount] is the amount of tokens being staked.
// [StartTime] is the Unix time when they start staking
// [Endtime] is the Unix time repr. of when they are done staking
// [NodeID] is the node ID of the staker
type APIStaker struct {
TxID ids.ID `json:"txID"`
StartTime json.Uint64 `json:"startTime"`
EndTime json.Uint64 `json:"endTime"`
Weight *json.Uint64 `json:"weight,omitempty"`
StakeAmount *json.Uint64 `json:"stakeAmount,omitempty"`
NodeID ids.NodeID `json:"nodeID"`
}
// APIOwner is the repr. of a reward owner sent over APIs.
type APIOwner struct {
Locktime json.Uint64 `json:"locktime"`
Threshold json.Uint32 `json:"threshold"`
Addresses []string `json:"addresses"`
}
// APIPrimaryValidator is the repr. of a primary network validator sent over APIs.
type APIPrimaryValidator struct {
APIStaker
// The owner the staking reward, if applicable, will go to
RewardOwner *APIOwner `json:"rewardOwner,omitempty"`
PotentialReward *json.Uint64 `json:"potentialReward,omitempty"`
DelegationFee json.Float32 `json:"delegationFee"`
ExactDelegationFee *json.Uint32 `json:"exactDelegationFee,omitempty"`
Uptime *json.Float32 `json:"uptime"`
Connected bool `json:"connected"`
Staked []APIUTXO `json:"staked,omitempty"`
// The delegators delegating to this validator
Delegators []APIPrimaryDelegator `json:"delegators"`
}
// APISubnetValidator is the repr. of a subnet validator sent over APIs.
type APISubnetValidator struct {
APIStaker
// The owner the staking reward, if applicable, will go to
Connected bool `json:"connected"`
}
// APIPrimaryDelegator is the repr. of a primary network delegator sent over APIs.
type APIPrimaryDelegator struct {
APIStaker
RewardOwner *APIOwner `json:"rewardOwner,omitempty"`
PotentialReward *json.Uint64 `json:"potentialReward,omitempty"`
}
func (v *APIStaker) weight() uint64 {
switch {
case v.Weight != nil:
return uint64(*v.Weight)
case v.StakeAmount != nil:
return uint64(*v.StakeAmount)
default:
return 0
}
}
// APIChain defines a chain that exists
// at the network's genesis.
// [GenesisData] is the initial state of the chain.
// [VMID] is the ID of the VM this chain runs.
// [FxIDs] are the IDs of the Fxs the chain supports.
// [Name] is a human-readable, non-unique name for the chain.
// [SubnetID] is the ID of the subnet that validates the chain
type APIChain struct {
GenesisData string `json:"genesisData"`
VMID ids.ID `json:"vmID"`
FxIDs []ids.ID `json:"fxIDs"`
Name string `json:"name"`
SubnetID ids.ID `json:"subnetID"`
}
// BuildGenesisArgs are the arguments used to create
// the genesis data of the Platform Chain.
// [NetworkID] is the ID of the network
// [UTXOs] are the UTXOs on the Platform Chain that exist at genesis.
// [Validators] are the validators of the primary network at genesis.
// [Chains] are the chains that exist at genesis.
// [Time] is the Platform Chain's time at network genesis.
type BuildGenesisArgs struct {
AvaxAssetID ids.ID `json:"avaxAssetID"`
NetworkID json.Uint32 `json:"networkID"`
UTXOs []APIUTXO `json:"utxos"`
Validators []APIPrimaryValidator `json:"validators"`
Chains []APIChain `json:"chains"`
Time json.Uint64 `json:"time"`
InitialSupply json.Uint64 `json:"initialSupply"`
Message string `json:"message"`
Encoding formatting.Encoding `json:"encoding"`
}
// BuildGenesisReply is the reply from BuildGenesis
type BuildGenesisReply struct {
Bytes string `json:"bytes"`
Encoding formatting.Encoding `json:"encoding"`
}
// GenesisUTXO adds messages to UTXOs
type GenesisUTXO struct {
avax.UTXO `serialize:"true"`
Message []byte `serialize:"true" json:"message"`
}
// Genesis represents a genesis state of the platform chain
type Genesis struct {
UTXOs []*GenesisUTXO `serialize:"true"`
Validators []*Tx `serialize:"true"`
Chains []*Tx `serialize:"true"`
Timestamp uint64 `serialize:"true"`
InitialSupply uint64 `serialize:"true"`
Message string `serialize:"true"`
}
func (g *Genesis) Initialize() error {
for _, tx := range g.Validators {
if err := tx.Sign(GenesisCodec, nil); err != nil {
return err
}
}
for _, tx := range g.Chains {
if err := tx.Sign(GenesisCodec, nil); err != nil {
return err
}
}
return nil
}
// beck32ToID takes bech32 address and produces a shortID
func bech32ToID(addrStr string) (ids.ShortID, error) {
_, addrBytes, err := address.ParseBech32(addrStr)
if err != nil {
return ids.ShortID{}, err
}
return ids.ToShortID(addrBytes)
}
// BuildGenesis build the genesis state of the Platform Chain (and thereby the Avalanche network.)
func (ss *StaticService) BuildGenesis(_ *http.Request, args *BuildGenesisArgs, reply *BuildGenesisReply) error {
// Specify the UTXOs on the Platform chain that exist at genesis.
utxos := make([]*GenesisUTXO, 0, len(args.UTXOs))
for i, apiUTXO := range args.UTXOs {
if apiUTXO.Amount == 0 {
return errUTXOHasNoValue
}
addrID, err := bech32ToID(apiUTXO.Address)
if err != nil {
return err
}
utxo := avax.UTXO{
UTXOID: avax.UTXOID{
TxID: ids.Empty,
OutputIndex: uint32(i),
},
Asset: avax.Asset{ID: args.AvaxAssetID},
Out: &secp256k1fx.TransferOutput{
Amt: uint64(apiUTXO.Amount),
OutputOwners: secp256k1fx.OutputOwners{
Locktime: 0,
Threshold: 1,
Addrs: []ids.ShortID{addrID},
},
},
}
if apiUTXO.Locktime > args.Time {
utxo.Out = &stakeable.LockOut{
Locktime: uint64(apiUTXO.Locktime),
TransferableOut: utxo.Out.(avax.TransferableOut),
}
}
messageBytes, err := formatting.Decode(args.Encoding, apiUTXO.Message)
if err != nil {
return fmt.Errorf("problem decoding UTXO message bytes: %w", err)
}
utxos = append(utxos, &GenesisUTXO{
UTXO: utxo,
Message: messageBytes,
})
}
// Specify the validators that are validating the primary network at genesis.
vdrs := newTxHeapByEndTime()
for _, validator := range args.Validators {
weight := uint64(0)
stake := make([]*avax.TransferableOutput, len(validator.Staked))
sortAPIUTXOs(validator.Staked)
for i, apiUTXO := range validator.Staked {
addrID, err := bech32ToID(apiUTXO.Address)
if err != nil {
return err
}
utxo := &avax.TransferableOutput{
Asset: avax.Asset{ID: args.AvaxAssetID},
Out: &secp256k1fx.TransferOutput{
Amt: uint64(apiUTXO.Amount),
OutputOwners: secp256k1fx.OutputOwners{
Locktime: 0,
Threshold: 1,
Addrs: []ids.ShortID{addrID},
},
},
}
if apiUTXO.Locktime > args.Time {
utxo.Out = &stakeable.LockOut{
Locktime: uint64(apiUTXO.Locktime),
TransferableOut: utxo.Out,
}
}
stake[i] = utxo
newWeight, err := safemath.Add64(weight, uint64(apiUTXO.Amount))
if err != nil {
return errStakeOverflow
}
weight = newWeight
}
if weight == 0 {
return errValidatorAddsNoValue
}
if uint64(validator.EndTime) <= uint64(args.Time) {
return errValidatorAddsNoValue
}
owner := &secp256k1fx.OutputOwners{
Locktime: uint64(validator.RewardOwner.Locktime),
Threshold: uint32(validator.RewardOwner.Threshold),
}
for _, addrStr := range validator.RewardOwner.Addresses {
addrID, err := bech32ToID(addrStr)
if err != nil {
return err
}
owner.Addrs = append(owner.Addrs, addrID)
}
ids.SortShortIDs(owner.Addrs)
delegationFee := uint32(0)
if validator.ExactDelegationFee != nil {
delegationFee = uint32(*validator.ExactDelegationFee)
}
tx := &Tx{UnsignedTx: &UnsignedAddValidatorTx{
BaseTx: BaseTx{BaseTx: avax.BaseTx{
NetworkID: uint32(args.NetworkID),
BlockchainID: ids.Empty,
}},
Validator: pChainValidator.Validator{
NodeID: validator.NodeID,
Start: uint64(args.Time),
End: uint64(validator.EndTime),
Wght: weight,
},
Stake: stake,
RewardsOwner: owner,
Shares: delegationFee,
}}
if err := tx.Sign(GenesisCodec, nil); err != nil {
return err
}
vdrs.Add(tx)
}
// Specify the chains that exist at genesis.
chains := []*Tx{}
for _, chain := range args.Chains {
genesisBytes, err := formatting.Decode(args.Encoding, chain.GenesisData)
if err != nil {
return fmt.Errorf("problem decoding chain genesis data: %w", err)
}
tx := &Tx{UnsignedTx: &UnsignedCreateChainTx{
BaseTx: BaseTx{BaseTx: avax.BaseTx{
NetworkID: uint32(args.NetworkID),
BlockchainID: ids.Empty,
}},
SubnetID: chain.SubnetID,
ChainName: chain.Name,
VMID: chain.VMID,
FxIDs: chain.FxIDs,
GenesisData: genesisBytes,
SubnetAuth: &secp256k1fx.Input{},
}}
if err := tx.Sign(GenesisCodec, nil); err != nil {
return err
}
chains = append(chains, tx)
}
validatorTxs := make([]*Tx, vdrs.Len())
for i, tx := range vdrs.txs {
validatorTxs[i] = tx.tx
}
// genesis holds the genesis state
genesis := Genesis{
UTXOs: utxos,
Validators: validatorTxs,
Chains: chains,
Timestamp: uint64(args.Time),
InitialSupply: uint64(args.InitialSupply),
Message: args.Message,
}
// Marshal genesis to bytes
bytes, err := GenesisCodec.Marshal(CodecVersion, genesis)
if err != nil {
return fmt.Errorf("couldn't marshal genesis: %w", err)
}
reply.Bytes, err = formatting.EncodeWithChecksum(args.Encoding, bytes)
if err != nil {
return fmt.Errorf("couldn't encode genesis as string: %w", err)
}
reply.Encoding = args.Encoding
return nil
}
type innerSortAPIUTXO []APIUTXO
func (xa innerSortAPIUTXO) Less(i, j int) bool {
if xa[i].Locktime < xa[j].Locktime {
return true
} else if xa[i].Locktime > xa[j].Locktime {
return false
}
if xa[i].Amount < xa[j].Amount {
return true
} else if xa[i].Amount > xa[j].Amount {
return false
}
iAddrID, err := bech32ToID(xa[i].Address)
if err != nil {
return false
}
jAddrID, err := bech32ToID(xa[j].Address)
if err != nil {
return false
}
return bytes.Compare(iAddrID.Bytes(), jAddrID.Bytes()) == -1
}
func (xa innerSortAPIUTXO) Len() int { return len(xa) }
func (xa innerSortAPIUTXO) Swap(i, j int) { xa[j], xa[i] = xa[i], xa[j] }
func sortAPIUTXOs(a []APIUTXO) { sort.Sort(innerSortAPIUTXO(a)) }