Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions core/forkid/forkid.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ type ID struct {
Next uint64 // Block number of the next upcoming fork, or 0 if no forks are known
}

// Filter is a fork id filter to validate a remotely advertised ID.
type Filter func(id ID) error

// NewID calculates the XDC fork ID from the chain config, genesis hash, head.
func NewID(config *params.ChainConfig, genesis *types.Block, head uint64) ID {
// Calculate the starting checksum from the genesis hash
Expand All @@ -79,7 +82,7 @@ func NewID(config *params.ChainConfig, genesis *types.Block, head uint64) ID {

// NewFilter creates a filter that returns if a fork ID should be rejected or not
// based on the local chain's status.
func NewFilter(chain Blockchain) func(id ID) error {
func NewFilter(chain Blockchain) Filter {
return newFilter(
chain.Config(),
chain.Genesis().Hash(),
Expand Down Expand Up @@ -136,7 +139,7 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui
for i, fork := range forks {
// If our head is beyond this fork, continue to the next (we have a dummy
// fork of maxuint64 as the last item to always fail this check eventually).
if head > fork {
if head >= fork {
continue
}
// Found the first unpassed fork block, check if our current state matches
Expand Down
7 changes: 5 additions & 2 deletions eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/forkid"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/eth/bft"
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
Expand Down Expand Up @@ -61,7 +62,8 @@ func errResp(code errCode, format string, v ...interface{}) error {
}

type ProtocolManager struct {
networkId uint64
networkId uint64
forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node

snapSync uint32 // Flag whether snap sync is enabled (gets disabled if we already have blocks)
acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
Expand Down Expand Up @@ -122,6 +124,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
// Create the protocol manager with the base fields
manager := &ProtocolManager{
networkId: networkID,
forkFilter: forkid.NewFilter(blockchain),
eventMux: mux,
txpool: txpool,
blockchain: blockchain,
Expand Down Expand Up @@ -343,7 +346,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
number = head.Number.Uint64()
td = pm.blockchain.GetTd(hash, number)
)
if err := p.Handshake(pm.networkId, td, hash, genesis.Hash()); err != nil {
if err := p.Handshake(pm.networkId, td, hash, genesis.Hash(), forkid.NewID(pm.blockchain.Config(), genesis, number), pm.forkFilter); err != nil {
p.Log().Debug("Ethereum handshake failed", "err", err)
return err
}
Expand Down
18 changes: 8 additions & 10 deletions eth/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ import (
)

// Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }

// TestGetBlockHeaders63 tests get block headers 63.
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func TestGetBlockHeaders164(t *testing.T) { testGetBlockHeaders(t, 164) }

func testGetBlockHeaders(t *testing.T, protocol int) {
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
Expand Down Expand Up @@ -200,10 +198,8 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
}

// Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }

// TestGetBlockBodies63 tests get block bodies 63.
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func TestGetBlockBodies164(t *testing.T) { testGetBlockBodies(t, 164) }

func testGetBlockBodies(t *testing.T, protocol int) {
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
Expand Down Expand Up @@ -274,7 +270,8 @@ func testGetBlockBodies(t *testing.T, protocol int) {
}

// Tests that the node state database can be retrieved based on hashes.
func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
func TestGetNodeData63(t *testing.T) { testGetNodeData(t, 63) }
func TestGetNodeData164(t *testing.T) { testGetNodeData(t, 164) }

func testGetNodeData(t *testing.T, protocol int) {
// Define three accounts to simulate transactions with
Expand Down Expand Up @@ -372,7 +369,8 @@ func testGetNodeData(t *testing.T, protocol int) {
}

// Tests that the transaction receipts can be retrieved based on hashes.
func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
func TestGetReceipt63(t *testing.T) { testGetReceipt(t, 63) }
func TestGetReceipt164(t *testing.T) { testGetReceipt(t, 164) }

func testGetReceipt(t *testing.T, protocol int) {
// Define three accounts to simulate transactions with
Expand Down
33 changes: 25 additions & 8 deletions eth/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package eth
import (
"crypto/ecdsa"
"crypto/rand"
"fmt"
"math/big"
"sort"
"sync"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/forkid"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/txpool"
"github.com/XinFinOrg/XDPoSChain/core/types"
Expand Down Expand Up @@ -229,20 +231,35 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
head = pm.blockchain.CurrentHeader()
td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
)
tp.handshake(nil, td, head.Hash(), genesis.Hash())
tp.handshake(nil, td, head.Hash(), genesis.Hash(), forkid.NewID(pm.blockchain.Config(), genesis, head.Number.Uint64()), forkid.NewFilter(pm.blockchain))
}
return tp, errc
}

// handshake simulates a trivial handshake that expects the same state from the
// remote side as we are simulating locally.
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
msg := &statusData{
ProtocolVersion: uint32(p.version),
NetworkId: ethconfig.Defaults.NetworkId,
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) {
var msg interface{}
switch p.version {
case xdc164:
msg = &statusData{
ProtocolVersion: uint32(p.version),
NetworkID: ethconfig.Defaults.NetworkId,
TD: td,
Head: head,
Genesis: genesis,
ForkID: forkID,
}
case xdc100, eth63:
msg = &statusData63{
ProtocolVersion: uint32(p.version),
NetworkId: ethconfig.Defaults.NetworkId,
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
}
default:
panic(fmt.Sprintf("unsupported eth protocol version: %d", p.version))
}
if err := p2p.ExpectMsg(p.app, StatusMsg, msg); err != nil {
t.Fatalf("status recv: %v", err)
Expand Down
92 changes: 77 additions & 15 deletions eth/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/forkid"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/p2p"
"github.com/XinFinOrg/XDPoSChain/rlp"
Expand Down Expand Up @@ -378,26 +379,50 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {

// Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash) error {
func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error {
// Send out own handshake in a new thread
errc := make(chan error, 2)
var status statusData // safe to read after two values have been received from errc

var (
status63 statusData63 // safe to read after two values have been received from errc
status statusData // safe to read after two values have been received from errc
)
go func() {
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.version),
NetworkId: network,
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
})
switch p.version {
case xdc164:
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.version),
NetworkID: network,
TD: td,
Head: head,
Genesis: genesis,
ForkID: forkID,
})
case xdc100, eth63:
errc <- p2p.Send(p.rw, StatusMsg, &statusData63{
ProtocolVersion: uint32(p.version),
NetworkId: network,
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
})
default:
errc <- errResp(ErrProtocolVersionMismatch, "unsupported eth protocol version: %d", p.version)
}
}()
go func() {
errc <- p.readStatus(network, &status, genesis)
switch p.version {
case xdc164:
errc <- p.readStatus(network, &status, genesis, forkFilter)
case xdc100, eth63:
errc <- p.readStatusLegacy(network, &status63, genesis)
default:
errc <- errResp(ErrProtocolVersionMismatch, "unsupported eth protocol version: %d", p.version)
}
}()
timeout := time.NewTimer(handshakeTimeout)
defer timeout.Stop()
for i := 0; i < 2; i++ {
for range 2 {
select {
case err := <-errc:
if err != nil {
Expand All @@ -407,11 +432,18 @@ func (p *peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis
return p2p.DiscReadTimeout
}
}
p.td, p.head = status.TD, status.CurrentBlock
switch p.version {
case xdc164:
p.td, p.head = status.TD, status.Head
case xdc100, eth63:
p.td, p.head = status63.TD, status63.CurrentBlock
default:
return errResp(ErrProtocolVersionMismatch, "unsupported eth protocol version: %d", p.version)
}
return nil
}

func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash) (err error) {
func (p *peer) readStatusLegacy(network uint64, status *statusData63, genesis common.Hash) error {
msg, err := p.rw.ReadMsg()
if err != nil {
return err
Expand All @@ -427,14 +459,44 @@ func (p *peer) readStatus(network uint64, status *statusData, genesis common.Has
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
if status.GenesisBlock != genesis {
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8])
return errResp(ErrGenesisMismatch, "%x (!= %x)", status.GenesisBlock[:8], genesis[:8])
}
if status.NetworkId != network {
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkId, network)
}
if int(status.ProtocolVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
}
return nil
}

func (p *peer) readStatus(network uint64, status *statusData, genesis common.Hash, forkFilter forkid.Filter) error {
msg, err := p.rw.ReadMsg()
if err != nil {
return err
}
if msg.Code != StatusMsg {
return errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg)
}
if msg.Size > protocolMaxMsgSize {
return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, protocolMaxMsgSize)
}
// Decode the handshake and make sure everything matches
if err := msg.Decode(&status); err != nil {
return errResp(ErrDecode, "msg %v: %v", msg, err)
}
if status.NetworkID != network {
return errResp(ErrNetworkIDMismatch, "%d (!= %d)", status.NetworkID, network)
}
if int(status.ProtocolVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
}
if status.Genesis != genesis {
return errResp(ErrGenesisMismatch, "%x (!= %x)", status.Genesis, genesis)
}
if err := forkFilter(status.ForkID); err != nil {
return errResp(ErrForkIDRejected, "%v", err)
}
return nil
}

Expand Down
Loading
Loading