Port axic/eth2-deposit-contract r2 to this repo

This commit is contained in:
Hsiao-Wei Wang 2020-08-17 22:11:13 +08:00
commit a6c168be04
No known key found for this signature in database
GPG Key ID: 95B070122902DEA4
13 changed files with 629 additions and 0 deletions

View File

@ -0,0 +1 @@
*.sol linguist-language=Solidity

3
solidity_deposit_contract/.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib/ds-test"]
path = lib/ds-test
url = https://github.com/dapphub/ds-test

View File

@ -0,0 +1,28 @@
The contract logic here is based on the deposit contract written in Vyper.
The original repository (https://github.com/ethereum/deposit_contract) had the following authors:
@NIC619
@carver
@hwwhww
@davesque
@ralexstokes
@ChihChengLiang
@nisdas
@djrtwo
@JustinDrake
@vbuterin
@njgheorghita
@CarlBeek
In eth2.0-specs repository (https://github.com/ethereum/eth2.0-specs), its current location, it had the following additional authors:
@daejunpark
And this repository has the following authors:
@axic
@MrChico
@chriseth
(All of the above are GitHub usernames in order of they appeared first in the commit history.)

View File

@ -0,0 +1,116 @@
CC0 1.0 Universal
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.
For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and
vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.
For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

View File

@ -0,0 +1,22 @@
all: compile
clean:
@rm -rf build
@rm -f deposit_contract.json
# Note: using /bin/echo for macOS support
compile: clean
@git submodule update --recursive --init
@solc --metadata-literal --optimize --optimize-runs 5000000 --bin --abi --combined-json=abi,bin,bin-runtime,srcmap,srcmap-runtime,ast,metadata,storage-layout --overwrite -o build deposit_contract.sol tests/deposit_contract.t.sol
@/bin/echo -n '{"abi": ' > deposit_contract.json
@cat build/DepositContract.abi >> deposit_contract.json
@/bin/echo -n ', "bytecode": "0x' >> deposit_contract.json
@cat build/DepositContract.bin >> deposit_contract.json
@/bin/echo -n '"}' >> deposit_contract.json
export DAPP_SKIP_BUILD:=1
export DAPP_SRC:=.
export DAPP_JSON:=build/combined.json
test:
dapp test -v --fuzz-runs 5

View File

@ -0,0 +1,26 @@
# eth2-deposit-contract
This is a port of the [Vyper Eth 2.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/dev/deposit_contract/contracts/validator_registration.vy) to Solidity.
The original motivation was to run the SMTChecker and the new Yul IR generator option (`--ir`) in the compiler.
As of June 2020, this contract (the version tagged as `r1`) has been verified and is considered for adoption.
See this [blog post](https://blog.ethereum.org/2020/06/23/eth2-quick-update-no-12/) for more information.
## Using this with the tests
1. Create the `deposit_contract.json` by running `make` (this requires `solc` to be in the path)
2. Download [eth2.0-specs](https://github.com/ethereum/eth2.0-specs)
3. Replace `eth2.0-specs/deposit_contract/contracts/validator_registration.json` with `deposit_contract.json`
4. In the `eth2.0-specs` directory run `make install_deposit_contract_tester` to install the tools needed (make sure to have Python 3.7 and pip installed)
5. Finally in the `eth2.0-specs` directory run `make test_deposit_contract` to execute the tests
The Makefile currently compiles the code without optimisations. To enable optimisations add `--optimize` to the `solc` line.
## Running randomized `dapp` tests:
Install the latest version of `dapp` by following the instructions at [dapp.tools](https://dapp.tools/). Then run
```sh
make test
```

View File

@ -0,0 +1,77 @@
version: 2.1
jobs:
build:
docker:
- image: ethereum/solc:0.6.11-alpine
steps:
- checkout
- run:
name: Install build essentials
command: |
apk update
apk add git make
- run:
name: Compile the contract
command: |
make
git diff --color --exit-code
- persist_to_workspace:
root: .
paths:
- deposit_contract.json
- build/combined.json
- lib
spectest:
docker:
- image: cimg/python:3.8.1
steps:
- checkout
- attach_workspace:
at: /tmp/
- run:
name: Update python3
command: |
sudo apt-get update
sudo apt-get install -y python3-venv
- run:
name: Install eth2.0-specs tests
command: |
git clone https://github.com/ethereum/eth2.0-specs --single-branch --branch v0.11.2
cp -f /tmp/deposit_contract.json eth2.0-specs/deposit_contract/contracts/validator_registration.json
cd eth2.0-specs
make install_deposit_contract_tester
- run:
name: Run eth2.0-specs tests
command: |
cd eth2.0-specs
make test_deposit_contract
test:
docker:
- image: nixorg/nix:circleci
steps:
- checkout
- attach_workspace:
at: /tmp/
- run:
name: Test the contract
command: |
mkdir build
cp -r /tmp/build/* build
cp -r /tmp/lib/* lib
nix-shell --command 'make test'
workflows:
version: 2
build_and_test:
jobs:
- build
- spectest:
requires:
- build
- test:
requires:
- spectest

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,178 @@
//
//
//
//
//
//
//
//
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.6.11;
// This interface is designed to be compatible with the Vyper version.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
interface IDepositContract {
/// @notice A processed deposit event.
event DepositEvent(
bytes pubkey,
bytes withdrawal_credentials,
bytes amount,
bytes signature,
bytes index
);
/// @notice Submit a Phase 0 DepositData object.
/// @param pubkey A BLS12-381 public key.
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
/// @param signature A BLS12-381 signature.
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
/// Used as a protection against malformed input.
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) external payable;
/// @notice Query the current deposit root hash.
/// @return The deposit root hash.
function get_deposit_root() external view returns (bytes32);
/// @notice Query the current deposit count.
/// @return The deposit count encoded as a little endian 64-bit number.
function get_deposit_count() external view returns (bytes memory);
}
// Based on official specification in https://eips.ethereum.org/EIPS/eip-165
interface ERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceId The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceId` and
/// `interfaceId` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
}
// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
// It tries to stay as close as possible to the original source code.
/// @notice This is the Ethereum 2.0 deposit contract interface.
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
contract DepositContract is IDepositContract, ERC165 {
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
// NOTE: this also ensures `deposit_count` will fit into 64-bits
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
uint256 deposit_count;
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
constructor() public {
// Compute hashes in empty sparse Merkle tree
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
}
function get_deposit_root() override external view returns (bytes32) {
bytes32 node;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1)
node = sha256(abi.encodePacked(branch[height], node));
else
node = sha256(abi.encodePacked(node, zero_hashes[height]));
size /= 2;
}
return sha256(abi.encodePacked(
node,
to_little_endian_64(uint64(deposit_count)),
bytes24(0)
));
}
function get_deposit_count() override external view returns (bytes memory) {
return to_little_endian_64(uint64(deposit_count));
}
function deposit(
bytes calldata pubkey,
bytes calldata withdrawal_credentials,
bytes calldata signature,
bytes32 deposit_data_root
) override external payable {
// Extended ABI length checks since dynamic types are used.
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
require(signature.length == 96, "DepositContract: invalid signature length");
// Check deposit amount
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
uint deposit_amount = msg.value / 1 gwei;
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
// Emit `DepositEvent` log
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
emit DepositEvent(
pubkey,
withdrawal_credentials,
amount,
signature,
to_little_endian_64(uint64(deposit_count))
);
// Compute deposit data root (`DepositData` hash tree root)
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
bytes32 signature_root = sha256(abi.encodePacked(
sha256(abi.encodePacked(signature[:64])),
sha256(abi.encodePacked(signature[64:], bytes32(0)))
));
bytes32 node = sha256(abi.encodePacked(
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
));
// Verify computed and expected deposit data roots match
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
// Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
// Add deposit data root to Merkle tree (update a single `branch` node)
deposit_count += 1;
uint size = deposit_count;
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
if ((size & 1) == 1) {
branch[height] = node;
return;
}
node = sha256(abi.encodePacked(branch[height], node));
size /= 2;
}
// As the loop should always end prematurely with the `return` statement,
// this code should be unreachable. We assert `false` just to be safe.
assert(false);
}
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
}
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
ret = new bytes(8);
bytes8 bytesValue = bytes8(value);
// Byteswapping during copying to bytes.
ret[0] = bytesValue[7];
ret[1] = bytesValue[6];
ret[2] = bytesValue[5];
ret[3] = bytesValue[4];
ret[4] = bytesValue[3];
ret[5] = bytesValue[2];
ret[6] = bytesValue[1];
ret[7] = bytesValue[0];
}
}

@ -0,0 +1 @@
Subproject commit eb7148d43c1ca6f9890361e2e2378364af2430ba

View File

@ -0,0 +1,18 @@
let dapptools = builtins.fetchGit {
url = "https://github.com/dapphub/dapptools.git";
rev = "11dcefe1f03b0acafe76b4d7d54821ef6bd63131";
};
nixpkgs = builtins.fetchGit {
url = "https://github.com/nixos/nixpkgs";
ref = "release-19.03";
rev = "f1707d8875276cfa110139435a7e8998b4c2a4fd";
};
pkgs-for-dapp = import nixpkgs {
overlays = [
(import (dapptools + /overlay.nix))
];
};
in
pkgs-for-dapp.mkShell {
buildInputs = [ pkgs-for-dapp.dapp pkgs-for-dapp.solc pkgs-for-dapp.hevm ];
}

View File

@ -0,0 +1,142 @@
pragma solidity ^0.6.2;
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));
}
}

File diff suppressed because one or more lines are too long