add: eth67 protocol

This commit is contained in:
anshalshukla 2024-09-12 16:04:41 +05:30
parent a291fa7649
commit a323609c31
No known key found for this signature in database
GPG key ID: 84BE5474523D8CBC
15 changed files with 439 additions and 237 deletions

View file

@ -167,7 +167,7 @@ func (c *Conn) ReadEth() (any, error) {
case eth.TransactionsMsg: case eth.TransactionsMsg:
msg = new(eth.TransactionsPacket) msg = new(eth.TransactionsPacket)
case eth.NewPooledTransactionHashesMsg: case eth.NewPooledTransactionHashesMsg:
msg = new(eth.NewPooledTransactionHashesPacket) msg = new(eth.NewPooledTransactionHashesPacket68)
case eth.GetPooledTransactionsMsg: case eth.GetPooledTransactionsMsg:
msg = new(eth.GetPooledTransactionsPacket) msg = new(eth.GetPooledTransactionsPacket)
case eth.PooledTransactionsMsg: case eth.PooledTransactionsMsg:

View file

@ -734,7 +734,7 @@ the transactions using a GetPooledTransactions request.`)
} }
// Send announcement. // Send announcement.
ann := eth.NewPooledTransactionHashesPacket{Types: txTypes, Sizes: sizes, Hashes: hashes} ann := eth.NewPooledTransactionHashesPacket68{Types: txTypes, Sizes: sizes, Hashes: hashes}
err = conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann) err = conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann)
if err != nil { if err != nil {
t.Fatalf("failed to write to connection: %v", err) t.Fatalf("failed to write to connection: %v", err)
@ -753,7 +753,7 @@ the transactions using a GetPooledTransactions request.`)
} }
return return
case *eth.NewPooledTransactionHashesPacket: case *eth.NewPooledTransactionHashesPacket68:
continue continue
case *eth.TransactionsPacket: case *eth.TransactionsPacket:
continue continue
@ -823,12 +823,12 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
t2 = s.makeBlobTxs(2, 3, 0x2) t2 = s.makeBlobTxs(2, 3, 0x2)
) )
for _, test := range []struct { for _, test := range []struct {
ann eth.NewPooledTransactionHashesPacket ann eth.NewPooledTransactionHashesPacket68
resp eth.PooledTransactionsResponse resp eth.PooledTransactionsResponse
}{ }{
// Invalid tx size. // Invalid tx size.
{ {
ann: eth.NewPooledTransactionHashesPacket{ ann: eth.NewPooledTransactionHashesPacket68{
Types: []byte{types.BlobTxType, types.BlobTxType}, Types: []byte{types.BlobTxType, types.BlobTxType},
Sizes: []uint32{uint32(t1[0].Size()), uint32(t1[1].Size() + 10)}, Sizes: []uint32{uint32(t1[0].Size()), uint32(t1[1].Size() + 10)},
Hashes: []common.Hash{t1[0].Hash(), t1[1].Hash()}, Hashes: []common.Hash{t1[0].Hash(), t1[1].Hash()},
@ -837,7 +837,7 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
}, },
// Wrong tx type. // Wrong tx type.
{ {
ann: eth.NewPooledTransactionHashesPacket{ ann: eth.NewPooledTransactionHashesPacket68{
Types: []byte{types.DynamicFeeTxType, types.BlobTxType}, Types: []byte{types.DynamicFeeTxType, types.BlobTxType},
Sizes: []uint32{uint32(t2[0].Size()), uint32(t2[1].Size())}, Sizes: []uint32{uint32(t2[0].Size()), uint32(t2[1].Size())},
Hashes: []common.Hash{t2[0].Hash(), t2[1].Hash()}, Hashes: []common.Hash{t2[0].Hash(), t2[1].Hash()},

View file

@ -72,7 +72,7 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
for _, tx := range *msg { for _, tx := range *msg {
got[tx.Hash()] = true got[tx.Hash()] = true
} }
case *eth.NewPooledTransactionHashesPacket: case *eth.NewPooledTransactionHashesPacket68:
for _, hash := range msg.Hashes { for _, hash := range msg.Hashes {
got[hash] = true got[hash] = true
} }
@ -157,7 +157,7 @@ func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
return fmt.Errorf("received bad tx: %s", tx.Hash()) return fmt.Errorf("received bad tx: %s", tx.Hash())
} }
} }
case *eth.NewPooledTransactionHashesPacket: case *eth.NewPooledTransactionHashesPacket68:
for _, hash := range msg.Hashes { for _, hash := range msg.Hashes {
if _, ok := invalids[hash]; ok { if _, ok := invalids[hash]; ok {
return fmt.Errorf("received bad tx: %s", hash) return fmt.Errorf("received bad tx: %s", hash)

View file

@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -480,6 +481,8 @@ func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
func TestCanonicalSynchronisation68Full(t *testing.T) { testCanonSync(t, eth.ETH68, FullSync) } func TestCanonicalSynchronisation68Full(t *testing.T) { testCanonSync(t, eth.ETH68, FullSync) }
func TestCanonicalSynchronisation68Snap(t *testing.T) { testCanonSync(t, eth.ETH68, SnapSync) } func TestCanonicalSynchronisation68Snap(t *testing.T) { testCanonSync(t, eth.ETH68, SnapSync) }
func TestCanonicalSynchronisation67Full(t *testing.T) { testCanonSync(t, eth.ETH67, FullSync) }
func TestCanonicalSynchronisation67Snap(t *testing.T) { testCanonSync(t, eth.ETH67, SnapSync) }
func testCanonSync(t *testing.T, protocol uint, mode SyncMode) { func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -501,6 +504,8 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
// until the cached blocks are retrieved. // until the cached blocks are retrieved.
func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH68, FullSync) } func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH68, FullSync) }
func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH68, SnapSync) } func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH68, SnapSync) }
func TestThrottling67Full(t *testing.T) { testThrottling(t, eth.ETH67, FullSync) }
func TestThrottling67Snap(t *testing.T) { testThrottling(t, eth.ETH67, SnapSync) }
func testThrottling(t *testing.T, protocol uint, mode SyncMode) { func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -586,6 +591,8 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
// binary search should be executed. // binary search should be executed.
func TestForkedSync68Full(t *testing.T) { testForkedSync(t, eth.ETH68, FullSync) } func TestForkedSync68Full(t *testing.T) { testForkedSync(t, eth.ETH68, FullSync) }
func TestForkedSync68Snap(t *testing.T) { testForkedSync(t, eth.ETH68, SnapSync) } func TestForkedSync68Snap(t *testing.T) { testForkedSync(t, eth.ETH68, SnapSync) }
func TestForkedSync67Full(t *testing.T) { testForkedSync(t, eth.ETH67, FullSync) }
func TestForkedSync67Snap(t *testing.T) { testForkedSync(t, eth.ETH67, SnapSync) }
func testForkedSync(t *testing.T, protocol uint, mode SyncMode) { func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -614,6 +621,8 @@ func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// currently and is not dropped. // currently and is not dropped.
func TestHeavyForkedSync68Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH68, FullSync) } func TestHeavyForkedSync68Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH68, FullSync) }
func TestHeavyForkedSync68Snap(t *testing.T) { testHeavyForkedSync(t, eth.ETH68, SnapSync) } func TestHeavyForkedSync68Snap(t *testing.T) { testHeavyForkedSync(t, eth.ETH68, SnapSync) }
func TestHeavyForkedSync67Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH67, FullSync) }
func TestHeavyForkedSync67Snap(t *testing.T) { testHeavyForkedSync(t, eth.ETH67, SnapSync) }
func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -644,6 +653,8 @@ func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// long dead chains. // long dead chains.
func TestBoundedForkedSync68Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH68, FullSync) } func TestBoundedForkedSync68Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH68, FullSync) }
func TestBoundedForkedSync68Snap(t *testing.T) { testBoundedForkedSync(t, eth.ETH68, SnapSync) } func TestBoundedForkedSync68Snap(t *testing.T) { testBoundedForkedSync(t, eth.ETH68, SnapSync) }
func TestBoundedForkedSync67Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH67, FullSync) }
func TestBoundedForkedSync67Snap(t *testing.T) { testBoundedForkedSync(t, eth.ETH67, SnapSync) }
func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) { func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -678,6 +689,15 @@ func TestBoundedHeavyForkedSync68Snap(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH68, SnapSync) testBoundedHeavyForkedSync(t, eth.ETH68, SnapSync)
} }
func TestBoundedHeavyForkedSync67Full(t *testing.T) {
t.Parallel()
testBoundedHeavyForkedSync(t, eth.ETH67, FullSync)
}
func TestBoundedHeavyForkedSync67Snap(t *testing.T) {
t.Parallel()
testBoundedHeavyForkedSync(t, eth.ETH67, SnapSync)
}
func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) { func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
defer tester.terminate() defer tester.terminate()
@ -705,6 +725,8 @@ func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that a canceled download wipes all previously accumulated state. // Tests that a canceled download wipes all previously accumulated state.
func TestCancel68Full(t *testing.T) { testCancel(t, eth.ETH68, FullSync) } func TestCancel68Full(t *testing.T) { testCancel(t, eth.ETH68, FullSync) }
func TestCancel68Snap(t *testing.T) { testCancel(t, eth.ETH68, SnapSync) } func TestCancel68Snap(t *testing.T) { testCancel(t, eth.ETH68, SnapSync) }
func TestCancel67Full(t *testing.T) { testCancel(t, eth.ETH67, FullSync) }
func TestCancel67Snap(t *testing.T) { testCancel(t, eth.ETH67, SnapSync) }
func testCancel(t *testing.T, protocol uint, mode SyncMode) { func testCancel(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -734,6 +756,8 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) {
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test). // Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
func TestMultiSynchronisation68Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH68, FullSync) } func TestMultiSynchronisation68Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH68, FullSync) }
func TestMultiSynchronisation68Snap(t *testing.T) { testMultiSynchronisation(t, eth.ETH68, SnapSync) } func TestMultiSynchronisation68Snap(t *testing.T) { testMultiSynchronisation(t, eth.ETH68, SnapSync) }
func TestMultiSynchronisation67Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH67, FullSync) }
func TestMultiSynchronisation67Snap(t *testing.T) { testMultiSynchronisation(t, eth.ETH67, SnapSync) }
func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) { func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -759,6 +783,8 @@ func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
// and not wreak havoc on other nodes in the network. // and not wreak havoc on other nodes in the network.
func TestMultiProtoSynchronisation68Full(t *testing.T) { testMultiProtoSync(t, eth.ETH68, FullSync) } func TestMultiProtoSynchronisation68Full(t *testing.T) { testMultiProtoSync(t, eth.ETH68, FullSync) }
func TestMultiProtoSynchronisation68Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH68, SnapSync) } func TestMultiProtoSynchronisation68Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH68, SnapSync) }
func TestMultiProtoSynchronisation67Full(t *testing.T) { testMultiProtoSync(t, eth.ETH67, FullSync) }
func TestMultiProtoSynchronisation67Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH67, SnapSync) }
func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) { func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -769,6 +795,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
// Create peers of every type // Create peers of every type
tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:]) tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:])
tester.newPeer("peer 67", eth.ETH67, chain.blocks[1:])
// Synchronise with the requested peer and make sure all blocks were retrieved // Synchronise with the requested peer and make sure all blocks were retrieved
if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil { if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil {
@ -778,7 +805,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
assertOwnChain(t, tester, len(chain.blocks)) assertOwnChain(t, tester, len(chain.blocks))
// Check that no peers have been dropped off // Check that no peers have been dropped off
for _, version := range []int{68} { for _, version := range []int{68, 67} {
peer := fmt.Sprintf("peer %d", version) peer := fmt.Sprintf("peer %d", version)
if _, ok := tester.peers[peer]; !ok { if _, ok := tester.peers[peer]; !ok {
t.Errorf("%s dropped", peer) t.Errorf("%s dropped", peer)
@ -790,6 +817,8 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
// made, and instead the header should be assembled into a whole block in itself. // made, and instead the header should be assembled into a whole block in itself.
func TestEmptyShortCircuit68Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, FullSync) } func TestEmptyShortCircuit68Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, FullSync) }
func TestEmptyShortCircuit68Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, SnapSync) } func TestEmptyShortCircuit68Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, SnapSync) }
func TestEmptyShortCircuit67Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH67, FullSync) }
func TestEmptyShortCircuit67Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH67, SnapSync) }
func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) { func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -819,7 +848,7 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
bodiesNeeded, receiptsNeeded := 0, 0 bodiesNeeded, receiptsNeeded := 0, 0
for _, block := range chain.blocks[1:] { for _, block := range chain.blocks[1:] {
if (mode == SnapSync || mode == FullSync) && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) { if len(block.Transactions()) > 0 || len(block.Uncles()) > 0 {
bodiesNeeded++ bodiesNeeded++
} }
} }
@ -843,6 +872,8 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
// stalling the downloader by feeding gapped header chains. // stalling the downloader by feeding gapped header chains.
func TestMissingHeaderAttack68Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH68, FullSync) } func TestMissingHeaderAttack68Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH68, FullSync) }
func TestMissingHeaderAttack68Snap(t *testing.T) { testMissingHeaderAttack(t, eth.ETH68, SnapSync) } func TestMissingHeaderAttack68Snap(t *testing.T) { testMissingHeaderAttack(t, eth.ETH68, SnapSync) }
func TestMissingHeaderAttack67Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH67, FullSync) }
func TestMissingHeaderAttack67Snap(t *testing.T) { testMissingHeaderAttack(t, eth.ETH67, SnapSync) }
func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -870,6 +901,8 @@ func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
// detects the invalid numbering. // detects the invalid numbering.
func TestShiftedHeaderAttack68Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH68, FullSync) } func TestShiftedHeaderAttack68Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH68, FullSync) }
func TestShiftedHeaderAttack68Snap(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH68, SnapSync) } func TestShiftedHeaderAttack68Snap(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH68, SnapSync) }
func TestShiftedHeaderAttack67Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH67, FullSync) }
func TestShiftedHeaderAttack67Snap(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH67, SnapSync) }
func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) { func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -903,6 +936,15 @@ func TestHighTDStarvationAttack68Snap(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH68, SnapSync) testHighTDStarvationAttack(t, eth.ETH68, SnapSync)
} }
func TestHighTDStarvationAttack67Full(t *testing.T) {
t.Parallel()
testHighTDStarvationAttack(t, eth.ETH67, FullSync)
}
func TestHighTDStarvationAttack67Snap(t *testing.T) {
t.Parallel()
testHighTDStarvationAttack(t, eth.ETH67, SnapSync)
}
func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) { func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
defer tester.terminate() defer tester.terminate()
@ -917,6 +959,7 @@ func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
// Tests that misbehaving peers are disconnected, whilst behaving ones are not. // Tests that misbehaving peers are disconnected, whilst behaving ones are not.
func TestBlockHeaderAttackerDropping68(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH68) } func TestBlockHeaderAttackerDropping68(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH68) }
func TestBlockHeaderAttackerDropping67(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH67) }
func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) { func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
// Define the disconnection requirement for individual hash fetch errors // Define the disconnection requirement for individual hash fetch errors
@ -969,6 +1012,8 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
// and highest block number) is tracked and updated correctly. // and highest block number) is tracked and updated correctly.
func TestSyncProgress68Full(t *testing.T) { testSyncProgress(t, eth.ETH68, FullSync) } func TestSyncProgress68Full(t *testing.T) { testSyncProgress(t, eth.ETH68, FullSync) }
func TestSyncProgress68Snap(t *testing.T) { testSyncProgress(t, eth.ETH68, SnapSync) } func TestSyncProgress68Snap(t *testing.T) { testSyncProgress(t, eth.ETH68, SnapSync) }
func TestSyncProgress67Full(t *testing.T) { testSyncProgress(t, eth.ETH67, FullSync) }
func TestSyncProgress67Snap(t *testing.T) { testSyncProgress(t, eth.ETH67, SnapSync) }
func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) { func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -1052,6 +1097,8 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync
// revertal). // revertal).
func TestForkedSyncProgress68Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH68, FullSync) } func TestForkedSyncProgress68Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH68, FullSync) }
func TestForkedSyncProgress68Snap(t *testing.T) { testForkedSyncProgress(t, eth.ETH68, SnapSync) } func TestForkedSyncProgress68Snap(t *testing.T) { testForkedSyncProgress(t, eth.ETH68, SnapSync) }
func TestForkedSyncProgress67Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH67, FullSync) }
func TestForkedSyncProgress67Snap(t *testing.T) { testForkedSyncProgress(t, eth.ETH67, SnapSync) }
func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -1130,6 +1177,8 @@ func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
// continuation of the previous sync and not a new instance. // continuation of the previous sync and not a new instance.
func TestFailedSyncProgress68Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH68, FullSync) } func TestFailedSyncProgress68Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH68, FullSync) }
func TestFailedSyncProgress68Snap(t *testing.T) { testFailedSyncProgress(t, eth.ETH68, SnapSync) } func TestFailedSyncProgress68Snap(t *testing.T) { testFailedSyncProgress(t, eth.ETH68, SnapSync) }
func TestFailedSyncProgress67Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH67, FullSync) }
func TestFailedSyncProgress67Snap(t *testing.T) { testFailedSyncProgress(t, eth.ETH67, SnapSync) }
func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -1203,6 +1252,8 @@ func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
// the progress height is successfully reduced at the next sync invocation. // the progress height is successfully reduced at the next sync invocation.
func TestFakedSyncProgress68Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH68, FullSync) } func TestFakedSyncProgress68Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH68, FullSync) }
func TestFakedSyncProgress68Snap(t *testing.T) { testFakedSyncProgress(t, eth.ETH68, SnapSync) } func TestFakedSyncProgress68Snap(t *testing.T) { testFakedSyncProgress(t, eth.ETH68, SnapSync) }
func TestFakedSyncProgress67Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH67, FullSync) }
func TestFakedSyncProgress67Snap(t *testing.T) { testFakedSyncProgress(t, eth.ETH67, SnapSync) }
func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) { func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t) tester := newTester(t)
@ -1367,6 +1418,8 @@ func TestRemoteHeaderRequestSpan(t *testing.T) {
// being fast-synced from, avoiding potential cheap eclipse attacks. // being fast-synced from, avoiding potential cheap eclipse attacks.
func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH68, FullSync) } func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH68, FullSync) }
func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH68, SnapSync) } func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH68, SnapSync) }
func TestBeaconSync67Full(t *testing.T) { testBeaconSync(t, eth.ETH67, FullSync) }
func TestBeaconSync67Snap(t *testing.T) { testBeaconSync(t, eth.ETH67, SnapSync) }
func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) { func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
t.Helper() t.Helper()
@ -1477,12 +1530,37 @@ func (w *whitelistFake) GetMilestoneIDsList() []string {
return nil return nil
} }
// TestFakedSyncProgress67WhitelistMatch tests if in case of whitelisted // TestFakedSyncProgress67WhitelistMismatch tests if in case of whitelisted
// checkpoint match with opposite peer, the sync should succeed. // checkpoint mismatch with opposite peer, the sync should fail.
func TestFakedSyncProgress68WhitelistMatch(t *testing.T) { func TestFakedSyncProgress67WhitelistMismatch(t *testing.T) {
t.Parallel() t.Parallel()
protocol := uint(eth.ETH68) protocol := uint(eth.ETH67)
mode := FullSync
tester := newTester(t)
validate := func(count int) (bool, error) {
return false, whitelist.ErrMismatch
}
tester.downloader.ChainValidator = newWhitelistFake(validate)
defer tester.terminate()
chainA := testChainForkLightA.blocks
tester.newPeer("light", protocol, chainA[1:])
// Synchronise with the peer and make sure all blocks were retrieved
if err := tester.sync("light", nil, mode); err == nil {
t.Fatal("succeeded attacker synchronisation")
}
}
// TestFakedSyncProgress67WhitelistMatch tests if in case of whitelisted
// checkpoint match with opposite peer, the sync should succeed.
func TestFakedSyncProgress67WhitelistMatch(t *testing.T) {
t.Parallel()
protocol := uint(eth.ETH67)
mode := FullSync mode := FullSync
tester := newTester(t) tester := newTester(t)
@ -1505,10 +1583,10 @@ func TestFakedSyncProgress68WhitelistMatch(t *testing.T) {
// TestFakedSyncProgress67NoRemoteCheckpoint tests if in case of missing/invalid // TestFakedSyncProgress67NoRemoteCheckpoint tests if in case of missing/invalid
// checkpointed blocks with opposite peer, the sync should fail initially but // checkpointed blocks with opposite peer, the sync should fail initially but
// with the retry mechanism, it should succeed eventually. // with the retry mechanism, it should succeed eventually.
func TestFakedSyncProgress68NoRemoteCheckpoint(t *testing.T) { func TestFakedSyncProgress67NoRemoteCheckpoint(t *testing.T) {
t.Parallel() t.Parallel()
protocol := uint(eth.ETH68) protocol := uint(eth.ETH67)
mode := FullSync mode := FullSync
tester := newTester(t) tester := newTester(t)
@ -1544,8 +1622,8 @@ func TestFakedSyncProgress68NoRemoteCheckpoint(t *testing.T) {
// TestFakedSyncProgress67BypassWhitelistValidation tests if peer validation // TestFakedSyncProgress67BypassWhitelistValidation tests if peer validation
// via whitelist is bypassed when remote peer is far away or not // via whitelist is bypassed when remote peer is far away or not
func TestFakedSyncProgress68BypassWhitelistValidation(t *testing.T) { func TestFakedSyncProgress67BypassWhitelistValidation(t *testing.T) {
protocol := uint(eth.ETH68) protocol := uint(eth.ETH67)
mode := FullSync mode := FullSync
tester := newTester(t) tester := newTester(t)

View file

@ -29,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -96,7 +95,7 @@ func newSkeletonTestPeer(id string, headers []*types.Header) *skeletonTestPeer {
} }
} }
// newSkeletonTestPeerWithHook creates a new mock peer to test the skeleton sync with, // newSkeletonTestPeer creates a new mock peer to test the skeleton sync with,
// and sets an optional serve hook that can return headers for delivery instead // and sets an optional serve hook that can return headers for delivery instead
// of the predefined chain. Useful for emulating malicious behavior that would // of the predefined chain. Useful for emulating malicious behavior that would
// otherwise require dedicated peer types. // otherwise require dedicated peer types.
@ -388,9 +387,23 @@ func TestSkeletonSyncInit(t *testing.T) {
_ = skeleton.Terminate() _ = skeleton.Terminate()
// Ensure the correct resulting sync status // Ensure the correct resulting sync status
expect := skeletonExpect{state: tt.newstate} var progress skeletonProgress
if err := checkSkeletonProgress(db, false, nil, expect); err != nil {
t.Errorf("test %d: %v", i, err) _ = json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
if len(progress.Subchains) != len(tt.newstate) {
t.Errorf("test %d: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.newstate))
continue
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != tt.newstate[j].Head {
t.Errorf("test %d: subchain %d head mismatch: have %d, want %d", i, j, progress.Subchains[j].Head, tt.newstate[j].Head)
}
if progress.Subchains[j].Tail != tt.newstate[j].Tail {
t.Errorf("test %d: subchain %d tail mismatch: have %d, want %d", i, j, progress.Subchains[j].Tail, tt.newstate[j].Tail)
}
} }
} }
} }
@ -496,37 +509,31 @@ func TestSkeletonSyncExtend(t *testing.T) {
skeleton.Terminate() skeleton.Terminate()
// Ensure the correct resulting sync status // Ensure the correct resulting sync status
expect := skeletonExpect{state: tt.newstate} var progress skeletonProgress
if err := checkSkeletonProgress(db, false, nil, expect); err != nil {
t.Errorf("test %d: %v", i, err) json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
if len(progress.Subchains) != len(tt.newstate) {
t.Errorf("test %d: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.newstate))
continue
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != tt.newstate[j].Head {
t.Errorf("test %d: subchain %d head mismatch: have %d, want %d", i, j, progress.Subchains[j].Head, tt.newstate[j].Head)
}
if progress.Subchains[j].Tail != tt.newstate[j].Tail {
t.Errorf("test %d: subchain %d tail mismatch: have %d, want %d", i, j, progress.Subchains[j].Tail, tt.newstate[j].Tail)
}
} }
} }
} }
type skeletonExpect struct {
state []*subchain // Expected sync state after the post-init event
serve uint64 // Expected number of header retrievals after initial cycle
drop uint64 // Expected number of peers dropped after initial cycle
}
type skeletonTest struct {
fill bool // Whether to run a real backfiller in this test case
unpredictable bool // Whether to ignore drops/serves due to uncertain packet assignments
head *types.Header // New head header to announce to reorg to
peers []*skeletonTestPeer // Initial peer set to start the sync with
mid skeletonExpect
newHead *types.Header // New header to anoint on top of the old one
newPeer *skeletonTestPeer // New peer to join the skeleton syncer
end skeletonExpect
}
// Tests that the skeleton sync correctly retrieves headers from one or more // Tests that the skeleton sync correctly retrieves headers from one or more
// peers without duplicates or other strange side effects. // peers without duplicates or other strange side effects.
func TestSkeletonSyncRetrievals(t *testing.T) { func TestSkeletonSyncRetrievals(t *testing.T) {
//log.SetDefault(log.NewLogger(log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false)))) //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
// Since skeleton headers don't need to be meaningful, beyond a parent hash // Since skeleton headers don't need to be meaningful, beyond a parent hash
// progression, create a long fake chain to test with. // progression, create a long fake chain to test with.
chain := []*types.Header{{Number: big.NewInt(0)}} chain := []*types.Header{{Number: big.NewInt(0)}}
@ -550,7 +557,23 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
Extra: []byte("B"), // force a different hash Extra: []byte("B"), // force a different hash
}) })
} }
tests := []skeletonTest{
tests := []struct {
fill bool // Whether to run a real backfiller in this test case
unpredictable bool // Whether to ignore drops/serves due to uncertain packet assignments
head *types.Header // New head header to announce to reorg to
peers []*skeletonTestPeer // Initial peer set to start the sync with
midstate []*subchain // Expected sync state after initial cycle
midserve uint64 // Expected number of header retrievals after initial cycle
middrop uint64 // Expected number of peers dropped after initial cycle
newHead *types.Header // New header to anoint on top of the old one
newPeer *skeletonTestPeer // New peer to join the skeleton syncer
endstate []*subchain // Expected sync state after the post-init event
endserve uint64 // Expected number of header retrievals after the post-init event
enddrop uint64 // Expected number of peers dropped after the post-init event
}{
// Completely empty database with only the genesis set. The sync is expected // Completely empty database with only the genesis set. The sync is expected
// to create a single subchain with the requested head. No peers however, so // to create a single subchain with the requested head. No peers however, so
// the sync should be stuck without any progression. // the sync should be stuck without any progression.
@ -558,16 +581,12 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
// When a new peer is added, it should detect the join and fill the headers // When a new peer is added, it should detect the join and fill the headers
// to the genesis block. // to the genesis block.
{ {
head: chain[len(chain)-1], head: chain[len(chain)-1],
mid: skeletonExpect{ midstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: uint64(len(chain) - 1)}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: uint64(len(chain) - 1)}},
},
newPeer: newSkeletonTestPeer("test-peer", chain), newPeer: newSkeletonTestPeer("test-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}}, endserve: uint64(len(chain) - 2), // len - head - genesis
serve: uint64(len(chain) - 2), // len - head - genesis
},
}, },
// Completely empty database with only the genesis set. The sync is expected // Completely empty database with only the genesis set. The sync is expected
// to create a single subchain with the requested head. With one valid peer, // to create a single subchain with the requested head. With one valid peer,
@ -575,18 +594,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
// //
// Adding a second peer should not have any effect. // Adding a second peer should not have any effect.
{ {
head: chain[len(chain)-1], head: chain[len(chain)-1],
peers: []*skeletonTestPeer{newSkeletonTestPeer("test-peer-1", chain)}, peers: []*skeletonTestPeer{newSkeletonTestPeer("test-peer-1", chain)},
mid: skeletonExpect{ midstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}}, midserve: uint64(len(chain) - 2), // len - head - genesis
serve: uint64(len(chain) - 2), // len - head - genesis
},
newPeer: newSkeletonTestPeer("test-peer-2", chain), newPeer: newSkeletonTestPeer("test-peer-2", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}}, endserve: uint64(len(chain) - 2), // len - head - genesis
serve: uint64(len(chain) - 2), // len - head - genesis
},
}, },
// Completely empty database with only the genesis set. The sync is expected // Completely empty database with only the genesis set. The sync is expected
// to create a single subchain with the requested head. With many valid peers, // to create a single subchain with the requested head. With many valid peers,
@ -600,16 +615,12 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
newSkeletonTestPeer("test-peer-2", chain), newSkeletonTestPeer("test-peer-2", chain),
newSkeletonTestPeer("test-peer-3", chain), newSkeletonTestPeer("test-peer-3", chain),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}}, midserve: uint64(len(chain) - 2), // len - head - genesis
serve: uint64(len(chain) - 2), // len - head - genesis
},
newPeer: newSkeletonTestPeer("test-peer-4", chain), newPeer: newSkeletonTestPeer("test-peer-4", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain) - 1), Tail: 1}}, endserve: uint64(len(chain) - 2), // len - head - genesis
serve: uint64(len(chain) - 2), // len - head - genesis
},
}, },
// This test checks if a peer tries to withhold a header - *on* the sync // This test checks if a peer tries to withhold a header - *on* the sync
// boundary - instead of sending the requested amount. The malicious short // boundary - instead of sending the requested amount. The malicious short
@ -621,18 +632,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
peers: []*skeletonTestPeer{ peers: []*skeletonTestPeer{
newSkeletonTestPeer("header-skipper", append(append(append([]*types.Header{}, chain[:99]...), nil), chain[100:]...)), newSkeletonTestPeer("header-skipper", append(append(append([]*types.Header{}, chain[:99]...), nil), chain[100:]...)),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 3, // len - head - genesis - missing
serve: requestHeaders + 101 - 3, // len - head - genesis - missing middrop: 1, // penalize shortened header deliveries
drop: 1, // penalize shortened header deliveries
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 3) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 3) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test checks if a peer tries to withhold a header - *off* the sync // This test checks if a peer tries to withhold a header - *off* the sync
// boundary - instead of sending the requested amount. The malicious short // boundary - instead of sending the requested amount. The malicious short
@ -644,18 +651,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
peers: []*skeletonTestPeer{ peers: []*skeletonTestPeer{
newSkeletonTestPeer("header-skipper", append(append(append([]*types.Header{}, chain[:50]...), nil), chain[51:]...)), newSkeletonTestPeer("header-skipper", append(append(append([]*types.Header{}, chain[:50]...), nil), chain[51:]...)),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 3, // len - head - genesis - missing
serve: requestHeaders + 101 - 3, // len - head - genesis - missing middrop: 1, // penalize shortened header deliveries
drop: 1, // penalize shortened header deliveries
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 3) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 3) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test checks if a peer tries to duplicate a header - *on* the sync // This test checks if a peer tries to duplicate a header - *on* the sync
// boundary - instead of sending the correct sequence. The malicious duped // boundary - instead of sending the correct sequence. The malicious duped
@ -667,18 +670,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
peers: []*skeletonTestPeer{ peers: []*skeletonTestPeer{
newSkeletonTestPeer("header-duper", append(append(append([]*types.Header{}, chain[:99]...), chain[98]), chain[100:]...)), newSkeletonTestPeer("header-duper", append(append(append([]*types.Header{}, chain[:99]...), chain[98]), chain[100:]...)),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 2, // len - head - genesis
serve: requestHeaders + 101 - 2, // len - head - genesis middrop: 1, // penalize invalid header sequences
drop: 1, // penalize invalid header sequences
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test checks if a peer tries to duplicate a header - *off* the sync // This test checks if a peer tries to duplicate a header - *off* the sync
// boundary - instead of sending the correct sequence. The malicious duped // boundary - instead of sending the correct sequence. The malicious duped
@ -690,18 +689,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
peers: []*skeletonTestPeer{ peers: []*skeletonTestPeer{
newSkeletonTestPeer("header-duper", append(append(append([]*types.Header{}, chain[:50]...), chain[49]), chain[51:]...)), newSkeletonTestPeer("header-duper", append(append(append([]*types.Header{}, chain[:50]...), chain[49]), chain[51:]...)),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 2, // len - head - genesis
serve: requestHeaders + 101 - 2, // len - head - genesis middrop: 1, // penalize invalid header sequences
drop: 1, // penalize invalid header sequences
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test checks if a peer tries to inject a different header - *on* // This test checks if a peer tries to inject a different header - *on*
// the sync boundary - instead of sending the correct sequence. The bad // the sync boundary - instead of sending the correct sequence. The bad
@ -724,18 +719,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
), ),
), ),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 2, // len - head - genesis
serve: requestHeaders + 101 - 2, // len - head - genesis middrop: 1, // different set of headers, drop // TODO(karalabe): maybe just diff sync?
drop: 1, // different set of headers, drop // TODO(karalabe): maybe just diff sync?
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test checks if a peer tries to inject a different header - *off* // This test checks if a peer tries to inject a different header - *off*
// the sync boundary - instead of sending the correct sequence. The bad // the sync boundary - instead of sending the correct sequence. The bad
@ -758,18 +749,14 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
), ),
), ),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: requestHeaders + 100, Tail: 100}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 100}}, midserve: requestHeaders + 101 - 2, // len - head - genesis
serve: requestHeaders + 101 - 2, // len - head - genesis middrop: 1, // different set of headers, drop
drop: 1, // different set of headers, drop
},
newPeer: newSkeletonTestPeer("good-peer", chain), newPeer: newSkeletonTestPeer("good-peer", chain),
end: skeletonExpect{ endstate: []*subchain{{Head: requestHeaders + 100, Tail: 1}},
state: []*subchain{{Head: requestHeaders + 100, Tail: 1}}, endserve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis
serve: (requestHeaders + 101 - 2) + (100 - 1), // midserve + lenrest - genesis enddrop: 1, // no new drops
drop: 1, // no new drops
},
}, },
// This test reproduces a bug caught during review (kudos to @holiman) // This test reproduces a bug caught during review (kudos to @holiman)
// where a subchain is merged with a previously interrupted one, causing // where a subchain is merged with a previously interrupted one, causing
@ -799,16 +786,12 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
return nil // Fallback to default behavior, just delayed return nil // Fallback to default behavior, just delayed
}), }),
}, },
mid: skeletonExpect{ midstate: []*subchain{{Head: 2 * requestHeaders, Tail: 1}},
state: []*subchain{{Head: 2 * requestHeaders, Tail: 1}}, midserve: 2*requestHeaders - 1, // len - head - genesis
serve: 2*requestHeaders - 1, // len - head - genesis
},
newHead: chain[2*requestHeaders+2], newHead: chain[2*requestHeaders+2],
end: skeletonExpect{ endstate: []*subchain{{Head: 2*requestHeaders + 2, Tail: 1}},
state: []*subchain{{Head: 2*requestHeaders + 2, Tail: 1}}, endserve: 4 * requestHeaders,
serve: 4 * requestHeaders,
},
}, },
// This test reproduces a bug caught by (@rjl493456442) where a skeleton // This test reproduces a bug caught by (@rjl493456442) where a skeleton
// header goes missing, causing the sync to get stuck and/or panic. // header goes missing, causing the sync to get stuck and/or panic.
@ -830,17 +813,13 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
fill: true, fill: true,
unpredictable: true, // We have good and bad peer too, bad may be dropped, test too short for certainty unpredictable: true, // We have good and bad peer too, bad may be dropped, test too short for certainty
head: chain[len(chain)/2+1], // Sync up until the sidechain common ancestor + 2 head: chain[len(chain)/2+1], // Sync up until the sidechain common ancestor + 2
peers: []*skeletonTestPeer{newSkeletonTestPeer("test-peer-oldchain", chain)}, peers: []*skeletonTestPeer{newSkeletonTestPeer("test-peer-oldchain", chain)},
mid: skeletonExpect{ midstate: []*subchain{{Head: uint64(len(chain)/2 + 1), Tail: 1}},
state: []*subchain{{Head: uint64(len(chain)/2 + 1), Tail: 1}},
},
newHead: sidechain[len(sidechain)/2+3], // Sync up until the sidechain common ancestor + 4 newHead: sidechain[len(sidechain)/2+3], // Sync up until the sidechain common ancestor + 4
newPeer: newSkeletonTestPeer("test-peer-newchain", sidechain), newPeer: newSkeletonTestPeer("test-peer-newchain", sidechain),
end: skeletonExpect{ endstate: []*subchain{{Head: uint64(len(sidechain)/2 + 3), Tail: uint64(len(chain) / 2)}},
state: []*subchain{{Head: uint64(len(sidechain)/2 + 3), Tail: uint64(len(chain) / 2)}},
},
}, },
} }
for i, tt := range tests { for i, tt := range tests {
@ -853,7 +832,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
// Create a peer set to feed headers through // Create a peer set to feed headers through
peerset := newPeerSet() peerset := newPeerSet()
for _, peer := range tt.peers { for _, peer := range tt.peers {
peerset.Register(newPeerConnection(peer.id, eth.ETH68, peer, log.New("id", peer.id))) peerset.Register(newPeerConnection(peer.id, eth.ETH67, peer, log.New("id", peer.id)))
} }
// Create a peer dropper to track malicious peers // Create a peer dropper to track malicious peers
dropped := make(map[string]int) dropped := make(map[string]int)
@ -906,84 +885,138 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
skeleton := newSkeleton(db, peerset, drop, filler) skeleton := newSkeleton(db, peerset, drop, filler)
_ = skeleton.Sync(tt.head, nil, true) _ = skeleton.Sync(tt.head, nil, true)
var progress skeletonProgress
// Wait a bit (bleah) for the initial sync loop to go to idle. This might // Wait a bit (bleah) for the initial sync loop to go to idle. This might
// be either a finish or a never-start hence why there's no event to hook. // be either a finish or a never-start hence why there's no event to hook.
check := func() error {
if len(progress.Subchains) != len(tt.midstate) {
return fmt.Errorf("test %d, mid state: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.midstate))
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != tt.midstate[j].Head {
return fmt.Errorf("test %d, mid state: subchain %d head mismatch: have %d, want %d", i, j, progress.Subchains[j].Head, tt.midstate[j].Head)
}
if progress.Subchains[j].Tail != tt.midstate[j].Tail {
return fmt.Errorf("test %d, mid state: subchain %d tail mismatch: have %d, want %d", i, j, progress.Subchains[j].Tail, tt.midstate[j].Tail)
}
}
return nil
}
waitStart := time.Now() waitStart := time.Now()
for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 { for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 {
time.Sleep(waitTime) time.Sleep(waitTime)
if err := checkSkeletonProgress(db, tt.unpredictable, tt.peers, tt.mid); err == nil { // Check the post-init end state if it matches the required results
json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
if err := check(); err == nil {
break break
} }
} }
if err := checkSkeletonProgress(db, tt.unpredictable, tt.peers, tt.mid); err != nil {
t.Errorf("test %d, mid: %v", i, err) if err := check(); err != nil {
t.Error(err)
continue continue
} }
// Apply the post-init events if there's any if !tt.unpredictable {
endpeers := tt.peers var served uint64
if tt.newPeer != nil { for _, peer := range tt.peers {
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH68, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil { served += peer.served.Load()
t.Errorf("test %d: failed to register new peer: %v", i, err) }
if served != tt.midserve {
t.Errorf("test %d, mid state: served headers mismatch: have %d, want %d", i, served, tt.midserve)
}
var drops uint64
for _, peer := range tt.peers {
drops += peer.dropped.Load()
}
if drops != tt.middrop {
t.Errorf("test %d, mid state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
} }
time.Sleep(time.Millisecond * 50) // given time for peer registration
endpeers = append(tt.peers, tt.newPeer)
} }
// Apply the post-init events if there's any
if tt.newHead != nil { if tt.newHead != nil {
skeleton.Sync(tt.newHead, nil, true) _ = skeleton.Sync(tt.newHead, nil, true)
} }
if tt.newPeer != nil {
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH67, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil {
t.Errorf("test %d: failed to register new peer: %v", i, err)
}
}
// Wait a bit (bleah) for the second sync loop to go to idle. This might // Wait a bit (bleah) for the second sync loop to go to idle. This might
// be either a finish or a never-start hence why there's no event to hook. // be either a finish or a never-start hence why there's no event to hook.
check = func() error {
if len(progress.Subchains) != len(tt.endstate) {
return fmt.Errorf("test %d, end state: subchain count mismatch: have %d, want %d", i, len(progress.Subchains), len(tt.endstate))
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != tt.endstate[j].Head {
return fmt.Errorf("test %d, end state: subchain %d head mismatch: have %d, want %d", i, j, progress.Subchains[j].Head, tt.endstate[j].Head)
}
if progress.Subchains[j].Tail != tt.endstate[j].Tail {
return fmt.Errorf("test %d, end state: subchain %d tail mismatch: have %d, want %d", i, j, progress.Subchains[j].Tail, tt.endstate[j].Tail)
}
}
return nil
}
waitStart = time.Now() waitStart = time.Now()
for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 { for waitTime := 20 * time.Millisecond; time.Since(waitStart) < 2*time.Second; waitTime = waitTime * 2 {
time.Sleep(waitTime) time.Sleep(waitTime)
if err := checkSkeletonProgress(db, tt.unpredictable, endpeers, tt.end); err == nil { // Check the post-init end state if it matches the required results
json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
if err := check(); err == nil {
break break
} }
} }
if err := checkSkeletonProgress(db, tt.unpredictable, endpeers, tt.end); err != nil {
t.Errorf("test %d, end: %v", i, err) if err := check(); err != nil {
t.Error(err)
continue continue
} }
// Check that the peers served no more headers than we actually needed // Check that the peers served no more headers than we actually needed
if !tt.unpredictable {
served := uint64(0)
for _, peer := range tt.peers {
served += peer.served.Load()
}
if tt.newPeer != nil {
served += tt.newPeer.served.Load()
}
if served != tt.endserve {
t.Errorf("test %d, end state: served headers mismatch: have %d, want %d", i, served, tt.endserve)
}
drops := uint64(0)
for _, peer := range tt.peers {
drops += peer.dropped.Load()
}
if tt.newPeer != nil {
drops += tt.newPeer.dropped.Load()
}
if drops != tt.enddrop {
t.Errorf("test %d, end state: dropped peers mismatch: have %d, want %d", i, drops, tt.middrop)
}
}
// Clean up any leftover skeleton sync resources // Clean up any leftover skeleton sync resources
skeleton.Terminate() skeleton.Terminate()
} }
} }
func checkSkeletonProgress(db ethdb.KeyValueReader, unpredictable bool, peers []*skeletonTestPeer, expected skeletonExpect) error {
var progress skeletonProgress
// Check the post-init end state if it matches the required results
json.Unmarshal(rawdb.ReadSkeletonSyncStatus(db), &progress)
if len(progress.Subchains) != len(expected.state) {
return fmt.Errorf("subchain count mismatch: have %d, want %d", len(progress.Subchains), len(expected.state))
}
for j := 0; j < len(progress.Subchains); j++ {
if progress.Subchains[j].Head != expected.state[j].Head {
return fmt.Errorf("subchain %d head mismatch: have %d, want %d", j, progress.Subchains[j].Head, expected.state[j].Head)
}
if progress.Subchains[j].Tail != expected.state[j].Tail {
return fmt.Errorf("subchain %d tail mismatch: have %d, want %d", j, progress.Subchains[j].Tail, expected.state[j].Tail)
}
}
if !unpredictable {
var served uint64
for _, peer := range peers {
served += peer.served.Load()
}
if served != expected.serve {
return fmt.Errorf("served headers mismatch: have %d, want %d", served, expected.serve)
}
var drops uint64
for _, peer := range peers {
drops += peer.dropped.Load()
}
if drops != expected.drop {
return fmt.Errorf("dropped peers mismatch: have %d, want %d", drops, expected.drop)
}
}
return nil
}

View file

@ -68,7 +68,10 @@ func (h *ethHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
case *eth.NewBlockPacket: case *eth.NewBlockPacket:
return h.handleBlockBroadcast(peer, packet.Block, packet.TD) return h.handleBlockBroadcast(peer, packet.Block, packet.TD)
case *eth.NewPooledTransactionHashesPacket: case *eth.NewPooledTransactionHashesPacket67:
return h.txFetcher.Notify(peer.ID(), nil, nil, *packet)
case *eth.NewPooledTransactionHashesPacket68:
return h.txFetcher.Notify(peer.ID(), packet.Types, packet.Sizes, packet.Hashes) return h.txFetcher.Notify(peer.ID(), packet.Types, packet.Sizes, packet.Hashes)
case *eth.TransactionsPacket: case *eth.TransactionsPacket:

View file

@ -57,7 +57,11 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
h.blockBroadcasts.Send(packet.Block) h.blockBroadcasts.Send(packet.Block)
return nil return nil
case *eth.NewPooledTransactionHashesPacket: case *eth.NewPooledTransactionHashesPacket67:
h.txAnnounces.Send(([]common.Hash)(*packet))
return nil
case *eth.NewPooledTransactionHashesPacket68:
h.txAnnounces.Send(packet.Hashes) h.txAnnounces.Send(packet.Hashes)
return nil return nil
@ -76,6 +80,7 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// Tests that peers are correctly accepted (or rejected) based on the advertised // Tests that peers are correctly accepted (or rejected) based on the advertised
// fork IDs in the protocol handshake. // fork IDs in the protocol handshake.
func TestForkIDSplit67(t *testing.T) { testForkIDSplit(t, eth.ETH67) }
func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, eth.ETH68) } func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, eth.ETH68) }
func testForkIDSplit(t *testing.T, protocol uint) { func testForkIDSplit(t *testing.T, protocol uint) {
@ -233,6 +238,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
} }
// Tests that received transactions are added to the local pool. // Tests that received transactions are added to the local pool.
func TestRecvTransactions67(t *testing.T) { testRecvTransactions(t, eth.ETH67) }
func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, eth.ETH68) } func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, eth.ETH68) }
func testRecvTransactions(t *testing.T, protocol uint) { func testRecvTransactions(t *testing.T, protocol uint) {
@ -292,6 +298,7 @@ func testRecvTransactions(t *testing.T, protocol uint) {
} }
// This test checks that pending transactions are sent. // This test checks that pending transactions are sent.
func TestSendTransactions67(t *testing.T) { testSendTransactions(t, eth.ETH67) }
func TestSendTransactions68(t *testing.T) { testSendTransactions(t, eth.ETH68) } func TestSendTransactions68(t *testing.T) { testSendTransactions(t, eth.ETH68) }
func testSendTransactions(t *testing.T, protocol uint) { func testSendTransactions(t *testing.T, protocol uint) {
@ -350,7 +357,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
seen := make(map[common.Hash]struct{}) seen := make(map[common.Hash]struct{})
for len(seen) < len(insert) { for len(seen) < len(insert) {
switch protocol { switch protocol {
case 68: case 67, 68:
select { select {
case hashes := <-anns: case hashes := <-anns:
for _, hash := range hashes { for _, hash := range hashes {
@ -376,6 +383,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
// Tests that transactions get propagated to all attached peers, either via direct // Tests that transactions get propagated to all attached peers, either via direct
// broadcasts or via announcements/retrievals. // broadcasts or via announcements/retrievals.
func TestTransactionPropagation67(t *testing.T) { testTransactionPropagation(t, eth.ETH67) }
func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, eth.ETH68) } func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, eth.ETH68) }
func testTransactionPropagation(t *testing.T, protocol uint) { func testTransactionPropagation(t *testing.T, protocol uint) {
@ -483,8 +491,8 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
defer sourcePipe.Close() defer sourcePipe.Close()
defer sinkPipe.Close() defer sinkPipe.Close()
sourcePeer := eth.NewPeer(eth.ETH68, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil) sourcePeer := eth.NewPeer(eth.ETH67, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil)
sinkPeer := eth.NewPeer(eth.ETH68, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil) sinkPeer := eth.NewPeer(eth.ETH67, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil)
defer sourcePeer.Close() defer sourcePeer.Close()
defer sinkPeer.Close() defer sinkPeer.Close()
@ -543,6 +551,7 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
// Tests that a propagated malformed block (uncles or transactions don't match // Tests that a propagated malformed block (uncles or transactions don't match
// with the hashes in the header) gets discarded and not broadcast forward. // with the hashes in the header) gets discarded and not broadcast forward.
func TestBroadcastMalformedBlock67(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH67) }
func TestBroadcastMalformedBlock68(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH68) } func TestBroadcastMalformedBlock68(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH68) }
func testBroadcastMalformedBlock(t *testing.T, protocol uint) { func testBroadcastMalformedBlock(t *testing.T, protocol uint) {

View file

@ -171,9 +171,16 @@ func (p *Peer) announceTransactions() {
if len(pending) > 0 { if len(pending) > 0 {
done = make(chan struct{}) done = make(chan struct{})
go func() { go func() {
if err := p.sendPooledTransactionHashes(pending, pendingTypes, pendingSizes); err != nil { if p.version >= ETH68 {
fail <- err if err := p.sendPooledTransactionHashes68(pending, pendingTypes, pendingSizes); err != nil {
return fail <- err
return
}
} else {
if err := p.sendPooledTransactionHashes66(pending); err != nil {
fail <- err
return
}
} }
close(done) close(done)
p.Log().Trace("Sent transaction announcements", "count", len(pending)) p.Log().Trace("Sent transaction announcements", "count", len(pending))

View file

@ -93,6 +93,10 @@ type TxPool interface {
func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol { func MakeProtocols(backend Backend, network uint64, dnsdisc enode.Iterator) []p2p.Protocol {
protocols := make([]p2p.Protocol, 0, len(ProtocolVersions)) protocols := make([]p2p.Protocol, 0, len(ProtocolVersions))
for _, version := range ProtocolVersions { for _, version := range ProtocolVersions {
// Blob transactions require eth/68 announcements, disable everything else
if version <= ETH67 && backend.Chain().Config().CancunBlock != nil {
continue
}
version := version // Closure version := version // Closure
protocols = append(protocols, p2p.Protocol{ protocols = append(protocols, p2p.Protocol{
@ -162,11 +166,26 @@ type Decoder interface {
Time() time.Time Time() time.Time
} }
var eth67 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes67,
GetBlockHeadersMsg: handleGetBlockHeaders,
BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts,
ReceiptsMsg: handleReceipts,
GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions,
}
var eth68 = map[uint64]msgHandler{ var eth68 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes, NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock, NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions, TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes, NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes68,
GetBlockHeadersMsg: handleGetBlockHeaders, GetBlockHeadersMsg: handleGetBlockHeaders,
BlockHeadersMsg: handleBlockHeaders, BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies, GetBlockBodiesMsg: handleGetBlockBodies,
@ -190,8 +209,10 @@ func handleMessage(backend Backend, peer *Peer) error {
} }
defer msg.Discard() defer msg.Discard()
var handlers = eth68 var handlers = eth67
if peer.Version() >= ETH68 {
handlers = eth68
}
// Track the amount of time it takes to serve the request and run the handler // Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled { if metrics.Enabled {
h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code) h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)

View file

@ -61,7 +61,7 @@ func newTestBackend(blocks int) *testBackend {
return newTestBackendWithGenerator(blocks, false, nil) return newTestBackendWithGenerator(blocks, false, nil)
} }
// newTestBackendWithGenerator creates a chain with a number of explicitly defined blocks and // newTestBackend creates a chain with a number of explicitly defined blocks and
// wraps it into a mock backend. // wraps it into a mock backend.
// nolint:typecheck // nolint:typecheck
func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend { func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, *core.BlockGen)) *testBackend {
@ -151,6 +151,7 @@ func (b *testBackend) Handle(*Peer, Packet) error {
} }
// Tests that block headers can be retrieved from a remote chain based on user queries. // Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeaders67(t *testing.T) { testGetBlockHeaders(t, ETH67) }
func TestGetBlockHeaders68(t *testing.T) { testGetBlockHeaders(t, ETH68) } func TestGetBlockHeaders68(t *testing.T) { testGetBlockHeaders(t, ETH68) }
func testGetBlockHeaders(t *testing.T, protocol uint) { func testGetBlockHeaders(t *testing.T, protocol uint) {
@ -338,6 +339,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
} }
// Tests that block contents can be retrieved from a remote chain based on their hashes. // Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodies67(t *testing.T) { testGetBlockBodies(t, ETH67) }
func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) } func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
func testGetBlockBodies(t *testing.T, protocol uint) { func testGetBlockBodies(t *testing.T, protocol uint) {
@ -432,6 +434,7 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
} }
// Tests that the transaction receipts can be retrieved based on hashes. // Tests that the transaction receipts can be retrieved based on hashes.
func TestGetBlockReceipts67(t *testing.T) { testGetBlockReceipts(t, ETH67) }
func TestGetBlockReceipts68(t *testing.T) { testGetBlockReceipts(t, ETH68) } func TestGetBlockReceipts68(t *testing.T) { testGetBlockReceipts(t, ETH68) }
func testGetBlockReceipts(t *testing.T, protocol uint) { func testGetBlockReceipts(t *testing.T, protocol uint) {

View file

@ -207,7 +207,7 @@ func serviceContiguousBlockHeaderQuery(chain *core.BlockChain, query *GetBlockHe
return headers return headers
} }
{ // Last mode: deliver ancestors of H { // Last mode: deliver ancestors of H
for i := uint64(1); i < count; i++ { for i := uint64(1); header != nil && i < count; i++ {
header = chain.GetHeaderByHash(header.ParentHash) header = chain.GetHeaderByHash(header.ParentHash)
if header == nil { if header == nil {
break break
@ -425,13 +425,33 @@ func handleReceipts(backend Backend, msg Decoder, peer *Peer) error {
}, metadata) }, metadata)
} }
func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer) error { func handleNewPooledTransactionHashes67(backend Backend, msg Decoder, peer *Peer) error {
// New transaction announcement arrived, make sure we have // New transaction announcement arrived, make sure we have
// a valid and fresh chain to handle them // a valid and fresh chain to handle them
if !backend.AcceptTxs() { if !backend.AcceptTxs() {
return nil return nil
} }
ann := new(NewPooledTransactionHashesPacket) ann := new(NewPooledTransactionHashesPacket67)
if err := msg.Decode(ann); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
}
// Schedule all the unknown hashes for retrieval
for _, hash := range *ann {
peer.markTransaction(hash)
}
return backend.Handle(peer, ann)
}
func handleNewPooledTransactionHashes68(backend Backend, msg Decoder, peer *Peer) error {
// New transaction announcement arrived, make sure we have
// a valid and fresh chain to handle them
if !backend.AcceptTxs() {
return nil
}
ann := new(NewPooledTransactionHashesPacket68)
if err := msg.Decode(ann); err != nil { if err := msg.Decode(ann); err != nil {
return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
} }

View file

@ -27,6 +27,7 @@ import (
) )
// Tests that handshake failures are detected and reported correctly. // Tests that handshake failures are detected and reported correctly.
func TestHandshake67(t *testing.T) { testHandshake(t, ETH67) }
func TestHandshake68(t *testing.T) { testHandshake(t, ETH68) } func TestHandshake68(t *testing.T) { testHandshake(t, ETH68) }
func testHandshake(t *testing.T, protocol uint) { func testHandshake(t *testing.T, protocol uint) {

View file

@ -56,6 +56,14 @@ const (
maxQueuedBlockAnns = 4 maxQueuedBlockAnns = 4
) )
// max is a helper function which returns the larger of the two given integers.
func max(a, b int) int {
if a > b {
return a
}
return b
}
// Peer is a collection of relevant information we have about a `eth` peer. // Peer is a collection of relevant information we have about a `eth` peer.
type Peer struct { type Peer struct {
id string // Unique ID for the peer, cached id string // Unique ID for the peer, cached
@ -84,7 +92,7 @@ type Peer struct {
lock sync.RWMutex // Mutex protecting the internal fields lock sync.RWMutex // Mutex protecting the internal fields
} }
// NewPeer creates a wrapper for a network connection and negotiated protocol // NewPeer create a wrapper for a network connection and negotiated protocol
// version. // version.
func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Peer { func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Peer {
peer := &Peer{ peer := &Peer{
@ -202,17 +210,29 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
} }
} }
// sendPooledTransactionHashes sends transaction hashes (tagged with their type // sendPooledTransactionHashes66 sends transaction hashes to the peer and includes
// them in its transaction hash set for future reference.
//
// This method is a helper used by the async transaction announcer. Don't call it
// directly as the queueing (memory) and transmission (bandwidth) costs should
// not be managed directly.
func (p *Peer) sendPooledTransactionHashes66(hashes []common.Hash) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
p.knownTxs.Add(hashes...)
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket67(hashes))
}
// sendPooledTransactionHashes68 sends transaction hashes (tagged with their type
// and size) to the peer and includes them in its transaction hash set for future // and size) to the peer and includes them in its transaction hash set for future
// reference. // reference.
// //
// This method is a helper used by the async transaction announcer. Don't call it // This method is a helper used by the async transaction announcer. Don't call it
// directly as the queueing (memory) and transmission (bandwidth) costs should // directly as the queueing (memory) and transmission (bandwidth) costs should
// not be managed directly. // not be managed directly.
func (p *Peer) sendPooledTransactionHashes(hashes []common.Hash, types []byte, sizes []uint32) error { func (p *Peer) sendPooledTransactionHashes68(hashes []common.Hash, types []byte, sizes []uint32) error {
// Mark all the transactions as known, but ensure we don't overflow our limits // Mark all the transactions as known, but ensure we don't overflow our limits
p.knownTxs.Add(hashes...) p.knownTxs.Add(hashes...)
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket{Types: types, Sizes: sizes, Hashes: hashes}) return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket68{Types: types, Sizes: sizes, Hashes: hashes})
} }
// AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually // AsyncSendPooledTransactionHashes queues a list of transactions hashes to eventually

View file

@ -30,6 +30,7 @@ import (
// Constants to match up protocol versions and messages // Constants to match up protocol versions and messages
const ( const (
ETH67 = 67
ETH68 = 68 ETH68 = 68
) )
@ -39,11 +40,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first // ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary). // is primary).
var ProtocolVersions = []uint{ETH68} var ProtocolVersions = []uint{ETH68, ETH67}
// protocolLengths are the number of implemented message corresponding to // protocolLengths are the number of implemented message corresponding to
// different protocol versions. // different protocol versions.
var protocolLengths = map[uint]uint64{ETH68: 17} var protocolLengths = map[uint]uint64{ETH68: 17, ETH67: 17}
// maxMessageSize is the maximum cap on the size of a protocol message. // maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024 const maxMessageSize = 10 * 1024 * 1024
@ -290,8 +291,11 @@ type ReceiptsRLPPacket struct {
ReceiptsRLPResponse ReceiptsRLPResponse
} }
// NewPooledTransactionHashesPacket represents a transaction announcement packet on eth/68 and newer. // NewPooledTransactionHashesPacket67 represents a transaction announcement packet on eth/67.
type NewPooledTransactionHashesPacket struct { type NewPooledTransactionHashesPacket67 []common.Hash
// NewPooledTransactionHashesPacket68 represents a transaction announcement packet on eth/68 and newer.
type NewPooledTransactionHashesPacket68 struct {
Types []byte Types []byte
Sizes []uint32 Sizes []uint32
Hashes []common.Hash Hashes []common.Hash
@ -350,8 +354,10 @@ func (*BlockBodiesResponse) Kind() byte { return BlockBodiesMsg }
func (*NewBlockPacket) Name() string { return "NewBlock" } func (*NewBlockPacket) Name() string { return "NewBlock" }
func (*NewBlockPacket) Kind() byte { return NewBlockMsg } func (*NewBlockPacket) Kind() byte { return NewBlockMsg }
func (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" } func (*NewPooledTransactionHashesPacket67) Name() string { return "NewPooledTransactionHashes" }
func (*NewPooledTransactionHashesPacket) Kind() byte { return NewPooledTransactionHashesMsg } func (*NewPooledTransactionHashesPacket67) Kind() byte { return NewPooledTransactionHashesMsg }
func (*NewPooledTransactionHashesPacket68) Name() string { return "NewPooledTransactionHashes" }
func (*NewPooledTransactionHashesPacket68) Kind() byte { return NewPooledTransactionHashesMsg }
func (*GetPooledTransactionsRequest) Name() string { return "GetPooledTransactions" } func (*GetPooledTransactionsRequest) Name() string { return "GetPooledTransactions" }
func (*GetPooledTransactionsRequest) Kind() byte { return GetPooledTransactionsMsg } func (*GetPooledTransactionsRequest) Kind() byte { return GetPooledTransactionsMsg }

View file

@ -28,6 +28,7 @@ import (
) )
// Tests that snap sync is disabled after a successful sync cycle. // Tests that snap sync is disabled after a successful sync cycle.
func TestSnapSyncDisabling67(t *testing.T) { testSnapSyncDisabling(t, eth.ETH67, snap.SNAP1) }
func TestSnapSyncDisabling68(t *testing.T) { testSnapSyncDisabling(t, eth.ETH68, snap.SNAP1) } func TestSnapSyncDisabling68(t *testing.T) { testSnapSyncDisabling(t, eth.ETH68, snap.SNAP1) }
// Tests that snap sync gets disabled as soon as a real block is successfully // Tests that snap sync gets disabled as soon as a real block is successfully