forked from ava-labs/avalanchego
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tx.go
127 lines (110 loc) · 3.32 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
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package txs
import (
"errors"
"fmt"
"github.com/kukrer/savannahnode/codec"
"github.com/kukrer/savannahnode/ids"
"github.com/kukrer/savannahnode/snow"
"github.com/kukrer/savannahnode/utils/crypto"
"github.com/kukrer/savannahnode/utils/hashing"
"github.com/kukrer/savannahnode/vms/components/avax"
"github.com/kukrer/savannahnode/vms/components/verify"
"github.com/kukrer/savannahnode/vms/secp256k1fx"
)
var (
errNilSignedTx = errors.New("nil signed tx is not valid")
errSignedTxNotInitialized = errors.New("signed tx was never initialized and is not valid")
)
// Tx is a signed transaction
type Tx struct {
// The body of this transaction
Unsigned UnsignedTx `serialize:"true" json:"unsignedTx"`
// The credentials of this transaction
Creds []verify.Verifiable `serialize:"true" json:"credentials"`
id ids.ID
bytes []byte
}
func NewSigned(
unsigned UnsignedTx,
c codec.Manager,
signers [][]*crypto.PrivateKeySECP256K1R,
) (*Tx, error) {
res := &Tx{Unsigned: unsigned}
return res, res.Sign(c, signers)
}
// Parse signed tx starting from its byte representation
func Parse(c codec.Manager, signedBytes []byte) (*Tx, error) {
tx := &Tx{}
if _, err := c.Unmarshal(signedBytes, tx); err != nil {
return nil, fmt.Errorf("couldn't parse tx: %w", err)
}
unsignedBytes, err := c.Marshal(Version, &tx.Unsigned)
if err != nil {
return nil, fmt.Errorf("couldn't marshal UnsignedTx: %w", err)
}
tx.Initialize(unsignedBytes, signedBytes)
return tx, nil
}
func (tx *Tx) Initialize(unsignedBytes, signedBytes []byte) {
tx.Unsigned.Initialize(unsignedBytes)
tx.bytes = signedBytes
tx.id = hashing.ComputeHash256Array(signedBytes)
}
func (tx *Tx) Bytes() []byte { return tx.bytes }
func (tx *Tx) ID() ids.ID { return tx.id }
// UTXOs returns the UTXOs transaction is producing.
func (tx *Tx) UTXOs() []*avax.UTXO {
outs := tx.Unsigned.Outputs()
utxos := make([]*avax.UTXO, len(outs))
for i, out := range outs {
utxos[i] = &avax.UTXO{
UTXOID: avax.UTXOID{
TxID: tx.id,
OutputIndex: uint32(i),
},
Asset: avax.Asset{ID: out.AssetID()},
Out: out.Out,
}
}
return utxos
}
func (tx *Tx) SyntacticVerify(ctx *snow.Context) error {
switch {
case tx == nil:
return errNilSignedTx
case tx.id == ids.Empty:
return errSignedTxNotInitialized
default:
return tx.Unsigned.SyntacticVerify(ctx)
}
}
// Sign this transaction with the provided signers
func (tx *Tx) Sign(c codec.Manager, signers [][]*crypto.PrivateKeySECP256K1R) error {
unsignedBytes, err := c.Marshal(Version, &tx.Unsigned)
if err != nil {
return fmt.Errorf("couldn't marshal UnsignedTx: %w", err)
}
// Attach credentials
hash := hashing.ComputeHash256(unsignedBytes)
for _, keys := range signers {
cred := &secp256k1fx.Credential{
Sigs: make([][crypto.SECP256K1RSigLen]byte, len(keys)),
}
for i, key := range keys {
sig, err := key.SignHash(hash) // Sign hash
if err != nil {
return fmt.Errorf("problem generating credential: %w", err)
}
copy(cred.Sigs[i][:], sig)
}
tx.Creds = append(tx.Creds, cred) // Attach credential
}
signedBytes, err := c.Marshal(Version, tx)
if err != nil {
return fmt.Errorf("couldn't marshal ProposalTx: %w", err)
}
tx.Initialize(unsignedBytes, signedBytes)
return nil
}