Add in ChainStart Listener (#1327)

* changing handling of logs

* gazelle

* removing outdated vars

* fixing tests

* adding vrc bindings to service

* updating vrc sol and bindings

* more changes

* adding trie

* remove functions

* addressing preston's review

* tests

* gazelle

* fixed tests

* note

* Lint

* doc

* exploration test

* adding new methods and tests

* adding log type checker

* lint

* Adding processChainstartLog

* gazelle

* addressing comments

* addressing comments and adding tests

* review comments

* comment

* comment

* abi naming
This commit is contained in:
Nishant Das
2019-01-17 23:14:32 +08:00
committed by Raul Jordan
parent c7eefff105
commit 08a06458d9
7 changed files with 372 additions and 179 deletions

View File

@@ -111,20 +111,20 @@ func setupBeaconChain(t *testing.T, faultyPoWClient bool, beaconDB *db.BeaconDB)
if faultyPoWClient {
client := &faultyClient{}
web3Service, err = powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: client,
Client: client,
Logger: client,
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: client,
Client: client,
Logger: client,
})
} else {
client := &mockClient{}
web3Service, err = powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: client,
Client: client,
Logger: client,
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: client,
Client: client,
Logger: client,
})
}
if err != nil {

View File

@@ -274,7 +274,7 @@ func (b *BeaconNode) registerPOWChainService(ctx *cli.Context) error {
web3Service, err := powchain.NewWeb3Service(context.TODO(), &powchain.Web3ServiceConfig{
Endpoint: b.ctx.GlobalString(utils.Web3ProviderFlag.Name),
VrcAddr: common.HexToAddress(b.ctx.GlobalString(utils.VrcContractFlag.Name)),
DepositContract: common.HexToAddress(b.ctx.GlobalString(utils.VrcContractFlag.Name)),
Client: powClient,
Reader: powClient,
Logger: powClient,

View File

@@ -7,11 +7,11 @@ 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",
"//shared/hashutil:go_default_library",
"//shared/trie:go_default_library",
"@com_github_ethereum_go_ethereum//:go_default_library",
"@com_github_ethereum_go_ethereum//accounts/abi:go_default_library",
"@com_github_ethereum_go_ethereum//accounts/abi/bind:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
@@ -25,6 +25,7 @@ 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",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/event:go_default_library",

View File

@@ -2,20 +2,20 @@
package powchain
import (
"bytes"
"context"
"encoding/binary"
"errors"
"fmt"
"math/big"
"strings"
"time"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"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"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/trie"
@@ -50,27 +50,27 @@ type Client interface {
// Validator Registration Contract on the PoW chain to kick off the beacon
// chain's validator registration process.
type Web3Service struct {
ctx context.Context
cancel context.CancelFunc
client Client
headerChan chan *gethTypes.Header
logChan chan gethTypes.Log
endpoint string
vrcAddress 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
depositRoot []byte
depositTrie *trie.DepositTrie
ctx context.Context
cancel context.CancelFunc
client Client
headerChan chan *gethTypes.Header
logChan chan gethTypes.Log
endpoint string
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
depositRoot []byte
depositTrie *trie.DepositTrie
}
// Web3ServiceConfig defines a config struct for web3 service to use through its life cycle.
type Web3ServiceConfig struct {
Endpoint string
VrcAddr common.Address
DepositContract common.Address
Client Client
Reader Reader
Logger bind.ContractFilterer
@@ -79,7 +79,7 @@ type Web3ServiceConfig struct {
var (
depositEventSignature = []byte("Deposit(bytes,bytes,bytes)")
chainStartEventSignature = []byte("ChainStart(bytes,bytes,bytes)")
chainStartEventSignature = []byte("ChainStart(bytes,bytes)")
)
// NewWeb3Service sets up a new instance with an ethclient when
@@ -92,25 +92,25 @@ func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Servic
)
}
vrcCaller, err := contracts.NewValidatorRegistrationCaller(config.VrcAddr, config.ContractBackend)
vrcCaller, err := contracts.NewValidatorRegistrationCaller(config.DepositContract, config.ContractBackend)
if err != nil {
return nil, fmt.Errorf("could not create VRC caller %v", err)
}
ctx, cancel := context.WithCancel(ctx)
return &Web3Service{
ctx: ctx,
cancel: cancel,
headerChan: make(chan *gethTypes.Header),
logChan: make(chan gethTypes.Log),
endpoint: config.Endpoint,
blockNumber: nil,
blockHash: common.BytesToHash([]byte{}),
vrcAddress: config.VrcAddr,
client: config.Client,
reader: config.Reader,
logger: config.Logger,
vrcCaller: vrcCaller,
ctx: ctx,
cancel: cancel,
headerChan: make(chan *gethTypes.Header),
logChan: make(chan gethTypes.Log),
endpoint: config.Endpoint,
blockNumber: nil,
blockHash: common.BytesToHash([]byte{}),
depositContractAddress: config.DepositContract,
client: config.Client,
reader: config.Reader,
logger: config.Logger,
vrcCaller: vrcCaller,
}, nil
}
@@ -172,7 +172,7 @@ func (w *Web3Service) run(done <-chan struct{}) {
}
query := ethereum.FilterQuery{
Addresses: []common.Address{
w.vrcAddress,
w.depositContractAddress,
},
}
logSub, err := w.logger.SubscribeFilterLogs(w.ctx, query, w.logChan)
@@ -206,88 +206,78 @@ func (w *Web3Service) run(done <-chan struct{}) {
"blockHash": w.blockHash.Hex(),
}).Debug("Latest web3 chain event")
case VRClog := <-w.logChan:
w.processLog(VRClog)
w.ProcessLog(VRClog)
}
}
}
func (w *Web3Service) processLog(VRClog gethTypes.Log) {
// ProcessLog is the main method which handles the processing of all
// logs from the deposit contract on the POW Chain.
func (w *Web3Service) ProcessLog(VRClog gethTypes.Log) {
// Process logs according to their event signature.
if VRClog.Topics[0] == hashutil.Hash(depositEventSignature) {
w.processDepositLog(VRClog)
w.ProcessDepositLog(VRClog)
return
}
if VRClog.Topics[0] == hashutil.Hash(chainStartEventSignature) {
w.processChainStartLog(VRClog)
w.ProcessChainStartLog(VRClog)
return
}
log.Debugf("Log is not of a valid event signature %#x", VRClog.Topics[0])
}
// processDepositLog processes the log which had been received from
// ProcessDepositLog processes the log which had been received from
// the POW chain by trying to ascertain which participant deposited
// in the contract.
func (w *Web3Service) processDepositLog(VRClog gethTypes.Log) {
func (w *Web3Service) ProcessDepositLog(VRClog gethTypes.Log) {
merkleRoot := VRClog.Topics[1]
depositData, MerkleTreeIndex, err := w.unPackLogData(VRClog.Data)
depositData, MerkleTreeIndex, err := utils.UnpackDepositLogData(VRClog.Data)
if err != nil {
log.Errorf("Could not unpack log %v", err)
return
}
if err := w.saveInTrie(depositData, merkleRoot); err != nil {
log.Errorf("Could not save in trie %v", err)
return
}
depositInput, err := blocks.DecodeDepositInput(depositData)
if err != nil {
log.Errorf("Could not decode deposit input %v", err)
return
}
index := binary.BigEndian.Uint64(MerkleTreeIndex)
log.WithFields(logrus.Fields{
"publicKey": depositInput.Pubkey,
"merkle tree index": index,
}).Info("Validator registered in VRC with public key and index")
}
// processChainStartLog processes the log which had been received from
// ProcessChainStartLog processes the log which had been received from
// the POW chain by trying to determine when to start the beacon chain.
func (w *Web3Service) processChainStartLog(VRClog gethTypes.Log) {
_ = VRClog
//TODO(#1288): Process ChainStart Logs.
}
// unPackLogData unpacks the data from a log using the abi decoder.
func (w *Web3Service) unPackLogData(data []byte) ([]byte, []byte, error) {
reader := bytes.NewReader([]byte(contracts.ValidatorRegistrationABI))
contractAbi, err := abi.JSON(reader)
func (w *Web3Service) ProcessChainStartLog(VRClog gethTypes.Log) {
receiptRoot := VRClog.Topics[1]
timestampData, err := utils.UnpackChainStartLogData(VRClog.Data)
if err != nil {
return nil, nil, fmt.Errorf("unable to generate contract abi %v", err)
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)
return
}
unpackedLogs := []*[]byte{
&[]byte{},
&[]byte{},
timestamp := binary.BigEndian.Uint64(timestampData)
if uint64(time.Now().Unix()) < timestamp {
log.Errorf("Invalid timestamp from log expected %d > %d", time.Now().Unix(), timestamp)
}
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
chainStartTime := time.Unix(int64(timestamp), 0)
log.WithFields(logrus.Fields{
"ChainStartTime": chainStartTime,
}).Info("Minimum Number of Validators Reached for beacon-chain to start")
}
// saveInTrie saves in the in-memory deposit trie.
@@ -300,6 +290,8 @@ func (w *Web3Service) saveInTrie(depositData []byte, merkleRoot common.Hash) err
return nil
}
// processPastLogs processes all the past logs from the deposit contract and
// updates the deposit trie with the data from each individual log.
func (w *Web3Service) processPastLogs(query ethereum.FilterQuery) error {
logs, err := w.logger.FilterLogs(w.ctx, query)
if err != nil {
@@ -307,7 +299,7 @@ func (w *Web3Service) processPastLogs(query ethereum.FilterQuery) error {
}
for _, log := range logs {
w.processLog(log)
w.ProcessLog(log)
}
return nil
}

View File

@@ -10,6 +10,7 @@ import (
"math/big"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
@@ -19,6 +20,7 @@ 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"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/event"
@@ -111,37 +113,37 @@ func TestNewWeb3Service(t *testing.T) {
ctx := context.Background()
var err error
if _, err = NewWeb3Service(ctx, &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
}); err == nil {
t.Errorf("passing in an HTTP endpoint should throw an error, received nil")
}
endpoint = "ftp://127.0.0.1"
if _, err = NewWeb3Service(ctx, &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
}); err == nil {
t.Errorf("passing in a non-ws, wss, or ipc endpoint should throw an error, received nil")
}
endpoint = "ws://127.0.0.1"
if _, err = NewWeb3Service(ctx, &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
}); err != nil {
t.Errorf("passing in as ws endpoint should not throw error, received %v", err)
}
endpoint = "ipc://geth.ipc"
if _, err = NewWeb3Service(ctx, &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
Endpoint: endpoint,
DepositContract: common.Address{},
Reader: &goodReader{},
Logger: &goodLogger{},
}); err != nil {
t.Errorf("passing in an ipc endpoint should not throw error, received %v", err)
}
@@ -157,7 +159,7 @@ func TestStart(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -188,7 +190,7 @@ func TestStop(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -224,7 +226,7 @@ func TestInitDataFromVRC(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -275,7 +277,7 @@ func TestSaveInTrie(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -296,76 +298,6 @@ func TestSaveInTrie(t *testing.T) {
}
func TestProcessDepositLog(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
testAcc, err := setup()
if err != nil {
t.Fatalf("Unable to set up simulated backend %v", err)
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
testAcc.backend.Commit()
web3Service.depositTrie = trie.NewDepositTrie()
currentRoot := web3Service.depositTrie.Root()
var stub [48]byte
copy(stub[:], []byte("testing"))
data := &pb.DepositInput{
Pubkey: stub[:],
ProofOfPossession: stub[:],
WithdrawalCredentialsHash32: []byte("withdraw"),
RandaoCommitmentHash32: []byte("randao"),
CustodyCommitmentHash32: []byte("custody"),
}
serializedData := new(bytes.Buffer)
if err := ssz.Encode(serializedData, data); err != nil {
t.Fatalf("Could not serialize data %v", err)
}
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
}
testAcc.backend.Commit()
query := ethereum.FilterQuery{
Addresses: []common.Address{
web3Service.vrcAddress,
},
}
logs, err := testAcc.backend.FilterLogs(web3Service.ctx, query)
if err != nil {
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")
testutil.AssertLogsDoNotContain(t, hook, "Could not save in trie")
testutil.AssertLogsDoNotContain(t, hook, "Could not decode deposit input")
testutil.AssertLogsContain(t, hook, "Validator registered in VRC with public key and index")
hook.Reset()
}
func TestBadReader(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
@@ -375,7 +307,7 @@ func TestBadReader(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &badReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -404,7 +336,7 @@ func TestLatestMainchainInfo(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -447,7 +379,7 @@ func TestBadLogger(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -469,7 +401,8 @@ func TestBadLogger(t *testing.T) {
hook.Reset()
}
func TestUnpackLogs(t *testing.T) {
func TestProcessDepositLog(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
testAcc, err := setup()
if err != nil {
@@ -477,7 +410,76 @@ func TestUnpackLogs(t *testing.T) {
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
VrcAddr: testAcc.contractAddr,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
testAcc.backend.Commit()
web3Service.depositTrie = trie.NewDepositTrie()
currentRoot := web3Service.depositTrie.Root()
var stub [48]byte
copy(stub[:], []byte("testing"))
data := &pb.DepositInput{
Pubkey: stub[:],
ProofOfPossession: stub[:],
WithdrawalCredentialsHash32: []byte("withdraw"),
RandaoCommitmentHash32: []byte("randao"),
CustodyCommitmentHash32: []byte("custody"),
}
serializedData := new(bytes.Buffer)
if err := ssz.Encode(serializedData, data); err != nil {
t.Fatalf("Could not serialize data %v", err)
}
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
}
testAcc.backend.Commit()
query := ethereum.FilterQuery{
Addresses: []common.Address{
web3Service.depositContractAddress,
},
}
logs, err := testAcc.backend.FilterLogs(web3Service.ctx, query)
if err != nil {
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")
testutil.AssertLogsDoNotContain(t, hook, "Could not save in trie")
testutil.AssertLogsDoNotContain(t, hook, "Could not decode deposit input")
testutil.AssertLogsContain(t, hook, "Validator registered in VRC with public key and index")
hook.Reset()
}
func TestUnpackDepositLogs(t *testing.T) {
endpoint := "ws://127.0.0.1"
testAcc, err := setup()
if err != nil {
t.Fatalf("Unable to set up simulated backend %v", err)
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
@@ -524,7 +526,7 @@ func TestUnpackLogs(t *testing.T) {
query := ethereum.FilterQuery{
Addresses: []common.Address{
web3Service.vrcAddress,
web3Service.depositContractAddress,
},
}
@@ -533,7 +535,7 @@ func TestUnpackLogs(t *testing.T) {
t.Fatalf("Unable to retrieve logs %v", err)
}
depData, index, err := web3Service.unPackLogData(logz[0].Data)
depData, index, err := utils.UnpackDepositLogData(logz[0].Data)
if err != nil {
t.Fatalf("Unable to unpack logs %v", err)
}
@@ -568,3 +570,152 @@ func TestUnpackLogs(t *testing.T) {
}
}
func TestProcessChainStartLog(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
testAcc, err := setup()
if err != nil {
t.Fatalf("Unable to set up simulated backend %v", err)
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
testAcc.backend.Commit()
testAcc.backend.AdjustTime(time.Duration(int64(time.Now().Nanosecond())))
web3Service.depositTrie = trie.NewDepositTrie()
currentRoot := web3Service.depositTrie.Root()
var stub [48]byte
copy(stub[:], []byte("testing"))
data := &pb.DepositInput{
Pubkey: stub[:],
ProofOfPossession: stub[:],
WithdrawalCredentialsHash32: []byte("withdraw"),
RandaoCommitmentHash32: []byte("randao"),
CustodyCommitmentHash32: []byte("custody"),
}
serializedData := new(bytes.Buffer)
if err := ssz.Encode(serializedData, data); err != nil {
t.Fatalf("Could not serialize data %v", err)
}
// 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++ {
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
}
testAcc.backend.Commit()
}
query := ethereum.FilterQuery{
Addresses: []common.Address{
web3Service.depositContractAddress,
},
}
logs, err := testAcc.backend.FilterLogs(web3Service.ctx, query)
if err != nil {
t.Fatalf("Unable to retrieve logs %v", err)
}
logs[len(logs)-1].Topics[1] = currentRoot
web3Service.ProcessLog(logs[len(logs)-1])
testutil.AssertLogsDoNotContain(t, hook, "Unable to unpack ChainStart log data")
testutil.AssertLogsDoNotContain(t, hook, "Receipt root from log doesn't match the root saved in memory")
testutil.AssertLogsDoNotContain(t, hook, "Invalid timestamp from log")
testutil.AssertLogsContain(t, hook, "Minimum Number of Validators Reached for beacon-chain to start")
hook.Reset()
}
func TestUnpackChainStartLogs(t *testing.T) {
endpoint := "ws://127.0.0.1"
testAcc, err := setup()
if err != nil {
t.Fatalf("Unable to set up simulated backend %v", err)
}
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{
Endpoint: endpoint,
DepositContract: testAcc.contractAddr,
Reader: &goodReader{},
Logger: &goodLogger{},
ContractBackend: testAcc.backend,
})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
testAcc.backend.Commit()
testAcc.backend.AdjustTime(time.Duration(int64(time.Now().Nanosecond())))
var stub [48]byte
copy(stub[:], []byte("testing"))
data := &pb.DepositInput{
Pubkey: stub[:],
ProofOfPossession: stub[:],
WithdrawalCredentialsHash32: []byte("withdraw"),
RandaoCommitmentHash32: []byte("randao"),
CustodyCommitmentHash32: []byte("custody"),
}
serializedData := new(bytes.Buffer)
if err := ssz.Encode(serializedData, data); err != nil {
t.Fatalf("Could not serialize data %v", err)
}
// 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++ {
testAcc.txOpts.Value = amount32Eth
if _, err := testAcc.contract.Deposit(testAcc.txOpts, serializedData.Bytes()); err != nil {
t.Fatalf("Could not deposit to VRC %v", err)
}
testAcc.backend.Commit()
}
query := ethereum.FilterQuery{
Addresses: []common.Address{
web3Service.depositContractAddress,
},
}
logs, err := testAcc.backend.FilterLogs(web3Service.ctx, query)
if err != nil {
t.Fatalf("Unable to retrieve logs %v", err)
}
timestampData, err := utils.UnpackChainStartLogData(logs[len(logs)-1].Data)
if err != nil {
t.Fatalf("Unable to unpack logs %v", err)
}
timestamp := binary.BigEndian.Uint64(timestampData)
if timestamp > uint64(time.Now().Unix()) {
t.Errorf("Timestamp from log is higher than the current time %d > %d", timestamp, time.Now().Unix())
}
}

View File

@@ -3,6 +3,7 @@ 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",
@@ -11,8 +12,10 @@ 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

@@ -0,0 +1,46 @@
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{
&[]byte{},
&[]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{
&[]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
}