From 1abfdec91251d4d1d564dc929ea5909e1a7aeb5d Mon Sep 17 00:00:00 2001 From: Peter Kieltyka Date: Mon, 16 Dec 2024 20:32:18 -0500 Subject: [PATCH] add wallet#SignTypedData and IsValidTypedDataSignature (#181) --- go.mod | 8 +++--- go.sum | 16 ++++++------ go.work.sum | 5 +++- signature.go | 15 +++++++++++ signature_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++ wallet.go | 12 +++++++++ 6 files changed, 107 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index b36c29e..61e90d5 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,8 @@ toolchain go1.23.1 // replace github.com/0xsequence/ethkit => /Users/peter/Dev/0xsequence/ethkit require ( - github.com/0xsequence/ethkit v1.30.1 - github.com/0xsequence/go-ethauth v0.13.0 + github.com/0xsequence/ethkit v1.30.2 + github.com/0xsequence/go-ethauth v0.14.0 github.com/BurntSushi/toml v1.2.1 github.com/davecgh/go-spew v1.1.1 github.com/gibson042/canonicaljson-go v1.0.3 @@ -29,7 +29,7 @@ require ( github.com/consensys/bavard v0.1.24 // indirect github.com/consensys/gnark-crypto v0.14.0 // indirect github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/deckarep/golang-set/v2 v2.7.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/ethereum/c-kzg-4844/bindings/go v0.0.0-20230126171313-363c7d7593b4 // indirect @@ -50,7 +50,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/net v0.29.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index 8e70ba3..c1ab00d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -github.com/0xsequence/ethkit v1.30.1 h1:i0jYd7OP8f30reAyIK9e0YJ62eH56JzYCeSTKVi5oWg= -github.com/0xsequence/ethkit v1.30.1/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk= -github.com/0xsequence/go-ethauth v0.13.0 h1:ZaqFEEqy574A2b1P7vjpcy5tb4W/izn+A3swwOYi9wA= -github.com/0xsequence/go-ethauth v0.13.0/go.mod h1:f3kx39S9F+W+qvZEB6bkKKbpUstmyB7goUntO3wvlhg= +github.com/0xsequence/ethkit v1.30.2 h1:TZCxXF+5kjJWE8+CKQGQkDm/coeLL7uwYuLiHiJ4iuM= +github.com/0xsequence/ethkit v1.30.2/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk= +github.com/0xsequence/go-ethauth v0.14.0 h1:0TQKu/bFpTZ0hNtedqfHuRn/fa2wI8qlLJE0rDgJ0Dw= +github.com/0xsequence/go-ethauth v0.14.0/go.mod h1:AX6zc0LNxzRHXW+i+IPyuLLgj+AdLl7KjLNRXB/10r0= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -54,8 +54,8 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= +github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= @@ -170,8 +170,8 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/go.work.sum b/go.work.sum index 3e59717..f71bbe2 100644 --- a/go.work.sum +++ b/go.work.sum @@ -40,6 +40,8 @@ github.com/0xsequence/ethkit v1.27.11 h1:fWcABQx1WIH4SWvesCuKBJ+D4XdwZcIGWGSYnBC github.com/0xsequence/ethkit v1.27.11/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk= github.com/0xsequence/ethkit v1.29.5 h1:piN0JAsMSvQA+AnlA7g+MXD+F+ggNqY+Po9UbENvfsc= github.com/0xsequence/ethkit v1.29.5/go.mod h1:rv0FAIyEyN0hhwGefbduAz4ujmyjyJXhCd6a0/yF3tk= +github.com/0xsequence/go-ethauth v0.13.0 h1:ZaqFEEqy574A2b1P7vjpcy5tb4W/izn+A3swwOYi9wA= +github.com/0xsequence/go-ethauth v0.13.0/go.mod h1:f3kx39S9F+W+qvZEB6bkKKbpUstmyB7goUntO3wvlhg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -225,6 +227,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -291,6 +294,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -353,7 +357,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/signature.go b/signature.go index 2579a69..f4837c5 100644 --- a/signature.go +++ b/signature.go @@ -172,6 +172,21 @@ func IsValidMessageSignature(address common.Address, message []byte, signature [ return IsValidSignature(log, address, common.BytesToHash(accounts.TextHash(message)), signature, SequenceContexts(), chainID, provider) } +func IsValidTypedDataSignature(address common.Address, encodedTypedData []byte, signature []byte, chainID *big.Int, provider *ethrpc.Provider, optLogger *logger.Logger) (bool, error) { + log := logger.Nop() + if optLogger != nil { + log = *optLogger + } + + isValid, err := ethwallet.IsValid191Signature(address, encodedTypedData, signature) + if err == nil && isValid { + return true, nil + } + + typedDataDigest := common.BytesToHash(ethcoder.Keccak256(encodedTypedData)) + return IsValidSignature(log, address, typedDataDigest, signature, SequenceContexts(), chainID, provider) +} + func IsValidSignature(log logger.Logger, walletAddress common.Address, digest common.Hash, seqSig []byte, walletContexts WalletContexts, chainID *big.Int, provider *ethrpc.Provider) (bool, error) { eip6492isValid, _ := eip6492.ValidateEIP6492Offchain(context.Background(), provider, walletAddress, digest, seqSig, nil) if eip6492isValid { diff --git a/signature_test.go b/signature_test.go index c68c8d0..7a8e46e 100644 --- a/signature_test.go +++ b/signature_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/0xsequence/ethkit/ethcoder" "github.com/0xsequence/ethkit/ethrpc" "github.com/0xsequence/ethkit/ethwallet" "github.com/0xsequence/ethkit/go-ethereum/accounts" @@ -64,6 +65,69 @@ func TestIsValidMessageSignatureSequence(t *testing.T) { assert.True(t, isValid) } +func TestIsValidSignatureEIP712Sequence(t *testing.T) { + eoa, err := ethwallet.NewWalletFromRandomEntropy() + assert.NoError(t, err) + wallet, err := sequence.NewWalletSingleOwner(eoa) + assert.NoError(t, err) + + provider, err := ethrpc.NewProvider(rpcURLEthereum) + assert.NoError(t, err) + + err = wallet.Connect(provider, nil) + assert.NoError(t, err) + + typedDataJson := `{ + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" }, + { "name": "salt", "type": "bytes32" } + ], + "ExampleMessage": [ + { "name": "message", "type": "string" }, + { "name": "value", "type": "uint256" }, + { "name": "from", "type": "address" }, + { "name": "to", "type": "address" } + ] + }, + "domain": { + "name": "EIP712Example", + "version": "1", + "chainId": 5, + "verifyingContract": "0xc0ffee254729296a45a3885639AC7E10F9d54979", + "salt": "0x70736575646f2d72616e646f6d2076616c756500000000000000000000000000" + }, + "message": { + "message": "Test message", + "value": 10000, + "from": "0xc0ffee254729296a45a3885639AC7E10F9d54979", + "to": "0xc0ffee254729296a45a3885639AC7E10F9d54979" + } + }` + + typedData, err := ethcoder.TypedDataFromJSON(typedDataJson) + require.NoError(t, err) + + // domainHash, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) + // require.NoError(t, err) + // require.Equal(t, "0xe073fe030277efdf89d39322ac9321f17774fce4f686e14ff161942bbae5fdcd", ethcoder.HexEncode(domainHash)) + + sig, encodedTypedData, err := wallet.SignTypedData(typedData) + require.NoError(t, err) + require.NotNil(t, sig) + require.NotNil(t, encodedTypedData) + + signature, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig()) + assert.NoError(t, err) + + isValid, err := sequence.IsValidTypedDataSignature(wallet.Address(), encodedTypedData, signature, big.NewInt(1), provider, nil) + assert.NoError(t, err) + assert.True(t, isValid) +} + func TestIsValideMessageSignatureSequence_EIP6492SignatureWithMultipleDeployments(t *testing.T) { message := "hello world!" diff --git a/wallet.go b/wallet.go index f144ffd..d715c1c 100644 --- a/wallet.go +++ b/wallet.go @@ -435,6 +435,18 @@ func (w *Wallet[C]) SignMessage(msg []byte) ([]byte, error) { return w.SignDigest(context.Background(), MessageDigest(msg)) } +func (w *Wallet[C]) SignTypedData(typedData *ethcoder.TypedData) ([]byte, []byte, error) { + digest, encodedTypedData, err := typedData.Encode() + if err != nil { + return nil, nil, err + } + signature, err := w.SignDigest(context.Background(), common.Hash(digest)) + if err != nil { + return nil, nil, err + } + return signature, encodedTypedData, nil +} + var _ MessageSigner = (*Wallet[*v1.WalletConfig])(nil) var _ MessageSigner = (*Wallet[*v2.WalletConfig])(nil)