Polished docstrings, ran goimports and swapped fmt.Errorf with errors.New where possible

This commit is contained in:
Paul Berg 2018-10-18 09:16:43 +01:00 committed by Martin Holst Swende
parent 78ee50d8c1
commit e2b4544fa0
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 130 additions and 77 deletions

View file

@ -245,21 +245,15 @@ func TestNewAcc(t *testing.T) {
} }
} }
func signApplicationValidator(t *testing.T) { func signTextValidator(t *testing.T) {
// TODO // TODO
} }
func signApplicationClique(t *testing.T) { func signApplicationClique(t *testing.T) {
// https://etherscan.io/block/1
//header := &types.Header{
// "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
// "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
// "0x05a56e2d52c817161883f50c441c3228cfe54d9f",
//}
// TODO // TODO
} }
func signDataPlain(t *testing.T) { func signTextPlain(t *testing.T) {
api, control := setup(t) api, control := setup(t)
//Create two accounts //Create two accounts
createAccount(control, api, t) createAccount(control, api, t)
@ -273,7 +267,7 @@ func signDataPlain(t *testing.T) {
control <- "Y" control <- "Y"
control <- "wrongpassword" control <- "wrongpassword"
h, err := api.SignData(context.Background(), DataPlain.Mime, a, []byte("EHLO world")) h, err := api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
if h != nil { if h != nil {
t.Errorf("Expected nil-data, got %x", h) t.Errorf("Expected nil-data, got %x", h)
} }
@ -281,7 +275,7 @@ func signDataPlain(t *testing.T) {
t.Errorf("Expected ErrLocked! %v", err) t.Errorf("Expected ErrLocked! %v", err)
} }
control <- "No way" control <- "No way"
h, err = api.SignData(context.Background(), DataPlain.Mime, a, []byte("EHLO world")) h, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
if h != nil { if h != nil {
t.Errorf("Expected nil-data, got %x", h) t.Errorf("Expected nil-data, got %x", h)
} }
@ -290,7 +284,7 @@ func signDataPlain(t *testing.T) {
} }
control <- "Y" control <- "Y"
control <- "a_long_password" control <- "a_long_password"
h, err = api.SignData(context.Background(), DataPlain.Mime, a, []byte("EHLO world")) h, err = api.SignData(context.Background(), TextPlain.Mime, a, []byte("EHLO world"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -299,22 +293,22 @@ func signDataPlain(t *testing.T) {
} }
} }
func signDataStructured(t *testing.T) { func signTypedData(t *testing.T) {
// TODO // TODO
} }
func TestSignData(t *testing.T) { func TestSignData(t *testing.T) {
// application/validator or `0x00` // application/validator or `0x00`
signApplicationValidator(t) signTextValidator(t)
// application/clique or `0x01` // data/structured `0x01`
signTypedData(t)
// application/clique or `0x02`
signApplicationClique(t) signApplicationClique(t)
// data/plain or `0x45` // text/plain or `0x45`
signDataPlain(t) signTextPlain(t)
// data/structured `0x46`
signDataStructured(t)
} }
func mkTestTx(from common.MixedcaseAddress) SendTxArgs { func mkTestTx(from common.MixedcaseAddress) SendTxArgs {

View file

@ -4,30 +4,39 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"errors"
"fmt" "fmt"
"math/big"
"math/rand"
"reflect"
"sort"
"strings"
"time"
"unicode"
"github.com/PaulRBerg/basics/helpers"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"math/big"
"sort"
"strings"
"unicode"
) )
type TypedData struct { type TypedData struct {
Types EIP712Types `json:"types"` Types map[string]EIP712Type `json:"types"`
PrimaryType string `json:"primaryType"` PrimaryType string `json:"primaryType"`
Domain EIP712Domain `json:"domain"` Domain EIP712Domain `json:"domain"`
Message EIP712Message `json:"message"` Message EIP712Message `json:"message"`
} }
type EIP712Types map[string][]map[string]string type EIP712Type []map[string]string
type EIP712TypePriority struct { type EIP712TypePriority struct {
Type string Type string
Value uint Value uint
} }
type EIP712Data = map[string]interface{}
type EIP712Domain struct { type EIP712Domain struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
@ -38,54 +47,45 @@ type EIP712Domain struct {
type EIP712Message map[string]interface{} type EIP712Message map[string]interface{}
// Typed data according to EIP712 // SignTypedData signs EIP712 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, data TypedData) (hexutil.Bytes, error) { func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) {
if err := data.Domain.IsValid(); err != nil { if err := data.Domain.IsValid(); err != nil {
return nil, err return nil, err
} }
if data.PrimaryType == "" { if data.PrimaryType == "" {
return nil, fmt.Errorf("primary type undefined") return nil, errors.New("primary type undefined")
} }
domainTypes := EIP712Types{ domainTypes := map[string]EIP712Type{
"EIP712Domain": data.Types["EIP712Domain"], "EIP712Domain": data.Types["EIP712Domain"],
} }
domainSeparator, err := hashStruct(domainTypes, data.Domain.Values(), "") domainSeparator := hashStruct(domainTypes, data.PrimaryType, data.Domain.Values(), 0)
if err != nil { //if err != nil {
return nil, err // return nil, err
} //}
delete(data.Types, "EIP712Domain") delete(data.Types, "EIP712Domain")
typedDataHash, err := hashStruct(data.Types, data.Message, data.PrimaryType) typedDataHash := hashStruct(data.Types, data.PrimaryType, data.Message, 0)
if err != nil { //if err != nil {
return nil, err // return nil, err
} //}
fmt.Println("domainSeparator", domainSeparator.String()) fmt.Println("domainSeparator", domainSeparator.String())
fmt.Println("typedDataHash", typedDataHash.String()) fmt.Println("typedDataHash", typedDataHash.String())
return common.FromHex("0xdeadbeef"), nil return common.FromHex("0xdeadbeef"), nil
} }
// hashStruct generates the following encoding for the given domain and message:
// `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)` // `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
func hashStruct(types EIP712Types, message EIP712Message, primaryType string) (common.Hash, error) { func hashStruct(types map[string]EIP712Type, key string, data EIP712Data, depth int) common.Hash {
if primaryType != "" { helpers.PrintJson("hashStruct", map[string]interface{}{
if types[primaryType] == nil { "depth": depth,
return common.Hash{}, fmt.Errorf("primaryType specified but undefined") })
}
}
typeEncoding, err := encodeType(types, primaryType) typeEncoding := encodeType(types)
if err != nil {
return common.Hash{}, err
}
typeHash := hex.EncodeToString(crypto.Keccak256([]byte(typeEncoding))) typeHash := hex.EncodeToString(crypto.Keccak256([]byte(typeEncoding)))
dataEncoding, err := encodeData(message) dataEncoding := encodeData(types, key, data, depth)
if err != nil {
return common.Hash{}, err
}
dataHash := hex.EncodeToString(crypto.Keccak256([]byte(dataEncoding))) dataHash := hex.EncodeToString(crypto.Keccak256([]byte(dataEncoding)))
var buffer bytes.Buffer var buffer bytes.Buffer
@ -93,14 +93,22 @@ func hashStruct(types EIP712Types, message EIP712Message, primaryType string) (c
buffer.WriteString(dataHash) buffer.WriteString(dataHash)
hash := common.BytesToHash(crypto.Keccak256(buffer.Bytes())) hash := common.BytesToHash(crypto.Keccak256(buffer.Bytes()))
return hash, nil if depth == 0 {
fmt.Printf("typeEncoding %s\n", typeEncoding)
fmt.Printf("dataEncoding %s\n", dataEncoding)
}
return hash
} }
// encodeType transforms the given types into an encoding of the form // encodeType generates the followign encoding:
// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"` // `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
// //
// Each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name // each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
func encodeType(types EIP712Types, primaryType string) (string, error) { func encodeType(types map[string]EIP712Type) string {
helpers.PrintJson("hashStruct", map[string]interface{}{
"types": types,
})
var priorities = make(map[string]uint) var priorities = make(map[string]uint)
for key := range types { for key := range types {
priorities[key] = 0 priorities[key] = 0
@ -137,7 +145,7 @@ func encodeType(types EIP712Types, primaryType string) (string, error) {
for _, typeObj := range typeArr { for _, typeObj := range typeArr {
typeVal := typeObj["type"] typeVal := typeObj["type"]
if typeKey == typeVal { if typeKey == typeVal {
return "", fmt.Errorf("type %s cannot reference itself", typeVal) panic(fmt.Errorf("type %s cannot reference itself", typeVal))
} }
firstChar := []rune(typeVal)[0] firstChar := []rune(typeVal)[0]
@ -148,14 +156,14 @@ func encodeType(types EIP712Types, primaryType string) (string, error) {
update(typeKey, typeVal) update(typeKey, typeVal)
} }
} else { } else {
return "", fmt.Errorf("referenced type %s is undefined", typeVal) panic(fmt.Errorf("referenced type %s is undefined", typeVal))
} }
} else { } else {
if !types.IsStandardType(typeVal) { if !isStandardType(typeVal) {
if types[typeVal] != nil { if types[typeVal] != nil {
return "", fmt.Errorf("Custom type %s must be capitalized", typeVal) panic(fmt.Errorf("Custom type %s must be capitalized", typeVal))
} else { } else {
return "", fmt.Errorf("Unknown type %s", typeVal) panic(fmt.Errorf("Unknown type %s", typeVal))
} }
} }
} }
@ -164,7 +172,7 @@ func encodeType(types EIP712Types, primaryType string) (string, error) {
typeValArr = []string{} typeValArr = []string{}
} }
sortedPriorities := types.SortByPriorityAndName(priorities) sortedPriorities := sortByPriorityAndName(priorities)
var buffer bytes.Buffer var buffer bytes.Buffer
for _, priority := range sortedPriorities { for _, priority := range sortedPriorities {
typeKey := priority.Type typeKey := priority.Type
@ -184,15 +192,64 @@ func encodeType(types EIP712Types, primaryType string) (string, error) {
buffer.WriteString(")") buffer.WriteString(")")
} }
return buffer.String(), nil return buffer.String()
} }
func encodeData(values EIP712Message) (string, error) { // encodeData generates the following encoding:
return "", nil // `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
//
// each encoded member is 32-byte long
func encodeData(types map[string]EIP712Type, key string, val interface{}, depth int) string {
helpers.PrintJson("hashStruct", map[string]interface{}{
"key": key,
"val": val,
"depth": depth,
})
var buffer bytes.Buffer
switch val.(type) {
case EIP712Data:
for mapKey, mapVal := range val.(EIP712Data) {
if reflect.TypeOf(mapVal) == reflect.TypeOf(EIP712Data{}) {
hash := hashStruct(types, mapKey, mapVal.(EIP712Data), depth+1)
buffer.WriteString(hash.String())
} else {
str := encodeData(types, mapKey, mapVal, depth+1)
buffer.WriteString(str)
}
}
break
case bool:
boolVal, _ := val.(bool)
var int64Val int64
if boolVal {
int64Val = 1
}
encodedVal := abi.U256(big.NewInt(int64Val))
fmt.Printf("bool encoded value:", encodedVal)
buffer.Write(encodedVal)
break
case string:
bytesVal := common.FromHex(val.(string))
hash := common.BytesToHash(crypto.Keccak256(bytesVal))
buffer.WriteString(hash.String())
break
default:
arr := [...]string{"(a)", "(b)", "(c)"}
rand.Seed(time.Now().UnixNano())
buffer.WriteString(arr[rand.Intn(3)])
break
}
return buffer.String()
} }
// Checks if the given type is a standard type accepted by EIP-712 // isStandardType checks if the given type is a EIP712 conformant type
func (types *EIP712Types) IsStandardType(typeStr string) bool { func isStandardType(typeStr string) bool {
standardTypes := []string{ standardTypes := []string{
"array", "array",
"address", "address",
@ -210,9 +267,9 @@ func (types *EIP712Types) IsStandardType(typeStr string) bool {
return false return false
} }
// Helper function to sort types by priority and name. Priority is calculated b // sortByPriorityAndName is a helper function to sort types by priority and name. Priority is calculated b
// based upon the number of references. // based upon the number of references.
func (types *EIP712Types) SortByPriorityAndName(input map[string]uint) []EIP712TypePriority { func sortByPriorityAndName(input map[string]uint) []EIP712TypePriority {
var priorities []EIP712TypePriority var priorities []EIP712TypePriority
for key, val := range input { for key, val := range input {
priorities = append(priorities, EIP712TypePriority{key, val}) priorities = append(priorities, EIP712TypePriority{key, val})
@ -234,20 +291,22 @@ func (types *EIP712Types) SortByPriorityAndName(input map[string]uint) []EIP712T
return priorities return priorities
} }
// Check if the given domain is valid, i.e. contains at least the minimum viable keys and values // IsValid checks if the given domain is valid, i.e. contains at least
// the minimum viable keys and values
func (domain *EIP712Domain) IsValid() error { func (domain *EIP712Domain) IsValid() error {
if domain.ChainId == big.NewInt(0) { if domain.ChainId == big.NewInt(0) {
return fmt.Errorf("chainId must be specified according to EIP-155") return errors.New("chainId must be specified according to EIP-155")
} }
if domain.Name == "" && domain.Version == "" && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { if len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 {
return fmt.Errorf("domain undefined") return errors.New("domain undefined")
} }
return nil return nil
} }
// Helper function to return the values of a domain in the form of a golang map // Values is a helper function to return the values of a domain as a map
// with arbitrary values
func (domain *EIP712Domain) Values() map[string]interface{} { func (domain *EIP712Domain) Values() map[string]interface{} {
return map[string]interface{}{ return map[string]interface{}{
"name": domain.Name, "name": domain.Name,