mirror of
https://github.com/trezor/blockbook.git
synced 2026-02-20 00:51:39 +01:00
perf: short-circuit address parsing
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user