Drafted recursive encodeData

This commit is contained in:
Paul Berg 2018-10-21 13:58:00 +01:00
parent a7003c7032
commit 35b83c1b9f
4 changed files with 595 additions and 329 deletions

View file

@ -62,3 +62,87 @@ t=2018-10-15T03:12:04+0100 lvl=info msg=SignTypedData api=signer type=response d
t=2018-10-15T03:14:48+0100 lvl=info msg=Configured api=signer audit log=audit.log 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=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 t=2018-10-15T03:15:25+0100 lvl=info msg=SignTypedData api=signer type=response data=deadbeef error=nil
t=2018-10-20T15:17:32+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:17:53+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:22:24+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:24:48+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:24:54+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56168\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[] PrimaryType: Domain:{Name: Version: ChainId:<nil> VerifyingContract:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Salt:0x} Message:map[]}"
t=2018-10-20T15:24:54+0100 lvl=info msg=SignTypedData api=signer type=response data= error="primary type undefined"
t=2018-10-20T15:25:28+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:27:12+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:27:59+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:28:02+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56262\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[] PrimaryType: Domain:{Name: Version: ChainId:<nil> VerifyingContract:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Salt:0x} Message:map[]}"
t=2018-10-20T15:28:02+0100 lvl=info msg=SignTypedData api=signer type=response data= error="primary type undefined"
t=2018-10-20T15:28:15+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56262\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[] PrimaryType: Domain:{Name: Version: ChainId:<nil> VerifyingContract:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Salt:0x} Message:map[]}"
t=2018-10-20T15:28:15+0100 lvl=info msg=SignTypedData api=signer type=response data= error="primary type undefined"
t=2018-10-20T15:29:47+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:29:52+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56281\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[] PrimaryType: Domain:{Name: Version: ChainId:<nil> VerifyingContract:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] Salt:0x} Message:map[]}"
t=2018-10-20T15:32:20+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:32:46+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56456\",\"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[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-20T15:34:35+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:35:24+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56476\",\"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[type:string name:contents]]] 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-20T15:37:05+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:37:21+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56561\",\"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:+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-20T15:38:27+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:38:36+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56583\",\"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[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]] 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[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
t=2018-10-20T15:40:49+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:40:54+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56604\",\"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:+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-20T15:41:20+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:41:38+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56681\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[type:string name:name] 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]] 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-20T15:49:10+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:49:20+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56804\",\"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[type:string name:contents]]] 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-20T15:51:01+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:51:31+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56859\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[Mail:[map[type:Person name:from] 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]] 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[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}"
t=2018-10-20T15:51:59+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T15:52:23+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:56905\",\"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:+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-20T16:05:44+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:05:59+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58137\",\"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[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[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob!]}"
t=2018-10-20T16:06:22+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:06:25+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58154\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[type:string name:name] map[type:string name:version] 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:+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-20T16:06:39+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:06:43+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58213\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[type:string name:name] map[name:version type:string] map[name:chainId type:uint256] map[type:address name:verifyingContract]] 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[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T16:06:55+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:07:53+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58301\",\"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[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]]] 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-20T16:10:03+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:10:05+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58446\",\"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[type:string name:contents]] EIP712Domain:[map[type:string name:name] 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[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T16:10:39+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:10:43+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58497\",\"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:+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-20T16:12:31+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:12:37+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58538\",\"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[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]] 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[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T16:12:55+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58538\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\",\"User-Agent\":\"PostmanRuntime/7.3.0\",\"Origin\":\"\"}" addr="0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 [chksum INVALID]" data="{Types:map[EIP712Domain:[map[type:string name:name] 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:+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-20T16:14:08+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:14:11+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58563\",\"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[type:string name:version] map[name:chainId type:uint256] map[type:address name:verifyingContract]] 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:+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[wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 name:Alice]]}"
t=2018-10-20T16:17:07+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:17:13+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58619\",\"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[type:string name:name] 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[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T16:18:51+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:19:01+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:20:03+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58718\",\"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:+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-20T16:24:34+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:25:59+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58919\",\"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[type:uint256 name:chainId] 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[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T16:30:18+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:30:40+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:58983\",\"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:+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-20T16:30:59+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:31:01+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59036\",\"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[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[contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB]]}"
t=2018-10-20T16:31:36+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T16:32:25+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59121\",\"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[type:string name:contents]]] 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[wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21 name:Alice]]}"
t=2018-10-20T19:01:23+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:01:51+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59614\",\"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[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]]] 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-20T19:02:26+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:02:41+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:59722\",\"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:+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-20T19:06:00+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:07:47+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60455\",\"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:+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-20T19:08:16+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60455\",\"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[type:uint256 name:chainId] 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-20T19:09:46+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60587\",\"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:+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-20T19:11:12+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:11:21+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60710\",\"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[to:map[wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB name:Bob] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
t=2018-10-20T19:11:35+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60710\",\"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[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[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T19:11:51+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:11:53+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60768\",\"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[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[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T19:12:11+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:12:30+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60809\",\"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[to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob! from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21]]}"
t=2018-10-20T19:13:53+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:14:04+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:60898\",\"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-20T19:14:32+0100 lvl=info msg=SignTypedData api=signer type=response data=686365d6fa3d2a98d7c67101a8acf295c644a8de80b8ecc89cff276355897f6d error=nil
t=2018-10-20T19:36:35+0100 lvl=info msg=Configured api=signer audit log=audit.log
t=2018-10-20T19:36:40+0100 lvl=info msg=SignTypedData api=signer type=request metadata="{\"remote\":\"127.0.0.1:61953\",\"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[from:map[name:Alice wallet:0x66d76e6a80dc7d46d7ec1b79b15dfa34c3c6ef21] to:map[name:Bob wallet:0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB] contents:Hello, Bob!]}"
t=2018-10-20T19:36:40+0100 lvl=info msg=SignTypedData api=signer type=response data=7951623617a41fec181fafc3555c0a494d01dce0408b6596964f5f50acba239e error=nil

View file

@ -21,11 +21,8 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/sha3"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"mime"
"reflect" "reflect"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -33,7 +30,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/usbwallet" "github.com/ethereum/go-ethereum/accounts/usbwallet"
"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/internal/ethapi" "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
@ -542,181 +538,6 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
} }
// SignData signs the hash of the provided data, but does so differently
// depending on the content-type specified.
//
// Depending on the content-type, different types of validations will occur.
//
// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
// where the V value will be 27 or 28 for legacy reasons.
func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
var req, err = api.determineSignatureFormat(contentType, data)
if err != nil {
return nil, err
}
req.Address = addr
req.Meta = MetadataFromContext(ctx)
// We make the request prior to looking up if we actually have the account, to prevent
// account-enumeration via the API
res, err := api.UI.ApproveSignData(req)
if err != nil {
return nil, err
}
if !res.Approved {
return nil, ErrRequestDenied
}
// Look up the wallet containing the requested signer
account := accounts.Account{Address: addr.Address()}
wallet, err := api.am.Find(account)
if err != nil {
return nil, err
}
// Sign the data with the wallet
signature, err := wallet.SignHashWithPassphrase(account, res.Password, req.Hash)
if err != nil {
api.UI.ShowError(err.Error())
return nil, err
}
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
return signature, nil
}
// Determines which signature method should be used based upon the mime type
func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.Bytes) (*SignDataRequest, error) {
var req *SignDataRequest
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return nil, err
}
switch mediaType {
case TextValidator.Mime:
// Data with an intended validator
sighash, msg := signTextWithValidator(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
case TextPlain.Mime:
// Sign calculates an Ethereum ECDSA signature for:
// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
// In the cases where it matters ensure that the charset is handled. The charset
// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
// charset, ok := params["charset"]
// As it is now, we accept any charset and just treat it as 'raw'.
sighash, msg := signTextPlain(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
case ApplicationClique.Mime:
// Clique is the Ethereum PoA standard
header := &types.Header{}
if err := rlp.DecodeBytes(data, header); err != nil {
return nil, err
}
sighash, err := signCliqueHeader(header)
if err != nil {
return nil, err
}
msg := fmt.Sprintf("Clique block %d [0x%x]", header.Number, header.Hash())
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
default:
return nil, fmt.Errorf("content type '%s' not implemented for signing", contentType)
}
return req, nil
}
// signTextPlain is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
// The hash is calculated as
// keccak256("\x19${byteVersion}Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func signTextPlain(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19\\x%xEthereum Signed Message:\n%d%s", TextPlain.ByteVersion, len(data), data)
return crypto.Keccak256([]byte(msg)), msg
}
// signTextWithValidator signs the given message which can be further recovered
// with the given validator.
func signTextWithValidator(data []byte) ([]byte, string) {
msg := "TODO"
return crypto.Keccak256([]byte(msg)), msg
}
// 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.
//
// The method requires the extra data to be at least 65 bytes -- the original implementation
// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
// and simply return an error instead
func signCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
hash := common.Hash{}
if len(header.Extra) < 65 {
return hash.Bytes(), fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
}
hasher := sha3.NewKeccak256()
rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65],
header.MixDigest,
header.Nonce,
})
hasher.Sum(hash[:0])
return hash.Bytes(), nil
}
// Determines the content type and then recovers the address associated with the given sig
func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) {
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return common.Address{}, err
}
switch mediaType {
case TextPlain.Mime:
// Returns the address for the Account that was used to create the signature.
//
// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
// the address of:
// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
// addr = ecrecover(hash, signature)
//
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be be 27 or 28 for legacy reasons.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
if len(sig) != 65 {
return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
}
if sig[64] != 27 && sig[64] != 28 {
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
}
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
hash, _ := signTextPlain(data)
rpk, err := crypto.SigToPub(hash, sig)
if err != nil {
return common.Address{}, err
}
return crypto.PubkeyToAddress(*rpk), nil
default:
return common.Address{}, fmt.Errorf("content type '%s' not implemented for ecRecover", contentType)
}
}
// Export returns encrypted private key associated with the given address in web3 keystore format. // Export returns encrypted private key associated with the given address in web3 keystore format.
func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)}) res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)})

View file

@ -1,7 +1,7 @@
# Specs # Specs
`encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)` `encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01EthereumSignedMessage\n" ‖ domainSeparator ‖ hashStruct(message)`
- data adheres to 𝕊, a structure defined in the rigorous eip-712 - data adheres to 𝕊, a structure defined in the rigorous eip-712
- `\x00` is needed to comply with EIP-191 - `\x01` is needed to comply with EIP-191
- `domainSeparator` and `hashStruct` are defined below - `domainSeparator` and `hashStruct` are defined below
## A) domainSeparator ## A) domainSeparator
@ -34,9 +34,9 @@ Example: `Mail(Person from,Person to,string contents)Person(string name,address
#### a) atomic #### a) atomic
- `boolean` => `uint256` - `bool` => `uint256`
- `address` => `uint160` - `address` => `uint160`
- `uint` => sign-extended `uint256` in big endian order - `int8:int256` and `uint8:uint256` => sign-extended `uint256` in big endian order
- `bytes1:31` => `bytes32` - `bytes1:31` => `bytes32`
#### b) dynamic #### b) dynamic
@ -49,7 +49,21 @@ Example: `Mail(Person from,Person to,string contents)Person(string name,address
- `array` => `keccak256(encodeData(array))` - `array` => `keccak256(encodeData(array))`
- `struct` => `rec(keccak256(hashStruct(struct)))` - `struct` => `rec(keccak256(hashStruct(struct)))`
## C) Example ## C) Algo
- hashStruct
- encodeType
- encodeData
- if primitive
- encode
- else
- if array
- encodeData
- else if struct
- hashStruct
- else
- break
## D) Example
### Query ### Query
```json ```json
{ {

View file

@ -4,14 +4,18 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp"
"math/big" "math/big"
"math/rand" "mime"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"time"
"unicode" "unicode"
"github.com/PaulRBerg/basics/helpers" "github.com/PaulRBerg/basics/helpers"
@ -22,14 +26,16 @@ import (
) )
type TypedData struct { type TypedData struct {
Types map[string]EIP712Type `json:"types"` Types EIP712Types `json:"types"`
PrimaryType string `json:"primaryType"` PrimaryType string `json:"primaryType"`
Domain EIP712Domain `json:"domain"` Domain EIP712Domain `json:"domain"`
Message EIP712Message `json:"message"` Message EIP712Data `json:"message"`
} }
type EIP712Type []map[string]string type EIP712Type []map[string]string
type EIP712Types map[string]EIP712Type
type EIP712TypePriority struct { type EIP712TypePriority struct {
Type string Type string
Value uint Value uint
@ -38,79 +44,255 @@ type EIP712TypePriority struct {
type EIP712Data = map[string]interface{} 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"`
ChainId *big.Int `json:"chainId"` ChainId *big.Int `json:"chainId"`
VerifyingContract common.Address `json:"verifyingContract"` VerifyingContract common.Address `json:"verifyingContract"`
Salt hexutil.Bytes `json:"salt"` Salt hexutil.Bytes `json:"salt"`
} }
type EIP712Message map[string]interface{} const (
TypeArray = "array"
TypeAddress = "address"
TypeBool = "bool"
TypeBytes = "bytes"
TypeInt = "int"
TypeString = "string"
)
// Sign receives a request and produces a signature
// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
// where the V value will be 27 or 28 for legacy reasons.
func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, req *SignDataRequest) ([]byte, error) {
req.Address = addr
req.Meta = MetadataFromContext(ctx)
// We make the request prior to looking up if we actually have the account, to prevent
// account-enumeration via the API
res, err := api.UI.ApproveSignData(req)
if err != nil {
return nil, err
}
if !res.Approved {
return nil, ErrRequestDenied
}
// Look up the wallet containing the requested signer
account := accounts.Account{Address: addr.Address()}
wallet, err := api.am.Find(account)
if err != nil {
return nil, err
}
// Sign the data with the wallet
signature, err := wallet.SignHashWithPassphrase(account, res.Password, req.Hash)
if err != nil {
return nil, err
}
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
return signature, nil
}
// SignData signs the hash of the provided data, but does so differently
// depending on the content-type specified.
//
// Different types of validation occur.
func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
var req, err = api.determineSignatureFormat(contentType, data)
if err != nil {
return nil, err
}
signature, err := api.Sign(ctx, addr, req)
if err != nil {
api.UI.ShowError(err.Error())
return nil, err
}
return signature, nil
}
// Determines which signature method should be used based upon the mime type
func (api *SignerAPI) determineSignatureFormat(contentType string, data hexutil.Bytes) (*SignDataRequest, error) {
var req *SignDataRequest
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return nil, err
}
switch mediaType {
case TextValidator.Mime:
// Data with an intended validator
sighash, msg := signTextWithValidator(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
case TextPlain.Mime:
// Sign calculates an Ethereum ECDSA signature for:
// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
// In the cases where it matters ensure that the charset is handled. The charset
// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
// charset, ok := params["charset"]
// As it is now, we accept any charset and just treat it as 'raw'.
sighash, msg := signTextPlain(data)
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
case ApplicationClique.Mime:
// Clique is the Ethereum PoA standard
header := &types.Header{}
if err := rlp.DecodeBytes(data, header); err != nil {
return nil, err
}
sighash, err := signCliqueHeader(header)
if err != nil {
return nil, err
}
msg := fmt.Sprintf("Clique block %d [0x%x]", header.Number, header.Hash())
req = &SignDataRequest{Rawdata: data, Message: msg, Hash: sighash, ContentType: mediaType}
break
default:
return nil, fmt.Errorf("content type '%s' not implemented for signing", contentType)
}
return req, nil
}
// signTextPlain is a helper function that calculates a hash for the given message that can be
// safely used to calculate a signature from.
//
// The hash is calculated as
// keccak256("\x19${byteVersion}Ethereum Signed Message:\n"${message length}${message}).
//
// This gives context to the signed message and prevents signing of transactions.
func signTextPlain(data []byte) ([]byte, string) {
msg := fmt.Sprintf("\x19\\x%xEthereum Signed Message:\n%d%s", TextPlain.ByteVersion, len(data), data)
return crypto.Keccak256([]byte(msg)), msg
}
// signTextWithValidator signs the given message which can be further recovered
// with the given validator.
func signTextWithValidator(data []byte) ([]byte, string) {
msg := "TODO"
return crypto.Keccak256([]byte(msg)), msg
}
// 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.
//
// The method requires the extra data to be at least 65 bytes -- the original implementation
// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
// and simply return an error instead
func signCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
hash := common.Hash{}
if len(header.Extra) < 65 {
return hash.Bytes(), fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
}
hasher := sha3.NewKeccak256()
rlp.Encode(hasher, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
header.Root,
header.TxHash,
header.ReceiptHash,
header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra[:len(header.Extra)-65],
header.MixDigest,
header.Nonce,
})
hasher.Sum(hash[:0])
return hash.Bytes(), nil
}
// SignTypedData signs EIP712 conformant typed data // 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, typedData TypedData) (hexutil.Bytes, error) {
if err := data.Domain.IsValid(); err != nil { domainTypes := EIP712Types{
"EIP712Domain": typedData.Types["EIP712Domain"],
}
domainSeparatorBytes := hashStruct(domainTypes, typedData.Domain.Map(), "EIP712Domain", 0)
domainSeparator := common.Bytes2Hex(domainSeparatorBytes)
//if err != nil {
// return nil, err
//}
delete(typedData.Types, "EIP712Domain")
typedDataHashBytes := hashStruct(typedData.Types, typedData.Message, typedData.PrimaryType, 0)
typedDataHash := common.Bytes2Hex(typedDataHashBytes)
//if err != nil {
// return nil, err
//}
fmt.Println("domainSeparator", domainSeparator)
fmt.Println("typedDataHash", typedDataHash)
var buffer bytes.Buffer
buffer.WriteString("\x19\x01")
buffer.WriteString(domainSeparator)
buffer.WriteString(typedDataHash)
sighash := crypto.Keccak256(buffer.Bytes())
msg := fmt.Sprintf("\x19\\x%xEthereum Signed Message\n:%s%s", DataTyped.ByteVersion, domainSeparator, typedDataHash)
//req := &SignDataRequest{Rawdata: typedData, Message: msg, Hash: sighash, ContentType: DataTyped.Mime}
var req, err = api.determineSignatureFormat(contentType, data)
if err != nil {
return nil, err return nil, err
} }
if data.PrimaryType == "" {
return nil, errors.New("primary type undefined") signature, err := api.Sign(ctx, addr, req)
if err != nil {
api.UI.ShowError(err.Error())
return nil, err
} }
domainTypes := map[string]EIP712Type{ return signature, nil
"EIP712Domain": data.Types["EIP712Domain"],
}
domainSeparator := hashStruct(domainTypes, data.PrimaryType, data.Domain.Values(), 0)
//if err != nil {
// return nil, err
//}
delete(data.Types, "EIP712Domain")
typedDataHash := hashStruct(data.Types, data.PrimaryType, data.Message, 0)
//if err != nil {
// return nil, err
//}
fmt.Println("domainSeparator", domainSeparator.String()) return crypto.Keccak256(buffer.Bytes()), nil
fmt.Println("typedDataHash", typedDataHash.String())
return common.FromHex("0xdeadbeef"), nil
} }
// hashStruct generates the following encoding for the given domain and message: // 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 map[string]EIP712Type, key string, data EIP712Data, depth int) common.Hash { func hashStruct(_types EIP712Types, data EIP712Data, dataType string, depth int) []byte {
helpers.PrintJson("hashStruct", map[string]interface{}{ helpers.PrintJson("hashStruct", map[string]interface{}{
"depth": depth, "depth": depth,
}) })
typeEncoding := encodeType(types) typeEncoding := encodeType(_types)
typeHash := hex.EncodeToString(crypto.Keccak256([]byte(typeEncoding))) typeHash := hex.EncodeToString(crypto.Keccak256(typeEncoding))
dataEncoding := encodeData(types, key, data, depth) dataEncoding := encodeData(_types, data, dataType, depth)
dataHash := hex.EncodeToString(crypto.Keccak256([]byte(dataEncoding))) dataHash := hex.EncodeToString(crypto.Keccak256(dataEncoding))
var buffer bytes.Buffer var buffer bytes.Buffer
buffer.WriteString(typeHash) buffer.WriteString(typeHash)
buffer.WriteString(dataHash) buffer.WriteString(dataHash)
hash := common.BytesToHash(crypto.Keccak256(buffer.Bytes())) encoding := crypto.Keccak256(buffer.Bytes())
if depth == 0 { if depth == 0 {
fmt.Printf("typeEncoding %s\n", typeEncoding) fmt.Printf("typeEncoding %s\n", common.Bytes2Hex(typeEncoding))
fmt.Printf("dataEncoding %s\n", dataEncoding) fmt.Printf("dataEncoding %s\n", common.Bytes2Hex(dataEncoding))
} }
return hash
return encoding
} }
// encodeType generates the followign encoding: // 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 map[string]EIP712Type) string { func encodeType(_types EIP712Types) []byte {
helpers.PrintJson("hashStruct", map[string]interface{}{ helpers.PrintJson("encodeType", map[string]interface{}{
"types": types, "types": _types,
}) })
//fmt.Printf("encodeType: types %v\n\n", 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
} }
@ -119,7 +301,7 @@ func encodeType(types map[string]EIP712Type) string {
priorities[typeVal]++ priorities[typeVal]++
// Importantly, we also have to check for parent types to increment them too // Importantly, we also have to check for parent types to increment them too
for _, typeObj := range types[typeVal] { for _, typeObj := range _types[typeVal] {
_typeVal := typeObj["type"] _typeVal := typeObj["type"]
firstChar := []rune(_typeVal)[0] firstChar := []rune(_typeVal)[0]
@ -139,32 +321,22 @@ func encodeType(types map[string]EIP712Type) string {
return false return false
} }
for typeKey, typeArr := range types { for typeKey, typeArr := range _types {
var typeValArr []string var typeValArr []string
for _, typeObj := range typeArr { for _, typeObj := range typeArr {
typeVal := typeObj["type"] typeVal := typeObj["type"]
if typeKey == typeVal { //if typeKey == typeVal {
panic(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]
if unicode.IsUpper(firstChar) { if unicode.IsUpper(firstChar) {
if types[typeVal] != nil { if _types[typeVal] != nil {
if !visited(typeValArr, typeVal) { if !visited(typeValArr, typeVal) {
typeValArr = append(typeValArr, typeVal) typeValArr = append(typeValArr, typeVal)
update(typeKey, typeVal) update(typeKey, typeVal)
} }
} else {
panic(fmt.Errorf("referenced type %s is undefined", typeVal))
}
} else {
if !isStandardType(typeVal) {
if types[typeVal] != nil {
panic(fmt.Errorf("Custom type %s must be capitalized", typeVal))
} else {
panic(fmt.Errorf("Unknown type %s", typeVal))
}
} }
} }
} }
@ -176,7 +348,7 @@ func encodeType(types map[string]EIP712Type) string {
var buffer bytes.Buffer var buffer bytes.Buffer
for _, priority := range sortedPriorities { for _, priority := range sortedPriorities {
typeKey := priority.Type typeKey := priority.Type
typeArr := types[typeKey] typeArr := _types[typeKey]
buffer.WriteString(typeKey) buffer.WriteString(typeKey)
buffer.WriteString("(") buffer.WriteString("(")
@ -192,79 +364,7 @@ func encodeType(types map[string]EIP712Type) string {
buffer.WriteString(")") buffer.WriteString(")")
} }
return buffer.String() return buffer.Bytes()
}
// encodeData generates the following encoding:
// `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()
}
// isStandardType checks if the given type is a EIP712 conformant type
func 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
} }
// sortByPriorityAndName is a 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
@ -283,14 +383,210 @@ func sortByPriorityAndName(input map[string]uint) []EIP712TypePriority {
return priorities[i].Value > priorities[j].Value return priorities[i].Value > priorities[j].Value
}) })
for _, priority := range priorities { //for _, priority := range priorities {
fmt.Printf("%s, Value %d\n", priority.Type, priority.Value) // fmt.Printf("%s, Value %d\n", priority.Type, priority.Value)
} //}
fmt.Printf("\n") //fmt.Printf("\n")
return priorities return priorities
} }
// encodeData generates the following encoding:
// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
//
// each encoded member is 32-byte long
func encodeData(_types EIP712Types, data interface{}, dataType string, depth int) []byte {
helpers.PrintJson("encodeData", map[string]interface{}{
"dataType": dataType,
"data": data,
"depth": depth,
})
var buffer bytes.Buffer
// TODO regex
// handle arrays
if strings.Contains(dataType, "[]") {
arrayVal := data.([]interface{})
dataType := "TODO"
var arrayBuffer bytes.Buffer
for obj := range arrayVal {
objEncoding := encodeData(_types, obj, dataType, depth+1)
arrayBuffer.Write(objEncoding)
}
encoding := arrayBuffer.Bytes()
buffer.Write(encoding)
return buffer.Bytes()
}
// handle maps
firstChar := []rune(dataType)[0]
if unicode.IsUpper(firstChar) {
for mapKey, mapVal := range data.(EIP712Data) {
nextDataType := findNextDataType(_types, dataType, mapKey)
if reflect.TypeOf(mapVal) == reflect.TypeOf(EIP712Data{}) {
data := mapVal.(map[string]interface{})
encoding := hashStruct(_types, data, nextDataType, depth+1)
buffer.Write(encoding)
} else {
encoding := encodeData(_types, mapVal, nextDataType, depth+1)
buffer.Write(encoding)
}
}
return buffer.Bytes()
}
// TODO regex
// handle bytes
if strings.Contains(dataType, TypeBytes) {
bytesVal := data.([]byte)
encoding := crypto.Keccak256(bytesVal)
buffer.Write(encoding)
}
// TODO regex
// handle ints
if strings.Contains(dataType, TypeInt) {
encoding := abi.U256(data.(*big.Int)) // not sure if this is big endian order, but it's definitey sign extended to 256 bit because of using the U256 function
buffer.Write(encoding)
return buffer.Bytes()
}
// handle what's left
switch dataType {
case TypeAddress:
addressVal, _ := data.(common.Address)
encoding := addressVal.Bytes() // hopefully this means uint160 encoding?
buffer.Write(encoding)
break
case TypeBool:
boolVal, _ := data.(bool)
var int64Val int64
if boolVal {
int64Val = 1
}
encoding := abi.U256(big.NewInt(int64Val))
buffer.Write(encoding)
break
case TypeString:
bytesVal := common.FromHex(data.(string))
encoding := crypto.Keccak256(bytesVal)
buffer.Write(encoding)
break
default:
break
}
return buffer.Bytes()
}
// findNextDataType
// blah blah
func findNextDataType(_types EIP712Types, mapType string, mapKey string) string {
eip712type := _types[mapType]
for _, mapObj := range eip712type {
if mapObj["name"] == mapKey {
return mapObj["type"]
}
}
return ""
}
// UnmarshalJSON validates the input data
func (typedData *TypedData) UnmarshalJSON(data []byte) error {
type input struct {
Types EIP712Types `json:"types"`
PrimaryType string `json:"primaryType"`
Domain EIP712Domain `json:"domain"`
Message EIP712Data `json:"message"`
}
var raw input
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if raw.Types == nil {
return errors.New("types are undefined")
}
if err := raw.Types.IsValid(); err != nil {
return err
}
typedData.Types = raw.Types
if raw.Types["EIP712Domain"] == nil {
return errors.New("domain types are undefined")
}
if err := raw.Domain.IsValid(); err != nil {
return err
}
typedData.Domain = raw.Domain
if len(raw.PrimaryType) == 0 {
return errors.New("primary type is undefined")
}
typedData.PrimaryType = raw.PrimaryType
if raw.Message == nil {
return errors.New("message is undefined")
}
typedData.Message = raw.Message
return nil
}
// isStandardType checks if the given type is a EIP712 conformant type
func isStandardTypeStr(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
}
// IsValid checks if the given types object is conformant to the specs
func (types *EIP712Types) IsValid() error {
for typeKey, typeArr := range (*types) {
for _, typeObj := range typeArr {
typeVal := typeObj["type"]
if typeKey == typeVal {
panic(fmt.Errorf("type %s cannot reference itself", typeVal))
}
firstChar := []rune(typeVal)[0]
if unicode.IsUpper(firstChar) {
if (*types)[typeVal] == nil {
return fmt.Errorf("referenced type %s is undefined", typeVal)
}
} else {
if !isStandardTypeStr(typeVal) {
if (*types)[typeVal] != nil {
return fmt.Errorf("custom type %s must be capitalized", typeVal)
} else {
return fmt.Errorf("unknown type %s", typeVal)
}
}
}
}
}
return nil
}
// IsValid checks if the given domain is valid, i.e. contains at least // IsValid checks if the given domain is valid, i.e. contains at least
// the minimum viable keys and values // the minimum viable keys and values
func (domain *EIP712Domain) IsValid() error { func (domain *EIP712Domain) IsValid() error {
@ -307,12 +603,63 @@ func (domain *EIP712Domain) IsValid() error {
// Values is a helper function to return the values of a domain as a map // Values is a helper function to return the values of a domain as a map
// with arbitrary values // with arbitrary values
func (domain *EIP712Domain) Values() map[string]interface{} { func (domain *EIP712Domain) Map() EIP712Data {
return map[string]interface{}{ dataMap := EIP712Data{
"name": domain.Name, "chainId": domain.ChainId,
"version": domain.Version,
"chainId": domain.Name,
"verifyingContract": domain.VerifyingContract,
"salt": domain.Salt,
} }
if len(domain.Name) > 0 {
dataMap["name"] = domain.Name
}
if len(domain.Version) > 0 {
dataMap["version"] = domain.Version
}
if len(domain.VerifyingContract) > 0 {
dataMap["verifyingContract"] = domain.VerifyingContract
}
if len(domain.Salt) > 0 {
dataMap["salt"] = domain.Salt
}
return dataMap
} }
// Determines the content type and then recovers the address associated with the given sig
func (api *SignerAPI) EcRecover(ctx context.Context, contentType string, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) {
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil {
return common.Address{}, err
}
switch mediaType {
case TextPlain.Mime:
// Returns the address for the Account that was used to create the signature.
//
// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
// the address of:
// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
// addr = ecrecover(hash, signature)
//
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be be 27 or 28 for legacy reasons.
//
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
if len(sig) != 65 {
return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
}
if sig[64] != 27 && sig[64] != 28 {
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
}
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
hash, _ := signTextPlain(data)
rpk, err := crypto.SigToPub(hash, sig)
if err != nil {
return common.Address{}, err
}
return crypto.PubkeyToAddress(*rpk), nil
default:
return common.Address{}, fmt.Errorf("content type '%s' not implemented for ecRecover", contentType)
}
}