diff --git a/cmd/clef/audit.log b/cmd/clef/audit.log
new file mode 100644
index 0000000000..66302cac97
--- /dev/null
+++ b/cmd/clef/audit.log
@@ -0,0 +1,11 @@
+t=2018-10-10T14:45:26-0700 lvl=info msg=Configured api=signer audit log=audit.log
+t=2018-10-10T14:45:37-0700 lvl=info msg=SignStructuredData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59942\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 [chksum ok]" data={PrimaryType:0x4d61696c}
+t=2018-10-10T14:45:37-0700 lvl=info msg=SignStructuredData api=signer type=response data= error=nil
+t=2018-10-10T14:45:55-0700 lvl=info msg=Configured api=signer audit log=audit.log
+t=2018-10-10T14:45:55-0700 lvl=info msg=SignStructuredData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59956\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 [chksum ok]" data={PrimaryType:0x4d61696c}
+t=2018-10-10T14:46:18-0700 lvl=info msg=Configured api=signer audit log=audit.log
+t=2018-10-10T14:46:50-0700 lvl=info msg=SignStructuredData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59967\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 [chksum ok]" data={PrimaryType:0x4d61696c}
+t=2018-10-10T14:51:32-0700 lvl=info msg=Configured api=signer audit log=audit.log
+t=2018-10-10T14:51:59-0700 lvl=info msg=SignStructuredData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60000\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 [chksum ok]" data="{Hash:[226 140 80 200 149 243 254 73 125 193 200 45 83 189 176 154 4 204 167 86 220 130 208 11 170 238 116 129 150 85 62 148]}"
+t=2018-10-10T14:52:51-0700 lvl=info msg=Configured api=signer audit log=audit.log
+t=2018-10-10T14:52:56-0700 lvl=info msg=SignStructuredData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60017\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 [chksum ok]" data="{Hash:[226 140 80 200 149 243 254 73 125 193 200 45 83 189 176 154 4 204 167 86 220 130 208 11 170 238 116 129 150 85 62 148]}"
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index 1dac85af92..7e5ae56868 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -330,9 +330,10 @@ func initialize(c *cli.Context) error {
// If using the stdioui, we can't do the 'confirm'-flow
fmt.Fprintf(logOutput, legalWarning)
} else {
- if !confirm(legalWarning) {
- return fmt.Errorf("aborted by user")
- }
+ // Temporarily disabled while in development
+ //if !confirm(legalWarning) {
+ // return fmt.Errorf("aborted by user")
+ //}
}
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true))))
@@ -555,14 +556,15 @@ func readMasterKey(ctx *cli.Context, ui core.SignerUI) ([]byte, error) {
var password string
// If ui is not nil, get the password from ui.
if ui != nil {
- resp, err := ui.OnInputRequired(core.UserInputRequest{
- Title: "Master Password",
- Prompt: "Please enter the password to decrypt the master seed",
- IsPassword: true})
- if err != nil {
- return nil, err
- }
- password = resp.Text
+ // Temporarily disabled while in development
+ //resp, err := ui.OnInputRequired(core.UserInputRequest{
+ // Title: "Master Password",
+ // Prompt: "Please enter the password to decrypt the master seed",
+ // IsPassword: true})
+ //if err != nil {
+ // return nil, err
+ //}
+ //password = resp.Text
} else {
password = getPassPhrase("Decrypt master seed of clef", false)
}
diff --git a/interfaces.go b/interfaces.go
index 1ff31f96b6..840e916aaf 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -209,3 +209,8 @@ type GasEstimator interface {
type PendingStateEventer interface {
SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error)
}
+
+// TypedData is the standard for EIP-712 signed data.
+type TypedData struct {
+ Hash *common.Hash `json:"hash"`
+}
diff --git a/signer/core/api.go b/signer/core/api.go
index 49748a690f..61d1406a50 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -51,6 +51,8 @@ type ExternalAPI interface {
SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error)
// SignData - request to sign the given data (plus prefix)
SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error)
+ // SignStructuredData - request to sign the given structured data (plus prefix)
+ SignStructuredData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error)
// EcRecover - recover public key from given message and signature
EcRecover(ctx context.Context, contentType string, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error)
// Export - request to export an account
diff --git a/signer/core/api_layout.md b/signer/core/api_layout.md
new file mode 100644
index 0000000000..bcb8c4cc5b
--- /dev/null
+++ b/signer/core/api_layout.md
@@ -0,0 +1,134 @@
+# Specs
+`encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)`
+- data adheres to 𝕊, a structure defined in the rigorous eip-712
+- `\x01` is needed to comply with EIP-191
+- `domainSeparator` and `hashStruct` are defined below
+
+## A) domainSeparator
+`domainSeparator = hashStruct(eip712Domain)`
+
+
+Struct named `EIP712Domain` with one or more of the below fields:
+
+- `string name`
+- `string version`
+- `uint256 chainId`, as per EIP-155
+- `address verifyingContract`
+- `bytes32 salt`
+
+## B) hashStruct
+`hashStruct(s : 𝕊) = keccak256(typeHash ‖ encodeData(s))`
+
+`typeHash = keccak256(encodeType(typeOf(s)))`
+
+### i) encodeType
+- `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
+- each member is written as `type ‖ " " ‖ name`
+- encodings cascade down and are sorted by name
+
+### ii) encodeData
+- `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
+- each encoded member is 32-byte long
+
+ #### a) atomic
+
+ - `boolean` => `uint256`
+ - `address` => `uint160`
+ - `uint` => sign-extended `uint256` in big endian order
+ - `bytes1:31` => `bytes32`
+
+ #### b) dynamic
+
+ - `bytes` => `keccak256(bytes)`
+ - `string` => `keccak256(string)`
+
+ #### c) referenced
+
+ - `array` => `keccak256(encodeData(array))`
+ - `struct` => `rec(keccak256(hashStruct(struct)))`
+
+## C) Example
+### Query
+```json
+{
+ "jsonrpc": "2.0",
+ "method": "account_signStructuredData",
+ "params": [
+ "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
+ {
+ "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!"
+ }
+ }
+ ],
+ "id": 1
+}
+```
+
+### Response
+```json
+{
+ "id":1,
+ "jsonrpc": "2.0",
+ "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
+}
+```
\ No newline at end of file
diff --git a/signer/core/apiv2.go b/signer/core/apiv2.go
new file mode 100644
index 0000000000..0ef25cb8d2
--- /dev/null
+++ b/signer/core/apiv2.go
@@ -0,0 +1,57 @@
+package core
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+//type TypedData struct {
+ //Types map[string] interface{} `json:"types"`
+ //PrimaryType *big.Int `json:"primaryType"`
+ //Domain EIP712Domain `json:"domain"`
+ //Message map[string] interface{} `json:"message"`
+//}
+
+//type EIP712Domain struct {
+// Name string `json:"name"`
+// Version string `json:"version"`
+// ChainId big.Int `json:"chainId"`
+// VerifyingContract common.Address `json:"verifyingContract"`
+// Salt hexutil.Bytes `json:"salt"`
+//}
+
+// TypedData represents a request to create a new filter.
+// Same as ethereum.FilterQuery but with UnmarshalJSON() method.
+type TypedData ethereum.TypedData
+
+// Typed data according to EIP712
+//
+// If the format "\x19\x46" ‖ domainSeparator ‖ hashStruct(message)` is not respected,
+// an error is returned
+func (api *SignerAPI) SignStructuredData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) {
+ fmt.Println("data", data)
+ fmt.Println("data.Hash", data.Hash)
+ return common.Hex2Bytes("0xdeadbeef"), nil
+}
+
+// UnmarshalJSON sets *args fields with given data.
+func (args *TypedData) UnmarshalJSON(data []byte) error {
+ type input struct {
+ Hash *common.Hash `json:"hash"`
+ }
+
+ var raw input
+ if err := json.Unmarshal(data, &raw); err != nil {
+ return err
+ }
+
+ if raw.Hash != nil {
+ args.Hash = raw.Hash
+ }
+
+ return nil
+}
\ No newline at end of file
diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go
index 0762415f54..7c6cf1f516 100644
--- a/signer/core/auditlog.go
+++ b/signer/core/auditlog.go
@@ -70,11 +70,11 @@ func (l *AuditLogger) SignData(ctx context.Context, contentType string, addr com
return b, e
}
-func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) {
- l.log.Info("SignTypedData", "type", "request", "metadata", MetadataFromContext(ctx).String(),
+func (l *AuditLogger) SignStructuredData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) {
+ l.log.Info("SignStructuredData", "type", "request", "metadata", MetadataFromContext(ctx).String(),
"addr", addr.String(), "data", data)
- b, e := l.api.SignTypedData(ctx, addr, data)
- l.log.Info("SignTypedData", "type", "response", "data", common.Bytes2Hex(b), "error", e)
+ b, e := l.api.SignStructuredData(ctx, addr, TypedData{})
+ l.log.Info("SignStructuredData", "type", "response", "data", common.Bytes2Hex(b), "error", e)
return b, e
}