Skip to content

Commit 68e8a69

Browse files
authored
Fix signature of typed transactions (#197)
* Fix signature of typed transactions * Add Changelog entry
1 parent 980c5ac commit 68e8a69

File tree

4 files changed

+96
-55
lines changed

4 files changed

+96
-55
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
# 0.1.2 (Unreleased)
33

4+
- Fix signing of typed transactions [[GH-197](https://github.com/umbracle/ethgo/issues/197)]
45
- Fix. Use `ethgo.BlockNumber` input to make `Call` in contract [[GH-194](https://github.com/umbracle/ethgo/issues/194)]
56
- Add `testcases` for contract signature and transaction signing [[GH-193](https://github.com/umbracle/ethgo/issues/193)]
67
- Add `eth_feeHistory` rpc endpoint [[GH-192](https://github.com/umbracle/ethgo/issues/192)]

structs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ func (t *Transaction) Copy() *Transaction {
228228
}
229229

230230
type AccessEntry struct {
231-
Address Address
232-
Storage []Hash
231+
Address Address `json:"address"`
232+
Storage []Hash `json:"storageKeys"`
233233
}
234234

235235
type AccessList []AccessEntry

testcases/transaction_test.go

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ func TestTransactions(t *testing.T) {
8181
}
8282

8383
func TestTypedTransactions(t *testing.T) {
84-
t.Skip()
85-
8684
var transactions []struct {
8785
Name string `json:"name"`
8886
AccountAddress ethgo.Address `json:"address"`
@@ -91,62 +89,68 @@ func TestTypedTransactions(t *testing.T) {
9189

9290
Tx struct {
9391
Type ethgo.TransactionType
94-
Data *ethgo.ArgBytes `json:"data,omitempty"`
95-
GasLimit *ethgo.ArgBig `json:"gasLimit,omitempty"`
96-
MaxPriorityFeePerGas *ethgo.ArgBig `json:"maxPriorityFeePerGas,omitempty"`
97-
MaxFeePerGas *ethgo.ArgBig `json:"maxFeePerGas,omitempty"`
98-
Nonce uint64 `json:"nonce,omitempty"`
99-
To *ethgo.Address `json:"to,omitempty"`
100-
Value *ethgo.ArgBig `json:"value,omitempty"`
101-
GasPrice *ethgo.ArgBig `json:"gasPrice,omitempty"`
102-
ChainID uint64 `json:"chainId,omitempty"`
92+
Data *ethgo.ArgBytes `json:"data,omitempty"`
93+
GasLimit *ethgo.ArgBig `json:"gasLimit,omitempty"`
94+
MaxPriorityFeePerGas *ethgo.ArgBig `json:"maxPriorityFeePerGas,omitempty"`
95+
MaxFeePerGas *ethgo.ArgBig `json:"maxFeePerGas,omitempty"`
96+
Nonce uint64 `json:"nonce,omitempty"`
97+
To *ethgo.Address `json:"to,omitempty"`
98+
Value *ethgo.ArgBig `json:"value,omitempty"`
99+
GasPrice *ethgo.ArgBig `json:"gasPrice,omitempty"`
100+
ChainID uint64 `json:"chainId,omitempty"`
101+
AccessList ethgo.AccessList `json:"accessList,omitempty"`
103102
}
104103
}
105104
ReadTestCase(t, "typed-transactions", &transactions)
106105

107106
for _, c := range transactions {
108-
key, err := wallet.NewWalletFromPrivKey(c.Key)
109-
assert.NoError(t, err)
110-
assert.Equal(t, key.Address(), c.AccountAddress)
111-
112-
txn := &ethgo.Transaction{
113-
ChainID: big.NewInt(0),
114-
Type: c.Tx.Type,
115-
MaxPriorityFeePerGas: (*big.Int)(c.Tx.MaxPriorityFeePerGas),
116-
MaxFeePerGas: (*big.Int)(c.Tx.MaxFeePerGas),
117-
}
118-
if c.Tx.Data != nil {
119-
txn.Input = *c.Tx.Data
120-
}
121-
if c.Tx.Value != nil {
122-
txn.Value = (*big.Int)(c.Tx.Value)
123-
}
124-
if c.Tx.To != nil {
125-
txn.To = c.Tx.To
126-
}
127-
if c.Tx.GasLimit != nil {
128-
gasLimit, isUint64 := getUint64FromBigInt(c.Tx.GasLimit)
129-
if !isUint64 {
130-
return
107+
t.Run(c.Name, func(t *testing.T) {
108+
key, err := wallet.NewWalletFromPrivKey(c.Key)
109+
assert.NoError(t, err)
110+
assert.Equal(t, key.Address(), c.AccountAddress)
111+
112+
chainID := big.NewInt(int64(c.Tx.ChainID))
113+
114+
txn := &ethgo.Transaction{
115+
ChainID: chainID,
116+
Type: c.Tx.Type,
117+
MaxPriorityFeePerGas: (*big.Int)(c.Tx.MaxPriorityFeePerGas),
118+
MaxFeePerGas: (*big.Int)(c.Tx.MaxFeePerGas),
119+
AccessList: c.Tx.AccessList,
131120
}
132-
txn.Gas = gasLimit
133-
}
134-
txn.Nonce = c.Tx.Nonce
135-
if c.Tx.GasPrice != nil {
136-
gasPrice, isUint64 := getUint64FromBigInt(c.Tx.GasPrice)
137-
if !isUint64 {
138-
return
121+
if c.Tx.Data != nil {
122+
txn.Input = *c.Tx.Data
123+
}
124+
if c.Tx.Value != nil {
125+
txn.Value = (*big.Int)(c.Tx.Value)
126+
}
127+
if c.Tx.To != nil {
128+
txn.To = c.Tx.To
129+
}
130+
if c.Tx.GasLimit != nil {
131+
gasLimit, isUint64 := getUint64FromBigInt(c.Tx.GasLimit)
132+
if !isUint64 {
133+
return
134+
}
135+
txn.Gas = gasLimit
136+
}
137+
txn.Nonce = c.Tx.Nonce
138+
if c.Tx.GasPrice != nil {
139+
gasPrice, isUint64 := getUint64FromBigInt(c.Tx.GasPrice)
140+
if !isUint64 {
141+
return
142+
}
143+
txn.GasPrice = gasPrice
139144
}
140-
txn.GasPrice = gasPrice
141-
}
142145

143-
signer := wallet.NewEIP155Signer(0)
144-
signedTxn, err := signer.SignTx(txn, key)
145-
assert.NoError(t, err)
146+
signer := wallet.NewEIP155Signer(chainID.Uint64())
147+
signedTxn, err := signer.SignTx(txn, key)
148+
assert.NoError(t, err)
146149

147-
txnRaw, err := signedTxn.MarshalRLPTo(nil)
148-
assert.NoError(t, err)
150+
txnRaw, err := signedTxn.MarshalRLPTo(nil)
151+
assert.NoError(t, err)
149152

150-
t.Log(txnRaw)
153+
assert.Equal(t, txnRaw, c.Signed.Bytes())
154+
})
151155
}
152156
}

wallet/signer.go

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ func (e *EIP1155Signer) SignTx(tx *ethgo.Transaction, key ethgo.Key) (*ethgo.Tra
5858
return nil, err
5959
}
6060

61-
vv := uint64(sig[64]) + 35 + e.chainID*2
61+
vv := uint64(sig[64])
62+
if tx.Type == 0 {
63+
vv = vv + 35 + e.chainID*2
64+
}
6265

6366
tx.R = trimBytesZeros(sig[:32])
6467
tx.S = trimBytesZeros(sig[32:64])
@@ -70,8 +73,23 @@ func signHash(tx *ethgo.Transaction, chainID uint64) []byte {
7073
a := fastrlp.DefaultArenaPool.Get()
7174

7275
v := a.NewArray()
76+
77+
if tx.Type != 0 {
78+
// either dynamic and access type
79+
v.Set(a.NewBigInt(tx.ChainID))
80+
}
81+
7382
v.Set(a.NewUint(tx.Nonce))
74-
v.Set(a.NewUint(tx.GasPrice))
83+
84+
if tx.Type == ethgo.TransactionDynamicFee {
85+
// dynamic fee uses
86+
v.Set(a.NewBigInt(tx.MaxPriorityFeePerGas))
87+
v.Set(a.NewBigInt(tx.MaxFeePerGas))
88+
} else {
89+
// legacy and access type use gas price
90+
v.Set(a.NewUint(tx.GasPrice))
91+
}
92+
7593
v.Set(a.NewUint(tx.Gas))
7694
if tx.To == nil {
7795
v.Set(a.NewNull())
@@ -81,14 +99,32 @@ func signHash(tx *ethgo.Transaction, chainID uint64) []byte {
8199
v.Set(a.NewBigInt(tx.Value))
82100
v.Set(a.NewCopyBytes(tx.Input))
83101

102+
if tx.Type != 0 {
103+
// either dynamic and access type
104+
accessList, err := tx.AccessList.MarshalRLPWith(a)
105+
if err != nil {
106+
panic(err)
107+
}
108+
v.Set(accessList)
109+
}
110+
84111
// EIP155
85-
if chainID != 0 {
112+
if chainID != 0 && tx.Type == 0 {
86113
v.Set(a.NewUint(chainID))
87114
v.Set(a.NewUint(0))
88115
v.Set(a.NewUint(0))
89116
}
90117

91-
hash := ethgo.Keccak256(v.MarshalTo(nil))
118+
dst := v.MarshalTo(nil)
119+
120+
// append the tx type byte
121+
if tx.Type == ethgo.TransactionAccessList {
122+
dst = append([]byte{0x1}, dst...)
123+
} else if tx.Type == ethgo.TransactionDynamicFee {
124+
dst = append([]byte{0x2}, dst...)
125+
}
126+
127+
hash := ethgo.Keccak256(dst)
92128
fastrlp.DefaultArenaPool.Put(a)
93129
return hash
94130
}

0 commit comments

Comments
 (0)