- Download required Python dependencies. This repository has been tested with Python3.9
pip install -r requirements.txt- Clone the forked harmony repository
git clone https://github.com/MaxMustermann2/harmony
cd harmony- Run the (modified) node
make debugNote the value of the string after bootnode bootnodeString, used in the next step
bootnode launched. + /ip4/127.0.0.1/tcp/19876/p2p/xxxxxxxxxxxxxxxxxxx
- Run a validator node from the
harmonyfolder using the key and the password file in this repository. This is needed to verify theCollectRewardsprecompile
./bin/harmony --log-folder logs --min-peers 3 --bootnodes /ip4/127.0.0.1/tcp/19876/p2p/<bootnodeString> --network_type=localnet --dns=false --verbosity=3 --p2p.security.max-conn-per-ip=100 --ip 127.0.0.1 --port 9110 --db_dir ./test/../db-127.0.0.1-9110 --broadcast_invalid_tx=false --http.ip=0.0.0.0 --ws.ip=0.0.0.0 --bls.keys /path/to/harmony-staking-precompiles/bls/0.key --run validator --bls.maxkeys 1 --bls.pass.src file --bls.pass.file /path/to/harmony-staking-precompiles/bls/blspass.txt- Update
.envwith the node API endpoints, and source it
source .env- Add the network to brownie
brownie networks add live private host=${endpoint} chainid=2- Run
./cleanandtest.sh
StakingContract.sol implements the StakingPrecompiles.sol library, which can be inherited and used to call the staking precompiles from any contract. In other words, a sample contract which uses StakingPrecompiles.sol to call the staking precompiles is available as StakingContract.sol.
StakingPrecompiles.sol uses abi.encodeWithSelector and assembly calls in Solidity to call the precompile. These calls require, at maximum, a gas of 25,000 per call, which is what this library uses. In case the precompile throws an error, the entire gas is consumed (as defined in the EVM); if it is successful, only the required gas (21,000 plus data cost) is used.
Other contracts include
MultipleCallsContract.solwhich demonstrates that it is possible to delegate across multiple validators, undelegate, and collect rewards in one single transactionMaliciousContract.solwhich attempts (but fails) to delegate for another addressContractWhichReverts.sol, which makes a delegation and then revertsTrySubsidizeContract.sol, which is used to demonstrate that a benevolent contract cannot use assemblydelegatecallto subsidize the cost of staking transactions for an EOA, that is, the gas used to delegate using the non-EVM staking API is not less than the gas used by the contract
The following tests are designed to cover the edge cases of staking, and do not impact the blockchain state.
Delegate/Undelegatewithout the validator existingCollectRewardswithout delegationDelegatewith balance < delegation amountUndelegatefrom a non-existing validatorUndelegatewith amount > previously delegated amountCollectRewardswhen there are no other rewards to collect- Malicious contracts attempting to
Delegate/Undelegate/CollectRewardsfrom accounts other than themselves will result in a failure (test_delegate_fail_maliciousintest_staking_contract.py) - Multiple calls to the staking precompile in one transaction (made via Solidity code in
MultipleCallsContract.sol) - Assessing the impact to the block time with maximum possible number of calls to the precompile (
test_many_calls_success) - Delegating, and then reverting the state (
test_contract_which_reverts_success) - Accessing the precompile directly through an EOA, instead of through a contract (
test_eoa_access_success). Along similar lines, a Contract which uses assemblydelegatecallhas been provided for demonstration to confirm that the precompile does not subsidize non-EVM staking transactions. (test_eoa_subsidize_success).
Correctly formed transactions (those which end with _success go through), and are part of the tests in this repository.