forked from TrueCloudLab/neoneo-go
network: add MPT-related P2P payloads
This commit is contained in:
parent
20f190ef69
commit
edcfdb3bde
5 changed files with 155 additions and 1 deletions
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
43
pkg/network/payload/state_root.go
Normal file
43
pkg/network/payload/state_root.go
Normal 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)
|
||||||
|
}
|
51
pkg/network/payload/state_root_test.go
Normal file
51
pkg/network/payload/state_root_test.go
Normal 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))
|
||||||
|
}
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue