mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
signer: address review concerns, check sign in integer parsing
This commit is contained in:
parent
aa46894dd5
commit
411b4bf5ba
4 changed files with 119 additions and 43 deletions
|
|
@ -327,7 +327,10 @@ func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAd
|
||||||
}
|
}
|
||||||
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
|
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
|
||||||
sighash := crypto.Keccak256(rawData)
|
sighash := crypto.Keccak256(rawData)
|
||||||
message := typedData.Format()
|
message, err := typedData.Format()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash}
|
req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash}
|
||||||
signature, err := api.sign(addr, req, true)
|
signature, err := api.sign(addr, req, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -482,8 +485,12 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
|
||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIntegerType(encType string, encValue interface{}) (*big.Int, error) {
|
func parseInteger(encType string, encValue interface{}) (*big.Int, error) {
|
||||||
length := 0
|
var (
|
||||||
|
length = 0
|
||||||
|
signed = strings.HasPrefix(encType, "int")
|
||||||
|
b *big.Int
|
||||||
|
)
|
||||||
if encType == "int" || encType == "uint" {
|
if encType == "int" || encType == "uint" {
|
||||||
length = 256
|
length = 256
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -499,7 +506,6 @@ func parseIntegerType(encType string, encValue interface{}) (*big.Int, error) {
|
||||||
}
|
}
|
||||||
length = atoiSize
|
length = atoiSize
|
||||||
}
|
}
|
||||||
var b *big.Int
|
|
||||||
switch v := encValue.(type) {
|
switch v := encValue.(type) {
|
||||||
case *math.HexOrDecimal256:
|
case *math.HexOrDecimal256:
|
||||||
b = (*big.Int)(v)
|
b = (*big.Int)(v)
|
||||||
|
|
@ -524,6 +530,9 @@ func parseIntegerType(encType string, encValue interface{}) (*big.Int, error) {
|
||||||
if b.BitLen() > length {
|
if b.BitLen() > length {
|
||||||
return nil, fmt.Errorf("integer larger than '%v'", encType)
|
return nil, fmt.Errorf("integer larger than '%v'", encType)
|
||||||
}
|
}
|
||||||
|
if !signed && b.Sign() == -1 {
|
||||||
|
return nil, fmt.Errorf("invalid negative value for unsigned type %v", encType)
|
||||||
|
}
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -577,7 +586,7 @@ func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
|
if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
|
||||||
b, err := parseIntegerType(encType, encValue)
|
b, err := parseInteger(encType, encValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -680,35 +689,32 @@ func (typedData *TypedData) Map() map[string]interface{} {
|
||||||
return dataMap
|
return dataMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrettyPrint generates a nice output to help the users
|
|
||||||
// of clef present data in their apps
|
|
||||||
func (typedData *TypedData) PrettyPrint() string {
|
|
||||||
output := bytes.Buffer{}
|
|
||||||
formatted := typedData.Format()
|
|
||||||
for _, item := range formatted {
|
|
||||||
output.WriteString(fmt.Sprintf("%v\n", item.Pprint(0)))
|
|
||||||
}
|
|
||||||
return output.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format returns a representation of typedData, which can be easily displayed by a user-interface
|
// Format returns a representation of typedData, which can be easily displayed by a user-interface
|
||||||
// without in-depth knowledge about 712 rules
|
// without in-depth knowledge about 712 rules
|
||||||
func (typedData *TypedData) Format() []*NameValueType {
|
func (typedData *TypedData) Format() ([]*NameValueType, error) {
|
||||||
|
domain, err := typedData.formatData("EIP712Domain", typedData.Domain.Map())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ptype, err := typedData.formatData(typedData.PrimaryType, typedData.Message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var nvts []*NameValueType
|
var nvts []*NameValueType
|
||||||
nvts = append(nvts, &NameValueType{
|
nvts = append(nvts, &NameValueType{
|
||||||
Name: "EIP712Domain",
|
Name: "EIP712Domain",
|
||||||
Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()),
|
Value: domain,
|
||||||
Typ: "domain",
|
Typ: "domain",
|
||||||
})
|
})
|
||||||
nvts = append(nvts, &NameValueType{
|
nvts = append(nvts, &NameValueType{
|
||||||
Name: typedData.PrimaryType,
|
Name: typedData.PrimaryType,
|
||||||
Value: typedData.formatData(typedData.PrimaryType, typedData.Message),
|
Value: ptype,
|
||||||
Typ: "primary type",
|
Typ: "primary type",
|
||||||
})
|
})
|
||||||
return nvts
|
return nvts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) []*NameValueType {
|
func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) ([]*NameValueType, error) {
|
||||||
var output []*NameValueType
|
var output []*NameValueType
|
||||||
|
|
||||||
// Add field contents. Structs and arrays have special handlers.
|
// Add field contents. Structs and arrays have special handlers.
|
||||||
|
|
@ -725,50 +731,70 @@ func (typedData *TypedData) formatData(primaryType string, data map[string]inter
|
||||||
for _, v := range arrayValue {
|
for _, v := range arrayValue {
|
||||||
if typedData.Types[parsedType] != nil {
|
if typedData.Types[parsedType] != nil {
|
||||||
mapValue, _ := v.(map[string]interface{})
|
mapValue, _ := v.(map[string]interface{})
|
||||||
mapOutput := typedData.formatData(parsedType, mapValue)
|
mapOutput, err := typedData.formatData(parsedType, mapValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
item.Value = mapOutput
|
item.Value = mapOutput
|
||||||
} else {
|
} else {
|
||||||
primitiveOutput := formatPrimitiveValue(field.Type, encValue)
|
primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
item.Value = primitiveOutput
|
item.Value = primitiveOutput
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if typedData.Types[field.Type] != nil {
|
} else if typedData.Types[field.Type] != nil {
|
||||||
if mapValue, ok := encValue.(map[string]interface{}); ok {
|
if mapValue, ok := encValue.(map[string]interface{}); ok {
|
||||||
mapOutput := typedData.formatData(field.Type, mapValue)
|
mapOutput, err := typedData.formatData(field.Type, mapValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
item.Value = mapOutput
|
item.Value = mapOutput
|
||||||
} else {
|
} else {
|
||||||
item.Value = "<nil>"
|
item.Value = "<nil>"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
primitiveOutput := formatPrimitiveValue(field.Type, encValue)
|
primitiveOutput, err := formatPrimitiveValue(field.Type, encValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
item.Value = primitiveOutput
|
item.Value = primitiveOutput
|
||||||
}
|
}
|
||||||
output = append(output, item)
|
output = append(output, item)
|
||||||
}
|
}
|
||||||
return output
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatPrimitiveValue(encType string, encValue interface{}) string {
|
func formatPrimitiveValue(encType string, encValue interface{}) (string, error) {
|
||||||
switch encType {
|
switch encType {
|
||||||
case "address":
|
case "address":
|
||||||
stringValue, _ := encValue.(string)
|
if stringValue, ok := encValue.(string); !ok {
|
||||||
return common.HexToAddress(stringValue).String()
|
return "", fmt.Errorf("could not format value %v as address", encValue)
|
||||||
|
} else {
|
||||||
|
return common.HexToAddress(stringValue).String(), nil
|
||||||
|
}
|
||||||
case "bool":
|
case "bool":
|
||||||
boolValue, _ := encValue.(bool)
|
if boolValue, ok := encValue.(bool); !ok {
|
||||||
return fmt.Sprintf("%t", boolValue)
|
return "", fmt.Errorf("could not format value %v as bool", encValue)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%t", boolValue), nil
|
||||||
|
}
|
||||||
case "bytes", "string":
|
case "bytes", "string":
|
||||||
return fmt.Sprintf("%s", encValue)
|
return fmt.Sprintf("%s", encValue), nil
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(encType, "bytes") {
|
if strings.HasPrefix(encType, "bytes") {
|
||||||
return fmt.Sprintf("%s", encValue)
|
return fmt.Sprintf("%s", encValue), nil
|
||||||
} else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
|
|
||||||
b, err := parseIntegerType(encType, encValue)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("ERROR: %v", err)
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%d (0x%x)", b, b)
|
if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
|
||||||
|
if b, err := parseInteger(encType, encValue); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%d (0x%x)", b, b), nil
|
||||||
}
|
}
|
||||||
return "NA"
|
}
|
||||||
|
return "", fmt.Errorf("unhandled type %v", encType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
|
// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
|
||||||
|
|
|
||||||
51
signer/core/signed_data_internal_test.go
Normal file
51
signer/core/signed_data_internal_test.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2019 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseInteger(t *testing.T) {
|
||||||
|
for i, tt := range []struct {
|
||||||
|
t string
|
||||||
|
v interface{}
|
||||||
|
exp *big.Int
|
||||||
|
}{
|
||||||
|
{"uint32", "-123", nil},
|
||||||
|
{"int32", "-123", big.NewInt(-123)},
|
||||||
|
{"uint32", "0xff", big.NewInt(0xff)},
|
||||||
|
{"int8", "0xffff", nil},
|
||||||
|
} {
|
||||||
|
res, err := parseInteger(tt.t, tt.v)
|
||||||
|
if tt.exp == nil && res == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tt.exp == nil && res != nil {
|
||||||
|
t.Errorf("test %d, got %v, expected nil", i, res)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tt.exp != nil && res == nil {
|
||||||
|
t.Errorf("test %d, got '%v', expected %v", i, err, tt.exp)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tt.exp.Cmp(res) != 0 {
|
||||||
|
t.Errorf("test %d, got %v expected %v", i, res, tt.exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -319,7 +319,7 @@ func TestFormatter(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unmarshalling failed '%v'", err)
|
t.Fatalf("unmarshalling failed '%v'", err)
|
||||||
}
|
}
|
||||||
formatted := d.Format()
|
formatted, _ := d.Format()
|
||||||
for _, item := range formatted {
|
for _, item := range formatted {
|
||||||
fmt.Printf("'%v'\n", item.Pprint(0))
|
fmt.Printf("'%v'\n", item.Pprint(0))
|
||||||
}
|
}
|
||||||
|
|
@ -403,7 +403,6 @@ func TestFuzzerFiles(t *testing.T) {
|
||||||
if verbose && err != nil {
|
if verbose && err != nil {
|
||||||
fmt.Printf("%d, EncodeData[2] err: %v\n", i, err)
|
fmt.Printf("%d, EncodeData[2] err: %v\n", i, err)
|
||||||
}
|
}
|
||||||
typedData.PrettyPrint()
|
|
||||||
typedData.Format()
|
typedData.Format()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue