mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
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:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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",
|
||||
46
contracts/deposit-contract/ETH1logs.go
Normal file
46
contracts/deposit-contract/ETH1logs.go
Normal 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,
|
||||
×tamp,
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -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
|
||||
```
|
||||
1
contracts/deposit-contract/abi.json
Normal file
1
contracts/deposit-contract/abi.json
Normal 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}]
|
||||
1
contracts/deposit-contract/bytecode.bin
Normal file
1
contracts/deposit-contract/bytecode.bin
Normal file
File diff suppressed because one or more lines are too long
30
contracts/deposit-contract/deployContract/BUILD.bazel
Normal file
30
contracts/deposit-contract/deployContract/BUILD.bazel
Normal 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"],
|
||||
)
|
||||
@@ -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...]
|
||||
@@ -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)
|
||||
}
|
||||
497
contracts/deposit-contract/depositContract.go
Normal file
497
contracts/deposit-contract/depositContract.go
Normal file
File diff suppressed because one or more lines are too long
69
contracts/deposit-contract/depositContract.v.py
Normal file
69
contracts/deposit-contract/depositContract.v.py
Normal 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
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user