-
Notifications
You must be signed in to change notification settings - Fork 646
/
tx.go
145 lines (114 loc) · 4.08 KB
/
tx.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
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package spchainvm
import (
"errors"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/utils/crypto"
)
var (
errNilTx = errors.New("nil tx")
errTxHasNoValue = errors.New("tx has no value")
errWrongNetworkID = errors.New("tx has wrong network ID")
errWrongChainID = errors.New("tx has wrong chain ID")
)
// Tx is a monetary transfer
type Tx struct {
// The ID of this transaction
id ids.ID
networkID uint32
// The ID of the chain this transaction was issued on.
// Used to prevent replay attacks. Without this field, an attacker could
// re-issue a transaction sent on another chain running the same vm.
chainID ids.ID
// The recipient of the transfered funds
to ids.ShortID
// The nonce of the transaction
nonce uint64
// The amount to be transfered
amount uint64
// The signature on this transaction (namely, on [bytes])
sig []byte
// The public key that authorized this transaction
pubkey crypto.PublicKey
// The byte representation of this transaction
bytes []byte
// Called when this transaction is decided
onDecide func(choices.Status)
startedVerification, finishedVerification bool
verificationErr error
verification chan error
}
// ID of this transaction
func (tx *Tx) ID() ids.ID { return tx.id }
// Nonce is the new nonce of the account this transaction is being sent from
func (tx *Tx) Nonce() uint64 { return tx.nonce }
// Amount is the number of units to transfer to the recipient
func (tx *Tx) Amount() uint64 { return tx.amount }
// To is the address this transaction is sending to
func (tx *Tx) To() ids.ShortID { return tx.to }
// Bytes is the byte representation of this transaction
func (tx *Tx) Bytes() []byte { return tx.bytes }
// Key returns the public key used to authorize this transaction
// Key may return nil if Verify returned an error
// This function also sets [tx]'s public key
func (tx *Tx) Key(ctx *snow.Context) crypto.PublicKey { return tx.key(ctx, &crypto.FactorySECP256K1R{}) }
func (tx *Tx) key(ctx *snow.Context, factory *crypto.FactorySECP256K1R) crypto.PublicKey {
// Verify must be called to check this error and ensure that the public key is valid
_ = tx.verify(ctx, factory) // Sets the public key, assuming this tx is valid
return tx.pubkey
}
// Verify that this transaction is well formed
func (tx *Tx) Verify(ctx *snow.Context) error { return tx.verify(ctx, &crypto.FactorySECP256K1R{}) }
func (tx *Tx) verify(ctx *snow.Context, factory *crypto.FactorySECP256K1R) error {
// Check if tx has already been verified
if tx.finishedVerification {
return tx.verificationErr
}
// past this point, we know verification has neither passed nor failed in the past
tx.startVerify(ctx, factory)
// Wait for verification to complete
tx.verificationErr = <-tx.verification
tx.finishedVerification = true
return tx.verificationErr
}
func (tx *Tx) startVerify(ctx *snow.Context, factory *crypto.FactorySECP256K1R) {
// See if verification has been started.
// If not, start verification
if !tx.startedVerification {
tx.startedVerification = true
go func(tx *Tx, ctx *snow.Context, factory *crypto.FactorySECP256K1R) {
tx.verification <- tx.syncVerify(ctx, factory)
}(tx, ctx, factory)
}
}
func (tx *Tx) syncVerify(ctx *snow.Context, factory *crypto.FactorySECP256K1R) error {
switch {
case tx == nil:
return errNilTx
case tx.pubkey != nil:
return nil
case tx.amount == 0:
return errTxHasNoValue
case tx.networkID != ctx.NetworkID:
return errWrongNetworkID
case !tx.chainID.Equals(ctx.ChainID):
return errWrongChainID
}
codec := Codec{}
// The byte repr. of this transaction, unsigned
unsignedBytes, err := codec.MarshalUnsignedTx(tx)
if err != nil {
return err
}
// Using [unsignedBytes] and [tx.sig], derive the public key
// that authorized this transaction
key, err := factory.RecoverPublicKey(unsignedBytes, tx.sig)
if err != nil {
return err
}
tx.pubkey = key
return nil
}