mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-21 19:20:38 +00:00
Add True Eth2 Deposit Contract, Bytecode, ABI (#9637)
* solidity contract, abi, and deposit util * gaz * gaz * drain contract remove * build * fix up deploy * add readme * fix e2e * revert * revert flag * fix * revert test flag * fix broken test Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
parent
f52f737d2b
commit
191bce3655
@ -6,12 +6,12 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//fuzz:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//shared/attestationutil:__pkg__",
|
||||
"//shared/depositutil:__pkg__",
|
||||
"//shared/keystore:__pkg__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//shared/testutil/altair:__pkg__",
|
||||
|
@ -31,6 +31,7 @@ go_library(
|
||||
"//config/params:go_default_library",
|
||||
"//container/slice:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//math:go_default_library",
|
||||
@ -41,7 +42,6 @@ go_library(
|
||||
"//proto/prysm/v1alpha1/slashings:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/depositutil:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
@ -9,11 +9,11 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
"github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/math"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/depositutil"
|
||||
)
|
||||
|
||||
// ProcessPreGenesisDeposits processes a deposit for the beacon state before chainstart.
|
||||
@ -238,7 +238,7 @@ func verifyDeposit(beaconState state.ReadOnlyBeaconState, deposit *ethpb.Deposit
|
||||
}
|
||||
|
||||
func verifyDepositDataSigningRoot(obj *ethpb.Deposit_Data, domain []byte) error {
|
||||
return depositutil.VerifyDepositSignature(obj, domain)
|
||||
return deposit.VerifyDepositSignature(obj, domain)
|
||||
}
|
||||
|
||||
func verifyDepositDataWithDomain(ctx context.Context, deps []*ethpb.Deposit, domain []byte) error {
|
||||
|
@ -18,12 +18,12 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__pkg__",
|
||||
"//fuzz:__pkg__",
|
||||
"//network/forks:__pkg__",
|
||||
"//proto/prysm/v1alpha1/attestation:__pkg__",
|
||||
"//runtime/interop:__pkg__",
|
||||
"//shared/attestationutil:__pkg__",
|
||||
"//shared/depositutil:__pkg__",
|
||||
"//shared/keystore:__pkg__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//shared/testutil/altair:__pkg__",
|
||||
|
@ -60,14 +60,14 @@ func TestConfigureProofOfWork(t *testing.T) {
|
||||
set.String(flags.DepositContractFlag.Name, "", "")
|
||||
require.NoError(t, set.Set(flags.ChainID.Name, strconv.Itoa(100)))
|
||||
require.NoError(t, set.Set(flags.NetworkID.Name, strconv.Itoa(200)))
|
||||
require.NoError(t, set.Set(flags.DepositContractFlag.Name, "deposit"))
|
||||
require.NoError(t, set.Set(flags.DepositContractFlag.Name, "deposit-contract"))
|
||||
cliCtx := cli.NewContext(&app, set, nil)
|
||||
|
||||
configureEth1Config(cliCtx)
|
||||
|
||||
assert.Equal(t, uint64(100), params.BeaconConfig().DepositChainID)
|
||||
assert.Equal(t, uint64(200), params.BeaconConfig().DepositNetworkID)
|
||||
assert.Equal(t, "deposit", params.BeaconConfig().DepositContractAddress)
|
||||
assert.Equal(t, "deposit-contract", params.BeaconConfig().DepositContractAddress)
|
||||
}
|
||||
|
||||
func TestConfigureNetwork(t *testing.T) {
|
||||
|
@ -32,7 +32,7 @@ go_library(
|
||||
"//beacon-chain/state/v1:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//io/logs:go_default_library",
|
||||
"//monitoring/clientstats:go_default_library",
|
||||
@ -85,7 +85,7 @@ go_test(
|
||||
"//beacon-chain/powchain/types:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//monitoring/clientstats:go_default_library",
|
||||
"//network:go_default_library",
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ import (
|
||||
coreState "github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
protodb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/io/logs"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/clientstats"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
|
||||
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/clientstats"
|
||||
"github.com/prysmaticlabs/prysm/network"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
|
@ -45,6 +45,7 @@ go_library(
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//crypto/rand:go_default_library",
|
||||
@ -59,7 +60,6 @@ go_library(
|
||||
"//proto/prysm/v1alpha1/wrapper:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/depositutil:go_default_library",
|
||||
"//time:go_default_library",
|
||||
"//time/slots:go_default_library",
|
||||
"@com_github_ferranbt_fastssz//:go_default_library",
|
||||
|
@ -9,10 +9,10 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/monitoring/tracing"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/depositutil"
|
||||
"go.opencensus.io/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@ -259,7 +259,7 @@ func (vs *Server) validatorStatus(
|
||||
log.Warn("Not connected to ETH1. Cannot determine validator ETH1 deposit block number")
|
||||
return resp, nonExistentIndex
|
||||
}
|
||||
deposit, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey)
|
||||
dep, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey)
|
||||
if eth1BlockNumBigInt == nil { // No deposit found in ETH1.
|
||||
return resp, nonExistentIndex
|
||||
}
|
||||
@ -272,13 +272,13 @@ func (vs *Server) validatorStatus(
|
||||
log.Warn("Could not compute domain")
|
||||
return resp, nonExistentIndex
|
||||
}
|
||||
if err := depositutil.VerifyDepositSignature(deposit.Data, domain); err != nil {
|
||||
if err := deposit.VerifyDepositSignature(dep.Data, domain); err != nil {
|
||||
resp.Status = ethpb.ValidatorStatus_INVALID
|
||||
log.Warn("Invalid Eth1 deposit")
|
||||
return resp, nonExistentIndex
|
||||
}
|
||||
// Set validator deposit status if their deposit is visible.
|
||||
resp.Status = depositStatus(deposit.Data.Amount)
|
||||
resp.Status = depositStatus(dep.Data.Amount)
|
||||
resp.Eth1DepositBlockNumber = eth1BlockNumBigInt.Uint64()
|
||||
|
||||
return resp, nonExistentIndex
|
||||
|
@ -9,9 +9,9 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//shared/aggregation:__subpackages__",
|
||||
"//shared/depositutil:__subpackages__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
|
@ -30,10 +30,10 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/state/v1",
|
||||
visibility = [
|
||||
"//beacon-chain:__subpackages__",
|
||||
"//contracts/deposit:__subpackages__",
|
||||
"//proto/testing:__subpackages__",
|
||||
"//runtime/interop:__subpackages__",
|
||||
"//shared/aggregation:__subpackages__",
|
||||
"//shared/depositutil:__subpackages__",
|
||||
"//shared/testutil:__pkg__",
|
||||
"//slasher/rpc:__subpackages__",
|
||||
"//testing/benchmark:__pkg__",
|
||||
|
@ -23,7 +23,7 @@ go_test(
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
|
@ -28,6 +28,13 @@ func NewTrie(depth uint64) (*SparseMerkleTrie, error) {
|
||||
return GenerateTrieFromItems(items, depth)
|
||||
}
|
||||
|
||||
// NewTrieWithBranches returns a new merkle trie filled with branches to use.
|
||||
func NewTrieWithBranches(branches [][][]byte, depth uint64) (*SparseMerkleTrie, error) {
|
||||
var zeroBytes [32]byte
|
||||
items := [][]byte{zeroBytes[:]}
|
||||
return GenerateTrieFromItems(items, depth)
|
||||
}
|
||||
|
||||
// CreateTrieFromProto creates a Sparse Merkle Trie from its corresponding merkle trie.
|
||||
func CreateTrieFromProto(trieObj *protodb.SparseMerkleTrie) *SparseMerkleTrie {
|
||||
trie := &SparseMerkleTrie{
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/crypto/hash"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
|
@ -1,6 +0,0 @@
|
||||
# Contracts
|
||||
|
||||
This page serves as a main reference for the smart contract tooling used internally in Prysm.
|
||||
|
||||
**THIS DOES NOT CONTAIN CONTRACTS TO BE USED IN PRODUCTION**.
|
||||
|
@ -1,28 +0,0 @@
|
||||
## Prysm Internal Validator Deposit Contract
|
||||
|
||||
**NOTE: THIS IS NOT THE OFFICIAL ETHEREUM VALIDATOR DEPOSIT CONTRACT. THE OFFICIAL CONTRACT CAN ONLY BE FOUND [HERE](https://github.com/ethereum/consensus-specs/blob/e4a9c5fa29def20c4264cd860868f131d6f40e72/solidity_deposit_contract/deposit_contract.sol). THE ONLY DEPOSIT CONTRACT ON MAINNET HAS ADDRESS 0x00000000219ab540356cbb839cbe05303d7705fa. DO NOT USE THE CONTRACT IN THIS FOLDER OUTSIDE OF DEVELOPMENT**
|
||||
|
||||
## How to execute tests
|
||||
|
||||
```
|
||||
bazel test //contracts/deposit-contract:go_default_test
|
||||
|
||||
```
|
||||
|
||||
Run with `-v` option for detailed log output
|
||||
|
||||
```
|
||||
bazel test //contracts/deposit-contract:go_default_test --test_arg=-test.v --test_output=streamed
|
||||
=== RUN TestSetupRegistrationContract_OK
|
||||
--- PASS: TestSetupRegistrationContract_OK (0.07s)
|
||||
=== RUN TestRegister_Below1ETH
|
||||
--- PASS: TestRegister_Below1ETH (0.02s)
|
||||
=== RUN TestRegister_Above32Eth
|
||||
--- PASS: TestRegister_Above32Eth (0.02s)
|
||||
=== RUN TestValidatorRegister_OK
|
||||
--- PASS: TestValidatorRegister_OK (0.08s)
|
||||
=== RUN TestDrain
|
||||
--- PASS: TestDrain (0.04s)
|
||||
PASS
|
||||
ok contracts/deposit-contract 0.633s
|
||||
```
|
@ -1 +0,0 @@
|
||||
[{"name": "DepositEvent", "inputs": [{"type": "bytes", "name": "pubkey", "indexed": false}, {"type": "bytes", "name": "withdrawal_credentials", "indexed": false}, {"type": "bytes", "name": "amount", "indexed": false}, {"type": "bytes", "name": "signature", "indexed": false}, {"type": "bytes", "name": "index", "indexed": false}], "anonymous": false, "type": "event"}, {"outputs": [], "inputs": [{"type": "address", "name": "_drain_address"}], "constant": false, "payable": false, "type": "constructor"}, {"name": "get_deposit_root", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 95389}, {"name": "get_deposit_count", "outputs": [{"type": "bytes", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 17683}, {"name": "deposit", "outputs": [], "inputs": [{"type": "bytes", "name": "pubkey"}, {"type": "bytes", "name": "withdrawal_credentials"}, {"type": "bytes", "name": "signature"}, {"type": "bytes32", "name": "deposit_data_root"}], "constant": false, "payable": true, "type": "function", "gas": 1754607}, {"name": "drain", "outputs": [], "inputs": [], "constant": false, "payable": false, "type": "function", "gas": 35793}, {"name": "drain_address", "outputs": [{"type": "address", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 663}]
|
File diff suppressed because one or more lines are too long
@ -1,117 +0,0 @@
|
||||
# Vyper target 0.1.0b12
|
||||
MIN_DEPOSIT_AMOUNT: constant(uint256) = 1000000000 # Gwei
|
||||
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
|
||||
MAX_DEPOSIT_COUNT: constant(uint256) = 4294967295 # 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1
|
||||
PUBKEY_LENGTH: constant(uint256) = 48 # bytes
|
||||
WITHDRAWAL_CREDENTIALS_LENGTH: constant(uint256) = 32 # bytes
|
||||
SIGNATURE_LENGTH: constant(uint256) = 96 # bytes
|
||||
AMOUNT_LENGTH: constant(uint256) = 8 # bytes
|
||||
|
||||
DepositEvent: event({
|
||||
pubkey: bytes[48],
|
||||
withdrawal_credentials: bytes[32],
|
||||
amount: bytes[8],
|
||||
signature: bytes[96],
|
||||
index: bytes[8],
|
||||
})
|
||||
|
||||
branch: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
deposit_count: uint256
|
||||
drain_address: public(address)
|
||||
|
||||
# Compute hashes in empty sparse Merkle tree
|
||||
zero_hashes: bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]
|
||||
@public
|
||||
def __init__(_drain_address: address):
|
||||
self.drain_address = _drain_address
|
||||
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH - 1):
|
||||
self.zero_hashes[i + 1] = sha256(concat(self.zero_hashes[i], self.zero_hashes[i]))
|
||||
|
||||
@private
|
||||
@constant
|
||||
def to_little_endian_64(value: uint256) -> bytes[8]:
|
||||
# Reversing bytes using bitwise uint256 manipulations
|
||||
# Note: array accesses of bytes[] are not currently supported in Vyper
|
||||
# Note: this function is only called when `value < 2**64`
|
||||
y: uint256 = 0
|
||||
x: uint256 = value
|
||||
for _ in range(8):
|
||||
y = shift(y, 8)
|
||||
y = y + bitwise_and(x, 255)
|
||||
x = shift(x, -8)
|
||||
return slice(convert(y, bytes32), start=24, len=8)
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_root() -> bytes32:
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
node: bytes32 = zero_bytes32
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
else:
|
||||
node = sha256(concat(node, self.zero_hashes[height]))
|
||||
size /= 2
|
||||
return sha256(concat(node, self.to_little_endian_64(self.deposit_count), slice(zero_bytes32, start=0, len=24)))
|
||||
|
||||
|
||||
@public
|
||||
@constant
|
||||
def get_deposit_count() -> bytes[8]:
|
||||
return self.to_little_endian_64(self.deposit_count)
|
||||
|
||||
|
||||
@payable
|
||||
@public
|
||||
def deposit(pubkey: bytes[PUBKEY_LENGTH],
|
||||
withdrawal_credentials: bytes[WITHDRAWAL_CREDENTIALS_LENGTH],
|
||||
signature: bytes[SIGNATURE_LENGTH],
|
||||
deposit_data_root: bytes32):
|
||||
# Avoid overflowing the Merkle tree (and prevent edge case in computing `self.branch`)
|
||||
assert self.deposit_count < MAX_DEPOSIT_COUNT
|
||||
|
||||
# Check deposit amount
|
||||
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
|
||||
assert deposit_amount >= MIN_DEPOSIT_AMOUNT
|
||||
|
||||
# Length checks to facilitate formal verification (see https://github.com/ethereum/consensus-specs/pull/1362/files#r320361859)
|
||||
assert len(pubkey) == PUBKEY_LENGTH
|
||||
assert len(withdrawal_credentials) == WITHDRAWAL_CREDENTIALS_LENGTH
|
||||
assert len(signature) == SIGNATURE_LENGTH
|
||||
|
||||
# Emit `DepositEvent` log
|
||||
amount: bytes[8] = self.to_little_endian_64(deposit_amount)
|
||||
log.DepositEvent(pubkey, withdrawal_credentials, amount, signature, self.to_little_endian_64(self.deposit_count))
|
||||
|
||||
# Compute deposit data root (`DepositData` hash tree root)
|
||||
zero_bytes32: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000000
|
||||
pubkey_root: bytes32 = sha256(concat(pubkey, slice(zero_bytes32, start=0, len=64 - PUBKEY_LENGTH)))
|
||||
signature_root: bytes32 = sha256(concat(
|
||||
sha256(slice(signature, start=0, len=64)),
|
||||
sha256(concat(slice(signature, start=64, len=SIGNATURE_LENGTH - 64), zero_bytes32)),
|
||||
))
|
||||
node: bytes32 = sha256(concat(
|
||||
sha256(concat(pubkey_root, withdrawal_credentials)),
|
||||
sha256(concat(amount, slice(zero_bytes32, start=0, len=32 - AMOUNT_LENGTH), signature_root)),
|
||||
))
|
||||
# Verify computed and expected deposit data roots match
|
||||
assert node == deposit_data_root
|
||||
|
||||
# Add deposit data root to Merkle tree (update a single `branch` node)
|
||||
self.deposit_count += 1
|
||||
size: uint256 = self.deposit_count
|
||||
for height in range(DEPOSIT_CONTRACT_TREE_DEPTH):
|
||||
if bitwise_and(size, 1) == 1: # More gas efficient than `size % 2 == 1`
|
||||
self.branch[height] = node
|
||||
break
|
||||
node = sha256(concat(self.branch[height], node))
|
||||
size /= 2
|
||||
|
||||
# !!! DEBUG ONLY !!!
|
||||
# This method is NOT part of the final ETH2.0 deposit contract, but we use it
|
||||
# to recover test funds.
|
||||
@public
|
||||
def drain():
|
||||
send(self.drain_address, self.balance)
|
@ -1,75 +0,0 @@
|
||||
package depositcontract
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
amount32Eth = "32000000000000000000"
|
||||
amountLessThan1Eth = "500000000000000000"
|
||||
)
|
||||
|
||||
// TestAccount represents a test account in the simulated backend,
|
||||
// through which we can perform actions on the eth1.0 chain.
|
||||
type TestAccount struct {
|
||||
Addr common.Address
|
||||
ContractAddr common.Address
|
||||
Contract *DepositContract
|
||||
Backend *backends.SimulatedBackend
|
||||
TxOpts *bind.TransactOpts
|
||||
}
|
||||
|
||||
// Setup creates the simulated backend with the deposit contract deployed
|
||||
func Setup() (*TestAccount, error) {
|
||||
genesis := make(core.GenesisAlloc)
|
||||
privKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKeyECDSA, ok := privKey.Public().(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error casting public key to ECDSA")
|
||||
}
|
||||
|
||||
// strip off the 0x and the first 2 characters 04 which is always the EC prefix and is not required.
|
||||
publicKeyBytes := crypto.FromECDSAPub(pubKeyECDSA)[4:]
|
||||
var pubKey = make([]byte, 48)
|
||||
copy(pubKey, publicKeyBytes)
|
||||
|
||||
addr := crypto.PubkeyToAddress(privKey.PublicKey)
|
||||
txOpts, err := bind.NewKeyedTransactorWithChainID(privKey, big.NewInt(1337))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
startingBalance, _ := new(big.Int).SetString("100000000000000000000000000000000000000", 10)
|
||||
genesis[addr] = core.GenesisAccount{Balance: startingBalance}
|
||||
backend := backends.NewSimulatedBackend(genesis, 210000000000)
|
||||
|
||||
contractAddr, _, contract, err := DeployDepositContract(txOpts, backend, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
backend.Commit()
|
||||
|
||||
return &TestAccount{addr, contractAddr, contract, backend, txOpts}, nil
|
||||
}
|
||||
|
||||
// Amount32Eth returns 32Eth(in wei) in terms of the big.Int type.
|
||||
func Amount32Eth() *big.Int {
|
||||
amount, _ := new(big.Int).SetString(amount32Eth, 10)
|
||||
return amount
|
||||
}
|
||||
|
||||
// LessThan1Eth returns less than 1 Eth(in wei) in terms of the big.Int type.
|
||||
func LessThan1Eth() *big.Int {
|
||||
amount, _ := new(big.Int).SetString(amountLessThan1Eth, 10)
|
||||
return amount
|
||||
}
|
@ -3,13 +3,20 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"ETH1logs.go",
|
||||
"depositContract.go",
|
||||
"testutils.go",
|
||||
"contract.go",
|
||||
"deposit.go",
|
||||
"logs.go",
|
||||
"mock.go",
|
||||
],
|
||||
importpath = "github.com/prysmaticlabs/prysm/contracts/deposit-contract",
|
||||
importpath = "github.com/prysmaticlabs/prysm/contracts/deposit",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
@ -27,14 +34,19 @@ go_test(
|
||||
name = "go_default_test",
|
||||
size = "medium",
|
||||
srcs = [
|
||||
"depositContract_test.go",
|
||||
"contract_test.go",
|
||||
"deposit_test.go",
|
||||
"deposit_tree_test.go",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//container/trie:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//runtime/interop:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//:go_default_library",
|
3
contracts/deposit/README.md
Normal file
3
contracts/deposit/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Validator Deposit Contract Local Copy
|
||||
|
||||
This package contains a copy of the official Ethereum [Validator Deposit Contract](https://github.com/ethereum/consensus-specs/tree/e4a9c5fa29def20c4264cd860868f131d6f40e72/solidity_deposit_contract) along with its ABI, bytecode, and Go bindings generated by go-ethereum's [abigen](https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen) `version 1.10.4-stable`. It contains useful test harnesses for setting up and deploying a validator deposit contract using Go bindings, which are used across tests in Prysm.
|
1
contracts/deposit/abi.json
Normal file
1
contracts/deposit/abi.json
Normal file
@ -0,0 +1 @@
|
||||
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"pubkey","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"withdrawal_credentials","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"amount","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"signature","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"index","type":"bytes"}],"name":"DepositEvent","type":"event"},{"inputs":[{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes","name":"withdrawal_credentials","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"deposit_data_root","type":"bytes32"}],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"get_deposit_count","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_deposit_root","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]
|
1
contracts/deposit/bytecode.bin
Normal file
1
contracts/deposit/bytecode.bin
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,14 +1,13 @@
|
||||
package depositcontract_test
|
||||
package deposit_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
@ -82,38 +81,3 @@ func TestValidatorRegister_OK(t *testing.T) {
|
||||
assert.Equal(t, uint64(1), merkleTreeIndex[1], "Deposit event total desposit count miss matched")
|
||||
assert.Equal(t, uint64(2), merkleTreeIndex[2], "Deposit event total desposit count miss matched")
|
||||
}
|
||||
|
||||
func TestDrain(t *testing.T) {
|
||||
testAccount, err := depositcontract.Setup()
|
||||
require.NoError(t, err)
|
||||
testAccount.TxOpts.Value = depositcontract.Amount32Eth()
|
||||
|
||||
// Generate deposit data
|
||||
privKeys, pubKeys, err := interop.DeterministicallyGenerateKeys(0 /*startIndex*/, 1)
|
||||
require.NoError(t, err)
|
||||
depositDataItems, depositDataRoots, err := interop.DepositDataFromKeys(privKeys, pubKeys)
|
||||
require.NoError(t, err)
|
||||
|
||||
var depositDataRoot [32]byte
|
||||
copy(depositDataRoot[:], depositDataRoots[0])
|
||||
_, err = testAccount.Contract.Deposit(testAccount.TxOpts, pubKeys[0].Marshal(), depositDataItems[0].WithdrawalCredentials, depositDataItems[0].Signature, depositDataRoot)
|
||||
testAccount.Backend.Commit()
|
||||
require.NoError(t, err, "Validator registration failed")
|
||||
|
||||
testAccount.Backend.Commit()
|
||||
|
||||
ctx := context.Background()
|
||||
bal, err := testAccount.Backend.BalanceAt(ctx, testAccount.ContractAddr, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, bal.Cmp(depositcontract.Amount32Eth()), "Deposit did not work")
|
||||
|
||||
testAccount.TxOpts.Value = big.NewInt(0)
|
||||
_, err = testAccount.Contract.Drain(testAccount.TxOpts)
|
||||
require.NoError(t, err)
|
||||
|
||||
testAccount.Backend.Commit()
|
||||
|
||||
bal, err = testAccount.Backend.BalanceAt(ctx, testAccount.ContractAddr, nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, big.NewInt(0).Cmp(bal), "Drain did not drain balance")
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Package depositutil contains useful functions for dealing
|
||||
// with Ethereum deposit inputs.
|
||||
package depositutil
|
||||
package deposit
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
178
contracts/deposit/deposit_contract.sol
Normal file
178
contracts/deposit/deposit_contract.sol
Normal 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];
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package depositutil_test
|
||||
package deposit_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/shared/depositutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
@ -19,7 +19,7 @@ func TestDepositInput_GeneratesPb(t *testing.T) {
|
||||
k2, err := bls.RandKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
result, _, err := depositutil.DepositInput(k1, k2, 0)
|
||||
result, _, err := deposit.DepositInput(k1, k2, 0)
|
||||
require.NoError(t, err)
|
||||
assert.DeepEqual(t, k1.PublicKey().Marshal(), result.PublicKey)
|
||||
|
||||
@ -46,29 +46,29 @@ func TestDepositInput_GeneratesPb(t *testing.T) {
|
||||
func TestVerifyDepositSignature_ValidSig(t *testing.T) {
|
||||
deposits, _, err := testutil.DeterministicDepositsAndKeys(1)
|
||||
require.NoError(t, err)
|
||||
deposit := deposits[0]
|
||||
dep := deposits[0]
|
||||
domain, err := helpers.ComputeDomain(
|
||||
params.BeaconConfig().DomainDeposit,
|
||||
params.BeaconConfig().GenesisForkVersion,
|
||||
params.BeaconConfig().ZeroHash[:],
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = depositutil.VerifyDepositSignature(deposit.Data, domain)
|
||||
err = deposit.VerifyDepositSignature(dep.Data, domain)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestVerifyDepositSignature_InvalidSig(t *testing.T) {
|
||||
deposits, _, err := testutil.DeterministicDepositsAndKeys(1)
|
||||
require.NoError(t, err)
|
||||
deposit := deposits[0]
|
||||
dep := deposits[0]
|
||||
domain, err := helpers.ComputeDomain(
|
||||
params.BeaconConfig().DomainDeposit,
|
||||
params.BeaconConfig().GenesisForkVersion,
|
||||
params.BeaconConfig().ZeroHash[:],
|
||||
)
|
||||
require.NoError(t, err)
|
||||
deposit.Data.Signature = deposit.Data.Signature[1:]
|
||||
err = depositutil.VerifyDepositSignature(deposit.Data, domain)
|
||||
dep.Data.Signature = dep.Data.Signature[1:]
|
||||
err = deposit.VerifyDepositSignature(dep.Data, domain)
|
||||
if err == nil {
|
||||
t.Fatal("Deposit Verification succeeds with a invalid signature")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package depositcontract_test
|
||||
package deposit_test
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
"github.com/prysmaticlabs/prysm/container/trie"
|
||||
depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
depositcontract "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/runtime/interop"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
@ -1,4 +1,4 @@
|
||||
package depositcontract
|
||||
package deposit
|
||||
|
||||
import (
|
||||
"bytes"
|
93
contracts/deposit/mock.go
Normal file
93
contracts/deposit/mock.go
Normal file
File diff suppressed because one or more lines are too long
@ -1,32 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["deposit.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/depositutil",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//crypto/hash:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["deposit_test.go"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//crypto/bls:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//shared/testutil/assert:go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
],
|
||||
)
|
@ -19,7 +19,7 @@ go_library(
|
||||
"//cmd/validator/flags:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//shared/bytesutil:go_default_library",
|
||||
"//shared/testutil:go_default_library",
|
||||
"//testing/endtoend/helpers:go_default_library",
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/helpers"
|
||||
e2e "github.com/prysmaticlabs/prysm/testing/endtoend/params"
|
||||
e2etypes "github.com/prysmaticlabs/prysm/testing/endtoend/types"
|
||||
@ -133,7 +133,7 @@ func (node *Eth1Node) Start(ctx context.Context) error {
|
||||
}
|
||||
txOpts.Nonce = big.NewInt(int64(nonce))
|
||||
txOpts.Context = context.Background()
|
||||
contractAddr, tx, _, err := contracts.DeployDepositContract(txOpts, web3, txOpts.From)
|
||||
contractAddr, tx, _, err := contracts.DeployDepositContract(txOpts, web3)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deploy deposit contract: %w", err)
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/cmd/validator/flags"
|
||||
"github.com/prysmaticlabs/prysm/config/features"
|
||||
"github.com/prysmaticlabs/prysm/config/params"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil"
|
||||
"github.com/prysmaticlabs/prysm/testing/endtoend/helpers"
|
||||
|
@ -7,11 +7,10 @@ go_library(
|
||||
importpath = "github.com/prysmaticlabs/prysm/tools/deployContract",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//contracts/deposit:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/keystore:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
|
@ -11,11 +11,10 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
@ -148,18 +147,12 @@ func main() {
|
||||
txOps.Context = context.Background()
|
||||
}
|
||||
|
||||
drain := txOps.From
|
||||
if drainAddress != "" {
|
||||
drain = common.HexToAddress(drainAddress)
|
||||
}
|
||||
|
||||
txOps.GasPrice = big.NewInt(10 * 1e9 /* 10 gwei */)
|
||||
|
||||
// Deploy validator registration contract
|
||||
addr, tx, _, err := contracts.DeployDepositContract(
|
||||
txOps,
|
||||
client,
|
||||
drain,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -1,30 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["drainContracts.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/tools/drainContracts",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//contracts/deposit-contract:go_default_library",
|
||||
"//runtime/version:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//accounts/keystore:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//crypto:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
|
||||
"@com_github_pkg_errors//:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@com_github_urfave_cli_v2//:go_default_library",
|
||||
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "drainContracts",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
@ -1,39 +0,0 @@
|
||||
## Utility to Drain All Deposit Contracts
|
||||
|
||||
This is a utility to help users drain the contract addresses they have deployed in order to get their testnet ether back. To run the utility, it defaults to an infura link but you can use your own provider through the flags. The utility will print out each address it sends a transaction to.
|
||||
|
||||
### Usage
|
||||
|
||||
_Name:_
|
||||
**drainContracts** - this is a util to drain all deposit contracts
|
||||
|
||||
_Usage:_
|
||||
drainContracts [global options]
|
||||
|
||||
_Flags:_
|
||||
|
||||
- --keystoreUTCPath value keystore JSON for account
|
||||
- --httpPath value HTTP-RPC server listening interface (default: "http://localhost:8545/")
|
||||
- --passwordFile value Password file for unlock account (default: "./password.txt")
|
||||
- --privKey value Private key to unlock account
|
||||
- --help, -h show help
|
||||
- --version, -v print the version
|
||||
|
||||
### Example
|
||||
|
||||
To use private key with default RPC:
|
||||
|
||||
```
|
||||
bazel run //contracts/deposit-contract/drainContracts -- --httpPath=https://goerli.prylabs.net --privKey=$(echo /path/to/private/key/file)
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
current address is 0xdbA543721462680431eC4eeB26163079B3645660
|
||||
nonce is 7060
|
||||
0xd1faa3f9bca1d698df559716fe6d1c9999155b38d3158fffbc98d76d568091fc
|
||||
1190 chain start logs found
|
||||
1190 contracts ready to drain found
|
||||
Contract address 0x4cb8976E4Bf0b6A462AF8704F0f724775B67b4Ce drained in TX hash: 0x3f963c30c4fd4ff875c641be1e7b873bfe02ae2cd2d73554cc6087c2d3acaa9e
|
||||
```
|
@ -1,227 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/pkg/errors"
|
||||
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||||
"github.com/prysmaticlabs/prysm/runtime/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
prefixed "github.com/x-cray/logrus-prefixed-formatter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var keystoreUTCPath string
|
||||
var passwordFile string
|
||||
var httpPath string
|
||||
var privKeyString string
|
||||
|
||||
customFormatter := new(prefixed.TextFormatter)
|
||||
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
customFormatter.FullTimestamp = true
|
||||
logrus.SetFormatter(customFormatter)
|
||||
|
||||
app := cli.App{}
|
||||
app.Name = "drainContracts"
|
||||
app.Usage = "this is a util to drain all (testing) deposit contracts of their ETH."
|
||||
app.Version = version.Version()
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "keystoreUTCPath",
|
||||
Usage: "Location of keystore",
|
||||
Destination: &keystoreUTCPath,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "httpPath",
|
||||
Value: "https://goerli.infura.io/v3/be3fb7ed377c418087602876a40affa1",
|
||||
Usage: "HTTP-RPC server listening interface",
|
||||
Destination: &httpPath,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "passwordFile",
|
||||
Value: "./password.txt",
|
||||
Usage: "Password file for unlock account",
|
||||
Destination: &passwordFile,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "privKey",
|
||||
Usage: "Private key to send ETH transaction",
|
||||
Destination: &privKeyString,
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
// Set up RPC client
|
||||
var rpcClient *rpc.Client
|
||||
var err error
|
||||
var txOps *bind.TransactOpts
|
||||
|
||||
// Uses HTTP-RPC if IPC is not set
|
||||
rpcClient, err = rpc.Dial(httpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := ethclient.NewClient(rpcClient)
|
||||
|
||||
// User inputs private key, sign tx with private key
|
||||
if privKeyString != "" {
|
||||
privKey, err := crypto.HexToECDSA(privKeyString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txOps, err = bind.NewKeyedTransactorWithChainID(privKey, big.NewInt(1337))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txOps.Value = big.NewInt(0)
|
||||
txOps.GasLimit = 4000000
|
||||
txOps.Context = context.Background()
|
||||
nonce, err := client.NonceAt(context.Background(), crypto.PubkeyToAddress(privKey.PublicKey), nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get account nonce")
|
||||
}
|
||||
txOps.Nonce = big.NewInt(int64(nonce))
|
||||
fmt.Printf("current address is %s\n", crypto.PubkeyToAddress(privKey.PublicKey).String())
|
||||
fmt.Printf("nonce is %d\n", nonce)
|
||||
// User inputs keystore json file, sign tx with keystore json
|
||||
} else {
|
||||
password := loadTextFromFile(passwordFile)
|
||||
|
||||
// #nosec - Inclusion of file via variable is OK for this tool.
|
||||
keyJSON, err := ioutil.ReadFile(keystoreUTCPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
privKey, err := keystore.DecryptKey(keyJSON, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txOps, err = bind.NewKeyedTransactorWithChainID(privKey.PrivateKey, big.NewInt(1337))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txOps.Value = big.NewInt(0)
|
||||
txOps.GasLimit = 4000000
|
||||
txOps.Context = context.Background()
|
||||
nonce, err := client.NonceAt(context.Background(), privKey.Address, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
txOps.Nonce = big.NewInt(int64(nonce))
|
||||
fmt.Printf("current address is %s\n", privKey.Address.String())
|
||||
fmt.Printf("nonce is %d\n", nonce)
|
||||
}
|
||||
|
||||
addresses, err := allDepositContractAddresses(client)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Could not get all deposit contract address")
|
||||
}
|
||||
|
||||
fmt.Printf("%d contracts ready to drain found\n", len(addresses))
|
||||
|
||||
for _, address := range addresses {
|
||||
bal, err := client.BalanceAt(context.Background(), address, nil /*blockNum*/)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bal.Cmp(big.NewInt(0)) < 1 {
|
||||
continue
|
||||
}
|
||||
depositContract, err := contracts.NewDepositContract(address, client)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tx, err := depositContract.Drain(txOps)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to send transaction to contract: %v", err)
|
||||
}
|
||||
|
||||
txOps.Nonce = txOps.Nonce.Add(txOps.Nonce, big.NewInt(1))
|
||||
|
||||
fmt.Printf("Contract address %s drained in TX hash: %s\n", address.String(), tx.Hash().String())
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadTextFromFile(filepath string) string {
|
||||
// #nosec - Inclusion of file via variable is OK for this tool.
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Split(bufio.ScanWords)
|
||||
scanner.Scan()
|
||||
return scanner.Text()
|
||||
}
|
||||
|
||||
func allDepositContractAddresses(client *ethclient.Client) ([]common.Address, error) {
|
||||
log.Print("Looking up contracts")
|
||||
addresses := make(map[common.Address]bool)
|
||||
|
||||
// Hash of deposit log signature
|
||||
// DepositEvent: event({
|
||||
// pubkey: bytes[48],
|
||||
// withdrawal_credentials: bytes[32],
|
||||
// amount: bytes[8],
|
||||
// signature: bytes[96],
|
||||
// index: bytes[8],
|
||||
// })
|
||||
depositTopicHash := common.HexToHash("0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5")
|
||||
fmt.Println(depositTopicHash.Hex())
|
||||
|
||||
query := ethereum.FilterQuery{
|
||||
Addresses: []common.Address{},
|
||||
Topics: [][]common.Hash{
|
||||
{depositTopicHash},
|
||||
},
|
||||
FromBlock: big.NewInt(800000), // Contracts before this may not have drain().
|
||||
}
|
||||
|
||||
logs, err := client.FilterLogs(context.Background(), query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get all deposit logs")
|
||||
}
|
||||
|
||||
fmt.Printf("%d deposit logs found\n", len(logs))
|
||||
for i := len(logs)/2 - 1; i >= 0; i-- {
|
||||
opp := len(logs) - 1 - i
|
||||
logs[i], logs[opp] = logs[opp], logs[i]
|
||||
}
|
||||
|
||||
for _, ll := range logs {
|
||||
addresses[ll.Address] = true
|
||||
}
|
||||
|
||||
keys := make([]common.Address, 0, len(addresses))
|
||||
for key := range addresses {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user