143 lines
7.6 KiB
Solidity
143 lines
7.6 KiB
Solidity
pragma solidity ^0.6.0;
|
|
|
|
import "../lib/ds-test/src/test.sol";
|
|
|
|
import "./vyper_setup.sol";
|
|
import "../deposit_contract.sol";
|
|
|
|
contract DepositContractTest is DSTest {
|
|
DepositContract depositContract_sol;
|
|
DepositContract depositContract_vyp;
|
|
uint64 constant GWEI = 1000000000;
|
|
|
|
function setUp() public {
|
|
VyperSetup vyperSetup = new VyperSetup();
|
|
depositContract_vyp = DepositContract(vyperSetup.deployDeposit());
|
|
depositContract_sol = new DepositContract();
|
|
}
|
|
|
|
// --- SUCCESS TESTS ---
|
|
|
|
// Tests initilized storage values, comparing vyper and solidity
|
|
function test_empty_root() public {
|
|
bytes32 zHash = 0x0000000000000000000000000000000000000000000000000000000000000000;
|
|
bytes32 zHashN = zHash;
|
|
for (uint i = 0; i <= 31; i++) {
|
|
zHashN = sha256(abi.encodePacked(zHashN, zHashN));
|
|
}
|
|
assertEq(sha256(abi.encodePacked(zHashN, zHash)), depositContract_vyp.get_deposit_root());
|
|
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
|
}
|
|
|
|
// Generates 16 random deposits, insert them in both vyper and solidity version and compare get_deposit_root after each insertion
|
|
function test_16_deposits(bytes32[16] memory pubkey_one, bytes16[16] memory pubkey_two, bytes32[16] memory _withdrawal_credentials,
|
|
bytes32[16] memory sig_one, bytes32[16] memory sig_two, bytes32[16] memory sig_three, uint32[16] memory amount) public {
|
|
uint j;
|
|
for (uint i = 0; i < 16; i++) {
|
|
// as of dcaa774, the solidity version is more restrictive than vyper and requires deposits to be divisible by GWEI
|
|
uint64 gweiamount = amount[i] * GWEI;
|
|
if (1 ether <= gweiamount) {
|
|
j++;
|
|
deposit_in(depositContract_sol, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
|
deposit_in(depositContract_vyp, pubkey_one[i], pubkey_two[i], _withdrawal_credentials[i], sig_one[i], sig_two[i], sig_three[i], gweiamount);
|
|
|
|
assertEq(depositContract_sol.get_deposit_root(), depositContract_vyp.get_deposit_root());
|
|
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(abi.encodePacked(depositContract_vyp.get_deposit_count())));
|
|
assertEq(keccak256(abi.encodePacked(depositContract_sol.get_deposit_count())), keccak256(to_little_endian_64(uint64(j))));
|
|
}
|
|
}
|
|
}
|
|
|
|
// The solidity contract fails when given a deposit which is not divisible by GWEI
|
|
|
|
function testFail_deposit_not_divisible_by_gwei(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials,
|
|
bytes32 sig_one, bytes32 sig_two, bytes32 sig_three) public {
|
|
deposit_in(depositContract_sol, pubkey_one, pubkey_two, _withdrawal_credentials, sig_one, sig_two, sig_three, 1 ether + 1);
|
|
}
|
|
|
|
// --- FAILURE TESTS ---
|
|
|
|
// if the node is given randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
|
function testFail_malformed_node_vyp(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
|
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
|
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
|
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials); //I wish just recasting to `bytes` would work..
|
|
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
|
depositContract_vyp.deposit.value(amount)(pubkey, withdrawal_credentials, signature, node);
|
|
}
|
|
|
|
// If the node is taken randomly instead of as the ssz root, the chances of success are so unlikely that we can assert it to be false
|
|
function testFail_malformed_node_sol(bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one,
|
|
bytes32 sig_two, bytes32 sig_three, uint64 amount, bytes32 node) public {
|
|
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
|
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
|
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
|
depositContract_sol.deposit.value(amount)(pubkey, withdrawal_credentials, signature, node);
|
|
}
|
|
|
|
// if bytes lengths are wrong, the call will fail
|
|
function testFail_malformed_calldata_vyp(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
|
if (amount >= 1000000000000000000) {
|
|
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
|
depositContract_vyp.deposit.value(amount)(pubkey, withdrawal_credentials, signature,
|
|
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
|
);
|
|
} else { revert(); }
|
|
} else { revert(); }
|
|
}
|
|
|
|
function testFail_malformed_calldata_sol(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, uint64 amount) public {
|
|
if (amount >= 1000000000000000000) {
|
|
if (!(pubkey.length == 48 && withdrawal_credentials.length == 32 && signature.length == 96)) {
|
|
depositContract_sol.deposit.value(amount)(pubkey, withdrawal_credentials, signature,
|
|
encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI))
|
|
);
|
|
} else { revert(); }
|
|
} else { revert(); }
|
|
}
|
|
|
|
// --- HELPER FUNCTIONS ---
|
|
|
|
function deposit_in(DepositContract depositContract, bytes32 pubkey_one, bytes16 pubkey_two, bytes32 _withdrawal_credentials, bytes32 sig_one, bytes32 sig_two, bytes32 sig_three, uint64 amount) public {
|
|
bytes memory pubkey = abi.encodePacked(pubkey_one, pubkey_two);
|
|
bytes memory withdrawal_credentials = abi.encodePacked(_withdrawal_credentials);
|
|
bytes memory signature = abi.encodePacked(sig_one, sig_two, sig_three);
|
|
bytes32 node = encode_node(pubkey, withdrawal_credentials, signature, to_little_endian_64(amount / GWEI));
|
|
depositContract.deposit.value(amount)(pubkey, withdrawal_credentials, signature, node);
|
|
}
|
|
|
|
function slice(bytes memory a, uint32 offset, uint32 size) pure internal returns (bytes memory result) {
|
|
result = new bytes(size);
|
|
for (uint i = 0; i < size; i++) {
|
|
result[i] = a[offset+i];
|
|
}
|
|
}
|
|
|
|
function encode_node(bytes memory pubkey, bytes memory withdrawal_credentials, bytes memory signature, bytes memory amount) public pure returns (bytes32) {
|
|
bytes16 zero_bytes16;
|
|
bytes24 zero_bytes24;
|
|
bytes32 zero_bytes32;
|
|
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, zero_bytes16));
|
|
bytes32 signature_root = sha256(abi.encodePacked(
|
|
sha256(abi.encodePacked(slice(signature, 0, 64))),
|
|
sha256(abi.encodePacked(slice(signature, 64, 32), zero_bytes32))
|
|
));
|
|
return sha256(abi.encodePacked(
|
|
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
|
|
sha256(abi.encodePacked(amount, zero_bytes24, signature_root))
|
|
));
|
|
}
|
|
|
|
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
|
|
ret = new bytes(8);
|
|
ret[0] = bytes1(uint8(value & 0xff));
|
|
ret[1] = bytes1(uint8((value >> 8) & 0xff));
|
|
ret[2] = bytes1(uint8((value >> 16) & 0xff));
|
|
ret[3] = bytes1(uint8((value >> 24) & 0xff));
|
|
ret[4] = bytes1(uint8((value >> 32) & 0xff));
|
|
ret[5] = bytes1(uint8((value >> 40) & 0xff));
|
|
ret[6] = bytes1(uint8((value >> 48) & 0xff));
|
|
ret[7] = bytes1(uint8((value >> 56) & 0xff));
|
|
}
|
|
}
|