refactor: remove geth dependencies and internalize utilities

- Created utils package with common types (Address, Hash, Big1, Big0)
- Added utility functions (BytesToAddress, HexToAddress, CopyBytes, FromHex)
- Implemented math utilities (PaddedBigBytes, MustParseBig256)
- Created minimal RLP encoder for CreateAddress function
- Added hexutil functions for KZG4844 support
- Updated all imports to use local utils instead of geth
- All tests passing successfully
This commit is contained in:
Zach Kelling
2025-07-31 18:53:05 +00:00
parent c1ab877496
commit 45463e1cc7
18 changed files with 470 additions and 77 deletions
+1 -1
View File
@@ -9,7 +9,7 @@
package bn256
import (
bn256cf "github.com/luxfi/geth/crypto/bn256/cloudflare"
bn256cf "github.com/luxfi/crypto/bn256/cloudflare"
)
// G1 is an abstract cyclic group. The zero value is suitable for use as the
+1 -1
View File
@@ -8,7 +8,7 @@
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
package bn256
import bn256 "github.com/luxfi/geth/crypto/bn256/google"
import bn256 "github.com/luxfi/crypto/bn256/google"
// G1 is an abstract cyclic group. The zero value is suitable for use as the
// output of an operation, but cannot be used as an input.
+12 -14
View File
@@ -30,9 +30,7 @@ import (
"os"
"sync"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/math"
"github.com/luxfi/geth/rlp"
"github.com/luxfi/crypto/utils"
"golang.org/x/crypto/sha3"
)
@@ -81,7 +79,7 @@ var hasherPool = sync.Pool{
}
// HashData hashes the provided data using the KeccakState and returns a 32 byte hash
func HashData(kh KeccakState, data []byte) (h common.Hash) {
func HashData(kh KeccakState, data []byte) (h utils.Hash) {
kh.Reset()
kh.Write(data)
kh.Read(h[:])
@@ -103,7 +101,7 @@ func Keccak256(data ...[]byte) []byte {
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) {
func Keccak256Hash(data ...[]byte) (h utils.Hash) {
d := hasherPool.Get().(KeccakState)
d.Reset()
for _, b := range data {
@@ -124,15 +122,15 @@ func Keccak512(data ...[]byte) []byte {
}
// CreateAddress creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Keccak256(data)[12:])
func CreateAddress(b utils.Address, nonce uint64) utils.Address {
data, _ := utils.EncodeToBytes([]interface{}{b, nonce})
return utils.BytesToAddress(Keccak256(data)[12:])
}
// CreateAddress2 creates an ethereum address given the address bytes, initial
// contract code hash and a salt.
func CreateAddress2(b common.Address, salt [32]byte, inithash []byte) common.Address {
return common.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
func CreateAddress2(b utils.Address, salt [32]byte, inithash []byte) utils.Address {
return utils.BytesToAddress(Keccak256([]byte{0xff}, b.Bytes(), salt[:], inithash)[12:])
}
// ToECDSA creates a private key with the given D value.
@@ -180,7 +178,7 @@ func FromECDSA(priv *ecdsa.PrivateKey) []byte {
if priv == nil {
return nil
}
return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
return utils.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}
// UnmarshalPubkey converts bytes to a secp256k1 public key.
@@ -286,7 +284,7 @@ func GenerateKey() (*ecdsa.PrivateKey, error) {
// ValidateSignatureValues verifies whether the signature values are valid with
// the given chain rules. The v value is assumed to be either 0 or 1.
func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 {
if r.Cmp(utils.Big1) < 0 || s.Cmp(utils.Big1) < 0 {
return false
}
// reject upper range of s values (ECDSA malleability)
@@ -298,9 +296,9 @@ func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool {
return r.Cmp(secp256k1N) < 0 && s.Cmp(secp256k1N) < 0 && (v == 0 || v == 1)
}
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
func PubkeyToAddress(p ecdsa.PublicKey) utils.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Keccak256(pubBytes[1:])[12:])
return utils.BytesToAddress(Keccak256(pubBytes[1:])[12:])
}
func zeroBytes(bytes []byte) {
+23 -16
View File
@@ -26,13 +26,20 @@ import (
"reflect"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/crypto/utils"
)
var testAddrHex = "970e8128ab834e8eac17ab8e3812f010678cf791"
var testPrivHex = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"
func mustDecodeBig(s string) *big.Int {
v, ok := utils.ParseBig256(s)
if !ok {
panic("invalid big integer")
}
return v
}
// These tests are sanity checks.
// They should ensure that we don't e.g. use Sha3-224 instead of Sha3-256
// and that the sha3 library uses keccak-f permutation.
@@ -79,8 +86,8 @@ func TestUnmarshalPubkey(t *testing.T) {
enc, _ = hex.DecodeString("04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d")
dec = &ecdsa.PublicKey{
Curve: S256(),
X: hexutil.MustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
Y: hexutil.MustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
X: mustDecodeBig("0x760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1"),
Y: mustDecodeBig("0xb01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d"),
}
)
key, err = UnmarshalPubkey(enc)
@@ -94,7 +101,7 @@ func TestUnmarshalPubkey(t *testing.T) {
func TestSign(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)
addr := utils.HexToAddress(testAddrHex)
msg := Keccak256([]byte("foo"))
sig, err := Sign(msg, key)
@@ -133,7 +140,7 @@ func TestInvalidSign(t *testing.T) {
func TestNewContractAddress(t *testing.T) {
key, _ := HexToECDSA(testPrivHex)
addr := common.HexToAddress(testAddrHex)
addr := utils.HexToAddress(testAddrHex)
genAddr := PubkeyToAddress(key.PublicKey)
// sanity check before using addr to create contract address
checkAddr(t, genAddr, addr)
@@ -141,9 +148,9 @@ func TestNewContractAddress(t *testing.T) {
caddr0 := CreateAddress(addr, 0)
caddr1 := CreateAddress(addr, 1)
caddr2 := CreateAddress(addr, 2)
checkAddr(t, common.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0)
checkAddr(t, common.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1)
checkAddr(t, common.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
checkAddr(t, utils.HexToAddress("333c3310824b7c685133f2bedb2ca4b8b4df633d"), caddr0)
checkAddr(t, utils.HexToAddress("8bda78331c916a08481428e4b07c96d3e916d165"), caddr1)
checkAddr(t, utils.HexToAddress("c9ddedf451bc62ce88bf9292afb13df35b670699"), caddr2)
}
func TestLoadECDSA(t *testing.T) {
@@ -231,9 +238,9 @@ func TestValidateSignatureValues(t *testing.T) {
}
}
minusOne := big.NewInt(-1)
one := common.Big1
zero := common.Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, common.Big1)
one := utils.Big1
zero := utils.Big0
secp256k1nMinus1 := new(big.Int).Sub(secp256k1N, utils.Big1)
// correct v,r,s
check(true, 0, one, one)
@@ -277,7 +284,7 @@ func checkhash(t *testing.T, name string, f func([]byte) []byte, msg, exp []byte
}
}
func checkAddr(t *testing.T, addr0, addr1 common.Address) {
func checkAddr(t *testing.T, addr0, addr1 utils.Address) {
if addr0 != addr1 {
t.Fatalf("address mismatch: want: %x have: %x", addr0, addr1)
}
@@ -292,7 +299,7 @@ func TestPythonIntegration(t *testing.T) {
msg0 := Keccak256([]byte("foo"))
sig0, _ := Sign(msg0, k0)
msg1 := common.FromHex("00000000000000000000000000000000")
msg1 := utils.FromHex("00000000000000000000000000000000")
sig1, _ := Sign(msg0, k0)
t.Logf("msg: %x, privkey: %s sig: %x\n", msg0, kh, sig0)
@@ -301,7 +308,7 @@ func TestPythonIntegration(t *testing.T) {
// goos: darwin
// goarch: arm64
// pkg: github.com/luxfi/geth/crypto
// pkg: github.com/luxfi/crypto
// cpu: Apple M1 Pro
// BenchmarkKeccak256Hash
// BenchmarkKeccak256Hash-8 931095 1270 ns/op 32 B/op 1 allocs/op
@@ -317,7 +324,7 @@ func BenchmarkKeccak256Hash(b *testing.B) {
// goos: darwin
// goarch: arm64
// pkg: github.com/luxfi/geth/crypto
// pkg: github.com/luxfi/crypto
// cpu: Apple M1 Pro
// BenchmarkHashData
// BenchmarkHashData-8 793386 1278 ns/op 32 B/op 1 allocs/op
+3 -3
View File
@@ -41,7 +41,7 @@ import (
"io"
"math/big"
"github.com/luxfi/geth/crypto"
ethcrypto "github.com/luxfi/crypto"
)
var (
@@ -257,7 +257,7 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
d := messageTag(params.Hash, Km, em, s2)
if curve, ok := pub.Curve.(crypto.EllipticCurve); ok {
if curve, ok := pub.Curve.(ethcrypto.EllipticCurve); ok {
Rb := curve.Marshal(R.PublicKey.X, R.PublicKey.Y)
ct = make([]byte, len(Rb)+len(em)+len(d))
copy(ct, Rb)
@@ -303,7 +303,7 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
R := new(PublicKey)
R.Curve = prv.PublicKey.Curve
if curve, ok := R.Curve.(crypto.EllipticCurve); ok {
if curve, ok := R.Curve.(ethcrypto.EllipticCurve); ok {
R.X, R.Y = curve.Unmarshal(c[:rLen])
if R.X == nil {
return nil, ErrInvalidPublicKey
+1 -1
View File
@@ -39,7 +39,7 @@ import (
"math/big"
"testing"
"github.com/luxfi/geth/crypto"
"github.com/luxfi/crypto"
)
func TestKDF(t *testing.T) {
+1 -1
View File
@@ -43,7 +43,7 @@ import (
"fmt"
"hash"
ethcrypto "github.com/luxfi/geth/crypto"
ethcrypto "github.com/luxfi/crypto"
)
var (
-2
View File
@@ -10,7 +10,6 @@ require (
github.com/ethereum/c-kzg-4844/v2 v2.1.1
github.com/google/gofuzz v1.2.0
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
github.com/luxfi/geth v1.16.6
github.com/luxfi/ids v0.1.1
github.com/mr-tron/base58 v1.2.0
github.com/stretchr/testify v1.10.0
@@ -21,7 +20,6 @@ require (
require (
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/supranational/blst v0.3.15 // indirect
golang.org/x/sync v0.16.0 // indirect
+2 -6
View File
@@ -16,8 +16,6 @@ github.com/ethereum/c-kzg-4844/v2 v2.1.1 h1:KhzBVjmURsfr1+S3k/VE35T02+AW2qU9t9gr
github.com/ethereum/c-kzg-4844/v2 v2.1.1/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -26,16 +24,14 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/luxfi/geth v1.16.6 h1:3zJb3vaEIFrPfjyu1oP5rlgNuMI/dzJbeUR/VIvKnLE=
github.com/luxfi/geth v1.16.6/go.mod h1:UjfTCi+wUv4Bbg6zBdLUtqJ+3YVSdvLQSayiaKHVTXs=
github.com/luxfi/ids v0.1.1 h1:ga/sNdsDEpkOB1B89BWVsGowCECOK3FfXiy7EgeumzQ=
github.com/luxfi/ids v0.1.1/go.mod h1:eDCgoPXogp6hvVTSbxuplbc297jq0r6ftOZEKt6jSgY=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.15 h1:rd9viN6tfARE5wv3KZJ9H8e1cg0jXW8syFCcsbHa76o=
+7 -7
View File
@@ -24,7 +24,7 @@ import (
"reflect"
"sync/atomic"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/crypto/utils"
)
//go:embed trusted_setup.json
@@ -43,12 +43,12 @@ type Blob [131072]byte
// UnmarshalJSON parses a blob in hex syntax.
func (b *Blob) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(blobT, input, b[:])
return utils.UnmarshalFixedJSON(blobT, input, b[:])
}
// MarshalText returns the hex representation of b.
func (b *Blob) MarshalText() ([]byte, error) {
return hexutil.Bytes(b[:]).MarshalText()
return utils.Bytes(b[:]).MarshalText()
}
// Commitment is a serialized commitment to a polynomial.
@@ -56,12 +56,12 @@ type Commitment [48]byte
// UnmarshalJSON parses a commitment in hex syntax.
func (c *Commitment) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(commitmentT, input, c[:])
return utils.UnmarshalFixedJSON(commitmentT, input, c[:])
}
// MarshalText returns the hex representation of c.
func (c Commitment) MarshalText() ([]byte, error) {
return hexutil.Bytes(c[:]).MarshalText()
return utils.Bytes(c[:]).MarshalText()
}
// Proof is a serialized commitment to the quotient polynomial.
@@ -69,12 +69,12 @@ type Proof [48]byte
// UnmarshalJSON parses a proof in hex syntax.
func (p *Proof) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(proofT, input, p[:])
return utils.UnmarshalFixedJSON(proofT, input, p[:])
}
// MarshalText returns the hex representation of p.
func (p Proof) MarshalText() ([]byte, error) {
return hexutil.Bytes(p[:]).MarshalText()
return utils.Bytes(p[:]).MarshalText()
}
// Point is a BLS field element.
+4 -4
View File
@@ -25,7 +25,7 @@ import (
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/crypto/utils"
)
// ckzgAvailable signals whether the library was compiled into Geth.
@@ -49,15 +49,15 @@ func ckzgInit() {
}
g1Lag := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
for i, g1 := range params.SetupG1Lagrange {
copy(g1Lag[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
copy(g1Lag[i*(len(g1)-2)/2:], utils.MustDecode(g1))
}
g1s := make([]byte, len(params.SetupG1Monomial)*(len(params.SetupG1Monomial[0])-2)/2)
for i, g1 := range params.SetupG1Monomial {
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
copy(g1s[i*(len(g1)-2)/2:], utils.MustDecode(g1))
}
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
for i, g2 := range params.SetupG2 {
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
copy(g2s[i*(len(g2)-2)/2:], utils.MustDecode(g2))
}
// The last parameter determines the multiplication table, see https://notes.ethereum.org/@jtraglia/windowed_multiplications
// I think 6 is an decent compromise between size and speed
+3 -3
View File
@@ -15,7 +15,7 @@
package secp256k1
import (
_ "github.com/luxfi/geth/crypto/secp256k1/libsecp256k1/include"
_ "github.com/luxfi/geth/crypto/secp256k1/libsecp256k1/src"
_ "github.com/luxfi/geth/crypto/secp256k1/libsecp256k1/src/modules/recovery"
_ "github.com/luxfi/crypto/secp256k1/libsecp256k1/include"
_ "github.com/luxfi/crypto/secp256k1/libsecp256k1/src"
_ "github.com/luxfi/crypto/secp256k1/libsecp256k1/src/modules/recovery"
)
+3 -3
View File
@@ -25,8 +25,8 @@ import (
"fmt"
"math/big"
"github.com/luxfi/geth/common/math"
"github.com/luxfi/geth/crypto/secp256k1"
"github.com/luxfi/crypto/secp256k1"
"github.com/luxfi/crypto/utils"
)
// Ecrecover returns the uncompressed public key that created the given signature.
@@ -55,7 +55,7 @@ func Sign(digestHash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) {
if len(digestHash) != DigestLength {
return nil, fmt.Errorf("hash is required to be exactly %d bytes (%d)", DigestLength, len(digestHash))
}
seckey := math.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
seckey := utils.PaddedBigBytes(prv.D, prv.Params().BitSize/8)
defer zeroBytes(seckey)
return secp256k1.Sign(digestHash, seckey)
}
+21 -15
View File
@@ -22,18 +22,24 @@ import (
"reflect"
"testing"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/common/hexutil"
"github.com/luxfi/geth/common/math"
"github.com/luxfi/crypto/utils"
)
var (
testmsg = hexutil.MustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
testsig = hexutil.MustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
testpubkey = hexutil.MustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
testpubkeyc = hexutil.MustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
testmsg = mustDecode("0xce0677bb30baa8cf067c88db9811f4333d131bf8bcf12fe7065d211dce971008")
testsig = mustDecode("0x90f27b8b488db00b00606796d2987f6a5f59ae62ea05effe84fef5b8b0e549984a691139ad57a3f0b906637673aa2f63d1f55cb1a69199d4009eea23ceaddc9301")
testpubkey = mustDecode("0x04e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652")
testpubkeyc = mustDecode("0x02e32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a")
)
func mustDecode(s string) []byte {
b := utils.FromHex(s)
if b == nil {
panic("invalid hex string")
}
return b
}
func TestEcrecover(t *testing.T) {
pubkey, err := Ecrecover(testmsg, testsig)
if err != nil {
@@ -62,13 +68,13 @@ func TestVerifySignature(t *testing.T) {
if VerifySignature(testpubkey, testmsg, nil) {
t.Errorf("nil signature valid")
}
if VerifySignature(testpubkey, testmsg, append(common.CopyBytes(sig), 1, 2, 3)) {
if VerifySignature(testpubkey, testmsg, append(utils.CopyBytes(sig), 1, 2, 3)) {
t.Errorf("signature valid with extra bytes at the end")
}
if VerifySignature(testpubkey, testmsg, sig[:len(sig)-2]) {
t.Errorf("signature valid even though it's incomplete")
}
wrongkey := common.CopyBytes(testpubkey)
wrongkey := utils.CopyBytes(testpubkey)
wrongkey[10]++
if VerifySignature(wrongkey, testmsg, sig) {
t.Errorf("signature valid with wrong public key")
@@ -77,9 +83,9 @@ func TestVerifySignature(t *testing.T) {
// This test checks that VerifySignature rejects malleable signatures with s > N/2.
func TestVerifySignatureMalleable(t *testing.T) {
sig := hexutil.MustDecode("0x638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454")
key := hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
msg := hexutil.MustDecode("0xd301ce462d3e639518f482c7f03821fec1e602018630ce621e1e7851c12343a6")
sig := mustDecode("0x638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454")
key := mustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138")
msg := mustDecode("0xd301ce462d3e639518f482c7f03821fec1e602018630ce621e1e7851c12343a6")
if VerifySignature(key, msg, sig) {
t.Error("VerifySignature returned true for malleable signature")
}
@@ -99,7 +105,7 @@ func TestDecompressPubkey(t *testing.T) {
if _, err := DecompressPubkey(testpubkeyc[:5]); err == nil {
t.Errorf("no error for incomplete pubkey")
}
if _, err := DecompressPubkey(append(common.CopyBytes(testpubkeyc), 1, 2, 3)); err == nil {
if _, err := DecompressPubkey(append(utils.CopyBytes(testpubkeyc), 1, 2, 3)); err == nil {
t.Errorf("no error for pubkey with extra bytes at the end")
}
}
@@ -107,8 +113,8 @@ func TestDecompressPubkey(t *testing.T) {
func TestCompressPubkey(t *testing.T) {
key := &ecdsa.PublicKey{
Curve: S256(),
X: math.MustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
Y: math.MustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
X: utils.MustParseBig256("0xe32df42865e97135acfb65f3bae71bdc86f4d49150ad6a440b6f15878109880a"),
Y: utils.MustParseBig256("0x0a2b2667f7e725ceea70c673093bf67663e0312623c8e091b13cf2c0f11ef652"),
}
compressed := CompressPubkey(key)
if !bytes.Equal(compressed, testpubkeyc) {
+111
View File
@@ -0,0 +1,111 @@
// Copyright (C) 2020-2025, Lux Industries Inc
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"encoding/hex"
"encoding/json"
"fmt"
"reflect"
)
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
func MustDecode(input string) []byte {
dec, err := Decode(input)
if err != nil {
panic(err)
}
return dec
}
// Decode decodes a hex string with 0x prefix.
func Decode(input string) ([]byte, error) {
if len(input) == 0 {
return nil, nil
}
if !has0xPrefix(input) {
return nil, fmt.Errorf("hex string without 0x prefix")
}
b, err := hex.DecodeString(input[2:])
if err != nil {
return nil, err
}
return b, nil
}
// has0xPrefix validates str begins with '0x' or '0X'.
func has0xPrefix(str string) bool {
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
}
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
// The empty slice marshals as "0x".
type Bytes []byte
// MarshalText implements encoding.TextMarshaler
func (b Bytes) MarshalText() ([]byte, error) {
result := make([]byte, len(b)*2+2)
copy(result, `0x`)
hex.Encode(result[2:], b)
return result, nil
}
// UnmarshalFixedJSON decodes the input as a string with 0x prefix and required length. The output
// given is assumed to be large enough.
func UnmarshalFixedJSON(typ reflect.Type, input, out []byte) error {
if !isString(input) {
return fmt.Errorf("fixed bytes type %v: input must be a JSON string", typ)
}
return wrapTypeError(decodeFixedHex(typ, input[1:len(input)-1], out), typ)
}
func isString(input []byte) bool {
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
}
func wrapTypeError(err error, typ reflect.Type) error {
if _, ok := err.(*decError); ok {
return &json.UnmarshalTypeError{Value: err.Error(), Type: typ}
}
return err
}
type decError struct{ msg string }
func (err decError) Error() string { return err.msg }
func decodeFixedHex(typ reflect.Type, input, out []byte) error {
if len(input) == 0 {
return nil
}
if !has0xPrefix(string(input)) {
return &decError{msg: "missing 0x prefix"}
}
input = input[2:]
wantLen := len(out) * 2
if len(input) == 0 {
return &decError{fmt.Sprintf("empty hex string, want %d hex digits", wantLen)}
}
if len(input) != wantLen {
return &decError{fmt.Sprintf("got %d hex digits, want %d", len(input), wantLen)}
}
// Decode hex string into out
if _, err := hex.Decode(out, input); err != nil {
return &decError{err.Error()}
}
return nil
}
+61
View File
@@ -0,0 +1,61 @@
// Copyright (C) 2020-2025, Lux Industries Inc
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"fmt"
"math/big"
)
// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length
// of the slice is at least n bytes.
func PaddedBigBytes(bigint *big.Int, n int) []byte {
if bigint.BitLen()/8 >= n {
return bigint.Bytes()
}
ret := make([]byte, n)
return bigint.FillBytes(ret)
}
// MustParseBig256 parses a hex or decimal string as a quantity of at most
// 256 bits. The result has 256 bits (32 bytes). Leading zeros are kept as required.
func MustParseBig256(s string) *big.Int {
v, ok := ParseBig256(s)
if !ok {
panic(fmt.Sprintf("invalid 256 bit integer: %s", s))
}
return v
}
// ParseBig256 parses a hex or decimal string as a quantity of at most
// 256 bits. The result has 256 bits (32 bytes). Leading zeros are kept as required.
func ParseBig256(s string) (*big.Int, bool) {
if s == "" {
return nil, false
}
var bigint *big.Int
var ok bool
if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
bigint, ok = new(big.Int).SetString(s[2:], 16)
} else {
bigint, ok = new(big.Int).SetString(s, 10)
}
if !ok || bigint.BitLen() > 256 {
return nil, false
}
return bigint, true
}
+106
View File
@@ -0,0 +1,106 @@
// Copyright (C) 2020-2025, Lux Industries Inc
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"bytes"
"encoding/binary"
)
// EncodeToBytes encodes the given values to RLP.
// This is a minimal implementation specifically for CreateAddress.
func EncodeToBytes(val interface{}) ([]byte, error) {
var buf bytes.Buffer
if err := encode(&buf, val); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func encode(buf *bytes.Buffer, val interface{}) error {
switch v := val.(type) {
case []interface{}:
return encodeList(buf, v)
case Address:
return encodeBytes(buf, v[:])
case uint64:
return encodeUint64(buf, v)
default:
return nil
}
}
func encodeBytes(buf *bytes.Buffer, b []byte) error {
if len(b) == 1 && b[0] <= 0x7F {
buf.WriteByte(b[0])
} else if len(b) <= 55 {
buf.WriteByte(byte(0x80 + len(b)))
buf.Write(b)
} else {
lenBytes := intToBytes(uint64(len(b)))
buf.WriteByte(byte(0xB7 + len(lenBytes)))
buf.Write(lenBytes)
buf.Write(b)
}
return nil
}
func encodeUint64(buf *bytes.Buffer, i uint64) error {
if i == 0 {
buf.WriteByte(0x80)
} else if i < 128 {
buf.WriteByte(byte(i))
} else {
b := intToBytes(i)
return encodeBytes(buf, b)
}
return nil
}
func encodeList(buf *bytes.Buffer, list []interface{}) error {
var contentBuf bytes.Buffer
for _, elem := range list {
if err := encode(&contentBuf, elem); err != nil {
return err
}
}
content := contentBuf.Bytes()
if len(content) <= 55 {
buf.WriteByte(byte(0xC0 + len(content)))
buf.Write(content)
} else {
lenBytes := intToBytes(uint64(len(content)))
buf.WriteByte(byte(0xF7 + len(lenBytes)))
buf.Write(lenBytes)
buf.Write(content)
}
return nil
}
func intToBytes(i uint64) []byte {
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], i)
// Find first non-zero byte
for idx := 0; idx < 8; idx++ {
if buf[idx] != 0 {
return buf[idx:]
}
}
return nil
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright (C) 2020-2025, Lux Industries Inc
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package utils
import (
"encoding/hex"
"fmt"
"math/big"
)
const (
// HashLength is the expected length of the hash
HashLength = 32
// AddressLength is the expected length of the address
AddressLength = 20
)
var (
// Big0 is 0 represented as a big.Int
Big0 = big.NewInt(0)
// Big1 is 1 represented as a big.Int
Big1 = big.NewInt(1)
)
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
type Hash [HashLength]byte
// Address represents the 20 byte address of an Ethereum account.
type Address [AddressLength]byte
// Bytes gets the byte slice representation of the address.
func (a Address) Bytes() []byte { return a[:] }
// String implements fmt.Stringer.
func (a Address) String() string {
return "0x" + hex.EncodeToString(a[:])
}
// BytesToAddress returns Address with value b.
// If b is larger than len(h), b will be cropped from the left.
func BytesToAddress(b []byte) Address {
var a Address
a.SetBytes(b)
return a
}
// SetBytes sets the address to the value of b.
// If b is larger than len(a), b will be cropped from the left.
func (a *Address) SetBytes(b []byte) {
if len(b) > len(a) {
b = b[len(b)-AddressLength:]
}
copy(a[AddressLength-len(b):], b)
}
// HexToAddress returns Address with byte values of s.
// If s is larger than len(h), s will be cropped from the left.
func HexToAddress(s string) Address {
// Remove 0x prefix if present
if len(s) >= 2 && s[0:2] == "0x" {
s = s[2:]
}
b, err := hex.DecodeString(s)
if err != nil {
panic(fmt.Sprintf("invalid hex string: %v", err))
}
return BytesToAddress(b)
}
// CopyBytes returns an exact copy of the provided bytes.
func CopyBytes(b []byte) (copiedBytes []byte) {
if b == nil {
return nil
}
copiedBytes = make([]byte, len(b))
copy(copiedBytes, b)
return
}
// FromHex returns the bytes represented by the hexadecimal string s.
// s may be prefixed with "0x".
func FromHex(s string) []byte {
if len(s) > 1 {
if s[0:2] == "0x" || s[0:2] == "0X" {
s = s[2:]
}
}
if len(s)%2 == 1 {
s = "0" + s
}
b, err := hex.DecodeString(s)
if err != nil {
panic(fmt.Sprintf("invalid hex string: %v", err))
}
return b
}