Skip to content

Commit 482aadf

Browse files
Merge pull request #16 from kaleido-io/tx-hash
Add features for calculating hashes of transactions
2 parents 94b6cf0 + 20a3aee commit 482aadf

File tree

3 files changed

+78
-23
lines changed

3 files changed

+78
-23
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ lint: ${LINT}
2121
${MOCKERY}:
2222
$(VGO) install github.com/vektra/mockery/cmd/mockery@latest
2323
${LINT}:
24-
$(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
24+
$(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.0
2525

2626

2727
define makemock

pkg/ethsigner/transaction.go

+70-16
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,24 @@ import (
2323
"github.com/hyperledger/firefly-signer/pkg/ethtypes"
2424
"github.com/hyperledger/firefly-signer/pkg/rlp"
2525
"github.com/hyperledger/firefly-signer/pkg/secp256k1"
26+
"golang.org/x/crypto/sha3"
2627
)
2728

29+
type TransactionSignaturePayload struct {
30+
rlpList rlp.List
31+
data []byte
32+
}
33+
34+
func (sp *TransactionSignaturePayload) Bytes() []byte {
35+
return sp.data
36+
}
37+
38+
func (sp *TransactionSignaturePayload) Hash() ethtypes.HexBytes0xPrefix {
39+
msgHash := sha3.NewLegacyKeccak256()
40+
msgHash.Write(sp.data)
41+
return msgHash.Sum(nil)
42+
}
43+
2844
const (
2945
TransactionTypeLegacy byte = 0x00
3046
TransactionType2930 byte = 0x01 // unused
@@ -87,47 +103,85 @@ func (t *Transaction) Sign(signer *secp256k1.KeyPair, chainID int64) ([]byte, er
87103
return t.SignLegacyEIP155(signer, chainID)
88104
}
89105

90-
// SignLegacyOriginal uses legacy transaction structure, with legacy V value (27/28)
91-
func (t *Transaction) SignLegacyOriginal(signer *secp256k1.KeyPair) ([]byte, error) {
106+
// Returns the bytes that would be used to sign the transaction, without actually
107+
// perform the signing. Can be used with Recover to verify a signing result.
108+
func (t *Transaction) SignaturePayload(chainID int64) (sp *TransactionSignaturePayload) {
109+
if t.MaxPriorityFeePerGas.BigInt().Sign() > 0 || t.MaxFeePerGas.BigInt().Sign() > 0 {
110+
return t.SignaturePayloadEIP1559(chainID)
111+
}
112+
return t.SignaturePayloadLegacyEIP155(chainID)
113+
}
114+
115+
// SignaturePayloadLegacyOriginal returns the rlpList of fields that are signed, and the
116+
// bytes. Note that for legacy and EIP-155 transactions (everything prior to EIP-2718),
117+
// there is no transaction type byte added (so the bytes are exactly rlpList.Encode())
118+
func (t *Transaction) SignaturePayloadLegacyOriginal() *TransactionSignaturePayload {
92119
rlpList := t.BuildLegacy()
120+
return &TransactionSignaturePayload{
121+
rlpList: rlpList,
122+
data: rlpList.Encode(),
123+
}
124+
}
93125

94-
txData := rlpList.Encode()
95-
sig, err := signer.Sign(txData)
126+
// SignLegacyOriginal uses legacy transaction structure, with legacy V value (27/28)
127+
func (t *Transaction) SignLegacyOriginal(signer *secp256k1.KeyPair) ([]byte, error) {
128+
signatureData := t.SignaturePayloadLegacyOriginal()
129+
sig, err := signer.Sign(signatureData.data)
96130
if err != nil {
97131
return nil, err
98132
}
99133

100-
rlpList = t.addSignature(rlpList, sig)
134+
rlpList := t.addSignature(signatureData.rlpList, sig)
101135
return rlpList.Encode(), nil
102136
}
103137

138+
// SignaturePayloadLegacyEIP155 returns the rlpList of fields that are signed, and the
139+
// bytes. Note that for legacy and EIP-155 transactions (everything prior to EIP-2718),
140+
// there is no transaction type byte added (so the bytes are exactly rlpList.Encode())
141+
func (t *Transaction) SignaturePayloadLegacyEIP155(chainID int64) *TransactionSignaturePayload {
142+
rlpList := t.BuildLegacy()
143+
rlpList = t.AddEIP155HashValues(rlpList, chainID)
144+
return &TransactionSignaturePayload{
145+
rlpList: rlpList,
146+
data: rlpList.Encode(),
147+
}
148+
}
149+
104150
// SignLegacyEIP155 uses legacy transaction structure, with EIP-155 signing V value (2*ChainID + 35 + Y-parity)
105151
func (t *Transaction) SignLegacyEIP155(signer *secp256k1.KeyPair, chainID int64) ([]byte, error) {
106-
rlpList := t.BuildLegacy()
107152

108-
rlpList = t.AddEIP155HashValues(rlpList, chainID)
153+
signaturePayload := t.SignaturePayloadLegacyEIP155(chainID)
109154

110-
hashData := rlpList.Encode()
111-
sig, err := signer.Sign(hashData)
155+
sig, err := signer.Sign(signaturePayload.data)
112156
if err != nil {
113157
return nil, err
114158
}
115159

116160
// Use the EIP-155 V value, of (2*ChainID + 35 + Y-parity)
117161
sig.UpdateEIP155(chainID)
118162

119-
rlpList = t.addSignature(rlpList[0:6] /* we don't include the chainID+0+0 hash values in the payload */, sig)
163+
rlpList := t.addSignature(signaturePayload.rlpList[0:6] /* we don't include the chainID+0+0 hash values in the payload */, sig)
120164
return rlpList.Encode(), nil
121165
}
122166

123-
// SignEIP1559 uses EIP-1559 transaction structure (with EIP-2718 transaction type byte), with EIP-2930 V value (0 / 1 - direct parity-Y)
124-
func (t *Transaction) SignEIP1559(signer *secp256k1.KeyPair, chainID int64) ([]byte, error) {
167+
// SignaturePayloadEIP1559 returns the rlpList of fields that are signed, along with the full
168+
// bytes for the signature / TX Hash - which have the transaction type prefixed
169+
func (t *Transaction) SignaturePayloadEIP1559(chainID int64) *TransactionSignaturePayload {
125170
rlpList := t.Build1559(chainID)
126171

127-
// First sign the transaction type, concattented with RLP list _excluding_ signature
172+
// The signature payload is the transaction type, concatenated with RLP list _excluding_ signature
128173
// keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))
129-
b := append([]byte{TransactionType1559}, rlpList.Encode()...)
130-
sig, err := signer.Sign(b)
174+
return &TransactionSignaturePayload{
175+
rlpList: rlpList,
176+
data: append([]byte{TransactionType1559}, rlpList.Encode()...),
177+
}
178+
}
179+
180+
// SignEIP1559 uses EIP-1559 transaction structure (with EIP-2718 transaction type byte), with EIP-2930 V value (0 / 1 - direct parity-Y)
181+
func (t *Transaction) SignEIP1559(signer *secp256k1.KeyPair, chainID int64) ([]byte, error) {
182+
183+
signaturePayload := t.SignaturePayloadEIP1559(chainID)
184+
sig, err := signer.Sign(signaturePayload.data)
131185
if err != nil {
132186
return nil, err
133187
}
@@ -137,7 +191,7 @@ func (t *Transaction) SignEIP1559(signer *secp256k1.KeyPair, chainID int64) ([]b
137191

138192
// Now we need a new RLP array, _including_ signature
139193
// 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])
140-
rlpList = t.addSignature(rlpList, sig)
194+
rlpList := t.addSignature(signaturePayload.rlpList, sig)
141195
return append([]byte{TransactionType1559}, rlpList.Encode()...), nil
142196
}
143197

pkg/ethsigner/transaction_test.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,12 @@ func TestSignAutoEIP155(t *testing.T) {
127127
foundSig.R.SetBytes([]byte(rlpList.(rlp.List)[7].(rlp.Data)))
128128
foundSig.S.SetBytes([]byte(rlpList.(rlp.List)[8].(rlp.Data)))
129129

130-
expectedUnsigned := txn.AddEIP155HashValues(txn.BuildLegacy(), 1001).Encode()
131-
addr, err := foundSig.Recover(expectedUnsigned, 1001)
130+
signaturePayload := txn.SignaturePayload(1001)
131+
addr, err := foundSig.Recover(signaturePayload.Bytes(), 1001)
132132
assert.NoError(t, err)
133133
assert.Equal(t, keypair.Address.String(), addr.String())
134134

135+
assert.Equal(t, "0x4524b8ac39ace2a3a2c061b73125c19c76daf0d25d44a4d88799f3c2ba686fe6", signaturePayload.Hash().String())
135136
}
136137

137138
func TestSignAutoEIP1559(t *testing.T) {
@@ -167,8 +168,8 @@ func TestSignAutoEIP1559(t *testing.T) {
167168
foundSig.R.SetBytes([]byte(rlpList.(rlp.List)[10].(rlp.Data)))
168169
foundSig.S.SetBytes([]byte(rlpList.(rlp.List)[11].(rlp.Data)))
169170

170-
expectedUnsigned := append([]byte{TransactionType1559}, txn.Build1559(1001).Encode()...)
171-
addr, err := foundSig.Recover(expectedUnsigned, 1001)
171+
signaturePayload := txn.SignaturePayload(1001)
172+
addr, err := foundSig.Recover(signaturePayload.Bytes(), 1001)
172173
assert.NoError(t, err)
173174
assert.Equal(t, keypair.Address.String(), addr.String())
174175

@@ -205,8 +206,8 @@ func TestSignLegacyOriginal(t *testing.T) {
205206
foundSig.R.SetBytes([]byte(rlpList.(rlp.List)[7].(rlp.Data)))
206207
foundSig.S.SetBytes([]byte(rlpList.(rlp.List)[8].(rlp.Data)))
207208

208-
expectedUnsigned := txn.BuildLegacy().Encode()
209-
addr, err := foundSig.Recover(expectedUnsigned, 0)
209+
signaturePayload := txn.SignaturePayloadLegacyOriginal()
210+
addr, err := foundSig.Recover(signaturePayload.Bytes(), 0)
210211
assert.NoError(t, err)
211212
assert.Equal(t, keypair.Address.String(), addr.String())
212213

0 commit comments

Comments
 (0)