Replace Solidity Contract With Vyper (#1343)

* adding deposit contract

* Adding bindings

* remove unused info

* shifting deploy function over

* new changes

* fixing tests and moving log utils to contract package

* new changes,helpers, fixing tests

* fix failing test

* readme

* contract change

* changes to contract

* new changes,helpers, fixing tests

* missing files

* adding constructor to contract

* lint

* updating with spec

* finally got it fixed

* add in deposit arguments

* new changes

* all tests pass

* addresing raul's and terence's comments

* remove vrc
This commit is contained in:
Nishant Das
2019-01-28 16:45:28 +08:00
committed by GitHub
parent a57659a43b
commit 1e862511aa
24 changed files with 906 additions and 1279 deletions

View File

@@ -7,8 +7,7 @@ go_library(
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/utils:go_default_library",
"//contracts/validator-registration-contract:go_default_library",
"//contracts/deposit-contract:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/trie:go_default_library",
"@com_github_ethereum_go_ethereum//:go_default_library",
@@ -25,10 +24,11 @@ go_test(
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/utils:go_default_library",
"//contracts/validator-registration-contract:go_default_library",
"//contracts/deposit-contract:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/event:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"//shared/ssz:go_default_library",
"//shared/testutil:go_default_library",
"//shared/trie:go_default_library",

View File

@@ -1,4 +1,4 @@
// Package powchain defines the services that interact with the PoWChain of Ethereum.
// Package powchain defines the services that interact with the ETH1.0 of Ethereum.
package powchain
import (
@@ -10,13 +10,12 @@ import (
"strings"
"time"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
contracts "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/trie"
"github.com/sirupsen/logrus"
@@ -34,7 +33,7 @@ type POWBlockFetcher interface {
BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error)
}
// Client defines a struct that combines all relevant PoW mainchain interactions required
// Client defines a struct that combines all relevant ETH1.0 mainchain interactions required
// by the beacon chain node.
type Client interface {
Reader
@@ -44,10 +43,10 @@ type Client interface {
}
// Web3Service fetches important information about the canonical
// Ethereum PoW chain via a web3 endpoint using an ethclient. The Random
// Beacon Chain requires synchronization with the PoW chain's current
// Ethereum ETH1.0 chain via a web3 endpoint using an ethclient. The Random
// Beacon Chain requires synchronization with the ETH1.0 chain's current
// blockhash, block number, and access to logs within the
// Validator Registration Contract on the PoW chain to kick off the beacon
// Validator Registration Contract on the ETH1.0 chain to kick off the beacon
// chain's validator registration process.
type Web3Service struct {
ctx context.Context
@@ -59,10 +58,9 @@ type Web3Service struct {
depositContractAddress common.Address
reader Reader
logger bind.ContractFilterer
blockNumber *big.Int // the latest PoW chain blockNumber.
blockHash common.Hash // the latest PoW chain blockHash.
vrcCaller *contracts.ValidatorRegistrationCaller
depositCount uint64
blockNumber *big.Int // the latest ETH1.0 chain blockNumber.
blockHash common.Hash // the latest ETH1.0 chain blockHash.
vrcCaller *contracts.DepositContractCaller
depositRoot []byte
depositTrie *trie.DepositTrie
}
@@ -78,8 +76,8 @@ type Web3ServiceConfig struct {
}
var (
depositEventSignature = []byte("Deposit(bytes,bytes,bytes)")
chainStartEventSignature = []byte("ChainStart(bytes,bytes)")
depositEventSignature = []byte("Deposit(bytes32,bytes,bytes)")
chainStartEventSignature = []byte("ChainStart(bytes32,bytes)")
)
// NewWeb3Service sets up a new instance with an ethclient when
@@ -92,7 +90,7 @@ func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Servic
)
}
vrcCaller, err := contracts.NewValidatorRegistrationCaller(config.DepositContract, config.ContractBackend)
vrcCaller, err := contracts.NewDepositContractCaller(config.DepositContract, config.ContractBackend)
if err != nil {
return nil, fmt.Errorf("could not create VRC caller %v", err)
}
@@ -139,25 +137,19 @@ func (w *Web3Service) Status() error {
// initDataFromVRC calls the vrc contract and finds the deposit count
// and deposit root.
func (w *Web3Service) initDataFromVRC() error {
depositCount, err := w.vrcCaller.DepositCount(&bind.CallOpts{})
if err != nil {
return fmt.Errorf("could not retrieve deposit count %v", err)
}
w.depositCount = depositCount.Uint64()
root, err := w.vrcCaller.GetDepositRoot(&bind.CallOpts{})
if err != nil {
return fmt.Errorf("could not retrieve deposit root %v", err)
}
w.depositRoot = root
w.depositRoot = root[:]
w.depositTrie = trie.NewDepositTrie()
return nil
}
// run subscribes to all the services for the powchain.
// run subscribes to all the services for the ETH1.0 chain.
func (w *Web3Service) run(done <-chan struct{}) {
if err := w.initDataFromVRC(); err != nil {
@@ -167,7 +159,7 @@ func (w *Web3Service) run(done <-chan struct{}) {
headSub, err := w.reader.SubscribeNewHead(w.ctx, w.headerChan)
if err != nil {
log.Errorf("Unable to subscribe to incoming PoW chain headers: %v", err)
log.Errorf("Unable to subscribe to incoming ETH1.0 chain headers: %v", err)
return
}
query := ethereum.FilterQuery{
@@ -190,7 +182,7 @@ func (w *Web3Service) run(done <-chan struct{}) {
for {
select {
case <-done:
log.Debug("Powchain service context closed, exiting goroutine")
log.Debug("ETH1.0 chain service context closed, exiting goroutine")
return
case <-headSub.Err():
log.Debug("Unsubscribed to head events, exiting goroutine")
@@ -213,7 +205,7 @@ func (w *Web3Service) run(done <-chan struct{}) {
}
// ProcessLog is the main method which handles the processing of all
// logs from the deposit contract on the POW Chain.
// logs from the deposit contract on the ETH1.0 chain.
func (w *Web3Service) ProcessLog(VRClog gethTypes.Log) {
// Process logs according to their event signature.
if VRClog.Topics[0] == hashutil.Hash(depositEventSignature) {
@@ -230,11 +222,11 @@ func (w *Web3Service) ProcessLog(VRClog gethTypes.Log) {
}
// ProcessDepositLog processes the log which had been received from
// the POW chain by trying to ascertain which participant deposited
// the ETH1.0 chain by trying to ascertain which participant deposited
// in the contract.
func (w *Web3Service) ProcessDepositLog(VRClog gethTypes.Log) {
merkleRoot := VRClog.Topics[1]
depositData, MerkleTreeIndex, err := utils.UnpackDepositLogData(VRClog.Data)
merkleRoot, depositData, MerkleTreeIndex, err := contracts.UnpackDepositLogData(VRClog.Data)
if err != nil {
log.Errorf("Could not unpack log %v", err)
return
@@ -256,16 +248,17 @@ func (w *Web3Service) ProcessDepositLog(VRClog gethTypes.Log) {
}
// ProcessChainStartLog processes the log which had been received from
// the POW chain by trying to determine when to start the beacon chain.
// the ETH1.0 chain by trying to determine when to start the beacon chain.
func (w *Web3Service) ProcessChainStartLog(VRClog gethTypes.Log) {
receiptRoot := VRClog.Topics[1]
timestampData, err := utils.UnpackChainStartLogData(VRClog.Data)
receiptRoot, timestampData, err := contracts.UnpackChainStartLogData(VRClog.Data)
if err != nil {
log.Errorf("Unable to unpack ChainStart log data %v", err)
return
}
if w.depositTrie.Root() != receiptRoot {
log.Errorf("Receipt root from log doesn't match the root saved in memory %#x", receiptRoot)
log.Errorf("Receipt root from log doesn't match the root saved in memory,"+
" want %#x but got %#x", w.depositTrie.Root(), receiptRoot)
return
}
@@ -304,17 +297,17 @@ func (w *Web3Service) processPastLogs(query ethereum.FilterQuery) error {
return nil
}
// LatestBlockNumber in the PoWChain.
// LatestBlockNumber in the ETH1.0 chain.
func (w *Web3Service) LatestBlockNumber() *big.Int {
return w.blockNumber
}
// LatestBlockHash in the PoWChain.
// LatestBlockHash in the ETH1.0 chain.
func (w *Web3Service) LatestBlockHash() common.Hash {
return w.blockHash
}
// Client for interacting with the PoWChain.
// Client for interacting with the ETH1.0 chain.
func (w *Web3Service) Client() Client {
return w.client
}

View File

@@ -20,10 +20,11 @@ import (
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
contracts "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/event"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/ssz"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/trie"
@@ -72,10 +73,11 @@ func (g *goodLogger) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]
}
var amount32Eth, _ = new(big.Int).SetString("32000000000000000000", 10)
var depositsReqForChainStart = 8
type testAccount struct {
addr common.Address
contract *contracts.ValidatorRegistration
contract *contracts.DepositContract
contractAddr common.Address
backend *backends.SimulatedBackend
txOpts *bind.TransactOpts
@@ -98,9 +100,12 @@ func setup() (*testAccount, error) {
txOpts := bind.NewKeyedTransactor(privKey)
startingBalance, _ := new(big.Int).SetString("1000000000000000000000", 10)
genesis[addr] = core.GenesisAccount{Balance: startingBalance}
backend := backends.NewSimulatedBackend(genesis, 2100000)
backend := backends.NewSimulatedBackend(genesis, 2100000000)
contractAddr, _, contract, err := contracts.DeployValidatorRegistration(txOpts, backend)
depositsRequired := big.NewInt(int64(depositsReqForChainStart))
minDeposit := big.NewInt(1e9)
maxDeposit := big.NewInt(32e9)
contractAddr, _, contract, err := contracts.DeployDepositContract(txOpts, backend, depositsRequired, minDeposit, maxDeposit)
if err != nil {
return nil, err
}
@@ -165,14 +170,14 @@ func TestStart(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
web3Service.Start()
msg := hook.LastEntry().Message
want := "Could not connect to PoW chain RPC client"
want := "Could not connect to ETH1.0 chain RPC client"
if strings.Contains(want, msg) {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
@@ -196,13 +201,13 @@ func TestStop(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
if err := web3Service.Stop(); err != nil {
t.Fatalf("Unable to stop web3 PoW chain service: %v", err)
t.Fatalf("Unable to stop web3 ETH1.0 chain service: %v", err)
}
msg := hook.LastEntry().Message
@@ -232,7 +237,7 @@ func TestInitDataFromVRC(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -240,12 +245,8 @@ func TestInitDataFromVRC(t *testing.T) {
if err := web3Service.initDataFromVRC(); err != nil {
t.Fatalf("Could not init from vrc %v", err)
}
if web3Service.depositCount != 0 {
t.Errorf("Deposit count is not equal to zero %d", web3Service.depositCount)
}
if !bytes.Equal(web3Service.depositRoot, []byte{}) {
empty32Byte := [32]byte{}
if !bytes.Equal(web3Service.depositRoot, empty32Byte[:]) {
t.Errorf("Deposit root is not empty %v", web3Service.depositRoot)
}
@@ -259,10 +260,6 @@ func TestInitDataFromVRC(t *testing.T) {
t.Fatalf("Could not init from vrc %v", err)
}
if web3Service.depositCount != 1 {
t.Errorf("Deposit count is not equal to one %d", web3Service.depositCount)
}
if bytes.Equal(web3Service.depositRoot, []byte{}) {
t.Errorf("Deposit root is empty %v", web3Service.depositRoot)
}
@@ -283,7 +280,7 @@ func TestSaveInTrie(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -313,7 +310,7 @@ func TestBadReader(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -321,7 +318,7 @@ func TestBadReader(t *testing.T) {
web3Service.logger = &goodLogger{}
web3Service.run(web3Service.ctx.Done())
msg := hook.LastEntry().Message
want := "Unable to subscribe to incoming PoW chain headers: subscription has failed"
want := "Unable to subscribe to incoming ETH1.0 chain headers: subscription has failed"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
@@ -342,7 +339,7 @@ func TestLatestMainchainInfo(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
web3Service.reader = &goodReader{}
@@ -385,7 +382,7 @@ func TestBadLogger(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -416,15 +413,13 @@ func TestProcessDepositLog(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
web3Service.depositTrie = trie.NewDepositTrie()
currentRoot := web3Service.depositTrie.Root()
var stub [48]byte
copy(stub[:], []byte("testing"))
@@ -459,8 +454,6 @@ func TestProcessDepositLog(t *testing.T) {
t.Fatalf("Unable to retrieve logs %v", err)
}
logs[0].Topics[1] = currentRoot
web3Service.ProcessLog(logs[0])
testutil.AssertLogsDoNotContain(t, hook, "Could not unpack log")
@@ -485,7 +478,7 @@ func TestUnpackDepositLogs(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -494,11 +487,9 @@ func TestUnpackDepositLogs(t *testing.T) {
t.Fatalf("Could not init from vrc %v", err)
}
if web3Service.depositCount != 0 {
t.Errorf("Deposit count is not equal to zero %d", web3Service.depositCount)
}
empty32byte := [32]byte{}
if !bytes.Equal(web3Service.depositRoot, []byte{}) {
if !bytes.Equal(web3Service.depositRoot, empty32byte[:]) {
t.Errorf("Deposit root is not empty %v", web3Service.depositRoot)
}
@@ -535,16 +526,18 @@ func TestUnpackDepositLogs(t *testing.T) {
t.Fatalf("Unable to retrieve logs %v", err)
}
depData, index, err := utils.UnpackDepositLogData(logz[0].Data)
_, depositData, index, err := contracts.UnpackDepositLogData(logz[0].Data)
if err != nil {
t.Fatalf("Unable to unpack logs %v", err)
}
if binary.BigEndian.Uint64(index) != 65536 {
twoTothePowerOfTreeDepth := mathutil.PowerOf2(params.BeaconConfig().DepositContractTreeDepth)
if binary.BigEndian.Uint64(index) != twoTothePowerOfTreeDepth {
t.Errorf("Retrieved merkle tree index is incorrect %d", index)
}
deserializeData, err := blocks.DecodeDepositInput(depData)
deserializeData, err := blocks.DecodeDepositInput(depositData)
if err != nil {
t.Fatalf("Unable to decode deposit input %v", err)
}
@@ -586,7 +579,7 @@ func TestProcessChainStartLog(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -594,8 +587,6 @@ func TestProcessChainStartLog(t *testing.T) {
web3Service.depositTrie = trie.NewDepositTrie()
currentRoot := web3Service.depositTrie.Root()
var stub [48]byte
copy(stub[:], []byte("testing"))
@@ -612,10 +603,12 @@ func TestProcessChainStartLog(t *testing.T) {
t.Fatalf("Could not serialize data %v", err)
}
blocks.EncodeDepositData(data, amount32Eth.Uint64(), time.Now().Unix())
// 8 Validators are used as size required for beacon-chain to start. This number
// is defined in the VRC as the number required for the testnet. The actual number
// is 2**14
for i := 0; i < 8; i++ {
for i := 0; i < depositsReqForChainStart; i++ {
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
@@ -635,7 +628,14 @@ func TestProcessChainStartLog(t *testing.T) {
t.Fatalf("Unable to retrieve logs %v", err)
}
logs[len(logs)-1].Topics[1] = currentRoot
for i := 0; i < depositsReqForChainStart; i++ {
_, depData, _, err := contracts.UnpackDepositLogData(logs[i].Data)
if err != nil {
t.Fatalf("Unable to unpack deposit logs %v", err)
}
web3Service.depositTrie.UpdateDepositTrie(depData)
}
web3Service.ProcessLog(logs[len(logs)-1])
@@ -662,7 +662,7 @@ func TestUnpackChainStartLogs(t *testing.T) {
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
t.Fatalf("unable to setup web3 ETH1.0 chain service: %v", err)
}
testAcc.backend.Commit()
@@ -687,7 +687,7 @@ func TestUnpackChainStartLogs(t *testing.T) {
// 8 Validators are used as size required for beacon-chain to start. This number
// is defined in the VRC as the number required for the testnet.
for i := 0; i < 8; i++ {
for i := 0; i < depositsReqForChainStart; i++ {
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
@@ -707,7 +707,7 @@ func TestUnpackChainStartLogs(t *testing.T) {
t.Fatalf("Unable to retrieve logs %v", err)
}
timestampData, err := utils.UnpackChainStartLogData(logs[len(logs)-1].Data)
_, timestampData, err := contracts.UnpackChainStartLogData(logs[len(logs)-1].Data)
if err != nil {
t.Fatalf("Unable to unpack logs %v", err)
}

View File

@@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"POWlogs.go",
"block_vote_cache.go",
"clock.go",
"flags.go",
@@ -12,10 +11,8 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/utils",
visibility = ["//beacon-chain:__subpackages__"],
deps = [
"//contracts/validator-registration-contract:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"@com_github_ethereum_go_ethereum//accounts/abi:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_urfave_cli//:go_default_library",
],

View File

@@ -1,46 +0,0 @@
package utils
import (
"bytes"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
contracts "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract"
)
// UnpackDepositLogData unpacks the data from a deposit log using the ABI decoder.
func UnpackDepositLogData(data []byte) (depositData []byte, merkleTreeIndex []byte, err error) {
reader := bytes.NewReader([]byte(contracts.ValidatorRegistrationABI))
contractAbi, err := abi.JSON(reader)
if err != nil {
return nil, nil, fmt.Errorf("unable to generate contract abi: %v", err)
}
unpackedLogs := []*[]byte{
{},
{},
}
if err := contractAbi.Unpack(&unpackedLogs, "Deposit", data); err != nil {
return nil, nil, fmt.Errorf("unable to unpack logs: %v", err)
}
depositData = *unpackedLogs[0]
merkleTreeIndex = *unpackedLogs[1]
return depositData, merkleTreeIndex, nil
}
// UnpackChainStartLogData unpacks the data from a chain start log using the ABI decoder.
func UnpackChainStartLogData(data []byte) ([]byte, error) {
reader := bytes.NewReader([]byte(contracts.ValidatorRegistrationABI))
contractAbi, err := abi.JSON(reader)
if err != nil {
return nil, fmt.Errorf("unable to generate contract abi: %v", err)
}
unpackedLogs := []*[]byte{
{},
}
if err := contractAbi.Unpack(&unpackedLogs, "ChainStart", data); err != nil {
return nil, fmt.Errorf("unable to unpack logs: %v", err)
}
timestamp := *unpackedLogs[0]
return timestamp, nil
}

View File

@@ -2,8 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["validator_registration.go"],
importpath = "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract",
srcs = [
"ETH1logs.go",
"depositContract.go",
],
importpath = "github.com/prysmaticlabs/prysm/contracts/deposit-contract",
visibility = ["//visibility:public"],
deps = [
"@com_github_ethereum_go_ethereum//:go_default_library",
@@ -17,9 +20,13 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["validator_registration_test.go"],
srcs = ["depositContract_test.go"],
embed = [":go_default_library"],
deps = [
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params: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/abi/bind/backends:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",

View File

@@ -0,0 +1,46 @@
package depositcontract
import (
"bytes"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
)
// UnpackDepositLogData unpacks the data from a deposit log using the ABI decoder.
func UnpackDepositLogData(data []byte) (depositRoot [32]byte, depositData []byte, merkleTreeIndex []byte, err error) {
reader := bytes.NewReader([]byte(DepositContractABI))
contractAbi, err := abi.JSON(reader)
if err != nil {
return [32]byte{}, nil, nil, fmt.Errorf("unable to generate contract abi: %v", err)
}
unpackedLogs := []interface{}{
&depositRoot,
&depositData,
&merkleTreeIndex,
}
if err := contractAbi.Unpack(&unpackedLogs, "Deposit", data); err != nil {
return [32]byte{}, nil, nil, fmt.Errorf("unable to unpack logs: %v", err)
}
return depositRoot, depositData, merkleTreeIndex, nil
}
// UnpackChainStartLogData unpacks the data from a chain start log using the ABI decoder.
func UnpackChainStartLogData(data []byte) (depositRoot [32]byte, timestamp []byte, err error) {
reader := bytes.NewReader([]byte(DepositContractABI))
contractAbi, err := abi.JSON(reader)
if err != nil {
return [32]byte{}, nil, fmt.Errorf("unable to generate contract abi: %v", err)
}
unpackedLogs := []interface{}{
&depositRoot,
&timestamp,
}
if err := contractAbi.Unpack(&unpackedLogs, "ChainStart", data); err != nil {
return [32]byte{}, nil, fmt.Errorf("unable to unpack logs: %v", err)
}
return depositRoot, timestamp, nil
}

View File

@@ -1,7 +1,7 @@
## Validator Registration Contract
## Deposit Contract
A validator will deposit 32 ETH to the registration
contract. The contract will generate a receipt showing the validator as a
A validator will deposit 32 ETH to the deposit
contract. The contract will generate a log showing the validator as a
qualified validator.
The deposit is considered to be burned. As you burn the 32 ETH to participate,
the beacon chain will see it and will credit the validator with the validator bond,
@@ -10,15 +10,47 @@ the original deposit + interest can be withdrawn back on one of the shards.
To call the `registration` function, it takes arguments of `pubkey`,
`proof_of_possession`, `withdrawal_credentials` and `randao_commitment`.
If the user wants to deposit more than `DEPOSIT_SIZE` ETH, they would
need to make multiple `registration` calls.
need to make multiple `deposit` calls.
When the contract publishes the `ChainStart` log, beacon nodes will
start off the beacon chain with slot 0 and last recorded `block.timestamp`
as beacon chain genesis time.
The registration contract generate receipts with the various arguments
The registration contract generate logs with the various arguments
for consumption by beacon nodes. It does not validate `proof_of_possession`
and `withdrawal_credentials`, pushing the validation logic to the
beacon chain.
## How to generate bindings for vyper contract
This requires that you have vyper and abigen installed in your local machine.
Vyper: https://github.com/ethereum/vyper
Abigen: https://github.com/ethereum/go-ethereum/tree/master/cmd/abigen
To generate the abi using the vyper compiler, you can use
```
vyper -f abi ./depositContract.v.py
```
Then the abi will be outputted and you can save it in `abi.json` in the folder.
To generate the bytecode you can then use
```
vyper ./depositContract.v.py
```
and save the bytecode in `bytecode.bin` in the folder. Now with both the abi and bytecode
we can generate the go bindings.
```
abigen -bin ./bytecode.bin -abi ./abi.json -out ./depositContract.go --pkg depositContract
```
## How to execute tests
```
@@ -41,5 +73,5 @@ go test ./... -v
=== RUN TestRegister
--- PASS: TestRegister (0.01s)
PASS
ok beacon-chain/contracts 0.151s
ok beacon-chain/contracts/deposit-contract/ 0.151s
```

View File

@@ -0,0 +1 @@
[{"name": "Deposit", "inputs": [{"type": "bytes32", "name": "previous_deposit_root", "indexed": false}, {"type": "bytes", "name": "data", "indexed": false}, {"type": "bytes", "name": "merkle_tree_index", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "ChainStart", "inputs": [{"type": "bytes32", "name": "deposit_root", "indexed": false}, {"type": "bytes", "name": "time", "indexed": false}], "anonymous": false, "type": "event"}, {"name": "__init__", "outputs": [], "inputs": [{"type": "uint256", "name": "depositThreshold"}, {"type": "uint256", "name": "minDeposit"}, {"type": "uint256", "name": "maxDeposit"}], "constant": false, "payable": false, "type": "constructor"}, {"name": "get_deposit_root", "outputs": [{"type": "bytes32", "name": "out"}], "inputs": [], "constant": true, "payable": false, "type": "function", "gas": 625}, {"name": "deposit", "outputs": [], "inputs": [{"type": "bytes", "name": "deposit_input"}], "constant": false, "payable": true, "type": "function", "gas": 1711311}, {"name": "get_branch", "outputs": [{"type": "bytes32[32]", "name": "out"}], "inputs": [{"type": "uint256", "name": "leaf"}], "constant": true, "payable": false, "type": "function", "gas": 20138}]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = ["deployContract.go"],
importpath = "github.com/prysmaticlabs/prysm/contracts/deposit-contract/deployContract",
visibility = ["//visibility:private"],
deps = [
"//contracts/deposit-contract:go_default_library",
"//shared/params:go_default_library",
"//shared/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//crypto:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli//:go_default_library",
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
"@io_k8s_client_go//rest:go_default_library",
],
)
go_binary(
name = "deployContract",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)

View File

@@ -1,11 +1,11 @@
## Utility to Deploy Validator Registration Contract
## Utility to Deploy Deposit Contract
This is a utility to help users deploy validator registration contract for running their own beacon chain node in a local containerized set up. To run the utility, it assumes there is a running geth node as a separate process attached to proof-of-work main chain. The utility will deploy the validator registration contract and print out the contract address. Users will pass the contract address to the beacon chain node to monitor when they have been conducted to become an active validator.
This is a utility to help users deploy deposit contract for running their own beacon chain node in a local containerized set up. To run the utility, it assumes there is a running geth node as a separate process attached to proof-of-work main chain. The utility will deploy the validator registration contract and print out the contract address. Users will pass the contract address to the beacon chain node to monitor when they have been conducted to become an active validator.
### Usage
*Name:*
**deployVRC** - this is a util to deploy validator registration contract
**deployVRC** - this is a util to deploy deposit contract
*Usage:*
deployVRC [global options] command [command options] [arguments...]

View File

@@ -14,7 +14,8 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
contracts "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -40,7 +41,7 @@ func main() {
app := cli.NewApp()
app.Name = "deployVRC"
app.Usage = "this is a util to deploy validator registration contract"
app.Usage = "this is a util to deploy deposit contract"
app.Version = version.GetVersion()
app.Flags = []cli.Flag{
cli.StringFlag{
@@ -72,7 +73,7 @@ func main() {
},
cli.StringFlag{
Name: "k8s-config",
Usage: "Name of kubernetes config map to update with the VRC address",
Usage: "Name of kubernetes config map to update with the contract address",
Destination: &k8sConfigMapName,
},
}
@@ -132,7 +133,9 @@ func main() {
}
// Deploy validator registration contract
addr, tx, _, err := contracts.DeployValidatorRegistration(txOps, client)
addr, tx, _, err := contracts.DeployDepositContract(
txOps, client, params.ContractConfig().DepositsForChainStart,
params.ContractConfig().MinDepositAmount, params.ContractConfig().MaxDepositAmount)
if err != nil {
log.Fatal(err)
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,69 @@
## compiled with v0.1.0-beta.7 ##
DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32
TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32
SECONDS_PER_DAY: constant(uint256) = 86400
Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], merkle_tree_index: bytes[8]})
ChainStart: event({deposit_root: bytes32, time: bytes[8]})
MIN_DEPOSIT_AMOUNT: uint256
MAX_DEPOSIT_AMOUNT: uint256
CHAIN_START_FULL_DEPOSIT_THRESHOLD: uint256
deposit_tree: map(uint256, bytes32)
deposit_count: uint256
full_deposit_count: uint256
@public
def __init__(depositThreshold: uint256,minDeposit: uint256,maxDeposit: uint256):
self.CHAIN_START_FULL_DEPOSIT_THRESHOLD = depositThreshold
self.MIN_DEPOSIT_AMOUNT = minDeposit
self.MAX_DEPOSIT_AMOUNT = maxDeposit
@private
@constant
def to_bytes(value: uint256) -> bytes[8]:
return slice(concat("", convert(value, bytes32)), start=24, len=8)
@public
@constant
def get_deposit_root() -> bytes32:
return self.deposit_tree[1]
@payable
@public
def deposit(deposit_input: bytes[2048]):
deposit_amount: uint256 = msg.value / as_wei_value(1, "gwei")
assert deposit_amount >= self.MIN_DEPOSIT_AMOUNT
assert deposit_amount <= self.MAX_DEPOSIT_AMOUNT
deposit_timestamp: uint256 = as_unitless_number(block.timestamp)
deposit_data: bytes[2064] = concat(self.to_bytes(deposit_amount), self.to_bytes(deposit_timestamp), deposit_input)
index: uint256 = self.deposit_count + TWO_TO_POWER_OF_TREE_DEPTH
log.Deposit(self.get_deposit_root(), deposit_data, self.to_bytes(index))
# Add deposit to merkle tree
self.deposit_tree[index] = sha3(deposit_data)
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH):
index /= 2
self.deposit_tree[index] = sha3(concat(self.deposit_tree[index * 2], self.deposit_tree[index * 2 + 1]))
self.deposit_count += 1
if deposit_amount == self.MAX_DEPOSIT_AMOUNT:
self.full_deposit_count += 1
if self.full_deposit_count == self.CHAIN_START_FULL_DEPOSIT_THRESHOLD:
timestamp_day_boundary: uint256 = deposit_timestamp - deposit_timestamp % SECONDS_PER_DAY + SECONDS_PER_DAY
log.ChainStart(self.get_deposit_root(), self.to_bytes(timestamp_day_boundary))
@public
@constant
def get_branch(leaf: uint256) -> bytes32[DEPOSIT_CONTRACT_TREE_DEPTH]:
branch: bytes32[32] # size is DEPOSIT_CONTRACT_TREE_DEPTH
index: uint256 = leaf + TWO_TO_POWER_OF_TREE_DEPTH
for i in range(DEPOSIT_CONTRACT_TREE_DEPTH):
branch[i] = self.deposit_tree[bitwise_xor(index, 1)]
index /= 2
return branch

View File

@@ -1,7 +1,7 @@
package vrc
package depositcontract
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/binary"
"fmt"
@@ -9,11 +9,15 @@ import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum"
"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"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
)
var (
@@ -23,10 +27,11 @@ var (
)
type testAccount struct {
addr common.Address
contract *ValidatorRegistration
backend *backends.SimulatedBackend
txOpts *bind.TransactOpts
addr common.Address
contract *DepositContract
contractAddr common.Address
backend *backends.SimulatedBackend
txOpts *bind.TransactOpts
}
func setup() (*testAccount, error) {
@@ -44,16 +49,19 @@ func setup() (*testAccount, error) {
addr := crypto.PubkeyToAddress(privKey.PublicKey)
txOpts := bind.NewKeyedTransactor(privKey)
startingBalance, _ := new(big.Int).SetString("1000000000000000000000", 10)
startingBalance, _ := new(big.Int).SetString("100000000000000000000000000000000000000", 10)
genesis[addr] = core.GenesisAccount{Balance: startingBalance}
backend := backends.NewSimulatedBackend(genesis, 2100000)
backend := backends.NewSimulatedBackend(genesis, 210000000000)
_, _, contract, err := DeployValidatorRegistration(txOpts, backend)
depositsRequired := big.NewInt(8)
minDeposit := big.NewInt(1e9)
maxDeposit := big.NewInt(32e9)
contractAddr, _, contract, err := DeployDepositContract(txOpts, backend, depositsRequired, minDeposit, maxDeposit)
if err != nil {
return nil, err
}
return &testAccount{addr, contract, backend, txOpts}, nil
return &testAccount{addr, contract, contractAddr, backend, txOpts}, nil
}
func TestSetupAndContractRegistration(t *testing.T) {
@@ -114,49 +122,42 @@ func TestValidatorRegisters(t *testing.T) {
if err != nil {
t.Errorf("Validator registration failed: %v", err)
}
log, err := testAccount.contract.FilterDeposit(&bind.FilterOpts{}, [][]byte{})
defer func() {
err = log.Close()
if err != nil {
t.Fatal(err)
}
}()
query := ethereum.FilterQuery{
Addresses: []common.Address{
testAccount.contractAddr,
},
}
logs, err := testAccount.backend.FilterLogs(context.Background(), query)
if err != nil {
t.Fatal(err)
}
if log.Error() != nil {
t.Fatal(log.Error())
}
log.Next()
index := make([]byte, 8)
binary.BigEndian.PutUint64(index, 65536)
if !bytes.Equal(log.Event.MerkleTreeIndex, index) {
t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", index, log.Event.MerkleTreeIndex)
}
if !bytes.Equal(log.Event.Data[len(log.Event.Data)-1:], []byte{'A'}) {
t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'A'}, log.Event.Data[len(log.Event.Data)-1:])
t.Fatalf("Unable to get logs of deposit contract: %v", err)
}
log.Next()
binary.BigEndian.PutUint64(index, 65537)
if !bytes.Equal(log.Event.MerkleTreeIndex, index) {
t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", index, log.Event.MerkleTreeIndex)
}
if !bytes.Equal(log.Event.Data[len(log.Event.Data)-1:], []byte{'B'}) {
t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'B'}, log.Event.Data[len(log.Event.Data)-1:])
merkleTreeIndex := make([]uint64, 5)
depositData := make([][]byte, 5)
for i, log := range logs {
_, data, idx, err := UnpackDepositLogData(log.Data)
if err != nil {
t.Fatalf("Unable to unpack log data: %v", err)
}
merkleTreeIndex[i] = binary.BigEndian.Uint64(idx)
depositData[i] = data
}
log.Next()
binary.BigEndian.PutUint64(index, 65538)
if !bytes.Equal(log.Event.MerkleTreeIndex, index) {
t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", index, log.Event.MerkleTreeIndex)
twoTothePowerOfTreeDepth := mathutil.PowerOf2(params.BeaconConfig().DepositContractTreeDepth)
if merkleTreeIndex[0] != twoTothePowerOfTreeDepth {
t.Errorf("Deposit event total desposit count miss matched. Want: %d, Got: %d", twoTothePowerOfTreeDepth+1, merkleTreeIndex[0])
}
if !bytes.Equal(log.Event.Data[len(log.Event.Data)-1:], []byte{'C'}) {
t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'B'}, log.Event.Data[len(log.Event.Data)-1:])
if merkleTreeIndex[1] != twoTothePowerOfTreeDepth+1 {
t.Errorf("Deposit event total desposit count miss matched. Want: %d, Got: %d", twoTothePowerOfTreeDepth+2, merkleTreeIndex[1])
}
if merkleTreeIndex[2] != twoTothePowerOfTreeDepth+2 {
t.Errorf("Deposit event total desposit count miss matched. Want: %v, Got: %v", twoTothePowerOfTreeDepth+3, merkleTreeIndex[2])
}
}
@@ -168,32 +169,27 @@ func TestChainStarts(t *testing.T) {
}
testAccount.txOpts.Value = amount32Eth
for i := 0; i < 9; i++ {
for i := 0; i < 8; i++ {
_, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{'A'})
testAccount.backend.Commit()
if err != nil {
t.Errorf("Validator registration failed: %v", err)
}
}
log, err := testAccount.contract.FilterChainStart(&bind.FilterOpts{}, [][]byte{})
testAccount.backend.Commit()
defer func() {
err = log.Close()
if err != nil {
t.Fatal(err)
}
}()
query := ethereum.FilterQuery{
Addresses: []common.Address{
testAccount.contractAddr,
},
}
logs, err := testAccount.backend.FilterLogs(context.Background(), query)
if err != nil {
t.Fatal(err)
t.Fatalf("Unable to get logs %v", err)
}
if log.Error() != nil {
t.Fatal(log.Error())
}
log.Next()
if len(log.Event.Time) == 0 {
t.Error("Chain start even did not get emitted, The start time is empty")
if logs[8].Topics[0] != hashutil.Hash([]byte("ChainStart(bytes32,bytes)")) {
t.Error("Chain start even did not get emitted")
}
}

View File

@@ -1 +0,0 @@
node_modules

View File

@@ -1,20 +0,0 @@
pragma solidity ^0.5.1;
library SafeMath {
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "The second parameter can not be greater than first one.");
uint256 c = a - b;
return c;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "The first parametert can not be greater than the sum of two parameters");
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "The second parameter can not be zero");
return a % b;
}
}

View File

@@ -1,69 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@io_bazel_rules_docker//go:image.bzl", "go_image")
load("@io_bazel_rules_docker//container:container.bzl", "container_push")
go_library(
name = "go_default_library",
srcs = ["deployVRC.go"],
importpath = "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract/deployVRC",
visibility = ["//visibility:private"],
deps = [
"//contracts/validator-registration-contract:go_default_library",
"//shared/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//crypto:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli//:go_default_library",
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
"@io_k8s_client_go//rest:go_default_library",
],
)
go_image(
name = "image",
srcs = ["deployVRC.go"],
importpath = "github.com/prysmaticlabs/prysm/contracts/validator-registration-contract/deployVRC",
visibility = ["//visibility:private"],
static = "on",
tags = ["manual"],
goarch = "amd64",
goos = "linux",
deps = [
"//contracts/validator-registration-contract:go_default_library",
"//shared/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//crypto:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli//:go_default_library",
"@com_github_x_cray_logrus_prefixed_formatter//:go_default_library",
"@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
"@io_k8s_client_go//kubernetes:go_default_library",
"@io_k8s_client_go//rest:go_default_library",
],
race = "off",
)
container_push(
name = "push_image",
format = "Docker",
image = ":image",
registry = "gcr.io",
repository = "prysmaticlabs/prysm/deployvrc",
tag = "latest",
tags = ["manual"],
visibility = ["//visibility:private"],
)
go_binary(
name = "deployVRC",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)

File diff suppressed because one or more lines are too long

View File

@@ -1,91 +0,0 @@
pragma solidity ^0.5.1;
import "./SafeMath.sol";
contract ValidatorRegistration {
event Deposit(
bytes indexed previousReceiptRoot,
bytes data,
bytes merkleTreeIndex
);
event ChainStart(
bytes indexed receiptRoot,
bytes time
);
uint public constant DEPOSIT_SIZE = 32 ether;
// 8 is for our local test net. 2.0 spec is 2**14 == 16384
uint public constant DEPOSITS_FOR_CHAIN_START = 8; // 2**14
uint public constant MIN_TOPUP_SIZE = 1 ether;
uint public constant GWEI_PER_ETH = 10 ** 9;
// Setting MERKLE_TREE_DEPTH to 16 instead of 32 due to gas limit
uint public constant MERKLE_TREE_DEPTH = 16;
uint public constant SECONDS_PER_DAY = 86400;
mapping (uint => bytes) public depositTree;
uint public depositCount;
uint public fullDepositCount;
using SafeMath for uint256;
// When users wish to become a validator by moving ETH from
// 1.0 chian to the 2.0 chain, they should call this function
// sending along DEPOSIT_SIZE ETH and providing depositParams
// as a simple serialize'd DepositParams object of the following
// form:
// {
// 'pubkey': 'uint384',
// 'proof_of_possession': ['uint384'],
// 'withdrawal_credentials`: 'hash32',
// 'randao_commitment`: 'hash32'
// }
function deposit(
bytes memory depositParams
)
public
payable
{
require(
msg.value <= DEPOSIT_SIZE,
"Deposit can't be greater than DEPOSIT_SIZE."
);
require(
msg.value >= MIN_TOPUP_SIZE,
"Deposit can't be lesser than MIN_TOPUP_SIZE."
);
uint index = depositCount + 2 ** MERKLE_TREE_DEPTH;
bytes memory msgGweiInBytes8 = abi.encodePacked(uint64(msg.value/GWEI_PER_ETH));
bytes memory timeStampInBytes8 = abi.encodePacked(uint64(block.timestamp));
bytes memory depositData = abi.encodePacked(msgGweiInBytes8, timeStampInBytes8, depositParams);
bytes memory merkleTreeIndex = abi.encodePacked(uint64(index));
emit Deposit(depositTree[1], depositData, merkleTreeIndex);
depositTree[index] = abi.encodePacked(keccak256(depositData));
for (uint i = 0; i < MERKLE_TREE_DEPTH; i++) {
index = index / 2;
depositTree[index] = abi.encodePacked(keccak256(abi.encodePacked(depositTree[index * 2], depositTree[index * 2 + 1])));
}
depositCount++;
if (msg.value == DEPOSIT_SIZE) {
fullDepositCount++;
// When ChainStart log publishes, beacon chain node initializes the chain and use timestampDayBoundry
// as genesis time.
if (fullDepositCount == DEPOSITS_FOR_CHAIN_START) {
uint timestampDayBoundry = block.timestamp.sub(block.timestamp).mod(SECONDS_PER_DAY).add(SECONDS_PER_DAY);
bytes memory timestampDayBoundryBytes = abi.encodePacked(uint64(timestampDayBoundry));
emit ChainStart(depositTree[1], timestampDayBoundryBytes);
}
}
}
function getDepositRoot() public view returns (bytes memory) {
return depositTree[1];
}
}

View File

@@ -29,3 +29,13 @@ func CeilDiv8(n int) int {
func IsPowerOf2(n uint64) bool {
return (n & (n - 1)) == 0
}
// PowerOf2 returns an integer that is the provided
// exponent of 2. Can only return powers of 2 till 63,
// after that it overflows
func PowerOf2(n uint64) uint64 {
if n >= 64 {
panic("integer overflow")
}
return 1 << n
}

View File

@@ -102,3 +102,32 @@ func TestIsPowerOf2(t *testing.T) {
}
}
}
func TestPowerOf2(t *testing.T) {
tests := []struct {
a uint64
b uint64
}{
{
a: 3,
b: 8,
},
{
a: 20,
b: 1048576,
},
{
a: 11,
b: 2048,
},
{
a: 8,
b: 256,
},
}
for _, tt := range tests {
if tt.b != PowerOf2(tt.a) {
t.Fatalf("PowerOf2(%d) = %d, wanted: %d", tt.a, PowerOf2(tt.a), tt.b)
}
}
}

View File

@@ -3,6 +3,7 @@
package params
import (
"math/big"
"time"
)
@@ -75,6 +76,13 @@ type BeaconChainConfig struct {
MaxNumLog2Validators uint64 // MaxNumLog2Validators is the Max number of validators in Log2 exists given total ETH supply.
}
// DepositContractConfig contains the deposits for
type DepositContractConfig struct {
DepositsForChainStart *big.Int // DepositsForChainStart defines how many validator deposits needed to kick off beacon chain.
MinDepositAmount *big.Int // MinDepositAmount defines the minimum deposit amount in gwei that is required in the deposit contract.
MaxDepositAmount *big.Int // // MaxDepositAmount defines the minimum deposit amount in gwei that is required in the deposit contract.
}
// ShardChainConfig contains configs for node to participate in shard chains.
type ShardChainConfig struct {
ChunkSize uint64 // ChunkSize defines the size of each chunk in bytes.
@@ -199,8 +207,15 @@ var defaultShardConfig = &ShardChainConfig{
MaxShardBlockSize: uint64(32768),
}
var defaultDepositContractConfig = &DepositContractConfig{
DepositsForChainStart: big.NewInt(16384),
MinDepositAmount: big.NewInt(1e9),
MaxDepositAmount: big.NewInt(32e9),
}
var beaconConfig = defaultBeaconConfig
var shardConfig = defaultShardConfig
var contractConfig = defaultDepositContractConfig
// BeaconConfig retrieves beacon chain config.
func BeaconConfig() *BeaconChainConfig {
@@ -212,6 +227,20 @@ func ShardConfig() *ShardChainConfig {
return shardConfig
}
// ContractConfig retrieves the deposit contract config
func ContractConfig() *DepositContractConfig {
return contractConfig
}
// DemoContractConfig uses the argument provided to initialize a fresh config.
func DemoContractConfig(depositsReq *big.Int, minDeposit *big.Int, maxDeposit *big.Int) *DepositContractConfig {
return &DepositContractConfig{
DepositsForChainStart: depositsReq,
MinDepositAmount: minDeposit,
MaxDepositAmount: maxDeposit,
}
}
// UseDemoBeaconConfig for beacon chain services.
func UseDemoBeaconConfig() {
beaconConfig = demoBeaconConfig