It is absolutely critical that you would have gone through at least one of our onboarding guides that will teach you the way EthVigil handles user accounts, signing up, logging in, deploying contracts etc.
If you haven't, go check them out.
While you are at it, you might also want to check out working with an ERC20 contract.
The Ethereum platform comes batteries included with primitives that allow cryptographic signing and verification of messages. 'Messages' can be any form of data, the structure of which is agreed upon in both the signing and verifying logic.
Asymmetric/Public Key cryptography
You have a public and a private key. Public key can be distributed, well, publicly. Private, you keep it, private.
The two keys are mathematically related.
You can generate an encrypted version of a piece of data on signing it with your private key. Others can verify i.e. decrypt it with your public key.
Others can use your public key to sign a piece of data only intended for you. You can decrypt the same with your private key.
The encryption algorithms make use of one-way functions -- mathematical functions that are
- computationally cheap to execute to arrive at an output given two inputs
- but computationally expensive by many orders to arrive at an expected set of inputs given an output
- Wikipedia - public key cryptography
- Reddit - What is the encryption algorithm used by Ethereum and how does it work?
Applications of signing and verifying of messages
Any use case which involves the blockchain being used to store proofs of computations/ business process lifecycle changes that are executed 'off-chain' to save transaction costs
State channels - submit proofs of settlements, challenges and verify the same to move the transaction lifecycle forward.
decentralized exchanges - orders take place on a network/chain/database separate from the main chain. As with state channels, the chain is used for settlement.
The signing and verification of data is purely computational and does not require any form of connection to the Ethereum networks.
Creating the signature
How is the signature computed?
Take a look at the
eth_sign method exposed by the JSON-RPC interface of Ethereum clients. The same is also available through other client libraries such as
The sign method calculates an Ethereum specific signature with:
sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message)))
By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim.
Formalizing the message to be signed
message noted above need not be only a string.
For this walkthrough, we have decided upon a unique identifier/counter packed with the address of the verifying contract. This is the smart contract method where the signed data is submitted.
We will get to
recoverSigner in a bit.
We are generating the signature from a constructed
message as described in the section How is the signature computed?
- Do a packed encoding of the individual data fields that make up the message.
address(this) i.e. the address of the verifying contract.
- each data field is encoded according to a hexadecimal representation according to the encoding rules laid out in the formal spec
abi.encodePacked()is available in Solidity that 'mashes' all the encoded values without any extra paddings. From the docs,
- types shorter than 32 bytes are neither zero padded nor sign extended
- dynamic types are encoded in-place and without the length
- array elements are padded, but still encoded in-place
- Do a
keccak256hash of the above packed data. This will always generate a message that is 32-bytes/256-bits long.
prefixed()generates the Ethereum specific signature
We have replaced
len(message) with 32 since it is already known that a
keccak256() always returns a value that is 32-bytes long.
Recovering the message signer in the smart contract
ECDSA signatures in Ethereum consist of three parameters:
s. The signature is always 65-bytes in length.
r= first 32 bytes of signature
s= second 32 bytes of signature
v= final 1 byte of signature
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
Solidity has a built-in function
ecrecover() that accepts
- the expected
keccak256hash correspondign to the correct reconstruction of the message:
uniqueID + address of verifying contract
- the components of the ECDSA signature as described above
and returns the address used to sign the message.
address(this) in calculating signature
We want to add as many unique fields in a message that would avoid "replay" attacks.
uniqueID: Assume submitting a confirmation on the string
ReleasePayment:invoice:0x00aabbccddeeff:amount:15000. The 65-byte signature, if intercepted by a man-in-the-middle, can be resubmitted to the contract and could trigger the same payout function twice.
address(this): let us the same payout contract code has been deployed on a new address, and the uniqueIDs that would have been recorded on the older contract don't hold valid on the new one any more. Hence, older unique identifiers can be used to re-release the amounts once more.
Generating signature and submitting to the smart contract
The code snippets can be found in the github repo that includes a command line script.
Setting up the contract
Launch the webhook listener that listens on port
5554. We will tunnel to it through an
ngrok endpoint that will allow the EthVigil Beta API gateway to deliver event data payloads to our local server.
./ngrok http 5554
Copy the HTTPS forwarding URL, for example,
Register the webhook listening endpoint with the EthVigil platform
Subscribe to all events emitted from this contract
Send a signed message to the contract
The command format is
submitconfirmation <unique sequence ID/nonce> <private key used to sign the message>
The public Ethereum address corresponding to the private key
Verify if the recovered signer address on the contract is the same as expected
The webhook listening endpoint receives the following update
As expected, the retrieved signer in the event data is the same: