mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-13 10:21:37 +00:00
430 lines
12 KiB
Go
430 lines
12 KiB
Go
// 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/>.
|
|
|
|
// This file contains some shares testing functionality, common to multiple
|
|
// different files and modules being tested.
|
|
|
|
package eth
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/p2p"
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
"github.com/ethereum/go-ethereum/p2p/tracker"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
// 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, backend.TxPool())
|
|
errc := make(chan error, 1)
|
|
go func() {
|
|
defer app.Close()
|
|
|
|
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()
|
|
}
|
|
|
|
func TestPeerSet(t *testing.T) {
|
|
size := 5
|
|
s := newKnownCache(size)
|
|
|
|
// add 10 items
|
|
for i := 0; i < size*2; i++ {
|
|
s.Add(common.Hash{byte(i)})
|
|
}
|
|
|
|
if s.Cardinality() != size {
|
|
t.Fatalf("wrong size, expected %d but found %d", size, s.Cardinality())
|
|
}
|
|
|
|
vals := []common.Hash{}
|
|
for i := 10; i < 20; i++ {
|
|
vals = append(vals, common.Hash{byte(i)})
|
|
}
|
|
|
|
// add item in batch
|
|
s.Add(vals...)
|
|
if s.Cardinality() < size {
|
|
t.Fatalf("bad size")
|
|
}
|
|
}
|
|
|
|
func TestPartialReceipt(t *testing.T) {
|
|
gen := func(_ int, g *core.BlockGen) {
|
|
signer := types.HomesteadSigner{}
|
|
|
|
for range 4 {
|
|
tx, _ := types.SignTx(types.NewTransaction(g.TxNonce(testAddr), testAddr, big.NewInt(10), params.TxGas, g.BaseFee(), nil), signer, testKey)
|
|
g.AddTx(tx)
|
|
}
|
|
}
|
|
|
|
backend := newTestBackendWithGenerator(4, true, true, gen)
|
|
defer backend.close()
|
|
|
|
app, net := p2p.MsgPipe()
|
|
var id enode.ID
|
|
if _, err := rand.Read(id[:]); err != nil {
|
|
t.Fatalf("failed to create random peer: %v", err)
|
|
}
|
|
|
|
peer := NewPeer(ETH70, p2p.NewPeer(id, "peer", nil), net, nil)
|
|
|
|
packetCh := make(chan *GetReceiptsPacket70, 1)
|
|
go func() {
|
|
for {
|
|
msg, err := app.ReadMsg()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if msg.Code == GetReceiptsMsg {
|
|
var pkt GetReceiptsPacket70
|
|
if err := msg.Decode(&pkt); err == nil {
|
|
select {
|
|
case packetCh <- &pkt:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
msg.Discard()
|
|
}
|
|
}()
|
|
|
|
hashes := []common.Hash{
|
|
backend.chain.GetBlockByNumber(1).Hash(),
|
|
backend.chain.GetBlockByNumber(2).Hash(),
|
|
backend.chain.GetBlockByNumber(3).Hash(),
|
|
backend.chain.GetBlockByNumber(4).Hash(),
|
|
}
|
|
gasUsed := []uint64{
|
|
backend.chain.GetBlockByNumber(1).GasUsed(),
|
|
backend.chain.GetBlockByNumber(2).GasUsed(),
|
|
backend.chain.GetBlockByNumber(3).GasUsed(),
|
|
backend.chain.GetBlockByNumber(4).GasUsed(),
|
|
}
|
|
|
|
sink := make(chan *Response, 1)
|
|
req, err := peer.RequestReceipts(hashes, gasUsed, sink)
|
|
if err != nil {
|
|
t.Fatalf("RequestReceipts failed: %v", err)
|
|
}
|
|
select {
|
|
case _ = <-packetCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timeout waiting for request packet")
|
|
}
|
|
|
|
receipts := []Receipt{
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 1))},
|
|
}
|
|
logReceipts := []Receipt{
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 1))},
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 1))},
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 1))},
|
|
}
|
|
delivery := &ReceiptsPacket70{
|
|
RequestId: req.id,
|
|
LastBlockIncomplete: true,
|
|
List: encodeRL([]*ReceiptList69{
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
}),
|
|
}
|
|
|
|
tresp := tracker.Response{ID: delivery.RequestId, MsgCode: ReceiptsMsg, Size: delivery.List.Len()}
|
|
if err := peer.tracker.Fulfil(tresp); err != nil {
|
|
t.Fatalf("Tracker failed: %v", err)
|
|
}
|
|
|
|
receiptList, _ := delivery.List.Items()
|
|
if err := peer.bufferReceipts(delivery.RequestId, receiptList, delivery.LastBlockIncomplete, backend); err != nil {
|
|
t.Fatalf("first bufferReceipts failed: %v", err)
|
|
}
|
|
|
|
if err := peer.requestPartialReceipts(req.id); err != nil {
|
|
t.Fatalf("requestPartialReceipts failed: %v", err)
|
|
}
|
|
|
|
var rereq *GetReceiptsPacket70
|
|
select {
|
|
case rereq = <-packetCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timeout waiting for re-request packet")
|
|
}
|
|
|
|
buffer, ok := peer.receiptBuffer[rereq.RequestId]
|
|
if !ok {
|
|
t.Fatalf("receiptBuffer should buffer incomplete receipts")
|
|
}
|
|
if rereq.FirstBlockReceiptIndex != uint64(buffer.list[len(buffer.list)-1].items.Len()) {
|
|
t.Fatalf("unexpected FirstBlockReceiptIndex, got %d want %d", rereq.FirstBlockReceiptIndex, buffer.list[len(buffer.list)-1].items.Len())
|
|
}
|
|
|
|
delivery = &ReceiptsPacket70{
|
|
RequestId: req.id,
|
|
LastBlockIncomplete: true,
|
|
List: encodeRL([]*ReceiptList69{
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
}),
|
|
}
|
|
tresp = tracker.Response{ID: delivery.RequestId, MsgCode: ReceiptsMsg, Size: delivery.List.Len()}
|
|
if err := peer.tracker.Fulfil(tresp); err != nil {
|
|
t.Fatalf("Tracker failed: %v", err)
|
|
}
|
|
receiptLists, _ := delivery.List.Items()
|
|
if err := peer.bufferReceipts(delivery.RequestId, receiptLists, delivery.LastBlockIncomplete, backend); err != nil {
|
|
t.Fatalf("second bufferReceipts failed: %v", err)
|
|
}
|
|
|
|
if err := peer.requestPartialReceipts(req.id); err != nil {
|
|
t.Fatalf("requestPartialReceipts failed: %v", err)
|
|
}
|
|
|
|
select {
|
|
case rereq = <-packetCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timeout waiting for re-request packet")
|
|
}
|
|
|
|
buffer, ok = peer.receiptBuffer[rereq.RequestId]
|
|
if !ok {
|
|
t.Fatalf("receiptBuffer should buffer incomplete receipts")
|
|
}
|
|
if rereq.FirstBlockReceiptIndex != uint64(buffer.list[len(buffer.list)-1].items.Len()) {
|
|
t.Fatalf("unexpected FirstBlockReceiptIndex, got %d want %d", rereq.FirstBlockReceiptIndex, buffer.list[len(buffer.list)-1].items.Len())
|
|
}
|
|
if len(rereq.GetReceiptsRequest) != 3 {
|
|
t.Fatalf("wrong partial request range, got %d want %d", len(rereq.GetReceiptsRequest), 3)
|
|
}
|
|
|
|
delivery = &ReceiptsPacket70{
|
|
RequestId: rereq.RequestId,
|
|
LastBlockIncomplete: false,
|
|
List: encodeRL([]*ReceiptList69{
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
{
|
|
items: encodeRL(receipts),
|
|
},
|
|
}),
|
|
}
|
|
|
|
tresp = tracker.Response{ID: delivery.RequestId, MsgCode: ReceiptsMsg, Size: delivery.List.Len()}
|
|
if err := peer.tracker.Fulfil(tresp); err != nil {
|
|
t.Fatalf("Tracker failed: %v", err)
|
|
}
|
|
receiptList, _ = delivery.List.Items()
|
|
if err := peer.bufferReceipts(delivery.RequestId, receiptList, delivery.LastBlockIncomplete, backend); err != nil {
|
|
t.Fatalf("third bufferReceipts failed: %v", err)
|
|
}
|
|
merged := peer.flushReceipts(rereq.RequestId)
|
|
if merged == nil {
|
|
t.Fatalf("flushReceipts should return merged receipt lists")
|
|
}
|
|
if _, ok := peer.receiptBuffer[rereq.RequestId]; ok {
|
|
t.Fatalf("receiptBuffer should be cleared after flush")
|
|
}
|
|
for i, list := range merged {
|
|
if i == 1 {
|
|
if list.items.Len() != len(logReceipts) {
|
|
t.Fatalf("wrong response buffering, got %d want %d", list.items.Len(), len(logReceipts))
|
|
}
|
|
} else {
|
|
if list.items.Len() != len(receipts) {
|
|
t.Fatalf("wrong response buffering, got %d want %d", list.items.Len(), len(receipts))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPartialReceiptFailure(t *testing.T) {
|
|
gen := func(_ int, g *core.BlockGen) {
|
|
signer := types.HomesteadSigner{}
|
|
|
|
for range 4 {
|
|
tx, _ := types.SignTx(types.NewTransaction(g.TxNonce(testAddr), testAddr, big.NewInt(10), params.TxGas, g.BaseFee(), nil), signer, testKey)
|
|
g.AddTx(tx)
|
|
}
|
|
}
|
|
|
|
backend := newTestBackendWithGenerator(4, true, true, gen)
|
|
defer backend.close()
|
|
|
|
app, net := p2p.MsgPipe()
|
|
var id enode.ID
|
|
if _, err := rand.Read(id[:]); err != nil {
|
|
t.Fatalf("failed to create random peer: %v", err)
|
|
}
|
|
|
|
peer := NewPeer(ETH70, p2p.NewPeer(id, "peer", nil), net, nil)
|
|
|
|
packetCh := make(chan *GetReceiptsPacket70, 1)
|
|
go func() {
|
|
for {
|
|
msg, err := app.ReadMsg()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if msg.Code == GetReceiptsMsg {
|
|
var pkt GetReceiptsPacket70
|
|
if err := msg.Decode(&pkt); err == nil {
|
|
select {
|
|
case packetCh <- &pkt:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
msg.Discard()
|
|
}
|
|
}()
|
|
|
|
// If a peer delivers response which is never requested, it should fail the validation
|
|
delivery := &ReceiptsPacket70{
|
|
RequestId: 66,
|
|
LastBlockIncomplete: true,
|
|
List: encodeRL([]*ReceiptList69{
|
|
{
|
|
items: encodeRL([]Receipt{
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 1))},
|
|
}),
|
|
},
|
|
{
|
|
items: encodeRL([]Receipt{
|
|
{GasUsed: 21_000, Logs: rlp.RawValue(make([]byte, 2))},
|
|
}),
|
|
},
|
|
}),
|
|
}
|
|
receiptList, _ := delivery.List.Items()
|
|
err := peer.bufferReceipts(delivery.RequestId, receiptList, delivery.LastBlockIncomplete, backend)
|
|
if err == nil {
|
|
t.Fatal("Unknown response should be dropped")
|
|
}
|
|
|
|
// If a peer deliverse excessive amount of receipts, it should also fail the validation
|
|
hashes := []common.Hash{
|
|
backend.chain.GetBlockByNumber(1).Hash(),
|
|
backend.chain.GetBlockByNumber(2).Hash(),
|
|
backend.chain.GetBlockByNumber(3).Hash(),
|
|
backend.chain.GetBlockByNumber(4).Hash(),
|
|
}
|
|
gasUsed := []uint64{
|
|
backend.chain.GetBlockByNumber(1).GasUsed(),
|
|
backend.chain.GetBlockByNumber(2).GasUsed(),
|
|
backend.chain.GetBlockByNumber(3).GasUsed(),
|
|
backend.chain.GetBlockByNumber(4).GasUsed(),
|
|
}
|
|
|
|
// Case 1 ) The number of receipts exceeds maximum tx count
|
|
sink := make(chan *Response, 1)
|
|
req, err := peer.RequestReceipts(hashes, gasUsed, sink)
|
|
if err != nil {
|
|
t.Fatalf("RequestReceipts failed: %v", err)
|
|
}
|
|
select {
|
|
case _ = <-packetCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timeout waiting for request packet")
|
|
}
|
|
|
|
maxTxCount := backend.chain.GetBlockByNumber(1).GasUsed() / 21_000
|
|
excessiveReceipts := []Receipt{{Logs: rlp.RawValue(make([]byte, 1))}}
|
|
for range maxTxCount {
|
|
excessiveReceipts = append(excessiveReceipts, Receipt{Logs: rlp.RawValue(make([]byte, 1))})
|
|
}
|
|
delivery = &ReceiptsPacket70{
|
|
RequestId: req.id,
|
|
LastBlockIncomplete: true,
|
|
List: encodeRL([]*ReceiptList69{{
|
|
items: encodeRL(excessiveReceipts),
|
|
}}),
|
|
}
|
|
receiptList, _ = delivery.List.Items()
|
|
err = peer.bufferReceipts(delivery.RequestId, receiptList, delivery.LastBlockIncomplete, backend)
|
|
if err == nil {
|
|
t.Fatal("Response with the excessive number of receipts should fail the validation")
|
|
}
|
|
|
|
// Case 2 ) Total receipt size exceeds the block gas limit
|
|
req, err = peer.RequestReceipts(hashes, gasUsed, sink)
|
|
if err != nil {
|
|
t.Fatalf("RequestReceipts failed: %v", err)
|
|
}
|
|
select {
|
|
case _ = <-packetCh:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("timeout waiting for request packet")
|
|
}
|
|
maxReceiptSize := backend.chain.GetBlockByNumber(1).GasUsed() / params.LogDataGas
|
|
delivery = &ReceiptsPacket70{
|
|
RequestId: req.id,
|
|
LastBlockIncomplete: true,
|
|
List: encodeRL([]*ReceiptList69{{
|
|
items: encodeRL([]Receipt{
|
|
{Logs: rlp.RawValue(make([]byte, maxReceiptSize+1))},
|
|
}),
|
|
}}),
|
|
}
|
|
receiptList, _ = delivery.List.Items()
|
|
err = peer.bufferReceipts(delivery.RequestId, receiptList, delivery.LastBlockIncomplete, backend)
|
|
if err == nil {
|
|
t.Fatal("Response with the large log size should fail the validation")
|
|
}
|
|
}
|