mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
Implemented latest recommendations
This commit is contained in:
parent
b0e872cd21
commit
8af9cda070
4 changed files with 388 additions and 192 deletions
|
|
@ -383,8 +383,8 @@ Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 3,
|
"id": 3,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
"result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
|
"result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -480,8 +480,8 @@ Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"jsonrpc": "2.0",
|
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
"result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
|
"result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -176,9 +176,9 @@ type (
|
||||||
ContentType string `json:"content_type"`
|
ContentType string `json:"content_type"`
|
||||||
Address common.MixedcaseAddress `json:"address"`
|
Address common.MixedcaseAddress `json:"address"`
|
||||||
Rawdata interface{} `json:"raw_data"`
|
Rawdata interface{} `json:"raw_data"`
|
||||||
Message []*NameValueType `json:"message"`
|
Message []*NameValueType `json:"message"`
|
||||||
Hash hexutil.Bytes `json:"hash"`
|
Hash hexutil.Bytes `json:"hash"`
|
||||||
Meta Metadata `json:"meta"`
|
Meta Metadata `json:"meta"`
|
||||||
}
|
}
|
||||||
SignDataResponse struct {
|
SignDataResponse struct {
|
||||||
Approved bool `json:"approved"`
|
Approved bool `json:"approved"`
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ type TypedDataDomain struct {
|
||||||
Salt string `json:"salt"`
|
Salt string `json:"salt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var typedDataRegexp = regexp.MustCompile(`^((address|bool|bytes|string)|((bytes)([1-9]|[1-2][0-9]|3[0-2]))|((int|uint)(8|16|32|64|128|256)))(\[\])?$`)
|
// var typedDataRegexp = regexp.MustCompile(`^((address|bool|bytes|int|uint|string)|((bytes)([1-9]|[1-2][0-9]|3[0-2]))|((int|uint)(8|16|32|64|128|256)))(\[\])?$`)
|
||||||
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[])?$`)
|
var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
|
||||||
|
|
||||||
// Sign receives a request and produces a signature
|
// Sign receives a request and produces a signature
|
||||||
|
|
||||||
|
|
@ -299,9 +299,6 @@ func SignTextPlain(data hexutil.Bytes) (hexutil.Bytes, string) {
|
||||||
// SignTypedData signs EIP-712 conformant typed data
|
// SignTypedData signs EIP-712 conformant typed data
|
||||||
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
|
// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
|
||||||
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
|
func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
|
||||||
if err := typedData.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -396,6 +393,10 @@ func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes {
|
||||||
//
|
//
|
||||||
// each encoded member is 32-byte long
|
// each encoded member is 32-byte long
|
||||||
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
|
func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
|
||||||
|
if err := typedData.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
|
|
||||||
// Verify extra data
|
// Verify extra data
|
||||||
|
|
@ -430,7 +431,7 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
|
||||||
}
|
}
|
||||||
arrayBuffer.Write(encodedData)
|
arrayBuffer.Write(encodedData)
|
||||||
} else {
|
} else {
|
||||||
bytesValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth)
|
bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -495,15 +496,14 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
|
||||||
}
|
}
|
||||||
return crypto.Keccak256(bytesValue), nil
|
return crypto.Keccak256(bytesValue), nil
|
||||||
}
|
}
|
||||||
// bytes32 etc
|
|
||||||
if strings.HasPrefix(encType, "bytes") {
|
if strings.HasPrefix(encType, "bytes") {
|
||||||
sizeStr := strings.TrimPrefix(encType, "bytes")
|
lengthStr := strings.TrimPrefix(encType, "bytes")
|
||||||
size, err := strconv.Atoi(sizeStr)
|
length, err := strconv.Atoi(lengthStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid size on bytes: %v", sizeStr)
|
return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr)
|
||||||
}
|
}
|
||||||
if size < 0 || size > 32 {
|
if length < 0 || length > 32 {
|
||||||
return nil, fmt.Errorf("invalid size on bytes: %d", size)
|
return nil, fmt.Errorf("invalid size on bytes: %d", length)
|
||||||
}
|
}
|
||||||
if byteValue, ok := encValue.(hexutil.Bytes); !ok {
|
if byteValue, ok := encValue.(hexutil.Bytes); !ok {
|
||||||
return nil, dataMismatchError(encType, encValue)
|
return nil, dataMismatchError(encType, encValue)
|
||||||
|
|
@ -511,8 +511,22 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
|
||||||
return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil
|
return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
|
if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
|
||||||
|
length := 0
|
||||||
|
if encType == "int" || encType == "uint" {
|
||||||
|
length = 256
|
||||||
|
} else {
|
||||||
|
lengthStr := strings.TrimPrefix(strings.TrimPrefix(encType, "uint"), "int")
|
||||||
|
atoiSize, err := strconv.Atoi(lengthStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid size on integer: %v", lengthStr)
|
||||||
|
}
|
||||||
|
length = atoiSize
|
||||||
|
}
|
||||||
bigIntValue, ok := encValue.(*big.Int)
|
bigIntValue, ok := encValue.(*big.Int)
|
||||||
|
if bigIntValue.BitLen() > length {
|
||||||
|
return nil, fmt.Errorf("integer larger than '%v'", encType)
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, dataMismatchError(encType, encValue)
|
return nil, dataMismatchError(encType, encValue)
|
||||||
}
|
}
|
||||||
|
|
@ -591,12 +605,12 @@ func UnmarshalValidatorData(data interface{}) (ValidatorData, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate make sure the types are sound
|
// Validate makes sure the types are sound
|
||||||
func (typedData *TypedData) Validate() error {
|
func (typedData *TypedData) validate() error {
|
||||||
if err := typedData.Types.Validate(); err != nil {
|
if err := typedData.Types.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := typedData.Domain.Validate(); err != nil {
|
if err := typedData.Domain.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -724,7 +738,7 @@ func (nvt *NameValueType) Pprint(depth int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if the types object is conformant to the specs
|
// Validate checks if the types object is conformant to the specs
|
||||||
func (t Types) Validate() error {
|
func (t Types) validate() error {
|
||||||
for typeKey, typeArr := range t {
|
for typeKey, typeArr := range t {
|
||||||
for _, typeObj := range typeArr {
|
for _, typeObj := range typeArr {
|
||||||
if typeKey == typeObj.Type {
|
if typeKey == typeObj.Type {
|
||||||
|
|
@ -737,8 +751,7 @@ func (t Types) Validate() error {
|
||||||
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
|
if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
|
||||||
return fmt.Errorf("unknown reference type '%s", typeObj.Type)
|
return fmt.Errorf("unknown reference type '%s", typeObj.Type)
|
||||||
}
|
}
|
||||||
|
} else if !isPrimitiveTypeValid(typeObj.Type) {
|
||||||
} else if !typedDataRegexp.MatchString(typeObj.Type) {
|
|
||||||
return fmt.Errorf("unknown type '%s'", typeObj.Type)
|
return fmt.Errorf("unknown type '%s'", typeObj.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -746,9 +759,120 @@ func (t Types) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the primitive value is valid
|
||||||
|
func isPrimitiveTypeValid(primitiveType string) bool {
|
||||||
|
if primitiveType == "address" ||
|
||||||
|
primitiveType == "address[]" ||
|
||||||
|
primitiveType == "bool" ||
|
||||||
|
primitiveType == "bool[]" ||
|
||||||
|
primitiveType == "string" ||
|
||||||
|
primitiveType == "string[]" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if primitiveType == "bytes" ||
|
||||||
|
primitiveType == "bytes[]" ||
|
||||||
|
primitiveType == "bytes1" ||
|
||||||
|
primitiveType == "bytes1[]" ||
|
||||||
|
primitiveType == "bytes2" ||
|
||||||
|
primitiveType == "bytes2[]" ||
|
||||||
|
primitiveType == "bytes3" ||
|
||||||
|
primitiveType == "bytes3[]" ||
|
||||||
|
primitiveType == "bytes4" ||
|
||||||
|
primitiveType == "bytes4[]" ||
|
||||||
|
primitiveType == "bytes5" ||
|
||||||
|
primitiveType == "bytes5[]" ||
|
||||||
|
primitiveType == "bytes6" ||
|
||||||
|
primitiveType == "bytes6[]" ||
|
||||||
|
primitiveType == "bytes7" ||
|
||||||
|
primitiveType == "bytes7[]" ||
|
||||||
|
primitiveType == "bytes8" ||
|
||||||
|
primitiveType == "bytes8[]" ||
|
||||||
|
primitiveType == "bytes9" ||
|
||||||
|
primitiveType == "bytes9[]" ||
|
||||||
|
primitiveType == "bytes10" ||
|
||||||
|
primitiveType == "bytes10[]" ||
|
||||||
|
primitiveType == "bytes11" ||
|
||||||
|
primitiveType == "bytes11[]" ||
|
||||||
|
primitiveType == "bytes12" ||
|
||||||
|
primitiveType == "bytes12[]" ||
|
||||||
|
primitiveType == "bytes13" ||
|
||||||
|
primitiveType == "bytes13[]" ||
|
||||||
|
primitiveType == "bytes14" ||
|
||||||
|
primitiveType == "bytes14[]" ||
|
||||||
|
primitiveType == "bytes15" ||
|
||||||
|
primitiveType == "bytes15[]" ||
|
||||||
|
primitiveType == "bytes16" ||
|
||||||
|
primitiveType == "bytes16[]" ||
|
||||||
|
primitiveType == "bytes17" ||
|
||||||
|
primitiveType == "bytes17[]" ||
|
||||||
|
primitiveType == "bytes18" ||
|
||||||
|
primitiveType == "bytes18[]" ||
|
||||||
|
primitiveType == "bytes19" ||
|
||||||
|
primitiveType == "bytes19[]" ||
|
||||||
|
primitiveType == "bytes20" ||
|
||||||
|
primitiveType == "bytes20[]" ||
|
||||||
|
primitiveType == "bytes21" ||
|
||||||
|
primitiveType == "bytes21[]" ||
|
||||||
|
primitiveType == "bytes22" ||
|
||||||
|
primitiveType == "bytes22[]" ||
|
||||||
|
primitiveType == "bytes23" ||
|
||||||
|
primitiveType == "bytes23[]" ||
|
||||||
|
primitiveType == "bytes24" ||
|
||||||
|
primitiveType == "bytes24[]" ||
|
||||||
|
primitiveType == "bytes25" ||
|
||||||
|
primitiveType == "bytes25[]" ||
|
||||||
|
primitiveType == "bytes26" ||
|
||||||
|
primitiveType == "bytes26[]" ||
|
||||||
|
primitiveType == "bytes27" ||
|
||||||
|
primitiveType == "bytes27[]" ||
|
||||||
|
primitiveType == "bytes28" ||
|
||||||
|
primitiveType == "bytes28[]" ||
|
||||||
|
primitiveType == "bytes29" ||
|
||||||
|
primitiveType == "bytes29[]" ||
|
||||||
|
primitiveType == "bytes30" ||
|
||||||
|
primitiveType == "bytes30[]" ||
|
||||||
|
primitiveType == "bytes31" ||
|
||||||
|
primitiveType == "bytes31[]" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if primitiveType == "int" ||
|
||||||
|
primitiveType == "int[]" ||
|
||||||
|
primitiveType == "int8" ||
|
||||||
|
primitiveType == "int8[]" ||
|
||||||
|
primitiveType == "int16" ||
|
||||||
|
primitiveType == "int16[]" ||
|
||||||
|
primitiveType == "int32" ||
|
||||||
|
primitiveType == "int32[]" ||
|
||||||
|
primitiveType == "int64" ||
|
||||||
|
primitiveType == "int64[]" ||
|
||||||
|
primitiveType == "int128" ||
|
||||||
|
primitiveType == "int128[]" ||
|
||||||
|
primitiveType == "int256" ||
|
||||||
|
primitiveType == "int256[]" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if primitiveType == "uint" ||
|
||||||
|
primitiveType == "uint[]" ||
|
||||||
|
primitiveType == "uint8" ||
|
||||||
|
primitiveType == "uint8[]" ||
|
||||||
|
primitiveType == "uint16" ||
|
||||||
|
primitiveType == "uint16[]" ||
|
||||||
|
primitiveType == "uint32" ||
|
||||||
|
primitiveType == "uint32[]" ||
|
||||||
|
primitiveType == "uint64" ||
|
||||||
|
primitiveType == "uint64[]" ||
|
||||||
|
primitiveType == "uint128" ||
|
||||||
|
primitiveType == "uint128[]" ||
|
||||||
|
primitiveType == "uint256" ||
|
||||||
|
primitiveType == "uint256[]" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Validate checks if the given domain is valid, i.e. contains at least
|
// Validate checks if the given domain is valid, i.e. contains at least
|
||||||
// the minimum viable keys and values
|
// the minimum viable keys and values
|
||||||
func (domain *TypedDataDomain) Validate() error {
|
func (domain *TypedDataDomain) validate() error {
|
||||||
if domain.ChainId == big.NewInt(0) {
|
if domain.ChainId == big.NewInt(0) {
|
||||||
return errors.New("chainId must be specified according to EIP-155")
|
return errors.New("chainId must be specified according to EIP-155")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
}
|
}
|
||||||
if err != keystore.ErrDecrypt {
|
if err != keystore.ErrDecrypt {
|
||||||
t.Errorf("Expected ErrLocked! %v", err)
|
t.Errorf("Expected ErrLocked! '%v'", err)
|
||||||
}
|
}
|
||||||
control <- "No way"
|
control <- "No way"
|
||||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||||
|
|
@ -201,7 +201,7 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
}
|
}
|
||||||
if err != ErrRequestDenied {
|
if err != ErrRequestDenied {
|
||||||
t.Errorf("Expected ErrRequestDenied! %v", err)
|
t.Errorf("Expected ErrRequestDenied! '%v'", err)
|
||||||
}
|
}
|
||||||
// text/plain
|
// text/plain
|
||||||
control <- "Y"
|
control <- "Y"
|
||||||
|
|
@ -283,7 +283,7 @@ func TestMalformedDomainkeys(t *testing.T) {
|
||||||
// "chainId": 1,
|
// "chainId": 1,
|
||||||
// "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
// "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||||
//}
|
//}
|
||||||
var jsonTypedData = `
|
jsonTypedData := `
|
||||||
{
|
{
|
||||||
"types": {
|
"types": {
|
||||||
"EIP712Domain": [
|
"EIP712Domain": [
|
||||||
|
|
@ -348,20 +348,106 @@ func TestMalformedDomainkeys(t *testing.T) {
|
||||||
"contents": "Hello, Bob!"
|
"contents": "Hello, Bob!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`
|
||||||
|
var malformedDomainTypedData TypedData
|
||||||
|
err := json.Unmarshal([]byte(jsonTypedData), &malformedDomainTypedData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
|
}
|
||||||
|
_, err = malformedDomainTypedData.HashStruct("EIP712Domain", malformedDomainTypedData.Domain.Map())
|
||||||
|
if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
|
||||||
|
t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got '%v'", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMalformedTypesAndExtradata(t *testing.T) {
|
||||||
|
// Verifies several quirks
|
||||||
|
// 1. Using dynamic types and only validating the prefix:
|
||||||
|
//{
|
||||||
|
// "name": "chainId",
|
||||||
|
// "type": "uint256 ... and now for something completely different"
|
||||||
|
//}
|
||||||
|
// 2. Extra data in message:
|
||||||
|
//{
|
||||||
|
// "blahonga": "zonk bonk"
|
||||||
|
//}
|
||||||
|
jsonTypedData := `
|
||||||
|
{
|
||||||
|
"types": {
|
||||||
|
"EIP712Domain": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chainId",
|
||||||
|
"type": "uint256 ... and now for something completely different"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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!"
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
var malformedTypedData TypedData
|
var malformedTypedData TypedData
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
}
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err != nil {
|
malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
malformedTypedData.Message["blahonga"] = "zonk bonk"
|
||||||
}
|
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
||||||
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
|
if err == nil || err.Error() != "there is extra data provided in the message" {
|
||||||
if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
|
t.Errorf("Expected `there is extra data provided in the message`, got '%v'", err)
|
||||||
t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -447,173 +533,146 @@ func TestTypeMismatch(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
var malformedTypedData TypedData
|
var mismatchTypedData TypedData
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
err := json.Unmarshal([]byte(jsonTypedData), &mismatchTypedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
}
|
||||||
err = malformedTypedData.Validate()
|
_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
|
||||||
}
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err.Error() != "provided data 'Hello, Bob!' doesn't match type 'Person'" {
|
if err.Error() != "provided data 'Hello, Bob!' doesn't match type 'Person'" {
|
||||||
t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got %v", err)
|
t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got '%v'", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
malformedTypedData.Types["Mail"][2].Type = "Blahonga"
|
mismatchTypedData.Types["Mail"][2].Type = "Blahonga"
|
||||||
err = malformedTypedData.Validate()
|
_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
|
||||||
if err == nil || err.Error() != "reference type 'Blahonga' is undefined" {
|
if err == nil || err.Error() != "reference type 'Blahonga' is undefined" {
|
||||||
t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got %v", err)
|
t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got '%v'", err)
|
||||||
}
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err == nil || err.Error() != "unrecognized type 'Blahonga'" {
|
|
||||||
t.Errorf("Expected `unrecognized type 'Blahonga'`, got %v", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMalformedTypesAndExtradata(t *testing.T) {
|
func TestTypeOverflow(t *testing.T) {
|
||||||
// Verifies several quirks
|
|
||||||
// 1. Using dynamic types and only validating the prefix:
|
|
||||||
//{
|
|
||||||
// "name": "chainId",
|
|
||||||
// "type": "uint256 ... and now for something completely different"
|
|
||||||
//}
|
|
||||||
// 2. Extra data in message:
|
|
||||||
//{
|
|
||||||
// "blahonga": "zonk bonk"
|
|
||||||
//}
|
|
||||||
jsonTypedData := `
|
|
||||||
{
|
|
||||||
"types": {
|
|
||||||
"EIP712Domain": [
|
|
||||||
{
|
|
||||||
"name": "name",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "version",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "chainId",
|
|
||||||
"type": "uint256 ... and now for something completely different"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
var malformedTypedData TypedData
|
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
|
||||||
}
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err == nil || err.Error() != "unknown type 'uint256 ... and now for something completely different'" {
|
|
||||||
t.Fatalf("Expected `unknown type 'uint256 ... and now for something completely different'`, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
|
|
||||||
malformedTypedData.Message["blahonga"] = "zonk bonk"
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err == nil || err.Error() != "there is extra data provided in the message" {
|
|
||||||
t.Errorf("Expected `there is extra data provided in the message`, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTypeMismatch(t *testing.T) {
|
|
||||||
// Verifies data that doesn't fit into it:
|
// Verifies data that doesn't fit into it:
|
||||||
//{
|
//{
|
||||||
// "test": 65536 <-- test defined as uint8
|
// "test": 65536 <-- test defined as uint8
|
||||||
//}
|
//}
|
||||||
var malformedTypedData TypedData
|
var overflowTypedData TypedData
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
err := json.Unmarshal([]byte(jsonTypedData), &overflowTypedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
}
|
||||||
// Set test to something outside uint8
|
// Set test to something outside uint8
|
||||||
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = 65536
|
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536)
|
||||||
|
|
||||||
err = malformedTypedData.Validate()
|
_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
|
||||||
if err != nil {
|
if err == nil || err.Error() != "integer larger than 'uint8'" {
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
t.Fatalf("Expected `integer larger than 'uint8'`, got '%v'", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
|
||||||
if err == nil || err.Error() != "provided data '65536' doesn't match type 'uint8'" {
|
(overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
|
||||||
t.Fatalf("Expected `provided data '65536' doesn't match type 'uint8'`, got %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
(malformedTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
|
_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
|
||||||
(malformedTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
|
|
||||||
|
|
||||||
_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Expected no err, got %v", err)
|
t.Fatalf("Expected no err, got '%v'", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFormatter(t *testing.T) {
|
func TestArray(t *testing.T) {
|
||||||
|
// Makes sure that arrays work fine
|
||||||
|
//{
|
||||||
|
// "type": "address[]"
|
||||||
|
//},
|
||||||
|
//{
|
||||||
|
// "type": "string[]"
|
||||||
|
//},
|
||||||
|
//{
|
||||||
|
// "type": "uint16[]",
|
||||||
|
//}
|
||||||
|
|
||||||
var d TypedData
|
jsonTypedData := `
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &d)
|
{
|
||||||
|
"types": {
|
||||||
|
"EIP712Domain": [
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "chainId",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "verifyingContract",
|
||||||
|
"type": "address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Foo": [
|
||||||
|
{
|
||||||
|
"name": "bar",
|
||||||
|
"type": "address[]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"primaryType": "Foo",
|
||||||
|
"domain": {
|
||||||
|
"name": "Lorem",
|
||||||
|
"version": "1",
|
||||||
|
"chainId": 1,
|
||||||
|
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"bar": [
|
||||||
|
"0x0000000000000000000000000000000000000001",
|
||||||
|
"0x0000000000000000000000000000000000000002",
|
||||||
|
"0x0000000000000000000000000000000000000003"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
var arrayTypedData TypedData
|
||||||
|
err := json.Unmarshal([]byte(jsonTypedData), &arrayTypedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
}
|
||||||
formatted := d.Format()
|
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
|
||||||
for _, item := range formatted {
|
if err != nil {
|
||||||
fmt.Printf("%v\n", item.Pprint(0))
|
t.Fatalf("Expected no err, got '%v'", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
j, _ := json.Marshal(formatted)
|
// Change array to string
|
||||||
fmt.Printf("%v\n", string(j))
|
arrayTypedData.Types["Foo"][0].Type = "string[]"
|
||||||
|
arrayTypedData.Message["bar"] = []interface{}{
|
||||||
|
"lorem",
|
||||||
|
"ipsum",
|
||||||
|
"dolores",
|
||||||
|
}
|
||||||
|
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no err, got '%v'", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change array to uint
|
||||||
|
arrayTypedData.Types["Foo"][0].Type = "uint[]"
|
||||||
|
arrayTypedData.Message["bar"] = []interface{}{
|
||||||
|
big.NewInt(1955),
|
||||||
|
big.NewInt(108),
|
||||||
|
big.NewInt(44010),
|
||||||
|
}
|
||||||
|
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no err, got '%v'", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not work with fixed-size arrays
|
||||||
|
arrayTypedData.Types["Foo"][0].Type = "uint[3]"
|
||||||
|
_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
|
||||||
|
if err == nil || err.Error() != "unknown type 'uint[3]'" {
|
||||||
|
t.Fatalf("Expected `unknown type 'uint[3]'`, got '%v'", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomTypeAsArray(t *testing.T) {
|
func TestCustomTypeAsArray(t *testing.T) {
|
||||||
|
|
@ -626,44 +685,45 @@ func TestCustomTypeAsArray(t *testing.T) {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "version",
|
"name": "version",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chainId",
|
"name": "chainId",
|
||||||
"type": "uint256"
|
"type": "uint256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "verifyingContract",
|
"name": "verifyingContract",
|
||||||
"type": "address"
|
"type": "address"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Person": [
|
"Person": [
|
||||||
{
|
{
|
||||||
"name": "name",
|
"name": "name",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "wallet",
|
"name": "wallet",
|
||||||
"type": "address"
|
"type": "address"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Person[]": [
|
"Person[]": [
|
||||||
{
|
{
|
||||||
"name": "baz",
|
"name": "baz",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
"Mail": [
|
"Mail": [
|
||||||
{
|
{
|
||||||
"name": "from",
|
"name": "from",
|
||||||
"type": "Person"
|
"type": "Person"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "to",
|
"name": "to",
|
||||||
"type": "Person[]"
|
"type": "Person[]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "contents",
|
"name": "contents",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -677,8 +737,8 @@ func TestCustomTypeAsArray(t *testing.T) {
|
||||||
},
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"from": {
|
"from": {
|
||||||
"name": "Cow",
|
"name": "Cow",
|
||||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
||||||
},
|
},
|
||||||
"to": {"baz": "foo"},
|
"to": {"baz": "foo"},
|
||||||
"contents": "Hello, Bob!"
|
"contents": "Hello, Bob!"
|
||||||
|
|
@ -689,14 +749,26 @@ func TestCustomTypeAsArray(t *testing.T) {
|
||||||
var malformedTypedData TypedData
|
var malformedTypedData TypedData
|
||||||
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed %v", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
|
||||||
err = malformedTypedData.Validate()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected no error, got %v", err)
|
|
||||||
}
|
}
|
||||||
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
|
_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error, got %v", err)
|
t.Errorf("Expected no error, got '%v'", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFormatter(t *testing.T) {
|
||||||
|
|
||||||
|
var d TypedData
|
||||||
|
err := json.Unmarshal([]byte(jsonTypedData), &d)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
|
}
|
||||||
|
formatted := d.Format()
|
||||||
|
for _, item := range formatted {
|
||||||
|
fmt.Printf("'%v'\n", item.Pprint(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
j, _ := json.Marshal(formatted)
|
||||||
|
fmt.Printf("'%v'\n", string(j))
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue