From 78ee50d8c1cb1ae1648026ad1b756754f90f232b Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Mon, 15 Oct 2018 03:36:07 +0100 Subject: [PATCH] Changed mimes and implemented the 'encodeType' function for EIP-712 --- cmd/clef/audit.log | 48 +++++++ cmd/clef/extapi_changelog.md | 6 +- cmd/clef/main.go | 34 ++++- interfaces.go | 2 +- signer/core/api.go | 32 ++--- signer/core/api_layout.md | 10 +- signer/core/apiv2.go | 251 +++++++++++++++++++++++++++++++++-- 7 files changed, 337 insertions(+), 46 deletions(-) diff --git a/cmd/clef/audit.log b/cmd/clef/audit.log index d339f7e75c..9b0ef72b85 100644 --- a/cmd/clef/audit.log +++ b/cmd/clef/audit.log @@ -14,3 +14,51 @@ t=2018-10-13T05:44:06-0700 lvl=info msg=SignTypedData api=signer type=response d t=2018-10-13T05:46:53-0700 lvl=info msg=Configured api=signer audit log=audit.log t=2018-10-13T05:46:58-0700 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:61694\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" t=2018-10-13T05:46:58-0700 lvl=info msg=SignTypedData api=signer type=response data= error=nil +t=2018-10-14T21:00:04+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:00:06+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56799\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}" +t=2018-10-14T21:00:06+0100 lvl=info msg=SignTypedData api=signer type=response data= error="Couldn't decode types of EIP712Domain" +t=2018-10-14T21:00:50+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:01:02+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56810\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[type:uint256 name:chainId] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:01:02+0100 lvl=info msg=SignTypedData api=signer type=response data= error="Couldn't decode types of EIP712Domain" +t=2018-10-14T21:01:39+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:01:53+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56823\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 name:Alice] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:01:53+0100 lvl=info msg=SignTypedData api=signer type=response data= error="Couldn't decode types of EIP712Domain" +t=2018-10-14T21:11:30+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:11:33+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56899\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[type:uint256 name:chainId] map[name:verifyingContract type:address]] Person:[map[type:string name:name] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:11:33+0100 lvl=info msg=SignTypedData api=signer type=response data= error="Couldn't decode types of EIP712Domain" +t=2018-10-14T21:15:28+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:15:34+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56968\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:15:34+0100 lvl=info msg=SignTypedData api=signer type=response data= error="Couldn't decode types of EIP712Domain" +t=2018-10-14T21:17:53+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:17:57+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56991\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[type:string name:name] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:17:57+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T21:18:32+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:18:36+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57006\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[type:Person name:from] map[name:to type:Person] map[type:string name:contents]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:18:36+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T21:18:59+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:19:00+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57017\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]] EIP712Domain:[map[name:name type:string] map[type:string name:version] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T21:19:00+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T21:30:19+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:30:29+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57188\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:{neg:false abs:[1]} VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}" +t=2018-10-14T21:30:29+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T21:51:40+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T21:51:42+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57392\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[type:Person name:from] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[name:name type:string] map[type:string name:version] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}" +t=2018-10-14T21:51:42+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T22:16:07+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T22:16:14+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57602\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}" +t=2018-10-14T22:16:14+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T22:18:31+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T22:18:35+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57653\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[type:Person name:from] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T22:18:35+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-14T22:20:47+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-14T22:20:50+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:57669\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]] EIP712Domain:[map[name:name type:string] map[type:string name:version] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-14T22:20:50+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-15T03:11:09+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-15T03:11:17+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:50605\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[type:string name:name] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}" +t=2018-10-15T03:11:17+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-15T03:12:01+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-15T03:12:04+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:50618\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[type:address name:wallet]] Mail:[map[name:from type:Person] map[name:to type:Person] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}" +t=2018-10-15T03:12:04+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil +t=2018-10-15T03:14:48+0100 lvl=info msg=Configured api=signer audit log=audit.log +t=2018-10-15T03:15:25+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:50635\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[name:name type:string] map[name:version type:string] map[name:chainId type:uint256] map[name:verifyingContract type:address]] Person:[map[name:name type:string] map[name:wallet type:address]] Mail:[map[name:from type:Person] map[type:Person name:to] map[name:contents type:string]]] PrimaryType:Mail Domain:{Name:Ether Mail Version:1 ChainId:+1 VerifyingContract:[204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204 204] Salt:0x} Message:map[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}" +t=2018-10-15T03:15:25+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md index 693e6b1759..a9ab42e5b5 100644 --- a/cmd/clef/extapi_changelog.md +++ b/cmd/clef/extapi_changelog.md @@ -2,13 +2,13 @@ #### 5.0.0 -* The external `account_EcRecover`-method was added again. -* The external method `accounts_Sign(address, data)` was replaced with `accounts_signData(contentType, address, data)`. +* The external `account_EcRecover`-method was reimplemented. +* The external method `account_sign(address, data)` was replaced with `account_signData(contentType, address, data)`. The addition of `contentType` makes it possible to use the method for different types of objects, such as: * signing data with an intended validator (not yet implemented) * signing clique headers, * signing plain personal messages, - * signing structured data adhering to [EIP-712](https://eips.ethereum.org/EIPS/eip-712) (not yet implemented) +* The external method `account_signTypedData` [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and makes it possible to sign typed data. #### 4.0.0 diff --git a/cmd/clef/main.go b/cmd/clef/main.go index 6c1290b9cd..dca06e9a7e 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -26,8 +26,11 @@ import ( "encoding/hex" "encoding/json" "fmt" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" "io" "io/ioutil" + "math/big" "os" "os/signal" "os/user" @@ -631,10 +634,40 @@ func testExternalUI(api *core.SignerAPI) { } var err error + cliqueHeader := types.Header{ + common.HexToHash("0000H45H"), + common.HexToHash("0000H45H"), + common.HexToAddress("0000H45H"), + common.HexToHash("0000H00H"), + common.HexToHash("0000H45H"), + common.HexToHash("0000H45H"), + types.Bloom{}, + big.NewInt(1337), + big.NewInt(1337), + 1338, + 1338, + big.NewInt(1338), + []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"), + common.HexToHash("0x0000H45H"), + types.BlockNonce{}, + } + cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader) + if err != nil { + utils.Fatalf("Should not error: %v", err) + } + addr, err := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") + if err != nil { + utils.Fatalf("Should not error: %v", err) + } + _, err = api.SignData(ctx, "application/clique", *addr, cliqueRlp) + checkErr("SignData", err) + _, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil) checkErr("SignTransaction", err) _, err = api.SignData(ctx, "text/plain", common.MixedcaseAddress{}, common.Hex2Bytes("01020304")) checkErr("SignData", err) + _, err = api.SignTypedData(ctx, common.MixedcaseAddress{}, core.TypedData{}) + checkErr("SignTypedData", err) _, err = api.List(ctx) checkErr("List", err) _, err = api.New(ctx) @@ -654,7 +687,6 @@ func testExternalUI(api *core.SignerAPI) { } else { log.Info("No errors") } - } // getPassPhrase retrieves the password associated with clef, either fetched diff --git a/interfaces.go b/interfaces.go index c6850cdbe0..be78344062 100644 --- a/interfaces.go +++ b/interfaces.go @@ -208,4 +208,4 @@ type GasEstimator interface { // pending state. type PendingStateEventer interface { SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error) -} \ No newline at end of file +} diff --git a/signer/core/api.go b/signer/core/api.go index 8384790de6..691372f249 100644 --- a/signer/core/api.go +++ b/signer/core/api.go @@ -118,21 +118,21 @@ type SigFormat struct { } var ( - TextPlain = SigFormat{ - "text/plain", - 0x00, - } TextValidator = SigFormat{ "text/validator", - 0x01, + 0x00, } DataTyped = SigFormat{ "data/typed", - 0x45, + 0x01, } ApplicationClique = SigFormat{ "application/clique", - 0x90, + 0x02, + } + TextPlain = SigFormat{ + "text/plain", + 0x45, } ) @@ -607,22 +607,6 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil. sighash, msg := signTextPlain(data) req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType} - //case DataTyped.Mime: - // // Typed data according to EIP712: - // // - // // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") - // fmt.Println("Did we get here, chief? #1") - // typedData := TypedData{} - // if err := rlp.DecodeBytes(data, typedData); err != nil { - // return nil, err - // } - // fmt.Println("Did we get here, chief? #2") - // sighash, err := signTypedData(context.Background(), typedData) - // if err != nil { - // return nil, err - // } - // msg := fmt.Sprintf("Typed data domain %s", typedData.Domain) - // req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType} case ApplicationClique.Mime: // Clique is the Ethereum PoA standard header := &types.Header{} @@ -661,7 +645,7 @@ func signTextWithValidator(data []byte) ([]byte, string) { return crypto.Keccak256([]byte(msg)), msg } -// SignCliqueHeader returns the hash which is used as input for the proof-of-authority +// signCliqueHeader returns the hash which is used as input for the proof-of-authority // signing. It is the hash of the entire header apart from the 65 byte signature // contained at the end of the extra data. // diff --git a/signer/core/api_layout.md b/signer/core/api_layout.md index 289f711eb9..1a251fe9ee 100644 --- a/signer/core/api_layout.md +++ b/signer/core/api_layout.md @@ -1,7 +1,7 @@ # Specs -`encode(domainSeparator : š”¹Ā²āµā¶, message : š•Š) = "\x19\x45" ‖ domainSeparator ‖ hashStruct(message)` +`encode(domainSeparator : š”¹Ā²āµā¶, message : š•Š) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)` - data adheres to š•Š, a structure defined in the rigorous eip-712 -- `\x45` is needed to comply with EIP-191 +- `\x00` is needed to comply with EIP-191 - `domainSeparator` and `hashStruct` are defined below ## A) domainSeparator @@ -26,6 +26,8 @@ Struct named `EIP712Domain` with one or more of the below fields: - each member is written as `type ‖ " " ‖ name` - encodings cascade down and are sorted by name +Example: `Mail(Person from,Person to,string contents)Person(string name,address wallet)` + ### ii) encodeData - `enc(value₁) ‖ enc(valueā‚‚) ‖ … ‖ enc(valueā‚™)` - each encoded member is 32-byte long @@ -52,7 +54,7 @@ Struct named `EIP712Domain` with one or more of the below fields: ```json { "jsonrpc": "2.0", - "method": "account_signStructuredData", + "method": "account_signTypedData", "params": [ "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", { @@ -131,4 +133,4 @@ Struct named `EIP712Domain` with one or more of the below fields: "jsonrpc": "2.0", "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" } -``` \ No newline at end of file +``` diff --git a/signer/core/apiv2.go b/signer/core/apiv2.go index 3485bcdf64..8c87ebd844 100644 --- a/signer/core/apiv2.go +++ b/signer/core/apiv2.go @@ -1,34 +1,259 @@ package core import ( + "bytes" "context" + "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" "math/big" + "sort" + "strings" + "unicode" ) type TypedData struct { - Types map[string] interface{} `json:"types"` - PrimaryType string `json:"primaryType"` - Domain EIP712Domain `json:"domain"` - Message map[string] interface{} `json:"message"` + Types EIP712Types `json:"types"` + PrimaryType string `json:"primaryType"` + Domain EIP712Domain `json:"domain"` + Message EIP712Message `json:"message"` +} + +type EIP712Types map[string][]map[string]string + +type EIP712TypePriority struct { + Type string + Value uint } 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"` + Name string `json:"name"` + Version string `json:"version"` + ChainId *big.Int `json:"chainId"` + VerifyingContract common.Address `json:"verifyingContract"` + Salt hexutil.Bytes `json:"salt"` } +type EIP712Message map[string]interface{} + // Typed data according to EIP712 // // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) { - fmt.Println("addr", addr) - //fmt.Println("data", data) - fmt.Println("data.Domain", data.Domain) - return common.Hex2Bytes("0xdeadbeef"), nil + if err := data.Domain.IsValid(); err != nil { + return nil, err + } + if data.PrimaryType == "" { + return nil, fmt.Errorf("primary type undefined") + } + + domainTypes := EIP712Types{ + "EIP712Domain": data.Types["EIP712Domain"], + } + domainSeparator, err := hashStruct(domainTypes, data.Domain.Values(), "") + if err != nil { + return nil, err + } + + delete(data.Types, "EIP712Domain") + typedDataHash, err := hashStruct(data.Types, data.Message, data.PrimaryType) + if err != nil { + return nil, err + } + + fmt.Println("domainSeparator", domainSeparator.String()) + fmt.Println("typedDataHash", typedDataHash.String()) + return common.FromHex("0xdeadbeef"), nil +} + +// `encode(domainSeparator : š”¹Ā²āµā¶, message : š•Š) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)` +func hashStruct(types EIP712Types, message EIP712Message, primaryType string) (common.Hash, error) { + if primaryType != "" { + if types[primaryType] == nil { + return common.Hash{}, fmt.Errorf("primaryType specified but undefined") + } + } + + typeEncoding, err := encodeType(types, primaryType) + if err != nil { + return common.Hash{}, err + } + typeHash := hex.EncodeToString(crypto.Keccak256([]byte(typeEncoding))) + + dataEncoding, err := encodeData(message) + if err != nil { + return common.Hash{}, err + } + dataHash := hex.EncodeToString(crypto.Keccak256([]byte(dataEncoding))) + + var buffer bytes.Buffer + buffer.WriteString(typeHash) + buffer.WriteString(dataHash) + hash := common.BytesToHash(crypto.Keccak256(buffer.Bytes())) + + return hash, nil +} + +// encodeType transforms the given types into an encoding of the form +// `name ‖ "(" ‖ member₁ ‖ "," ‖ memberā‚‚ ‖ "," ‖ … ‖ memberā‚™ ")"` +// +// Each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name +func encodeType(types EIP712Types, primaryType string) (string, error) { + var priorities = make(map[string]uint) + for key := range types { + priorities[key] = 0 + } + + // Updates the priority for every new custom type discovered + update := func(typeKey string, typeVal string) { + priorities[typeVal]++ + + // Importantly, we also have to check for parent types to increment them too + for _, typeObj := range types[typeVal] { + _typeVal := typeObj["type"] + + firstChar := []rune(_typeVal)[0] + if unicode.IsUpper(firstChar) { + priorities[_typeVal]++ + } + } + } + + // Checks if referenced type has already been visited to optimise algo + visited := func(arr []string, val string) bool { + for _, elem := range arr { + if elem == val { + return true + } + } + return false + } + + for typeKey, typeArr := range types { + var typeValArr []string + + for _, typeObj := range typeArr { + typeVal := typeObj["type"] + if typeKey == typeVal { + return "", fmt.Errorf("type %s cannot reference itself", typeVal) + } + + firstChar := []rune(typeVal)[0] + if unicode.IsUpper(firstChar) { + if types[typeVal] != nil { + if !visited(typeValArr, typeVal) { + typeValArr = append(typeValArr, typeVal) + update(typeKey, typeVal) + } + } else { + return "", fmt.Errorf("referenced type %s is undefined", typeVal) + } + } else { + if !types.IsStandardType(typeVal) { + if types[typeVal] != nil { + return "", fmt.Errorf("Custom type %s must be capitalized", typeVal) + } else { + return "", fmt.Errorf("Unknown type %s", typeVal) + } + } + } + } + + typeValArr = []string{} + } + + sortedPriorities := types.SortByPriorityAndName(priorities) + var buffer bytes.Buffer + for _, priority := range sortedPriorities { + typeKey := priority.Type + typeArr := types[typeKey] + + buffer.WriteString(typeKey) + buffer.WriteString("(") + + for _, typeObj := range typeArr { + buffer.WriteString(typeObj["type"]) + buffer.WriteString(" ") + buffer.WriteString(typeObj["name"]) + buffer.WriteString(",") + } + + buffer.Truncate(buffer.Len() - 1) + buffer.WriteString(")") + } + + return buffer.String(), nil +} + +func encodeData(values EIP712Message) (string, error) { + return "", nil +} + +// Checks if the given type is a standard type accepted by EIP-712 +func (types *EIP712Types) IsStandardType(typeStr string) bool { + standardTypes := []string{ + "array", + "address", + "boolean", + "bytes", + "string", + "struct", + "uint", + } + for _, val := range standardTypes { + if strings.HasPrefix(typeStr, val) { + return true + } + } + return false +} + +// Helper function to sort types by priority and name. Priority is calculated b +// based upon the number of references. +func (types *EIP712Types) SortByPriorityAndName(input map[string]uint) []EIP712TypePriority { + var priorities []EIP712TypePriority + for key, val := range input { + priorities = append(priorities, EIP712TypePriority{key, val}) + } + // Alphabetically + sort.Slice(priorities, func(i, j int) bool { + return priorities[i].Type < priorities[j].Type + }) + // Priority + sort.Slice(priorities, func(i, j int) bool { + return priorities[i].Value > priorities[j].Value + }) + + for _, priority := range priorities { + fmt.Printf("%s, Value %d\n", priority.Type, priority.Value) + } + fmt.Printf("\n") + + return priorities +} + +// Check if the given domain is valid, i.e. contains at least the minimum viable keys and values +func (domain *EIP712Domain) IsValid() error { + if domain.ChainId == big.NewInt(0) { + return fmt.Errorf("chainId must be specified according to EIP-155") + } + + if domain.Name == "" && domain.Version == "" && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 { + return fmt.Errorf("domain undefined") + } + + return nil +} + +// Helper function to return the values of a domain in the form of a golang map +func (domain *EIP712Domain) Values() map[string]interface{} { + return map[string]interface{}{ + "name": domain.Name, + "version": domain.Version, + "chainId": domain.Name, + "verifyingContract": domain.VerifyingContract, + "salt": domain.Salt, + } }