Skip to content

Commit 4e86390

Browse files
authored
Merge pull request #10590 from ethereum/deposit-test
Include the eth2 deposit contract as an external test
2 parents 31107e0 + 34cab95 commit 4e86390

File tree

3 files changed

+203
-3
lines changed

3 files changed

+203
-3
lines changed

test/libsolidity/AnalysisFramework.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ AnalysisFramework::parseAnalyseAndReturnError(
5151
)
5252
{
5353
compiler().reset();
54+
// Do not insert license if it is already present.
55+
bool insertLicense = _insertLicenseAndVersionPragma && _source.find("// SPDX-License-Identifier:") == string::npos;
5456
compiler().setSources({{"",
55-
_insertLicenseAndVersionPragma ?
56-
"pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n" + _source :
57+
string{_insertLicenseAndVersionPragma ? "pragma solidity >=0.0;\n" : ""} +
58+
string{insertLicense ? "// SPDX-License-Identifier: GPL-3.0\n" : ""} +
5759
_source
5860
}});
5961
compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion());

test/libsolidity/SolidityExecutionFramework.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ bytes SolidityExecutionFramework::compileContract(
132132
string SolidityExecutionFramework::addPreamble(string const& _sourceCode)
133133
{
134134
// Silence compiler version warning
135-
string preamble = "pragma solidity >=0.0;\n// SPDX-License-Identifier: unlicensed\n";
135+
string preamble = "pragma solidity >=0.0;\n";
136+
if (_sourceCode.find("// SPDX-License-Identifier:") == string::npos)
137+
preamble += "// SPDX-License-Identifier: unlicensed\n";
136138
if (
137139
solidity::test::CommonOptions::get().useABIEncoderV2 &&
138140
_sourceCode.find("pragma experimental ABIEncoderV2;") == string::npos &&
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// ┏━━━┓━┏┓━┏┓━━┏━━━┓━━┏━━━┓━━━━┏━━━┓━━━━━━━━━━━━━━━━━━━┏┓━━━━━┏━━━┓━━━━━━━━━┏┓━━━━━━━━━━━━━━┏┓━
2+
// ┃┏━━┛┏┛┗┓┃┃━━┃┏━┓┃━━┃┏━┓┃━━━━┗┓┏┓┃━━━━━━━━━━━━━━━━━━┏┛┗┓━━━━┃┏━┓┃━━━━━━━━┏┛┗┓━━━━━━━━━━━━┏┛┗┓
3+
// ┃┗━━┓┗┓┏┛┃┗━┓┗┛┏┛┃━━┃┃━┃┃━━━━━┃┃┃┃┏━━┓┏━━┓┏━━┓┏━━┓┏┓┗┓┏┛━━━━┃┃━┗┛┏━━┓┏━┓━┗┓┏┛┏━┓┏━━┓━┏━━┓┗┓┏┛
4+
// ┃┏━━┛━┃┃━┃┏┓┃┏━┛┏┛━━┃┃━┃┃━━━━━┃┃┃┃┃┏┓┃┃┏┓┃┃┏┓┃┃━━┫┣┫━┃┃━━━━━┃┃━┏┓┃┏┓┃┃┏┓┓━┃┃━┃┏┛┗━┓┃━┃┏━┛━┃┃━
5+
// ┃┗━━┓━┃┗┓┃┃┃┃┃┃┗━┓┏┓┃┗━┛┃━━━━┏┛┗┛┃┃┃━┫┃┗┛┃┃┗┛┃┣━━┃┃┃━┃┗┓━━━━┃┗━┛┃┃┗┛┃┃┃┃┃━┃┗┓┃┃━┃┗┛┗┓┃┗━┓━┃┗┓
6+
// ┗━━━┛━┗━┛┗┛┗┛┗━━━┛┗┛┗━━━┛━━━━┗━━━┛┗━━┛┃┏━┛┗━━┛┗━━┛┗┛━┗━┛━━━━┗━━━┛┗━━┛┗┛┗┛━┗━┛┗┛━┗━━━┛┗━━┛━┗━┛
7+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┃┃━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8+
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┗┛━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
9+
10+
// SPDX-License-Identifier: CC0-1.0
11+
12+
// This interface is designed to be compatible with the Vyper version.
13+
/// @notice This is the Ethereum 2.0 deposit contract interface.
14+
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
15+
interface IDepositContract {
16+
/// @notice A processed deposit event.
17+
event DepositEvent(
18+
bytes pubkey,
19+
bytes withdrawal_credentials,
20+
bytes amount,
21+
bytes signature,
22+
bytes index
23+
);
24+
25+
/// @notice Submit a Phase 0 DepositData object.
26+
/// @param pubkey A BLS12-381 public key.
27+
/// @param withdrawal_credentials Commitment to a public key for withdrawals.
28+
/// @param signature A BLS12-381 signature.
29+
/// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object.
30+
/// Used as a protection against malformed input.
31+
function deposit(
32+
bytes calldata pubkey,
33+
bytes calldata withdrawal_credentials,
34+
bytes calldata signature,
35+
bytes32 deposit_data_root
36+
) external payable;
37+
38+
/// @notice Query the current deposit root hash.
39+
/// @return The deposit root hash.
40+
function get_deposit_root() external view returns (bytes32);
41+
42+
/// @notice Query the current deposit count.
43+
/// @return The deposit count encoded as a little endian 64-bit number.
44+
function get_deposit_count() external view returns (bytes memory);
45+
}
46+
47+
// Based on official specification in https://eips.ethereum.org/EIPS/eip-165
48+
interface ERC165 {
49+
/// @notice Query if a contract implements an interface
50+
/// @param interfaceId The interface identifier, as specified in ERC-165
51+
/// @dev Interface identification is specified in ERC-165. This function
52+
/// uses less than 30,000 gas.
53+
/// @return `true` if the contract implements `interfaceId` and
54+
/// `interfaceId` is not 0xffffffff, `false` otherwise
55+
function supportsInterface(bytes4 interfaceId) external pure returns (bool);
56+
}
57+
58+
// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity.
59+
// It tries to stay as close as possible to the original source code.
60+
/// @notice This is the Ethereum 2.0 deposit contract interface.
61+
/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs
62+
contract DepositContract is IDepositContract, ERC165 {
63+
uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32;
64+
// NOTE: this also ensures `deposit_count` will fit into 64-bits
65+
uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1;
66+
67+
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch;
68+
uint256 deposit_count;
69+
70+
bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes;
71+
72+
constructor() public {
73+
// Compute hashes in empty sparse Merkle tree
74+
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++)
75+
zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height]));
76+
}
77+
78+
function get_deposit_root() override external view returns (bytes32) {
79+
bytes32 node;
80+
uint size = deposit_count;
81+
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
82+
if ((size & 1) == 1)
83+
node = sha256(abi.encodePacked(branch[height], node));
84+
else
85+
node = sha256(abi.encodePacked(node, zero_hashes[height]));
86+
size /= 2;
87+
}
88+
return sha256(abi.encodePacked(
89+
node,
90+
to_little_endian_64(uint64(deposit_count)),
91+
bytes24(0)
92+
));
93+
}
94+
95+
function get_deposit_count() override external view returns (bytes memory) {
96+
return to_little_endian_64(uint64(deposit_count));
97+
}
98+
99+
function deposit(
100+
bytes calldata pubkey,
101+
bytes calldata withdrawal_credentials,
102+
bytes calldata signature,
103+
bytes32 deposit_data_root
104+
) override external payable {
105+
// Extended ABI length checks since dynamic types are used.
106+
require(pubkey.length == 48, "DepositContract: invalid pubkey length");
107+
require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length");
108+
require(signature.length == 96, "DepositContract: invalid signature length");
109+
110+
// Check deposit amount
111+
require(msg.value >= 1 ether, "DepositContract: deposit value too low");
112+
require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei");
113+
uint deposit_amount = msg.value / 1 gwei;
114+
require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high");
115+
116+
// Emit `DepositEvent` log
117+
bytes memory amount = to_little_endian_64(uint64(deposit_amount));
118+
emit DepositEvent(
119+
pubkey,
120+
withdrawal_credentials,
121+
amount,
122+
signature,
123+
to_little_endian_64(uint64(deposit_count))
124+
);
125+
126+
// Compute deposit data root (`DepositData` hash tree root)
127+
bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0)));
128+
bytes32 signature_root = sha256(abi.encodePacked(
129+
sha256(abi.encodePacked(signature[:64])),
130+
sha256(abi.encodePacked(signature[64:], bytes32(0)))
131+
));
132+
bytes32 node = sha256(abi.encodePacked(
133+
sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)),
134+
sha256(abi.encodePacked(amount, bytes24(0), signature_root))
135+
));
136+
137+
// Verify computed and expected deposit data roots match
138+
require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root");
139+
140+
// Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`)
141+
require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full");
142+
143+
// Add deposit data root to Merkle tree (update a single `branch` node)
144+
deposit_count += 1;
145+
uint size = deposit_count;
146+
for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) {
147+
if ((size & 1) == 1) {
148+
branch[height] = node;
149+
return;
150+
}
151+
node = sha256(abi.encodePacked(branch[height], node));
152+
size /= 2;
153+
}
154+
// As the loop should always end prematurely with the `return` statement,
155+
// this code should be unreachable. We assert `false` just to be safe.
156+
assert(false);
157+
}
158+
159+
function supportsInterface(bytes4 interfaceId) override external pure returns (bool) {
160+
return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId;
161+
}
162+
163+
function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) {
164+
ret = new bytes(8);
165+
bytes8 bytesValue = bytes8(value);
166+
// Byteswapping during copying to bytes.
167+
ret[0] = bytesValue[7];
168+
ret[1] = bytesValue[6];
169+
ret[2] = bytesValue[5];
170+
ret[3] = bytesValue[4];
171+
ret[4] = bytesValue[3];
172+
ret[5] = bytesValue[2];
173+
ret[6] = bytesValue[1];
174+
ret[7] = bytesValue[0];
175+
}
176+
}
177+
// ====
178+
// compileViaYul: also
179+
// ----
180+
// constructor()
181+
// supportsInterface(bytes4): 0x0 -> 0
182+
// supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 #
183+
// supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id #
184+
// supportsInterface(bytes4): 0x8564090700000000000000000000000000000000000000000000000000000000 -> true # the deposit interface id #
185+
// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e
186+
// get_deposit_count() -> 0x20, 8, 0
187+
// # TODO: check balance and logs after each deposit #
188+
// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0 -> FAILURE # Empty input #
189+
// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e
190+
// get_deposit_count() -> 0x20, 8, 0
191+
// deposit(bytes,bytes,bytes,bytes32), 1 ether: 0x80, 0xe0, 0x120, 0xaa4a8d0b7d9077248630f1a4701ae9764e42271d7f22b7838778411857fd349e, 0x30, 0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f73292, 0x67a8811c397529dac52ae1342ba58c9500000000000000000000000000000000, 0x20, 0x00f50428677c60f997aadeab24aabf7fceaef491c96a52b463ae91f95611cf71, 0x60, 0xa29d01cc8c6296a8150e515b5995390ef841dc18948aa3e79be6d7c1851b4cbb, 0x5d6ff49fa70b9c782399506a22a85193151b9b691245cebafd2063012443c132, 0x4b6c36debaedefb7b2d71b0503ffdc00150aaffd42e63358238ec888901738b8 -> # txhash: 0x7085c586686d666e8bb6e9477a0f0b09565b2060a11f1c4209d3a52295033832 #
192+
// get_deposit_root() -> 0x2089653123d9c721215120b6db6738ba273bbc5228ac093b1f983badcdc8a438
193+
// get_deposit_count() -> 0x20, 8, 0x0100000000000000000000000000000000000000000000000000000000000000
194+
// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0x80, 0xe0, 0x120, 0xdbd986dc85ceb382708cf90a3500f500f0a393c5ece76963ac3ed72eccd2c301, 0x30, 0xb2ce0f79f90e7b3a113ca5783c65756f96c4b4673c2b5c1eb4efc22280259441, 0x06d601211e8866dc5b50dc48a244dd7c00000000000000000000000000000000, 0x20, 0x00344b6c73f71b11c56aba0d01b7d8ad83559f209d0a4101a515f6ad54c89771, 0x60, 0x945caaf82d18e78c033927d51f452ebcd76524497b91d7a11219cb3db6a1d369, 0x7595fc095ce489e46b2ef129591f2f6d079be4faaf345a02c5eb133c072e7c56, 0x0c6c3617eee66b4b878165c502357d49485326bc6b31bc96873f308c8f19c09d -> # txhash: 0x404d8e109822ce448e68f45216c12cb051b784d068fbe98317ab8e50c58304ac #
195+
// get_deposit_root() -> 0x40255975859377d912c53aa853245ebd939bdd2b33a28e084babdcc1ed8238ee
196+
// get_deposit_count() -> 0x20, 8, 0x0200000000000000000000000000000000000000000000000000000000000000

0 commit comments

Comments
 (0)