Skip to content

Commit

Permalink
scratch, working on test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
pro-wh committed Oct 8, 2019
1 parent d811773 commit 976e4d9
Showing 1 changed file with 275 additions and 12 deletions.
287 changes: 275 additions & 12 deletions go/roothash/tests/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

const (
recvTimeout = 5 * time.Second
nrRuntimes = 1
nrRuntimes = 3
)

type runtimeState struct {
Expand All @@ -46,7 +46,7 @@ type runtimeState struct {

// RootHashImplementationTests exercises the basic functionality of a
// roothash backend.
func RootHashImplementationTests(t *testing.T, backend api.Backend, epochtime epochtime.SetableBackend, scheduler scheduler.Backend, storage storage.Backend, registry registry.Backend, st staking.Backend) {
func RootHashImplementationTests(t *testing.T, backend api.Backend, epochtime epochtime.SetableBackend, scheduler scheduler.Backend, storage storage.Backend, registry registry.Backend, stakingBackend staking.Backend) {
seedBase := []byte("RootHashImplementationTests")

require := require.New(t)
Expand Down Expand Up @@ -94,16 +94,15 @@ func RootHashImplementationTests(t *testing.T, backend api.Backend, epochtime ep
testEpochTransitionBlock(t, backend, epochtime, scheduler, rtStates)
})
if success {
// It only makes sense to run the SuccessfulRound test in case the
// EpochTransitionBlock was successful. Otherwise this may leave the
// committees set to nil and cause a crash.
t.Run("SucessfulRound", func(t *testing.T) {
testSucessfulRound(t, backend, storage, rtStates)

genBal, _, _, _, err := st.AccountInfo(context.Background(), stakingTests.SrcID)
require.NoError(err, "AccountInfo %s", stakingTests.SrcID)
expectedGenBal := stakingTests.TestTotalSupply.Clone()
require.Equal(expectedGenBal, genBal, "staking src account general balance")
//// It only makes sense to run the SuccessfulRound test in case the
//// EpochTransitionBlock was successful. Otherwise this may leave the
//// committees set to nil and cause a crash.
//t.Run("SucessfulRound", func(t *testing.T) {
// testSucessfulRound(t, backend, storage, rtStates)
//})

t.Run("RoothashMessages", func(t *testing.T) {
testRoothashMessages(t, backend, rtStates, stakingBackend)
})
}

Expand Down Expand Up @@ -481,6 +480,270 @@ func (s *runtimeState) testSuccessfulRound(t *testing.T, backend api.Backend, st
}
}

func hashFromHex(t *testing.T, text string) hash.Hash {
var h hash.Hash
require.NoError(t, h.UnmarshalHex(text), "hash UnmarshalHex")
return h
}

func testRoothashMessages(t *testing.T, backend api.Backend, states []*runtimeState, stakingBackend staking.Backend) {
require := require.New(t)

var emptyRoot hash.Hash
emptyRoot.Empty()

var zero, adjustmentAmount staking.Quantity
require.NoError(adjustmentAmount.FromBigInt(big.NewInt(1004)), "adjustment amount FromBigInt")
supplyPlusAdjustment := stakingTests.TestTotalSupply.Clone()
require.NoError(supplyPlusAdjustment.Add(&adjustmentAmount), "Quantity Add")

for _, testCase := range []struct {
name string
runtimeState *runtimeState
commitStateRoot hash.Hash
roothashMessages []*block.RoothashMessage
shouldSucceed bool
checkStakingAccount signature.PublicKey
checkStakingBalance *staking.Quantity
}{
{
name: "increase",
runtimeState: states[0],
commitStateRoot: hashFromHex(t, "0100000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.SrcID,
Op: block.Increase,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: true,
checkStakingAccount: stakingTests.SrcID,
checkStakingBalance: supplyPlusAdjustment,
},

{
name: "decrease",
runtimeState: states[0],
commitStateRoot: hashFromHex(t, "0200000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.SrcID,
Op: block.Decrease,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: true,
checkStakingAccount: stakingTests.SrcID,
checkStakingBalance: stakingTests.TestTotalSupply.Clone(),
},

{
name: "invalid op",
runtimeState: states[0],
commitStateRoot: hashFromHex(t, "0300000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.SrcID,
Op: 99,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: false,
checkStakingAccount: stakingTests.SrcID,
checkStakingBalance: stakingTests.TestTotalSupply.Clone(),
},

{
name: "insufficient balance",
runtimeState: states[0],
commitStateRoot: hashFromHex(t, "0400000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.DestID,
Op: block.Decrease,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: false,
checkStakingAccount: stakingTests.DestID,
checkStakingBalance: &zero,
},

{
name: "all or nothing",
runtimeState: states[0],
commitStateRoot: hashFromHex(t, "0500000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.DestID,
Op: block.Increase,
Amount: &adjustmentAmount,
},
},
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.SrcID,
Op: 99,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: false,
checkStakingAccount: stakingTests.DestID,
checkStakingBalance: &zero,
},

{
name: "unacceptable transfer peer",
runtimeState: states[1],
commitStateRoot: hashFromHex(t, "0600000000000000000000000000000000000000000000000000000000000000"),
roothashMessages: []*block.RoothashMessage{
{
StakingGeneralAdjustmentRoothashMessage: &block.StakingGeneralAdjustmentRoothashMessage{
Account: stakingTests.DestID,
Op: block.Increase,
Amount: &adjustmentAmount,
},
},
},
shouldSucceed: false,
checkStakingAccount: stakingTests.DestID,
checkStakingBalance: &zero,
},
} {
t.Run(testCase.name, func(t *testing.T){
rt, computeCommittee, mergeCommittee := testCase.runtimeState.rt, testCase.runtimeState.computeCommittee, testCase.runtimeState.mergeCommittee

child, err := backend.GetLatestBlock(context.Background(), rt.Runtime.ID)
require.NoError(err, "GetLatestBlock")
require.NotEqual(testCase.commitStateRoot, child.Header.StateRoot, "trying to commit to a different state root")

ch, sub, err := backend.WatchBlocks(rt.Runtime.ID)
require.NoError(err, "WatchBlocks")
defer sub.Close()

// Create the new block header that the leader and nodes will commit to.
parent := &block.Block{
Header: block.Header{
Version: 0,
Namespace: child.Header.Namespace,
Round: child.Header.Round + 1,
Timestamp: uint64(time.Now().Unix()),
HeaderType: block.Normal,
PreviousHash: child.Header.EncodedHash(),
IORoot: emptyRoot,
StateRoot: testCase.commitStateRoot,
},
}
require.True(parent.Header.IsParentOf(&child.Header), "parent is parent of child")

// Generate all the compute commitments.
var toCommit []*registryTests.TestNode
var computeCommits []commitment.ComputeCommitment
toCommit = append(toCommit, computeCommittee.workers...)
for _, node := range toCommit {
commitBody := commitment.ComputeBody{
CommitteeID: computeCommittee.committee.EncodedMembersHash(),
Header: commitment.ComputeResultsHeader{
PreviousHash: parent.Header.PreviousHash,
IORoot: parent.Header.IORoot,
StateRoot: parent.Header.StateRoot,
RoothashMessages: testCase.roothashMessages,
},
StorageSignatures: parent.Header.StorageSignatures,
InputRoot: hash.Hash{},
InputStorageSigs: []signature.Signature{},
}

// Fake txn scheduler signature.
dispatch := &commitment.TxnSchedulerBatchDispatch{
CommitteeID: commitBody.CommitteeID,
IORoot: commitBody.InputRoot,
StorageSignatures: commitBody.InputStorageSigs,
Header: child.Header,
}
signer := testCase.runtimeState.txnSchedCommittee.leader.Signer
var signedDispatch *signature.Signed
signedDispatch, err = signature.SignSigned(signer, commitment.TxnSchedulerBatchDispatchSigCtx, dispatch)
require.NoError(err, "SignSigned")
commitBody.TxnSchedSig = signedDispatch.Signature

// `err` shadows outside.
commit, err := commitment.SignComputeCommitment(node.Signer, &commitBody) // nolint: vetshadow
require.NoError(err, "SignSigned")

computeCommits = append(computeCommits, *commit)
}

// Generate all the merge commitments.
var mergeCommits []commitment.MergeCommitment
toCommit = nil
toCommit = append(toCommit, mergeCommittee.workers...)
mergedHeader := parent.Header
mergedHeader.RoothashMessages = testCase.roothashMessages
for _, node := range toCommit {
commitBody := commitment.MergeBody{
ComputeCommits: computeCommits,
Header: mergedHeader,
}
// `err` shadows outside.
commit, err := commitment.SignMergeCommitment(node.Signer, &commitBody) // nolint: vetshadow
require.NoError(err, "SignSigned")

mergeCommits = append(mergeCommits, *commit)
}

err = backend.MergeCommit(context.Background(), rt.Runtime.ID, mergeCommits)
require.NoError(err, "MergeCommit")

// Ensure that the round was finalized.
for {
select {
case blk := <-ch:
header := blk.Block.Header

// Ensure that WatchBlocks uses the correct latest block.
require.True(header.Round >= child.Header.Round, "WatchBlocks must start at child block")

if header.Round == child.Header.Round {
require.EqualValues(child.Header, header, "old block is equal")
continue
}

// Ensure that we properly committed or reverted.
if testCase.shouldSucceed {
require.EqualValues(parent.Header.HeaderType, header.HeaderType, "block header type")
require.EqualValues(parent.Header.StateRoot, header.StateRoot, "block root hash")
} else {
require.EqualValues(block.RoundFailed, header.HeaderType, "block header type")
require.EqualValues(child.Header.StateRoot, header.StateRoot, "block root hash")
}

// Ensure that staking balances are correct.
genBal, _, _, _, err := stakingBackend.AccountInfo(context.Background(), testCase.checkStakingAccount)
require.NoError(err, "AccountInfo %s", testCase.checkStakingAccount)
require.Equal(testCase.checkStakingBalance, genBal, "staking balance matches")

// Nothing more to do after the block was received.
break
case <-time.After(recvTimeout):
t.Fatalf("failed to receive block")
}
}
})
}
}

type testCommittee struct {
committee *scheduler.Committee
leader *registryTests.TestNode
Expand Down

0 comments on commit 976e4d9

Please sign in to comment.