forked from forks/go-ethereum
core/types: reduce allocs in transaction signing (#31258)
This PR roughly halves the number of allocations needed to compute the
sigHash for a transaction.
This sigHash is used whenever we recover a signature of a transaction,
so quite often. During a recent benchmark full syncing on Holesky,
roughly 2.8% of all allocations were happening here because the fields
from the transaction would be copied multiple times.
```
66168733 153175654 (flat, cum) 2.80% of Total
. . 368:func (s londonSigner) Hash(tx *Transaction) common.Hash {
. . 369: if tx.Type() != DynamicFeeTxType {
. . 370: return s.eip2930Signer.Hash(tx)
. . 371: }
. 19169966 372: return prefixedRlpHash(
. . 373: tx.Type(),
26442187 26442187 374: []interface{}{
. . 375: s.chainId,
6848616 6848616 376: tx.Nonce(),
. 19694077 377: tx.GasTipCap(),
. 18956774 378: tx.GasFeeCap(),
6357089 6357089 379: tx.Gas(),
. 12321050 380: tx.To(),
. 16865054 381: tx.Value(),
13435187 13435187 382: tx.Data(),
13085654 13085654 383: tx.AccessList(),
. . 384: })
. . 385:}
```
This PR reduces the allocations and speeds up the computation of the
sigHash by ~22%, which is quite significantly given that this operation
involves a call to Keccak
```
// BenchmarkHash-8 440082 2639 ns/op 384 B/op 13 allocs/op
// BenchmarkHash-8 493566 2033 ns/op 240 B/op 6 allocs/op
```
```
Hash-8 2.691µ ± 8% 2.097µ ± 9% -22.07% (p=0.000 n=10)
```
It also kinda cleans up stuff in my opinion, since the transaction
should itself know best how to compute the sighash

---------
Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
930836ed66
commit
b47e4d5b38
8 changed files with 105 additions and 64 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
@ -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 }
|
||||
|
|
@ -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 }
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue