package main
import (
"crypto/rand"
"fmt"
"github.com/ava-labs/coreth"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/eth"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/go-ethereum/common"
"github.com/ava-labs/go-ethereum/common/hexutil"
"github.com/ava-labs/go-ethereum/log"
"github.com/ava-labs/go-ethereum/rlp"
"math/big"
"sync"
)
func checkError(err error) {
if err != nil {
panic(err)
}
}
type TestChain struct {
hasBlock map[common.Hash]struct{}
blocks []common.Hash
blkCount uint32
chain *coreth.ETHChain
parentBlock common.Hash
outBlockCh chan<- []byte
blockWait sync.WaitGroup
}
func (tc *TestChain) insertBlock(block *types.Block) {
if _, ok := tc.hasBlock[block.Hash()]; !ok {
tc.hasBlock[block.Hash()] = struct{}{}
tc.blocks = append(tc.blocks, block.Hash())
}
}
func NewTestChain(name string, config *eth.Config,
inBlockCh <-chan []byte, outBlockCh chan<- []byte,
inAckCh <-chan struct{}, outAckCh chan<- struct{}) *TestChain {
tc := &TestChain{
hasBlock: make(map[common.Hash]struct{}),
blocks: make([]common.Hash, 0),
blkCount: 0,
chain: coreth.NewETHChain(config, nil, nil, nil),
outBlockCh: outBlockCh,
}
tc.insertBlock(tc.chain.GetGenesisBlock())
tc.chain.SetOnHeaderNew(func(header *types.Header) {
hid := make([]byte, 32)
_, err := rand.Read(hid)
if err != nil {
panic("cannot generate hid")
}
header.Extra = append(header.Extra, hid...)
})
tc.chain.SetOnSealFinish(func(block *types.Block) error {
blkID := tc.blkCount
tc.blkCount++
if len(block.Uncles()) != 0 {
panic("#uncles should be zero")
}
if tc.parentBlock != block.ParentHash() {
panic("mismatching parent hash")
}
log.Info(fmt.Sprintf("%s: create %s <= (%d = %s)",
name, tc.parentBlock.Hex(), blkID, block.Hash().Hex()))
tc.insertBlock(block)
if tc.outBlockCh != nil {
serialized, err := rlp.EncodeToBytes(block)
if err != nil {
panic(err)
}
tc.outBlockCh <- serialized
<-inAckCh
log.Info(fmt.Sprintf("%s: got ack", name))
}
tc.blockWait.Done()
return nil
})
go func() {
for {
select {
case serialized := <-inBlockCh:
block := new(types.Block)
err := rlp.DecodeBytes(serialized, block)
if err != nil {
panic(err)
}
if !tc.chain.VerifyBlock(block) {
panic("invalid block")
}
tc.chain.InsertChain([]*types.Block{block})
tc.insertBlock(block)
log.Info(fmt.Sprintf("%s: got block %s, sending ack", name, block.Hash().Hex()))
outAckCh <- struct{}{}
}
}
}()
return tc
}
func (tc *TestChain) Start() {
tc.chain.Start()
}
func (tc *TestChain) Stop() {
tc.chain.Stop()
}
func (tc *TestChain) GenRandomTree(n int, max int) {
for i := 0; i < n; i++ {
nblocks := len(tc.blocks)
m := max
if m < 0 || nblocks < m {
m = nblocks
}
pb, _ := rand.Int(rand.Reader, big.NewInt((int64)(m)))
pn := pb.Int64()
tc.parentBlock = tc.blocks[nblocks-1-(int)(pn)]
tc.chain.SetTail(tc.parentBlock)
tc.blockWait.Add(1)
tc.chain.GenBlock()
tc.blockWait.Wait()
}
}
func run(config *eth.Config, a1, a2, b1, b2 int) {
aliceBlk := make(chan []byte)
bobBlk := make(chan []byte)
aliceAck := make(chan struct{})
bobAck := make(chan struct{})
alice := NewTestChain("alice", config, bobBlk, aliceBlk, bobAck, aliceAck)
bob := NewTestChain("bob", config, aliceBlk, bobBlk, aliceAck, bobAck)
alice.Start()
bob.Start()
alice.GenRandomTree(