-
Notifications
You must be signed in to change notification settings - Fork 319
/
rlp_tx.go
157 lines (140 loc) · 4.37 KB
/
rlp_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
146
147
148
149
150
151
152
153
154
155
156
157
package action
import (
"encoding/hex"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/iotexproject/go-pkgs/crypto"
"github.com/iotexproject/go-pkgs/hash"
"github.com/iotexproject/iotex-proto/golang/iotextypes"
"github.com/pkg/errors"
)
func rlpRawHash(rawTx *types.Transaction, signer types.Signer) (hash.Hash256, error) {
h := signer.Hash(rawTx)
return hash.BytesToHash256(h[:]), nil
}
func rlpSignedHash(tx *types.Transaction, signer types.Signer, sig []byte) (hash.Hash256, error) {
signedTx, err := RawTxToSignedTx(tx, signer, sig)
if err != nil {
return hash.ZeroHash256, err
}
h := signedTx.Hash()
return hash.BytesToHash256(h[:]), nil
}
// RawTxToSignedTx converts the raw tx to corresponding signed tx
func RawTxToSignedTx(rawTx *types.Transaction, signer types.Signer, sig []byte) (*types.Transaction, error) {
if len(sig) != 65 {
return nil, errors.Errorf("invalid signature length = %d, expecting 65", len(sig))
}
sc := make([]byte, 65)
copy(sc, sig)
if sc[64] >= 27 {
sc[64] -= 27
}
signedTx, err := rawTx.WithSignature(signer, sc)
if err != nil {
return nil, err
}
return signedTx, nil
}
// DecodeRawTx decodes raw data string into eth tx
func DecodeRawTx(rawData string, chainID uint32) (tx *types.Transaction, sig []byte, pubkey crypto.PublicKey, err error) {
//remove Hex prefix and decode string to byte
rawData = strings.Replace(rawData, "0x", "", -1)
rawData = strings.Replace(rawData, "0X", "", -1)
var dataInString []byte
dataInString, err = hex.DecodeString(rawData)
if err != nil {
return
}
// decode raw data into rlp tx
tx = &types.Transaction{}
err = rlp.DecodeBytes(dataInString, tx)
if err != nil {
return
}
// extract signature and recover pubkey
v, r, s := tx.RawSignatureValues()
recID := uint32(v.Int64()) - 2*chainID - 8
sig = make([]byte, 65)
rSize := len(r.Bytes())
copy(sig[32-rSize:32], r.Bytes())
sSize := len(s.Bytes())
copy(sig[64-sSize:], s.Bytes())
sig[64] = byte(recID)
// recover public key
rawHash := types.NewEIP155Signer(big.NewInt(int64(chainID))).Hash(tx)
pubkey, err = crypto.RecoverPubkey(rawHash[:], sig)
return
}
// NewEthSigner returns the proper signer for Eth-compatible tx
func NewEthSigner(txType iotextypes.Encoding, chainID uint32) (types.Signer, error) {
switch txType {
case iotextypes.Encoding_IOTEX_PROTOBUF, iotextypes.Encoding_ETHEREUM_UNPROTECTED:
// native tx use same signature format as that of Homestead (for pre-EIP155 unprotected tx)
return types.HomesteadSigner{}, nil
case iotextypes.Encoding_ETHEREUM_EIP155:
return types.NewEIP2930Signer(big.NewInt(int64(chainID))), nil
default:
return nil, ErrInvalidAct
}
}
// DecodeEtherTx decodes raw data string into eth tx
func DecodeEtherTx(rawData string) (*types.Transaction, error) {
//remove Hex prefix and decode string to byte
if strings.HasPrefix(rawData, "0x") || strings.HasPrefix(rawData, "0X") {
rawData = rawData[2:]
}
rawTxBytes, err := hex.DecodeString(rawData)
if err != nil {
return nil, err
}
// decode raw data into eth tx
tx := types.Transaction{}
if err = tx.UnmarshalBinary(rawTxBytes); err != nil {
return nil, err
}
return &tx, nil
}
// ExtractTypeSigPubkey extracts tx type, signature, and pubkey
func ExtractTypeSigPubkey(tx *types.Transaction) (iotextypes.Encoding, []byte, crypto.PublicKey, error) {
var (
encoding iotextypes.Encoding
signer = types.NewEIP2930Signer(tx.ChainId()) // by default assume latest signer
V, R, S = tx.RawSignatureValues()
)
// extract correct V value
switch tx.Type() {
case types.LegacyTxType:
if tx.Protected() {
chainIDMul := tx.ChainId()
V = new(big.Int).Sub(V, new(big.Int).Lsh(chainIDMul, 1))
V.Sub(V, big.NewInt(8))
encoding = iotextypes.Encoding_ETHEREUM_EIP155
} else {
// tx has pre-EIP155 signature
encoding = iotextypes.Encoding_ETHEREUM_UNPROTECTED
signer = types.HomesteadSigner{}
}
default:
return encoding, nil, nil, ErrNotSupported
}
// construct signature
if V.BitLen() > 8 {
return encoding, nil, nil, ErrNotSupported
}
var (
r, s = R.Bytes(), S.Bytes()
sig = make([]byte, 65)
pubkey crypto.PublicKey
err error
)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = byte(V.Uint64())
// recover public key
rawHash := signer.Hash(tx)
pubkey, err = crypto.RecoverPubkey(rawHash[:], sig)
return encoding, sig, pubkey, err
}