prysm-pulse/client/syncer/handlers_test.go

272 lines
7.9 KiB
Go

package syncer
import (
"bytes"
"context"
"errors"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/accounts"
"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/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/prysmaticlabs/prysm/client/contracts"
shardparams "github.com/prysmaticlabs/prysm/client/params"
shardingTypes "github.com/prysmaticlabs/prysm/client/types"
pb "github.com/prysmaticlabs/prysm/proto/sharding/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/p2p"
)
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey)
accountBalance, _ = new(big.Int).SetString("1001000000000000000000", 10)
)
// Mock client for testing proposer.
type mockNode struct {
smc *contracts.SMC
t *testing.T
depositFlag bool
Backend *backends.SimulatedBackend
BlockNumber int64
}
type faultySMCCaller struct{}
func (f *faultySMCCaller) CollationRecords(opts *bind.CallOpts, arg0 *big.Int, arg1 *big.Int) (struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
}, error) {
res := new(struct {
ChunkRoot [32]byte
Proposer common.Address
IsElected bool
Signature [32]byte
})
return *res, errors.New("error fetching collation record")
}
func (m *mockNode) CreateTXOpts(value *big.Int) (*bind.TransactOpts, error) {
txOpts := transactOpts()
txOpts.Value = value
return txOpts, nil
}
func (m *mockNode) SMCTransactor() *contracts.SMCTransactor {
return &m.smc.SMCTransactor
}
func (m *mockNode) SMCCaller() *contracts.SMCCaller {
return &m.smc.SMCCaller
}
func (m *mockNode) GetShardCount() (int64, error) {
shardCount, err := m.SMCCaller().ShardCount(&bind.CallOpts{})
if err != nil {
return 0, err
}
return shardCount.Int64(), nil
}
func (m *mockNode) Account() *accounts.Account {
return &accounts.Account{Address: addr}
}
func (m *mockNode) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds time.Duration) error {
m.CommitWithBlock()
m.FastForward(1)
return nil
}
func (m *mockNode) TransactionReceipt(hash common.Hash) (*types.Receipt, error) {
return m.Backend.TransactionReceipt(context.Background(), hash)
}
func (m *mockNode) DepositFlag() bool {
return m.depositFlag
}
func (m *mockNode) FastForward(p int) {
c := shardparams.DefaultConfig()
for i := 0; i < p*int(c.PeriodLength); i++ {
m.CommitWithBlock()
}
}
func (m *mockNode) CommitWithBlock() {
m.Backend.Commit()
m.BlockNumber = m.BlockNumber + 1
}
func (m *mockNode) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
return types.NewBlockWithHeader(&types.Header{Number: big.NewInt(m.BlockNumber)}), nil
}
type faultyRequest struct{}
type faultyCollationFetcher struct{}
type mockCollationFetcher struct{}
func (m *mockCollationFetcher) CollationByHeaderHash(headerHash *common.Hash) (*shardingTypes.Collation, error) {
shardID := big.NewInt(1)
chunkRoot := common.BytesToHash([]byte{})
period := big.NewInt(1)
proposerAddress := common.BytesToAddress([]byte{})
header := shardingTypes.NewCollationHeader(shardID, &chunkRoot, period, &proposerAddress, [32]byte{})
return shardingTypes.NewCollation(header, []byte{}, []*types.Transaction{}), nil
}
func (f *faultyCollationFetcher) CollationByHeaderHash(headerHash *common.Hash) (*shardingTypes.Collation, error) {
return nil, errors.New("could not fetch collation")
}
func transactOpts() *bind.TransactOpts {
return bind.NewKeyedTransactor(key)
}
func setup(t *testing.T) (*backends.SimulatedBackend, *contracts.SMC) {
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance}})
_, _, smc, err := contracts.DeploySMC(transactOpts(), backend)
if err != nil {
t.Fatalf("Failed to deploy SMC contract: %v", err)
}
backend.Commit()
return backend, smc
}
func TestCollationBodyResponse(t *testing.T) {
proposerAddress := common.BytesToAddress([]byte{})
chunkRoot := common.BytesToHash([]byte{})
goodReq := pb.CollationBodyRequest{
ChunkRoot: chunkRoot.Bytes(),
ShardId: 1,
Period: 1,
ProposerAddress: proposerAddress.Bytes(),
}
incorrectReq := faultyRequest{}
fetcher := &mockCollationFetcher{}
faultyFetcher := &faultyCollationFetcher{}
badMsg := p2p.Message{
Peer: p2p.Peer{},
Data: &incorrectReq,
}
goodMsg := p2p.Message{
Peer: p2p.Peer{},
Data: &goodReq,
}
if _, err := RespondCollationBody(badMsg, fetcher); err == nil {
t.Errorf("Incorrect request should throw error. Expecting pb.CollationBodyRequest{}, received: %v", incorrectReq)
}
if _, err := RespondCollationBody(goodMsg, faultyFetcher); err == nil {
t.Error("Faulty collatiom fetcher should cause function to throw error. no error thrown.")
}
shardID := new(big.Int).SetUint64(goodReq.ShardId)
chunkRoot = common.BytesToHash(goodReq.ChunkRoot)
period := new(big.Int).SetUint64(goodReq.Period)
proposer := common.BytesToAddress(goodReq.ProposerAddress)
header := shardingTypes.NewCollationHeader(
shardID,
&chunkRoot,
period,
&proposer,
[32]byte{})
body := []byte{}
response, err := RespondCollationBody(goodMsg, fetcher)
if err != nil {
t.Fatalf("Could not construct collation body response: %v", err)
}
if common.BytesToHash(response.HeaderHash).Hex() != header.Hash().Hex() {
t.Errorf("Incorrect header hash received. want: %v, received: %v", header.Hash().Hex(), common.BytesToHash(response.HeaderHash).Hex())
}
if !bytes.Equal(response.Body, body) {
t.Errorf("Incorrect collation body received. want: %v, received: %v", response.Body, body)
}
}
func TestConstructAttesterRequest(t *testing.T) {
backend, smc := setup(t)
node := &mockNode{
t: t,
smc: smc,
Backend: backend,
}
// Fast forward to next period.
c := shardparams.DefaultConfig()
for i := 0; i < int(c.PeriodLength); i++ {
backend.Commit()
}
shardID := big.NewInt(0)
period := big.NewInt(1)
// We set the proposer address to the address used to setup the backend.
proposerAddress := addr
chunkRoot := common.BytesToHash([]byte("chunkroottest"))
// Adds the header to the SMC.
opt, err := node.CreateTXOpts(big.NewInt(0))
if err != nil {
t.Error(err)
}
smc.AddHeader(opt, shardID, period, chunkRoot, [32]byte{})
backend.Commit()
if _, err := RequestCollationBody(&faultySMCCaller{}, shardID, period); err == nil {
t.Errorf("Expected error from RequestCollationBody when using faulty SMCCaller, got nil")
}
request, err := RequestCollationBody(node.SMCCaller(), shardID, period)
if err != nil {
t.Fatalf("Could not construct request: %v", err)
}
// fetching an inexistent shardID, period pair from the SMC will return a nil request.
nilRequest, err := RequestCollationBody(node.SMCCaller(), big.NewInt(20), big.NewInt(20))
if err != nil {
t.Fatalf("Could not construct request: %v", err)
}
if nilRequest != nil {
t.Errorf("constructAttesterRequest should return nil for an inexistent collation header. got: %v", err)
}
if common.BytesToHash(request.ChunkRoot).Hex() != chunkRoot.Hex() {
t.Errorf("Chunk root from attester request incorrect. want: %v, got: %v", chunkRoot.Hex(), common.BytesToHash(request.ChunkRoot).Hex())
}
if common.BytesToAddress(request.ProposerAddress).Hex() != proposerAddress.Hex() {
t.Errorf("Proposer address from attester request incorrect. want: %v, got: %v", proposerAddress.Hex(), common.BytesToAddress(request.ProposerAddress).Hex())
}
if shardID.Uint64() != request.ShardId {
t.Errorf("ShardID from attester request incorrect. want: %d, got: %d", shardID.Uint64(), request.ShardId)
}
if request.Period != period.Uint64() {
t.Errorf("Proposer address from attester request incorrect. want: %d, got: %d", period.Uint64(), request.Period)
}
}