-
Notifications
You must be signed in to change notification settings - Fork 328
/
sealedenvelope.go
254 lines (234 loc) · 7.18 KB
/
sealedenvelope.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
package action
import (
"encoding/hex"
"github.com/ethereum/go-ethereum/core/types"
"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"github.com/iotexproject/iotex-core/v2/pkg/log"
"github.com/iotexproject/iotex-core/v2/pkg/util/byteutil"
)
// SealedEnvelope is a signed action envelope.
type SealedEnvelope struct {
Envelope
encoding iotextypes.Encoding
evmNetworkID uint32
srcPubkey crypto.PublicKey
signature []byte
srcAddress address.Address
hash hash.Hash256
}
// envelopeHash returns the raw hash of embedded Envelope (this is the hash to be signed)
// an all-0 return value means the transaction is invalid
func (sealed *SealedEnvelope) envelopeHash() (hash.Hash256, error) {
switch sealed.encoding {
case iotextypes.Encoding_TX_CONTAINER:
act, ok := sealed.Envelope.(*txContainer)
if !ok {
return hash.ZeroHash256, ErrInvalidAct
}
encoding, err := act.typeToEncoding()
if err != nil {
return hash.ZeroHash256, err
}
signer, err := NewEthSigner(encoding, sealed.evmNetworkID)
if err != nil {
return hash.ZeroHash256, err
}
return rlpRawHash(act.tx, signer)
case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
tx, err := sealed.ToEthTx()
if err != nil {
return hash.ZeroHash256, err
}
signer, err := NewEthSigner(sealed.encoding, sealed.evmNetworkID)
if err != nil {
return hash.ZeroHash256, err
}
return rlpRawHash(tx, signer)
case iotextypes.Encoding_IOTEX_PROTOBUF:
return hash.Hash256b(byteutil.Must(proto.Marshal(sealed.Envelope.ProtoForHash()))), nil
default:
return hash.ZeroHash256, errors.Errorf("unknown encoding type %v", sealed.encoding)
}
}
// Hash returns the hash value of SealedEnvelope.
// an all-0 return value means the transaction is invalid
func (sealed *SealedEnvelope) Hash() (hash.Hash256, error) {
if sealed.hash == hash.ZeroHash256 {
hashVal, hashErr := sealed.calcHash()
if hashErr == nil {
sealed.hash = hashVal
}
return sealed.hash, hashErr
}
return sealed.hash, nil
}
func (sealed *SealedEnvelope) calcHash() (hash.Hash256, error) {
switch sealed.encoding {
case iotextypes.Encoding_TX_CONTAINER:
act, ok := sealed.Envelope.(*txContainer)
if !ok {
return hash.ZeroHash256, ErrInvalidAct
}
return act.hash(), nil
case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
tx, err := sealed.ToEthTx()
if err != nil {
return hash.ZeroHash256, err
}
signer, err := NewEthSigner(sealed.encoding, sealed.evmNetworkID)
if err != nil {
return hash.ZeroHash256, err
}
return rlpSignedHash(tx, signer, sealed.Signature())
case iotextypes.Encoding_IOTEX_PROTOBUF:
return hash.Hash256b(byteutil.Must(proto.Marshal(sealed.protoForHash()))), nil
default:
return hash.ZeroHash256, errors.Errorf("unknown encoding type %v", sealed.encoding)
}
}
// SrcPubkey returns the source public key
func (sealed *SealedEnvelope) SrcPubkey() crypto.PublicKey { return sealed.srcPubkey }
// SenderAddress returns address of the source public key
func (sealed *SealedEnvelope) SenderAddress() address.Address {
if sealed.srcAddress == nil {
sealed.srcAddress = sealed.srcPubkey.Address()
}
return sealed.srcAddress
}
// Signature returns signature bytes
func (sealed *SealedEnvelope) Signature() []byte {
sig := make([]byte, len(sealed.signature))
copy(sig, sealed.signature)
return sig
}
// Encoding returns the encoding
func (sealed *SealedEnvelope) Encoding() uint32 {
return uint32(sealed.encoding)
}
// ToEthTx converts to Ethereum tx
func (sealed *SealedEnvelope) ToEthTx() (*types.Transaction, error) {
return sealed.Envelope.ToEthTx(sealed.evmNetworkID, sealed.encoding)
}
// Proto converts it to it's proto scheme.
func (sealed *SealedEnvelope) Proto() *iotextypes.Action {
return &iotextypes.Action{
Core: sealed.Envelope.Proto(),
SenderPubKey: sealed.srcPubkey.Bytes(),
Signature: sealed.signature,
Encoding: sealed.encoding,
}
}
func (sealed *SealedEnvelope) protoForHash() *iotextypes.Action {
return &iotextypes.Action{
Core: sealed.Envelope.ProtoForHash(),
SenderPubKey: sealed.srcPubkey.Bytes(),
Signature: sealed.signature,
Encoding: sealed.encoding,
}
}
// loadProto loads from proto scheme.
func (sealed *SealedEnvelope) loadProto(pbAct *iotextypes.Action, evmID uint32) error {
if pbAct == nil {
return ErrNilProto
}
if sealed == nil {
return ErrNilAction
}
sigSize := len(pbAct.GetSignature())
if sigSize != 65 {
return errors.Errorf("invalid signature length = %d, expecting 65", sigSize)
}
elp, err := protoToEnvelope(pbAct)
if err != nil {
return err
}
// populate pubkey and signature
srcPub, err := crypto.BytesToPublicKey(pbAct.GetSenderPubKey())
if err != nil {
return err
}
encoding := pbAct.GetEncoding()
switch encoding {
case iotextypes.Encoding_TX_CONTAINER:
// verify it is container format
if _, ok := elp.(TxContainer); !ok {
return ErrInvalidAct
}
sealed.evmNetworkID = evmID
case iotextypes.Encoding_ETHEREUM_EIP155, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
// verify action type can support RLP-encoding
tx, err := elp.ToEthTx(evmID, encoding)
if err != nil {
return err
}
signer, err := NewEthSigner(encoding, evmID)
if err != nil {
return err
}
if _, err = rlpSignedHash(tx, signer, pbAct.GetSignature()); err != nil {
return err
}
sealed.evmNetworkID = evmID
case iotextypes.Encoding_IOTEX_PROTOBUF:
break
default:
return errors.Errorf("unknown encoding type %v", encoding)
}
// clear 'sealed' and populate new value
sealed.Envelope = elp
sealed.srcPubkey = srcPub
sealed.signature = make([]byte, sigSize)
copy(sealed.signature, pbAct.GetSignature())
sealed.encoding = encoding
sealed.hash = hash.ZeroHash256
sealed.srcAddress = nil
return nil
}
func protoToEnvelope(pbAct *iotextypes.Action) (Envelope, error) {
var (
elp = &envelope{}
cnt = &txContainer{}
)
if pbAct.GetEncoding() == iotextypes.Encoding_TX_CONTAINER {
if err := cnt.LoadProto(pbAct.GetCore()); err != nil {
return nil, err
}
return cnt, nil
}
if err := elp.LoadProto(pbAct.GetCore()); err != nil {
return nil, err
}
return elp, nil
}
// VerifySignature verifies the action using sender's public key
func (sealed *SealedEnvelope) VerifySignature() error {
if sealed.SrcPubkey() == nil {
return errors.New("empty public key")
}
h, err := sealed.envelopeHash()
if err != nil {
return errors.Wrap(err, "failed to generate envelope hash")
}
if !sealed.SrcPubkey().Verify(h[:], sealed.Signature()) {
log.L().Info("failed to verify action hash",
zap.String("hash", hex.EncodeToString(h[:])),
zap.String("signature", hex.EncodeToString(sealed.Signature())))
return ErrInvalidSender
}
return nil
}
// Protected says whether the transaction is replay-protected.
func (sealed *SealedEnvelope) Protected() bool {
switch sealed.encoding {
case iotextypes.Encoding_TX_CONTAINER:
return sealed.Envelope.(*txContainer).tx.Protected()
default:
return sealed.encoding != iotextypes.Encoding_ETHEREUM_UNPROTECTED
}
}