perf: short-circuit address parsing

This commit is contained in:
pragmaxim
2026-01-18 07:07:55 +01:00
parent 970581b111
commit 164fe2de3d
2 changed files with 89 additions and 6 deletions

View File

@@ -2,6 +2,7 @@ package eth
import (
"context"
"encoding/hex"
"math/big"
"strings"
@@ -32,14 +33,20 @@ const contractDecimalsSignature = "0x313ce567"
const contractBalanceOfSignature = "0x70a08231"
func addressFromPaddedHex(s string) (string, error) {
var t big.Int
var ok bool
// Logs/topics and calldata often include a 0x prefix; strip it so length math and decoding are consistent.
if has0xPrefix(s) {
_, ok = t.SetString(s[2:], 16)
} else {
_, ok = t.SetString(s, 16)
s = s[2:]
}
if !ok {
if len(s) >= EthereumTypeAddressDescriptorLen*2 {
// Fast path: padded topics/data store the address in the last 20 bytes.
s = s[len(s)-EthereumTypeAddressDescriptorLen*2:]
if b, err := hex.DecodeString(s); err == nil && len(b) == EthereumTypeAddressDescriptorLen {
return ethcommon.BytesToAddress(b).String(), nil
}
}
// Fallback: handle odd formats by parsing the full value as a number.
var t big.Int
if _, ok := t.SetString(s, 16); !ok {
return "", errors.New("Data is not a number")
}
a := ethcommon.BigToAddress(&t)

View File

@@ -3,6 +3,8 @@
package eth
import (
"bytes"
"encoding/hex"
"fmt"
"math/big"
"strings"
@@ -12,6 +14,80 @@ import (
"github.com/trezor/blockbook/tests/dbtestdata"
)
func Test_addressFromPaddedHex(t *testing.T) {
tests := []struct {
name string
input string
wantHex string
wantErr bool
}{
{
name: "address_no_padding_with_prefix",
input: "0x5dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
wantHex: "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
},
{
name: "uppercase_prefix",
input: "0X0000000000000000000000005dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
wantHex: "5dc6288b35e0807a3d6feb89b3a2ff4ab773168e",
},
{
name: "padded",
input: "0x0000000000000000000000002aacf811ac1a60081ea39f7783c0d26c500871a8",
wantHex: "2aacf811ac1a60081ea39f7783c0d26c500871a8",
},
{
name: "all_zero",
input: "0x0000000000000000000000000000000000000000000000000000000000000000",
wantHex: "0000000000000000000000000000000000000000",
},
{
name: "unpadded",
input: "1a2b3c",
wantHex: "00000000000000000000000000000000001a2b3c",
},
{
name: "invalid_fast_path",
input: "0x0000000000000000000000002aacf811ac1a60081ea39f7783c0d26c500871zz",
wantErr: true,
},
{
name: "invalid",
input: "0xzz",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := addressFromPaddedHex(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error for %q", tt.input)
}
return
}
if err != nil {
t.Fatalf("addressFromPaddedHex(%q) error %v", tt.input, err)
}
if len(got) < 2 || !strings.HasPrefix(got, "0x") {
t.Fatalf("addressFromPaddedHex(%q) returned %q", tt.input, got)
}
gotBytes, err := hex.DecodeString(got[2:])
if err != nil {
t.Fatalf("decode got %q error %v", got, err)
}
wantBytes, err := hex.DecodeString(tt.wantHex)
if err != nil {
t.Fatalf("decode want %q error %v", tt.wantHex, err)
}
if !bytes.Equal(gotBytes, wantBytes) {
t.Fatalf("addressFromPaddedHex(%q) bytes %x want %x", tt.input, gotBytes, wantBytes)
}
})
}
}
func Test_contractGetTransfersFromLog(t *testing.T) {
tests := []struct {
name string