Primer reading:
-
EthVigil API usage example on
eth_sign
The broad agenda was to explore offline message signing using only the cryptographic functions offered by Ethereum.
Basically the most widespread usage of public-key cryptography (also known as asymmetric cryptography).
-
One party signs a message (technically, a collection of bytes) with their private key, securely, offline.
-
In our example, this is the party that wishes to confirm a withdrawal ID
- not by sending a transaction from their Ethereum account
- but by signing a certain expected message format with their private key which generates a signed object (65 bytes in length)
- and sending the resulting signed object to the smart contract with the context of a
_requestId
-
The expected message format is known by mutual agreement to the receiver: in this case: the
KidMultiSig
contract. The message is agreed upon both by the prover and the receiver to bea concatenation of request ID and the current contract address of KidMultiSig
. -
Based on this information, the public Ethereum address of the party that signed the known message format is revealed. Think of it as a seal identifying a certain institution or authority. Very close to the concept of how a notary functions. Authorized notes from such parties open up doors in different processes.
This folder contains the smart contract along with python and JS code to interact with it.
Taking the relevant bit of source code from the contract
function confirmRequestWithSignature(
uint256 _requestId,
bytes memory signatureObject
)
public {
[...]
bytes memory expectedFormat = abi.encodePacked(_requestId, address(this));
// bind the expected format within 32 bytes by hashing
bytes32 message = prefixed(keccak256(expectedFormat));
// run the same signing algorithm on the hashed 32-byte message, and recover the public key from the Signed Object
address signer = recoverSigner(message, signatureObject);
[...]
}
Taking relevant bits of source code from multisig_interaction.py
def sign_confirmation(request_id, contractaddr, private_key):
print('Signing data with requestId, contractaddr...')
print(request_id)
print(contractaddr)
pre_hash = solidityKeccak(abi_types=['uint256', 'address'], values=[request_id, contractaddr], validity_check=True)
msg_hash = defunct_hash_message(hexstr=pre_hash.hex())
signed_msg_hash = Account.signHash(msg_hash, private_key)
print(f'Signed message hash: {signed_msg_hash.signature.hex()}')
# return bytes.fromhex(s=signed_msg_hash.signature.hex())
return signed_msg_hash.signature.hex()
Once the above Python code sends out the signed message object to the smart contract as a hex string that represents a series of bytes, recoverSigner()
does the work of splitting this 65 byte signed message object into three parts (v, r, s)
With these three values of (v, r, s)
and the final bytes memory signatureObject
,
- we can run the ECDSA primitive offered in Ethereum as well as the Solidity programming language,
ecrecover
- and finally recover the public Ethereum account address that signed the
signatureObject
Ensure you have created at least one open Withdrawal request on the smart contract.
- Install Python SDK with a single command if you already initialized
ev-cli
. The Python SDK automatically picks up your credentials from~/.ethvigil/settings.json
in that case.
pip install git+https://github.com/blockvigil/ethvigil-python-sdk.git
Detailed installation instructions on EthVigil Docs in case you need to reinstall from scratch.
-
pip install -r requirements.txt
-
Change the very last line in the python file to assign the private key of one of the participants used in the contract constructor arguments
if __name__ == '__main__':
confirm_request_with_sig(
request_id=int(sys.argv[1]),
contract_address=sys.argv[2],
# ensure this is a 0x-prefixed hexadecimal string
privatekey='0xHexadecimalStringGoesHere'
)
-
Run the python script with the request ID as the first argument and the contract address as the second
python multisig_interaction.py <request_id_integer> <contract_address_KidMultiSig>
For eg.
python multisig_interaction.py 1 0x2f57f5748d988e6cf46cf3d8a9ce60ea420ba6b4
-
Run
npm install
-
copy
config.example.js
toconfig.js
and fill up the values. -
Run
node .
-
Attempt to understand the
function splitSignature(bytes memory sig)
and report on it -
Emit a new event
NewDeposit(uint256 amount, address depositor)
after updating thebalances
in the following function
function deposit() public onlyParticipant {
}
- In the function
confirmRequestWithSignature
ensure that when the withdrawal is confirmed, individual balances inbalances
also gets updated just liketotalBalance
below
if (isRequestConfirmed(_requestId)) {
emit WithdrawalConfirmed(_requestId, requestedWithdrawals[_requestId]);
emit ConfirmationSigRecieved(_requestId, signer);
totalBalance -= requestedWithdrawals[_requestId];
}
- Introduce a function to get the status of confirmation on requests for a specific participant.
function getRequestConfirmationStatus(uint256 _requestId, address participant)
public view
returns (bool)
{
}
-
How can you use the above function to get the confirmation status of all participants in the contract against a specific request ID?
-
Attempt to modify the
confirmRequestWithSignature
function so that once aWithdrawalConfirmed
event has been emitted for a specific_requestId
, no more participants should be allowed to call this function.
Hint: add a new function modifier requestIsNotConfirmed
that will revert()
when it finds that
number of approving parties >= total participants÷2