mirror of https://github.com/poanetwork/gecko.git
210 lines
5.8 KiB
Go
210 lines
5.8 KiB
Go
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||
|
// See the file LICENSE for licensing terms.
|
||
|
|
||
|
package timestampvm
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/ava-labs/gecko/database/memdb"
|
||
|
"github.com/ava-labs/gecko/ids"
|
||
|
"github.com/ava-labs/gecko/snow"
|
||
|
"github.com/ava-labs/gecko/snow/engine/common"
|
||
|
"github.com/ava-labs/gecko/utils/formatting"
|
||
|
)
|
||
|
|
||
|
var blockchainID = ids.NewID([32]byte{1, 2, 3})
|
||
|
|
||
|
// Utility function to assert that [block] has:
|
||
|
// * Parent with ID [parentID]
|
||
|
// * Data [expectedData]
|
||
|
// * Verify() returns nil iff passesVerify == true
|
||
|
func assertBlock(block *Block, parentID ids.ID, expectedData [dataLen]byte, passesVerify bool) error {
|
||
|
if !block.ParentID().Equals(parentID) {
|
||
|
return fmt.Errorf("expect parent ID to be %s but was %s", parentID, block.ParentID())
|
||
|
}
|
||
|
if block.Data != expectedData {
|
||
|
return fmt.Errorf("expected data to be %v but was %v", expectedData, block.Data)
|
||
|
}
|
||
|
if block.Verify() != nil && passesVerify {
|
||
|
return fmt.Errorf("expected block to pass verification but it fails")
|
||
|
}
|
||
|
if block.Verify() == nil && !passesVerify {
|
||
|
return fmt.Errorf("expected block to fail verification but it passes")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Assert that after initialization, the vm has the state we expect
|
||
|
func TestGenesis(t *testing.T) {
|
||
|
// Initialize the vm
|
||
|
db := memdb.New()
|
||
|
msgChan := make(chan common.Message, 1)
|
||
|
vm := &VM{}
|
||
|
ctx := snow.DefaultContextTest()
|
||
|
ctx.ChainID = blockchainID
|
||
|
vm.Initialize(ctx, db, []byte{0, 0, 0, 0, 0}, msgChan, nil)
|
||
|
|
||
|
// Verify that the db is initialized
|
||
|
if !vm.DBInitialized() {
|
||
|
t.Fatal("db should be initialized")
|
||
|
}
|
||
|
|
||
|
// Get lastAccepted
|
||
|
lastAccepted := vm.LastAccepted()
|
||
|
if lastAccepted.IsZero() {
|
||
|
t.Fatal("lastAccepted should not be empty")
|
||
|
}
|
||
|
|
||
|
// Verify that getBlock returns the genesis block, and the genesis block
|
||
|
// is the type we expect
|
||
|
genesisSnowmanBlock, err := vm.GetBlock(lastAccepted) // genesisBlock as snowman.Block
|
||
|
if err != nil {
|
||
|
t.Fatalf("couldn't get genesisBlock: %s", err)
|
||
|
}
|
||
|
genesisBlock, ok := genesisSnowmanBlock.(*Block) // type assert that genesisBlock is a *Block
|
||
|
if !ok {
|
||
|
t.Fatal("type of genesisBlock should be *Block")
|
||
|
}
|
||
|
|
||
|
// Verify that the genesis block has the data we expect
|
||
|
if err := assertBlock(genesisBlock, ids.Empty, [32]byte{0, 0, 0, 0, 0}, true); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestHappyPath(t *testing.T) {
|
||
|
// Initialize the vm
|
||
|
db := memdb.New()
|
||
|
msgChan := make(chan common.Message, 1)
|
||
|
vm := &VM{}
|
||
|
ctx := snow.DefaultContextTest()
|
||
|
ctx.ChainID = blockchainID
|
||
|
if err := vm.Initialize(ctx, db, []byte{0, 0, 0, 0, 0}, msgChan, nil); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
genesisBlock, err := vm.GetBlock(vm.LastAccepted())
|
||
|
if err != nil {
|
||
|
t.Fatal("could not get genesis block")
|
||
|
}
|
||
|
// in an actual execution, the engine would set the preference
|
||
|
vm.SetPreference(genesisBlock.ID())
|
||
|
|
||
|
ctx.Lock.Lock()
|
||
|
vm.proposeBlock([dataLen]byte{0, 0, 0, 0, 1}) // propose a value
|
||
|
ctx.Lock.Unlock()
|
||
|
|
||
|
select { // assert there is a pending tx message to the engine
|
||
|
case msg := <-msgChan:
|
||
|
if msg != common.PendingTxs {
|
||
|
t.Fatal("Wrong message")
|
||
|
}
|
||
|
default:
|
||
|
t.Fatal("should have been pendingTxs message on channel")
|
||
|
}
|
||
|
|
||
|
// build the block
|
||
|
ctx.Lock.Lock()
|
||
|
snowmanBlock2, err := vm.BuildBlock()
|
||
|
if err != nil {
|
||
|
t.Fatalf("problem building block: %s", err)
|
||
|
}
|
||
|
if err := snowmanBlock2.Verify(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
snowmanBlock2.Accept() // accept the block
|
||
|
vm.SetPreference(snowmanBlock2.ID())
|
||
|
|
||
|
// Should be the block we just accepted
|
||
|
snowmanBlock2, err = vm.GetBlock(vm.LastAccepted())
|
||
|
if err != nil {
|
||
|
t.Fatal("couldn't get block")
|
||
|
}
|
||
|
block2, ok := snowmanBlock2.(*Block)
|
||
|
if !ok {
|
||
|
t.Fatal("genesis block should be type *Block")
|
||
|
}
|
||
|
// Assert the block we accepted has the data we expect
|
||
|
if err := assertBlock(block2, genesisBlock.ID(), [dataLen]byte{0, 0, 0, 0, 1}, true); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
vm.proposeBlock([dataLen]byte{0, 0, 0, 0, 2}) // propose a block
|
||
|
ctx.Lock.Unlock()
|
||
|
|
||
|
select { // verify there is a pending tx message to the engine
|
||
|
case msg := <-msgChan:
|
||
|
if msg != common.PendingTxs {
|
||
|
t.Fatal("Wrong message")
|
||
|
}
|
||
|
default:
|
||
|
t.Fatal("should have been pendingTxs message on channel")
|
||
|
}
|
||
|
|
||
|
ctx.Lock.Lock()
|
||
|
|
||
|
// build the block
|
||
|
if block, err := vm.BuildBlock(); err != nil {
|
||
|
t.Fatalf("problem building block: %s", err)
|
||
|
} else {
|
||
|
if err := block.Verify(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
block.Accept() // accept the block
|
||
|
vm.SetPreference(block.ID())
|
||
|
}
|
||
|
|
||
|
// The block we just accepted
|
||
|
snowmanBlock3, err := vm.GetBlock(vm.LastAccepted())
|
||
|
if err != nil {
|
||
|
t.Fatal("couldn't get block")
|
||
|
}
|
||
|
block3, ok := snowmanBlock3.(*Block)
|
||
|
if !ok {
|
||
|
t.Fatal("genesis block should be type *Block")
|
||
|
}
|
||
|
// Assert the block we accepted has the data we expect
|
||
|
if err := assertBlock(block3, snowmanBlock2.ID(), [dataLen]byte{0, 0, 0, 0, 2}, true); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Next, check the blocks we added are there
|
||
|
if block2FromState, err := vm.GetBlock(block2.ID()); err != nil {
|
||
|
t.Fatal(err)
|
||
|
} else if !block2FromState.ID().Equals(block2.ID()) {
|
||
|
t.Fatal("expected IDs to match but they don't")
|
||
|
}
|
||
|
if block3FromState, err := vm.GetBlock(block3.ID()); err != nil {
|
||
|
t.Fatal(err)
|
||
|
} else if !block3FromState.ID().Equals(block3.ID()) {
|
||
|
t.Fatal("expected IDs to match but they don't")
|
||
|
}
|
||
|
|
||
|
ctx.Lock.Unlock()
|
||
|
}
|
||
|
|
||
|
func TestMakeStringFrom32Bytes(t *testing.T) {
|
||
|
bytes := [32]byte{'w', 'o', 'o'}
|
||
|
bytesFormatter := formatting.CB58{Bytes: bytes[:]}
|
||
|
t.Log(bytesFormatter.String())
|
||
|
}
|
||
|
|
||
|
func TestService(t *testing.T) {
|
||
|
// Initialize the vm
|
||
|
db := memdb.New()
|
||
|
msgChan := make(chan common.Message, 1)
|
||
|
vm := &VM{}
|
||
|
ctx := snow.DefaultContextTest()
|
||
|
ctx.ChainID = blockchainID
|
||
|
if err := vm.Initialize(ctx, db, []byte{0, 0, 0, 0, 0}, msgChan, nil); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
service := Service{vm}
|
||
|
if err := service.GetBlock(nil, &GetBlockArgs{}, &GetBlockReply{}); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|