|
|
@@ -20,6 +20,7 @@ import (
|
|
|
"math/big"
|
|
|
"math/rand"
|
|
|
"testing"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
"github.com/ethereum/go-ethereum/core"
|
|
|
@@ -28,6 +29,7 @@ import (
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
|
+ "github.com/ethereum/go-ethereum/event"
|
|
|
"github.com/ethereum/go-ethereum/p2p"
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
)
|
|
|
@@ -580,3 +582,74 @@ func testGetReceipt(t *testing.T, protocol int) {
|
|
|
t.Errorf("receipts mismatch: %v", err)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// Tests that post eth protocol handshake, DAO fork-enabled clients also execute
|
|
|
+// a DAO "challenge" verifying each others' DAO fork headers to ensure they're on
|
|
|
+// compatible chains.
|
|
|
+func TestDAOChallengeNoVsNo(t *testing.T) { testDAOChallenge(t, false, false, false) }
|
|
|
+func TestDAOChallengeNoVsPro(t *testing.T) { testDAOChallenge(t, false, true, false) }
|
|
|
+func TestDAOChallengeProVsNo(t *testing.T) { testDAOChallenge(t, true, false, false) }
|
|
|
+func TestDAOChallengeProVsPro(t *testing.T) { testDAOChallenge(t, true, true, false) }
|
|
|
+func TestDAOChallengeNoVsTimeout(t *testing.T) { testDAOChallenge(t, false, false, true) }
|
|
|
+func TestDAOChallengeProVsTimeout(t *testing.T) { testDAOChallenge(t, true, true, true) }
|
|
|
+
|
|
|
+func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool) {
|
|
|
+ // Reduce the DAO handshake challenge timeout
|
|
|
+ if timeout {
|
|
|
+ defer func(old time.Duration) { daoChallengeTimeout = old }(daoChallengeTimeout)
|
|
|
+ daoChallengeTimeout = 500 * time.Millisecond
|
|
|
+ }
|
|
|
+ // Create a DAO aware protocol manager
|
|
|
+ var (
|
|
|
+ evmux = new(event.TypeMux)
|
|
|
+ pow = new(core.FakePow)
|
|
|
+ db, _ = ethdb.NewMemDatabase()
|
|
|
+ genesis = core.WriteGenesisBlockForTesting(db)
|
|
|
+ config = &core.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
|
|
+ blockchain, _ = core.NewBlockChain(db, config, pow, evmux)
|
|
|
+ )
|
|
|
+ pm, err := NewProtocolManager(config, false, NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("failed to start test protocol manager: %v", err)
|
|
|
+ }
|
|
|
+ pm.Start()
|
|
|
+ defer pm.Stop()
|
|
|
+
|
|
|
+ // Connect a new peer and check that we receive the DAO challenge
|
|
|
+ peer, _ := newTestPeer("peer", eth63, pm, true)
|
|
|
+ defer peer.close()
|
|
|
+
|
|
|
+ challenge := &getBlockHeadersData{
|
|
|
+ Origin: hashOrNumber{Number: config.DAOForkBlock.Uint64()},
|
|
|
+ Amount: 1,
|
|
|
+ Skip: 0,
|
|
|
+ Reverse: false,
|
|
|
+ }
|
|
|
+ if err := p2p.ExpectMsg(peer.app, GetBlockHeadersMsg, challenge); err != nil {
|
|
|
+ t.Fatalf("challenge mismatch: %v", err)
|
|
|
+ }
|
|
|
+ // Create a block to reply to the challenge if no timeout is simualted
|
|
|
+ if !timeout {
|
|
|
+ blocks, _ := core.GenerateChain(genesis, db, 1, func(i int, block *core.BlockGen) {
|
|
|
+ if remoteForked {
|
|
|
+ block.SetExtra(params.DAOForkBlockExtra)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if err := p2p.Send(peer.app, BlockHeadersMsg, []*types.Header{blocks[0].Header()}); err != nil {
|
|
|
+ t.Fatalf("failed to answer challenge: %v", err)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Otherwise wait until the test timeout passes
|
|
|
+ time.Sleep(daoChallengeTimeout + 500*time.Millisecond)
|
|
|
+ }
|
|
|
+ // Verify that depending on fork side, the remote peer is maintained or dropped
|
|
|
+ if localForked == remoteForked && !timeout {
|
|
|
+ if peers := pm.peers.Len(); peers != 1 {
|
|
|
+ t.Fatalf("peer count mismatch: have %d, want %d", peers, 1)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if peers := pm.peers.Len(); peers != 0 {
|
|
|
+ t.Fatalf("peer count mismatch: have %d, want %d", peers, 0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|