From f580294d69fff46a0fc8e7fa07a41020cd57ca80 Mon Sep 17 00:00:00 2001 From: Andrew Richardson Date: Thu, 21 Sep 2023 17:28:31 +0100 Subject: [PATCH 01/28] First pass at adding typed data signing This is very incomplete - just a start based on reading of EIP-712. Signed-off-by: Andrew Richardson --- pkg/ethsigner/typed_data.go | 172 ++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 pkg/ethsigner/typed_data.go diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go new file mode 100644 index 00000000..79d5d911 --- /dev/null +++ b/pkg/ethsigner/typed_data.go @@ -0,0 +1,172 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ethsigner + +import ( + "fmt" + "math/big" + "strings" + + "golang.org/x/crypto/sha3" +) + +type TypeMember struct { + Name string + Type string +} + +type Type struct { + Name string + Members []*TypeMember +} + +var EIP712DomainType = Type{ + Name: "EIP712Domain", + Members: []*TypeMember{ + { + Name: "name", + Type: "string", + }, + { + Name: "version", + Type: "string", + }, + { + Name: "chainId", + Type: "uint256", + }, + { + Name: "verifyingContract", + Type: "address", + }, + { + Name: "salt", + Type: "bytes32", + }, + }, +} + +func (t *TypeMember) Encode() string { + return t.Type + " " + t.Name +} + +func (t *Type) Encode() string { + params := make([]string, len(t.Members)) + for i, member := range t.Members { + params[i] = member.Encode() + } + return t.Name + "(" + strings.Join(params, ",") + ")" +} + +func (t *Type) Hash() []byte { + hash := sha3.NewLegacyKeccak256() + hash.Write([]byte(t.Encode())) + return hash.Sum(nil) +} + +func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { + var result []byte + for _, member := range members { + val := data[member.Name] + encodedVal, err := encodeValue(member, val) + if err != nil { + return nil, err + } + result = append(result, encodedVal...) + } + return result, nil +} + +func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { + switch m.Type { + case "bool": + return encodeBool(val.(bool)), nil + case "address": + return encodeAddress(val.(string)), nil + case "uint256": + return encodeUintString(val.(string)), nil + case "string": + return encodeString(val.(string)), nil + default: + return nil, fmt.Errorf("unsupported type: %s", m.Type) + } +} + +func encodeBool(val bool) []byte { + var i *big.Int + if val { + i = big.NewInt(1) + } else { + i = big.NewInt(0) + } + return encodeUint(i) +} + +func encodeAddress(val string) []byte { + i := big.NewInt(0) + i.SetString(val, 16) + return encodeUint(i) +} + +func encodeUint(val *big.Int) []byte { + data := make([]byte, 32) + _ = val.FillBytes(data) + return data +} + +func encodeUintString(val string) []byte { + i := big.NewInt(0) + i.SetString(val, 10) + return encodeUint(i) +} + +func encodeString(val string) []byte { + return encodeDynamicBytes([]byte(val)) +} + +func encodeDynamicBytes(val []byte) []byte { + hash := sha3.NewLegacyKeccak256() + hash.Write(val) + return hash.Sum(nil) +} + +func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { + encodedData, err := encodeData(t.Members, data) + if err != nil { + return nil, err + } + hash := sha3.NewLegacyKeccak256() + hash.Write(t.Hash()) + hash.Write(encodedData) + return hash.Sum(nil), nil +} + +func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { + domainSeparator, err := hashStruct(&EIP712DomainType, domain) + if err != nil { + return nil, err + } + messageHash, err := hashStruct(t, message) + if err != nil { + return nil, err + } + hash := sha3.NewLegacyKeccak256() + hash.Write([]byte{0x19, 0x01}) + hash.Write(domainSeparator) + hash.Write(messageHash) + return hash.Sum(nil), nil +} From 9e233c26c275ecdfdd1b2c48afffe4dceceddd8d Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 22 Oct 2023 14:39:27 -0400 Subject: [PATCH 02/28] Work to integrate EIP-712 code with ABI code (wip) Signed-off-by: Peter Broadhurst --- .golangci.yml | 1 - internal/signermsgs/en_error_messges.go | 7 + pkg/abi/abi_test.go | 5 + pkg/abi/inputparsing.go | 15 +- pkg/abi/inputparsing_test.go | 36 ++++ pkg/abi/typecomponents.go | 63 ++++-- pkg/eip712/abi_to_eip712.go | 247 ++++++++++++++++++++++++ pkg/eip712/abi_to_eip712_test.go | 81 ++++++++ pkg/eip712/typed_data.go | 231 ++++++++++++++++++++++ pkg/ethsigner/typed_data.go | 172 ----------------- 10 files changed, 671 insertions(+), 187 deletions(-) create mode 100644 pkg/eip712/abi_to_eip712.go create mode 100644 pkg/eip712/abi_to_eip712_test.go create mode 100644 pkg/eip712/typed_data.go delete mode 100644 pkg/ethsigner/typed_data.go diff --git a/.golangci.yml b/.golangci.yml index 6c6e82f6..4d2b3a35 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -33,7 +33,6 @@ linters: enable: - bodyclose - deadcode - - depguard - dogsled - errcheck - goconst diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index 1443174e..c55beb2f 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -82,4 +82,11 @@ var ( MsgRequestCanceledContext = ffe("FF22063", "Request with id %s failed due to canceled context") MsgInvalidSigner = ffe("FF22064", "Invalid signer") MsgResultParseFailed = ffe("FF22065", "Failed to parse result (expected=%T): %s") + MsgEIP712UnknownABICompType = ffe("FF22066", "Unknown ABI component type: %s") + MsgEIP712NotElementary = ffe("FF22067", "Not elementary type: %s") + MsgEIP712UnsupportedStrType = ffe("FF22068", "Unsupported type: %s") + MsgEIP712UnsupportedABIType = ffe("FF22069", "ABI type not supported by EIP-712 encoding: %s") + MsgNotABIElementaryType = ffe("FF22070", "Not elementary type: %s") + MsgEIP712PrimaryNotTuple = ffe("FF22071", "Type primary type must be a struct: %s") + MsgEIP712BadInternalType = ffe("FF22072", "Failed to extract struct name from ABI internalType '%s'") ) diff --git a/pkg/abi/abi_test.go b/pkg/abi/abi_test.go index d03506ac..bf04dd3f 100644 --- a/pkg/abi/abi_test.go +++ b/pkg/abi/abi_test.go @@ -296,11 +296,16 @@ func TestABIGetTupleTypeTree(t *testing.T) { assert.Equal(t, TupleComponent, tc.ComponentType()) assert.Len(t, tc.TupleChildren(), 3) assert.Equal(t, "(uint256,string[2],bytes)", tc.String()) + assert.False(t, tc.ElementaryFixed()) // not fixed, as not elementary assert.Equal(t, ElementaryComponent, tc.TupleChildren()[0].ComponentType()) assert.Equal(t, ElementaryTypeUint, tc.TupleChildren()[0].ElementaryType()) + assert.Equal(t, "256", tc.TupleChildren()[0].ElementarySuffix()) // alias resolved + assert.True(t, tc.TupleChildren()[0].ElementaryFixed()) assert.Equal(t, FixedArrayComponent, tc.TupleChildren()[1].ComponentType()) + assert.Equal(t, 2, tc.TupleChildren()[1].FixedArrayLen()) + assert.Equal(t, BaseTypeString, tc.TupleChildren()[1].ArrayChild().ElementaryType().BaseType()) assert.Equal(t, ElementaryComponent, tc.TupleChildren()[1].ArrayChild().ComponentType()) assert.Equal(t, ElementaryTypeString, tc.TupleChildren()[1].ArrayChild().ElementaryType()) diff --git a/pkg/abi/inputparsing.go b/pkg/abi/inputparsing.go index 4b3c2a26..cc146720 100644 --- a/pkg/abi/inputparsing.go +++ b/pkg/abi/inputparsing.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2023 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -49,6 +49,19 @@ func (cv *ComponentValue) JSON() ([]byte, error) { return NewSerializer().SerializeJSON(cv) } +func (cv *ComponentValue) ElementaryABIData() ([]byte, bool, error) { + return cv.ElementaryABIDataCtx(context.Background()) +} + +func (cv *ComponentValue) ElementaryABIDataCtx(ctx context.Context) (data []byte, dynamic bool, err error) { + c := cv.Component + et := cv.Component.ElementaryType().(*elementaryTypeInfo) + if et == nil { + return nil, false, i18n.NewError(ctx, signermsgs.MsgNotABIElementaryType, c.String()) + } + return et.encodeABIData(ctx, c.String(), c.(*typeComponent), cv.Value) +} + // getPtrValOrRawTypeNil sees if v is a pointer, with a non-nil value. If so returns that value, else nil func getPtrValOrNil(v interface{}) interface{} { val := reflect.ValueOf(v) diff --git a/pkg/abi/inputparsing_test.go b/pkg/abi/inputparsing_test.go index facb4c96..ee6d9c61 100644 --- a/pkg/abi/inputparsing_test.go +++ b/pkg/abi/inputparsing_test.go @@ -21,6 +21,7 @@ import ( "math/big" "testing" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/stretchr/testify/assert" ) @@ -673,3 +674,38 @@ func TestTuplesMissingName(t *testing.T) { _, err = inputs.ParseJSON([]byte(values)) assert.Regexp(t, "FF22039", err) } + +func TestTupleEncodeIndividualFixedParam(t *testing.T) { + const sample = `[ + { + "name": "foo", + "type": "function", + "inputs": [ + { + "name": "a", + "type": "int" + } + ], + "outputs": [] + } + ]` + + inputs := testABI(t, sample)[0].Inputs + + // Fine if you use the array syntax + values := `{ "a": 12345 }` + cv, err := inputs.ParseJSON([]byte(values)) + assert.NoError(t, err) + assert.Equal(t, TupleComponent, cv.Component.ComponentType()) + assert.Len(t, cv.Children, 1) + _, _, err = cv.ElementaryABIData() + assert.Regexp(t, "FF22070", err) + + intComp := cv.Children[0] + assert.Equal(t, ElementaryComponent, intComp.Component.ComponentType()) + b, dynamic, err := intComp.ElementaryABIData() + assert.NoError(t, err) + assert.False(t, dynamic) + assert.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000003039", ethtypes.HexBytes0xPrefix(b).String()) + +} diff --git a/pkg/abi/typecomponents.go b/pkg/abi/typecomponents.go index d317e15a..4ad192ee 100644 --- a/pkg/abi/typecomponents.go +++ b/pkg/abi/typecomponents.go @@ -47,7 +47,10 @@ type TypeComponent interface { String() string // gives the signature for this type level of the type component hierarchy ComponentType() ComponentType // classification of the component type (tuple, array or elemental) ElementaryType() ElementaryTypeInfo // only non-nil for elementary components + ElementarySuffix() string // only on elementary types with a suffix - expands "aliases" (so "uint" would have "256") + ElementaryFixed() bool // whether the elementary type if fixed ArrayChild() TypeComponent // only non-nil for array components + FixedArrayLen() int // only for fixed-array components TupleChildren() []TypeComponent // only non-nil for tuple components KeyName() string // the name of the ABI property/component, only set for top-level parameters and tuple entries Parameter() *Parameter // the ABI property/component, only set for top-level parameters and tuple entries @@ -73,7 +76,7 @@ type typeComponent struct { // elementaryTypeInfo defines the string parsing rules, as well as a pointer to the functions for // serialization to a set of bytes, and back again type elementaryTypeInfo struct { - name string // The name of the type - the alphabetic characters up to an optional suffix + name BaseTypeName // The name of the type - the alphabetic characters up to an optional suffix suffixType suffixType // Whether there is a length suffix, and its type defaultSuffix string // If set and there is no suffix supplied, the following suffix is used defaultM uint16 // If the type implicitly has an M value that is not expressed (such as "function") @@ -114,7 +117,7 @@ func (et *elementaryTypeInfo) String() string { } return s default: - return et.name + return string(et.name) } } @@ -122,7 +125,11 @@ func (et *elementaryTypeInfo) JSONEncodingType() JSONEncodingType { return et.jsonEncodingType } -var elementaryTypes = map[string]*elementaryTypeInfo{} +func (et *elementaryTypeInfo) BaseType() BaseTypeName { + return et.name +} + +var elementaryTypes = map[BaseTypeName]*elementaryTypeInfo{} func registerElementaryType(et elementaryTypeInfo) ElementaryTypeInfo { elementaryTypes[et.name] = &et @@ -133,6 +140,7 @@ func registerElementaryType(et elementaryTypeInfo) ElementaryTypeInfo { // You can do an equality check against the appropriate constant, to check if this is the type you are expecting. // e.g. type ElementaryTypeInfo interface { + BaseType() BaseTypeName // const for each of the elementary types String() string // gives a summary of the rules the elemental type (used in error reporting) JSONEncodingType() JSONEncodingType // categorizes JSON input/output type to one of a small number of options } @@ -147,6 +155,20 @@ const ( JSONEncodingTypeString // JSON string containing any unicode characters ) +type BaseTypeName string + +const ( + BaseTypeInt BaseTypeName = "int" + BaseTypeUInt BaseTypeName = "uint" + BaseTypeAddress BaseTypeName = "address" + BaseTypeBool BaseTypeName = "bool" + BaseTypeFixed BaseTypeName = "fixed" + BaseTypeUFixed BaseTypeName = "ufixed" + BaseTypeBytes BaseTypeName = "bytes" + BaseTypeFunction BaseTypeName = "function" + BaseTypeString BaseTypeName = "string" +) + // tupleTypeString appears in the same place in the ABI as elementary type strings, but it is not an elementary type. // We treat it separately. const tupleTypeString = "tuple" @@ -155,7 +177,7 @@ var alwaysFixed = func(tc *typeComponent) bool { return false } var ( ElementaryTypeInt = registerElementaryType(elementaryTypeInfo{ - name: "int", + name: BaseTypeInt, suffixType: suffixTypeMRequired, defaultSuffix: "256", mMin: 8, @@ -171,7 +193,7 @@ var ( decodeABIData: decodeABISignedInt, }) ElementaryTypeUint = registerElementaryType(elementaryTypeInfo{ - name: "uint", + name: BaseTypeUInt, suffixType: suffixTypeMRequired, defaultSuffix: "256", mMin: 8, @@ -187,7 +209,7 @@ var ( decodeABIData: decodeABIUnsignedInt, }) ElementaryTypeAddress = registerElementaryType(elementaryTypeInfo{ - name: "address", + name: BaseTypeAddress, suffixType: suffixTypeNone, defaultM: 160, // encoded as "uint160" fixed32: true, @@ -200,7 +222,7 @@ var ( decodeABIData: decodeABIUnsignedInt, }) ElementaryTypeBool = registerElementaryType(elementaryTypeInfo{ - name: "bool", + name: BaseTypeBool, suffixType: suffixTypeNone, defaultM: 8, // encoded as "uint8" fixed32: true, @@ -213,7 +235,7 @@ var ( decodeABIData: decodeABIUnsignedInt, }) ElementaryTypeFixed = registerElementaryType(elementaryTypeInfo{ - name: "fixed", + name: BaseTypeFixed, suffixType: suffixTypeMxNRequired, defaultSuffix: "128x18", mMin: 8, @@ -231,7 +253,7 @@ var ( decodeABIData: decodeABISignedFloat, }) ElementaryTypeUfixed = registerElementaryType(elementaryTypeInfo{ - name: "ufixed", + name: BaseTypeUFixed, suffixType: suffixTypeMxNRequired, defaultSuffix: "128x18", mMin: 8, @@ -249,7 +271,7 @@ var ( decodeABIData: decodeABIUnsignedFloat, }) ElementaryTypeBytes = registerElementaryType(elementaryTypeInfo{ - name: "bytes", + name: BaseTypeBytes, suffixType: suffixTypeMOptional, // note that "bytes" without a suffix is a special dynamic sized byte sequence mMin: 1, mMax: 32, @@ -263,7 +285,7 @@ var ( decodeABIData: decodeABIBytes, }) ElementaryTypeFunction = registerElementaryType(elementaryTypeInfo{ - name: "function", + name: BaseTypeFunction, suffixType: suffixTypeNone, defaultM: 24, // encoded as "bytes24" fixed32: true, @@ -276,7 +298,7 @@ var ( decodeABIData: decodeABIBytes, }) ElementaryTypeString = registerElementaryType(elementaryTypeInfo{ - name: "string", + name: BaseTypeString, suffixType: suffixTypeNone, fixed32: false, dynamic: func(tc *typeComponent) bool { return true }, @@ -339,6 +361,17 @@ func (tc *typeComponent) ElementaryType() ElementaryTypeInfo { return tc.elementaryType } +func (tc *typeComponent) ElementarySuffix() string { + return tc.elementarySuffix +} + +func (tc *typeComponent) ElementaryFixed() bool { + if tc.elementaryType != nil { + return !tc.elementaryType.dynamic(tc) + } + return false +} + func (tc *typeComponent) KeyName() string { return tc.keyName } @@ -347,6 +380,10 @@ func (tc *typeComponent) ArrayChild() TypeComponent { return tc.arrayChild } +func (tc *typeComponent) FixedArrayLen() int { + return tc.arrayLength +} + func (tc *typeComponent) TupleChildren() []TypeComponent { children := make([]TypeComponent, len(tc.tupleChildren)) for i, c := range tc.tupleChildren { @@ -414,7 +451,7 @@ func (p *Parameter) parseABIParameterComponents(ctx context.Context) (tc *typeCo } } } else { - et, ok := elementaryTypes[etStr] + et, ok := elementaryTypes[BaseTypeName(etStr)] if !ok { return nil, i18n.NewError(ctx, signermsgs.MsgUnsupportedABIType, etStr, abiTypeString) } diff --git a/pkg/eip712/abi_to_eip712.go b/pkg/eip712/abi_to_eip712.go new file mode 100644 index 00000000..e5bd0963 --- /dev/null +++ b/pkg/eip712/abi_to_eip712.go @@ -0,0 +1,247 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +import ( + "bytes" + "context" + "fmt" + "regexp" + + "github.com/hyperledger/firefly-common/pkg/i18n" + "github.com/hyperledger/firefly-signer/internal/signermsgs" + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" + "golang.org/x/crypto/sha3" +) + +var internalTypeStructExtractor = regexp.MustCompile(`^struct (.*\.)?([^.]+)$`) + +// EIP-712 hashStruct() implementation using abi.ComponentValue as an input definition +// +// This file takes ComponentValue trees from the ABI encoding package, +// and traverses them into EIP-712 encodable structures according to +// the rules laid out in: +// +// https://eips.ethereum.org/EIPS/eip-712 +func HashStructABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { + encodedType, err := EncodeTypeABI(ctx, v.Component) + if err != nil { + return nil, err + } + + encodedData, err := EncodeDataABI(ctx, v) + if err != nil { + return nil, err + } + + // typeHash = keccak256(encodeType(typeOf(s))) + typeHash := hashString(encodedType) + /// hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s)) + hash := sha3.NewLegacyKeccak256() + hash.Write(typeHash) + hash.Write([]byte(encodedData)) + return hash.Sum(nil), nil +} + +func hashString(s string) []byte { + hash := sha3.NewLegacyKeccak256() + hash.Write([]byte(s)) + return hash.Sum(nil) +} + +// EIP-712 encodeType() implementation using abi.TypeComponent as an input definition +// +// Builds the map of struct names to types +func EncodeTypeABI(ctx context.Context, tc abi.TypeComponent) (string, error) { + typeSet, err := buildTypeSetABI(ctx, tc) + if err != nil { + return "", err + } + return typeSet.Encode(tc.Parameter().Name), nil +} + +// EIP-712 encodeData() implementation using abi.ComponentValue as an input definition +// +// Recurses into the structure following the rules of EIP-712 to construct the encoded hash. +func EncodeDataABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { + tc := v.Component + switch tc.ComponentType() { + case abi.TupleComponent, abi.DynamicArrayComponent, abi.FixedArrayComponent: + // Concatenate an encoding of each component + buff := new(bytes.Buffer) + for _, child := range v.Children { + childData, err := EncodeDataABI(ctx, child) + if err != nil { + return nil, err + } + buff.Write(childData) + } + return buff.Bytes(), nil + case abi.ElementaryComponent: + return encodeElementaryDataABI(ctx, v) + default: + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712UnknownABICompType, tc.ComponentType()) + } +} + +func buildTypeSetABI(ctx context.Context, tc abi.TypeComponent) (TypeSet, error) { + if tc.ComponentType() != abi.TupleComponent { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712PrimaryNotTuple, tc.String()) + } + // First we need to build the sorted array of types for `encodeType` + typeSet := make(TypeSet) + if err := addABITypes(ctx, tc, typeSet); err != nil { + return nil, err + } + return typeSet, nil +} + +// Maps a parsed ABI component type to an EIP-712 type string +// - Subset of elementary types, with aliases resolved +// - Struct types are simply the name of the type +// - Fixed and dynamic array suffixes are supported +func mapABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { + switch tc.ComponentType() { + case abi.TupleComponent: + return tc.Parameter().Type, nil + case abi.DynamicArrayComponent, abi.FixedArrayComponent: + child, err := mapABIType(ctx, tc.ArrayChild()) + if err != nil { + return "", err + } + if tc.ComponentType() == abi.FixedArrayComponent { + return fmt.Sprintf("%s[%d]", child, tc.FixedArrayLen()), nil + } + return child + "[]", nil + default: + return mapElementaryABIType(ctx, tc) + } +} + +// Maps one of the parsed ABI elementary types to an EIP-712 elementary type +func mapElementaryABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { + et := tc.ElementaryType() + if et == nil { + return "", i18n.NewError(ctx, signermsgs.MsgEIP712NotElementary, tc) + } + switch et.BaseType() { + case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeString: + // Types that need no transposition + return string(et.BaseType()), nil + case abi.BaseTypeInt, abi.BaseTypeUInt: + // Types with supported suffixes - note ABI package sorts alias resolution for us + return string(et.BaseType()) + tc.ElementarySuffix(), nil + case abi.BaseTypeBytes: + // Bytes is special + if tc.ElementaryFixed() { + return string(et.BaseType()) + tc.ElementarySuffix(), nil + } + return string(et.BaseType()), nil + default: + // EIP-712 does not support the other types + return "", i18n.NewError(ctx, signermsgs.MsgEIP712UnsupportedABIType, tc) + } +} + +func encodeElementaryDataABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { + tc := v.Component + et := tc.ElementaryType() + if et == nil { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712NotElementary, tc) + } + switch et.BaseType() { + case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeInt, abi.BaseTypeUInt: + // Types that need no transposition + b, _, err := v.ElementaryABIDataCtx(ctx) + return b, err + case abi.BaseTypeBytes: + if tc.ElementaryFixed() { + // If it's bytes1 -> bytes32 + b, _, err := v.ElementaryABIDataCtx(ctx) + return b, err + } + b, ok := v.Value.([]byte) + if !ok { + return nil, i18n.NewError(ctx, signermsgs.MsgWrongTypeComponentABIEncode, "[]byte", v.Value, v.Component.KeyName()) + } + return b, nil + case abi.BaseTypeString: + s, ok := v.Value.(string) + if !ok { + return nil, i18n.NewError(ctx, signermsgs.MsgWrongTypeComponentABIEncode, "string", v.Value, v.Component.KeyName()) + } + return []byte(s), nil + default: + // EIP-712 does not support the other types + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712UnsupportedABIType, tc) + } +} + +// ABI does not formally contain the Struct name - as it's not required for encoding the value. +// EIP-712 requires the Struct name as it is used through the standard, including to de-dup definitions +// +// Solidity uses the "internalType" field by convention as an extension to ABI, so we require +// that here for EIP-712 encoding to be successful. +func extractSolidityTypeName(ctx context.Context, param *abi.Parameter) (string, error) { + match := internalTypeStructExtractor.FindStringSubmatch(param.InternalType) + if match == nil { + return "", i18n.NewError(ctx, signermsgs.MsgEIP712BadInternalType, param.InternalType) + } + return match[2], nil +} + +// Recursively find all types, with a name -> encoded name map. +func addABITypes(ctx context.Context, tc abi.TypeComponent, typeSet TypeSet) error { + switch tc.ComponentType() { + case abi.TupleComponent: + typeName, err := extractSolidityTypeName(ctx, tc.Parameter()) + if err != nil { + return err + } + + if _, mapped := typeSet[typeName]; mapped { + // we've already mapped this type + return nil + } + t := make(Type, len(tc.TupleChildren())) + for i, child := range tc.TupleChildren() { + ts, err := mapABIType(ctx, child) + if err != nil { + return err + } + t[i] = &TypeMember{ + Name: child.KeyName(), + Type: ts, + } + } + typeSet[typeName] = t + // recurse + for _, child := range tc.TupleChildren() { + if err := addABITypes(ctx, child, typeSet); err != nil { + return err + } + } + return nil + case abi.DynamicArrayComponent, abi.FixedArrayComponent: + // recurse into the child + return addABITypes(ctx, tc.ArrayChild(), typeSet) + default: + // from a type collection perspective, this is a leaf - nothing to do + return nil + } +} diff --git a/pkg/eip712/abi_to_eip712_test.go b/pkg/eip712/abi_to_eip712_test.go new file mode 100644 index 00000000..3413cda3 --- /dev/null +++ b/pkg/eip712/abi_to_eip712_test.go @@ -0,0 +1,81 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +import ( + "context" + "encoding/json" + "testing" + + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/stretchr/testify/assert" +) + +func TestSimpleStruct(t *testing.T) { + + // struct Mail { + // address from; + // address to; + // string contents; + // } + mailABI := []byte(`{ + "components": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "string", + "name": "contents", + "type": "string" + } + ], + "internalType": "struct EIP712Examples.Mail", + "name": "", + "type": "tuple" + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + cv, err := tc.ParseExternal([]interface{}{ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "Hello, Bob!", + }) + assert.NoError(t, err) + + eip712Type, err := EncodeTypeABI(context.Background(), cv.Component) + assert.NoError(t, err) + + assert.Equal(t, "Mail(address from,address to,string contents)", eip712Type) + + encodedData, err := EncodeDataABI(context.Background(), cv) + assert.NoError(t, err) + + assert.Equal(t, "0x12345", encodedData.String()) + +} diff --git a/pkg/eip712/typed_data.go b/pkg/eip712/typed_data.go new file mode 100644 index 00000000..7be08ad3 --- /dev/null +++ b/pkg/eip712/typed_data.go @@ -0,0 +1,231 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +import ( + "sort" + "strings" + + "github.com/hyperledger/firefly-signer/pkg/ethtypes" +) + +type TypeMember struct { + Name string + Type string +} + +type Type []*TypeMember + +type SigningRequest struct { + Types TypeSet `json:"types"` + PrimaryType string `json:"primaryType"` + Domain Domain `json:"domain"` + Message map[string]interface{} `json:"message"` +} + +type TypeSet map[string]Type + +type Domain struct { + Name string `json:"name"` + Version string `json:"version"` + ChainID int64 `json:"chainId"` + VerifyingContract ethtypes.Address0xHex `json:"verifyingContract"` +} + +// A map from type names to types is encoded per encodeType: +// +// > If the struct type references other struct types (and these in turn reference even more struct types), +// > then the set of referenced struct types is collected, sorted by name and appended to the encoding. +func (ts TypeSet) Encode(primaryType string) string { + // Write the primary type first always + buff := new(strings.Builder) + buff.WriteString(ts[primaryType].Encode(primaryType)) + + // Then the reference types sorted by name + referenceTypes := make([]string, 0, len(ts)) + for typeName := range ts { + if typeName != primaryType { + referenceTypes = append(referenceTypes, typeName) + } + } + sort.Strings(referenceTypes) + for _, typeName := range referenceTypes { + buff.WriteString(ts[typeName].Encode(typeName)) + } + return buff.String() +} + +// An individual member is encoded as: +// +// > type ‖ " " ‖ name +func (tm *TypeMember) Encode() string { + return tm.Type + " " + tm.Name +} + +// A type is encoded as: +// +// > name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" +func (t Type) Encode(name string) string { + buff := new(strings.Builder) + buff.WriteString(name) + buff.WriteRune('(') + for i, tm := range t { + if i > 0 { + buff.WriteRune(',') + } + buff.WriteString(tm.Encode()) + } + buff.WriteRune(')') + return buff.String() +} + +// var EIP712DomainType = Type{ +// Name: "EIP712Domain", +// Members: []*TypeMember{ +// { +// Name: "name", +// Type: "string", +// }, +// { +// Name: "version", +// Type: "string", +// }, +// { +// Name: "chainId", +// Type: "uint256", +// }, +// { +// Name: "verifyingContract", +// Type: "address", +// }, +// { +// Name: "salt", +// Type: "bytes32", +// }, +// }, +// } + +// func (t *TypeMember) Encode() string { +// return t.Type + " " + t.Name +// } + +// func (t *Type) Encode() string { +// params := make([]string, len(t.Members)) +// for i, member := range t.Members { +// params[i] = member.Encode() +// } +// return t.Name + "(" + strings.Join(params, ",") + ")" +// } + +// func (t *Type) Hash() []byte { +// hash := sha3.NewLegacyKeccak256() +// hash.Write([]byte(t.Encode())) +// return hash.Sum(nil) +// } + +// func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { +// var result []byte +// for _, member := range members { +// val := data[member.Name] +// encodedVal, err := encodeValue(member, val) +// if err != nil { +// return nil, err +// } +// result = append(result, encodedVal...) +// } +// return result, nil +// } + +// func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { +// switch m.Type { +// case "bool": +// return encodeBool(val.(bool)), nil +// case "address": +// return encodeAddress(val.(string)), nil +// case "uint256": +// return encodeUintString(val.(string)), nil +// case "string": +// return encodeString(val.(string)), nil +// default: +// return nil, fmt.Errorf("unsupported type: %s", m.Type) +// } +// } + +// func encodeBool(val bool) []byte { +// var i *big.Int +// if val { +// i = big.NewInt(1) +// } else { +// i = big.NewInt(0) +// } +// return encodeUint(i) +// } + +// func encodeAddress(val string) []byte { +// i := big.NewInt(0) +// i.SetString(val, 16) +// return encodeUint(i) +// } + +// func encodeUint(val *big.Int) []byte { +// data := make([]byte, 32) +// _ = val.FillBytes(data) +// return data +// } + +// func encodeUintString(val string) []byte { +// i := big.NewInt(0) +// i.SetString(val, 10) +// return encodeUint(i) +// } + +// func encodeString(val string) []byte { +// return encodeDynamicBytes([]byte(val)) +// } + +// func encodeDynamicBytes(val []byte) []byte { +// hash := sha3.NewLegacyKeccak256() +// hash.Write(val) +// return hash.Sum(nil) +// } + +// func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { +// encodedData, err := encodeData(t.Members, data) +// if err != nil { +// return nil, err +// } +// hash := sha3.NewLegacyKeccak256() +// hash.Write(t.Hash()) +// hash.Write(encodedData) +// return hash.Sum(nil), nil +// } + +// func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { +// domainSeparator, err := hashStruct(&EIP712DomainType, domain) +// if err != nil { +// return nil, err +// } +// messageHash, err := hashStruct(t, message) +// if err != nil { +// return nil, err +// } +// hash := sha3.NewLegacyKeccak256() +// hash.Write([]byte{0x19, 0x01}) +// hash.Write(domainSeparator) +// hash.Write(messageHash) +// return hash.Sum(nil), nil +// } diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go deleted file mode 100644 index 79d5d911..00000000 --- a/pkg/ethsigner/typed_data.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright © 2023 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ethsigner - -import ( - "fmt" - "math/big" - "strings" - - "golang.org/x/crypto/sha3" -) - -type TypeMember struct { - Name string - Type string -} - -type Type struct { - Name string - Members []*TypeMember -} - -var EIP712DomainType = Type{ - Name: "EIP712Domain", - Members: []*TypeMember{ - { - Name: "name", - Type: "string", - }, - { - Name: "version", - Type: "string", - }, - { - Name: "chainId", - Type: "uint256", - }, - { - Name: "verifyingContract", - Type: "address", - }, - { - Name: "salt", - Type: "bytes32", - }, - }, -} - -func (t *TypeMember) Encode() string { - return t.Type + " " + t.Name -} - -func (t *Type) Encode() string { - params := make([]string, len(t.Members)) - for i, member := range t.Members { - params[i] = member.Encode() - } - return t.Name + "(" + strings.Join(params, ",") + ")" -} - -func (t *Type) Hash() []byte { - hash := sha3.NewLegacyKeccak256() - hash.Write([]byte(t.Encode())) - return hash.Sum(nil) -} - -func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { - var result []byte - for _, member := range members { - val := data[member.Name] - encodedVal, err := encodeValue(member, val) - if err != nil { - return nil, err - } - result = append(result, encodedVal...) - } - return result, nil -} - -func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { - switch m.Type { - case "bool": - return encodeBool(val.(bool)), nil - case "address": - return encodeAddress(val.(string)), nil - case "uint256": - return encodeUintString(val.(string)), nil - case "string": - return encodeString(val.(string)), nil - default: - return nil, fmt.Errorf("unsupported type: %s", m.Type) - } -} - -func encodeBool(val bool) []byte { - var i *big.Int - if val { - i = big.NewInt(1) - } else { - i = big.NewInt(0) - } - return encodeUint(i) -} - -func encodeAddress(val string) []byte { - i := big.NewInt(0) - i.SetString(val, 16) - return encodeUint(i) -} - -func encodeUint(val *big.Int) []byte { - data := make([]byte, 32) - _ = val.FillBytes(data) - return data -} - -func encodeUintString(val string) []byte { - i := big.NewInt(0) - i.SetString(val, 10) - return encodeUint(i) -} - -func encodeString(val string) []byte { - return encodeDynamicBytes([]byte(val)) -} - -func encodeDynamicBytes(val []byte) []byte { - hash := sha3.NewLegacyKeccak256() - hash.Write(val) - return hash.Sum(nil) -} - -func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { - encodedData, err := encodeData(t.Members, data) - if err != nil { - return nil, err - } - hash := sha3.NewLegacyKeccak256() - hash.Write(t.Hash()) - hash.Write(encodedData) - return hash.Sum(nil), nil -} - -func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { - domainSeparator, err := hashStruct(&EIP712DomainType, domain) - if err != nil { - return nil, err - } - messageHash, err := hashStruct(t, message) - if err != nil { - return nil, err - } - hash := sha3.NewLegacyKeccak256() - hash.Write([]byte{0x19, 0x01}) - hash.Write(domainSeparator) - hash.Write(messageHash) - return hash.Sum(nil), nil -} From cc6a2c9c86e806dd42ae88303f26e95789da516c Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 22 Oct 2023 21:54:21 -0400 Subject: [PATCH 03/28] Work through ABI->EIP-712 direction Signed-off-by: Peter Broadhurst --- pkg/eip712/abi_to_eip712.go | 20 ++++++++++++-------- pkg/eip712/abi_to_eip712_test.go | 31 ++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/pkg/eip712/abi_to_eip712.go b/pkg/eip712/abi_to_eip712.go index e5bd0963..e41ba7b5 100644 --- a/pkg/eip712/abi_to_eip712.go +++ b/pkg/eip712/abi_to_eip712.go @@ -58,7 +58,7 @@ func HashStructABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexByte return hash.Sum(nil), nil } -func hashString(s string) []byte { +func hashString(s string) ethtypes.HexBytes0xPrefix { hash := sha3.NewLegacyKeccak256() hash.Write([]byte(s)) return hash.Sum(nil) @@ -68,11 +68,11 @@ func hashString(s string) []byte { // // Builds the map of struct names to types func EncodeTypeABI(ctx context.Context, tc abi.TypeComponent) (string, error) { - typeSet, err := buildTypeSetABI(ctx, tc) + primaryName, typeSet, err := ABItoEIP712TypeSet(ctx, tc) if err != nil { return "", err } - return typeSet.Encode(tc.Parameter().Name), nil + return typeSet.Encode(primaryName), nil } // EIP-712 encodeData() implementation using abi.ComponentValue as an input definition @@ -99,16 +99,20 @@ func EncodeDataABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexByte } } -func buildTypeSetABI(ctx context.Context, tc abi.TypeComponent) (TypeSet, error) { +func ABItoEIP712TypeSet(ctx context.Context, tc abi.TypeComponent) (primaryType string, typeSet TypeSet, err error) { if tc.ComponentType() != abi.TupleComponent { - return nil, i18n.NewError(ctx, signermsgs.MsgEIP712PrimaryNotTuple, tc.String()) + return "", nil, i18n.NewError(ctx, signermsgs.MsgEIP712PrimaryNotTuple, tc.String()) + } + primaryType, err = extractSolidityTypeName(ctx, tc.Parameter()) + if err != nil { + return "", nil, err } // First we need to build the sorted array of types for `encodeType` - typeSet := make(TypeSet) + typeSet = make(TypeSet) if err := addABITypes(ctx, tc, typeSet); err != nil { - return nil, err + return "", nil, err } - return typeSet, nil + return primaryType, typeSet, nil } // Maps a parsed ABI component type to an EIP-712 type string diff --git a/pkg/eip712/abi_to_eip712_test.go b/pkg/eip712/abi_to_eip712_test.go index 3413cda3..896b7b45 100644 --- a/pkg/eip712/abi_to_eip712_test.go +++ b/pkg/eip712/abi_to_eip712_test.go @@ -61,6 +61,26 @@ func TestSimpleStruct(t *testing.T) { tc, err := abiElem.TypeComponentTree() assert.NoError(t, err) + pt, ts, err := ABItoEIP712TypeSet(context.Background(), tc) + assert.NoError(t, err) + assert.Equal(t, "Mail", pt) + assert.Equal(t, TypeSet{ + "Mail": Type{ + { + Name: "from", + Type: "address", + }, + { + Name: "to", + Type: "address", + }, + { + Name: "contents", + Type: "string", + }, + }, + }, ts) + cv, err := tc.ParseExternal([]interface{}{ "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", @@ -76,6 +96,15 @@ func TestSimpleStruct(t *testing.T) { encodedData, err := EncodeDataABI(context.Background(), cv) assert.NoError(t, err) - assert.Equal(t, "0x12345", encodedData.String()) + assert.Equal(t, `0x`+ + `000000000000000000000000cd2a3d9f938e13cd947ec05abc7fe734df8dd826`+ // uint160 address + `000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`+ // uint160 address + `48656c6c6f2c20426f6221`, // ASCII: "Hello, Bob!" + encodedData.String()) + + hash, err := HashStructABI(context.Background(), cv) + assert.NoError(t, err) + + assert.Equal(t, hashString(string(hashString(eip712Type))+string(encodedData)).String(), hash.String()) } From 5e1d6d73a283515427cf14a1593be2334c14ede2 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 13:46:25 -0400 Subject: [PATCH 04/28] Significant additional refactoring of typed data V3 encoding Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_error_messges.go | 6 +- pkg/abi/inputparsing.go | 20 +- pkg/abi/typecomponents.go | 16 +- pkg/abi/typecomponents_test.go | 1 + pkg/eip712/abi_to_eip712_test.go | 110 ----- ...{abi_to_eip712.go => abi_to_typed_data.go} | 112 +---- pkg/eip712/abi_to_typed_data_test.go | 101 ++++ pkg/eip712/typed_data.go | 231 --------- pkg/eip712/typed_data_v4.go | 453 ++++++++++++++++++ pkg/eip712/typed_data_v4_test.go | 104 ++++ 10 files changed, 695 insertions(+), 459 deletions(-) delete mode 100644 pkg/eip712/abi_to_eip712_test.go rename pkg/eip712/{abi_to_eip712.go => abi_to_typed_data.go} (56%) create mode 100644 pkg/eip712/abi_to_typed_data_test.go delete mode 100644 pkg/eip712/typed_data.go create mode 100644 pkg/eip712/typed_data_v4.go create mode 100644 pkg/eip712/typed_data_v4_test.go diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index c55beb2f..14ca013a 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -83,10 +83,14 @@ var ( MsgInvalidSigner = ffe("FF22064", "Invalid signer") MsgResultParseFailed = ffe("FF22065", "Failed to parse result (expected=%T): %s") MsgEIP712UnknownABICompType = ffe("FF22066", "Unknown ABI component type: %s") - MsgEIP712NotElementary = ffe("FF22067", "Not elementary type: %s") + MsgNotElementary = ffe("FF22067", "Not elementary type: %s") MsgEIP712UnsupportedStrType = ffe("FF22068", "Unsupported type: %s") MsgEIP712UnsupportedABIType = ffe("FF22069", "ABI type not supported by EIP-712 encoding: %s") MsgNotABIElementaryType = ffe("FF22070", "Not elementary type: %s") MsgEIP712PrimaryNotTuple = ffe("FF22071", "Type primary type must be a struct: %s") MsgEIP712BadInternalType = ffe("FF22072", "Failed to extract struct name from ABI internalType '%s'") + MsgEIP712ValueNotMap = ffe("FF22073", "Value for struct '%s' not a map (%T)") + MsgEIP712InvalidArraySuffix = ffe("FF22074", "Type '%s' has invalid array suffix") + MsgEIP712ValueNotArray = ffe("FF22075", "Value for '%s' not an array (%T)") + MsgEIP712InvalidArrayLen = ffe("FF22076", "Value for '%s' must have %d entries (found %d)") ) diff --git a/pkg/abi/inputparsing.go b/pkg/abi/inputparsing.go index cc146720..a0fcf2d8 100644 --- a/pkg/abi/inputparsing.go +++ b/pkg/abi/inputparsing.go @@ -379,17 +379,21 @@ func getStringInterfaceMap(ctx context.Context, breadcrumbs string, input interf return iMap, nil } +func (tc *typeComponent) readElementaryType(ctx context.Context, breadcrumbs string, input interface{}) (cv *ComponentValue, err error) { + value, err := tc.elementaryType.readExternalData(ctx, breadcrumbs, input) + if err != nil { + return nil, err + } + return &ComponentValue{ + Component: tc, + Value: value, + }, nil +} + func walkInput(ctx context.Context, breadcrumbs string, input interface{}, component *typeComponent) (cv *ComponentValue, err error) { switch component.cType { case ElementaryComponent: - value, err := component.elementaryType.readExternalData(ctx, breadcrumbs, input) - if err != nil { - return nil, err - } - return &ComponentValue{ - Component: component, - Value: value, - }, nil + return component.readElementaryType(ctx, breadcrumbs, input) case FixedArrayComponent, DynamicArrayComponent: return walkArrayInput(ctx, breadcrumbs, input, component) case TupleComponent: diff --git a/pkg/abi/typecomponents.go b/pkg/abi/typecomponents.go index 4ad192ee..79c4dc42 100644 --- a/pkg/abi/typecomponents.go +++ b/pkg/abi/typecomponents.go @@ -56,6 +56,7 @@ type TypeComponent interface { Parameter() *Parameter // the ABI property/component, only set for top-level parameters and tuple entries ParseExternal(v interface{}) (*ComponentValue, error) ParseExternalCtx(ctx context.Context, v interface{}) (*ComponentValue, error) + ParseExternalDesc(ctx context.Context, v interface{}, desc string) (*ComponentValue, error) DecodeABIData(d []byte, offset int) (*ComponentValue, error) DecodeABIDataCtx(ctx context.Context, d []byte, offest int) (*ComponentValue, error) } @@ -88,11 +89,13 @@ type elementaryTypeInfo struct { fixed32 bool // True if the is at most 32 bytes in length, so directly fits into an event topic dynamic func(c *typeComponent) bool // True if the type is dynamic length jsonEncodingType JSONEncodingType // categorizes how the type can be read/written from input JSON data - readExternalData func(ctx context.Context, desc string, input interface{}) (interface{}, error) + readExternalData DataReader encodeABIData func(ctx context.Context, desc string, tc *typeComponent, value interface{}) (data []byte, dynamic bool, err error) decodeABIData func(ctx context.Context, desc string, block []byte, headStart, headPosition int, component *typeComponent) (cv *ComponentValue, err error) } +type DataReader func(ctx context.Context, desc string, input interface{}) (interface{}, error) + func (et *elementaryTypeInfo) String() string { switch et.suffixType { case suffixTypeMOptional, suffixTypeMRequired: @@ -129,6 +132,10 @@ func (et *elementaryTypeInfo) BaseType() BaseTypeName { return et.name } +func (et *elementaryTypeInfo) DataReader() DataReader { + return et.readExternalData +} + var elementaryTypes = map[BaseTypeName]*elementaryTypeInfo{} func registerElementaryType(et elementaryTypeInfo) ElementaryTypeInfo { @@ -143,6 +150,7 @@ type ElementaryTypeInfo interface { BaseType() BaseTypeName // const for each of the elementary types String() string // gives a summary of the rules the elemental type (used in error reporting) JSONEncodingType() JSONEncodingType // categorizes JSON input/output type to one of a small number of options + DataReader() DataReader // allows a side-door into the ABI code read data - caller has to know what to expect } type JSONEncodingType int @@ -397,7 +405,11 @@ func (tc *typeComponent) ParseExternal(input interface{}) (*ComponentValue, erro } func (tc *typeComponent) ParseExternalCtx(ctx context.Context, input interface{}) (*ComponentValue, error) { - return tc.parseExternal(ctx, "", input) + return tc.ParseExternalDesc(ctx, input, "") +} + +func (tc *typeComponent) ParseExternalDesc(ctx context.Context, input interface{}, desc string) (*ComponentValue, error) { + return tc.parseExternal(ctx, desc, input) } func (tc *typeComponent) DecodeABIData(b []byte, offset int) (*ComponentValue, error) { diff --git a/pkg/abi/typecomponents_test.go b/pkg/abi/typecomponents_test.go index 17f5956a..64986708 100644 --- a/pkg/abi/typecomponents_test.go +++ b/pkg/abi/typecomponents_test.go @@ -35,6 +35,7 @@ func TestElementalTypeInfoRules(t *testing.T) { assert.Equal(t, "function", ElementaryTypeFunction.String()) assert.Equal(t, "string", ElementaryTypeString.String()) assert.Equal(t, JSONEncodingTypeString, ElementaryTypeString.JSONEncodingType()) + assert.NotNil(t, ElementaryTypeString.DataReader()) } diff --git a/pkg/eip712/abi_to_eip712_test.go b/pkg/eip712/abi_to_eip712_test.go deleted file mode 100644 index 896b7b45..00000000 --- a/pkg/eip712/abi_to_eip712_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright © 2023 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package eip712 - -import ( - "context" - "encoding/json" - "testing" - - "github.com/hyperledger/firefly-signer/pkg/abi" - "github.com/stretchr/testify/assert" -) - -func TestSimpleStruct(t *testing.T) { - - // struct Mail { - // address from; - // address to; - // string contents; - // } - mailABI := []byte(`{ - "components": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "string", - "name": "contents", - "type": "string" - } - ], - "internalType": "struct EIP712Examples.Mail", - "name": "", - "type": "tuple" - }`) - var abiElem abi.Parameter - err := json.Unmarshal(mailABI, &abiElem) - assert.NoError(t, err) - - tc, err := abiElem.TypeComponentTree() - assert.NoError(t, err) - - pt, ts, err := ABItoEIP712TypeSet(context.Background(), tc) - assert.NoError(t, err) - assert.Equal(t, "Mail", pt) - assert.Equal(t, TypeSet{ - "Mail": Type{ - { - Name: "from", - Type: "address", - }, - { - Name: "to", - Type: "address", - }, - { - Name: "contents", - Type: "string", - }, - }, - }, ts) - - cv, err := tc.ParseExternal([]interface{}{ - "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - "Hello, Bob!", - }) - assert.NoError(t, err) - - eip712Type, err := EncodeTypeABI(context.Background(), cv.Component) - assert.NoError(t, err) - - assert.Equal(t, "Mail(address from,address to,string contents)", eip712Type) - - encodedData, err := EncodeDataABI(context.Background(), cv) - assert.NoError(t, err) - - assert.Equal(t, `0x`+ - `000000000000000000000000cd2a3d9f938e13cd947ec05abc7fe734df8dd826`+ // uint160 address - `000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`+ // uint160 address - `48656c6c6f2c20426f6221`, // ASCII: "Hello, Bob!" - encodedData.String()) - - hash, err := HashStructABI(context.Background(), cv) - assert.NoError(t, err) - - assert.Equal(t, hashString(string(hashString(eip712Type))+string(encodedData)).String(), hash.String()) - -} diff --git a/pkg/eip712/abi_to_eip712.go b/pkg/eip712/abi_to_typed_data.go similarity index 56% rename from pkg/eip712/abi_to_eip712.go rename to pkg/eip712/abi_to_typed_data.go index e41ba7b5..06c052a2 100644 --- a/pkg/eip712/abi_to_eip712.go +++ b/pkg/eip712/abi_to_typed_data.go @@ -17,7 +17,6 @@ package eip712 import ( - "bytes" "context" "fmt" "regexp" @@ -25,81 +24,14 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-signer/internal/signermsgs" "github.com/hyperledger/firefly-signer/pkg/abi" - "github.com/hyperledger/firefly-signer/pkg/ethtypes" - "golang.org/x/crypto/sha3" ) var internalTypeStructExtractor = regexp.MustCompile(`^struct (.*\.)?([^.]+)$`) -// EIP-712 hashStruct() implementation using abi.ComponentValue as an input definition -// -// This file takes ComponentValue trees from the ABI encoding package, -// and traverses them into EIP-712 encodable structures according to -// the rules laid out in: -// -// https://eips.ethereum.org/EIPS/eip-712 -func HashStructABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { - encodedType, err := EncodeTypeABI(ctx, v.Component) - if err != nil { - return nil, err - } - - encodedData, err := EncodeDataABI(ctx, v) - if err != nil { - return nil, err - } - - // typeHash = keccak256(encodeType(typeOf(s))) - typeHash := hashString(encodedType) - /// hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s)) - hash := sha3.NewLegacyKeccak256() - hash.Write(typeHash) - hash.Write([]byte(encodedData)) - return hash.Sum(nil), nil -} - -func hashString(s string) ethtypes.HexBytes0xPrefix { - hash := sha3.NewLegacyKeccak256() - hash.Write([]byte(s)) - return hash.Sum(nil) -} - -// EIP-712 encodeType() implementation using abi.TypeComponent as an input definition -// -// Builds the map of struct names to types -func EncodeTypeABI(ctx context.Context, tc abi.TypeComponent) (string, error) { - primaryName, typeSet, err := ABItoEIP712TypeSet(ctx, tc) - if err != nil { - return "", err - } - return typeSet.Encode(primaryName), nil -} - -// EIP-712 encodeData() implementation using abi.ComponentValue as an input definition -// -// Recurses into the structure following the rules of EIP-712 to construct the encoded hash. -func EncodeDataABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { - tc := v.Component - switch tc.ComponentType() { - case abi.TupleComponent, abi.DynamicArrayComponent, abi.FixedArrayComponent: - // Concatenate an encoding of each component - buff := new(bytes.Buffer) - for _, child := range v.Children { - childData, err := EncodeDataABI(ctx, child) - if err != nil { - return nil, err - } - buff.Write(childData) - } - return buff.Bytes(), nil - case abi.ElementaryComponent: - return encodeElementaryDataABI(ctx, v) - default: - return nil, i18n.NewError(ctx, signermsgs.MsgEIP712UnknownABICompType, tc.ComponentType()) - } -} - -func ABItoEIP712TypeSet(ctx context.Context, tc abi.TypeComponent) (primaryType string, typeSet TypeSet, err error) { +// Convert an ABI tuple definition, into the EIP-712 structure that's embedded into the +// "eth_signTypedData" signing request payload. It's a much simpler structure that +// flattens out a map of types (requiring each type to be named by a struct definition) +func ABItoTypedDataV4(ctx context.Context, tc abi.TypeComponent) (primaryType string, typeSet TypeSet, err error) { if tc.ComponentType() != abi.TupleComponent { return "", nil, i18n.NewError(ctx, signermsgs.MsgEIP712PrimaryNotTuple, tc.String()) } @@ -141,7 +73,7 @@ func mapABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { func mapElementaryABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { et := tc.ElementaryType() if et == nil { - return "", i18n.NewError(ctx, signermsgs.MsgEIP712NotElementary, tc) + return "", i18n.NewError(ctx, signermsgs.MsgNotElementary, tc) } switch et.BaseType() { case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeString: @@ -162,40 +94,6 @@ func mapElementaryABIType(ctx context.Context, tc abi.TypeComponent) (string, er } } -func encodeElementaryDataABI(ctx context.Context, v *abi.ComponentValue) (ethtypes.HexBytes0xPrefix, error) { - tc := v.Component - et := tc.ElementaryType() - if et == nil { - return nil, i18n.NewError(ctx, signermsgs.MsgEIP712NotElementary, tc) - } - switch et.BaseType() { - case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeInt, abi.BaseTypeUInt: - // Types that need no transposition - b, _, err := v.ElementaryABIDataCtx(ctx) - return b, err - case abi.BaseTypeBytes: - if tc.ElementaryFixed() { - // If it's bytes1 -> bytes32 - b, _, err := v.ElementaryABIDataCtx(ctx) - return b, err - } - b, ok := v.Value.([]byte) - if !ok { - return nil, i18n.NewError(ctx, signermsgs.MsgWrongTypeComponentABIEncode, "[]byte", v.Value, v.Component.KeyName()) - } - return b, nil - case abi.BaseTypeString: - s, ok := v.Value.(string) - if !ok { - return nil, i18n.NewError(ctx, signermsgs.MsgWrongTypeComponentABIEncode, "string", v.Value, v.Component.KeyName()) - } - return []byte(s), nil - default: - // EIP-712 does not support the other types - return nil, i18n.NewError(ctx, signermsgs.MsgEIP712UnsupportedABIType, tc) - } -} - // ABI does not formally contain the Struct name - as it's not required for encoding the value. // EIP-712 requires the Struct name as it is used through the standard, including to de-dup definitions // diff --git a/pkg/eip712/abi_to_typed_data_test.go b/pkg/eip712/abi_to_typed_data_test.go new file mode 100644 index 00000000..f9c99ed3 --- /dev/null +++ b/pkg/eip712/abi_to_typed_data_test.go @@ -0,0 +1,101 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +// func TestSimpleStruct(t *testing.T) { + +// // struct Mail { +// // address from; +// // address to; +// // string contents; +// // } +// mailABI := []byte(`{ +// "components": [ +// { +// "internalType": "address", +// "name": "from", +// "type": "address" +// }, +// { +// "internalType": "address", +// "name": "to", +// "type": "address" +// }, +// { +// "internalType": "string", +// "name": "contents", +// "type": "string" +// } +// ], +// "internalType": "struct EIP712Examples.Mail", +// "name": "", +// "type": "tuple" +// }`) +// var abiElem abi.Parameter +// err := json.Unmarshal(mailABI, &abiElem) +// assert.NoError(t, err) + +// tc, err := abiElem.TypeComponentTree() +// assert.NoError(t, err) + +// pt, ts, err := ABItoEIP712TypeSet(context.Background(), tc) +// assert.NoError(t, err) +// assert.Equal(t, "Mail", pt) +// assert.Equal(t, TypeSet{ +// "Mail": Type{ +// { +// Name: "from", +// Type: "address", +// }, +// { +// Name: "to", +// Type: "address", +// }, +// { +// Name: "contents", +// Type: "string", +// }, +// }, +// }, ts) + +// cv, err := tc.ParseExternal([]interface{}{ +// "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", +// "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", +// "Hello, Bob!", +// }) +// assert.NoError(t, err) + +// eip712Type, err := EncodeTypeABI(context.Background(), cv.Component) +// assert.NoError(t, err) + +// assert.Equal(t, "Mail(address from,address to,string contents)", eip712Type) + +// encodedData, err := EncodeDataABI(context.Background(), cv) +// assert.NoError(t, err) + +// assert.Equal(t, `0x`+ +// `000000000000000000000000cd2a3d9f938e13cd947ec05abc7fe734df8dd826`+ // uint160 address +// `000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`+ // uint160 address +// `48656c6c6f2c20426f6221`, // ASCII: "Hello, Bob!" +// encodedData.String()) + +// hash, err := HashStructABI(context.Background(), cv) +// assert.NoError(t, err) + +// assert.Equal(t, hashString(string(hashString(eip712Type))+string(encodedData)).String(), hash.String()) + +// } diff --git a/pkg/eip712/typed_data.go b/pkg/eip712/typed_data.go deleted file mode 100644 index 7be08ad3..00000000 --- a/pkg/eip712/typed_data.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright © 2023 Kaleido, Inc. -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package eip712 - -import ( - "sort" - "strings" - - "github.com/hyperledger/firefly-signer/pkg/ethtypes" -) - -type TypeMember struct { - Name string - Type string -} - -type Type []*TypeMember - -type SigningRequest struct { - Types TypeSet `json:"types"` - PrimaryType string `json:"primaryType"` - Domain Domain `json:"domain"` - Message map[string]interface{} `json:"message"` -} - -type TypeSet map[string]Type - -type Domain struct { - Name string `json:"name"` - Version string `json:"version"` - ChainID int64 `json:"chainId"` - VerifyingContract ethtypes.Address0xHex `json:"verifyingContract"` -} - -// A map from type names to types is encoded per encodeType: -// -// > If the struct type references other struct types (and these in turn reference even more struct types), -// > then the set of referenced struct types is collected, sorted by name and appended to the encoding. -func (ts TypeSet) Encode(primaryType string) string { - // Write the primary type first always - buff := new(strings.Builder) - buff.WriteString(ts[primaryType].Encode(primaryType)) - - // Then the reference types sorted by name - referenceTypes := make([]string, 0, len(ts)) - for typeName := range ts { - if typeName != primaryType { - referenceTypes = append(referenceTypes, typeName) - } - } - sort.Strings(referenceTypes) - for _, typeName := range referenceTypes { - buff.WriteString(ts[typeName].Encode(typeName)) - } - return buff.String() -} - -// An individual member is encoded as: -// -// > type ‖ " " ‖ name -func (tm *TypeMember) Encode() string { - return tm.Type + " " + tm.Name -} - -// A type is encoded as: -// -// > name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" -func (t Type) Encode(name string) string { - buff := new(strings.Builder) - buff.WriteString(name) - buff.WriteRune('(') - for i, tm := range t { - if i > 0 { - buff.WriteRune(',') - } - buff.WriteString(tm.Encode()) - } - buff.WriteRune(')') - return buff.String() -} - -// var EIP712DomainType = Type{ -// Name: "EIP712Domain", -// Members: []*TypeMember{ -// { -// Name: "name", -// Type: "string", -// }, -// { -// Name: "version", -// Type: "string", -// }, -// { -// Name: "chainId", -// Type: "uint256", -// }, -// { -// Name: "verifyingContract", -// Type: "address", -// }, -// { -// Name: "salt", -// Type: "bytes32", -// }, -// }, -// } - -// func (t *TypeMember) Encode() string { -// return t.Type + " " + t.Name -// } - -// func (t *Type) Encode() string { -// params := make([]string, len(t.Members)) -// for i, member := range t.Members { -// params[i] = member.Encode() -// } -// return t.Name + "(" + strings.Join(params, ",") + ")" -// } - -// func (t *Type) Hash() []byte { -// hash := sha3.NewLegacyKeccak256() -// hash.Write([]byte(t.Encode())) -// return hash.Sum(nil) -// } - -// func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { -// var result []byte -// for _, member := range members { -// val := data[member.Name] -// encodedVal, err := encodeValue(member, val) -// if err != nil { -// return nil, err -// } -// result = append(result, encodedVal...) -// } -// return result, nil -// } - -// func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { -// switch m.Type { -// case "bool": -// return encodeBool(val.(bool)), nil -// case "address": -// return encodeAddress(val.(string)), nil -// case "uint256": -// return encodeUintString(val.(string)), nil -// case "string": -// return encodeString(val.(string)), nil -// default: -// return nil, fmt.Errorf("unsupported type: %s", m.Type) -// } -// } - -// func encodeBool(val bool) []byte { -// var i *big.Int -// if val { -// i = big.NewInt(1) -// } else { -// i = big.NewInt(0) -// } -// return encodeUint(i) -// } - -// func encodeAddress(val string) []byte { -// i := big.NewInt(0) -// i.SetString(val, 16) -// return encodeUint(i) -// } - -// func encodeUint(val *big.Int) []byte { -// data := make([]byte, 32) -// _ = val.FillBytes(data) -// return data -// } - -// func encodeUintString(val string) []byte { -// i := big.NewInt(0) -// i.SetString(val, 10) -// return encodeUint(i) -// } - -// func encodeString(val string) []byte { -// return encodeDynamicBytes([]byte(val)) -// } - -// func encodeDynamicBytes(val []byte) []byte { -// hash := sha3.NewLegacyKeccak256() -// hash.Write(val) -// return hash.Sum(nil) -// } - -// func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { -// encodedData, err := encodeData(t.Members, data) -// if err != nil { -// return nil, err -// } -// hash := sha3.NewLegacyKeccak256() -// hash.Write(t.Hash()) -// hash.Write(encodedData) -// return hash.Sum(nil), nil -// } - -// func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { -// domainSeparator, err := hashStruct(&EIP712DomainType, domain) -// if err != nil { -// return nil, err -// } -// messageHash, err := hashStruct(t, message) -// if err != nil { -// return nil, err -// } -// hash := sha3.NewLegacyKeccak256() -// hash.Write([]byte{0x19, 0x01}) -// hash.Write(domainSeparator) -// hash.Write(messageHash) -// return hash.Sum(nil), nil -// } diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go new file mode 100644 index 00000000..0275d971 --- /dev/null +++ b/pkg/eip712/typed_data_v4.go @@ -0,0 +1,453 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +import ( + "bytes" + "context" + "fmt" + "sort" + "strconv" + "strings" + + "github.com/hyperledger/firefly-common/pkg/i18n" + "github.com/hyperledger/firefly-common/pkg/log" + "github.com/hyperledger/firefly-signer/internal/signermsgs" + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" + "golang.org/x/crypto/sha3" +) + +type TypeMember struct { + Name string + Type string +} + +type Type []*TypeMember + +type SignTypedDataPayload struct { + Types TypeSet `json:"types"` + PrimaryType string `json:"primaryType"` + Domain Domain `json:"domain"` + Message map[string]interface{} `json:"message"` +} + +type TypeSet map[string]Type + +type Domain struct { + Name string `json:"name"` + Version string `json:"version"` + ChainID int64 `json:"chainId"` + VerifyingContract ethtypes.Address0xHex `json:"verifyingContract"` +} + +func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (ethtypes.HexBytes0xPrefix, error) { + b, err := hashStruct(ctx, payload.PrimaryType, payload.Message, payload.Types, "") + if err != nil { + return nil, err + } + return b, err +} + +// A map from type names to types is encoded per encodeType: +// +// > If the struct type references other struct types (and these in turn reference even more struct types), +// > then the set of referenced struct types is collected, sorted by name and appended to the encoding. +func (ts TypeSet) Encode(primaryType string) string { + // Write the primary type first always + buff := new(strings.Builder) + buff.WriteString(ts[primaryType].Encode(primaryType)) + + // Then the reference types sorted by name + referenceTypes := make([]string, 0, len(ts)) + for typeName := range ts { + if typeName != primaryType { + referenceTypes = append(referenceTypes, typeName) + } + } + sort.Strings(referenceTypes) + for _, typeName := range referenceTypes { + buff.WriteString(ts[typeName].Encode(typeName)) + } + return buff.String() +} + +// An individual member is encoded as: +// +// > type ‖ " " ‖ name +func (tm *TypeMember) Encode() string { + return tm.Type + " " + tm.Name +} + +// A type is encoded as: +// +// > name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")" +func (t Type) Encode(name string) string { + buff := new(strings.Builder) + buff.WriteString(name) + buff.WriteRune('(') + for i, tm := range t { + if i > 0 { + buff.WriteRune(',') + } + buff.WriteString(tm.Encode()) + } + buff.WriteRune(')') + return buff.String() +} + +func nextCrumb(breadcrumbs string, name string) string { + return breadcrumbs + "." + name +} + +func idxCrumb(breadcrumbs string, idx int) string { + return fmt.Sprintf("%s[%d]", breadcrumbs, idx) +} + +func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) (Type, error) { + // We're not interested in array semantics here + iBracket := strings.Index(typeName, "[") + if iBracket >= 0 { + typeName = typeName[0:iBracket] + } + // See if it's a defined structure type + t, ok := allTypes[typeName] + if !ok { + return nil, nil + } + if typeSet[typeName] == nil { + typeSet[typeName] = t + for _, tm := range t { + if _, err := addNestedTypes(tm.Type, allTypes, typeSet); err != nil { + return nil, err + } + } + } + return t, nil +} + +func keccak256(b []byte) ethtypes.HexBytes0xPrefix { + hash := sha3.NewLegacyKeccak256() + hash.Write(b) + return hash.Sum(nil) +} + +// A map from type names to types is encoded per encodeType: +// +// > If the struct type references other struct types (and these in turn reference even more struct types), +// > then the set of referenced struct types is collected, sorted by name and appended to the encoding. +func encodeType(ctx context.Context, typeName string, allTypes TypeSet) (Type, string, error) { + depSet := make(TypeSet) + t, err := addNestedTypes(typeName, allTypes, depSet) + if err != nil { + return nil, "", err + } + typeEncoded := depSet.Encode(typeName) + log.L(ctx).Tracef("encodeType(%s): %s", typeName, typeEncoded) + return t, typeEncoded, nil +} + +func encodeData(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (encoded ethtypes.HexBytes0xPrefix, err error) { + // Get the local typeset for the struct and all its deps + t, typeEncoded, err := encodeType(ctx, typeName, allTypes) + if err != nil { + return nil, err + } + // Check the value we have is a map + switch vt := v.(type) { + case nil: + // special rule for a nil value - we don't even include the type info, just return a nil bytes array + bytes32Enc, _ := abiElementalType(ctx, "bytes32") + encoded, _ = abiEncode(ctx, bytes32Enc, []byte{ /* empty */ }) + case map[string]interface{}: + typeHashed := keccak256([]byte(typeEncoded)) + buf := bytes.NewBuffer(typeHashed) + log.L(ctx).Tracef("hashType(%s): %s", typeName, typeHashed) + // Encode the data of the struct, and write it after the hash of the type + for _, tm := range t { + b, err := encodeElement(ctx, tm.Type, vt[tm.Name], allTypes, nextCrumb(breadcrumbs, tm.Name)) + if err != nil { + return nil, err + } + buf.Write(b) + } + encoded = buf.Bytes() + default: + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712ValueNotMap, breadcrumbs, v) + } + log.L(ctx).Tracef("encodeData(%s, %T): %s", typeName, v, encoded) + return encoded, nil +} + +func hashStruct(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (ethtypes.HexBytes0xPrefix, error) { + encoded, err := encodeData(ctx, typeName, v, allTypes, breadcrumbs) + if err != nil { + return nil, err + } + hashed := keccak256(encoded) + log.L(ctx).Tracef("hashStruct(%s): %s", typeName, hashed) + return hashed, nil +} + +func encodeElement(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (ethtypes.HexBytes0xPrefix, error) { + if strings.HasSuffix(typeName, "]") { + // recurse into the array + return hashArray(ctx, typeName, allTypes, v, breadcrumbs) + } else if _, isStruct := allTypes[typeName]; isStruct { + // recurse into the struct + return hashStruct(ctx, typeName, v, allTypes, breadcrumbs) + } + // Need to process based on the elemental type + tc, err := abiElementalType(ctx, typeName) + if err != nil { + return nil, err + } + baseType := tc.ElementaryType().BaseType() + switch baseType { + case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeInt, abi.BaseTypeUInt: + return abiEncode(ctx, tc, v) + case abi.BaseTypeBytes: + // Handle fixed bytes1 to bytes32 + if baseType == abi.BaseTypeBytes && tc.ElementaryFixed() { + return abiEncode(ctx, tc, v) + } + // These dynamic bytes/string arrays are special handling, where we need to use the same + // rules as ABI to extract the byte string from the input... but we need to actually + // return a keccak256 of the contents + // - We have special knowledge here that the type will be coercible to []byte + reader := tc.ElementaryType().DataReader() + di, err := reader(ctx, breadcrumbs, v) + if err != nil { + return nil, err + } + return keccak256(di.([]byte)), nil + case abi.BaseTypeString: + reader := tc.ElementaryType().DataReader() + di, err := reader(ctx, breadcrumbs, v) + if err != nil { + return nil, err + } + return keccak256([]byte(di.(string))), nil + default: + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712UnsupportedABIType, tc) + } +} + +func abiElementalType(ctx context.Context, typeName string) (abi.TypeComponent, error) { + p := &abi.Parameter{Type: typeName} + tc, err := p.TypeComponentTreeCtx(ctx) + if err != nil { + return nil, err + } + + // Check it's one of the EIP-712 supported types + et := tc.ElementaryType() + if et == nil { + return nil, i18n.NewError(ctx, signermsgs.MsgNotElementary, tc) + } + return tc, nil +} + +func abiEncode(ctx context.Context, tc abi.TypeComponent, v interface{}) (b ethtypes.HexBytes0xPrefix, err error) { + // Re-use the ABI function to parse the input value for elemental types. + // (we weren't able to do this for structs/tuples and arrays, due to EIP-712 specifics) + cv, err := tc.ParseExternalCtx(ctx, v) + if err != nil { + return nil, err + } + b, err = cv.EncodeABIDataCtx(ctx) + if err != nil { + return nil, err + } + log.L(ctx).Tracef("encodeElement(%s, %T): %s", tc.String(), v, b) + return b, nil +} + +// hashArray is only called when the last character of the type is `]` +func hashArray(ctx context.Context, typeName string, allTypes TypeSet, v interface{}, breadcrumbs string) (ethtypes.HexBytes0xPrefix, error) { + // Extract the dimension of the array + openPos := strings.LastIndex(typeName, "[") + if openPos <= 0 || typeName[len(typeName)-1] != ']' { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712InvalidArraySuffix, typeName) + } + dimStr := typeName[openPos+1 : len(typeName)-1] + trimmedTypeName := typeName[0:openPos] + + // We should have an array in the input. + // Note Go JSON unmarshal always gives []interface{}, regardless of type of entry. + va, ok := v.([]interface{}) + if !ok { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712ValueNotArray, typeName, v) + } + // If we have a fixed dimension, then check we have the right number of elements + if dimStr != "" { + dim, err := strconv.Atoi(dimStr) + if err != nil { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712InvalidArraySuffix, typeName) + } + if len(va) != dim { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712InvalidArrayLen, typeName, dim, len(va)) + } + } + // Append all the data + buf := new(bytes.Buffer) + for i, ve := range va { + b, err := encodeElement(ctx, trimmedTypeName, ve, allTypes, idxCrumb(breadcrumbs, i)) + if err != nil { + return nil, err + } + buf.Write(b) + } + return buf.Bytes(), nil +} + +// var EIP712DomainType = Type{ +// Name: "EIP712Domain", +// Members: []*TypeMember{ +// { +// Name: "name", +// Type: "string", +// }, +// { +// Name: "version", +// Type: "string", +// }, +// { +// Name: "chainId", +// Type: "uint256", +// }, +// { +// Name: "verifyingContract", +// Type: "address", +// }, +// { +// Name: "salt", +// Type: "bytes32", +// }, +// }, +// } + +// func (t *TypeMember) Encode() string { +// return t.Type + " " + t.Name +// } + +// func (t *Type) Encode() string { +// params := make([]string, len(t.Members)) +// for i, member := range t.Members { +// params[i] = member.Encode() +// } +// return t.Name + "(" + strings.Join(params, ",") + ")" +// } + +// func (t *Type) Hash() []byte { +// hash := sha3.NewLegacyKeccak256() +// hash.Write([]byte(t.Encode())) +// return hash.Sum(nil) +// } + +// func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { +// var result []byte +// for _, member := range members { +// val := data[member.Name] +// encodedVal, err := encodeValue(member, val) +// if err != nil { +// return nil, err +// } +// result = append(result, encodedVal...) +// } +// return result, nil +// } + +// func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { +// switch m.Type { +// case "bool": +// return encodeBool(val.(bool)), nil +// case "address": +// return encodeAddress(val.(string)), nil +// case "uint256": +// return encodeUintString(val.(string)), nil +// case "string": +// return encodeString(val.(string)), nil +// default: +// return nil, fmt.Errorf("unsupported type: %s", m.Type) +// } +// } + +// func encodeBool(val bool) []byte { +// var i *big.Int +// if val { +// i = big.NewInt(1) +// } else { +// i = big.NewInt(0) +// } +// return encodeUint(i) +// } + +// func encodeAddress(val string) []byte { +// i := big.NewInt(0) +// i.SetString(val, 16) +// return encodeUint(i) +// } + +// func encodeUint(val *big.Int) []byte { +// data := make([]byte, 32) +// _ = val.FillBytes(data) +// return data +// } + +// func encodeUintString(val string) []byte { +// i := big.NewInt(0) +// i.SetString(val, 10) +// return encodeUint(i) +// } + +// func encodeString(val string) []byte { +// return encodeDynamicBytes([]byte(val)) +// } + +// func encodeDynamicBytes(val []byte) []byte { +// hash := sha3.NewLegacyKeccak256() +// hash.Write(val) +// return hash.Sum(nil) +// } + +// func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { +// encodedData, err := encodeData(t.Members, data) +// if err != nil { +// return nil, err +// } +// hash := sha3.NewLegacyKeccak256() +// hash.Write(t.Hash()) +// hash.Write(encodedData) +// return hash.Sum(nil), nil +// } + +// func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { +// domainSeparator, err := hashStruct(&EIP712DomainType, domain) +// if err != nil { +// return nil, err +// } +// messageHash, err := hashStruct(t, message) +// if err != nil { +// return nil, err +// } +// hash := sha3.NewLegacyKeccak256() +// hash.Write([]byte{0x19, 0x01}) +// hash.Write(domainSeparator) +// hash.Write(messageHash) +// return hash.Sum(nil), nil +// } diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go new file mode 100644 index 00000000..8d0fefa4 --- /dev/null +++ b/pkg/eip712/typed_data_v4_test.go @@ -0,0 +1,104 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eip712 + +import ( + "context" + "encoding/json" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +const request1 = `{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } +}` + +func TestMessage1(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(request1), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.Equal(t, "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e", ed.String()) + +} From f830806198ea5c6ba6d2dae7b6906e0a9d83d370 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 14:21:17 -0400 Subject: [PATCH 05/28] More varation of scenarios Signed-off-by: Peter Broadhurst --- pkg/abi/inputparsing.go | 4 +- pkg/eip712/typed_data_v4.go | 65 ++++++------ pkg/eip712/typed_data_v4_test.go | 174 ++++++++++++++++++++----------- 3 files changed, 144 insertions(+), 99 deletions(-) diff --git a/pkg/abi/inputparsing.go b/pkg/abi/inputparsing.go index a0fcf2d8..54329088 100644 --- a/pkg/abi/inputparsing.go +++ b/pkg/abi/inputparsing.go @@ -319,7 +319,7 @@ func getBytesFromInterface(ctx context.Context, desc string, v interface{}) ([]b vt = strings.TrimPrefix(vt, "0x") hb, err := hex.DecodeString(vt) if err != nil { - return nil, i18n.WrapError(ctx, err, signermsgs.MsgInvalidHexABIInput, vt, v, desc) + return nil, i18n.WrapError(ctx, err, signermsgs.MsgInvalidHexABIInput, v, desc) } return hb, nil default: @@ -333,7 +333,7 @@ func getBytesFromInterface(ctx context.Context, desc string, v interface{}) ([]b if vi != nil { return getBytesFromInterface(ctx, desc, vi) } - return nil, i18n.NewError(ctx, signermsgs.MsgInvalidHexABIInput, vt, v) + return nil, i18n.NewError(ctx, signermsgs.MsgInvalidHexABIInput, v, desc) } } diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index 0275d971..e23f4d14 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -42,7 +42,7 @@ type Type []*TypeMember type SignTypedDataPayload struct { Types TypeSet `json:"types"` PrimaryType string `json:"primaryType"` - Domain Domain `json:"domain"` + Domain map[string]interface{} `json:"domain"` Message map[string]interface{} `json:"message"` } @@ -55,12 +55,35 @@ type Domain struct { VerifyingContract ethtypes.Address0xHex `json:"verifyingContract"` } +const EIP712Domain = "EIP712Domain" + func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (ethtypes.HexBytes0xPrefix, error) { - b, err := hashStruct(ctx, payload.PrimaryType, payload.Message, payload.Types, "") + // Add empty EIP712Domain type specification if missing + if _, found := payload.Types[EIP712Domain]; !found { + payload.Types[EIP712Domain] = Type{} + } + + // Start with the EIP-712 prefix + buf := new(bytes.Buffer) + buf.Write([]byte{0x19, 0x01}) + + // Encode EIP712Domain from message + domainHash, err := hashStruct(ctx, EIP712Domain, payload.Domain, payload.Types, "domain") if err != nil { return nil, err } - return b, err + buf.Write(domainHash) + + // If that wasn't the primary type, encode the primary type + if payload.PrimaryType != EIP712Domain { + // Encode the hash + structHash, err := hashStruct(ctx, payload.PrimaryType, payload.Message, payload.Types, "") + if err != nil { + return nil, err + } + buf.Write(structHash) + } + return keccak256(buf.Bytes()), nil } // A map from type names to types is encoded per encodeType: @@ -172,7 +195,7 @@ func encodeData(ctx context.Context, typeName string, v interface{}, allTypes Ty case nil: // special rule for a nil value - we don't even include the type info, just return a nil bytes array bytes32Enc, _ := abiElementalType(ctx, "bytes32") - encoded, _ = abiEncode(ctx, bytes32Enc, []byte{ /* empty */ }) + encoded, _ = abiEncode(ctx, bytes32Enc, []byte{ /* empty */ }, breadcrumbs) case map[string]interface{}: typeHashed := keccak256([]byte(typeEncoded)) buf := bytes.NewBuffer(typeHashed) @@ -219,11 +242,11 @@ func encodeElement(ctx context.Context, typeName string, v interface{}, allTypes baseType := tc.ElementaryType().BaseType() switch baseType { case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeInt, abi.BaseTypeUInt: - return abiEncode(ctx, tc, v) + return abiEncode(ctx, tc, v, breadcrumbs) case abi.BaseTypeBytes: // Handle fixed bytes1 to bytes32 if baseType == abi.BaseTypeBytes && tc.ElementaryFixed() { - return abiEncode(ctx, tc, v) + return abiEncode(ctx, tc, v, breadcrumbs) } // These dynamic bytes/string arrays are special handling, where we need to use the same // rules as ABI to extract the byte string from the input... but we need to actually @@ -262,10 +285,10 @@ func abiElementalType(ctx context.Context, typeName string) (abi.TypeComponent, return tc, nil } -func abiEncode(ctx context.Context, tc abi.TypeComponent, v interface{}) (b ethtypes.HexBytes0xPrefix, err error) { +func abiEncode(ctx context.Context, tc abi.TypeComponent, v interface{}, breadcrumbs string) (b ethtypes.HexBytes0xPrefix, err error) { // Re-use the ABI function to parse the input value for elemental types. // (we weren't able to do this for structs/tuples and arrays, due to EIP-712 specifics) - cv, err := tc.ParseExternalCtx(ctx, v) + cv, err := tc.ParseExternalDesc(ctx, v, breadcrumbs) if err != nil { return nil, err } @@ -315,32 +338,6 @@ func hashArray(ctx context.Context, typeName string, allTypes TypeSet, v interfa return buf.Bytes(), nil } -// var EIP712DomainType = Type{ -// Name: "EIP712Domain", -// Members: []*TypeMember{ -// { -// Name: "name", -// Type: "string", -// }, -// { -// Name: "version", -// Type: "string", -// }, -// { -// Name: "chainId", -// Type: "uint256", -// }, -// { -// Name: "verifyingContract", -// Type: "address", -// }, -// { -// Name: "salt", -// Type: "bytes32", -// }, -// }, -// } - // func (t *TypeMember) Encode() string { // return t.Type + " " + t.Name // } diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index 8d0fefa4..a5af66c6 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -25,80 +25,128 @@ import ( "github.com/stretchr/testify/assert" ) -const request1 = `{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] +const PersonType = `[ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } +]` + +const MailType = `[ + { + "name": "from", + "type": "Person" }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + { + "name": "to", + "type": "Person" }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + { + "name": "contents", + "type": "string" + } +]` + +func TestMessage_ExampleFromEIP712Spec(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": `+PersonType+`, + "Mail": `+MailType+` }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" }, - "contents": "Hello, Bob!" - } -}` + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + }`), &p) + assert.NoError(t, err) -func TestMessage1(t *testing.T) { + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2", ed.String()) +} + +func TestMessage_EmptyMessage(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) var p SignTypedDataPayload - err := json.Unmarshal([]byte(request1), &p) + err := json.Unmarshal([]byte(`{ + "types": {}, + "primaryType": "EIP712Domain" + }`), &p) assert.NoError(t, err) ctx := context.Background() ed, err := EncodeTypedDataV4(ctx, &p) - assert.Equal(t, "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e", ed.String()) + assert.NoError(t, err) + assert.Equal(t, "0x8d4a3f4082945b7879e2b55f181c31a77c8c0a464b70669458abbaaf99de4c38", ed.String()) +} + +func TestMessage_EmptyDomain(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": `+PersonType+`, + "Mail": `+MailType+` + }, + "primaryType": "Mail", + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0x25c3d40a39e639a4d0b6e4d2ace5e1281e039c88494d97d8d08f99a6ea75d775", ed.String()) } From 74b550bbc86040a6b11d9343ce320f01017461ed Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 15:02:56 -0400 Subject: [PATCH 06/28] Work through nil handling Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_error_messges.go | 1 + pkg/eip712/typed_data_v4.go | 62 ++++++++++++++++-------- pkg/eip712/typed_data_v4_test.go | 64 +++++++++++++------------ 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index 14ca013a..f48fa7a8 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -93,4 +93,5 @@ var ( MsgEIP712InvalidArraySuffix = ffe("FF22074", "Type '%s' has invalid array suffix") MsgEIP712ValueNotArray = ffe("FF22075", "Value for '%s' not an array (%T)") MsgEIP712InvalidArrayLen = ffe("FF22076", "Value for '%s' must have %d entries (found %d)") + MsgEIP712Version4Required = ffe("FF22077", "Domain version must be 'V4': %s") ) diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index e23f4d14..223c244e 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -57,11 +57,18 @@ type Domain struct { const EIP712Domain = "EIP712Domain" -func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (ethtypes.HexBytes0xPrefix, error) { +func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (encoded ethtypes.HexBytes0xPrefix, err error) { // Add empty EIP712Domain type specification if missing if _, found := payload.Types[EIP712Domain]; !found { payload.Types[EIP712Domain] = Type{} } + if payload.Domain == nil { + payload.Domain = make(map[string]interface{}) + } + if v, didSupplyVersion := payload.Domain["version"].(string); didSupplyVersion && v != "V4" { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712Version4Required, v) + } + payload.Domain["version"] = "V4" // Start with the EIP-712 prefix buf := new(bytes.Buffer) @@ -83,7 +90,10 @@ func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (etht } buf.Write(structHash) } - return keccak256(buf.Bytes()), nil + + encoded = buf.Bytes() + log.L(ctx).Tracef("Encoded EIP-712: %s", encoded) + return keccak256(encoded), nil } // A map from type names to types is encoded per encodeType: @@ -191,39 +201,49 @@ func encodeData(ctx context.Context, typeName string, v interface{}, allTypes Ty return nil, err } // Check the value we have is a map + var vMap map[string]interface{} switch vt := v.(type) { case nil: - // special rule for a nil value - we don't even include the type info, just return a nil bytes array - bytes32Enc, _ := abiElementalType(ctx, "bytes32") - encoded, _ = abiEncode(ctx, bytes32Enc, []byte{ /* empty */ }, breadcrumbs) case map[string]interface{}: - typeHashed := keccak256([]byte(typeEncoded)) - buf := bytes.NewBuffer(typeHashed) - log.L(ctx).Tracef("hashType(%s): %s", typeName, typeHashed) - // Encode the data of the struct, and write it after the hash of the type - for _, tm := range t { - b, err := encodeElement(ctx, tm.Type, vt[tm.Name], allTypes, nextCrumb(breadcrumbs, tm.Name)) - if err != nil { - return nil, err - } - buf.Write(b) - } - encoded = buf.Bytes() + vMap = vt default: return nil, i18n.NewError(ctx, signermsgs.MsgEIP712ValueNotMap, breadcrumbs, v) } + if vMap == nil { + // V4 says the caller writes an empty bytes32, rather than a hash of anything + return nil, nil + } + typeHashed := keccak256([]byte(typeEncoded)) + buf := bytes.NewBuffer(typeHashed) + log.L(ctx).Tracef("hashType(%s): %s", typeName, typeHashed) + // Encode the data of the struct, and write it after the hash of the type + for _, tm := range t { + b, err := encodeElement(ctx, tm.Type, vMap[tm.Name], allTypes, nextCrumb(breadcrumbs, tm.Name)) + if err != nil { + return nil, err + } + buf.Write(b) + } + encoded = buf.Bytes() log.L(ctx).Tracef("encodeData(%s, %T): %s", typeName, v, encoded) return encoded, nil } -func hashStruct(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (ethtypes.HexBytes0xPrefix, error) { +func hashStruct(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (result ethtypes.HexBytes0xPrefix, err error) { encoded, err := encodeData(ctx, typeName, v, allTypes, breadcrumbs) if err != nil { return nil, err } - hashed := keccak256(encoded) - log.L(ctx).Tracef("hashStruct(%s): %s", typeName, hashed) - return hashed, nil + if encoded == nil { + // special rule for a nil value - we don't even include the type info, just return a nil bytes array + bytes32Enc, _ := abiElementalType(ctx, "bytes32") + encoded, _ = abiEncode(ctx, bytes32Enc, "0x0000000000000000000000000000000000000000000000000000000000000000", breadcrumbs) + result = encoded + } else { + result = keccak256(encoded) + } + log.L(ctx).Tracef("hashStruct(%s): %s", typeName, result) + return result, nil } func encodeElement(ctx context.Context, typeName string, v interface{}, allTypes TypeSet, breadcrumbs string) (ethtypes.HexBytes0xPrefix, error) { diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index a5af66c6..a077948d 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -25,31 +25,9 @@ import ( "github.com/stretchr/testify/assert" ) -const PersonType = `[ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } -]` - -const MailType = `[ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } -]` +const PersonType = `[{"name": "name","type": "string"},{"name": "wallet","type": "address"}]` + +const MailType = `[{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string"}]` func TestMessage_ExampleFromEIP712Spec(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) @@ -75,13 +53,13 @@ func TestMessage_ExampleFromEIP712Spec(t *testing.T) { "type": "address" } ], - "Person": `+PersonType+`, - "Mail": `+MailType+` + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string"}] }, "primaryType": "Mail", "domain": { "name": "Ether Mail", - "version": "1", + "version": "V4", "chainId": 1, "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" }, @@ -102,7 +80,7 @@ func TestMessage_ExampleFromEIP712Spec(t *testing.T) { ctx := context.Background() ed, err := EncodeTypedDataV4(ctx, &p) assert.NoError(t, err) - assert.Equal(t, "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2", ed.String()) + assert.Equal(t, "0xde26f53b35dd5ffdc13f8297e5cc7bbcb1a04bf33803bd2bf4a45eb251360cb8", ed.String()) } func TestMessage_EmptyMessage(t *testing.T) { @@ -127,8 +105,8 @@ func TestMessage_EmptyDomain(t *testing.T) { var p SignTypedDataPayload err := json.Unmarshal([]byte(`{ "types": { - "Person": `+PersonType+`, - "Mail": `+MailType+` + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string"}] }, "primaryType": "Mail", "message": { @@ -150,3 +128,27 @@ func TestMessage_EmptyDomain(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "0x25c3d40a39e639a4d0b6e4d2ace5e1281e039c88494d97d8d08f99a6ea75d775", ed.String()) } + +func TestMessage_NilReference(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": "Hello, Bob!" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0x326faa52849c078e0e04abe863b29fc28d9d2885d2c4b515fcfb7ba1fac30534", ed.String()) +} From 64b28eeb527ee70ceb02120cfece7ce792766e2e Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 16:51:59 -0400 Subject: [PATCH 07/28] Updated tests on ABI->TypedData interface Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_error_messges.go | 1 + pkg/eip712/abi_to_typed_data.go | 9 +- pkg/eip712/abi_to_typed_data_test.go | 284 ++++++++++++++++++------ pkg/eip712/typed_data_v4.go | 130 +---------- pkg/eip712/typed_data_v4_test.go | 189 ++++++++++++++++ 5 files changed, 417 insertions(+), 196 deletions(-) diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index f48fa7a8..0beb6df0 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -94,4 +94,5 @@ var ( MsgEIP712ValueNotArray = ffe("FF22075", "Value for '%s' not an array (%T)") MsgEIP712InvalidArrayLen = ffe("FF22076", "Value for '%s' must have %d entries (found %d)") MsgEIP712Version4Required = ffe("FF22077", "Domain version must be 'V4': %s") + MsgEIP712TypeNotFound = ffe("FF22078", "Type '%s' not found in type map") ) diff --git a/pkg/eip712/abi_to_typed_data.go b/pkg/eip712/abi_to_typed_data.go index 06c052a2..70b35c98 100644 --- a/pkg/eip712/abi_to_typed_data.go +++ b/pkg/eip712/abi_to_typed_data.go @@ -26,7 +26,7 @@ import ( "github.com/hyperledger/firefly-signer/pkg/abi" ) -var internalTypeStructExtractor = regexp.MustCompile(`^struct (.*\.)?([^.]+)$`) +var internalTypeStructExtractor = regexp.MustCompile(`^struct (.*\.)?([^.\[\]]+)(\[\d*\])*$`) // Convert an ABI tuple definition, into the EIP-712 structure that's embedded into the // "eth_signTypedData" signing request payload. It's a much simpler structure that @@ -54,7 +54,7 @@ func ABItoTypedDataV4(ctx context.Context, tc abi.TypeComponent) (primaryType st func mapABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { switch tc.ComponentType() { case abi.TupleComponent: - return tc.Parameter().Type, nil + return extractSolidityTypeName(ctx, tc.Parameter()) case abi.DynamicArrayComponent, abi.FixedArrayComponent: child, err := mapABIType(ctx, tc.ArrayChild()) if err != nil { @@ -76,11 +76,8 @@ func mapElementaryABIType(ctx context.Context, tc abi.TypeComponent) (string, er return "", i18n.NewError(ctx, signermsgs.MsgNotElementary, tc) } switch et.BaseType() { - case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeString: + case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeString, abi.BaseTypeInt, abi.BaseTypeUInt: // Types that need no transposition - return string(et.BaseType()), nil - case abi.BaseTypeInt, abi.BaseTypeUInt: - // Types with supported suffixes - note ABI package sorts alias resolution for us return string(et.BaseType()) + tc.ElementarySuffix(), nil case abi.BaseTypeBytes: // Bytes is special diff --git a/pkg/eip712/abi_to_typed_data_test.go b/pkg/eip712/abi_to_typed_data_test.go index f9c99ed3..2fdb3d1c 100644 --- a/pkg/eip712/abi_to_typed_data_test.go +++ b/pkg/eip712/abi_to_typed_data_test.go @@ -16,86 +16,226 @@ package eip712 -// func TestSimpleStruct(t *testing.T) { +import ( + "context" + "encoding/json" + "testing" -// // struct Mail { -// // address from; -// // address to; -// // string contents; -// // } -// mailABI := []byte(`{ -// "components": [ -// { -// "internalType": "address", -// "name": "from", -// "type": "address" -// }, -// { -// "internalType": "address", -// "name": "to", -// "type": "address" -// }, -// { -// "internalType": "string", -// "name": "contents", -// "type": "string" -// } -// ], -// "internalType": "struct EIP712Examples.Mail", -// "name": "", -// "type": "tuple" -// }`) -// var abiElem abi.Parameter -// err := json.Unmarshal(mailABI, &abiElem) -// assert.NoError(t, err) + "github.com/hyperledger/firefly-signer/pkg/abi" + "github.com/stretchr/testify/assert" +) -// tc, err := abiElem.TypeComponentTree() -// assert.NoError(t, err) +func TestSimpleStruct(t *testing.T) { -// pt, ts, err := ABItoEIP712TypeSet(context.Background(), tc) -// assert.NoError(t, err) -// assert.Equal(t, "Mail", pt) -// assert.Equal(t, TypeSet{ -// "Mail": Type{ -// { -// Name: "from", -// Type: "address", -// }, -// { -// Name: "to", -// Type: "address", -// }, -// { -// Name: "contents", -// Type: "string", -// }, -// }, -// }, ts) + // // Extracted from ABI in this solidity: + // // solc --combined-json abi eip712_examples.sol > eip712_examples.json + // pragma solidity ^0.8.0; + // contract EIP712Examples { + // struct Person { + // string name; + // address wallet; + // } + // struct Mail { + // Person from; + // Person to; + // string contents; + // } + // constructor() {} + // function mail() public pure returns (Mail memory) { + // return Mail(Person("", address(0)), Person("", address(0)), ""); + // } + // } + mailABI := []byte(`{ + "components": [ + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "internalType": "struct EIP712Examples.Person", + "name": "from", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + } + ], + "internalType": "struct EIP712Examples.Person", + "name": "to", + "type": "tuple" + }, + { + "internalType": "string", + "name": "contents", + "type": "string" + } + ], + "internalType": "struct EIP712Examples.Mail", + "name": "", + "type": "tuple" + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) -// cv, err := tc.ParseExternal([]interface{}{ -// "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", -// "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", -// "Hello, Bob!", -// }) -// assert.NoError(t, err) + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) -// eip712Type, err := EncodeTypeABI(context.Background(), cv.Component) -// assert.NoError(t, err) + pt, ts, err := ABItoTypedDataV4(context.Background(), tc) + assert.NoError(t, err) + assert.Equal(t, "Mail", pt) + assert.Equal(t, TypeSet{ + "Person": Type{ + { + Name: "name", + Type: "string", + }, + { + Name: "wallet", + Type: "address", + }, + }, + "Mail": Type{ + { + Name: "from", + Type: "Person", + }, + { + Name: "to", + Type: "Person", + }, + { + Name: "contents", + Type: "string", + }, + }, + }, ts) -// assert.Equal(t, "Mail(address from,address to,string contents)", eip712Type) +} -// encodedData, err := EncodeDataABI(context.Background(), cv) -// assert.NoError(t, err) +func TestArrays(t *testing.T) { -// assert.Equal(t, `0x`+ -// `000000000000000000000000cd2a3d9f938e13cd947ec05abc7fe734df8dd826`+ // uint160 address -// `000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb`+ // uint160 address -// `48656c6c6f2c20426f6221`, // ASCII: "Hello, Bob!" -// encodedData.String()) + // // solc --combined-json abi eip712_examples.sol > eip712_examples.json + // pragma solidity ^0.8.0; + // contract EIP712Examples { + // struct TopLevel { + // string[] strings; + // int32[5] ints; + // bool[][] multidim; + // Nested[] nested; + // } + // struct Nested { + // uint[] unaliased; + // bytes[5] bytestrings; + // bytes1[8] eightbytes; + // } + // constructor() {} + // function mail(TopLevel memory) public pure {} + // } + mailABI := []byte(`{ + "components": [ + { + "internalType": "string[]", + "name": "strings", + "type": "string[]" + }, + { + "internalType": "int32[5]", + "name": "ints", + "type": "int32[5]" + }, + { + "internalType": "bool[][]", + "name": "multidim", + "type": "bool[][]" + }, + { + "components": [ + { + "internalType": "uint256[]", + "name": "unaliased", + "type": "uint256[]" + }, + { + "internalType": "bytes[5]", + "name": "bytestrings", + "type": "bytes[5]" + }, + { + "internalType": "bytes1[8]", + "name": "eightbytes", + "type": "bytes1[8]" + } + ], + "internalType": "struct EIP712Examples.Nested[]", + "name": "nested", + "type": "tuple[]" + } + ], + "internalType": "struct EIP712Examples.TopLevel", + "name": "", + "type": "tuple" + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) -// hash, err := HashStructABI(context.Background(), cv) -// assert.NoError(t, err) + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) -// assert.Equal(t, hashString(string(hashString(eip712Type))+string(encodedData)).String(), hash.String()) + pt, ts, err := ABItoTypedDataV4(context.Background(), tc) + assert.NoError(t, err) + assert.Equal(t, "TopLevel", pt) + assert.Equal(t, TypeSet{ + "Nested": Type{ + { + Name: "unaliased", + Type: "uint256[]", + }, + { + Name: "bytestrings", + Type: "bytes[5]", + }, + { + Name: "eightbytes", + Type: "bytes1[8]", + }, + }, + "TopLevel": Type{ + { + Name: "strings", + Type: "string[]", + }, + { + Name: "ints", + Type: "int32[5]", + }, + { + Name: "multidim", + Type: "bool[][]", + }, + { + Name: "nested", + Type: "Nested[]", + }, + }, + }, ts) -// } +} diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index 223c244e..68547b0b 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -151,7 +151,7 @@ func idxCrumb(breadcrumbs string, idx int) string { return fmt.Sprintf("%s[%d]", breadcrumbs, idx) } -func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) (Type, error) { +func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) error { // We're not interested in array semantics here iBracket := strings.Index(typeName, "[") if iBracket >= 0 { @@ -160,17 +160,17 @@ func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) (Type, e // See if it's a defined structure type t, ok := allTypes[typeName] if !ok { - return nil, nil + return nil } if typeSet[typeName] == nil { typeSet[typeName] = t for _, tm := range t { - if _, err := addNestedTypes(tm.Type, allTypes, typeSet); err != nil { - return nil, err + if err := addNestedTypes(tm.Type, allTypes, typeSet); err != nil { + return err } } } - return t, nil + return nil } func keccak256(b []byte) ethtypes.HexBytes0xPrefix { @@ -184,8 +184,13 @@ func keccak256(b []byte) ethtypes.HexBytes0xPrefix { // > If the struct type references other struct types (and these in turn reference even more struct types), // > then the set of referenced struct types is collected, sorted by name and appended to the encoding. func encodeType(ctx context.Context, typeName string, allTypes TypeSet) (Type, string, error) { + t := allTypes[typeName] + if t == nil { + return nil, "", i18n.NewError(ctx, signermsgs.MsgEIP712TypeNotFound, typeName) + } + depSet := make(TypeSet) - t, err := addNestedTypes(typeName, allTypes, depSet) + err := addNestedTypes(typeName, allTypes, depSet) if err != nil { return nil, "", err } @@ -355,116 +360,5 @@ func hashArray(ctx context.Context, typeName string, allTypes TypeSet, v interfa } buf.Write(b) } - return buf.Bytes(), nil + return keccak256(buf.Bytes()), nil } - -// func (t *TypeMember) Encode() string { -// return t.Type + " " + t.Name -// } - -// func (t *Type) Encode() string { -// params := make([]string, len(t.Members)) -// for i, member := range t.Members { -// params[i] = member.Encode() -// } -// return t.Name + "(" + strings.Join(params, ",") + ")" -// } - -// func (t *Type) Hash() []byte { -// hash := sha3.NewLegacyKeccak256() -// hash.Write([]byte(t.Encode())) -// return hash.Sum(nil) -// } - -// func encodeData(members []*TypeMember, data map[string]interface{}) ([]byte, error) { -// var result []byte -// for _, member := range members { -// val := data[member.Name] -// encodedVal, err := encodeValue(member, val) -// if err != nil { -// return nil, err -// } -// result = append(result, encodedVal...) -// } -// return result, nil -// } - -// func encodeValue(m *TypeMember, val interface{}) ([]byte, error) { -// switch m.Type { -// case "bool": -// return encodeBool(val.(bool)), nil -// case "address": -// return encodeAddress(val.(string)), nil -// case "uint256": -// return encodeUintString(val.(string)), nil -// case "string": -// return encodeString(val.(string)), nil -// default: -// return nil, fmt.Errorf("unsupported type: %s", m.Type) -// } -// } - -// func encodeBool(val bool) []byte { -// var i *big.Int -// if val { -// i = big.NewInt(1) -// } else { -// i = big.NewInt(0) -// } -// return encodeUint(i) -// } - -// func encodeAddress(val string) []byte { -// i := big.NewInt(0) -// i.SetString(val, 16) -// return encodeUint(i) -// } - -// func encodeUint(val *big.Int) []byte { -// data := make([]byte, 32) -// _ = val.FillBytes(data) -// return data -// } - -// func encodeUintString(val string) []byte { -// i := big.NewInt(0) -// i.SetString(val, 10) -// return encodeUint(i) -// } - -// func encodeString(val string) []byte { -// return encodeDynamicBytes([]byte(val)) -// } - -// func encodeDynamicBytes(val []byte) []byte { -// hash := sha3.NewLegacyKeccak256() -// hash.Write(val) -// return hash.Sum(nil) -// } - -// func hashStruct(t *Type, data map[string]interface{}) ([]byte, error) { -// encodedData, err := encodeData(t.Members, data) -// if err != nil { -// return nil, err -// } -// hash := sha3.NewLegacyKeccak256() -// hash.Write(t.Hash()) -// hash.Write(encodedData) -// return hash.Sum(nil), nil -// } - -// func EncodeTypedData(domain map[string]interface{}, t *Type, message map[string]interface{}) ([]byte, error) { -// domainSeparator, err := hashStruct(&EIP712DomainType, domain) -// if err != nil { -// return nil, err -// } -// messageHash, err := hashStruct(t, message) -// if err != nil { -// return nil, err -// } -// hash := sha3.NewLegacyKeccak256() -// hash.Write([]byte{0x19, 0x01}) -// hash.Write(domainSeparator) -// hash.Write(messageHash) -// return hash.Sum(nil), nil -// } diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index a077948d..a39be336 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -152,3 +152,192 @@ func TestMessage_NilReference(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "0x326faa52849c078e0e04abe863b29fc28d9d2885d2c4b515fcfb7ba1fac30534", ed.String()) } + +func TestMessage_BytesString(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "bytes"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": "0x48656C6C6F2C20426F6221" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0x3e4282c3bc7b7d6df14ef1c1c90f7bef0516134f4ca08d56eb38b061e5632a6b", ed.String()) +} + +func TestMessage_Bytes11(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "bytes11"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": "0x48656C6C6F2C20426F6221" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0xb13b01acae69dbd0fef3568f1b060a692247aa207609d008f344c8cd7f664220", ed.String()) +} + +func TestMessage_StringArray(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string[]"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": ["Hello,", "Bob!"] + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0xd0ac411802ea14e4e64eeed229227be1bf2909f0a30bda74c79447dfbf2f5431", ed.String()) +} + +func TestMessage_StringArrayArray(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string[][]"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": [ + ["Hello,", "Bob!"], + ["How,", "do"] + ] + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0x88454de00616bf6b3697b55281de2e8fb542b3997c397ad70e0c8f8f72d164f0", ed.String()) +} + +func TestMessage_FixedStringArray(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], + "Mail": [{"name": "from","type": "Person"},{"name": "to","type": "Person"},{"name": "contents","type": "string[2]"}] + }, + "primaryType": "Mail", + "message": { + "from": null, + "to": null, + "contents": ["Hello,", "Bob!"] + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0xb1bf2c8635345d6fc3e86e493180f96043548ac761683a4d069725f08a6ea2bf", ed.String()) +} + +func TestMessage_StructArray(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "AllTheTypes": [ + { + "name": "i32", + "type": "int32" + }, + { + "name": "i256", + "type": "int256" + }, + { + "name": "ui32", + "type": "uint32" + }, + { + "name": "ui256", + "type": "uint256" + }, + { + "name": "t", + "type": "bool" + }, + { + "name": "b16", + "type": "bytes16" + }, + { + "name": "b32", + "type": "bytes32" + }, + { + "name": "b", + "type": "bytes" + }, + { + "name": "s", + "type": "string" + } + ] + }, + "primaryType": "AllTheTypes", + "message": { + "i32": -12345, + "i256": "-12345", + "ui32": "0x3039", + "ui256": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "t": true, + "b16": "0x000102030405060708090a0b0c0f0e0f", + "b32": "0x000102030405060708090a0b0c0f0e0f000102030405060708090a0b0c0f0e0f", + "b": "0xfeedbeef", + "s": "Hello World!" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0x651579f58b3a8c79ba668e0f5d83e1c9f6e2715586dc11c62696ec376b595a00", ed.String()) +} From 9bd1df9a0ff43e8d7bdac1737452c8c1c3918ec2 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 22:29:57 -0400 Subject: [PATCH 08/28] Work through tests on encoding Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_error_messges.go | 2 +- pkg/eip712/abi_to_typed_data.go | 4 +- pkg/eip712/abi_to_typed_data_test.go | 183 ++++++++++++++- pkg/eip712/typed_data_v4.go | 45 ++-- pkg/eip712/typed_data_v4_test.go | 286 ++++++++++++++++++++++++ 5 files changed, 489 insertions(+), 31 deletions(-) diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index 0beb6df0..f9ab3cbb 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -87,7 +87,7 @@ var ( MsgEIP712UnsupportedStrType = ffe("FF22068", "Unsupported type: %s") MsgEIP712UnsupportedABIType = ffe("FF22069", "ABI type not supported by EIP-712 encoding: %s") MsgNotABIElementaryType = ffe("FF22070", "Not elementary type: %s") - MsgEIP712PrimaryNotTuple = ffe("FF22071", "Type primary type must be a struct: %s") + MsgEIP712PrimaryNotTuple = ffe("FF22071", "Type primary type must be a struct/tuple: %s") MsgEIP712BadInternalType = ffe("FF22072", "Failed to extract struct name from ABI internalType '%s'") MsgEIP712ValueNotMap = ffe("FF22073", "Value for struct '%s' not a map (%T)") MsgEIP712InvalidArraySuffix = ffe("FF22074", "Type '%s' has invalid array suffix") diff --git a/pkg/eip712/abi_to_typed_data.go b/pkg/eip712/abi_to_typed_data.go index 70b35c98..796daede 100644 --- a/pkg/eip712/abi_to_typed_data.go +++ b/pkg/eip712/abi_to_typed_data.go @@ -71,10 +71,10 @@ func mapABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { // Maps one of the parsed ABI elementary types to an EIP-712 elementary type func mapElementaryABIType(ctx context.Context, tc abi.TypeComponent) (string, error) { - et := tc.ElementaryType() - if et == nil { + if tc.ComponentType() != abi.ElementaryComponent { return "", i18n.NewError(ctx, signermsgs.MsgNotElementary, tc) } + et := tc.ElementaryType() switch et.BaseType() { case abi.BaseTypeAddress, abi.BaseTypeBool, abi.BaseTypeString, abi.BaseTypeInt, abi.BaseTypeUInt: // Types that need no transposition diff --git a/pkg/eip712/abi_to_typed_data_test.go b/pkg/eip712/abi_to_typed_data_test.go index 2fdb3d1c..5b0debdb 100644 --- a/pkg/eip712/abi_to_typed_data_test.go +++ b/pkg/eip712/abi_to_typed_data_test.go @@ -25,7 +25,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSimpleStruct(t *testing.T) { +func TestABISimpleStruct(t *testing.T) { // // Extracted from ABI in this solidity: // // solc --combined-json abi eip712_examples.sol > eip712_examples.json @@ -130,7 +130,7 @@ func TestSimpleStruct(t *testing.T) { } -func TestArrays(t *testing.T) { +func TestABIArraysAndTypes(t *testing.T) { // // solc --combined-json abi eip712_examples.sol > eip712_examples.json // pragma solidity ^0.8.0; @@ -239,3 +239,182 @@ func TestArrays(t *testing.T) { }, ts) } + +func TestABINotTuple(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "uint256" + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, _, err = ABItoTypedDataV4(context.Background(), tc) + assert.Regexp(t, "FF22071", err) + +} + +func TestABINoInternalType(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple", + "components": [] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, _, err = ABItoTypedDataV4(context.Background(), tc) + assert.Regexp(t, "FF22072", err) + +} + +func TestABINoInternalTypeChild(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple", + "internalType": "struct MyType", + "components": [ + { + "name": "noInternal", + "type": "tuple", + "components": [] + } + ] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, _, err = ABItoTypedDataV4(context.Background(), tc) + assert.Regexp(t, "FF22072", err) + +} + +func TestMapElementaryABITypeNonElementary(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple", + "internalType": "struct MyType", + "components": [] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, err = mapElementaryABIType(context.Background(), tc) + assert.Regexp(t, "FF22067", err) + +} + +func TestMapABITypeBadArray(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple[]", + "components": [] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, err = mapABIType(context.Background(), tc) + assert.Regexp(t, "FF22072", err) + +} + +func TestAddABITypesFailToExtractStructName(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple" + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + err = addABITypes(context.Background(), tc, TypeSet{}) + assert.Regexp(t, "FF22072", err) + +} + +func TestABIUnsupportedType(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple", + "internalType": "struct MyType", + "components": [ + { + "name": "fixed", + "type": "fixed256x18" + } + ] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, _, err = ABItoTypedDataV4(context.Background(), tc) + assert.Regexp(t, "FF22069", err) + +} + +func TestABINestedError(t *testing.T) { + + mailABI := []byte(`{ + "name": "", + "type": "tuple", + "internalType": "struct MyType", + "components": [ + { + "name": "nested1", + "type": "tuple", + "internalType": "struct Nested1", + "components": [ + { + "name": "nested1", + "type": "tuple", + "internalType": "", + "components": [] + } + ] + } + ] + }`) + var abiElem abi.Parameter + err := json.Unmarshal(mailABI, &abiElem) + assert.NoError(t, err) + + tc, err := abiElem.TypeComponentTree() + assert.NoError(t, err) + + _, _, err = ABItoTypedDataV4(context.Background(), tc) + assert.Regexp(t, "FF22072", err) + +} diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index 68547b0b..98b6b45f 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -59,6 +59,9 @@ const EIP712Domain = "EIP712Domain" func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (encoded ethtypes.HexBytes0xPrefix, err error) { // Add empty EIP712Domain type specification if missing + if payload.Types == nil { + payload.Types = TypeSet{} + } if _, found := payload.Types[EIP712Domain]; !found { payload.Types[EIP712Domain] = Type{} } @@ -144,14 +147,17 @@ func (t Type) Encode(name string) string { } func nextCrumb(breadcrumbs string, name string) string { - return breadcrumbs + "." + name + if len(breadcrumbs) > 0 { + return breadcrumbs + "." + name + } + return name } func idxCrumb(breadcrumbs string, idx int) string { return fmt.Sprintf("%s[%d]", breadcrumbs, idx) } -func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) error { +func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) { // We're not interested in array semantics here iBracket := strings.Index(typeName, "[") if iBracket >= 0 { @@ -159,18 +165,12 @@ func addNestedTypes(typeName string, allTypes TypeSet, typeSet TypeSet) error { } // See if it's a defined structure type t, ok := allTypes[typeName] - if !ok { - return nil - } - if typeSet[typeName] == nil { + if ok && typeSet[typeName] == nil { typeSet[typeName] = t for _, tm := range t { - if err := addNestedTypes(tm.Type, allTypes, typeSet); err != nil { - return err - } + addNestedTypes(tm.Type, allTypes, typeSet) } } - return nil } func keccak256(b []byte) ethtypes.HexBytes0xPrefix { @@ -190,10 +190,7 @@ func encodeType(ctx context.Context, typeName string, allTypes TypeSet) (Type, s } depSet := make(TypeSet) - err := addNestedTypes(typeName, allTypes, depSet) - if err != nil { - return nil, "", err - } + addNestedTypes(typeName, allTypes, depSet) typeEncoded := depSet.Encode(typeName) log.L(ctx).Tracef("encodeType(%s): %s", typeName, typeEncoded) return t, typeEncoded, nil @@ -241,7 +238,7 @@ func hashStruct(ctx context.Context, typeName string, v interface{}, allTypes Ty } if encoded == nil { // special rule for a nil value - we don't even include the type info, just return a nil bytes array - bytes32Enc, _ := abiElementalType(ctx, "bytes32") + bytes32Enc, _ := abiElementaryType(ctx, "bytes32") encoded, _ = abiEncode(ctx, bytes32Enc, "0x0000000000000000000000000000000000000000000000000000000000000000", breadcrumbs) result = encoded } else { @@ -259,8 +256,8 @@ func encodeElement(ctx context.Context, typeName string, v interface{}, allTypes // recurse into the struct return hashStruct(ctx, typeName, v, allTypes, breadcrumbs) } - // Need to process based on the elemental type - tc, err := abiElementalType(ctx, typeName) + // Need to process based on the Elementary type + tc, err := abiElementaryType(ctx, typeName) if err != nil { return nil, err } @@ -295,29 +292,25 @@ func encodeElement(ctx context.Context, typeName string, v interface{}, allTypes } } -func abiElementalType(ctx context.Context, typeName string) (abi.TypeComponent, error) { +func abiElementaryType(ctx context.Context, typeName string) (abi.TypeComponent, error) { p := &abi.Parameter{Type: typeName} tc, err := p.TypeComponentTreeCtx(ctx) if err != nil { return nil, err } - - // Check it's one of the EIP-712 supported types - et := tc.ElementaryType() - if et == nil { + if tc.ComponentType() != abi.ElementaryComponent { return nil, i18n.NewError(ctx, signermsgs.MsgNotElementary, tc) } return tc, nil } func abiEncode(ctx context.Context, tc abi.TypeComponent, v interface{}, breadcrumbs string) (b ethtypes.HexBytes0xPrefix, err error) { - // Re-use the ABI function to parse the input value for elemental types. + // Re-use the ABI function to parse the input value for Elementary types. // (we weren't able to do this for structs/tuples and arrays, due to EIP-712 specifics) cv, err := tc.ParseExternalDesc(ctx, v, breadcrumbs) - if err != nil { - return nil, err + if err == nil { + b, err = cv.EncodeABIDataCtx(ctx) } - b, err = cv.EncodeABIDataCtx(ctx) if err != nil { return nil, err } diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index a39be336..7006f8d4 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -341,3 +341,289 @@ func TestMessage_StructArray(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "0x651579f58b3a8c79ba668e0f5d83e1c9f6e2715586dc11c62696ec376b595a00", ed.String()) } + +func TestInvalidVersion(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "primaryType": "EIP712Domain", + "domain": {"version": "V2"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22077", err) +} + +func TestInvalidDomain(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "EIP712Domain": [{"name":"name","type":"string"}] + }, + "primaryType": "EIP712Domain", + "domain": {"name": ["not","a","string"]} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22032", err) +} + +func TestInvalidPrimaryType(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"name","type":"string"}] + }, + "primaryType": "MyType", + "message": {"name": ["not","a","string"]} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22032", err) +} + +func TestMissingPrimaryType(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType2": [{"name":"name","type":"string"}] + }, + "primaryType": "MyType1", + "message": {"name": "Bob"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22078", err) +} + +func TestSecondaryTypeNotMap(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType1": [{"name":"t2","type":"MyType2"}], + "MyType2": [{"name":"name","type":"string"}] + }, + "primaryType": "MyType1", + "message": {"t2": "this is not a map"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22073", err) +} + +func TestTypeInvalid(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"number","type":"int2560"}] + }, + "primaryType": "MyType", + "message": {"number": 12345} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22028", err) +} + +func TestIntValueInvalid(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"number","type":"int256"}] + }, + "primaryType": "MyType", + "message": {"number": "!!! not a number"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22030", err) +} + +func TestBytesValueInvalid(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"bite","type":"bytes"}] + }, + "primaryType": "MyType", + "message": {"bite": -99} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22034", err) +} + +func TestFixedNotSupported(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"unmovable","type":"fixed256x18"}] + }, + "primaryType": "MyType", + "message": {"unmovable": "1.23"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22069", err) +} + +func TestTupleNotSupported(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"abi_struct","type":"tuple"}] + }, + "primaryType": "MyType", + "message": {"abi_struct": {}} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22067", err) +} + +func TestAddressInvalid(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"addr","type":"address"}] + }, + "primaryType": "MyType", + "message": {"addr": "not here"} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22034", err) +} + +func TestArrayBadSuffix(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"wonky","type":"int256]"}] + }, + "primaryType": "MyType", + "message": {"wonky": 1} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22074", err) +} + +func TestArrayFieldNonArrayValue(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"many","type":"int256[]"}] + }, + "primaryType": "MyType", + "message": {"many": 1} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22075", err) +} + +func TestArrayFieldBadSize(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"many","type":"int256[n]"}] + }, + "primaryType": "MyType", + "message": {"many": [1]} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22074", err) +} + +func TestArrayFieldSizeMismatch(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"many","type":"int256[2]"}] + }, + "primaryType": "MyType", + "message": {"many": [1]} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22076", err) +} + +func TestArrayValueInvalid(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p SignTypedDataPayload + err := json.Unmarshal([]byte(`{ + "types": { + "MyType": [{"name":"many","type":"int256[]"}] + }, + "primaryType": "MyType", + "message": {"many": ["not a number"]} + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + _, err = EncodeTypedDataV4(ctx, &p) + assert.Regexp(t, "FF22030", err) +} From e47d7f89552c1d6e8d83fe59ea6339512ee7591f Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 23:29:35 -0400 Subject: [PATCH 09/28] Work through changes in signer interface Signed-off-by: Peter Broadhurst --- .github/workflows/go.yml | 2 +- .golangci.yml | 3 - Dockerfile | 4 +- config.md | 2 + go.mod | 87 +++++----- go.sum | 288 +++++++++++++------------------ mocks/ethsignermocks/wallet.go | 11 +- mocks/rpcbackendmocks/backend.go | 11 +- mocks/rpcservermocks/server.go | 11 +- mocks/secp256k1mocks/signer.go | 11 +- pkg/eip712/typed_data_v4.go | 23 +-- pkg/eip712/typed_data_v4_test.go | 52 +++--- pkg/ethsigner/typed_data.go | 43 +++++ pkg/ethsigner/typed_data_test.go | 97 +++++++++++ pkg/ethsigner/wallet.go | 8 +- pkg/fswallet/fswallet.go | 19 +- pkg/fswallet/fswallet_test.go | 62 +++++-- pkg/rpcbackend/backend.go | 12 +- 18 files changed, 440 insertions(+), 306 deletions(-) create mode 100644 pkg/ethsigner/typed_data.go create mode 100644 pkg/ethsigner/typed_data_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 3000e537..1380bf42 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.19 + go-version: '1.20' - name: Build and Test run: make diff --git a/.golangci.yml b/.golangci.yml index 4d2b3a35..57b54fa5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -32,7 +32,6 @@ linters: disable-all: false enable: - bodyclose - - deadcode - dogsled - errcheck - goconst @@ -50,10 +49,8 @@ linters: - nakedret - revive - staticcheck - - structcheck - stylecheck - typecheck - unconvert - unparam - unused - - varcheck diff --git a/Dockerfile b/Dockerfile index 4b15882b..504fc332 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ -FROM golang:1.19-buster AS builder +FROM golang:1.20-bullseye AS builder ARG BUILD_VERSION ENV BUILD_VERSION=${BUILD_VERSION} ADD . /ffsigner WORKDIR /ffsigner RUN make -FROM debian:buster-slim +FROM debian:bullseye-slim WORKDIR /ffsigner RUN apt update -y \ && apt install -y curl jq \ diff --git a/config.md b/config.md index a83cd8b0..312930e7 100644 --- a/config.md +++ b/config.md @@ -26,6 +26,7 @@ nav_order: 2 |expectContinueTimeout|See [ExpectContinueTimeout in the Go docs](https://pkg.go.dev/net/http#Transport)|[`time.Duration`](https://pkg.go.dev/time#Duration)|`1s` |headers|Adds custom headers to HTTP requests|`map[string]string`|`` |idleTimeout|The max duration to hold a HTTP keepalive connection between calls|[`time.Duration`](https://pkg.go.dev/time#Duration)|`475ms` +|maxConnsPerHost|The max number of connections, per unique hostname. Zero means no limit|`int`|`0` |maxIdleConns|The max number of idle connections to hold pooled|`int`|`100` |passthroughHeadersEnabled|Enable passing through the set of allowed HTTP request headers|`boolean`|`false` |requestTimeout|The maximum amount of time that a request is allowed to remain open|[`time.Duration`](https://pkg.go.dev/time#Duration)|`30s` @@ -69,6 +70,7 @@ nav_order: 2 |Key|Description|Type|Default Value| |---|-----------|----|-------------| +|connectionTimeout|The amount of time to wait while establishing a connection (or auto-reconnection)|[`time.Duration`](https://pkg.go.dev/time#Duration)|`45s` |heartbeatInterval|The amount of time to wait between heartbeat signals on the WebSocket connection|[`time.Duration`](https://pkg.go.dev/time#Duration)|`30s` |initialConnectAttempts|The number of attempts FireFly will make to connect to the WebSocket when starting up, before failing|`int`|`5` |path|The WebSocket sever URL to which FireFly should connect|WebSocket URL `string`|`` diff --git a/go.mod b/go.mod index 7aa04e38..e4da5aa5 100644 --- a/go.mod +++ b/go.mod @@ -1,75 +1,78 @@ module github.com/hyperledger/firefly-signer -go 1.19 +go 1.20 require ( - github.com/btcsuite/btcd/btcec/v2 v2.1.3 - github.com/fsnotify/fsnotify v1.6.0 - github.com/go-resty/resty/v2 v2.7.0 + github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/fsnotify/fsnotify v1.7.0 + github.com/go-resty/resty/v2 v2.10.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.2.11 + github.com/hyperledger/firefly-common v1.3.0 github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 - github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 - github.com/sirupsen/logrus v1.9.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.14.0 - github.com/stretchr/testify v1.8.1 - golang.org/x/crypto v0.1.0 - golang.org/x/text v0.8.0 + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.17.0 + github.com/stretchr/testify v1.8.4 + golang.org/x/crypto v0.14.0 + golang.org/x/text v0.13.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/aidarkhanov/nanoid v1.0.8 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/getkin/kin-openapi v0.116.0 // indirect + github.com/getkin/kin-openapi v0.120.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.11.1 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.30.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/rs/cors v1.8.2 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rs/cors v1.10.1 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect + github.com/stretchr/objx v0.5.1 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect gitlab.com/hfuss/mux-prometheus v0.0.5 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/term v0.6.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 81cd2629..4e995369 100644 --- a/go.sum +++ b/go.sum @@ -36,27 +36,18 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/aidarkhanov/nanoid v1.0.8 h1:yxyJkgsEDFXP7+97vc6JevMcjyb03Zw+/9fqhlVXBXA= github.com/aidarkhanov/nanoid v1.0.8/go.mod h1:vadfZHT+m4uDhttg0yY4wW3GKtl2T6i4d2Age+45pYk= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= -github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0 h1:MSskdM4/xJYcFzy0altH/C/xHopifpWzHUi1JeVI34Q= +github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= +github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -66,12 +57,12 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -80,34 +71,24 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E= -github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= +github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -134,8 +115,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -149,7 +128,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -165,8 +143,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -178,147 +156,112 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hyperledger/firefly-common v1.2.11 h1:ePDHJtorKE6ss8PtoPlyqLb+cB0TDB7ziM85Gtyerqs= -github.com/hyperledger/firefly-common v1.2.11/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= +github.com/hyperledger/firefly-common v1.3.0 h1:eLFUJuPU8E5iZXYGHlXghQuN+opWG/qp7zvMKavKEPU= +github.com/hyperledger/firefly-common v1.3.0/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache v2.0.3+incompatible h1:j68C9tWOROiOLWTS/kCGg9IcJG+ACqn5+0+t8Oh83UU= github.com/karlseguin/ccache v2.0.3+incompatible/go.mod h1:CM9tNPzT6EdRh14+jiW8mEF9mkNZuuE51qmgGYUB93w= github.com/karlseguin/expect v1.0.8 h1:Bb0H6IgBWQpadY25UDNkYPDB9ITqK1xnSoZfAq362fw= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +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/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= -github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2 h1:zOYFITq/5SO7YOv39/Taw8s1skb0Py39K5V2XvCEP48= -github.com/santhosh-tekuri/jsonschema/v5 v5.0.2/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= @@ -327,6 +270,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/hfuss/mux-prometheus v0.0.5 h1:Kcqyiekx8W2dO1EHg+6wOL1F0cFNgRO1uCK18V31D0s= gitlab.com/hfuss/mux-prometheus v0.0.5/go.mod h1:xcedy8rVGr9TFgRu2urfGuh99B4NdfYdpE4aUMQ0dxA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -335,16 +279,18 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= 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= @@ -355,6 +301,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -378,9 +326,10 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -388,7 +337,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -411,10 +359,12 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -424,7 +374,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -435,13 +384,12 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -451,7 +399,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -464,8 +411,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -473,20 +418,25 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -494,11 +444,16 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -546,6 +501,8 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -639,26 +596,19 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/mocks/ethsignermocks/wallet.go b/mocks/ethsignermocks/wallet.go index b6136e67..58889f13 100644 --- a/mocks/ethsignermocks/wallet.go +++ b/mocks/ethsignermocks/wallet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.35.4. DO NOT EDIT. package ethsignermocks @@ -110,13 +110,12 @@ func (_m *Wallet) Sign(ctx context.Context, txn *ethsigner.Transaction, chainID return r0, r1 } -type mockConstructorTestingTNewWallet interface { +// NewWallet creates a new instance of Wallet. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewWallet(t interface { mock.TestingT Cleanup(func()) -} - -// NewWallet creates a new instance of Wallet. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewWallet(t mockConstructorTestingTNewWallet) *Wallet { +}) *Wallet { mock := &Wallet{} mock.Mock.Test(t) diff --git a/mocks/rpcbackendmocks/backend.go b/mocks/rpcbackendmocks/backend.go index e5865fa2..00bb48f4 100644 --- a/mocks/rpcbackendmocks/backend.go +++ b/mocks/rpcbackendmocks/backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.35.4. DO NOT EDIT. package rpcbackendmocks @@ -59,13 +59,12 @@ func (_m *Backend) SyncRequest(ctx context.Context, rpcReq *rpcbackend.RPCReques return r0, r1 } -type mockConstructorTestingTNewBackend interface { +// NewBackend creates a new instance of Backend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBackend(t interface { mock.TestingT Cleanup(func()) -} - -// NewBackend creates a new instance of Backend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBackend(t mockConstructorTestingTNewBackend) *Backend { +}) *Backend { mock := &Backend{} mock.Mock.Test(t) diff --git a/mocks/rpcservermocks/server.go b/mocks/rpcservermocks/server.go index 7ff7a0e5..02b12127 100644 --- a/mocks/rpcservermocks/server.go +++ b/mocks/rpcservermocks/server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.35.4. DO NOT EDIT. package rpcservermocks @@ -42,13 +42,12 @@ func (_m *Server) WaitStop() error { return r0 } -type mockConstructorTestingTNewServer interface { +// NewServer creates a new instance of Server. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServer(t interface { mock.TestingT Cleanup(func()) -} - -// NewServer creates a new instance of Server. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewServer(t mockConstructorTestingTNewServer) *Server { +}) *Server { mock := &Server{} mock.Mock.Test(t) diff --git a/mocks/secp256k1mocks/signer.go b/mocks/secp256k1mocks/signer.go index bab05349..5ab6e3b0 100644 --- a/mocks/secp256k1mocks/signer.go +++ b/mocks/secp256k1mocks/signer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.26.1. DO NOT EDIT. +// Code generated by mockery v2.35.4. DO NOT EDIT. package secp256k1mocks @@ -38,13 +38,12 @@ func (_m *Signer) Sign(message []byte) (*secp256k1.SignatureData, error) { return r0, r1 } -type mockConstructorTestingTNewSigner interface { +// NewSigner creates a new instance of Signer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSigner(t interface { mock.TestingT Cleanup(func()) -} - -// NewSigner creates a new instance of Signer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSigner(t mockConstructorTestingTNewSigner) *Signer { +}) *Signer { mock := &Signer{} mock.Mock.Test(t) diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index 98b6b45f..e468bf24 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -32,6 +32,13 @@ import ( "golang.org/x/crypto/sha3" ) +type TypedData struct { + Types TypeSet `ffstruct:"TypedData" json:"types"` + PrimaryType string `ffstruct:"TypedData" json:"primaryType"` + Domain map[string]interface{} `ffstruct:"TypedData" json:"domain"` + Message map[string]interface{} `ffstruct:"TypedData" json:"message"` +} + type TypeMember struct { Name string Type string @@ -39,25 +46,11 @@ type TypeMember struct { type Type []*TypeMember -type SignTypedDataPayload struct { - Types TypeSet `json:"types"` - PrimaryType string `json:"primaryType"` - Domain map[string]interface{} `json:"domain"` - Message map[string]interface{} `json:"message"` -} - type TypeSet map[string]Type -type Domain struct { - Name string `json:"name"` - Version string `json:"version"` - ChainID int64 `json:"chainId"` - VerifyingContract ethtypes.Address0xHex `json:"verifyingContract"` -} - const EIP712Domain = "EIP712Domain" -func EncodeTypedDataV4(ctx context.Context, payload *SignTypedDataPayload) (encoded ethtypes.HexBytes0xPrefix, err error) { +func EncodeTypedDataV4(ctx context.Context, payload *TypedData) (encoded ethtypes.HexBytes0xPrefix, err error) { // Add empty EIP712Domain type specification if missing if payload.Types == nil { payload.Types = TypeSet{} diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index 7006f8d4..b48fecda 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -32,7 +32,7 @@ const MailType = `[{"name": "from","type": "Person"},{"name": "to","type": "Pers func TestMessage_ExampleFromEIP712Spec(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "EIP712Domain": [ @@ -86,7 +86,7 @@ func TestMessage_ExampleFromEIP712Spec(t *testing.T) { func TestMessage_EmptyMessage(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": {}, "primaryType": "EIP712Domain" @@ -102,7 +102,7 @@ func TestMessage_EmptyMessage(t *testing.T) { func TestMessage_EmptyDomain(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -132,7 +132,7 @@ func TestMessage_EmptyDomain(t *testing.T) { func TestMessage_NilReference(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -156,7 +156,7 @@ func TestMessage_NilReference(t *testing.T) { func TestMessage_BytesString(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -180,7 +180,7 @@ func TestMessage_BytesString(t *testing.T) { func TestMessage_Bytes11(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -204,7 +204,7 @@ func TestMessage_Bytes11(t *testing.T) { func TestMessage_StringArray(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -228,7 +228,7 @@ func TestMessage_StringArray(t *testing.T) { func TestMessage_StringArrayArray(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -255,7 +255,7 @@ func TestMessage_StringArrayArray(t *testing.T) { func TestMessage_FixedStringArray(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "Person": [{"name": "name","type": "string"},{"name": "wallet","type": "address"}], @@ -279,7 +279,7 @@ func TestMessage_FixedStringArray(t *testing.T) { func TestMessage_StructArray(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "AllTheTypes": [ @@ -345,7 +345,7 @@ func TestMessage_StructArray(t *testing.T) { func TestInvalidVersion(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "primaryType": "EIP712Domain", "domain": {"version": "V2"} @@ -360,7 +360,7 @@ func TestInvalidVersion(t *testing.T) { func TestInvalidDomain(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "EIP712Domain": [{"name":"name","type":"string"}] @@ -378,7 +378,7 @@ func TestInvalidDomain(t *testing.T) { func TestInvalidPrimaryType(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"name","type":"string"}] @@ -396,7 +396,7 @@ func TestInvalidPrimaryType(t *testing.T) { func TestMissingPrimaryType(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType2": [{"name":"name","type":"string"}] @@ -414,7 +414,7 @@ func TestMissingPrimaryType(t *testing.T) { func TestSecondaryTypeNotMap(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType1": [{"name":"t2","type":"MyType2"}], @@ -433,7 +433,7 @@ func TestSecondaryTypeNotMap(t *testing.T) { func TestTypeInvalid(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"number","type":"int2560"}] @@ -451,7 +451,7 @@ func TestTypeInvalid(t *testing.T) { func TestIntValueInvalid(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"number","type":"int256"}] @@ -469,7 +469,7 @@ func TestIntValueInvalid(t *testing.T) { func TestBytesValueInvalid(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"bite","type":"bytes"}] @@ -487,7 +487,7 @@ func TestBytesValueInvalid(t *testing.T) { func TestFixedNotSupported(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"unmovable","type":"fixed256x18"}] @@ -505,7 +505,7 @@ func TestFixedNotSupported(t *testing.T) { func TestTupleNotSupported(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"abi_struct","type":"tuple"}] @@ -523,7 +523,7 @@ func TestTupleNotSupported(t *testing.T) { func TestAddressInvalid(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"addr","type":"address"}] @@ -541,7 +541,7 @@ func TestAddressInvalid(t *testing.T) { func TestArrayBadSuffix(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"wonky","type":"int256]"}] @@ -559,7 +559,7 @@ func TestArrayBadSuffix(t *testing.T) { func TestArrayFieldNonArrayValue(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"many","type":"int256[]"}] @@ -577,7 +577,7 @@ func TestArrayFieldNonArrayValue(t *testing.T) { func TestArrayFieldBadSize(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"many","type":"int256[n]"}] @@ -595,7 +595,7 @@ func TestArrayFieldBadSize(t *testing.T) { func TestArrayFieldSizeMismatch(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"many","type":"int256[2]"}] @@ -613,7 +613,7 @@ func TestArrayFieldSizeMismatch(t *testing.T) { func TestArrayValueInvalid(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) - var p SignTypedDataPayload + var p TypedData err := json.Unmarshal([]byte(`{ "types": { "MyType": [{"name":"many","type":"int256[]"}] diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go new file mode 100644 index 00000000..e00869c7 --- /dev/null +++ b/pkg/ethsigner/typed_data.go @@ -0,0 +1,43 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ethsigner + +import ( + "context" + + "github.com/hyperledger/firefly-signer/pkg/eip712" + "github.com/hyperledger/firefly-signer/pkg/rlp" + "github.com/hyperledger/firefly-signer/pkg/secp256k1" +) + +func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip712.TypedData) ([]byte, error) { + encodedData, err := eip712.EncodeTypedDataV4(ctx, payload) + if err != nil { + return nil, err + } + sig, err := signer.Sign(encodedData) + if err != nil { + return nil, err + } + + rlpList := make(rlp.List, 0, 4) + rlpList = append(rlpList, rlp.Data(encodedData)) + rlpList = append(rlpList, rlp.WrapInt(sig.R)) + rlpList = append(rlpList, rlp.WrapInt(sig.S)) + rlpList = append(rlpList, rlp.WrapInt(sig.V)) + return rlpList.Encode(), nil +} diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go new file mode 100644 index 00000000..b1b7642c --- /dev/null +++ b/pkg/ethsigner/typed_data_test.go @@ -0,0 +1,97 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ethsigner + +import ( + "context" + "fmt" + "math/big" + "testing" + + "github.com/hyperledger/firefly-signer/mocks/secp256k1mocks" + "github.com/hyperledger/firefly-signer/pkg/eip712" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" + "github.com/hyperledger/firefly-signer/pkg/rlp" + "github.com/hyperledger/firefly-signer/pkg/secp256k1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestSignTypedDataV4(t *testing.T) { + + // We use a simple empty message payload + payload := &eip712.TypedData{ + PrimaryType: eip712.EIP712Domain, + } + keypair, err := secp256k1.GenerateSecp256k1KeyPair() + assert.NoError(t, err) + + ctx := context.Background() + raw, err := SignTypedDataV4(ctx, keypair, payload) + assert.NoError(t, err) + + rlpList, _, err := rlp.Decode(raw) + assert.NoError(t, err) + foundSig := &secp256k1.SignatureData{ + V: new(big.Int), + R: new(big.Int), + S: new(big.Int), + } + foundSig.R.SetBytes([]byte(rlpList.(rlp.List)[1].(rlp.Data))) + foundSig.S.SetBytes([]byte(rlpList.(rlp.List)[2].(rlp.Data))) + foundSig.V.SetBytes([]byte(rlpList.(rlp.List)[3].(rlp.Data))) + + signaturePayload := ethtypes.HexBytes0xPrefix(rlpList.(rlp.List)[0].(rlp.Data)) + addr, err := foundSig.Recover(signaturePayload, -1 /* chain id is in the domain (not applied EIP-155 style to the V value) */) + assert.NoError(t, err) + assert.Equal(t, keypair.Address.String(), addr.String()) + + encoded, err := eip712.EncodeTypedDataV4(ctx, payload) + assert.NoError(t, err) + + // Check all is as we expect + assert.Equal(t, "0x8d4a3f4082945b7879e2b55f181c31a77c8c0a464b70669458abbaaf99de4c38", encoded.String()) + assert.Equal(t, "0x8d4a3f4082945b7879e2b55f181c31a77c8c0a464b70669458abbaaf99de4c38", signaturePayload.String()) +} + +func TestSignTypedDataV4BadPayload(t *testing.T) { + + payload := &eip712.TypedData{ + PrimaryType: "missing", + } + + keypair, err := secp256k1.GenerateSecp256k1KeyPair() + assert.NoError(t, err) + + ctx := context.Background() + _, err = SignTypedDataV4(ctx, keypair, payload) + assert.Regexp(t, "FF22078", err) +} + +func TestSignTypedDataV4SignFail(t *testing.T) { + + payload := &eip712.TypedData{ + PrimaryType: eip712.EIP712Domain, + } + + msn := &secp256k1mocks.Signer{} + msn.On("Sign", mock.Anything).Return(nil, fmt.Errorf("pop")) + + ctx := context.Background() + _, err := SignTypedDataV4(ctx, msn, payload) + assert.Regexp(t, "pop", err) +} diff --git a/pkg/ethsigner/wallet.go b/pkg/ethsigner/wallet.go index 3b6e21de..b7091248 100644 --- a/pkg/ethsigner/wallet.go +++ b/pkg/ethsigner/wallet.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2023 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -19,6 +19,7 @@ package ethsigner import ( "context" + "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethtypes" ) @@ -31,3 +32,8 @@ type Wallet interface { Refresh(ctx context.Context) error Close() error } + +type WalletTypedData interface { + Wallet + SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) ([]byte, error) +} diff --git a/pkg/fswallet/fswallet.go b/pkg/fswallet/fswallet.go index db872509..9f4a95e1 100644 --- a/pkg/fswallet/fswallet.go +++ b/pkg/fswallet/fswallet.go @@ -32,6 +32,7 @@ import ( "github.com/hyperledger/firefly-common/pkg/i18n" "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly-signer/internal/signermsgs" + "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethsigner" "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/hyperledger/firefly-signer/pkg/keystorev3" @@ -45,7 +46,7 @@ import ( // to the ethsigner.Wallet interface and providing notifications when new // keys are added to the wallet (via FS listener). type Wallet interface { - ethsigner.Wallet + ethsigner.WalletTypedData GetWalletFile(ctx context.Context, addr ethtypes.Address0xHex) (keystorev3.WalletFile, error) AddListener(listener chan<- ethtypes.Address0xHex) } @@ -109,13 +110,21 @@ type fsWallet struct { } func (w *fsWallet) Sign(ctx context.Context, txn *ethsigner.Transaction, chainID int64) ([]byte, error) { - keypair, err := w.getSignerForAccount(ctx, txn.From) + keypair, err := w.getSignerForJSONAccount(ctx, txn.From) if err != nil { return nil, err } return txn.Sign(keypair, chainID) } +func (w *fsWallet) SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) ([]byte, error) { + keypair, err := w.getSignerForAddr(ctx, from) + if err != nil { + return nil, err + } + return ethsigner.SignTypedDataV4(ctx, keypair, payload) +} + func (w *fsWallet) Initialize(ctx context.Context) error { // Run a get accounts pass, to check all is ok lCtx, lCancel := context.WithCancel(log.WithLogField(ctx, "fswallet", w.conf.Path)) @@ -233,7 +242,7 @@ func (w *fsWallet) Close() error { return nil } -func (w *fsWallet) getSignerForAccount(ctx context.Context, rawAddrJSON json.RawMessage) (*secp256k1.KeyPair, error) { +func (w *fsWallet) getSignerForJSONAccount(ctx context.Context, rawAddrJSON json.RawMessage) (*secp256k1.KeyPair, error) { // We require an ethereum address in the "from" field var from ethtypes.Address0xHex @@ -241,6 +250,10 @@ func (w *fsWallet) getSignerForAccount(ctx context.Context, rawAddrJSON json.Raw if err != nil { return nil, err } + return w.getSignerForAddr(ctx, from) +} + +func (w *fsWallet) getSignerForAddr(ctx context.Context, from ethtypes.Address0xHex) (*secp256k1.KeyPair, error) { wf, err := w.GetWalletFile(ctx, from) if err != nil { diff --git a/pkg/fswallet/fswallet_test.go b/pkg/fswallet/fswallet_test.go index eca20938..d17b3a93 100644 --- a/pkg/fswallet/fswallet_test.go +++ b/pkg/fswallet/fswallet_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/hyperledger/firefly-common/pkg/config" + "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethsigner" "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/sirupsen/logrus" @@ -83,7 +84,7 @@ func TestGetAccountSimpleFilenamesOK(t *testing.T) { ctx, f, done := newTestRegexpFilenameOnlyWallet(t, true) defer done() - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.NoError(t, err) } @@ -95,7 +96,7 @@ func TestGetAccountSimpleFilenamesMissingPWD(t *testing.T) { f.conf.Filenames.PasswordExt = ".wrong" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -105,7 +106,7 @@ func TestGetAccountSimpleFilenamesMismatchAddress(t *testing.T) { ctx, f, done := newTestRegexpFilenameOnlyWallet(t, true) defer done() - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"abcd1234abcd1234abcd1234abcd1234abcd1234"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"abcd1234abcd1234abcd1234abcd1234abcd1234"`)) assert.Regexp(t, "FF22059", err) } @@ -125,11 +126,11 @@ func TestListAccountsTOMLOk(t *testing.T) { assert.True(t, all["0x497eedc4299dea2f2a364be10025d0ad0f702de3"]) assert.True(t, all["0x5d093e9b41911be5f5c4cf91b108bac5d130fa83"]) - _, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.NoError(t, err) - _, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x497eedc4299dea2f2a364be10025d0ad0f702de3"`)) + _, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x497eedc4299dea2f2a364be10025d0ad0f702de3"`)) assert.Regexp(t, "FF22015", err) - _, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x5d093e9b41911be5f5c4cf91b108bac5d130fa83"`)) + _, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x5d093e9b41911be5f5c4cf91b108bac5d130fa83"`)) assert.Regexp(t, "FF22015", err) } @@ -216,6 +217,19 @@ func TestSignOK(t *testing.T) { } +func TestSignTypedDataOK(t *testing.T) { + + ctx, f, done := newTestTOMLMetadataWallet(t, true) + defer done() + + b, err := f.SignTypedDataV4(ctx, *ethtypes.MustNewAddress(`0x1f185718734552d08278aa70f804580bab5fd2b4`), &eip712.TypedData{ + PrimaryType: eip712.EIP712Domain, + }) + assert.NoError(t, err) + assert.NotNil(t, b) + +} + func TestSignNotFound(t *testing.T) { ctx, f, done := newTestTOMLMetadataWallet(t, true) @@ -228,17 +242,29 @@ func TestSignNotFound(t *testing.T) { } +func TestSignTypedDataNotFound(t *testing.T) { + + ctx, f, done := newTestTOMLMetadataWallet(t, true) + defer done() + + _, err := f.SignTypedDataV4(ctx, *ethtypes.MustNewAddress(`0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF`), &eip712.TypedData{ + PrimaryType: eip712.EIP712Domain, + }) + assert.Regexp(t, "FF22014", err) + +} + func TestGetAccountCached(t *testing.T) { ctx, f, done := newTestTOMLMetadataWallet(t, true) defer done() - s, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + s, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.NoError(t, err) assert.NotNil(t, s) // 2nd time is cached - s, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + s, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.NoError(t, err) assert.NotNil(t, s) @@ -250,7 +276,7 @@ func TestGetAccountBadYAML(t *testing.T) { defer done() f.conf.Metadata.Format = "yaml" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -260,7 +286,7 @@ func TestGetAccountBadAddress(t *testing.T) { ctx, f, done := newTestTOMLMetadataWallet(t, true) defer done() - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"bad address"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"bad address"`)) assert.Regexp(t, "bad address", err) } @@ -271,7 +297,7 @@ func TestGetAccountBadJSON(t *testing.T) { defer done() f.conf.Metadata.Format = "json" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -319,7 +345,7 @@ func TestGetAccountBadTOMLRefKey(t *testing.T) { assert.NoError(t, err) f := ff.(*fsWallet) - _, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22014", err) } @@ -336,7 +362,7 @@ func TestGetAccountNoTemplates(t *testing.T) { assert.NoError(t, err) f := ff.(*fsWallet) - _, err = f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err = f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22014", err) } @@ -348,7 +374,7 @@ func TestGetAccountBadKeyfile(t *testing.T) { f.metadataPasswordFileProperty = nil f.conf.DefaultPasswordFile = "../../test/keystore_toml/1f185718734552d08278aa70f804580bab5fd2b4.pwd" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -361,7 +387,7 @@ func TestGetAccountBadDefaultPasswordfile(t *testing.T) { f.metadataPasswordFileProperty = nil f.conf.DefaultPasswordFile = "!!!" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -372,7 +398,7 @@ func TestGetAccountNoPassword(t *testing.T) { defer done() f.metadataPasswordFileProperty = nil - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0x1f185718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22015", err) } @@ -383,7 +409,7 @@ func TestGetAccountWrongPath(t *testing.T) { defer done() f.metadataPasswordFileProperty = nil - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"5d093e9b41911be5f5c4cf91b108bac5d130fa83"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"5d093e9b41911be5f5c4cf91b108bac5d130fa83"`)) assert.Regexp(t, "FF22015", err) } @@ -396,7 +422,7 @@ func TestGetAccountNotFound(t *testing.T) { f.metadataPasswordFileProperty = nil f.conf.DefaultPasswordFile = "!!!" - _, err := f.getSignerForAccount(ctx, json.RawMessage(`"0xFFFF5718734552d08278aa70f804580bab5fd2b4"`)) + _, err := f.getSignerForJSONAccount(ctx, json.RawMessage(`"0xFFFF5718734552d08278aa70f804580bab5fd2b4"`)) assert.Regexp(t, "FF22014", err) } diff --git a/pkg/rpcbackend/backend.go b/pkg/rpcbackend/backend.go index d642ad78..7e56b127 100644 --- a/pkg/rpcbackend/backend.go +++ b/pkg/rpcbackend/backend.go @@ -195,8 +195,16 @@ func (rc *RPCClient) SyncRequest(ctx context.Context, rpcReq *RPCRequest) (rpcRe } // JSON/RPC allows errors to be returned with a 200 status code, as well as other status codes if res.IsError() || rpcRes.Error != nil && rpcRes.Error.Code != 0 { - log.L(ctx).Errorf("RPC[%s] <-- [%d]: %s", rpcTraceID, res.StatusCode(), rpcRes.Message()) - err := fmt.Errorf(rpcRes.Message()) + rpcMsg := rpcRes.Message() + errLog := rpcMsg + if rpcMsg == "" { + // Log the raw result in the case of JSON parse error etc. (note that Resty no longer + // returns this as an error - rather the body comes back raw) + errLog = string(res.Body()) + rpcMsg = i18n.NewError(ctx, signermsgs.MsgRPCRequestFailed, res.Status()).Error() + } + log.L(ctx).Errorf("RPC[%s] <-- [%d]: %s", rpcTraceID, res.StatusCode(), errLog) + err := fmt.Errorf(rpcMsg) return rpcRes, err } log.L(ctx).Infof("RPC[%s] <-- %s [%d] OK (%.2fms)", rpcTraceID, rpcReq.Method, res.StatusCode(), float64(time.Since(rpcStartTime))/float64(time.Millisecond)) From 38bcfa640e39c75eea53e58acff7073750942870 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Mon, 23 Oct 2023 23:59:07 -0400 Subject: [PATCH 10/28] Update linter Signed-off-by: Peter Broadhurst --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f0737fba..3e7f9a69 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ lint: ${LINT} ${MOCKERY}: $(VGO) install github.com/vektra/mockery/cmd/mockery@latest ${LINT}: - $(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.0 + $(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.0 define makemock From a5db2486b2ff3d3199c638c16d400b7c8d3d361f Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 25 Oct 2023 09:13:18 -0400 Subject: [PATCH 11/28] Provide individual parts of signature in Go utility Signed-off-by: Peter Broadhurst --- pkg/ethsigner/typed_data.go | 23 +++++++++++++++-------- pkg/ethsigner/typed_data_test.go | 18 ++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go index e00869c7..ba8fa59a 100644 --- a/pkg/ethsigner/typed_data.go +++ b/pkg/ethsigner/typed_data.go @@ -20,11 +20,18 @@ import ( "context" "github.com/hyperledger/firefly-signer/pkg/eip712" - "github.com/hyperledger/firefly-signer/pkg/rlp" + "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/hyperledger/firefly-signer/pkg/secp256k1" ) -func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip712.TypedData) ([]byte, error) { +type EIP712Result struct { + Hash ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"hash"` + V ethtypes.HexInteger `ffstruct:"EIP712Result" json:"v"` + R ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"r"` + S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` +} + +func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip712.TypedData) (*EIP712Result, error) { encodedData, err := eip712.EncodeTypedDataV4(ctx, payload) if err != nil { return nil, err @@ -34,10 +41,10 @@ func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip7 return nil, err } - rlpList := make(rlp.List, 0, 4) - rlpList = append(rlpList, rlp.Data(encodedData)) - rlpList = append(rlpList, rlp.WrapInt(sig.R)) - rlpList = append(rlpList, rlp.WrapInt(sig.S)) - rlpList = append(rlpList, rlp.WrapInt(sig.V)) - return rlpList.Encode(), nil + return &EIP712Result{ + Hash: encodedData, + V: ethtypes.HexInteger(*sig.V), + R: sig.R.FillBytes(make([]byte, 32)), + S: sig.S.FillBytes(make([]byte, 32)), + }, nil } diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index cab03a72..0208eeee 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -18,14 +18,15 @@ package ethsigner import ( "context" + "encoding/json" "fmt" "math/big" "testing" + "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly-signer/mocks/secp256k1mocks" "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethtypes" - "github.com/hyperledger/firefly-signer/pkg/rlp" "github.com/hyperledger/firefly-signer/pkg/secp256k1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -41,21 +42,22 @@ func TestSignTypedDataV4(t *testing.T) { assert.NoError(t, err) ctx := context.Background() - raw, err := SignTypedDataV4(ctx, keypair, payload) + sig, err := SignTypedDataV4(ctx, keypair, payload) assert.NoError(t, err) - rlpList, _, err := rlp.Decode(raw) + b, err := json.Marshal(sig) assert.NoError(t, err) + log.L(context.Background()).Infof("Signature: %s", b) + foundSig := &secp256k1.SignatureData{ - V: new(big.Int), + V: sig.V.BigInt(), R: new(big.Int), S: new(big.Int), } - foundSig.R.SetBytes([]byte(rlpList.(rlp.List)[1].(rlp.Data))) - foundSig.S.SetBytes([]byte(rlpList.(rlp.List)[2].(rlp.Data))) - foundSig.V.SetBytes([]byte(rlpList.(rlp.List)[3].(rlp.Data))) + foundSig.R.SetBytes(sig.R) + foundSig.S.SetBytes(sig.S) - signaturePayload := ethtypes.HexBytes0xPrefix(rlpList.(rlp.List)[0].(rlp.Data)) + signaturePayload := ethtypes.HexBytes0xPrefix(sig.Hash) addr, err := foundSig.Recover(signaturePayload, -1 /* chain id is in the domain (not applied EIP-155 style to the V value) */) assert.NoError(t, err) assert.Equal(t, keypair.Address.String(), addr.String()) From 1e59fbf4382520fe21332743b5241975ec69c33d Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 25 Oct 2023 09:14:51 -0400 Subject: [PATCH 12/28] Use simple structure for signature Signed-off-by: Peter Broadhurst --- pkg/ethsigner/wallet.go | 2 +- pkg/fswallet/fswallet.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/ethsigner/wallet.go b/pkg/ethsigner/wallet.go index b7091248..30d66cf8 100644 --- a/pkg/ethsigner/wallet.go +++ b/pkg/ethsigner/wallet.go @@ -35,5 +35,5 @@ type Wallet interface { type WalletTypedData interface { Wallet - SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) ([]byte, error) + SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) (*EIP712Result, error) } diff --git a/pkg/fswallet/fswallet.go b/pkg/fswallet/fswallet.go index 9f4a95e1..fe32e4d0 100644 --- a/pkg/fswallet/fswallet.go +++ b/pkg/fswallet/fswallet.go @@ -117,7 +117,7 @@ func (w *fsWallet) Sign(ctx context.Context, txn *ethsigner.Transaction, chainID return txn.Sign(keypair, chainID) } -func (w *fsWallet) SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) ([]byte, error) { +func (w *fsWallet) SignTypedDataV4(ctx context.Context, from ethtypes.Address0xHex, payload *eip712.TypedData) (*ethsigner.EIP712Result, error) { keypair, err := w.getSignerForAddr(ctx, from) if err != nil { return nil, err From 1e82757107a28ecfeaef39594cd065d0651e0f7a Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 25 Oct 2023 11:00:40 -0400 Subject: [PATCH 13/28] RLP signature too Signed-off-by: Peter Broadhurst --- pkg/ethsigner/typed_data.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go index ba8fa59a..55324a48 100644 --- a/pkg/ethsigner/typed_data.go +++ b/pkg/ethsigner/typed_data.go @@ -21,14 +21,16 @@ import ( "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethtypes" + "github.com/hyperledger/firefly-signer/pkg/rlp" "github.com/hyperledger/firefly-signer/pkg/secp256k1" ) type EIP712Result struct { - Hash ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"hash"` - V ethtypes.HexInteger `ffstruct:"EIP712Result" json:"v"` - R ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"r"` - S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` + Hash ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"hash"` + Signature ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"signature"` + V ethtypes.HexInteger `ffstruct:"EIP712Result" json:"v"` + R ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"r"` + S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` } func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip712.TypedData) (*EIP712Result, error) { @@ -41,10 +43,17 @@ func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip7 return nil, err } + rlpList := make(rlp.List, 0, 4) + rlpList = append(rlpList, rlp.Data(encodedData)) + rlpList = append(rlpList, rlp.WrapInt(sig.R)) + rlpList = append(rlpList, rlp.WrapInt(sig.S)) + rlpList = append(rlpList, rlp.WrapInt(sig.V)) + return &EIP712Result{ - Hash: encodedData, - V: ethtypes.HexInteger(*sig.V), - R: sig.R.FillBytes(make([]byte, 32)), - S: sig.S.FillBytes(make([]byte, 32)), + Hash: encodedData, + Signature: rlpList.Encode(), // per eth_signTypedData_v4 convention + V: ethtypes.HexInteger(*sig.V), + R: sig.R.FillBytes(make([]byte, 32)), + S: sig.S.FillBytes(make([]byte, 32)), }, nil } From fd5b5d1b18cc155bed064c5db1e360ca799beecd Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Tue, 31 Oct 2023 12:33:01 -0400 Subject: [PATCH 14/28] Fix docs on ABI structure Signed-off-by: Peter Broadhurst --- config.md | 3 ++ go.mod | 2 +- go.sum | 23 +++++++++++++ internal/signermsgs/en_field_descriptions.go | 34 ++++++++++++++++++++ pkg/abi/abi.go | 26 +++++++-------- pkg/abi/abi_test.go | 5 +++ 6 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 internal/signermsgs/en_field_descriptions.go diff --git a/config.md b/config.md index 9d159626..aa0c42c7 100644 --- a/config.md +++ b/config.md @@ -64,6 +64,7 @@ nav_order: 2 |certFile|The path to the certificate file for TLS on this API|`string`|`` |clientAuth|Enables or disables client auth for TLS on this API|`string`|`` |enabled|Enables or disables TLS on this API|`boolean`|`false` +|insecureSkipHostVerify|When to true in unit test development environments to disable TLS verification. Use with extreme caution|`boolean`|`` |keyFile|The path to the private key file for TLS on this API|`string`|`` |requiredDNAttributes|A set of required subject DN attributes. Each entry is a regular expression, and the subject certificate must have a matching attribute of the specified type (CN, C, O, OU, ST, L, STREET, POSTALCODE, SERIALNUMBER are valid attributes)|`map[string]string`|`` @@ -76,6 +77,7 @@ nav_order: 2 |initialConnectAttempts|The number of attempts FireFly will make to connect to the WebSocket when starting up, before failing|`int`|`5` |path|The WebSocket sever URL to which FireFly should connect|WebSocket URL `string`|`` |readBufferSize|The size in bytes of the read buffer for the WebSocket connection|[`BytesSize`](https://pkg.go.dev/github.com/docker/go-units#BytesSize)|`16Kb` +|url|URL to use for WebSocket - overrides url one level up (in the HTTP config)|`string`|`` |writeBufferSize|The size in bytes of the write buffer for the WebSocket connection|[`BytesSize`](https://pkg.go.dev/github.com/docker/go-units#BytesSize)|`16Kb` ## cors @@ -183,5 +185,6 @@ nav_order: 2 |certFile|The path to the certificate file for TLS on this API|`string`|`` |clientAuth|Enables or disables client auth for TLS on this API|`string`|`` |enabled|Enables or disables TLS on this API|`boolean`|`false` +|insecureSkipHostVerify|When to true in unit test development environments to disable TLS verification. Use with extreme caution|`boolean`|`` |keyFile|The path to the private key file for TLS on this API|`string`|`` |requiredDNAttributes|A set of required subject DN attributes. Each entry is a regular expression, and the subject certificate must have a matching attribute of the specified type (CN, C, O, OU, ST, L, STREET, POSTALCODE, SERIALNUMBER are valid attributes)|`map[string]string`|`` \ No newline at end of file diff --git a/go.mod b/go.mod index bd3f8fb0..297f1a2b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-resty/resty/v2 v2.10.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.3.1-0.20231003142556-37246a8c5e72 + github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 diff --git a/go.sum b/go.sum index 119f456f..f75ba290 100644 --- a/go.sum +++ b/go.sum @@ -82,6 +82,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= @@ -89,6 +90,7 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -156,8 +158,18 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hyperledger/firefly-common v1.3.0 h1:eLFUJuPU8E5iZXYGHlXghQuN+opWG/qp7zvMKavKEPU= +github.com/hyperledger/firefly-common v1.3.0/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= github.com/hyperledger/firefly-common v1.3.1-0.20231003142556-37246a8c5e72 h1:l/Yo3woV2pih5EVmUHFwXr0LVr0twtXCVAdJAWInf5Q= github.com/hyperledger/firefly-common v1.3.1-0.20231003142556-37246a8c5e72/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= +github.com/hyperledger/firefly-common v1.3.1-0.20231020204634-4b496bf3bf56 h1:fYBS5v/JBfy9054ZVU97ZmrIXujDaxJz28btk9PFsb8= +github.com/hyperledger/firefly-common v1.3.1-0.20231020204634-4b496bf3bf56/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.3.1-0.20231031161137-046ec287442d h1:vXZtJA1F8SxjRespwRdbFTuaPYR7F1GuyMqwthTlKj8= +github.com/hyperledger/firefly-common v1.3.1-0.20231031161137-046ec287442d/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc h1:lLNQ64jbUJA9zlFFAcFg/EjrxmubNi3+JiihymyDr0o= +github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f h1:EmHmBRVL8TuOayPUJIKSFwPVTSfq07qzbvOEuYWgQ20= +github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -167,8 +179,11 @@ github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache v2.0.3+incompatible h1:j68C9tWOROiOLWTS/kCGg9IcJG+ACqn5+0+t8Oh83UU= github.com/karlseguin/ccache v2.0.3+incompatible/go.mod h1:CM9tNPzT6EdRh14+jiW8mEF9mkNZuuE51qmgGYUB93w= github.com/karlseguin/expect v1.0.8 h1:Bb0H6IgBWQpadY25UDNkYPDB9ITqK1xnSoZfAq362fw= @@ -179,6 +194,7 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -194,8 +210,11 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -262,6 +281,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= @@ -601,6 +621,7 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -616,7 +637,9 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/signermsgs/en_field_descriptions.go b/internal/signermsgs/en_field_descriptions.go new file mode 100644 index 00000000..6fcaae00 --- /dev/null +++ b/internal/signermsgs/en_field_descriptions.go @@ -0,0 +1,34 @@ +// Copyright © 2023 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package signermsgs + +var ( + ABIEntryAnonymous = ffm("ABIEntry.anonymous", "If the event is anonymous then the signature of the event does not take up a topic slot") + ABIEntryType = ffm("ABIEntry.type", "The type of the ABI entry: 'event', 'error', 'function', 'constructor', 'receive', or 'fallback'") + ABIEntryName = ffm("ABIEntry.name", "The name of the ABI entry") + ABIEntryInputs = ffm("ABIEntry.inputs", "Array of ABI parameter definitions for inputs to a function, or the fields of an event") + ABIEntryOutputs = ffm("ABIEntry.outputs", "Array of ABI parameter definitions for return values from a function") + ABIEntryStateMutability = ffm("ABIEntry.stateMutability", "The state mutability of the function: 'pure', 'view', 'nonpayable' (the default) and 'payable'") + ABIEntryConstant = ffm("ABIEntry.constant", "Functions only: Superseded by stateMutability payable/nonpayable") + ABIEntryPayable = ffm("ABIEntry.payable", "Functions only: Superseded by stateMutability pure/view") + + ABIParameterName = ffm("ABIParameter.name", "The name of the parameter") + ABIParameterType = ffm("ABIParameter.type", "The type of the parameter per the ABI specification") + ABIParameterComponents = ffm("ABIParameter.components", "An array of components, if the parameter is a tuple") + ABIParameterIndexed = ffm("ABIParameter.indexed", "Whether this parameter uses one of the topics, or is in the data area") + ABIParameterInternalType = ffm("ABIParameter.internalType", "Used by the solc compiler to include additional details - importantly the struct name for tuples") +) diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index b6ab01df..23d43806 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -227,23 +227,23 @@ type ParameterArray []*Parameter // Defines the name / inputs / outputs which can be used to generate the signature // of the function/event, and used to encode input data, or decode output data. type Entry struct { - Type EntryType `json:"type,omitempty"` // Type of the entry - there are multiple function sub-types, events and errors - Name string `json:"name,omitempty"` // Name of the function/event/error - Payable bool `json:"payable,omitempty"` // Functions only: Superseded by stateMutability payable/nonpayable - Constant bool `json:"constant,omitempty"` // Functions only: Superseded by stateMutability pure/view - Anonymous bool `json:"anonymous,omitempty"` // Events only: The event is emitted without a signature (topic[0] is not generated) - StateMutability StateMutability `json:"stateMutability,omitempty"` // How the function interacts with the blockchain state - Inputs ParameterArray `json:"inputs"` // The list of input parameters to a function, or fields of an event / error - Outputs ParameterArray `json:"outputs"` // Functions only: The list of return values from a function + Type EntryType `ffstruct:"ABIEntry" json:"type,omitempty"` // Type of the entry - there are multiple function sub-types, events and errors + Name string `ffstruct:"ABIEntry" json:"name,omitempty"` // Name of the function/event/error + Payable bool `ffstruct:"ABIEntry" json:"payable,omitempty"` // Functions only: Superseded by stateMutability payable/nonpayable + Constant bool `ffstruct:"ABIEntry" json:"constant,omitempty"` // Functions only: Superseded by stateMutability pure/view + Anonymous bool `ffstruct:"ABIEntry" json:"anonymous,omitempty"` // Events only: The event is emitted without a signature (topic[0] is not generated) + StateMutability StateMutability `ffstruct:"ABIEntry" json:"stateMutability,omitempty"` // How the function interacts with the blockchain state + Inputs ParameterArray `ffstruct:"ABIEntry" json:"inputs"` // The list of input parameters to a function, or fields of an event / error + Outputs ParameterArray `ffstruct:"ABIEntry" json:"outputs"` // Functions only: The list of return values from a function } // Parameter is an individual typed parameter input/output type Parameter struct { - Name string `json:"name"` // The name of the argument - does not affect the signature - Type string `json:"type"` // The canonical type of the parameter - InternalType string `json:"internalType,omitempty"` // Additional internal type information that might be generated by the compiler - Components ParameterArray `json:"components,omitempty"` // An ordered list (tuple) of nested elements for array/object types - Indexed bool `json:"indexed,omitempty"` // Events only: Whether the parameter is indexed into one of the topics of the log, or in the log's data segment + Name string `ffstruct:"ABIParameter" json:"name"` // The name of the argument - does not affect the signature + Type string `ffstruct:"ABIParameter" json:"type"` // The canonical type of the parameter + InternalType string `ffstruct:"ABIParameter" json:"internalType,omitempty"` // Additional internal type information that might be generated by the compiler + Components ParameterArray `ffstruct:"ABIParameter" json:"components,omitempty"` // An ordered list (tuple) of nested elements for array/object types + Indexed bool `ffstruct:"ABIParameter" json:"indexed,omitempty"` // Events only: Whether the parameter is indexed into one of the topics of the log, or in the log's data segment parsed *typeComponent // cached components } diff --git a/pkg/abi/abi_test.go b/pkg/abi/abi_test.go index bf04dd3f..1181f63c 100644 --- a/pkg/abi/abi_test.go +++ b/pkg/abi/abi_test.go @@ -23,6 +23,7 @@ import ( "math/big" "testing" + "github.com/hyperledger/firefly-common/pkg/ffapi" "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/stretchr/testify/assert" ) @@ -842,3 +843,7 @@ func TestEncodeCallDataValuesHelper(t *testing.T) { assert.Equal(t, "113bc475000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000047465737400000000000000000000000000000000000000000000000000000000", hex.EncodeToString(b)) } + +func TestABIDocumented(t *testing.T) { + ffapi.CheckObjectDocumented(&ABI{}) +} From 406085997f0c810df184ec42336417f6bb0aff60 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Tue, 31 Oct 2023 13:31:28 -0400 Subject: [PATCH 15/28] Avoid clashes Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_field_descriptions.go | 26 ++++++++++---------- pkg/abi/abi.go | 26 ++++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/internal/signermsgs/en_field_descriptions.go b/internal/signermsgs/en_field_descriptions.go index 6fcaae00..707dafbb 100644 --- a/internal/signermsgs/en_field_descriptions.go +++ b/internal/signermsgs/en_field_descriptions.go @@ -17,18 +17,18 @@ package signermsgs var ( - ABIEntryAnonymous = ffm("ABIEntry.anonymous", "If the event is anonymous then the signature of the event does not take up a topic slot") - ABIEntryType = ffm("ABIEntry.type", "The type of the ABI entry: 'event', 'error', 'function', 'constructor', 'receive', or 'fallback'") - ABIEntryName = ffm("ABIEntry.name", "The name of the ABI entry") - ABIEntryInputs = ffm("ABIEntry.inputs", "Array of ABI parameter definitions for inputs to a function, or the fields of an event") - ABIEntryOutputs = ffm("ABIEntry.outputs", "Array of ABI parameter definitions for return values from a function") - ABIEntryStateMutability = ffm("ABIEntry.stateMutability", "The state mutability of the function: 'pure', 'view', 'nonpayable' (the default) and 'payable'") - ABIEntryConstant = ffm("ABIEntry.constant", "Functions only: Superseded by stateMutability payable/nonpayable") - ABIEntryPayable = ffm("ABIEntry.payable", "Functions only: Superseded by stateMutability pure/view") + ABIEntryAnonymous = ffm("EthABIEntry.anonymous", "If the event is anonymous then the signature of the event does not take up a topic slot") + ABIEntryType = ffm("EthABIEntry.type", "The type of the ABI entry: 'event', 'error', 'function', 'constructor', 'receive', or 'fallback'") + ABIEntryName = ffm("EthABIEntry.name", "The name of the ABI entry") + ABIEntryInputs = ffm("EthABIEntry.inputs", "Array of ABI parameter definitions for inputs to a function, or the fields of an event") + ABIEntryOutputs = ffm("EthABIEntry.outputs", "Array of ABI parameter definitions for return values from a function") + ABIEntryStateMutability = ffm("EthABIEntry.stateMutability", "The state mutability of the function: 'pure', 'view', 'nonpayable' (the default) and 'payable'") + ABIEntryConstant = ffm("EthABIEntry.constant", "Functions only: Superseded by stateMutability payable/nonpayable") + ABIEntryPayable = ffm("EthABIEntry.payable", "Functions only: Superseded by stateMutability pure/view") - ABIParameterName = ffm("ABIParameter.name", "The name of the parameter") - ABIParameterType = ffm("ABIParameter.type", "The type of the parameter per the ABI specification") - ABIParameterComponents = ffm("ABIParameter.components", "An array of components, if the parameter is a tuple") - ABIParameterIndexed = ffm("ABIParameter.indexed", "Whether this parameter uses one of the topics, or is in the data area") - ABIParameterInternalType = ffm("ABIParameter.internalType", "Used by the solc compiler to include additional details - importantly the struct name for tuples") + ABIParameterName = ffm("EthABIParameter.name", "The name of the parameter") + ABIParameterType = ffm("EthABIParameter.type", "The type of the parameter per the ABI specification") + ABIParameterComponents = ffm("EthABIParameter.components", "An array of components, if the parameter is a tuple") + ABIParameterIndexed = ffm("EthABIParameter.indexed", "Whether this parameter uses one of the topics, or is in the data area") + ABIParameterInternalType = ffm("EthABIParameter.internalType", "Used by the solc compiler to include additional details - importantly the struct name for tuples") ) diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index 23d43806..4a1e88e7 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -227,23 +227,23 @@ type ParameterArray []*Parameter // Defines the name / inputs / outputs which can be used to generate the signature // of the function/event, and used to encode input data, or decode output data. type Entry struct { - Type EntryType `ffstruct:"ABIEntry" json:"type,omitempty"` // Type of the entry - there are multiple function sub-types, events and errors - Name string `ffstruct:"ABIEntry" json:"name,omitempty"` // Name of the function/event/error - Payable bool `ffstruct:"ABIEntry" json:"payable,omitempty"` // Functions only: Superseded by stateMutability payable/nonpayable - Constant bool `ffstruct:"ABIEntry" json:"constant,omitempty"` // Functions only: Superseded by stateMutability pure/view - Anonymous bool `ffstruct:"ABIEntry" json:"anonymous,omitempty"` // Events only: The event is emitted without a signature (topic[0] is not generated) - StateMutability StateMutability `ffstruct:"ABIEntry" json:"stateMutability,omitempty"` // How the function interacts with the blockchain state - Inputs ParameterArray `ffstruct:"ABIEntry" json:"inputs"` // The list of input parameters to a function, or fields of an event / error - Outputs ParameterArray `ffstruct:"ABIEntry" json:"outputs"` // Functions only: The list of return values from a function + Type EntryType `ffstruct:"EthABIEntry" json:"type,omitempty"` // Type of the entry - there are multiple function sub-types, events and errors + Name string `ffstruct:"EthABIEntry" json:"name,omitempty"` // Name of the function/event/error + Payable bool `ffstruct:"EthABIEntry" json:"payable,omitempty"` // Functions only: Superseded by stateMutability payable/nonpayable + Constant bool `ffstruct:"EthABIEntry" json:"constant,omitempty"` // Functions only: Superseded by stateMutability pure/view + Anonymous bool `ffstruct:"EthABIEntry" json:"anonymous,omitempty"` // Events only: The event is emitted without a signature (topic[0] is not generated) + StateMutability StateMutability `ffstruct:"EthABIEntry" json:"stateMutability,omitempty"` // How the function interacts with the blockchain state + Inputs ParameterArray `ffstruct:"EthABIEntry" json:"inputs"` // The list of input parameters to a function, or fields of an event / error + Outputs ParameterArray `ffstruct:"EthABIEntry" json:"outputs"` // Functions only: The list of return values from a function } // Parameter is an individual typed parameter input/output type Parameter struct { - Name string `ffstruct:"ABIParameter" json:"name"` // The name of the argument - does not affect the signature - Type string `ffstruct:"ABIParameter" json:"type"` // The canonical type of the parameter - InternalType string `ffstruct:"ABIParameter" json:"internalType,omitempty"` // Additional internal type information that might be generated by the compiler - Components ParameterArray `ffstruct:"ABIParameter" json:"components,omitempty"` // An ordered list (tuple) of nested elements for array/object types - Indexed bool `ffstruct:"ABIParameter" json:"indexed,omitempty"` // Events only: Whether the parameter is indexed into one of the topics of the log, or in the log's data segment + Name string `ffstruct:"EthABIParameter" json:"name"` // The name of the argument - does not affect the signature + Type string `ffstruct:"EthABIParameter" json:"type"` // The canonical type of the parameter + InternalType string `ffstruct:"EthABIParameter" json:"internalType,omitempty"` // Additional internal type information that might be generated by the compiler + Components ParameterArray `ffstruct:"EthABIParameter" json:"components,omitempty"` // An ordered list (tuple) of nested elements for array/object types + Indexed bool `ffstruct:"EthABIParameter" json:"indexed,omitempty"` // Events only: Whether the parameter is indexed into one of the topics of the log, or in the log's data segment parsed *typeComponent // cached components } From f46dee61e7096892e5f7d256725022265516186a Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Tue, 31 Oct 2023 13:50:51 -0400 Subject: [PATCH 16/28] Work around odd issue Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_field_descriptions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/signermsgs/en_field_descriptions.go b/internal/signermsgs/en_field_descriptions.go index 707dafbb..9746955f 100644 --- a/internal/signermsgs/en_field_descriptions.go +++ b/internal/signermsgs/en_field_descriptions.go @@ -20,8 +20,8 @@ var ( ABIEntryAnonymous = ffm("EthABIEntry.anonymous", "If the event is anonymous then the signature of the event does not take up a topic slot") ABIEntryType = ffm("EthABIEntry.type", "The type of the ABI entry: 'event', 'error', 'function', 'constructor', 'receive', or 'fallback'") ABIEntryName = ffm("EthABIEntry.name", "The name of the ABI entry") - ABIEntryInputs = ffm("EthABIEntry.inputs", "Array of ABI parameter definitions for inputs to a function, or the fields of an event") - ABIEntryOutputs = ffm("EthABIEntry.outputs", "Array of ABI parameter definitions for return values from a function") + ABIEntryInputs = ffm("EthABIEntry.inputs", "Array of ABI parameter definitions") + ABIEntryOutputs = ffm("EthABIEntry.outputs", "Array of ABI parameter definitions") ABIEntryStateMutability = ffm("EthABIEntry.stateMutability", "The state mutability of the function: 'pure', 'view', 'nonpayable' (the default) and 'payable'") ABIEntryConstant = ffm("EthABIEntry.constant", "Functions only: Superseded by stateMutability payable/nonpayable") ABIEntryPayable = ffm("EthABIEntry.payable", "Functions only: Superseded by stateMutability pure/view") From b1f5b390adff5635b425a6f6122c2b3ac24d39a9 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 1 Nov 2023 03:02:53 -0400 Subject: [PATCH 17/28] Add YAML support Signed-off-by: Peter Broadhurst --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 297f1a2b..bed9713b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-resty/resty/v2 v2.10.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f + github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 diff --git a/go.sum b/go.sum index f75ba290..262b8849 100644 --- a/go.sum +++ b/go.sum @@ -170,6 +170,8 @@ github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc h1:lL github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f h1:EmHmBRVL8TuOayPUJIKSFwPVTSfq07qzbvOEuYWgQ20= github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e h1:5TyGzTZ3Ogv3esgjRm/+UrnQYNTYYg9H6bW6DC2MdsE= +github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= From be7301744783cd7a84830ec155b90cf0eda67992 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 1 Nov 2023 03:08:18 -0400 Subject: [PATCH 18/28] Update common Signed-off-by: Peter Broadhurst --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bed9713b..4800fae7 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-resty/resty/v2 v2.10.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e + github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 diff --git a/go.sum b/go.sum index 262b8849..a730407c 100644 --- a/go.sum +++ b/go.sum @@ -172,6 +172,8 @@ github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f h1:Em github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e h1:5TyGzTZ3Ogv3esgjRm/+UrnQYNTYYg9H6bW6DC2MdsE= github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf h1:CQMNZUTCQb0naxY7J14QEJGPBu31YY+qcSYUTC49epM= +github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= From 5f677b4e32cac6aa1017d8d2e3a78589b38e01cd Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 2 Nov 2023 00:07:58 -0400 Subject: [PATCH 19/28] Do not assert version (confusion in TypeScript lib) Signed-off-by: Peter Broadhurst --- go.sum | 27 ------------------------- internal/signermsgs/en_error_messges.go | 2 +- pkg/eip712/typed_data_v4.go | 5 ++--- pkg/eip712/typed_data_v4_test.go | 7 ++----- 4 files changed, 5 insertions(+), 36 deletions(-) diff --git a/go.sum b/go.sum index a730407c..2d292a13 100644 --- a/go.sum +++ b/go.sum @@ -82,7 +82,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= @@ -90,7 +89,6 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -158,20 +156,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hyperledger/firefly-common v1.3.0 h1:eLFUJuPU8E5iZXYGHlXghQuN+opWG/qp7zvMKavKEPU= -github.com/hyperledger/firefly-common v1.3.0/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= -github.com/hyperledger/firefly-common v1.3.1-0.20231003142556-37246a8c5e72 h1:l/Yo3woV2pih5EVmUHFwXr0LVr0twtXCVAdJAWInf5Q= -github.com/hyperledger/firefly-common v1.3.1-0.20231003142556-37246a8c5e72/go.mod h1:17lOH4YufiPy82LpKm8fPa/YXJ0pUyq01zK1CmklJwM= -github.com/hyperledger/firefly-common v1.3.1-0.20231020204634-4b496bf3bf56 h1:fYBS5v/JBfy9054ZVU97ZmrIXujDaxJz28btk9PFsb8= -github.com/hyperledger/firefly-common v1.3.1-0.20231020204634-4b496bf3bf56/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= -github.com/hyperledger/firefly-common v1.3.1-0.20231031161137-046ec287442d h1:vXZtJA1F8SxjRespwRdbFTuaPYR7F1GuyMqwthTlKj8= -github.com/hyperledger/firefly-common v1.3.1-0.20231031161137-046ec287442d/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= -github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc h1:lLNQ64jbUJA9zlFFAcFg/EjrxmubNi3+JiihymyDr0o= -github.com/hyperledger/firefly-common v1.3.1-0.20231031162856-3f4c9a6f29cc/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= -github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f h1:EmHmBRVL8TuOayPUJIKSFwPVTSfq07qzbvOEuYWgQ20= -github.com/hyperledger/firefly-common v1.3.1-0.20231031163145-c7aa519f0b5f/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= -github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e h1:5TyGzTZ3Ogv3esgjRm/+UrnQYNTYYg9H6bW6DC2MdsE= -github.com/hyperledger/firefly-common v1.3.1-0.20231101070154-db8f6c63822e/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf h1:CQMNZUTCQb0naxY7J14QEJGPBu31YY+qcSYUTC49epM= github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -183,11 +167,8 @@ github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache v2.0.3+incompatible h1:j68C9tWOROiOLWTS/kCGg9IcJG+ACqn5+0+t8Oh83UU= github.com/karlseguin/ccache v2.0.3+incompatible/go.mod h1:CM9tNPzT6EdRh14+jiW8mEF9mkNZuuE51qmgGYUB93w= github.com/karlseguin/expect v1.0.8 h1:Bb0H6IgBWQpadY25UDNkYPDB9ITqK1xnSoZfAq362fw= @@ -198,7 +179,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -214,11 +194,8 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -285,7 +262,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= @@ -625,7 +601,6 @@ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -641,9 +616,7 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index 4f3e06a9..a22af0c5 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -96,6 +96,6 @@ var ( MsgEIP712InvalidArraySuffix = ffe("FF22077", "Type '%s' has invalid array suffix") MsgEIP712ValueNotArray = ffe("FF22078", "Value for '%s' not an array (%T)") MsgEIP712InvalidArrayLen = ffe("FF22079", "Value for '%s' must have %d entries (found %d)") - MsgEIP712Version4Required = ffe("FF22080", "Domain version must be 'V4': %s") + MsgEIP712PrimaryTypeRequired = ffe("FF22080", "Primary type must be specified") MsgEIP712TypeNotFound = ffe("FF22081", "Type '%s' not found in type map") ) diff --git a/pkg/eip712/typed_data_v4.go b/pkg/eip712/typed_data_v4.go index e468bf24..02fac454 100644 --- a/pkg/eip712/typed_data_v4.go +++ b/pkg/eip712/typed_data_v4.go @@ -61,10 +61,9 @@ func EncodeTypedDataV4(ctx context.Context, payload *TypedData) (encoded ethtype if payload.Domain == nil { payload.Domain = make(map[string]interface{}) } - if v, didSupplyVersion := payload.Domain["version"].(string); didSupplyVersion && v != "V4" { - return nil, i18n.NewError(ctx, signermsgs.MsgEIP712Version4Required, v) + if payload.PrimaryType == "" { + return nil, i18n.NewError(ctx, signermsgs.MsgEIP712PrimaryTypeRequired) } - payload.Domain["version"] = "V4" // Start with the EIP-712 prefix buf := new(bytes.Buffer) diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index e666a614..bf31208f 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -342,14 +342,11 @@ func TestMessage_StructArray(t *testing.T) { assert.Equal(t, "0x651579f58b3a8c79ba668e0f5d83e1c9f6e2715586dc11c62696ec376b595a00", ed.String()) } -func TestInvalidVersion(t *testing.T) { +func TestMissingPrimaryTypeField(t *testing.T) { logrus.SetLevel(logrus.TraceLevel) var p TypedData - err := json.Unmarshal([]byte(`{ - "primaryType": "EIP712Domain", - "domain": {"version": "V2"} - }`), &p) + err := json.Unmarshal([]byte(`{}`), &p) assert.NoError(t, err) ctx := context.Background() From df2f89e6b6bbb9b40c53b4c158f328a3b4779d39 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 9 Nov 2023 00:35:53 -0500 Subject: [PATCH 20/28] Correct double hashing issue Signed-off-by: Peter Broadhurst --- mocks/ethsignermocks/wallet.go | 2 +- mocks/rpcbackendmocks/backend.go | 2 +- mocks/rpcservermocks/server.go | 2 +- mocks/secp256k1mocks/signer.go | 28 +++++++++++- pkg/ethsigner/typed_data.go | 15 +++---- pkg/ethsigner/typed_data_test.go | 74 +++++++++++++++++++++++++++++++- pkg/secp256k1/signer.go | 27 +++++++++--- 7 files changed, 129 insertions(+), 21 deletions(-) diff --git a/mocks/ethsignermocks/wallet.go b/mocks/ethsignermocks/wallet.go index 58889f13..3e59b487 100644 --- a/mocks/ethsignermocks/wallet.go +++ b/mocks/ethsignermocks/wallet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.36.0. DO NOT EDIT. package ethsignermocks diff --git a/mocks/rpcbackendmocks/backend.go b/mocks/rpcbackendmocks/backend.go index 00bb48f4..9d03542c 100644 --- a/mocks/rpcbackendmocks/backend.go +++ b/mocks/rpcbackendmocks/backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.36.0. DO NOT EDIT. package rpcbackendmocks diff --git a/mocks/rpcservermocks/server.go b/mocks/rpcservermocks/server.go index 02b12127..36090de0 100644 --- a/mocks/rpcservermocks/server.go +++ b/mocks/rpcservermocks/server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.36.0. DO NOT EDIT. package rpcservermocks diff --git a/mocks/secp256k1mocks/signer.go b/mocks/secp256k1mocks/signer.go index 5ab6e3b0..d52c4f28 100644 --- a/mocks/secp256k1mocks/signer.go +++ b/mocks/secp256k1mocks/signer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.36.0. DO NOT EDIT. package secp256k1mocks @@ -38,6 +38,32 @@ func (_m *Signer) Sign(message []byte) (*secp256k1.SignatureData, error) { return r0, r1 } +// SignDirect provides a mock function with given fields: message +func (_m *Signer) SignDirect(message []byte) (*secp256k1.SignatureData, error) { + ret := _m.Called(message) + + var r0 *secp256k1.SignatureData + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (*secp256k1.SignatureData, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func([]byte) *secp256k1.SignatureData); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*secp256k1.SignatureData) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // NewSigner creates a new instance of Signer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewSigner(t interface { diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go index 55324a48..4d7b7614 100644 --- a/pkg/ethsigner/typed_data.go +++ b/pkg/ethsigner/typed_data.go @@ -21,7 +21,6 @@ import ( "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethtypes" - "github.com/hyperledger/firefly-signer/pkg/rlp" "github.com/hyperledger/firefly-signer/pkg/secp256k1" ) @@ -38,20 +37,20 @@ func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip7 if err != nil { return nil, err } - sig, err := signer.Sign(encodedData) + // Note that signer.Sign performs the hash + sig, err := signer.SignDirect(encodedData) if err != nil { return nil, err } - rlpList := make(rlp.List, 0, 4) - rlpList = append(rlpList, rlp.Data(encodedData)) - rlpList = append(rlpList, rlp.WrapInt(sig.R)) - rlpList = append(rlpList, rlp.WrapInt(sig.S)) - rlpList = append(rlpList, rlp.WrapInt(sig.V)) + signatureBytes := make([]byte, 65) + signatureBytes[0] = byte(sig.V.Int64()) + sig.R.FillBytes(signatureBytes[1:33]) + sig.S.FillBytes(signatureBytes[33:65]) return &EIP712Result{ Hash: encodedData, - Signature: rlpList.Encode(), // per eth_signTypedData_v4 convention + Signature: signatureBytes, // compact ECDSA signature V: ethtypes.HexInteger(*sig.V), R: sig.R.FillBytes(make([]byte, 32)), S: sig.S.FillBytes(make([]byte, 32)), diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index 0208eeee..6bdfdaee 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -23,11 +23,13 @@ import ( "math/big" "testing" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly-signer/mocks/secp256k1mocks" "github.com/hyperledger/firefly-signer/pkg/eip712" "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/hyperledger/firefly-signer/pkg/secp256k1" + "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -58,7 +60,7 @@ func TestSignTypedDataV4(t *testing.T) { foundSig.S.SetBytes(sig.S) signaturePayload := ethtypes.HexBytes0xPrefix(sig.Hash) - addr, err := foundSig.Recover(signaturePayload, -1 /* chain id is in the domain (not applied EIP-155 style to the V value) */) + addr, err := foundSig.RecoverDirect(signaturePayload, -1 /* chain id is in the domain (not applied EIP-155 style to the V value) */) assert.NoError(t, err) assert.Equal(t, keypair.Address.String(), addr.String()) @@ -91,9 +93,77 @@ func TestSignTypedDataV4SignFail(t *testing.T) { } msn := &secp256k1mocks.Signer{} - msn.On("Sign", mock.Anything).Return(nil, fmt.Errorf("pop")) + msn.On("SignDirect", mock.Anything).Return(nil, fmt.Errorf("pop")) ctx := context.Background() _, err := SignTypedDataV4(ctx, msn, payload) assert.Regexp(t, "pop", err) } + +func TestMessage_2(t *testing.T) { + logrus.SetLevel(logrus.TraceLevel) + + var p eip712.TypedData + err := json.Unmarshal([]byte(`{ + "domain": { + "name": "test-app", + "version": "1", + "chainId": 31337, + "verifyingContract": "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0" + }, + "types": { + "Issuance": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "to", + "type": "address" + } + ], + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ] + }, + "primaryType": "Issuance", + "message": { + "amount": "1000", + "to": "0xce3a47d24140cca16f8839357ca5fada44a1baef" + } + }`), &p) + assert.NoError(t, err) + + ctx := context.Background() + ed, err := eip712.EncodeTypedDataV4(ctx, &p) + assert.NoError(t, err) + assert.Equal(t, "0xb0132202fa81cafac0e405917f86705728ba02912d185065697cc4ba4e61aec3", ed.String()) + + keys, err := secp256k1.NewSecp256k1KeyPair([]byte(`8d01666832be7eb2dbd57cd3d4410d0231a91533f895de76d0930c689618aefd`)) + assert.NoError(t, err) + assert.Equal(t, "0xbcef501facf72ddacdb055acc2716786ff038728", keys.Address.String()) + + signed, err := SignTypedDataV4(ctx, keys, &p) + assert.NoError(t, err) + + assert.Equal(t, "0xb0132202fa81cafac0e405917f86705728ba02912d185065697cc4ba4e61aec3", signed.Hash.String()) + + pubKey, _, err := ecdsa.RecoverCompact(signed.Signature, signed.Hash) + assert.NoError(t, err) + assert.Equal(t, "0xbcef501facf72ddacdb055acc2716786ff038728", secp256k1.PublicKeyToAddress(pubKey).String()) +} diff --git a/pkg/secp256k1/signer.go b/pkg/secp256k1/signer.go index 4094ed01..06e22304 100644 --- a/pkg/secp256k1/signer.go +++ b/pkg/secp256k1/signer.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2023 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -34,6 +34,7 @@ type SignatureData struct { // Signer is the low level common interface that can be implemented by any module which provides signature capability type Signer interface { Sign(message []byte) (*SignatureData, error) + SignDirect(message []byte) (*SignatureData, error) } // getVNormalized returns the original 27/28 parity @@ -67,10 +68,16 @@ func (s *SignatureData) UpdateEIP2930() { s.V = s.V.Sub(s.V, big.NewInt(27)) } -// Recover obtains the original signer +// Recover obtains the original signer from the hash of the message func (s *SignatureData) Recover(message []byte, chainID int64) (a *ethtypes.Address0xHex, err error) { msgHash := sha3.NewLegacyKeccak256() msgHash.Write(message) + return s.RecoverDirect(msgHash.Sum(nil), chainID) +} + +// Recover obtains the original signer +func (s *SignatureData) RecoverDirect(message []byte, chainID int64) (a *ethtypes.Address0xHex, err error) { + signatureBytes := make([]byte, 65) signatureBytes[0], err = s.getVNormalized(chainID) if err != nil { @@ -78,21 +85,27 @@ func (s *SignatureData) Recover(message []byte, chainID int64) (a *ethtypes.Addr } s.R.FillBytes(signatureBytes[1:33]) s.S.FillBytes(signatureBytes[33:65]) - pubKey, _, err := ecdsa.RecoverCompact(signatureBytes, msgHash.Sum(nil)) // uses S256() by default + pubKey, _, err := ecdsa.RecoverCompact(signatureBytes, message) // uses S256() by default if err != nil { return nil, err } return PublicKeyToAddress(pubKey), nil } -// Sign performs raw signing - give legacy 27/28 V values +// Sign hashes the input then signs it func (k *KeyPair) Sign(message []byte) (ethSig *SignatureData, err error) { + msgHash := sha3.NewLegacyKeccak256() + msgHash.Write(message) + hashed := msgHash.Sum(nil) + return k.SignDirect(hashed) +} + +// SignDirect performs raw signing - give legacy 27/28 V values +func (k *KeyPair) SignDirect(message []byte) (ethSig *SignatureData, err error) { if k == nil { return nil, fmt.Errorf("nil signer") } - msgHash := sha3.NewLegacyKeccak256() - msgHash.Write(message) - sig, err := ecdsa.SignCompact(k.PrivateKey, msgHash.Sum(nil), false) // uses S256() by default + sig, err := ecdsa.SignCompact(k.PrivateKey, message, false) // uses S256() by default if err == nil { // btcec does all the hard work for us. However, the interface of btcec is such // that we need to unpack the result for Ethereum encoding. From f06d0f7c87b7a163e988ea87a0874b31d09833ae Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 9 Nov 2023 00:50:55 -0500 Subject: [PATCH 21/28] Update interface to split sign/sign-direct Signed-off-by: Peter Broadhurst --- Makefile | 9 +++-- mocks/secp256k1mocks/signer.go | 26 ------------- mocks/secp256k1mocks/signer_direct.go | 53 +++++++++++++++++++++++++++ pkg/ethsigner/typed_data.go | 2 +- pkg/ethsigner/typed_data_test.go | 2 +- pkg/secp256k1/signer.go | 3 ++ 6 files changed, 63 insertions(+), 32 deletions(-) create mode 100644 mocks/secp256k1mocks/signer_direct.go diff --git a/Makefile b/Makefile index 3e7f9a69..5a8abad1 100644 --- a/Makefile +++ b/Makefile @@ -30,10 +30,11 @@ mocks-$(strip $(1))-$(strip $(2)): ${MOCKERY} ${MOCKERY} --case underscore --dir $(1) --name $(2) --outpkg $(3) --output mocks/$(strip $(3)) endef -$(eval $(call makemock, pkg/ethsigner, Wallet, ethsignermocks)) -$(eval $(call makemock, pkg/secp256k1, Signer, secp256k1mocks)) -$(eval $(call makemock, internal/rpcserver, Server, rpcservermocks)) -$(eval $(call makemock, pkg/rpcbackend, Backend, rpcbackendmocks)) +$(eval $(call makemock, pkg/ethsigner, Wallet, ethsignermocks)) +$(eval $(call makemock, pkg/secp256k1, Signer, secp256k1mocks)) +$(eval $(call makemock, pkg/secp256k1, SignerDirect, secp256k1mocks)) +$(eval $(call makemock, internal/rpcserver, Server, rpcservermocks)) +$(eval $(call makemock, pkg/rpcbackend, Backend, rpcbackendmocks)) firefly-signer: ${GOFILES} $(VGO) build -o ./firefly-signer -ldflags "-X main.buildDate=`date -u +\"%Y-%m-%dT%H:%M:%SZ\"` -X main.buildVersion=$(BUILD_VERSION)" -tags=prod -tags=prod -v ./ffsigner diff --git a/mocks/secp256k1mocks/signer.go b/mocks/secp256k1mocks/signer.go index d52c4f28..c02c082c 100644 --- a/mocks/secp256k1mocks/signer.go +++ b/mocks/secp256k1mocks/signer.go @@ -38,32 +38,6 @@ func (_m *Signer) Sign(message []byte) (*secp256k1.SignatureData, error) { return r0, r1 } -// SignDirect provides a mock function with given fields: message -func (_m *Signer) SignDirect(message []byte) (*secp256k1.SignatureData, error) { - ret := _m.Called(message) - - var r0 *secp256k1.SignatureData - var r1 error - if rf, ok := ret.Get(0).(func([]byte) (*secp256k1.SignatureData, error)); ok { - return rf(message) - } - if rf, ok := ret.Get(0).(func([]byte) *secp256k1.SignatureData); ok { - r0 = rf(message) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*secp256k1.SignatureData) - } - } - - if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(message) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - // NewSigner creates a new instance of Signer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewSigner(t interface { diff --git a/mocks/secp256k1mocks/signer_direct.go b/mocks/secp256k1mocks/signer_direct.go new file mode 100644 index 00000000..7e5f7c3d --- /dev/null +++ b/mocks/secp256k1mocks/signer_direct.go @@ -0,0 +1,53 @@ +// Code generated by mockery v2.36.0. DO NOT EDIT. + +package secp256k1mocks + +import ( + secp256k1 "github.com/hyperledger/firefly-signer/pkg/secp256k1" + mock "github.com/stretchr/testify/mock" +) + +// SignerDirect is an autogenerated mock type for the SignerDirect type +type SignerDirect struct { + mock.Mock +} + +// SignDirect provides a mock function with given fields: message +func (_m *SignerDirect) SignDirect(message []byte) (*secp256k1.SignatureData, error) { + ret := _m.Called(message) + + var r0 *secp256k1.SignatureData + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (*secp256k1.SignatureData, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func([]byte) *secp256k1.SignatureData); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*secp256k1.SignatureData) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewSignerDirect creates a new instance of SignerDirect. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSignerDirect(t interface { + mock.TestingT + Cleanup(func()) +}) *SignerDirect { + mock := &SignerDirect{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go index 4d7b7614..b111a780 100644 --- a/pkg/ethsigner/typed_data.go +++ b/pkg/ethsigner/typed_data.go @@ -32,7 +32,7 @@ type EIP712Result struct { S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` } -func SignTypedDataV4(ctx context.Context, signer secp256k1.Signer, payload *eip712.TypedData) (*EIP712Result, error) { +func SignTypedDataV4(ctx context.Context, signer secp256k1.SignerDirect, payload *eip712.TypedData) (*EIP712Result, error) { encodedData, err := eip712.EncodeTypedDataV4(ctx, payload) if err != nil { return nil, err diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index 6bdfdaee..2e50cbd4 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -92,7 +92,7 @@ func TestSignTypedDataV4SignFail(t *testing.T) { PrimaryType: eip712.EIP712Domain, } - msn := &secp256k1mocks.Signer{} + msn := &secp256k1mocks.SignerDirect{} msn.On("SignDirect", mock.Anything).Return(nil, fmt.Errorf("pop")) ctx := context.Background() diff --git a/pkg/secp256k1/signer.go b/pkg/secp256k1/signer.go index 06e22304..5a4f71ea 100644 --- a/pkg/secp256k1/signer.go +++ b/pkg/secp256k1/signer.go @@ -34,6 +34,9 @@ type SignatureData struct { // Signer is the low level common interface that can be implemented by any module which provides signature capability type Signer interface { Sign(message []byte) (*SignatureData, error) +} + +type SignerDirect interface { SignDirect(message []byte) (*SignatureData, error) } From 187d4581d7d6a5a5d7e812c064ef61293f627fa1 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Thu, 9 Nov 2023 00:55:21 -0500 Subject: [PATCH 22/28] More fiddling with interface Signed-off-by: Peter Broadhurst --- mocks/secp256k1mocks/signer.go | 12 ++++++------ mocks/secp256k1mocks/signer_direct.go | 26 ++++++++++++++++++++++++++ pkg/secp256k1/signer.go | 3 ++- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/mocks/secp256k1mocks/signer.go b/mocks/secp256k1mocks/signer.go index c02c082c..737cfd2c 100644 --- a/mocks/secp256k1mocks/signer.go +++ b/mocks/secp256k1mocks/signer.go @@ -12,17 +12,17 @@ type Signer struct { mock.Mock } -// Sign provides a mock function with given fields: message -func (_m *Signer) Sign(message []byte) (*secp256k1.SignatureData, error) { - ret := _m.Called(message) +// Sign provides a mock function with given fields: msgToHashAndSign +func (_m *Signer) Sign(msgToHashAndSign []byte) (*secp256k1.SignatureData, error) { + ret := _m.Called(msgToHashAndSign) var r0 *secp256k1.SignatureData var r1 error if rf, ok := ret.Get(0).(func([]byte) (*secp256k1.SignatureData, error)); ok { - return rf(message) + return rf(msgToHashAndSign) } if rf, ok := ret.Get(0).(func([]byte) *secp256k1.SignatureData); ok { - r0 = rf(message) + r0 = rf(msgToHashAndSign) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*secp256k1.SignatureData) @@ -30,7 +30,7 @@ func (_m *Signer) Sign(message []byte) (*secp256k1.SignatureData, error) { } if rf, ok := ret.Get(1).(func([]byte) error); ok { - r1 = rf(message) + r1 = rf(msgToHashAndSign) } else { r1 = ret.Error(1) } diff --git a/mocks/secp256k1mocks/signer_direct.go b/mocks/secp256k1mocks/signer_direct.go index 7e5f7c3d..f0bd08a2 100644 --- a/mocks/secp256k1mocks/signer_direct.go +++ b/mocks/secp256k1mocks/signer_direct.go @@ -12,6 +12,32 @@ type SignerDirect struct { mock.Mock } +// Sign provides a mock function with given fields: msgToHashAndSign +func (_m *SignerDirect) Sign(msgToHashAndSign []byte) (*secp256k1.SignatureData, error) { + ret := _m.Called(msgToHashAndSign) + + var r0 *secp256k1.SignatureData + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (*secp256k1.SignatureData, error)); ok { + return rf(msgToHashAndSign) + } + if rf, ok := ret.Get(0).(func([]byte) *secp256k1.SignatureData); ok { + r0 = rf(msgToHashAndSign) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*secp256k1.SignatureData) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(msgToHashAndSign) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SignDirect provides a mock function with given fields: message func (_m *SignerDirect) SignDirect(message []byte) (*secp256k1.SignatureData, error) { ret := _m.Called(message) diff --git a/pkg/secp256k1/signer.go b/pkg/secp256k1/signer.go index 5a4f71ea..dd76ef2b 100644 --- a/pkg/secp256k1/signer.go +++ b/pkg/secp256k1/signer.go @@ -33,10 +33,11 @@ type SignatureData struct { // Signer is the low level common interface that can be implemented by any module which provides signature capability type Signer interface { - Sign(message []byte) (*SignatureData, error) + Sign(msgToHashAndSign []byte) (*SignatureData, error) } type SignerDirect interface { + Signer SignDirect(message []byte) (*SignatureData, error) } From ada360f7a6b641132aad02ca76c2644ece0deacc Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 10 Nov 2023 13:00:19 -0500 Subject: [PATCH 23/28] Use Eth convention for signature Signed-off-by: Peter Broadhurst --- pkg/ethsigner/typed_data.go | 30 +++++++++++++++++------------- pkg/ethsigner/typed_data_test.go | 11 ++++++++++- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/pkg/ethsigner/typed_data.go b/pkg/ethsigner/typed_data.go index b111a780..63aaeba5 100644 --- a/pkg/ethsigner/typed_data.go +++ b/pkg/ethsigner/typed_data.go @@ -25,11 +25,11 @@ import ( ) type EIP712Result struct { - Hash ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"hash"` - Signature ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"signature"` - V ethtypes.HexInteger `ffstruct:"EIP712Result" json:"v"` - R ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"r"` - S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` + Hash ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"hash"` + SignatureRSV ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"signatureRSV"` + V ethtypes.HexInteger `ffstruct:"EIP712Result" json:"v"` + R ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"r"` + S ethtypes.HexBytes0xPrefix `ffstruct:"EIP712Result" json:"s"` } func SignTypedDataV4(ctx context.Context, signer secp256k1.SignerDirect, payload *eip712.TypedData) (*EIP712Result, error) { @@ -44,15 +44,19 @@ func SignTypedDataV4(ctx context.Context, signer secp256k1.SignerDirect, payload } signatureBytes := make([]byte, 65) - signatureBytes[0] = byte(sig.V.Int64()) - sig.R.FillBytes(signatureBytes[1:33]) - sig.S.FillBytes(signatureBytes[33:65]) + sig.R.FillBytes(signatureBytes[0:32]) + sig.S.FillBytes(signatureBytes[32:64]) + signatureBytes[64] = byte(sig.V.Int64()) return &EIP712Result{ - Hash: encodedData, - Signature: signatureBytes, // compact ECDSA signature - V: ethtypes.HexInteger(*sig.V), - R: sig.R.FillBytes(make([]byte, 32)), - S: sig.S.FillBytes(make([]byte, 32)), + Hash: encodedData, + // Include the clearly distinguished V, R & S values of the signature + V: ethtypes.HexInteger(*sig.V), + R: sig.R.FillBytes(make([]byte, 32)), + S: sig.S.FillBytes(make([]byte, 32)), + // the Ethereum convention (which is different to the Golang convention) is to encode compact signatures as + // 65 bytes - R (32B), S (32B), V (1B) + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7294d34c17ca215c201b3772ff67036fa4b1ef12/contracts/utils/cryptography/ECDSA.sol#L56-L73 + SignatureRSV: signatureBytes, }, nil } diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index 2e50cbd4..fb222006 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -163,7 +163,16 @@ func TestMessage_2(t *testing.T) { assert.Equal(t, "0xb0132202fa81cafac0e405917f86705728ba02912d185065697cc4ba4e61aec3", signed.Hash.String()) - pubKey, _, err := ecdsa.RecoverCompact(signed.Signature, signed.Hash) + // The golang convention is V, R, S for the compact signature (differing from Ethereum's convention of R, S, V) + golangCompactSignature := make([]byte, 65) + golangCompactSignature[0] = signed.SignatureRSV[64] + copy(golangCompactSignature[1:33], signed.SignatureRSV[0:32]) + copy(golangCompactSignature[33:65], signed.SignatureRSV[32:64]) + + fmt.Printf("%s\n", ethtypes.HexBytes0xPrefix(golangCompactSignature)) + fmt.Printf("%s\n", ethtypes.HexBytes0xPrefix(signed.SignatureRSV)) + + pubKey, _, err := ecdsa.RecoverCompact(golangCompactSignature, signed.Hash) assert.NoError(t, err) assert.Equal(t, "0xbcef501facf72ddacdb055acc2716786ff038728", secp256k1.PublicKeyToAddress(pubKey).String()) } From 8108a602012d39c4007de11c0480c4bb62bb8ef7 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 10 Nov 2023 16:33:46 -0500 Subject: [PATCH 24/28] Ensure documentation of field values Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_field_descriptions.go | 16 ++++++++++++++++ pkg/ethsigner/transaction.go | 18 +++++++++--------- pkg/ethsigner/transaction_test.go | 5 +++++ pkg/ethsigner/typed_data_test.go | 5 +++++ 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/internal/signermsgs/en_field_descriptions.go b/internal/signermsgs/en_field_descriptions.go index 9746955f..9730d8fa 100644 --- a/internal/signermsgs/en_field_descriptions.go +++ b/internal/signermsgs/en_field_descriptions.go @@ -31,4 +31,20 @@ var ( ABIParameterComponents = ffm("EthABIParameter.components", "An array of components, if the parameter is a tuple") ABIParameterIndexed = ffm("EthABIParameter.indexed", "Whether this parameter uses one of the topics, or is in the data area") ABIParameterInternalType = ffm("EthABIParameter.internalType", "Used by the solc compiler to include additional details - importantly the struct name for tuples") + + EthTransactionFrom = ffm("EthTransaction.internalType", "The from address (not encoded into the transaction directly, but used on this structure on input)") + EthTransactionNonce = ffm("EthTransaction.nonce", "Number used once (nonce) that specifies the sequence of this transaction in all transactions sent to the chain from this signing address") + EthTransactionGasPrice = ffm("EthTransaction.gasPrice", "The price per unit offered for the gas used when executing this transaction, if submitting to a chain that requires gas fees (in wei of the native chain token)") + EthTransactionMaxPriorityFeePerGas = ffm("EthTransaction.maxPriorityFeePerGas", "Part of the EIP-1559 extension to transaction pricing. The amount provided to the miner of the block per unit of gas, in addition to the base fee (which is burned when the block is mined)") + EthTransactionMaxFeePerGas = ffm("EthTransaction.maxFeePerGas", "Part of the EIP-1559 extension to transaction pricing. The total amount you are willing to pay per unit of gas used by your contract, which is the total of the baseFeePerGas (determined by the chain at execution time) and the maxPriorityFeePerGas") + EthTransactionGas = ffm("EthTransaction.gas", "The gas limit for execution of your transaction. Must be provided regardless of whether you paying a fee for the gas") + EthTransactionTo = ffm("EthTransaction.to", "The target address of the transaction. Omitted for contract deployments") + EthTransactionValue = ffm("EthTransaction.value", "An optional amount of native token to transfer along with the transaction (in wei)") + EthTransactionData = ffm("EthTransaction.data", "The encoded and signed transaction payload") + + EIP712ResultHash = ffm("EIP712Result.hash", "The EIP-712 hash generated according to the Typed Data V4 algorithm") + EIP712ResultSignatureRSV = ffm("EIP712Result.signatureRSV", "Hex encoded array of 65 bytes containing the R, S & V of the ECDSA signature. This is the standard signature encoding used in Ethereum recover utilities (note that some other utilities might expect a different encoding/packing of the data)") + EIP712ResultV = ffm("EIP712Result.v", "The V value of the ECDSA signature as a hex encoded integer") + EIP712ResultR = ffm("EIP712Result.r", "The R value of the ECDSA signature as a 32byte hex encoded array") + EIP712ResultS = ffm("EIP712Result.s", "The S value of the ECDSA signature as a 32byte hex encoded array") ) diff --git a/pkg/ethsigner/transaction.go b/pkg/ethsigner/transaction.go index 9f5b84a0..191d892f 100644 --- a/pkg/ethsigner/transaction.go +++ b/pkg/ethsigner/transaction.go @@ -52,15 +52,15 @@ const ( ) type Transaction struct { - From json.RawMessage `json:"from,omitempty"` // only here as a possible input to signing key selection (eth_sendTransaction) - Nonce *ethtypes.HexInteger `json:"nonce,omitempty"` - GasPrice *ethtypes.HexInteger `json:"gasPrice,omitempty"` - MaxPriorityFeePerGas *ethtypes.HexInteger `json:"maxPriorityFeePerGas,omitempty"` - MaxFeePerGas *ethtypes.HexInteger `json:"maxFeePerGas,omitempty"` - GasLimit *ethtypes.HexInteger `json:"gas,omitempty"` // note this is required for some methods (eth_estimateGas) - To *ethtypes.Address0xHex `json:"to,omitempty"` - Value *ethtypes.HexInteger `json:"value,omitempty"` - Data ethtypes.HexBytes0xPrefix `json:"data"` + From json.RawMessage `ffstruct:"EthTransaction" json:"from,omitempty"` // only here as a possible input to signing key selection (eth_sendTransaction) + Nonce *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"nonce,omitempty"` + GasPrice *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"gasPrice,omitempty"` + MaxPriorityFeePerGas *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"maxPriorityFeePerGas,omitempty"` + MaxFeePerGas *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"maxFeePerGas,omitempty"` + GasLimit *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"gas,omitempty"` // note this is required for some methods (eth_estimateGas) + To *ethtypes.Address0xHex `ffstruct:"EthTransaction" json:"to,omitempty"` + Value *ethtypes.HexInteger `ffstruct:"EthTransaction" json:"value,omitempty"` + Data ethtypes.HexBytes0xPrefix `ffstruct:"EthTransaction" json:"data"` } func (t *Transaction) BuildLegacy() rlp.List { diff --git a/pkg/ethsigner/transaction_test.go b/pkg/ethsigner/transaction_test.go index 03f66b2d..6f7c8124 100644 --- a/pkg/ethsigner/transaction_test.go +++ b/pkg/ethsigner/transaction_test.go @@ -22,6 +22,7 @@ import ( "math/big" "testing" + "github.com/hyperledger/firefly-common/pkg/ffapi" "github.com/hyperledger/firefly-signer/mocks/secp256k1mocks" "github.com/hyperledger/firefly-signer/pkg/ethtypes" "github.com/hyperledger/firefly-signer/pkg/rlp" @@ -265,3 +266,7 @@ func TestSignEIP1559Error(t *testing.T) { _, err := txn.SignEIP1559(msn, 12345) assert.Regexp(t, "pop", err) } + +func TestEthTXDocumented(t *testing.T) { + ffapi.CheckObjectDocumented(&Transaction{}) +} diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index fb222006..2bc3269b 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/btcsuite/btcd/btcec/v2/ecdsa" + "github.com/hyperledger/firefly-common/pkg/ffapi" "github.com/hyperledger/firefly-common/pkg/log" "github.com/hyperledger/firefly-signer/mocks/secp256k1mocks" "github.com/hyperledger/firefly-signer/pkg/eip712" @@ -176,3 +177,7 @@ func TestMessage_2(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "0xbcef501facf72ddacdb055acc2716786ff038728", secp256k1.PublicKeyToAddress(pubKey).String()) } + +func TestEIP712ResultDocumented(t *testing.T) { + ffapi.CheckObjectDocumented(&EIP712Result{}) +} From a302f6ea692dcdbe389055787f9818988114ba39 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Fri, 10 Nov 2023 16:47:16 -0500 Subject: [PATCH 25/28] Also document TypedData structure Signed-off-by: Peter Broadhurst --- internal/signermsgs/en_field_descriptions.go | 5 +++++ pkg/eip712/typed_data_v4_test.go | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/internal/signermsgs/en_field_descriptions.go b/internal/signermsgs/en_field_descriptions.go index 9730d8fa..3fba68b1 100644 --- a/internal/signermsgs/en_field_descriptions.go +++ b/internal/signermsgs/en_field_descriptions.go @@ -47,4 +47,9 @@ var ( EIP712ResultV = ffm("EIP712Result.v", "The V value of the ECDSA signature as a hex encoded integer") EIP712ResultR = ffm("EIP712Result.r", "The R value of the ECDSA signature as a 32byte hex encoded array") EIP712ResultS = ffm("EIP712Result.s", "The S value of the ECDSA signature as a 32byte hex encoded array") + + TypedDataDomain = ffm("TypedData.domain", "The data to encode into the EIP712Domain as part fo signing the transaction") + TypedDataMessage = ffm("TypedData.message", "The data to encode into primaryType structure, with nested values for any sub-structures") + TypedDataTypes = ffm("TypedData.types", "Array of types to use when encoding, which must include the primaryType and the EIP712Domain (noting the primary type can be EIP712Domain if the message is empty)") + TypedDataPrimaryType = ffm("TypedData.primaryType", "The primary type to begin encoding the EIP-712 hash from in the list of types, using the input message (unless set directly to EIP712Domain, in which case the message can be omitted)") ) diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index bf31208f..8c5d5a51 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -21,6 +21,7 @@ import ( "encoding/json" "testing" + "github.com/hyperledger/firefly-common/pkg/ffapi" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" ) @@ -624,3 +625,7 @@ func TestArrayValueInvalid(t *testing.T) { _, err = EncodeTypedDataV4(ctx, &p) assert.Regexp(t, "FF22030", err) } + +func TestTypedDataDocumented(t *testing.T) { + ffapi.CheckObjectDocumented(&TypedData{}) +} From ba683672a5122c90b9e1e1cf5f13898c0d1496bc Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 29 Nov 2023 08:58:50 -0500 Subject: [PATCH 26/28] Upgrade common and use consistent resty with common Signed-off-by: Peter Broadhurst --- go.mod | 4 ++-- go.sum | 31 +++++---------------------- mocks/ethsignermocks/wallet.go | 2 +- mocks/rpcbackendmocks/backend.go | 2 +- mocks/rpcservermocks/server.go | 2 +- mocks/secp256k1mocks/signer.go | 2 +- mocks/secp256k1mocks/signer_direct.go | 2 +- 7 files changed, 12 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 4800fae7..549c58fd 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,9 @@ go 1.20 require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/fsnotify/fsnotify v1.7.0 - github.com/go-resty/resty/v2 v2.10.0 + github.com/go-resty/resty/v2 v2.7.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf + github.com/hyperledger/firefly-common v1.4.0 github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 diff --git a/go.sum b/go.sum index 2d292a13..66ea1f03 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,8 @@ github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3K github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= -github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -156,8 +156,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf h1:CQMNZUTCQb0naxY7J14QEJGPBu31YY+qcSYUTC49epM= -github.com/hyperledger/firefly-common v1.3.1-0.20231101070740-00ccb16807bf/go.mod h1:FOV/HcGyaX94U4PcNvrP+lt9F2ROHFMbdX0rnLFrU1U= +github.com/hyperledger/firefly-common v1.4.0 h1:V5ikWLKkpT7XOmrpD/8MEpeQMamgFYI8JuffwXdLb3A= +github.com/hyperledger/firefly-common v1.4.0/go.mod h1:79ZrPclGZwBOqyapN0qNR+EWZm5BSvXfmCfbBEABYbM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -270,7 +270,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/hfuss/mux-prometheus v0.0.5 h1:Kcqyiekx8W2dO1EHg+6wOL1F0cFNgRO1uCK18V31D0s= gitlab.com/hfuss/mux-prometheus v0.0.5/go.mod h1:xcedy8rVGr9TFgRu2urfGuh99B4NdfYdpE4aUMQ0dxA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -287,7 +286,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= @@ -326,8 +324,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -359,10 +355,8 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -384,8 +378,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -422,19 +414,12 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -445,15 +430,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -501,8 +482,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/mocks/ethsignermocks/wallet.go b/mocks/ethsignermocks/wallet.go index 3e59b487..e879a103 100644 --- a/mocks/ethsignermocks/wallet.go +++ b/mocks/ethsignermocks/wallet.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package ethsignermocks diff --git a/mocks/rpcbackendmocks/backend.go b/mocks/rpcbackendmocks/backend.go index 9d03542c..c98bd97d 100644 --- a/mocks/rpcbackendmocks/backend.go +++ b/mocks/rpcbackendmocks/backend.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package rpcbackendmocks diff --git a/mocks/rpcservermocks/server.go b/mocks/rpcservermocks/server.go index 36090de0..b28604ff 100644 --- a/mocks/rpcservermocks/server.go +++ b/mocks/rpcservermocks/server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package rpcservermocks diff --git a/mocks/secp256k1mocks/signer.go b/mocks/secp256k1mocks/signer.go index 737cfd2c..435ad220 100644 --- a/mocks/secp256k1mocks/signer.go +++ b/mocks/secp256k1mocks/signer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package secp256k1mocks diff --git a/mocks/secp256k1mocks/signer_direct.go b/mocks/secp256k1mocks/signer_direct.go index f0bd08a2..6bf5f346 100644 --- a/mocks/secp256k1mocks/signer_direct.go +++ b/mocks/secp256k1mocks/signer_direct.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.36.0. DO NOT EDIT. +// Code generated by mockery v2.37.1. DO NOT EDIT. package secp256k1mocks From f470ea1b3827eb909fc62b66d75adc4e6d6f2ff6 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Wed, 29 Nov 2023 11:29:14 -0500 Subject: [PATCH 27/28] Pick up PR-112 from common Signed-off-by: Peter Broadhurst --- go.mod | 2 +- go.sum | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 549c58fd..388e96ee 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 github.com/go-resty/resty/v2 v2.7.0 github.com/gorilla/mux v1.8.0 - github.com/hyperledger/firefly-common v1.4.0 + github.com/hyperledger/firefly-common v1.4.1-0.20231129162504-c603e8182052 github.com/karlseguin/ccache v2.0.3+incompatible github.com/pelletier/go-toml v1.9.5 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 diff --git a/go.sum b/go.sum index 66ea1f03..e706b992 100644 --- a/go.sum +++ b/go.sum @@ -82,6 +82,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= @@ -89,6 +90,7 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -158,6 +160,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hyperledger/firefly-common v1.4.0 h1:V5ikWLKkpT7XOmrpD/8MEpeQMamgFYI8JuffwXdLb3A= github.com/hyperledger/firefly-common v1.4.0/go.mod h1:79ZrPclGZwBOqyapN0qNR+EWZm5BSvXfmCfbBEABYbM= +github.com/hyperledger/firefly-common v1.4.1-0.20231129162504-c603e8182052 h1:HCOU/wsY3SvD6M5qHt4j4RgV8s/eFtvPiqo6VHDZNis= +github.com/hyperledger/firefly-common v1.4.1-0.20231129162504-c603e8182052/go.mod h1:79ZrPclGZwBOqyapN0qNR+EWZm5BSvXfmCfbBEABYbM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -167,8 +171,11 @@ github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache v2.0.3+incompatible h1:j68C9tWOROiOLWTS/kCGg9IcJG+ACqn5+0+t8Oh83UU= github.com/karlseguin/ccache v2.0.3+incompatible/go.mod h1:CM9tNPzT6EdRh14+jiW8mEF9mkNZuuE51qmgGYUB93w= github.com/karlseguin/expect v1.0.8 h1:Bb0H6IgBWQpadY25UDNkYPDB9ITqK1xnSoZfAq362fw= @@ -194,8 +201,11 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -262,6 +272,7 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= From 04de0493516827421a99c176a353b1c8a9442683 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 3 Dec 2023 16:00:18 -0500 Subject: [PATCH 28/28] Remove duplicate message Signed-off-by: Peter Broadhurst --- go.sum | 11 ----------- internal/signermsgs/en_error_messges.go | 7 +++---- pkg/abi/inputparsing.go | 2 +- pkg/abi/inputparsing_test.go | 2 +- pkg/eip712/abi_to_typed_data_test.go | 2 +- pkg/eip712/typed_data_v4_test.go | 4 ++-- pkg/ethsigner/typed_data_test.go | 2 +- 7 files changed, 9 insertions(+), 21 deletions(-) diff --git a/go.sum b/go.sum index e706b992..f35bcee6 100644 --- a/go.sum +++ b/go.sum @@ -82,7 +82,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= @@ -90,7 +89,6 @@ github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+ github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -158,8 +156,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hyperledger/firefly-common v1.4.0 h1:V5ikWLKkpT7XOmrpD/8MEpeQMamgFYI8JuffwXdLb3A= -github.com/hyperledger/firefly-common v1.4.0/go.mod h1:79ZrPclGZwBOqyapN0qNR+EWZm5BSvXfmCfbBEABYbM= github.com/hyperledger/firefly-common v1.4.1-0.20231129162504-c603e8182052 h1:HCOU/wsY3SvD6M5qHt4j4RgV8s/eFtvPiqo6VHDZNis= github.com/hyperledger/firefly-common v1.4.1-0.20231129162504-c603e8182052/go.mod h1:79ZrPclGZwBOqyapN0qNR+EWZm5BSvXfmCfbBEABYbM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -171,11 +167,8 @@ github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdi github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/karlseguin/ccache v2.0.3+incompatible h1:j68C9tWOROiOLWTS/kCGg9IcJG+ACqn5+0+t8Oh83UU= github.com/karlseguin/ccache v2.0.3+incompatible/go.mod h1:CM9tNPzT6EdRh14+jiW8mEF9mkNZuuE51qmgGYUB93w= github.com/karlseguin/expect v1.0.8 h1:Bb0H6IgBWQpadY25UDNkYPDB9ITqK1xnSoZfAq362fw= @@ -201,11 +194,8 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= @@ -272,7 +262,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0 h1:3UeQBvD0TFrlVjOeLOBz+CPAI8dnbqNSVwUwRrkp7vQ= github.com/wsxiaoys/terminal v0.0.0-20160513160801-0940f3fc43a0/go.mod h1:IXCdmsXIht47RaVFLEdVnh1t+pgYtTAhQGj73kz+2DM= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= diff --git a/internal/signermsgs/en_error_messges.go b/internal/signermsgs/en_error_messges.go index a22af0c5..5ec6fdcf 100644 --- a/internal/signermsgs/en_error_messges.go +++ b/internal/signermsgs/en_error_messges.go @@ -85,11 +85,11 @@ var ( MsgSubscribeResponseInvalid = ffe("FF22066", "Subscription response invalid") MsgWebSocketReconnected = ffe("FF22067", "WebSocket reconnected during JSON/RPC call") MsgContextCancelledWSConnect = ffe("FF22068", "Context canceled while connecting WebSocket") - MsgEIP712UnknownABICompType = ffe("FF22069", "Unknown ABI component type: %s") - MsgNotElementary = ffe("FF22070", "Not elementary type: %s") + MsgNotElementary = ffe("FF22069", "Not elementary type: %s") + MsgEIP712UnknownABICompType = ffe("FF22070", "Unknown ABI component type: %s") MsgEIP712UnsupportedStrType = ffe("FF22071", "Unsupported type: %s") MsgEIP712UnsupportedABIType = ffe("FF22072", "ABI type not supported by EIP-712 encoding: %s") - MsgNotABIElementaryType = ffe("FF22073", "Not elementary type: %s") + MsgEIP712TypeNotFound = ffe("FF22073", "Type '%s' not found in type map") MsgEIP712PrimaryNotTuple = ffe("FF22074", "Type primary type must be a struct/tuple: %s") MsgEIP712BadInternalType = ffe("FF22075", "Failed to extract struct name from ABI internalType '%s'") MsgEIP712ValueNotMap = ffe("FF22076", "Value for struct '%s' not a map (%T)") @@ -97,5 +97,4 @@ var ( MsgEIP712ValueNotArray = ffe("FF22078", "Value for '%s' not an array (%T)") MsgEIP712InvalidArrayLen = ffe("FF22079", "Value for '%s' must have %d entries (found %d)") MsgEIP712PrimaryTypeRequired = ffe("FF22080", "Primary type must be specified") - MsgEIP712TypeNotFound = ffe("FF22081", "Type '%s' not found in type map") ) diff --git a/pkg/abi/inputparsing.go b/pkg/abi/inputparsing.go index 54329088..6c62933e 100644 --- a/pkg/abi/inputparsing.go +++ b/pkg/abi/inputparsing.go @@ -57,7 +57,7 @@ func (cv *ComponentValue) ElementaryABIDataCtx(ctx context.Context) (data []byte c := cv.Component et := cv.Component.ElementaryType().(*elementaryTypeInfo) if et == nil { - return nil, false, i18n.NewError(ctx, signermsgs.MsgNotABIElementaryType, c.String()) + return nil, false, i18n.NewError(ctx, signermsgs.MsgNotElementary, c.String()) } return et.encodeABIData(ctx, c.String(), c.(*typeComponent), cv.Value) } diff --git a/pkg/abi/inputparsing_test.go b/pkg/abi/inputparsing_test.go index 3b0a8591..0bfb9ad0 100644 --- a/pkg/abi/inputparsing_test.go +++ b/pkg/abi/inputparsing_test.go @@ -699,7 +699,7 @@ func TestTupleEncodeIndividualFixedParam(t *testing.T) { assert.Equal(t, TupleComponent, cv.Component.ComponentType()) assert.Len(t, cv.Children, 1) _, _, err = cv.ElementaryABIData() - assert.Regexp(t, "FF22073", err) + assert.Regexp(t, "FF22069", err) intComp := cv.Children[0] assert.Equal(t, ElementaryComponent, intComp.Component.ComponentType()) diff --git a/pkg/eip712/abi_to_typed_data_test.go b/pkg/eip712/abi_to_typed_data_test.go index d65c76f8..9b751da0 100644 --- a/pkg/eip712/abi_to_typed_data_test.go +++ b/pkg/eip712/abi_to_typed_data_test.go @@ -319,7 +319,7 @@ func TestMapElementaryABITypeNonElementary(t *testing.T) { assert.NoError(t, err) _, err = mapElementaryABIType(context.Background(), tc) - assert.Regexp(t, "FF22070", err) + assert.Regexp(t, "FF22069", err) } diff --git a/pkg/eip712/typed_data_v4_test.go b/pkg/eip712/typed_data_v4_test.go index 8c5d5a51..e3e7381c 100644 --- a/pkg/eip712/typed_data_v4_test.go +++ b/pkg/eip712/typed_data_v4_test.go @@ -406,7 +406,7 @@ func TestMissingPrimaryType(t *testing.T) { ctx := context.Background() _, err = EncodeTypedDataV4(ctx, &p) - assert.Regexp(t, "FF22081", err) + assert.Regexp(t, "FF22073", err) } func TestSecondaryTypeNotMap(t *testing.T) { @@ -515,7 +515,7 @@ func TestTupleNotSupported(t *testing.T) { ctx := context.Background() _, err = EncodeTypedDataV4(ctx, &p) - assert.Regexp(t, "FF22070", err) + assert.Regexp(t, "FF22069", err) } func TestAddressInvalid(t *testing.T) { diff --git a/pkg/ethsigner/typed_data_test.go b/pkg/ethsigner/typed_data_test.go index 2bc3269b..e29ab83f 100644 --- a/pkg/ethsigner/typed_data_test.go +++ b/pkg/ethsigner/typed_data_test.go @@ -84,7 +84,7 @@ func TestSignTypedDataV4BadPayload(t *testing.T) { ctx := context.Background() _, err = SignTypedDataV4(ctx, keypair, payload) - assert.Regexp(t, "FF22081", err) + assert.Regexp(t, "FF22073", err) } func TestSignTypedDataV4SignFail(t *testing.T) {