diff --git a/cmd/clef/README.md b/cmd/clef/README.md index 8fcaae7cb5..205b183e50 100644 --- a/cmd/clef/README.md +++ b/cmd/clef/README.md @@ -189,7 +189,9 @@ None "method": "account_new", "params": [] } - +``` +Response +``` { "id": 0, "jsonrpc": "2.0", @@ -222,7 +224,9 @@ None "jsonrpc": "2.0", "method": "account_list" } - +``` +Response +``` { "id": 1, "jsonrpc": "2.0", @@ -285,8 +289,8 @@ Response ```json { + "id": 2, "jsonrpc": "2.0", - "id": 67, "error": { "code": -32000, "message": "Request denied" @@ -298,6 +302,7 @@ Response ```json { + "id": 67, "jsonrpc": "2.0", "method": "account_signTransaction", "params": [ @@ -311,8 +316,7 @@ Response "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" }, "safeSend(address)" - ], - "id": 67 + ] } ``` Response @@ -346,16 +350,18 @@ Bash example: {"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}} ``` - ### account_signData #### Sign data Signs a chunk of data and returns the calculated signature. #### Arguments - - content type [string]: type of data to sign + - content type [string]: type of signed data + - `text/validator`: hex data with custom validator defined in a contract + - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers + - `text/plain`: simple hex data validated by `account_ecRecover` - account [address]: account to sign with - - data [data]: data to sign + - data [object]: data to sign #### Result - calculated signature [data] @@ -377,19 +383,115 @@ Response ```json { - "id": 3, "jsonrpc": "2.0", + "id": 3, "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" } ``` +### account_signTypedData + +#### Sign data + Signs a chunk of structured data conformant to [EIP712]([EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)) and returns the calculated signature. + +#### Arguments + - account [address]: account to sign with + - data [object]: data to sign + +#### Result + - calculated signature [data] + +#### Sample call +```json +{ + "id": 68, + "jsonrpc": "2.0", + "method": "account_signTypedData", + "params": [ + "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + { + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "version", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "verifyingContract", + "type": "address" + } + ], + "Person": [ + { + "name": "name", + "type": "string" + }, + { + "name": "wallet", + "type": "address" + } + ], + "Mail": [ + { + "name": "from", + "type": "Person" + }, + { + "name": "to", + "type": "Person" + }, + { + "name": "contents", + "type": "string" + } + ] + }, + "primaryType": "Mail", + "domain": { + "name": "Ether Mail", + "version": "1", + "chainId": 1, + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" + }, + "message": { + "from": { + "name": "Cow", + "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" + }, + "to": { + "name": "Bob", + "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" + }, + "contents": "Hello, Bob!" + } + } + ] +} +``` +Response + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" +} +``` + ### account_ecRecover #### Sign data - Derive the address from the account that was used to sign data from the data and signature. + Derive the address from the account that was used to sign data with content type `text/plain` and the signature. #### Arguments - - content type [string]: type of signed data - data [data]: data that was signed - signature [data]: the signature to verify @@ -461,7 +563,7 @@ Response }, "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9", "version": 3 - }, + } ] } ``` diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md index a9ab42e5b5..7adc339605 100644 --- a/cmd/clef/extapi_changelog.md +++ b/cmd/clef/extapi_changelog.md @@ -8,7 +8,7 @@ The addition of `contentType` makes it possible to use the method for different * signing data with an intended validator (not yet implemented) * signing clique headers, * signing plain personal messages, -* 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. +* The external method `account_signTypedData` implements [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/signer/core/signed_data.go b/signer/core/signed_data.go index b8dd636a87..532d2c99a5 100644 --- a/signer/core/signed_data.go +++ b/signer/core/signed_data.go @@ -175,7 +175,6 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, addr common.M } sighash, msg := SignTextValidator(validatorData) req = &SignDataRequest{Rawdata: validatorData, Message: msg, Hash: sighash, ContentType: mediaType} - break case ApplicationClique.Mime: // Clique is the Ethereum PoA standard cliqueData, err := hexutil.Decode(data.(string)) @@ -192,7 +191,6 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, addr common.M } msg := fmt.Sprintf("clique block %d [0x%x]", header.Number, header.Hash()) req = &SignDataRequest{Rawdata: cliqueData, Message: msg, Hash: sighash, ContentType: mediaType} - break case TextPlain.Mime: // Calculates an Ethereum ECDSA signature for: // hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}") @@ -202,7 +200,6 @@ func (api *SignerAPI) determineSignatureFormat(contentType string, addr common.M } sighash, msg := SignTextPlain(plainData) req = &SignDataRequest{Rawdata: plainData, Message: msg, Hash: sighash, ContentType: mediaType} - break default: return nil, fmt.Errorf("content type '%s' not implemented for signing", contentType) } @@ -382,7 +379,6 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter bytesValue = append(bytesValue, _byte) } primitiveEncValue = bytesValue - break case "bool": primitiveEncType = "uint256" var int64Val int64 @@ -390,11 +386,9 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter int64Val = 1 } primitiveEncValue = abi.U256(big.NewInt(int64Val)) - break case "bytes", "string": primitiveEncType = "bytes32" primitiveEncValue = crypto.Keccak256(bytesValueOf(encValue)) - break default: if strings.HasPrefix(encType, "bytes") { encTypes = append(encTypes, "bytes32") @@ -404,15 +398,12 @@ func (typedData *TypedData) EncodeData(primaryType string, data map[string]inter for i := 0; i < 32-size; i++ { bytesValue = append(bytesValue, 0) } - for _, _byte := range encValue.(hexutil.Bytes) { - bytesValue = append(bytesValue, _byte) - } + bytesValue = append(bytesValue, encValue.(hexutil.Bytes)...) primitiveEncValue = bytesValue } else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") { primitiveEncType = "uint256" primitiveEncValue = abi.U256(encValue.(*big.Int)) } - break } return primitiveEncType, primitiveEncValue } @@ -600,11 +591,7 @@ func isStandardTypeStr(encType string) bool { } // Reference types - if encType[len(encType)-1] == ']' { - return true - } - - return false + return encType[len(encType)-1] == ']' } // IsValid checks if the given domain is valid, i.e. contains at least