network: add MPT-related P2P payloads

This commit is contained in:
Evgenii Stratonikov 2020-05-29 17:20:37 +03:00
parent 20f190ef69
commit edcfdb3bde
5 changed files with 155 additions and 1 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/consensus" "github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
@ -59,12 +60,15 @@ const (
CMDGetBlocks CommandType = "getblocks" CMDGetBlocks CommandType = "getblocks"
CMDGetData CommandType = "getdata" CMDGetData CommandType = "getdata"
CMDGetHeaders CommandType = "getheaders" CMDGetHeaders CommandType = "getheaders"
CMDGetRoots CommandType = "getroots"
CMDHeaders CommandType = "headers" CMDHeaders CommandType = "headers"
CMDInv CommandType = "inv" CMDInv CommandType = "inv"
CMDMempool CommandType = "mempool" CMDMempool CommandType = "mempool"
CMDMerkleBlock CommandType = "merkleblock" CMDMerkleBlock CommandType = "merkleblock"
CMDPing CommandType = "ping" CMDPing CommandType = "ping"
CMDPong CommandType = "pong" CMDPong CommandType = "pong"
CMDRoots CommandType = "roots"
CMDStateRoot CommandType = "stateroot"
CMDTX CommandType = "tx" CMDTX CommandType = "tx"
CMDUnknown CommandType = "unknown" CMDUnknown CommandType = "unknown"
CMDVerack CommandType = "verack" CMDVerack CommandType = "verack"
@ -124,6 +128,8 @@ func (m *Message) CommandType() CommandType {
return CMDGetData return CMDGetData
case "getheaders": case "getheaders":
return CMDGetHeaders return CMDGetHeaders
case "getroots":
return CMDGetRoots
case "headers": case "headers":
return CMDHeaders return CMDHeaders
case "inv": case "inv":
@ -136,6 +142,10 @@ func (m *Message) CommandType() CommandType {
return CMDPing return CMDPing
case "pong": case "pong":
return CMDPong return CMDPong
case "roots":
return CMDRoots
case "stateroot":
return CMDStateRoot
case "tx": case "tx":
return CMDTX return CMDTX
case "verack": case "verack":
@ -191,6 +201,8 @@ func (m *Message) decodePayload(br *io.BinReader) error {
fallthrough fallthrough
case CMDGetHeaders: case CMDGetHeaders:
p = &payload.GetBlocks{} p = &payload.GetBlocks{}
case CMDGetRoots:
p = &payload.GetStateRoots{}
case CMDHeaders: case CMDHeaders:
p = &payload.Headers{} p = &payload.Headers{}
case CMDTX: case CMDTX:
@ -199,6 +211,10 @@ func (m *Message) decodePayload(br *io.BinReader) error {
p = &payload.MerkleBlock{} p = &payload.MerkleBlock{}
case CMDPing, CMDPong: case CMDPing, CMDPong:
p = &payload.Ping{} p = &payload.Ping{}
case CMDRoots:
p = &payload.StateRoots{}
case CMDStateRoot:
p = &state.MPTRoot{}
default: default:
return fmt.Errorf("can't decode command %s", cmdByteArrayToString(m.Command)) return fmt.Errorf("can't decode command %s", cmdByteArrayToString(m.Command))
} }

View file

@ -18,6 +18,8 @@ func (i InventoryType) String() string {
return "TX" return "TX"
case 0x02: case 0x02:
return "block" return "block"
case StateRootType:
return "stateroot"
case 0xe0: case 0xe0:
return "consensus" return "consensus"
default: default:
@ -27,13 +29,14 @@ func (i InventoryType) String() string {
// Valid returns true if the inventory (type) is known. // Valid returns true if the inventory (type) is known.
func (i InventoryType) Valid() bool { func (i InventoryType) Valid() bool {
return i == BlockType || i == TXType || i == ConsensusType return i == BlockType || i == TXType || i == ConsensusType || i == StateRootType
} }
// List of valid InventoryTypes. // List of valid InventoryTypes.
const ( const (
TXType InventoryType = 0x01 // 1 TXType InventoryType = 0x01 // 1
BlockType InventoryType = 0x02 // 2 BlockType InventoryType = 0x02 // 2
StateRootType InventoryType = 0x03 // 3
ConsensusType InventoryType = 0xe0 // 224 ConsensusType InventoryType = 0xe0 // 224
) )

View file

@ -0,0 +1,43 @@
package payload
import (
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// MaxStateRootsAllowed is a maxumum amount of state roots
// which can be sent in a single payload.
const MaxStateRootsAllowed = 2000
// StateRoots contains multiple StateRoots.
type StateRoots struct {
Roots []state.MPTRoot
}
// GetStateRoots represents request for state roots.
type GetStateRoots struct {
Start uint32
Count uint32
}
// EncodeBinary implements io.Serializable.
func (s *StateRoots) EncodeBinary(w *io.BinWriter) {
w.WriteArray(s.Roots)
}
// DecodeBinary implements io.Serializable.
func (s *StateRoots) DecodeBinary(r *io.BinReader) {
r.ReadArray(&s.Roots, MaxStateRootsAllowed)
}
// DecodeBinary implements io.Serializable.
func (g *GetStateRoots) DecodeBinary(r *io.BinReader) {
g.Start = r.ReadU32LE()
g.Count = r.ReadU32LE()
}
// EncodeBinary implements io.Serializable.
func (g *GetStateRoots) EncodeBinary(w *io.BinWriter) {
w.WriteU32LE(g.Start)
w.WriteU32LE(g.Count)
}

View file

@ -0,0 +1,51 @@
package payload
import (
"math/rand"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
)
func TestStateRoots_Serializable(t *testing.T) {
expected := &StateRoots{
Roots: []state.MPTRoot{
{
MPTRootBase: state.MPTRootBase{
Index: rand.Uint32(),
PrevHash: random.Uint256(),
Root: random.Uint256(),
},
Witness: &transaction.Witness{
InvocationScript: random.Bytes(10),
VerificationScript: random.Bytes(11),
},
},
{
MPTRootBase: state.MPTRootBase{
Index: rand.Uint32(),
PrevHash: random.Uint256(),
Root: random.Uint256(),
},
Witness: &transaction.Witness{
InvocationScript: random.Bytes(10),
VerificationScript: random.Bytes(11),
},
},
},
}
testserdes.EncodeDecodeBinary(t, expected, new(StateRoots))
}
func TestGetStateRoots_Serializable(t *testing.T) {
expected := &GetStateRoots{
Start: rand.Uint32(),
Count: rand.Uint32(),
}
testserdes.EncodeDecodeBinary(t, expected, new(GetStateRoots))
}

View file

@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/consensus" "github.com/nspcc-dev/neo-go/pkg/consensus"
"github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -507,6 +508,8 @@ func (s *Server) handleGetDataCmd(p Peer, inv *payload.Inventory) error {
if err == nil { if err == nil {
msg = s.MkMsg(CMDBlock, b) msg = s.MkMsg(CMDBlock, b)
} }
case payload.StateRootType:
return nil // do nothing
case payload.ConsensusType: case payload.ConsensusType:
if cp := s.consensus.GetPayload(hash); cp != nil { if cp := s.consensus.GetPayload(hash); cp != nil {
msg = s.MkMsg(CMDConsensus, cp) msg = s.MkMsg(CMDConsensus, cp)
@ -589,6 +592,35 @@ func (s *Server) handleGetHeadersCmd(p Peer, gh *payload.GetBlocks) error {
return p.EnqueueP2PMessage(msg) return p.EnqueueP2PMessage(msg)
} }
// handleGetRootsCmd processees `getroots` request.
func (s *Server) handleGetRootsCmd(p Peer, gr *payload.GetStateRoots) error {
count := gr.Count
if count > payload.MaxStateRootsAllowed {
count = payload.MaxStateRootsAllowed
}
var rs payload.StateRoots
for height := gr.Start; height < gr.Start+gr.Count; height++ {
r, err := s.chain.GetStateRoot(height)
if err != nil {
return err
} else if r.Flag == state.Verified {
rs.Roots = append(rs.Roots, r.MPTRoot)
}
}
msg := s.MkMsg(CMDRoots, &rs)
return p.EnqueueP2PMessage(msg)
}
// handleStateRootsCmd processees `roots` request.
func (s *Server) handleRootsCmd(rs *payload.StateRoots) error {
return nil // TODO
}
// handleStateRootCmd processees `stateroot` request.
func (s *Server) handleStateRootCmd(r *state.MPTRoot) error {
return nil // TODO
}
// handleConsensusCmd processes received consensus payload. // handleConsensusCmd processes received consensus payload.
// It never returns an error. // It never returns an error.
func (s *Server) handleConsensusCmd(cp *consensus.Payload) error { func (s *Server) handleConsensusCmd(cp *consensus.Payload) error {
@ -697,6 +729,9 @@ func (s *Server) handleMessage(peer Peer, msg *Message) error {
case CMDGetHeaders: case CMDGetHeaders:
gh := msg.Payload.(*payload.GetBlocks) gh := msg.Payload.(*payload.GetBlocks)
return s.handleGetHeadersCmd(peer, gh) return s.handleGetHeadersCmd(peer, gh)
case CMDGetRoots:
gr := msg.Payload.(*payload.GetStateRoots)
return s.handleGetRootsCmd(peer, gr)
case CMDHeaders: case CMDHeaders:
headers := msg.Payload.(*payload.Headers) headers := msg.Payload.(*payload.Headers)
go s.handleHeadersCmd(peer, headers) go s.handleHeadersCmd(peer, headers)
@ -718,6 +753,12 @@ func (s *Server) handleMessage(peer Peer, msg *Message) error {
case CMDPong: case CMDPong:
pong := msg.Payload.(*payload.Ping) pong := msg.Payload.(*payload.Ping)
return s.handlePong(peer, pong) return s.handlePong(peer, pong)
case CMDRoots:
rs := msg.Payload.(*payload.StateRoots)
return s.handleRootsCmd(rs)
case CMDStateRoot:
r := msg.Payload.(*state.MPTRoot)
return s.handleStateRootCmd(r)
case CMDVersion, CMDVerack: case CMDVersion, CMDVerack:
return fmt.Errorf("received '%s' after the handshake", msg.CommandType()) return fmt.Errorf("received '%s' after the handshake", msg.CommandType())
} }