|
@@ -1,19 +1,29 @@
|
|
|
|
|
+// Copyright 2020 The go-ethereum Authors
|
|
|
|
|
+// This file is part of the go-ethereum library.
|
|
|
|
|
+//
|
|
|
|
|
+// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
|
|
|
+// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
|
+// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
+// (at your option) any later version.
|
|
|
|
|
+//
|
|
|
|
|
+// The go-ethereum library is distributed in the hope that it will be useful,
|
|
|
|
|
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
+// GNU Lesser General Public License for more details.
|
|
|
|
|
+//
|
|
|
|
|
+// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
+
|
|
|
package ethtest
|
|
package ethtest
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
- "crypto/ecdsa"
|
|
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"net"
|
|
"net"
|
|
|
- "reflect"
|
|
|
|
|
- "time"
|
|
|
|
|
|
|
|
|
|
- "github.com/ethereum/go-ethereum/core/types"
|
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
|
"github.com/ethereum/go-ethereum/internal/utesting"
|
|
|
- "github.com/ethereum/go-ethereum/p2p"
|
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
|
"github.com/ethereum/go-ethereum/p2p/rlpx"
|
|
|
- "github.com/ethereum/go-ethereum/rlp"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/assert"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
@@ -26,137 +36,6 @@ type Suite struct {
|
|
|
fullChain *Chain
|
|
fullChain *Chain
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-type Conn struct {
|
|
|
|
|
- *rlpx.Conn
|
|
|
|
|
- ourKey *ecdsa.PrivateKey
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (c *Conn) Read() Message {
|
|
|
|
|
- code, rawData, _, err := c.Conn.Read()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return &Error{fmt.Errorf("could not read from connection: %v", err)}
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- var msg Message
|
|
|
|
|
- switch int(code) {
|
|
|
|
|
- case (Hello{}).Code():
|
|
|
|
|
- msg = new(Hello)
|
|
|
|
|
- case (Disconnect{}).Code():
|
|
|
|
|
- msg = new(Disconnect)
|
|
|
|
|
- case (Status{}).Code():
|
|
|
|
|
- msg = new(Status)
|
|
|
|
|
- case (GetBlockHeaders{}).Code():
|
|
|
|
|
- msg = new(GetBlockHeaders)
|
|
|
|
|
- case (BlockHeaders{}).Code():
|
|
|
|
|
- msg = new(BlockHeaders)
|
|
|
|
|
- case (GetBlockBodies{}).Code():
|
|
|
|
|
- msg = new(GetBlockBodies)
|
|
|
|
|
- case (BlockBodies{}).Code():
|
|
|
|
|
- msg = new(BlockBodies)
|
|
|
|
|
- case (NewBlock{}).Code():
|
|
|
|
|
- msg = new(NewBlock)
|
|
|
|
|
- case (NewBlockHashes{}).Code():
|
|
|
|
|
- msg = new(NewBlockHashes)
|
|
|
|
|
- default:
|
|
|
|
|
- return &Error{fmt.Errorf("invalid message code: %d", code)}
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if err := rlp.DecodeBytes(rawData, msg); err != nil {
|
|
|
|
|
- return &Error{fmt.Errorf("could not rlp decode message: %v", err)}
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return msg
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-func (c *Conn) Write(msg Message) error {
|
|
|
|
|
- payload, err := rlp.EncodeToBytes(msg)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
- _, err = c.Conn.Write(uint64(msg.Code()), payload)
|
|
|
|
|
- return err
|
|
|
|
|
-
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// handshake checks to make sure a `HELLO` is received.
|
|
|
|
|
-func (c *Conn) handshake(t *utesting.T) Message {
|
|
|
|
|
- // write protoHandshake to client
|
|
|
|
|
- pub0 := crypto.FromECDSAPub(&c.ourKey.PublicKey)[1:]
|
|
|
|
|
- ourHandshake := &Hello{
|
|
|
|
|
- Version: 5,
|
|
|
|
|
- Caps: []p2p.Cap{{Name: "eth", Version: 64}, {Name: "eth", Version: 65}},
|
|
|
|
|
- ID: pub0,
|
|
|
|
|
- }
|
|
|
|
|
- if err := c.Write(ourHandshake); err != nil {
|
|
|
|
|
- t.Fatalf("could not write to connection: %v", err)
|
|
|
|
|
- }
|
|
|
|
|
- // read protoHandshake from client
|
|
|
|
|
- switch msg := c.Read().(type) {
|
|
|
|
|
- case *Hello:
|
|
|
|
|
- return msg
|
|
|
|
|
- default:
|
|
|
|
|
- t.Fatalf("bad handshake: %v", msg)
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// statusExchange performs a `Status` message exchange with the given
|
|
|
|
|
-// node.
|
|
|
|
|
-func (c *Conn) statusExchange(t *utesting.T, chain *Chain) Message {
|
|
|
|
|
- // read status message from client
|
|
|
|
|
- var message Message
|
|
|
|
|
- switch msg := c.Read().(type) {
|
|
|
|
|
- case *Status:
|
|
|
|
|
- if msg.Head != chain.blocks[chain.Len()-1].Hash() {
|
|
|
|
|
- t.Fatalf("wrong head in status: %v", msg.Head)
|
|
|
|
|
- }
|
|
|
|
|
- if msg.TD.Cmp(chain.TD(chain.Len())) != 0 {
|
|
|
|
|
- t.Fatalf("wrong TD in status: %v", msg.TD)
|
|
|
|
|
- }
|
|
|
|
|
- if !reflect.DeepEqual(msg.ForkID, chain.ForkID()) {
|
|
|
|
|
- t.Fatalf("wrong fork ID in status: %v", msg.ForkID)
|
|
|
|
|
- }
|
|
|
|
|
- message = msg
|
|
|
|
|
- default:
|
|
|
|
|
- t.Fatalf("bad status message: %v", msg)
|
|
|
|
|
- }
|
|
|
|
|
- // write status message to client
|
|
|
|
|
- status := Status{
|
|
|
|
|
- ProtocolVersion: 64,
|
|
|
|
|
- NetworkID: 1,
|
|
|
|
|
- TD: chain.TD(chain.Len()),
|
|
|
|
|
- Head: chain.blocks[chain.Len()-1].Hash(),
|
|
|
|
|
- Genesis: chain.blocks[0].Hash(),
|
|
|
|
|
- ForkID: chain.ForkID(),
|
|
|
|
|
- }
|
|
|
|
|
- if err := c.Write(status); err != nil {
|
|
|
|
|
- t.Fatalf("could not write to connection: %v", err)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return message
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// waitForBlock waits for confirmation from the client that it has
|
|
|
|
|
-// imported the given block.
|
|
|
|
|
-func (c *Conn) waitForBlock(block *types.Block) error {
|
|
|
|
|
- for {
|
|
|
|
|
- req := &GetBlockHeaders{Origin: hashOrNumber{Hash: block.Hash()}, Amount: 1}
|
|
|
|
|
- if err := c.Write(req); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- switch msg := c.Read().(type) {
|
|
|
|
|
- case *BlockHeaders:
|
|
|
|
|
- if len(*msg) > 0 {
|
|
|
|
|
- return nil
|
|
|
|
|
- }
|
|
|
|
|
- time.Sleep(100 * time.Millisecond)
|
|
|
|
|
- default:
|
|
|
|
|
- return fmt.Errorf("invalid message: %v", msg)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// NewSuite creates and returns a new eth-test suite that can
|
|
// NewSuite creates and returns a new eth-test suite that can
|
|
|
// be used to test the given node against the given blockchain
|
|
// be used to test the given node against the given blockchain
|
|
|
// data.
|
|
// data.
|
|
@@ -196,7 +75,7 @@ func (s *Suite) TestStatus(t *utesting.T) {
|
|
|
case *Status:
|
|
case *Status:
|
|
|
t.Logf("%+v\n", msg)
|
|
t.Logf("%+v\n", msg)
|
|
|
default:
|
|
default:
|
|
|
- t.Fatalf("error: %v", msg)
|
|
|
|
|
|
|
+ t.Fatalf("unexpected: %#v", msg)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -225,7 +104,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- switch msg := conn.Read().(type) {
|
|
|
|
|
|
|
+ switch msg := conn.ReadAndServe(s.chain).(type) {
|
|
|
case *BlockHeaders:
|
|
case *BlockHeaders:
|
|
|
headers := msg
|
|
headers := msg
|
|
|
for _, header := range *headers {
|
|
for _, header := range *headers {
|
|
@@ -234,7 +113,7 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
|
|
t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header)
|
|
t.Logf("\nHEADER FOR BLOCK NUMBER %d: %+v\n", header.Number, header)
|
|
|
}
|
|
}
|
|
|
default:
|
|
default:
|
|
|
- t.Fatalf("error: %v", msg)
|
|
|
|
|
|
|
+ t.Fatalf("unexpected: %#v", msg)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -254,14 +133,14 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- switch msg := conn.Read().(type) {
|
|
|
|
|
|
|
+ switch msg := conn.ReadAndServe(s.chain).(type) {
|
|
|
case *BlockBodies:
|
|
case *BlockBodies:
|
|
|
bodies := msg
|
|
bodies := msg
|
|
|
for _, body := range *bodies {
|
|
for _, body := range *bodies {
|
|
|
t.Logf("\nBODY: %+v\n", body)
|
|
t.Logf("\nBODY: %+v\n", body)
|
|
|
}
|
|
}
|
|
|
default:
|
|
default:
|
|
|
- t.Fatalf("error: %v", msg)
|
|
|
|
|
|
|
+ t.Fatalf("unexpected: %#v", msg)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -294,7 +173,7 @@ func (s *Suite) TestBroadcast(t *utesting.T) {
|
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
t.Fatalf("could not write to connection: %v", err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- switch msg := receiveConn.Read().(type) {
|
|
|
|
|
|
|
+ switch msg := receiveConn.ReadAndServe(s.chain).(type) {
|
|
|
case *NewBlock:
|
|
case *NewBlock:
|
|
|
assert.Equal(t, blockAnnouncement.Block.Header(), msg.Block.Header(),
|
|
assert.Equal(t, blockAnnouncement.Block.Header(), msg.Block.Header(),
|
|
|
"wrong block header in announcement")
|
|
"wrong block header in announcement")
|
|
@@ -305,7 +184,7 @@ func (s *Suite) TestBroadcast(t *utesting.T) {
|
|
|
assert.Equal(t, blockAnnouncement.Block.Hash(), hashes[0].Hash,
|
|
assert.Equal(t, blockAnnouncement.Block.Hash(), hashes[0].Hash,
|
|
|
"wrong block hash in announcement")
|
|
"wrong block hash in announcement")
|
|
|
default:
|
|
default:
|
|
|
- t.Fatal(msg)
|
|
|
|
|
|
|
+ t.Fatalf("unexpected: %#v", msg)
|
|
|
}
|
|
}
|
|
|
// update test suite chain
|
|
// update test suite chain
|
|
|
s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000])
|
|
s.chain.blocks = append(s.chain.blocks, s.fullChain.blocks[1000])
|