Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[R4R]Separate Processing and State Verification on BSC: add test cases for trust protocol #742

Merged
merged 1 commit into from
Jan 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
270 changes: 270 additions & 0 deletions eth/protocols/trust/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package trust

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
)

var (
// testKey is a private key to use for funding a tester account.
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")

// testAddr is the Ethereum address of the tester account.
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
)

// testBackend is a mock implementation of the live Ethereum message handler. Its
// purpose is to allow testing the request/reply workflows and wire serialization
// in the `eth` protocol without actually doing any data processing.
type testBackend struct {
db ethdb.Database
chain *core.BlockChain
txpool *core.TxPool
}

// newTestBackend creates an empty chain and wraps it into a mock backend.
func newTestBackend(blocks int) *testBackend {
return newTestBackendWithGenerator(blocks)
}

// newTestBackend creates a chain with a number of explicitly defined blocks and
// wraps it into a mock backend.
func newTestBackendWithGenerator(blocks int) *testBackend {
signer := types.HomesteadSigner{}
db := rawdb.NewMemoryDatabase()
engine := clique.New(params.AllCliqueProtocolChanges.Clique, db)
genspec := &core.Genesis{
//Config: params.TestChainConfig,
ExtraData: make([]byte, 32+common.AddressLength+65),
Alloc: core.GenesisAlloc{testAddr: {Balance: big.NewInt(100000000000000000)}},
}
copy(genspec.ExtraData[32:], testAddr[:])
genesis := genspec.MustCommit(db)

chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
generator := func(i int, block *core.BlockGen) {
// The chain maker doesn't have access to a chain, so the difficulty will be
// lets unset (nil). Set it here to the correct value.
// block.SetCoinbase(testAddr)
block.SetDifficulty(big.NewInt(2))

// We want to simulate an empty middle block, having the same state as the
// first one. The last is needs a state change again to force a reorg.
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddr), common.Address{0x01}, big.NewInt(1), params.TxGas, nil, nil), signer, testKey)
if err != nil {
panic(err)
}
block.AddTxWithChain(chain, tx)
}

bs, _ := core.GenerateChain(params.AllCliqueProtocolChanges, genesis, engine, db, blocks, generator)
for i, block := range bs {
header := block.Header()
if i > 0 {
header.ParentHash = bs[i-1].Hash()
}
header.Extra = make([]byte, 32+65)
header.Difficulty = big.NewInt(2)

sig, _ := crypto.Sign(clique.SealHash(header).Bytes(), testKey)
copy(header.Extra[len(header.Extra)-65:], sig)
bs[i] = block.WithSeal(header)
}

if _, err := chain.InsertChain(bs); err != nil {
panic(err)
}

txconfig := core.DefaultTxPoolConfig
txconfig.Journal = "" // Don't litter the disk with test journals

return &testBackend{
db: db,
chain: chain,
txpool: core.NewTxPool(txconfig, params.AllCliqueProtocolChanges, chain),
}
}

// close tears down the transaction pool and chain behind the mock backend.
func (b *testBackend) close() {
b.txpool.Stop()
b.chain.Stop()
}

func (b *testBackend) Chain() *core.BlockChain { return b.chain }

func (b *testBackend) RunPeer(peer *Peer, handler Handler) error {
// Normally the backend would do peer mainentance and handshakes. All that
// is omitted and we will just give control back to the handler.
return handler(peer)
}
func (b *testBackend) PeerInfo(enode.ID) interface{} { panic("not implemented") }

func (b *testBackend) Handle(*Peer, Packet) error {
panic("data processing tests should be done in the handler package")
}

func TestRequestRoot(t *testing.T) { testRequestRoot(t, Trust1) }

func testRequestRoot(t *testing.T, protocol uint) {
t.Parallel()

blockNum := 1032 // The latest 1024 blocks' DiffLayer will be cached.
backend := newTestBackend(blockNum)
defer backend.close()

peer, _ := newTestPeer("peer", protocol, backend)
defer peer.close()

pairs := []struct {
req RootRequestPacket
res RootResponsePacket
}{
{
req: RootRequestPacket{
RequestId: 1,
BlockNumber: 1,
},
res: RootResponsePacket{
RequestId: 1,
Status: types.StatusPartiallyVerified,
BlockNumber: 1,
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 2,
BlockNumber: 128,
},
res: RootResponsePacket{
RequestId: 2,
Status: types.StatusFullVerified,
BlockNumber: 128,
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 3,
BlockNumber: 128,
BlockHash: types.EmptyRootHash,
DiffHash: types.EmptyRootHash,
},
res: RootResponsePacket{
RequestId: 3,
Status: types.StatusImpossibleFork,
BlockNumber: 128,
BlockHash: types.EmptyRootHash,
Root: common.Hash{},
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 4,
BlockNumber: 128,
DiffHash: types.EmptyRootHash,
},
res: RootResponsePacket{
RequestId: 4,
Status: types.StatusDiffHashMismatch,
BlockNumber: 128,
Root: common.Hash{},
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 5,
BlockNumber: 1024,
},
res: RootResponsePacket{
RequestId: 5,
Status: types.StatusFullVerified,
BlockNumber: 1024,
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 6,
BlockNumber: 1024,
BlockHash: types.EmptyRootHash,
DiffHash: types.EmptyRootHash,
},
res: RootResponsePacket{
RequestId: 6,
Status: types.StatusPossibleFork,
BlockNumber: 1024,
BlockHash: types.EmptyRootHash,
Root: common.Hash{},
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 7,
BlockNumber: 1033,
BlockHash: types.EmptyRootHash,
DiffHash: types.EmptyRootHash,
},
res: RootResponsePacket{
RequestId: 7,
Status: types.StatusBlockNewer,
BlockNumber: 1033,
BlockHash: types.EmptyRootHash,
Root: common.Hash{},
Extra: defaultExtra,
},
},
{
req: RootRequestPacket{
RequestId: 8,
BlockNumber: 1044,
BlockHash: types.EmptyRootHash,
DiffHash: types.EmptyRootHash,
},
res: RootResponsePacket{
RequestId: 8,
Status: types.StatusBlockTooNew,
BlockNumber: 1044,
BlockHash: types.EmptyRootHash,
Root: common.Hash{},
Extra: defaultExtra,
},
},
}

for idx, pair := range pairs {
header := backend.Chain().GetHeaderByNumber(pair.req.BlockNumber)
if header != nil {
if pair.res.Status.Code&0xFF00 == types.StatusVerified.Code {
pair.req.BlockHash = header.Hash()
pair.req.DiffHash, _ = core.CalculateDiffHash(backend.Chain().GetTrustedDiffLayer(header.Hash()))
pair.res.BlockHash = pair.req.BlockHash
pair.res.Root = header.Root
} else if pair.res.Status.Code == types.StatusDiffHashMismatch.Code {
pair.req.BlockHash = header.Hash()
pair.res.BlockHash = pair.req.BlockHash
}
}

p2p.Send(peer.app, RequestRootMsg, pair.req)
if err := p2p.ExpectMsg(peer.app, RespondRootMsg, pair.res); err != nil {
t.Errorf("test %d: root response not expected: %v", idx, err)
}
}
}
42 changes: 42 additions & 0 deletions eth/protocols/trust/peer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package trust

import (
"math/rand"

"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
)

// testPeer is a simulated peer to allow testing direct network calls.
type testPeer struct {
*Peer

net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging
app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side
}

// newTestPeer creates a new peer registered at the given data backend.
func newTestPeer(name string, version uint, backend Backend) (*testPeer, <-chan error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()

// Start the peer on a new thread
var id enode.ID
rand.Read(id[:])

peer := NewPeer(version, p2p.NewPeer(id, name, nil), net)
errc := make(chan error, 1)
go func() {
errc <- backend.RunPeer(peer, func(peer *Peer) error {
return Handle(backend, peer)
})
}()
return &testPeer{app: app, net: net, Peer: peer}, errc
}

// close terminates the local side of the peer, notifying the remote protocol
// manager of termination.
func (p *testPeer) close() {
p.Peer.Close()
p.app.Close()
}