-
Notifications
You must be signed in to change notification settings - Fork 4
/
cmd.go
136 lines (113 loc) · 3.29 KB
/
cmd.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
// Copyright (C) 2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package importtx
import (
"encoding/json"
"fmt"
"log"
"time"
"github.com/spf13/cobra"
"github.com/ava-labs/avalanchego/api/info"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/xsvm/api"
"github.com/ava-labs/xsvm/tx"
)
func Command() *cobra.Command {
c := &cobra.Command{
Use: "import",
Short: "Issues an import transaction",
RunE: importFunc,
}
flags := c.Flags()
AddFlags(flags)
return c
}
func importFunc(c *cobra.Command, args []string) error {
flags := c.Flags()
config, err := ParseFlags(flags, args)
if err != nil {
return err
}
ctx := c.Context()
var (
// Note: here we assume the unsigned message is correct from the last
// URI in sourceURIs. In practice this shouldn't be done.
unsignedMessage *warp.UnsignedMessage
// Note: assumes that sourceURIs are all of the validators of the subnet
// and that they do not share public keys.
signatures = make([]*bls.Signature, len(config.SourceURIs))
)
for i, uri := range config.SourceURIs {
xsClient := api.NewClient(uri, config.SourceChainID)
fetchStartTime := time.Now()
var rawSignature []byte
unsignedMessage, rawSignature, err = xsClient.Message(ctx, config.TxID)
if err != nil {
return fmt.Errorf("failed to fetch BLS signature from %s with: %w", uri, err)
}
sig, err := bls.SignatureFromBytes(rawSignature)
if err != nil {
return fmt.Errorf("failed to parse BLS signature from %s with: %w", uri, err)
}
// Note: the public key should not be fetched from the node in practice.
// The public key should be fetched from the P-chain directly.
infoClient := info.NewClient(uri)
_, nodePOP, err := infoClient.GetNodeID(ctx)
if err != nil {
return fmt.Errorf("failed to fetch BLS public key from %s with: %w", uri, err)
}
pk := nodePOP.Key()
if !bls.Verify(pk, sig, unsignedMessage.Bytes()) {
return fmt.Errorf("failed to verify BLS signature against public key from %s", uri)
}
log.Printf("fetched BLS signature from %s in %s\n", uri, time.Since(fetchStartTime))
signatures[i] = sig
}
signers := set.NewBits()
for i := range signatures {
signers.Add(i)
}
signature := &warp.BitSetSignature{
Signers: signers.Bytes(),
}
aggSignature, err := bls.AggregateSignatures(signatures)
if err != nil {
return err
}
aggSignatureBytes := bls.SignatureToBytes(aggSignature)
copy(signature.Signature[:], aggSignatureBytes)
message, err := warp.NewMessage(
unsignedMessage,
signature,
)
if err != nil {
return err
}
client := api.NewClient(config.URI, config.DestinationChainID)
nonce, err := client.Nonce(ctx, config.PrivateKey.Address())
if err != nil {
return err
}
utx := &tx.Import{
Nonce: nonce,
MaxFee: config.MaxFee,
Message: message.Bytes(),
}
stx, err := tx.Sign(utx, config.PrivateKey)
if err != nil {
return err
}
txJSON, err := json.MarshalIndent(stx, "", " ")
if err != nil {
return err
}
issueTxStartTime := time.Now()
txID, err := client.IssueTx(ctx, stx)
if err != nil {
return err
}
log.Printf("issued tx %s in %s\n%s\n", txID, time.Since(issueTxStartTime), string(txJSON))
return nil
}