Skip to content

core/types: reduce allocs in transaction signing #31258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ type TxData interface {

encode(*bytes.Buffer) error
decode([]byte) error

// sigHash returns the hash of the transaction that is ought to be signed
sigHash(*big.Int) common.Hash
}

// EncodeRLP implements rlp.Encoder
Expand Down
70 changes: 6 additions & 64 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,20 +233,7 @@ func (s pragueSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != SetCodeTxType {
return s.cancunSigner.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.SetCodeAuthorizations(),
})
return tx.inner.sigHash(s.chainId)
}

type cancunSigner struct{ londonSigner }
Expand Down Expand Up @@ -301,21 +288,7 @@ func (s cancunSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != BlobTxType {
return s.londonSigner.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
tx.BlobGasFeeCap(),
tx.BlobHashes(),
})
return tx.inner.sigHash(s.chainId)
}

type londonSigner struct{ eip2930Signer }
Expand Down Expand Up @@ -369,19 +342,7 @@ func (s londonSigner) Hash(tx *Transaction) common.Hash {
if tx.Type() != DynamicFeeTxType {
return s.eip2930Signer.Hash(tx)
}
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasTipCap(),
tx.GasFeeCap(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
})
return tx.inner.sigHash(s.chainId)
}

type eip2930Signer struct{ EIP155Signer }
Expand Down Expand Up @@ -444,18 +405,7 @@ func (s eip2930Signer) Hash(tx *Transaction) common.Hash {
case LegacyTxType:
return s.EIP155Signer.Hash(tx)
case AccessListTxType:
return prefixedRlpHash(
tx.Type(),
[]interface{}{
s.chainId,
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
tx.AccessList(),
})
return tx.inner.sigHash(s.chainId)
default:
// This _should_ not happen, but in case someone sends in a bad
// json struct via RPC, it's probably more prudent to return an
Expand Down Expand Up @@ -525,15 +475,7 @@ func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
s.chainId, uint(0), uint(0),
})
return tx.inner.sigHash(s.chainId)
}

// HomesteadSigner implements Signer interface using the
Expand Down Expand Up @@ -597,7 +539,7 @@ func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *
// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
return rlpHash([]any{
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
Expand Down
17 changes: 17 additions & 0 deletions core/types/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,20 @@ func TestYParityJSONUnmarshalling(t *testing.T) {
}
}
}

func BenchmarkHash(b *testing.B) {
signer := NewLondonSigner(big.NewInt(1))
to := common.Address{}
tx := NewTx(&DynamicFeeTx{
ChainID: big.NewInt(123),
Nonce: 1,
Gas: 1000000,
To: &to,
Value: big.NewInt(1),
GasTipCap: big.NewInt(500),
GasFeeCap: big.NewInt(500),
})
for i := 0; i < b.N; i++ {
signer.Hash(tx)
}
}
15 changes: 15 additions & 0 deletions core/types/tx_access_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,18 @@ func (tx *AccessListTx) encode(b *bytes.Buffer) error {
func (tx *AccessListTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *AccessListTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
AccessListTxType,
[]any{
chainID,
tx.Nonce,
tx.GasPrice,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
})
}
18 changes: 18 additions & 0 deletions core/types/tx_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,21 @@ func (tx *BlobTx) decode(input []byte) error {
}
return nil
}

func (tx *BlobTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
BlobTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
tx.BlobFeeCap,
tx.BlobHashes,
})
}
16 changes: 16 additions & 0 deletions core/types/tx_dynamic_fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,19 @@ func (tx *DynamicFeeTx) encode(b *bytes.Buffer) error {
func (tx *DynamicFeeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *DynamicFeeTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
DynamicFeeTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
})
}
13 changes: 13 additions & 0 deletions core/types/tx_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,16 @@ func (tx *LegacyTx) encode(*bytes.Buffer) error {
func (tx *LegacyTx) decode([]byte) error {
panic("decode called on LegacyTx)")
}

// OBS: This is the post-EIP155 hash, the pre-EIP155 does not contain a chainID.
func (tx *LegacyTx) sigHash(chainID *big.Int) common.Hash {
return rlpHash([]any{
tx.Nonce,
tx.GasPrice,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
chainID, uint(0), uint(0),
})
}
17 changes: 17 additions & 0 deletions core/types/tx_setcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,20 @@ func (tx *SetCodeTx) encode(b *bytes.Buffer) error {
func (tx *SetCodeTx) decode(input []byte) error {
return rlp.DecodeBytes(input, tx)
}

func (tx *SetCodeTx) sigHash(chainID *big.Int) common.Hash {
return prefixedRlpHash(
SetCodeTxType,
[]any{
chainID,
tx.Nonce,
tx.GasTipCap,
tx.GasFeeCap,
tx.Gas,
tx.To,
tx.Value,
tx.Data,
tx.AccessList,
tx.AuthList,
})
}