signer: refactor formatting and UI

This commit is contained in:
Martin Holst Swende 2018-12-03 13:26:00 +01:00 committed by Paul Berg
parent 2763dd41d6
commit 5f45cdc9fb
2 changed files with 103 additions and 52 deletions

View file

@ -425,7 +425,6 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter
buffer.Write(bytesValue) buffer.Write(bytesValue)
} }
} }
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
@ -609,87 +608,109 @@ func (typedData *TypedData) Map() map[string]interface{} {
// of clef present data in their apps // of clef present data in their apps
func (typedData *TypedData) PrettyPrint() string { func (typedData *TypedData) PrettyPrint() string {
output := bytes.Buffer{} output := bytes.Buffer{}
formatted := typedData.Format()
output.WriteString(fmt.Sprintf("%s {\n", "Domain")) for _, item := range formatted {
output.WriteString(typedData.PrettyPrintData("EIP712Domain", typedData.Domain.Map(), 1)) output.WriteString(fmt.Sprintf("%v\n", item.Pprint(0)))
output.Truncate(output.Len() - 2) }
output.WriteString(fmt.Sprintf("\n}\n"))
output.WriteString(fmt.Sprintf("%s {\n", typedData.PrimaryType))
output.WriteString(typedData.PrettyPrintData(typedData.PrimaryType, typedData.Message, 1))
output.Truncate(output.Len() - 2)
output.WriteString(fmt.Sprintf("\n}"))
return output.String() return output.String()
} }
// PrettyPrintData generates a formatted output for the // Format returns a representation of d, which can be easily displayed by a user-interface
// given data // without in-depth knowledge about 712 rules
func (typedData *TypedData) PrettyPrintData(primaryType string, data map[string]interface{}, depth int) string { func (typedData *TypedData) Format() []*NameValueType {
output := bytes.Buffer{} var nvts []*NameValueType
nvts = append(nvts, &NameValueType{
Name: "EIP712Domain",
Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()),
Typ: "domain",
})
nvts = append(nvts, &NameValueType{
Name: typedData.PrimaryType,
Value: typedData.formatData(typedData.PrimaryType, typedData.Message),
Typ: "primary type",
})
return nvts
}
func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) []*NameValueType {
var output []*NameValueType
// Add field contents. Structs and arrays have special handlers. // Add field contents. Structs and arrays have special handlers.
for _, field := range typedData.Types[primaryType] { for _, field := range typedData.Types[primaryType] {
encType := field["type"] encType := field["type"]
encName := field["name"] encName := field["name"]
encValue := data[encName] encValue := data[encName]
item := &NameValueType{
Name: encName,
Typ: encType,
}
if encType[len(encType)-1:] == "]" { if encType[len(encType)-1:] == "]" {
arrayValue, _ := encValue.([]interface{}) arrayValue, _ := encValue.([]interface{})
parsedType := strings.Split(encType, "[")[0] parsedType := strings.Split(encType, "[")[0]
for _, item := range arrayValue { for _, v := range arrayValue {
if typedData.Types[parsedType] != nil { if typedData.Types[parsedType] != nil {
mapValue, _ := item.(map[string]interface{}) mapValue, _ := v.(map[string]interface{})
mapOutput := typedData.PrettyPrintData(parsedType, mapValue, depth+1) mapOutput := typedData.formatData(parsedType, mapValue)
output.WriteString(mapOutput) item.Value = mapOutput
} else { } else {
primitiveOutput := typedData.PrettyPrintPrimitiveValue(encType, encName, encValue, depth) primitiveOutput := formatPrimitiveValue(encType, encValue)
output.WriteString(primitiveOutput) item.Value = primitiveOutput
} }
} }
} else if typedData.Types[field["type"]] != nil { } else if typedData.Types[field["type"]] != nil {
output.WriteString(strings.Repeat("\u00a0", depth*2))
output.WriteString(fmt.Sprintf("\"%s\": { %s\n", field["name"], encType))
mapValue, _ := encValue.(map[string]interface{}) mapValue, _ := encValue.(map[string]interface{})
mapOutput := typedData.PrettyPrintData(field["type"], mapValue, depth+1) mapOutput := typedData.formatData(field["type"], mapValue)
output.WriteString(mapOutput) item.Value = mapOutput
output.Truncate(output.Len() - 2)
output.WriteString(fmt.Sprintf("\n%s},\n", strings.Repeat("\u00a0", depth*2)))
} else { } else {
primitiveOutput := typedData.PrettyPrintPrimitiveValue(encType, encName, encValue, depth) primitiveOutput := formatPrimitiveValue(encType, encValue)
output.WriteString(primitiveOutput) item.Value = primitiveOutput
} }
output = append(output, item)
}
return output
} }
return output.String() func formatPrimitiveValue(encType string, encValue interface{}) string {
}
// PrettyPrintPrimitiveValue generates a formatted output for the
// given primitive value
func (typedData *TypedData) PrettyPrintPrimitiveValue(encType string, encName string, encValue interface{}, depth int) string {
output := bytes.Buffer{}
output.WriteString(strings.Repeat("\u00a0", depth*2))
output.WriteString(fmt.Sprintf("\"%s\": ", encName))
switch encType { switch encType {
case "address": case "address":
stringValue, _ := encValue.(string) stringValue, _ := encValue.(string)
addressValue := common.HexToAddress(stringValue) return common.HexToAddress(stringValue).String()
output.WriteString(fmt.Sprintf("%s,\n", addressValue.String()))
case "bool": case "bool":
boolValue, _ := encValue.(bool) boolValue, _ := encValue.(bool)
output.WriteString(fmt.Sprintf("%t,\n", boolValue)) return fmt.Sprintf("%t", boolValue)
case "bytes", "string": case "bytes", "string":
output.WriteString(fmt.Sprintf("\"%s\",\n", encValue)) return fmt.Sprintf("%s", encValue)
default: }
if strings.HasPrefix(encType, "bytes") { if strings.HasPrefix(encType, "bytes") {
output.WriteString(fmt.Sprintf("\"%s\",\n", encValue)) return fmt.Sprintf("%s", encValue)
} else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { } else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
bigIntValue, _ := encValue.(*big.Int) bigIntValue, _ := encValue.(*big.Int)
output.WriteString(fmt.Sprintf("%d,\n", bigIntValue)) return fmt.Sprintf("%d (0x%x)", bigIntValue, bigIntValue)
} }
return "NA"
}
// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
// json structures used to communicate signing-info about typed data with the UI
type NameValueType struct {
Name string `json:"name"`
Value interface{} `json:"value"`
Typ string `json:"type"`
}
// Pprint returns a pretty-printed version of nvt
func (nvt *NameValueType) Pprint(depth int) string {
output := bytes.Buffer{}
output.WriteString(strings.Repeat("\u00a0", depth*2))
output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ))
if nvts, ok := nvt.Value.([]*NameValueType); ok {
output.WriteString("\n")
for _, next := range nvts {
sublevel := next.Pprint(depth + 1)
output.WriteString(sublevel)
}
} else {
output.WriteString(fmt.Sprintf("%s\n", nvt.Value))
} }
return output.String() return output.String()
} }

View file

@ -598,3 +598,33 @@ func TestMalformedData4(t *testing.T) {
t.Fatalf("Expected no err, got %v", err) t.Fatalf("Expected no err, 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)
}
//x := PrettyPrintPrimitiveValue("address", "wallet", "0x123123123", 1)
//fmt.Printf(x)
//y := FormatPrimitiveValue("address", "wallet", "0x123123123")
//fmt.Printf("%v\n", y)
//fmt.Printf(d.PrettyPrintData(d.PrimaryType,d.Message, 1))
//formatted := d.FormatData(d.PrimaryType,d.Message)
//for _,item := range formatted{
// fmt.Printf("%v\n", item.Pprint(0))
//}
formatted := d.Format()
for _, item := range formatted {
fmt.Printf("%v\n", item.Pprint(0))
}
j, _ := json.Marshal(formatted)
fmt.Printf("%v\n", string(j))
}