mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
Init wire Messages
This commit is contained in:
parent
d24560ad65
commit
971409030b
5 changed files with 1926 additions and 0 deletions
897
qkc/cluster/wire/messages.go
Normal file
897
qkc/cluster/wire/messages.go
Normal file
|
|
@ -0,0 +1,897 @@
|
||||||
|
// Copyright 2026-2027, QuarkChain.
|
||||||
|
|
||||||
|
// Package wire: serializable message structs for every cluster RPC opcode.
|
||||||
|
//
|
||||||
|
// Each struct mirrors a pyquarkchain Serializable from
|
||||||
|
// quarkchain/cluster/rpc.py (ClusterOp messages) or
|
||||||
|
// quarkchain/cluster/p2p_commands.py (CommandOp messages).
|
||||||
|
//
|
||||||
|
// Field layout MUST stay byte-compatible with the Python wire format. Every
|
||||||
|
// field name matches the Python FIELDS order and the wire encoding is enforced
|
||||||
|
// by qkc/serialize/ struct tags (see typecache.go):
|
||||||
|
//
|
||||||
|
// bytesizeofslicelen:"4" 4-byte big-endian length prefix for slices
|
||||||
|
// (Python PrependedSizeBytesSerializer(4) and
|
||||||
|
// PrependedSizeListSerializer(4, T))
|
||||||
|
// ser:"nil" nullable pointer — 1-byte presence marker
|
||||||
|
// (Python Optional(T))
|
||||||
|
// ser:"-" ignored field (not serialised)
|
||||||
|
//
|
||||||
|
// Primitive type mapping (Python → Go):
|
||||||
|
//
|
||||||
|
// uint8 → uint8 1 byte
|
||||||
|
// uint16 → uint16 2 bytes big-endian
|
||||||
|
// uint32 → uint32 4 bytes big-endian
|
||||||
|
// uint64 → uint64 8 bytes big-endian
|
||||||
|
// uint128 → [16]byte 16 bytes big-endian
|
||||||
|
// uint256 → *big.Int 1-byte length prefix + big-endian bytes
|
||||||
|
// biguint → *big.Int same as uint256
|
||||||
|
// hash256 → [32]byte 32 bytes
|
||||||
|
// Branch → uint32 4 bytes
|
||||||
|
// Address → [20]byte 20 bytes
|
||||||
|
// signature65 → [65]byte 65 bytes
|
||||||
|
// boolean → bool 1 byte (0x00 / 0x01)
|
||||||
|
//
|
||||||
|
// =============================================================================
|
||||||
|
// Placeholder: RawBytes
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// pyquarkchain defines many complex Serializable types (RootBlock,
|
||||||
|
// MinorBlockHeader, TypedTransaction, CrossShardTransactionList,
|
||||||
|
// TokenBalanceMap, TransactionReceipt, Log, MinorBlock, RootBlockHeader)
|
||||||
|
// that are NOT yet ported to Go. They all have a Python FIELDS list, so
|
||||||
|
// the Go wire length for any given field is well-defined — it just depends
|
||||||
|
// on the future Go type's encoding.
|
||||||
|
//
|
||||||
|
// To keep this PR self-contained and the wire layout pinned down NOW, each
|
||||||
|
// not-yet-ported type is referenced as *RawBytes. RawBytes is a transparent
|
||||||
|
// Serializable that round-trips arbitrary bytes, matching the wire length
|
||||||
|
// of the future Go struct once the fields are filled in. When the real Go
|
||||||
|
// type lands, only the field type needs to change — the wire encoding stays
|
||||||
|
// byte-identical.
|
||||||
|
//
|
||||||
|
// SAFETY: RawBytes.Deserialize consumes ALL remaining bytes in the buffer.
|
||||||
|
// It is only safe when the *RawBytes field is the LAST field of its parent
|
||||||
|
// struct. Fields that are not last are annotated with a WARNING and cannot be
|
||||||
|
// correctly deserialized until the real Go type is ported.
|
||||||
|
//
|
||||||
|
// =============================================================================
|
||||||
|
// Layout
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// Grouped to match the wire opcode sections in opcode.go:
|
||||||
|
//
|
||||||
|
// §1 Cluster initialisation (PING, CONNECT_TO_SLAVES, MINE, GEN_TX)
|
||||||
|
// §2 Virtual connection mgmt (CREATE/DESTROY_CLUSTER_PEER)
|
||||||
|
// §3 Block updates (ADD_ROOT_BLOCK, ADD_MINOR_BLOCK,
|
||||||
|
// SYNC_MINOR_BLOCK_LIST, CHECK_MINOR_BLOCK,
|
||||||
|
// GET_UNCONFIRMED_HEADERS, ADD_MINOR_BLOCK_HEADER)
|
||||||
|
// §4 Block queries (GET_ECO_INFO_LIST, GET_NEXT_BLOCK_TO_MINE,
|
||||||
|
// GET_MINOR_BLOCK, GET_TRANSACTION, EXECUTE_TX,
|
||||||
|
// GET_TX_RECEIPT, GET_TX_LIST_BY_ADDRESS,
|
||||||
|
// GET_ALL_TX, GET_LOG, ESTIMATE_GAS, GET_STORAGE,
|
||||||
|
// GET_CODE, GAS_PRICE, GET_WORK, SUBMIT_WORK)
|
||||||
|
// §5 Account / staking (GET_ACCOUNT_DATA, GET_ROOT_CHAIN_STAKES,
|
||||||
|
// GET_TOTAL_BALANCE)
|
||||||
|
// §6 Cross-shard (Slave↔Slave) (ADD_XSHARD_TX_LIST, BATCH_ADD_XSHARD_TX_LIST)
|
||||||
|
// §7 P2P commands (HELLO, NEW_MINOR_BLOCK_HEADER_LIST,
|
||||||
|
// NEW_TRANSACTION_LIST, NEW_BLOCK_MINOR,
|
||||||
|
// PING_PONG, NEW_ROOT_BLOCK)
|
||||||
|
// §8 P2P queries (GET_ROOT_BLOCK_*, GET_MINOR_BLOCK_*)
|
||||||
|
//
|
||||||
|
// Every struct is defined in one place to keep the opcode-to-struct mapping
|
||||||
|
// in protocol.go complete and avoid scattering definitions across PRs.
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Wire-level address / branch / hash constants
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// These match quarkchain/core.py:Constant and Python Branch/Address types.
|
||||||
|
|
||||||
|
// AddressLength is the byte length of an Address (20 bytes).
|
||||||
|
const AddressLength = 20
|
||||||
|
|
||||||
|
// HashLength is the byte length of a hash256.
|
||||||
|
const HashLength = 32
|
||||||
|
|
||||||
|
// SignatureLength is the byte length of a signature65.
|
||||||
|
const SignatureLength = 65
|
||||||
|
|
||||||
|
// UInt128Length is the byte length of a uint128.
|
||||||
|
const UInt128Length = 16
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §1 Cluster initialisation
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// PingRequest (ClusterOp.PING, 0x81) — sent by master to initialise a slave.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("id", PrependedSizeBytesSerializer(4)),
|
||||||
|
// ("full_shard_id_list", PrependedSizeListSerializer(4, uint32)),
|
||||||
|
// ("root_tip", Optional(RootBlock)),
|
||||||
|
// ]
|
||||||
|
type PingRequest struct {
|
||||||
|
ID []byte `bytesizeofslicelen:"4"`
|
||||||
|
FullShardIDList []uint32 `bytesizeofslicelen:"4"`
|
||||||
|
RootTip *RawBytes `ser:"nil"` // TODO: Replace with *RootBlock once core.RootBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// PongResponse (ClusterOp.PONG, 0x82) — slave's reply to PING.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("id", PrependedSizeBytesSerializer(4)),
|
||||||
|
// ("full_shard_id_list", PrependedSizeListSerializer(4, uint32)),
|
||||||
|
// ]
|
||||||
|
type PongResponse struct {
|
||||||
|
ID []byte `bytesizeofslicelen:"4"`
|
||||||
|
FullShardIDList []uint32 `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlaveInfo (used by ConnectToSlavesRequest) — describes a remote slave.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("id", PrependedSizeBytesSerializer(4)),
|
||||||
|
// ("host", PrependedSizeBytesSerializer(4)),
|
||||||
|
// ("port", uint16),
|
||||||
|
// ("full_shard_id_list", PrependedSizeListSerializer(4, uint32)),
|
||||||
|
// ]
|
||||||
|
type SlaveInfo struct {
|
||||||
|
ID []byte `bytesizeofslicelen:"4"`
|
||||||
|
Host []byte `bytesizeofslicelen:"4"`
|
||||||
|
Port uint16
|
||||||
|
FullShardIDList []uint32 `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectToSlavesRequest (ClusterOp.CONNECT_TO_SLAVES_REQUEST, 0x83).
|
||||||
|
//
|
||||||
|
// FIELDS = [("slave_info_list", PrependedSizeListSerializer(4, SlaveInfo))]
|
||||||
|
type ConnectToSlavesRequest struct {
|
||||||
|
SlaveInfoList []SlaveInfo `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectToSlavesResponse (ClusterOp.CONNECT_TO_SLAVES_RESPONSE, 0x84).
|
||||||
|
//
|
||||||
|
// result_list has the same size as slave_info_list; empty result = success,
|
||||||
|
// otherwise the bytes are a serialised error message.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("result_list", PrependedSizeListSerializer(4, PrependedSizeBytesSerializer(4)))
|
||||||
|
// ]
|
||||||
|
type ConnectToSlavesResponse struct {
|
||||||
|
ResultList []PrependedSizeBytes4 `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtificialTxConfig — used by MineRequest / GetNextBlockToMineRequest /
|
||||||
|
// AddMinorBlockHeaderResponse.
|
||||||
|
//
|
||||||
|
// FIELDS = [("target_root_block_time", uint32), ("target_minor_block_time", uint32)]
|
||||||
|
type ArtificialTxConfig struct {
|
||||||
|
TargetRootBlockTime uint32
|
||||||
|
TargetMinorBlockTime uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MineRequest (ClusterOp.MINE_REQUEST, 0xA7) — start/stop mining on slaves.
|
||||||
|
//
|
||||||
|
// FIELDS = [("artificial_tx_config", ArtificialTxConfig), ("mining", boolean)]
|
||||||
|
type MineRequest struct {
|
||||||
|
ArtificialTxConfig ArtificialTxConfig
|
||||||
|
Mining bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MineResponse (ClusterOp.MINE_RESPONSE, 0xA8).
|
||||||
|
type MineResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenTxRequest (ClusterOp.GEN_TX_REQUEST, 0xA9) — generate transactions.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("num_tx_per_shard", uint32),
|
||||||
|
// ("x_shard_percent", uint32), # [0, 100]
|
||||||
|
// ("tx", TypedTransaction),
|
||||||
|
// ]
|
||||||
|
type GenTxRequest struct {
|
||||||
|
NumTxPerShard uint32
|
||||||
|
XShardPercent uint32
|
||||||
|
Tx *RawBytes // TODO: Replace with *TypedTransaction once core.TypedTransaction is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenTxResponse (ClusterOp.GEN_TX_RESPONSE, 0xAA).
|
||||||
|
type GenTxResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §2 Virtual connection management (mode 3 — Peer→Master→Slave)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// CreateClusterPeerConnectionRequest (ClusterOp.CREATE_CLUSTER_PEER_CONNECTION_REQUEST, 0x99).
|
||||||
|
type CreateClusterPeerConnectionRequest struct {
|
||||||
|
ClusterPeerID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateClusterPeerConnectionResponse (ClusterOp.CREATE_CLUSTER_PEER_CONNECTION_RESPONSE, 0x9A).
|
||||||
|
type CreateClusterPeerConnectionResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyClusterPeerConnectionCommand (ClusterOp.DESTROY_CLUSTER_PEER_CONNECTION_COMMAND, 0x9B)
|
||||||
|
// — fire-and-forget, no response.
|
||||||
|
type DestroyClusterPeerConnectionCommand struct {
|
||||||
|
ClusterPeerID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §3 Block updates
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// AddRootBlockRequest (ClusterOp.ADD_ROOT_BLOCK_REQUEST, 0x85).
|
||||||
|
//
|
||||||
|
// FIELDS = [("root_block", RootBlock), ("expect_switch", boolean)]
|
||||||
|
type AddRootBlockRequest struct {
|
||||||
|
// TODO: Replace with *RootBlock once core.RootBlock is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
RootBlock *RawBytes
|
||||||
|
ExpectSwitch bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRootBlockResponse (ClusterOp.ADD_ROOT_BLOCK_RESPONSE, 0x86).
|
||||||
|
type AddRootBlockResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Switched bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// EcoInfo — used by GetEcoInfoListResponse.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("branch", Branch),
|
||||||
|
// ("height", uint64),
|
||||||
|
// ("coinbase_amount", uint256),
|
||||||
|
// ("difficulty", biguint),
|
||||||
|
// ("unconfirmed_headers_coinbase_amount", uint256),
|
||||||
|
// ]
|
||||||
|
type EcoInfo struct {
|
||||||
|
Branch uint32
|
||||||
|
Height uint64
|
||||||
|
CoinbaseAmount *big.Int // uint256
|
||||||
|
Difficulty *big.Int // biguint
|
||||||
|
UnconfirmedHeadersCoinbaseAmount *big.Int // uint256
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEcoInfoListRequest (ClusterOp.GET_ECO_INFO_LIST_REQUEST, 0x87) — empty body.
|
||||||
|
type GetEcoInfoListRequest struct{}
|
||||||
|
|
||||||
|
// GetEcoInfoListResponse (ClusterOp.GET_ECO_INFO_LIST_RESPONSE, 0x88).
|
||||||
|
type GetEcoInfoListResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
EcoInfoList []EcoInfo `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextBlockToMineRequest (ClusterOp.GET_NEXT_BLOCK_TO_MINE_REQUEST, 0x89).
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("branch", Branch),
|
||||||
|
// ("address", Address),
|
||||||
|
// ("artificial_tx_config", ArtificialTxConfig),
|
||||||
|
// ]
|
||||||
|
type GetNextBlockToMineRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
Address [AddressLength]byte
|
||||||
|
ArtificialTxConfig ArtificialTxConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextBlockToMineResponse (ClusterOp.GET_NEXT_BLOCK_TO_MINE_RESPONSE, 0x8A).
|
||||||
|
type GetNextBlockToMineResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Block *RawBytes // TODO: Replace with *MinorBlock once core.MinorBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockRequest (ClusterOp.ADD_MINOR_BLOCK_REQUEST, 0x97) — JRPC-mined blocks.
|
||||||
|
//
|
||||||
|
// FIELDS = [("minor_block_data", PrependedSizeBytesSerializer(4))]
|
||||||
|
type AddMinorBlockRequest struct {
|
||||||
|
MinorBlockData []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockResponse (ClusterOp.ADD_MINOR_BLOCK_RESPONSE, 0x98).
|
||||||
|
type AddMinorBlockResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMinorBlockRequest (ClusterOp.CHECK_MINOR_BLOCK_REQUEST, 0xBD).
|
||||||
|
type CheckMinorBlockRequest struct {
|
||||||
|
MinorBlockHeader *RawBytes // TODO: Replace with *MinorBlockHeader once core.MinorBlockHeader is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckMinorBlockResponse (ClusterOp.CHECK_MINOR_BLOCK_RESPONSE, 0xBE).
|
||||||
|
type CheckMinorBlockResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadersInfo — used by GetUnconfirmedHeadersResponse.
|
||||||
|
type HeadersInfo struct {
|
||||||
|
Branch uint32
|
||||||
|
HeaderList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*MinorBlockHeader once core.MinorBlockHeader is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnconfirmedHeadersRequest (ClusterOp.GET_UNCONFIRMED_HEADERS_REQUEST, 0x8B) — empty body.
|
||||||
|
type GetUnconfirmedHeadersRequest struct{}
|
||||||
|
|
||||||
|
// GetUnconfirmedHeadersResponse (ClusterOp.GET_UNCONFIRMED_HEADERS_RESPONSE, 0x8C).
|
||||||
|
type GetUnconfirmedHeadersResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
HeadersInfoList []HeadersInfo `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountBranchData — used by GetAccountDataResponse.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("branch", Branch),
|
||||||
|
// ("transaction_count", uint256),
|
||||||
|
// ("token_balances", TokenBalanceMap),
|
||||||
|
// ("is_contract", boolean),
|
||||||
|
// ("posw_mineable_blocks", uint16),
|
||||||
|
// ("mined_blocks", uint16),
|
||||||
|
// ]
|
||||||
|
type AccountBranchData struct {
|
||||||
|
Branch uint32
|
||||||
|
TransactionCount *big.Int // uint256
|
||||||
|
// TODO: Replace with *TokenBalanceMap once core.TokenBalanceMap is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
TokenBalances *RawBytes
|
||||||
|
IsContract bool
|
||||||
|
PoswMineableBlocks uint16
|
||||||
|
MinedBlocks uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccountDataRequest (ClusterOp.GET_ACCOUNT_DATA_REQUEST, 0x8D).
|
||||||
|
type GetAccountDataRequest struct {
|
||||||
|
Address [AddressLength]byte
|
||||||
|
BlockHeight *uint64 `ser:"nil"` // Optional uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccountDataResponse (ClusterOp.GET_ACCOUNT_DATA_RESPONSE, 0x8E).
|
||||||
|
type GetAccountDataResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
AccountBranchDataList []AccountBranchData `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransactionRequest (ClusterOp.ADD_TRANSACTION_REQUEST, 0x8F).
|
||||||
|
type AddTransactionRequest struct {
|
||||||
|
Tx *RawBytes // TODO: Replace with *TypedTransaction once core.TypedTransaction is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransactionResponse (ClusterOp.ADD_TRANSACTION_RESPONSE, 0x90).
|
||||||
|
type AddTransactionResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShardStats — used by AddMinorBlockHeaderRequest / SyncMinorBlockListResponse.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("branch", Branch),
|
||||||
|
// ("height", uint64),
|
||||||
|
// ("difficulty", biguint),
|
||||||
|
// ("coinbase_address", Address),
|
||||||
|
// ("timestamp", uint64),
|
||||||
|
// ("tx_count60s", uint32),
|
||||||
|
// ("pending_tx_count", uint32),
|
||||||
|
// ("total_tx_count", uint32),
|
||||||
|
// ("block_count60s", uint32),
|
||||||
|
// ("stale_block_count60s", uint32),
|
||||||
|
// ("last_block_time", uint32),
|
||||||
|
// ]
|
||||||
|
type ShardStats struct {
|
||||||
|
Branch uint32
|
||||||
|
Height uint64
|
||||||
|
Difficulty *big.Int // biguint
|
||||||
|
CoinbaseAddress [AddressLength]byte
|
||||||
|
Timestamp uint64
|
||||||
|
TxCount60s uint32
|
||||||
|
PendingTxCount uint32
|
||||||
|
TotalTxCount uint32
|
||||||
|
BlockCount60s uint32
|
||||||
|
StaleBlockCount60s uint32
|
||||||
|
LastBlockTime uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockHeaderRequest (ClusterOp.ADD_MINOR_BLOCK_HEADER_REQUEST, 0x91) — slave→master.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("minor_block_header", MinorBlockHeader),
|
||||||
|
// ("tx_count", uint32),
|
||||||
|
// ("x_shard_tx_count", uint32),
|
||||||
|
// ("coinbase_amount_map", TokenBalanceMap),
|
||||||
|
// ("shard_stats", ShardStats),
|
||||||
|
// ]
|
||||||
|
type AddMinorBlockHeaderRequest struct {
|
||||||
|
// TODO: Replace with *MinorBlockHeader once core.MinorBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
MinorBlockHeader *RawBytes
|
||||||
|
TxCount uint32
|
||||||
|
XShardTxCount uint32
|
||||||
|
// TODO: Replace with *TokenBalanceMap once core.TokenBalanceMap is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
CoinbaseAmountMap *RawBytes
|
||||||
|
ShardStats ShardStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockHeaderResponse (ClusterOp.ADD_MINOR_BLOCK_HEADER_RESPONSE, 0x92).
|
||||||
|
type AddMinorBlockHeaderResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
ArtificialTxConfig ArtificialTxConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockHeaderListRequest (ClusterOp.ADD_MINOR_BLOCK_HEADER_LIST_REQUEST, 0xBB) — slave→master.
|
||||||
|
type AddMinorBlockHeaderListRequest struct {
|
||||||
|
MinorBlockHeaderList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*MinorBlockHeader once core.MinorBlockHeader is ported
|
||||||
|
CoinbaseAmountMapList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*TokenBalanceMap once core.TokenBalanceMap is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMinorBlockHeaderListResponse (ClusterOp.ADD_MINOR_BLOCK_HEADER_LIST_RESPONSE, 0xBC).
|
||||||
|
type AddMinorBlockHeaderListResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncMinorBlockListRequest (ClusterOp.SYNC_MINOR_BLOCK_LIST_REQUEST, 0x95).
|
||||||
|
type SyncMinorBlockListRequest struct {
|
||||||
|
MinorBlockHashList [][HashLength]byte `bytesizeofslicelen:"4"`
|
||||||
|
Branch uint32
|
||||||
|
ClusterPeerID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncMinorBlockListResponse (ClusterOp.SYNC_MINOR_BLOCK_LIST_RESPONSE, 0x96).
|
||||||
|
//
|
||||||
|
// block_coinbase_map: PrependedSizeMapSerializer(4, hash256, TokenBalanceMap)
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("error_code", uint32),
|
||||||
|
// ("block_coinbase_map", PrependedSizeMapSerializer(4, hash256, TokenBalanceMap)),
|
||||||
|
// ("shard_stats", Optional(ShardStats)),
|
||||||
|
// ]
|
||||||
|
type SyncMinorBlockListResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
// TODO: Replace with real block_coinbase_map once core.TokenBalanceMap is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
BlockCoinbaseMap *RawBytes
|
||||||
|
ShardStats *ShardStats `ser:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §4 Block queries
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// MinorBlockExtraInfo — used by GetMinorBlockResponse.
|
||||||
|
type MinorBlockExtraInfo struct {
|
||||||
|
EffectiveDifficulty *big.Int // biguint
|
||||||
|
PoswMineableBlocks uint16
|
||||||
|
PoswMinedBlocks uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockRequest (ClusterOp.GET_MINOR_BLOCK_REQUEST, 0x9D).
|
||||||
|
type GetMinorBlockRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
MinorBlockHash [HashLength]byte
|
||||||
|
Height uint64
|
||||||
|
NeedExtraInfo bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockResponse (ClusterOp.GET_MINOR_BLOCK_RESPONSE, 0x9E).
|
||||||
|
type GetMinorBlockResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
// TODO: Replace with *MinorBlock once core.MinorBlock is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
MinorBlock *RawBytes
|
||||||
|
ExtraInfo *MinorBlockExtraInfo `ser:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionRequest (ClusterOp.GET_TRANSACTION_REQUEST, 0x9F).
|
||||||
|
type GetTransactionRequest struct {
|
||||||
|
TxHash [HashLength]byte
|
||||||
|
Branch uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionResponse (ClusterOp.GET_TRANSACTION_RESPONSE, 0xA0).
|
||||||
|
type GetTransactionResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
// TODO: Replace with *MinorBlock once core.MinorBlock is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
MinorBlock *RawBytes
|
||||||
|
Index uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteTransactionRequest (ClusterOp.EXECUTE_TRANSACTION_REQUEST, 0xA3).
|
||||||
|
type ExecuteTransactionRequest struct {
|
||||||
|
// TODO: Replace with *TypedTransaction once core.TypedTransaction is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
Tx *RawBytes
|
||||||
|
FromAddress [AddressLength]byte
|
||||||
|
BlockHeight *uint64 `ser:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteTransactionResponse (ClusterOp.EXECUTE_TRANSACTION_RESPONSE, 0xA4).
|
||||||
|
type ExecuteTransactionResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Result []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionReceiptRequest (ClusterOp.GET_TRANSACTION_RECEIPT_REQUEST, 0xA5).
|
||||||
|
type GetTransactionReceiptRequest struct {
|
||||||
|
TxHash [HashLength]byte
|
||||||
|
Branch uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionReceiptResponse (ClusterOp.GET_TRANSACTION_RECEIPT_RESPONSE, 0xA6).
|
||||||
|
type GetTransactionReceiptResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
// TODO: Replace with *MinorBlock once core.MinorBlock is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
MinorBlock *RawBytes
|
||||||
|
Index uint32
|
||||||
|
// TODO: Replace with *TransactionReceipt once core.TransactionReceipt is ported.
|
||||||
|
Receipt *RawBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionDetail — used by GetTransactionListByAddressResponse and
|
||||||
|
// GetAllTransactionsResponse.
|
||||||
|
type TransactionDetail struct {
|
||||||
|
TxHash [HashLength]byte
|
||||||
|
Nonce uint64
|
||||||
|
FromAddress [AddressLength]byte
|
||||||
|
ToAddress *[AddressLength]byte `ser:"nil"` // Optional Address
|
||||||
|
Value *big.Int // uint256
|
||||||
|
BlockHeight uint64
|
||||||
|
Timestamp uint64
|
||||||
|
Success bool
|
||||||
|
GasTokenID uint64
|
||||||
|
TransferTokenID uint64
|
||||||
|
IsFromRootChain bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionListByAddressRequest (ClusterOp.GET_TRANSACTION_LIST_BY_ADDRESS_REQUEST, 0xAB).
|
||||||
|
type GetTransactionListByAddressRequest struct {
|
||||||
|
Address [AddressLength]byte
|
||||||
|
TransferTokenID *uint64 `ser:"nil"`
|
||||||
|
Start []byte `bytesizeofslicelen:"4"`
|
||||||
|
Limit uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactionListByAddressResponse (ClusterOp.GET_TRANSACTION_LIST_BY_ADDRESS_RESPONSE, 0xAC).
|
||||||
|
type GetTransactionListByAddressResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
TxList []TransactionDetail `bytesizeofslicelen:"4"`
|
||||||
|
Next []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllTransactionsRequest (ClusterOp.GET_ALL_TRANSACTIONS_REQUEST, 0xBF).
|
||||||
|
type GetAllTransactionsRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
Start []byte `bytesizeofslicelen:"4"`
|
||||||
|
Limit uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllTransactionsResponse (ClusterOp.GET_ALL_TRANSACTIONS_RESPONSE, 0xC0).
|
||||||
|
type GetAllTransactionsResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
TxList []TransactionDetail `bytesizeofslicelen:"4"`
|
||||||
|
Next []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogRequest (ClusterOp.GET_LOG_REQUEST, 0xAD).
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("branch", Branch),
|
||||||
|
// ("addresses", PrependedSizeListSerializer(4, Address)),
|
||||||
|
// ("topics", PrependedSizeListSerializer(4, PrependedSizeListSerializer(4, hash256))),
|
||||||
|
// ("start_block", uint64),
|
||||||
|
// ("end_block", uint64),
|
||||||
|
// ]
|
||||||
|
type GetLogRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
Addresses [][AddressLength]byte `bytesizeofslicelen:"4"`
|
||||||
|
Topics []PrependedSizeHashList4 `bytesizeofslicelen:"4"`
|
||||||
|
StartBlock uint64
|
||||||
|
EndBlock uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLogResponse (ClusterOp.GET_LOG_RESPONSE, 0xAE).
|
||||||
|
type GetLogResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Logs []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*Log once core.Log is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateGasRequest (ClusterOp.ESTIMATE_GAS_REQUEST, 0xAF).
|
||||||
|
type EstimateGasRequest struct {
|
||||||
|
// TODO: Replace with *TypedTransaction once core.TypedTransaction is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
Tx *RawBytes
|
||||||
|
FromAddress [AddressLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateGasResponse (ClusterOp.ESTIMATE_GAS_RESPONSE, 0xB0).
|
||||||
|
type EstimateGasResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Result uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorageRequest (ClusterOp.GET_STORAGE_REQUEST, 0xB1).
|
||||||
|
type GetStorageRequest struct {
|
||||||
|
Address [AddressLength]byte
|
||||||
|
Key *big.Int // uint256
|
||||||
|
BlockHeight *uint64 `ser:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorageResponse (ClusterOp.GET_STORAGE_RESPONSE, 0xB2).
|
||||||
|
type GetStorageResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Result [HashLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCodeRequest (ClusterOp.GET_CODE_REQUEST, 0xB3).
|
||||||
|
type GetCodeRequest struct {
|
||||||
|
Address [AddressLength]byte
|
||||||
|
BlockHeight *uint64 `ser:"nil"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCodeResponse (ClusterOp.GET_CODE_RESPONSE, 0xB4).
|
||||||
|
type GetCodeResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Result []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasPriceRequest (ClusterOp.GAS_PRICE_REQUEST, 0xB5).
|
||||||
|
type GasPriceRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
TokenID uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasPriceResponse (ClusterOp.GAS_PRICE_RESPONSE, 0xB6).
|
||||||
|
type GasPriceResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Result uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkRequest (ClusterOp.GET_WORK_REQUEST, 0xB7).
|
||||||
|
type GetWorkRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
CoinbaseAddr *[AddressLength]byte `ser:"nil"` // Optional Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkResponse (ClusterOp.GET_WORK_RESPONSE, 0xB8).
|
||||||
|
type GetWorkResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
HeaderHash [HashLength]byte
|
||||||
|
Height uint64
|
||||||
|
Difficulty *big.Int // biguint
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitWorkRequest (ClusterOp.SUBMIT_WORK_REQUEST, 0xB9).
|
||||||
|
type SubmitWorkRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
HeaderHash [HashLength]byte
|
||||||
|
Nonce uint64
|
||||||
|
Mixhash [HashLength]byte
|
||||||
|
Signature *[SignatureLength]byte `ser:"nil"` // Optional signature65
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitWorkResponse (ClusterOp.SUBMIT_WORK_RESPONSE, 0xBA).
|
||||||
|
type SubmitWorkResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Success bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §5 Account / staking
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// GetRootChainStakesRequest (ClusterOp.GET_ROOT_CHAIN_STAKES_REQUEST, 0xC1).
|
||||||
|
type GetRootChainStakesRequest struct {
|
||||||
|
Address [AddressLength]byte
|
||||||
|
MinorBlockHash [HashLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootChainStakesResponse (ClusterOp.GET_ROOT_CHAIN_STAKES_RESPONSE, 0xC2).
|
||||||
|
type GetRootChainStakesResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
Stakes *big.Int // biguint
|
||||||
|
Signer [AddressLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTotalBalanceRequest (ClusterOp.GET_TOTAL_BALANCE_REQUEST, 0xC3).
|
||||||
|
type GetTotalBalanceRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
Start *[HashLength]byte `ser:"nil"` // Optional hash256
|
||||||
|
TokenID uint64
|
||||||
|
Limit uint32
|
||||||
|
MinorBlockHash [HashLength]byte
|
||||||
|
RootBlockHash *[HashLength]byte `ser:"nil"` // Optional hash256
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTotalBalanceResponse (ClusterOp.GET_TOTAL_BALANCE_RESPONSE, 0xC4).
|
||||||
|
type GetTotalBalanceResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
TotalBalance *big.Int // biguint
|
||||||
|
Next []byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §6 Cross-shard (Slave↔Slave, direct TCP, no metadata)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// AddXshardTxListRequest (ClusterOp.ADD_XSHARD_TX_LIST_REQUEST, 0x93).
|
||||||
|
type AddXshardTxListRequest struct {
|
||||||
|
Branch uint32
|
||||||
|
MinorBlockHash [HashLength]byte
|
||||||
|
TxList *RawBytes // TODO: Replace with *CrossShardTransactionList once core.CrossShardTransactionList is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddXshardTxListResponse (ClusterOp.ADD_XSHARD_TX_LIST_RESPONSE, 0x94).
|
||||||
|
type AddXshardTxListResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchAddXshardTxListRequest (ClusterOp.BATCH_ADD_XSHARD_TX_LIST_REQUEST, 0xA1).
|
||||||
|
type BatchAddXshardTxListRequest struct {
|
||||||
|
AddXshardTxListRequestList []AddXshardTxListRequest `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchAddXshardTxListResponse (ClusterOp.BATCH_ADD_XSHARD_TX_LIST_RESPONSE, 0xA2).
|
||||||
|
type BatchAddXshardTxListResponse struct {
|
||||||
|
ErrorCode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §7 P2P commands (CommandOp, cluster_peer_id != 0)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// HelloCommand (CommandOp.HELLO, 0x00) — initial inter-cluster handshake.
|
||||||
|
//
|
||||||
|
// FIELDS = [
|
||||||
|
// ("version", uint32),
|
||||||
|
// ("network_id", uint32),
|
||||||
|
// ("peer_id", hash256),
|
||||||
|
// ("peer_ip", uint128),
|
||||||
|
// ("peer_port", uint16),
|
||||||
|
// ("chain_mask_list", PrependedSizeListSerializer(4, uint32)),
|
||||||
|
// ("root_block_header", RootBlockHeader),
|
||||||
|
// ("genesis_root_block_hash", hash256),
|
||||||
|
// ]
|
||||||
|
type HelloCommand struct {
|
||||||
|
Version uint32
|
||||||
|
NetworkID uint32
|
||||||
|
PeerID [HashLength]byte
|
||||||
|
PeerIP [UInt128Length]byte
|
||||||
|
PeerPort uint16
|
||||||
|
ChainMaskList []uint32 `bytesizeofslicelen:"4"`
|
||||||
|
// TODO: Replace with *RootBlockHeader once core.RootBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
RootBlockHeader *RawBytes
|
||||||
|
GenesisRootBlockHash [HashLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMinorBlockHeaderListCommand (CommandOp.NEW_MINOR_BLOCK_HEADER_LIST, 0x01).
|
||||||
|
type NewMinorBlockHeaderListCommand struct {
|
||||||
|
// TODO: Replace with *RootBlockHeader once core.RootBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
RootBlockHeader *RawBytes
|
||||||
|
MinorBlockHeaderList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*MinorBlockHeader once core.MinorBlockHeader is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransactionListCommand (CommandOp.NEW_TRANSACTION_LIST, 0x02).
|
||||||
|
type NewTransactionListCommand struct {
|
||||||
|
TransactionList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*TypedTransaction once core.TypedTransaction is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockMinorCommand (CommandOp.NEW_BLOCK_MINOR, 0x0D).
|
||||||
|
type NewBlockMinorCommand struct {
|
||||||
|
Block *RawBytes // TODO: Replace with *MinorBlock once core.MinorBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingPongCommand (CommandOp.PING 0x0E, PONG 0x0F).
|
||||||
|
type PingPongCommand struct {
|
||||||
|
Message [HashLength]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRootBlockCommand (CommandOp.NEW_ROOT_BLOCK, 0x12).
|
||||||
|
type NewRootBlockCommand struct {
|
||||||
|
Block *RawBytes // TODO: Replace with *RootBlock once core.RootBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §8 P2P queries (CommandOp)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
// PeerInfo — used by GetPeerListResponse.
|
||||||
|
type PeerInfo struct {
|
||||||
|
IP [UInt128Length]byte
|
||||||
|
Port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerListRequest (CommandOp.GET_PEER_LIST_REQUEST, 0x03).
|
||||||
|
type GetPeerListRequest struct {
|
||||||
|
MaxPeers uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerListResponse (CommandOp.GET_PEER_LIST_RESPONSE, 0x04).
|
||||||
|
type GetPeerListResponse struct {
|
||||||
|
PeerInfoList []PeerInfo `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootBlockHeaderListRequest (CommandOp.GET_ROOT_BLOCK_HEADER_LIST_REQUEST, 0x05).
|
||||||
|
type GetRootBlockHeaderListRequest struct {
|
||||||
|
BlockHash [HashLength]byte
|
||||||
|
Limit uint32
|
||||||
|
Direction Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootBlockHeaderListResponse (CommandOp.GET_ROOT_BLOCK_HEADER_LIST_RESPONSE, 0x06).
|
||||||
|
type GetRootBlockHeaderListResponse struct {
|
||||||
|
// TODO: Replace with *RootBlockHeader once core.RootBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
RootTip *RawBytes
|
||||||
|
BlockHeaderList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*RootBlockHeader once core.RootBlockHeader is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootBlockHeaderListWithSkipRequest (CommandOp.GET_ROOT_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST, 0x10).
|
||||||
|
type GetRootBlockHeaderListWithSkipRequest struct {
|
||||||
|
Type uint8
|
||||||
|
Data [HashLength]byte
|
||||||
|
Limit uint32
|
||||||
|
Skip uint32
|
||||||
|
Direction Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootBlockListRequest (CommandOp.GET_ROOT_BLOCK_LIST_REQUEST, 0x07).
|
||||||
|
type GetRootBlockListRequest struct {
|
||||||
|
RootBlockHashList [][HashLength]byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootBlockListResponse (CommandOp.GET_ROOT_BLOCK_LIST_RESPONSE, 0x08).
|
||||||
|
type GetRootBlockListResponse struct {
|
||||||
|
RootBlockList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*RootBlock once core.RootBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockListRequest (CommandOp.GET_MINOR_BLOCK_LIST_REQUEST, 0x09).
|
||||||
|
type GetMinorBlockListRequest struct {
|
||||||
|
MinorBlockHashList [][HashLength]byte `bytesizeofslicelen:"4"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockListResponse (CommandOp.GET_MINOR_BLOCK_LIST_RESPONSE, 0x0A).
|
||||||
|
type GetMinorBlockListResponse struct {
|
||||||
|
MinorBlockList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*MinorBlock once core.MinorBlock is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockHeaderListRequest (CommandOp.GET_MINOR_BLOCK_HEADER_LIST_REQUEST, 0x0B).
|
||||||
|
type GetMinorBlockHeaderListRequest struct {
|
||||||
|
BlockHash [HashLength]byte
|
||||||
|
Branch uint32
|
||||||
|
Limit uint32
|
||||||
|
Direction Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockHeaderListResponse (CommandOp.GET_MINOR_BLOCK_HEADER_LIST_RESPONSE, 0x0C).
|
||||||
|
type GetMinorBlockHeaderListResponse struct {
|
||||||
|
// TODO: Replace with *RootBlockHeader once core.RootBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
RootTip *RawBytes
|
||||||
|
// TODO: Replace with *MinorBlockHeader once core.MinorBlockHeader is ported.
|
||||||
|
// WARNING: not the last field; RawBytes.Deserialize consumes remaining bytes.
|
||||||
|
ShardTip *RawBytes
|
||||||
|
BlockHeaderList []*RawBytes `bytesizeofslicelen:"4"` // TODO: Replace with []*MinorBlockHeader once core.MinorBlockHeader is ported
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMinorBlockHeaderListWithSkipRequest (CommandOp.GET_MINOR_BLOCK_HEADER_LIST_WITH_SKIP_REQUEST, 0x13).
|
||||||
|
type GetMinorBlockHeaderListWithSkipRequest struct {
|
||||||
|
Type uint8
|
||||||
|
Data [HashLength]byte
|
||||||
|
Branch uint32
|
||||||
|
Limit uint32
|
||||||
|
Skip uint32
|
||||||
|
Direction Direction
|
||||||
|
}
|
||||||
646
qkc/cluster/wire/messages_test.go
Normal file
646
qkc/cluster/wire/messages_test.go
Normal file
|
|
@ -0,0 +1,646 @@
|
||||||
|
// Copyright 2026-2027, QuarkChain.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/qkc/serialize"
|
||||||
|
)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §1 Custom wire types
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
func TestPrependedSizeBytes4_RoundTrip(t *testing.T) {
|
||||||
|
cases := [][]byte{
|
||||||
|
nil,
|
||||||
|
{},
|
||||||
|
{0x00},
|
||||||
|
{0xAA, 0xBB},
|
||||||
|
bytes.Repeat([]byte{0xFF}, 100),
|
||||||
|
}
|
||||||
|
for i, data := range cases {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
p := PrependedSizeBytes4(data)
|
||||||
|
var buf []byte
|
||||||
|
if err := p.Serialize(&buf); err != nil {
|
||||||
|
t.Fatalf("case %d: Serialize: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
var got PrependedSizeBytes4
|
||||||
|
if err := got.Deserialize(bb); err != nil {
|
||||||
|
t.Fatalf("case %d: Deserialize: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(got, data) {
|
||||||
|
t.Errorf("case %d: mismatch\n got %x\n want %x", i, got, data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependedSizeBytes4_WireFormat(t *testing.T) {
|
||||||
|
wantHex := "00000002aabb"
|
||||||
|
|
||||||
|
p := PrependedSizeBytes4{0xAA, 0xBB}
|
||||||
|
var buf []byte
|
||||||
|
if err := p.Serialize(&buf); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotHex := hexEncode(buf)
|
||||||
|
if gotHex != wantHex {
|
||||||
|
t.Errorf("wire format mismatch:\n got %s\n want %s", gotHex, wantHex)
|
||||||
|
}
|
||||||
|
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
var got PrependedSizeBytes4
|
||||||
|
if err := got.Deserialize(bb); err != nil {
|
||||||
|
t.Fatalf("Deserialize: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(got, p) {
|
||||||
|
t.Errorf("round-trip mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependedSizeBytes4_Deserialize_InvalidLength(t *testing.T) {
|
||||||
|
buf := []byte{0xFF, 0xFF, 0xFF, 0xFF}
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
|
||||||
|
var p PrependedSizeBytes4
|
||||||
|
err := p.Deserialize(bb)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for length exceeding remaining buffer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependedSizeHashList4_RoundTrip(t *testing.T) {
|
||||||
|
cases := [][][HashLength]byte{
|
||||||
|
nil,
|
||||||
|
{},
|
||||||
|
{makeHash(1)},
|
||||||
|
{makeHash(1), makeHash(2)},
|
||||||
|
}
|
||||||
|
for i, hashes := range cases {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
p := PrependedSizeHashList4(hashes)
|
||||||
|
var buf []byte
|
||||||
|
if err := p.Serialize(&buf); err != nil {
|
||||||
|
t.Fatalf("case %d: Serialize: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
var got PrependedSizeHashList4
|
||||||
|
if err := got.Deserialize(bb); err != nil {
|
||||||
|
t.Fatalf("case %d: Deserialize: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(got) != len(hashes) {
|
||||||
|
t.Fatalf("case %d: length mismatch: got %d, want %d", i, len(got), len(hashes))
|
||||||
|
}
|
||||||
|
for j := range got {
|
||||||
|
if got[j] != hashes[j] {
|
||||||
|
t.Errorf("case %d: hash[%d] mismatch", i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependedSizeHashList4_WireFormat(t *testing.T) {
|
||||||
|
p := PrependedSizeHashList4{makeHash(0x11), makeHash(0x22)}
|
||||||
|
var buf []byte
|
||||||
|
if err := p.Serialize(&buf); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) != 4+2*HashLength {
|
||||||
|
t.Errorf("wire length mismatch: got %d, want %d", len(buf), 4+2*HashLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
// count prefix is already checked by Deserialize; this is supplementary.
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
var got PrependedSizeHashList4
|
||||||
|
if err := got.Deserialize(bb); err != nil {
|
||||||
|
t.Fatalf("Deserialize: %v", err)
|
||||||
|
}
|
||||||
|
if len(got) != 2 {
|
||||||
|
t.Errorf("round-trip length mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrependedSizeHashList4_Deserialize_InvalidLength(t *testing.T) {
|
||||||
|
buf := []byte{0xFF, 0xFF, 0xFF, 0xFF}
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
|
||||||
|
var p PrependedSizeHashList4
|
||||||
|
err := p.Deserialize(bb)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for count exceeding buffer capacity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §2 Message struct round-trips
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// Only structs with *RawBytes as the LAST field (or no RawBytes at all) can
|
||||||
|
// safely round-trip. Structs with non-last RawBytes are only verified via the
|
||||||
|
// factory completeness test (§7) — they serialize correctly but cannot be
|
||||||
|
// deserialized until the real Go type replaces RawBytes.
|
||||||
|
|
||||||
|
func TestMessageRoundTrip(t *testing.T) {
|
||||||
|
toAddr := makeAddress(0x00010001, 2)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
msg any
|
||||||
|
}{
|
||||||
|
// --- no RawBytes ---
|
||||||
|
{"PingRequest_without_root_tip", PingRequest{
|
||||||
|
ID: []byte("slave1"),
|
||||||
|
FullShardIDList: []uint32{0x00010001, 0x00020002},
|
||||||
|
RootTip: nil,
|
||||||
|
}},
|
||||||
|
{"PongResponse", PongResponse{
|
||||||
|
ID: []byte("master"),
|
||||||
|
FullShardIDList: []uint32{0x00010001},
|
||||||
|
}},
|
||||||
|
{"SlaveInfo", SlaveInfo{
|
||||||
|
ID: []byte("s1"),
|
||||||
|
Host: []byte("127.0.0.1"),
|
||||||
|
Port: 38391,
|
||||||
|
FullShardIDList: []uint32{0x00010001, 0x00010002},
|
||||||
|
}},
|
||||||
|
{"ConnectToSlavesRequest", ConnectToSlavesRequest{
|
||||||
|
SlaveInfoList: []SlaveInfo{
|
||||||
|
{ID: []byte("s1"), Host: []byte("10.0.0.1"), Port: 38391, FullShardIDList: []uint32{0x00010001}},
|
||||||
|
{ID: []byte("s2"), Host: []byte("10.0.0.2"), Port: 38392, FullShardIDList: []uint32{0x00020001}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{"ConnectToSlavesResponse", ConnectToSlavesResponse{
|
||||||
|
ResultList: []PrependedSizeBytes4{{0xAA, 0xBB}, {0xCC, 0xDD, 0xEE}},
|
||||||
|
}},
|
||||||
|
{"ArtificialTxConfig", ArtificialTxConfig{60, 10}},
|
||||||
|
{"MineRequest", MineRequest{
|
||||||
|
ArtificialTxConfig: ArtificialTxConfig{TargetRootBlockTime: 60, TargetMinorBlockTime: 10},
|
||||||
|
Mining: true,
|
||||||
|
}},
|
||||||
|
{"EcoInfo", EcoInfo{
|
||||||
|
Branch: 0x00010001,
|
||||||
|
Height: 12345,
|
||||||
|
CoinbaseAmount: big.NewInt(1000),
|
||||||
|
Difficulty: big.NewInt(1000000),
|
||||||
|
UnconfirmedHeadersCoinbaseAmount: big.NewInt(500),
|
||||||
|
}},
|
||||||
|
{"GetEcoInfoListRequest", GetEcoInfoListRequest{}},
|
||||||
|
{"GetEcoInfoListResponse", GetEcoInfoListResponse{
|
||||||
|
ErrorCode: 0,
|
||||||
|
EcoInfoList: []EcoInfo{
|
||||||
|
{Branch: 0x00010001, Height: 1, CoinbaseAmount: big.NewInt(1), Difficulty: big.NewInt(1), UnconfirmedHeadersCoinbaseAmount: big.NewInt(1)},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{"GetNextBlockToMineRequest", GetNextBlockToMineRequest{
|
||||||
|
Branch: 0x00010001,
|
||||||
|
Address: makeAddress(0x00010001, 1),
|
||||||
|
ArtificialTxConfig: ArtificialTxConfig{TargetRootBlockTime: 60, TargetMinorBlockTime: 10},
|
||||||
|
}},
|
||||||
|
{"GetAccountDataRequest", GetAccountDataRequest{
|
||||||
|
Address: makeAddress(0x00010001, 1),
|
||||||
|
BlockHeight: nil,
|
||||||
|
}},
|
||||||
|
{"GetLogRequest", GetLogRequest{
|
||||||
|
Branch: 0x00010001,
|
||||||
|
Addresses: [][AddressLength]byte{makeAddress(0x00010001, 1)},
|
||||||
|
Topics: []PrependedSizeHashList4{
|
||||||
|
{makeHash(0xAA)},
|
||||||
|
{makeHash(0xBB), makeHash(0xCC)},
|
||||||
|
},
|
||||||
|
StartBlock: 100,
|
||||||
|
EndBlock: 200,
|
||||||
|
}},
|
||||||
|
{"PingPongCommand", PingPongCommand{makeHash(42)}},
|
||||||
|
{"PeerInfo", PeerInfo{IP: makeUint128(0x0102030405060708), Port: 38391}},
|
||||||
|
{"TransactionDetail", TransactionDetail{
|
||||||
|
TxHash: makeHash(1),
|
||||||
|
Nonce: 10,
|
||||||
|
FromAddress: makeAddress(0x00010001, 1),
|
||||||
|
ToAddress: &toAddr,
|
||||||
|
Value: big.NewInt(100),
|
||||||
|
BlockHeight: 50,
|
||||||
|
Timestamp: 1600000000,
|
||||||
|
Success: true,
|
||||||
|
GasTokenID: 1,
|
||||||
|
TransferTokenID: 1,
|
||||||
|
IsFromRootChain: false,
|
||||||
|
}},
|
||||||
|
|
||||||
|
// --- RawBytes as last field (safe to round-trip) ---
|
||||||
|
{"GenTxRequest", GenTxRequest{
|
||||||
|
NumTxPerShard: 10,
|
||||||
|
XShardPercent: 30,
|
||||||
|
Tx: &RawBytes{0x01, 0x02, 0x03},
|
||||||
|
}},
|
||||||
|
{"GetNextBlockToMineResponse", GetNextBlockToMineResponse{
|
||||||
|
ErrorCode: 0,
|
||||||
|
Block: &RawBytes{0xAA, 0xBB},
|
||||||
|
}},
|
||||||
|
{"AddTransactionRequest", AddTransactionRequest{
|
||||||
|
Tx: &RawBytes{0x01, 0x02},
|
||||||
|
}},
|
||||||
|
{"CheckMinorBlockRequest", CheckMinorBlockRequest{
|
||||||
|
MinorBlockHeader: &RawBytes{0x01, 0x02},
|
||||||
|
}},
|
||||||
|
{"GetLogResponse", GetLogResponse{
|
||||||
|
ErrorCode: 0,
|
||||||
|
Logs: []*RawBytes{{0x01, 0x02}}, // single element only — multi-element []*RawBytes cannot round-trip
|
||||||
|
}},
|
||||||
|
{"BatchAddXshardTxListRequest", BatchAddXshardTxListRequest{
|
||||||
|
AddXshardTxListRequestList: []AddXshardTxListRequest{
|
||||||
|
{Branch: 0x00010001, MinorBlockHash: makeHash(1), TxList: &RawBytes{0x01, 0x02}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{"AddXshardTxListRequest", AddXshardTxListRequest{
|
||||||
|
Branch: 0x00010001,
|
||||||
|
MinorBlockHash: makeHash(1),
|
||||||
|
TxList: &RawBytes{0x01, 0x02},
|
||||||
|
}},
|
||||||
|
{"NewTransactionListCommand", NewTransactionListCommand{
|
||||||
|
TransactionList: []*RawBytes{{0x01, 0x02}}, // single element only
|
||||||
|
}},
|
||||||
|
{"NewBlockMinorCommand", NewBlockMinorCommand{
|
||||||
|
Block: &RawBytes{0x01, 0x02},
|
||||||
|
}},
|
||||||
|
{"NewRootBlockCommand", NewRootBlockCommand{
|
||||||
|
Block: &RawBytes{0x01, 0x02},
|
||||||
|
}},
|
||||||
|
{"GetRootBlockListResponse", GetRootBlockListResponse{
|
||||||
|
RootBlockList: []*RawBytes{{0x01, 0x02}}, // single element only
|
||||||
|
}},
|
||||||
|
{"GetMinorBlockListResponse", GetMinorBlockListResponse{
|
||||||
|
MinorBlockList: []*RawBytes{{0x01, 0x02}}, // single element only
|
||||||
|
}},
|
||||||
|
{"GetUnconfirmedHeadersResponse", GetUnconfirmedHeadersResponse{
|
||||||
|
ErrorCode: 0,
|
||||||
|
HeadersInfoList: []HeadersInfo{
|
||||||
|
{Branch: 0x00010001, HeaderList: []*RawBytes{{0x01, 0x02}}}, // single element only
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
{"PingRequest_with_root_tip", PingRequest{
|
||||||
|
ID: []byte("test"),
|
||||||
|
FullShardIDList: []uint32{1, 2},
|
||||||
|
RootTip: &RawBytes{0xAA, 0xBB, 0xCC},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, tc.msg); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bb := serialize.NewByteBuffer(buf)
|
||||||
|
got := reflect.New(reflect.TypeOf(tc.msg)).Interface()
|
||||||
|
if err := serialize.Deserialize(bb, got); err != nil {
|
||||||
|
t.Fatalf("Deserialize: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gotVal := reflect.ValueOf(got).Elem().Interface()
|
||||||
|
|
||||||
|
// Re-serialize and compare bytes. This sidesteps reflect.DeepEqual
|
||||||
|
// pointer-identity problems with *RawBytes while still verifying
|
||||||
|
// that the wire format is preserved through round-trip.
|
||||||
|
var buf2 []byte
|
||||||
|
if err := serialize.Serialize(&buf2, gotVal); err != nil {
|
||||||
|
t.Fatalf("Re-serialize: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf, buf2) {
|
||||||
|
t.Errorf("round-trip mismatch\n got %x\n want %x", buf2, buf)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §3 ser:"nil" behavior
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
func TestOptionalMarker_PresentAndAbsent(t *testing.T) {
|
||||||
|
absent := PingRequest{ID: []byte("x"), RootTip: nil}
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, &absent); err != nil {
|
||||||
|
t.Fatalf("Serialize absent: %v", err)
|
||||||
|
}
|
||||||
|
if buf[len(buf)-1] != 0x00 {
|
||||||
|
t.Errorf("absent optional should end with 0x00, got %x", buf[len(buf)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
present := PingRequest{ID: []byte("x"), RootTip: &RawBytes{0xAA}}
|
||||||
|
buf = nil
|
||||||
|
if err := serialize.Serialize(&buf, &present); err != nil {
|
||||||
|
t.Fatalf("Serialize present: %v", err)
|
||||||
|
}
|
||||||
|
if buf[len(buf)-2] != 0x01 || buf[len(buf)-1] != 0xAA {
|
||||||
|
t.Errorf("present optional should write marker 0x01 then data, got %x", buf[len(buf)-2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonOptionalRawBytes_NoMarker(t *testing.T) {
|
||||||
|
req := AddRootBlockRequest{RootBlock: &RawBytes{0xAA}, ExpectSwitch: true}
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, &req); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
if buf[0] != 0xAA {
|
||||||
|
t.Errorf("non-optional RawBytes should not have presence marker, got %x", buf[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §4 Wire compatibility (hand-computed vectors)
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// These test vectors are hand-computed from the Python FIELDS definitions to
|
||||||
|
// verify that Go serialization produces identical bytes. They are NOT produced
|
||||||
|
// by running pyquarkchain directly.
|
||||||
|
|
||||||
|
func TestWireCompat_PingRequest(t *testing.T) {
|
||||||
|
wantHex := "0000000474657374000000020000000100000002" + "00"
|
||||||
|
|
||||||
|
ping := PingRequest{
|
||||||
|
ID: []byte("test"),
|
||||||
|
FullShardIDList: []uint32{1, 2},
|
||||||
|
RootTip: nil,
|
||||||
|
}
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, &ping); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
gotHex := hexEncode(buf)
|
||||||
|
if gotHex != wantHex {
|
||||||
|
t.Errorf("wire mismatch:\n got %s\n want %s", gotHex, wantHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWireCompat_PongResponse(t *testing.T) {
|
||||||
|
wantHex := "000000026f6b0000000100000003"
|
||||||
|
|
||||||
|
pong := PongResponse{
|
||||||
|
ID: []byte("ok"),
|
||||||
|
FullShardIDList: []uint32{3},
|
||||||
|
}
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, &pong); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
gotHex := hexEncode(buf)
|
||||||
|
if gotHex != wantHex {
|
||||||
|
t.Errorf("wire mismatch:\n got %s\n want %s", gotHex, wantHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWireCompat_SlaveInfo(t *testing.T) {
|
||||||
|
wantHex := "000000027331" +
|
||||||
|
"000000096c6f63616c686f7374" +
|
||||||
|
"95f7" +
|
||||||
|
"0000000100010001"
|
||||||
|
|
||||||
|
slave := SlaveInfo{
|
||||||
|
ID: []byte("s1"),
|
||||||
|
Host: []byte("localhost"),
|
||||||
|
Port: 38391,
|
||||||
|
FullShardIDList: []uint32{0x00010001},
|
||||||
|
}
|
||||||
|
var buf []byte
|
||||||
|
if err := serialize.Serialize(&buf, &slave); err != nil {
|
||||||
|
t.Fatalf("Serialize: %v", err)
|
||||||
|
}
|
||||||
|
gotHex := hexEncode(buf)
|
||||||
|
if gotHex != wantHex {
|
||||||
|
t.Errorf("wire mismatch:\n got %s\n want %s", gotHex, wantHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §5 Field order verification
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
func TestFieldOrder(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
typ reflect.Type
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{"PingRequest", reflect.TypeFor[PingRequest](), []string{"ID", "FullShardIDList", "RootTip"}},
|
||||||
|
{"PongResponse", reflect.TypeFor[PongResponse](), []string{"ID", "FullShardIDList"}},
|
||||||
|
{"SlaveInfo", reflect.TypeFor[SlaveInfo](), []string{"ID", "Host", "Port", "FullShardIDList"}},
|
||||||
|
{"EcoInfo", reflect.TypeFor[EcoInfo](), []string{"Branch", "Height", "CoinbaseAmount", "Difficulty", "UnconfirmedHeadersCoinbaseAmount"}},
|
||||||
|
{"ShardStats", reflect.TypeFor[ShardStats](), []string{"Branch", "Height", "Difficulty", "CoinbaseAddress", "Timestamp",
|
||||||
|
"TxCount60s", "PendingTxCount", "TotalTxCount", "BlockCount60s", "StaleBlockCount60s", "LastBlockTime"}},
|
||||||
|
{"TransactionDetail", reflect.TypeFor[TransactionDetail](), []string{"TxHash", "Nonce", "FromAddress", "ToAddress", "Value",
|
||||||
|
"BlockHeight", "Timestamp", "Success", "GasTokenID", "TransferTokenID", "IsFromRootChain"}},
|
||||||
|
{"HelloCommand", reflect.TypeFor[HelloCommand](), []string{"Version", "NetworkID", "PeerID", "PeerIP", "PeerPort",
|
||||||
|
"ChainMaskList", "RootBlockHeader", "GenesisRootBlockHash"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if tc.typ.NumField() != len(tc.expected) {
|
||||||
|
t.Fatalf("field count mismatch: got %d, want %d", tc.typ.NumField(), len(tc.expected))
|
||||||
|
}
|
||||||
|
for i, want := range tc.expected {
|
||||||
|
if tc.typ.Field(i).Name != want {
|
||||||
|
t.Errorf("field %d: got %s, want %s", i, tc.typ.Field(i).Name, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// §6 Factory completeness
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
func TestNewClusterMessage_Completeness(t *testing.T) {
|
||||||
|
ops := []ClusterOp{
|
||||||
|
ClusterOpPing,
|
||||||
|
ClusterOpPong,
|
||||||
|
ClusterOpConnectToSlavesRequest,
|
||||||
|
ClusterOpConnectToSlavesResponse,
|
||||||
|
ClusterOpAddRootBlockRequest,
|
||||||
|
ClusterOpAddRootBlockResponse,
|
||||||
|
ClusterOpGetEcoInfoListRequest,
|
||||||
|
ClusterOpGetEcoInfoListResponse,
|
||||||
|
ClusterOpGetNextBlockToMineRequest,
|
||||||
|
ClusterOpGetNextBlockToMineResponse,
|
||||||
|
ClusterOpGetUnconfirmedHeadersRequest,
|
||||||
|
ClusterOpGetUnconfirmedHeadersResponse,
|
||||||
|
ClusterOpGetAccountDataRequest,
|
||||||
|
ClusterOpGetAccountDataResponse,
|
||||||
|
ClusterOpAddTransactionRequest,
|
||||||
|
ClusterOpAddTransactionResponse,
|
||||||
|
ClusterOpAddMinorBlockHeaderRequest,
|
||||||
|
ClusterOpAddMinorBlockHeaderResponse,
|
||||||
|
ClusterOpAddXshardTxListRequest,
|
||||||
|
ClusterOpAddXshardTxListResponse,
|
||||||
|
ClusterOpSyncMinorBlockListRequest,
|
||||||
|
ClusterOpSyncMinorBlockListResponse,
|
||||||
|
ClusterOpAddMinorBlockRequest,
|
||||||
|
ClusterOpAddMinorBlockResponse,
|
||||||
|
ClusterOpCreateClusterPeerConnectionRequest,
|
||||||
|
ClusterOpCreateClusterPeerConnectionResponse,
|
||||||
|
ClusterOpDestroyClusterPeerConnectionCommand,
|
||||||
|
ClusterOpGetMinorBlockRequest,
|
||||||
|
ClusterOpGetMinorBlockResponse,
|
||||||
|
ClusterOpGetTransactionRequest,
|
||||||
|
ClusterOpGetTransactionResponse,
|
||||||
|
ClusterOpBatchAddXshardTxListRequest,
|
||||||
|
ClusterOpBatchAddXshardTxListResponse,
|
||||||
|
ClusterOpExecuteTransactionRequest,
|
||||||
|
ClusterOpExecuteTransactionResponse,
|
||||||
|
ClusterOpGetTransactionReceiptRequest,
|
||||||
|
ClusterOpGetTransactionReceiptResponse,
|
||||||
|
ClusterOpMineRequest,
|
||||||
|
ClusterOpMineResponse,
|
||||||
|
ClusterOpGenTxRequest,
|
||||||
|
ClusterOpGenTxResponse,
|
||||||
|
ClusterOpGetTransactionListByAddressRequest,
|
||||||
|
ClusterOpGetTransactionListByAddressResponse,
|
||||||
|
ClusterOpGetLogRequest,
|
||||||
|
ClusterOpGetLogResponse,
|
||||||
|
ClusterOpEstimateGasRequest,
|
||||||
|
ClusterOpEstimateGasResponse,
|
||||||
|
ClusterOpGetStorageRequest,
|
||||||
|
ClusterOpGetStorageResponse,
|
||||||
|
ClusterOpGetCodeRequest,
|
||||||
|
ClusterOpGetCodeResponse,
|
||||||
|
ClusterOpGasPriceRequest,
|
||||||
|
ClusterOpGasPriceResponse,
|
||||||
|
ClusterOpGetWorkRequest,
|
||||||
|
ClusterOpGetWorkResponse,
|
||||||
|
ClusterOpSubmitWorkRequest,
|
||||||
|
ClusterOpSubmitWorkResponse,
|
||||||
|
ClusterOpAddMinorBlockHeaderListRequest,
|
||||||
|
ClusterOpAddMinorBlockHeaderListResponse,
|
||||||
|
ClusterOpCheckMinorBlockRequest,
|
||||||
|
ClusterOpCheckMinorBlockResponse,
|
||||||
|
ClusterOpGetAllTransactionsRequest,
|
||||||
|
ClusterOpGetAllTransactionsResponse,
|
||||||
|
ClusterOpGetRootChainStakesRequest,
|
||||||
|
ClusterOpGetRootChainStakesResponse,
|
||||||
|
ClusterOpGetTotalBalanceRequest,
|
||||||
|
ClusterOpGetTotalBalanceResponse,
|
||||||
|
}
|
||||||
|
for _, op := range ops {
|
||||||
|
t.Run("ClusterOp(0x"+string("0123456789ABCDEF"[byte(op)>>4])+string("0123456789ABCDEF"[byte(op)&0x0F])+")", func(t *testing.T) {
|
||||||
|
msg, err := NewClusterMessage(op)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewClusterMessage(0x%x): %v", op, err)
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
t.Fatalf("NewClusterMessage(0x%x) returned nil", op)
|
||||||
|
}
|
||||||
|
typ := reflect.TypeOf(msg)
|
||||||
|
if typ.Kind() != reflect.Pointer || typ.Elem().Kind() != reflect.Struct {
|
||||||
|
t.Errorf("expected *struct, got %T", msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewCommandMessage_Completeness(t *testing.T) {
|
||||||
|
ops := []CommandOp{
|
||||||
|
CommandOpHello,
|
||||||
|
CommandOpNewMinorBlockHeaderList,
|
||||||
|
CommandOpNewTransactionList,
|
||||||
|
CommandOpGetPeerListRequest,
|
||||||
|
CommandOpGetPeerListResponse,
|
||||||
|
CommandOpGetRootBlockHeaderListRequest,
|
||||||
|
CommandOpGetRootBlockHeaderListResponse,
|
||||||
|
CommandOpGetRootBlockListRequest,
|
||||||
|
CommandOpGetRootBlockListResponse,
|
||||||
|
CommandOpGetMinorBlockListRequest,
|
||||||
|
CommandOpGetMinorBlockListResponse,
|
||||||
|
CommandOpGetMinorBlockHeaderListRequest,
|
||||||
|
CommandOpGetMinorBlockHeaderListResponse,
|
||||||
|
CommandOpNewBlockMinor,
|
||||||
|
CommandOpPing,
|
||||||
|
CommandOpPong,
|
||||||
|
CommandOpGetRootBlockHeaderListWithSkipRequest,
|
||||||
|
CommandOpGetRootBlockHeaderListWithSkipResponse,
|
||||||
|
CommandOpNewRootBlock,
|
||||||
|
CommandOpGetMinorBlockHeaderListWithSkipRequest,
|
||||||
|
CommandOpGetMinorBlockHeaderListWithSkipResponse,
|
||||||
|
}
|
||||||
|
for _, op := range ops {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
msg, err := NewCommandMessage(op)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewCommandMessage(0x%x): %v", op, err)
|
||||||
|
}
|
||||||
|
if msg == nil {
|
||||||
|
t.Fatalf("NewCommandMessage(0x%x) returned nil", op)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewClusterMessage_UnknownOpcode(t *testing.T) {
|
||||||
|
_, err := NewClusterMessage(ClusterOp(0xFF))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for unknown ClusterOp")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewCommandMessage_UnknownOpcode(t *testing.T) {
|
||||||
|
_, err := NewCommandMessage(CommandOp(0xEE))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error for unknown CommandOp")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Helpers
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
func makeAddress(fullShardID uint32, recipient byte) [AddressLength]byte {
|
||||||
|
var addr [AddressLength]byte
|
||||||
|
addr[16] = byte(fullShardID >> 24)
|
||||||
|
addr[17] = byte(fullShardID >> 16)
|
||||||
|
addr[18] = byte(fullShardID >> 8)
|
||||||
|
addr[19] = byte(fullShardID)
|
||||||
|
addr[0] = recipient
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeHash(seed byte) [HashLength]byte {
|
||||||
|
var h [HashLength]byte
|
||||||
|
for i := range h {
|
||||||
|
h[i] = seed + byte(i)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUint128(seed uint64) [UInt128Length]byte {
|
||||||
|
var u [UInt128Length]byte
|
||||||
|
for i := range 8 {
|
||||||
|
u[i] = byte(seed >> (56 - 8*i))
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func hexEncode(b []byte) string {
|
||||||
|
const hexChars = "0123456789abcdef"
|
||||||
|
s := make([]byte, len(b)*2)
|
||||||
|
for i, v := range b {
|
||||||
|
s[i*2] = hexChars[v>>4]
|
||||||
|
s[i*2+1] = hexChars[v&0x0f]
|
||||||
|
}
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
236
qkc/cluster/wire/protocol.go
Normal file
236
qkc/cluster/wire/protocol.go
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
// Copyright 2026-2027, QuarkChain.
|
||||||
|
|
||||||
|
// Package wire: opcode → message factory and protocol-level enumeration
|
||||||
|
// types. This file maps each ClusterOp / CommandOp to the concrete message
|
||||||
|
// struct that decodes a frame payload of that opcode.
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Direction matches the P2P sync Direction enum in
|
||||||
|
// quarkchain/cluster/p2p_commands.py.
|
||||||
|
//
|
||||||
|
// DIRECTIONS = [GENESIS, TIP]
|
||||||
|
// class Direction(IntEnum):
|
||||||
|
// GENESIS = 0
|
||||||
|
// TIP = 1
|
||||||
|
type Direction uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
DirectionGenesis Direction = 0
|
||||||
|
DirectionTip Direction = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d Direction) String() string {
|
||||||
|
switch d {
|
||||||
|
case DirectionGenesis:
|
||||||
|
return "GENESIS"
|
||||||
|
case DirectionTip:
|
||||||
|
return "TIP"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Direction(%d)", uint8(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClusterMessage returns a new empty message struct pointer corresponding
|
||||||
|
// to the given ClusterOp. Used by slave/master_conn.go to allocate the
|
||||||
|
// concrete type before deserialising a frame payload.
|
||||||
|
func NewClusterMessage(op ClusterOp) (interface{}, error) {
|
||||||
|
switch op {
|
||||||
|
case ClusterOpPing:
|
||||||
|
return &PingRequest{}, nil
|
||||||
|
case ClusterOpPong:
|
||||||
|
return &PongResponse{}, nil
|
||||||
|
case ClusterOpConnectToSlavesRequest:
|
||||||
|
return &ConnectToSlavesRequest{}, nil
|
||||||
|
case ClusterOpConnectToSlavesResponse:
|
||||||
|
return &ConnectToSlavesResponse{}, nil
|
||||||
|
case ClusterOpAddRootBlockRequest:
|
||||||
|
return &AddRootBlockRequest{}, nil
|
||||||
|
case ClusterOpAddRootBlockResponse:
|
||||||
|
return &AddRootBlockResponse{}, nil
|
||||||
|
case ClusterOpGetEcoInfoListRequest:
|
||||||
|
return &GetEcoInfoListRequest{}, nil
|
||||||
|
case ClusterOpGetEcoInfoListResponse:
|
||||||
|
return &GetEcoInfoListResponse{}, nil
|
||||||
|
case ClusterOpGetNextBlockToMineRequest:
|
||||||
|
return &GetNextBlockToMineRequest{}, nil
|
||||||
|
case ClusterOpGetNextBlockToMineResponse:
|
||||||
|
return &GetNextBlockToMineResponse{}, nil
|
||||||
|
case ClusterOpGetUnconfirmedHeadersRequest:
|
||||||
|
return &GetUnconfirmedHeadersRequest{}, nil
|
||||||
|
case ClusterOpGetUnconfirmedHeadersResponse:
|
||||||
|
return &GetUnconfirmedHeadersResponse{}, nil
|
||||||
|
case ClusterOpGetAccountDataRequest:
|
||||||
|
return &GetAccountDataRequest{}, nil
|
||||||
|
case ClusterOpGetAccountDataResponse:
|
||||||
|
return &GetAccountDataResponse{}, nil
|
||||||
|
case ClusterOpAddTransactionRequest:
|
||||||
|
return &AddTransactionRequest{}, nil
|
||||||
|
case ClusterOpAddTransactionResponse:
|
||||||
|
return &AddTransactionResponse{}, nil
|
||||||
|
case ClusterOpAddMinorBlockHeaderRequest:
|
||||||
|
return &AddMinorBlockHeaderRequest{}, nil
|
||||||
|
case ClusterOpAddMinorBlockHeaderResponse:
|
||||||
|
return &AddMinorBlockHeaderResponse{}, nil
|
||||||
|
case ClusterOpAddXshardTxListRequest:
|
||||||
|
return &AddXshardTxListRequest{}, nil
|
||||||
|
case ClusterOpAddXshardTxListResponse:
|
||||||
|
return &AddXshardTxListResponse{}, nil
|
||||||
|
case ClusterOpSyncMinorBlockListRequest:
|
||||||
|
return &SyncMinorBlockListRequest{}, nil
|
||||||
|
case ClusterOpSyncMinorBlockListResponse:
|
||||||
|
return &SyncMinorBlockListResponse{}, nil
|
||||||
|
case ClusterOpAddMinorBlockRequest:
|
||||||
|
return &AddMinorBlockRequest{}, nil
|
||||||
|
case ClusterOpAddMinorBlockResponse:
|
||||||
|
return &AddMinorBlockResponse{}, nil
|
||||||
|
case ClusterOpCreateClusterPeerConnectionRequest:
|
||||||
|
return &CreateClusterPeerConnectionRequest{}, nil
|
||||||
|
case ClusterOpCreateClusterPeerConnectionResponse:
|
||||||
|
return &CreateClusterPeerConnectionResponse{}, nil
|
||||||
|
case ClusterOpDestroyClusterPeerConnectionCommand:
|
||||||
|
return &DestroyClusterPeerConnectionCommand{}, nil
|
||||||
|
case ClusterOpGetMinorBlockRequest:
|
||||||
|
return &GetMinorBlockRequest{}, nil
|
||||||
|
case ClusterOpGetMinorBlockResponse:
|
||||||
|
return &GetMinorBlockResponse{}, nil
|
||||||
|
case ClusterOpGetTransactionRequest:
|
||||||
|
return &GetTransactionRequest{}, nil
|
||||||
|
case ClusterOpGetTransactionResponse:
|
||||||
|
return &GetTransactionResponse{}, nil
|
||||||
|
case ClusterOpBatchAddXshardTxListRequest:
|
||||||
|
return &BatchAddXshardTxListRequest{}, nil
|
||||||
|
case ClusterOpBatchAddXshardTxListResponse:
|
||||||
|
return &BatchAddXshardTxListResponse{}, nil
|
||||||
|
case ClusterOpExecuteTransactionRequest:
|
||||||
|
return &ExecuteTransactionRequest{}, nil
|
||||||
|
case ClusterOpExecuteTransactionResponse:
|
||||||
|
return &ExecuteTransactionResponse{}, nil
|
||||||
|
case ClusterOpGetTransactionReceiptRequest:
|
||||||
|
return &GetTransactionReceiptRequest{}, nil
|
||||||
|
case ClusterOpGetTransactionReceiptResponse:
|
||||||
|
return &GetTransactionReceiptResponse{}, nil
|
||||||
|
case ClusterOpMineRequest:
|
||||||
|
return &MineRequest{}, nil
|
||||||
|
case ClusterOpMineResponse:
|
||||||
|
return &MineResponse{}, nil
|
||||||
|
case ClusterOpGenTxRequest:
|
||||||
|
return &GenTxRequest{}, nil
|
||||||
|
case ClusterOpGenTxResponse:
|
||||||
|
return &GenTxResponse{}, nil
|
||||||
|
case ClusterOpGetTransactionListByAddressRequest:
|
||||||
|
return &GetTransactionListByAddressRequest{}, nil
|
||||||
|
case ClusterOpGetTransactionListByAddressResponse:
|
||||||
|
return &GetTransactionListByAddressResponse{}, nil
|
||||||
|
case ClusterOpGetLogRequest:
|
||||||
|
return &GetLogRequest{}, nil
|
||||||
|
case ClusterOpGetLogResponse:
|
||||||
|
return &GetLogResponse{}, nil
|
||||||
|
case ClusterOpEstimateGasRequest:
|
||||||
|
return &EstimateGasRequest{}, nil
|
||||||
|
case ClusterOpEstimateGasResponse:
|
||||||
|
return &EstimateGasResponse{}, nil
|
||||||
|
case ClusterOpGetStorageRequest:
|
||||||
|
return &GetStorageRequest{}, nil
|
||||||
|
case ClusterOpGetStorageResponse:
|
||||||
|
return &GetStorageResponse{}, nil
|
||||||
|
case ClusterOpGetCodeRequest:
|
||||||
|
return &GetCodeRequest{}, nil
|
||||||
|
case ClusterOpGetCodeResponse:
|
||||||
|
return &GetCodeResponse{}, nil
|
||||||
|
case ClusterOpGasPriceRequest:
|
||||||
|
return &GasPriceRequest{}, nil
|
||||||
|
case ClusterOpGasPriceResponse:
|
||||||
|
return &GasPriceResponse{}, nil
|
||||||
|
case ClusterOpGetWorkRequest:
|
||||||
|
return &GetWorkRequest{}, nil
|
||||||
|
case ClusterOpGetWorkResponse:
|
||||||
|
return &GetWorkResponse{}, nil
|
||||||
|
case ClusterOpSubmitWorkRequest:
|
||||||
|
return &SubmitWorkRequest{}, nil
|
||||||
|
case ClusterOpSubmitWorkResponse:
|
||||||
|
return &SubmitWorkResponse{}, nil
|
||||||
|
case ClusterOpAddMinorBlockHeaderListRequest:
|
||||||
|
return &AddMinorBlockHeaderListRequest{}, nil
|
||||||
|
case ClusterOpAddMinorBlockHeaderListResponse:
|
||||||
|
return &AddMinorBlockHeaderListResponse{}, nil
|
||||||
|
case ClusterOpCheckMinorBlockRequest:
|
||||||
|
return &CheckMinorBlockRequest{}, nil
|
||||||
|
case ClusterOpCheckMinorBlockResponse:
|
||||||
|
return &CheckMinorBlockResponse{}, nil
|
||||||
|
case ClusterOpGetAllTransactionsRequest:
|
||||||
|
return &GetAllTransactionsRequest{}, nil
|
||||||
|
case ClusterOpGetAllTransactionsResponse:
|
||||||
|
return &GetAllTransactionsResponse{}, nil
|
||||||
|
case ClusterOpGetRootChainStakesRequest:
|
||||||
|
return &GetRootChainStakesRequest{}, nil
|
||||||
|
case ClusterOpGetRootChainStakesResponse:
|
||||||
|
return &GetRootChainStakesResponse{}, nil
|
||||||
|
case ClusterOpGetTotalBalanceRequest:
|
||||||
|
return &GetTotalBalanceRequest{}, nil
|
||||||
|
case ClusterOpGetTotalBalanceResponse:
|
||||||
|
return &GetTotalBalanceResponse{}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown ClusterOp: 0x%x", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCommandMessage returns a new empty message struct pointer corresponding
|
||||||
|
// to the given CommandOp. Note: both PING and PONG share PingPongCommand —
|
||||||
|
// this matches the Python implementation where the same class is registered
|
||||||
|
// for both opcodes (see p2p_commands.py: REGISTER_OP_TO_SERIALIZER).
|
||||||
|
func NewCommandMessage(op CommandOp) (interface{}, error) {
|
||||||
|
switch op {
|
||||||
|
case CommandOpHello:
|
||||||
|
return &HelloCommand{}, nil
|
||||||
|
case CommandOpNewMinorBlockHeaderList:
|
||||||
|
return &NewMinorBlockHeaderListCommand{}, nil
|
||||||
|
case CommandOpNewTransactionList:
|
||||||
|
return &NewTransactionListCommand{}, nil
|
||||||
|
case CommandOpGetPeerListRequest:
|
||||||
|
return &GetPeerListRequest{}, nil
|
||||||
|
case CommandOpGetPeerListResponse:
|
||||||
|
return &GetPeerListResponse{}, nil
|
||||||
|
case CommandOpGetRootBlockHeaderListRequest:
|
||||||
|
return &GetRootBlockHeaderListRequest{}, nil
|
||||||
|
case CommandOpGetRootBlockHeaderListResponse:
|
||||||
|
return &GetRootBlockHeaderListResponse{}, nil
|
||||||
|
case CommandOpGetRootBlockListRequest:
|
||||||
|
return &GetRootBlockListRequest{}, nil
|
||||||
|
case CommandOpGetRootBlockListResponse:
|
||||||
|
return &GetRootBlockListResponse{}, nil
|
||||||
|
case CommandOpGetMinorBlockListRequest:
|
||||||
|
return &GetMinorBlockListRequest{}, nil
|
||||||
|
case CommandOpGetMinorBlockListResponse:
|
||||||
|
return &GetMinorBlockListResponse{}, nil
|
||||||
|
case CommandOpGetMinorBlockHeaderListRequest:
|
||||||
|
return &GetMinorBlockHeaderListRequest{}, nil
|
||||||
|
case CommandOpGetMinorBlockHeaderListResponse:
|
||||||
|
return &GetMinorBlockHeaderListResponse{}, nil
|
||||||
|
case CommandOpNewBlockMinor:
|
||||||
|
return &NewBlockMinorCommand{}, nil
|
||||||
|
case CommandOpPing, CommandOpPong:
|
||||||
|
return &PingPongCommand{}, nil
|
||||||
|
case CommandOpGetRootBlockHeaderListWithSkipRequest:
|
||||||
|
return &GetRootBlockHeaderListWithSkipRequest{}, nil
|
||||||
|
case CommandOpGetRootBlockHeaderListWithSkipResponse:
|
||||||
|
// Shares GetRootBlockHeaderListResponse with CommandOpGetRootBlockHeaderListResponse (0x06).
|
||||||
|
// Python's REGISTER_OP_TO_SERIALIZER also maps both opcodes (0x06, 0x11) to the same class.
|
||||||
|
// Verified: p2p_commands.py line 357.
|
||||||
|
return &GetRootBlockHeaderListResponse{}, nil
|
||||||
|
case CommandOpNewRootBlock:
|
||||||
|
return &NewRootBlockCommand{}, nil
|
||||||
|
case CommandOpGetMinorBlockHeaderListWithSkipRequest:
|
||||||
|
return &GetMinorBlockHeaderListWithSkipRequest{}, nil
|
||||||
|
case CommandOpGetMinorBlockHeaderListWithSkipResponse:
|
||||||
|
// Shares GetMinorBlockHeaderListResponse with CommandOpGetMinorBlockHeaderListResponse (0x0C).
|
||||||
|
// Python's REGISTER_OP_TO_SERIALIZER also maps both opcodes (0x0C, 0x14) to the same class.
|
||||||
|
// Verified: p2p_commands.py line 360.
|
||||||
|
return &GetMinorBlockHeaderListResponse{}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown CommandOp: 0x%x", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
57
qkc/cluster/wire/rawbytes_placeholder.go
Normal file
57
qkc/cluster/wire/rawbytes_placeholder.go
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2026-2027, QuarkChain.
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// TEMPORARY PLACEHOLDER FILE — DELETE after real types merge
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// RawBytes is a placeholder used during pyquarkchain → Go migration.
|
||||||
|
// Delete this file once real types (RootBlock, MinorBlockHeader, etc.) are ported.
|
||||||
|
// Replace `*RawBytes` fields in messages.go with real typed pointers.
|
||||||
|
//
|
||||||
|
// DO NOT REVIEW AS PRODUCTION CODE.
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/qkc/serialize"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RawBytes is a transparent byte passthrough placeholder for unported complex types.
|
||||||
|
type RawBytes []byte
|
||||||
|
|
||||||
|
func (r RawBytes) Serialize(w *[]byte) error {
|
||||||
|
const maxRawBytesSize = 100 * 1024 * 1024 // 100 MB
|
||||||
|
if len(r) > maxRawBytesSize {
|
||||||
|
return fmt.Errorf("RawBytes.Serialize: size %d exceeds max %d", len(r), maxRawBytesSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
*w = append(*w, r...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize consumes all remaining bytes from the buffer.
|
||||||
|
//
|
||||||
|
// SAFETY: This is only safe when RawBytes is the LAST field in its parent
|
||||||
|
// struct. If RawBytes appears before other fields, consuming the remaining
|
||||||
|
// bytes will corrupt subsequent fields. Structs with non-last RawBytes fields
|
||||||
|
// are marked with a WARNING in messages.go and cannot be correctly deserialized
|
||||||
|
// until the real Go type is ported.
|
||||||
|
//
|
||||||
|
// TEMPORARY: Delete this placeholder once real types (RootBlock, MinorBlockHeader) are ported.
|
||||||
|
func (r *RawBytes) Deserialize(bb *serialize.ByteBuffer) error {
|
||||||
|
const maxRawBytesSize = 100 * 1024 * 1024 // 100 MB, matches Serialize
|
||||||
|
if bb.Remaining() > maxRawBytesSize {
|
||||||
|
return fmt.Errorf("RawBytes.Deserialize: size %d exceeds max %d", bb.Remaining(), maxRawBytesSize)
|
||||||
|
}
|
||||||
|
bytes, err := bb.ReadRemaining()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*r = RawBytes(bytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time check: RawBytes implements Serializable (required by serialize package).
|
||||||
|
// This ensures serialize.Serialize(&buf, &struct{Field *RawBytes}) works.
|
||||||
|
var _ serialize.Serializable = (*RawBytes)(nil)
|
||||||
90
qkc/cluster/wire/types.go
Normal file
90
qkc/cluster/wire/types.go
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2026-2027, QuarkChain.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/qkc/serialize"
|
||||||
|
)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Custom wire types for Python format compatibility
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// Python uses 4-byte length prefix for nested slices in some wire messages.
|
||||||
|
// Go's serialize framework defaults to 1-byte prefix for nested elements.
|
||||||
|
// These custom types enforce 4-byte prefix to match Python wire format.
|
||||||
|
|
||||||
|
// PrependedSizeBytes4 is []byte with 4-byte length prefix (matches Python PrependedSizeBytesSerializer(4)).
|
||||||
|
type PrependedSizeBytes4 []byte
|
||||||
|
|
||||||
|
func (p PrependedSizeBytes4) Serialize(w *[]byte) error {
|
||||||
|
lenBuf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(lenBuf, uint32(len(p)))
|
||||||
|
*w = append(*w, lenBuf...)
|
||||||
|
*w = append(*w, p...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrependedSizeBytes4) Deserialize(bb *serialize.ByteBuffer) error {
|
||||||
|
length, err := bb.GetUInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > math.MaxInt32 || int(length) > bb.Remaining() {
|
||||||
|
return fmt.Errorf("PrependedSizeBytes4.Deserialize: length %d exceeds remaining %d", length, bb.Remaining())
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := bb.ReadBytes(int(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = PrependedSizeBytes4(bytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ serialize.Serializable = (*PrependedSizeBytes4)(nil)
|
||||||
|
|
||||||
|
// PrependedSizeHashList4 is [][HashLength]byte with 4-byte length prefix (matches Python PrependedSizeListSerializer(4, hash256)).
|
||||||
|
type PrependedSizeHashList4 [][HashLength]byte
|
||||||
|
|
||||||
|
func (p PrependedSizeHashList4) Serialize(w *[]byte) error {
|
||||||
|
lenBuf := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(lenBuf, uint32(len(p)))
|
||||||
|
*w = append(*w, lenBuf...)
|
||||||
|
|
||||||
|
for _, hash := range p {
|
||||||
|
*w = append(*w, hash[:]...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrependedSizeHashList4) Deserialize(bb *serialize.ByteBuffer) error {
|
||||||
|
length, err := bb.GetUInt32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if length > math.MaxInt32/HashLength || int(length)*HashLength > bb.Remaining() {
|
||||||
|
return fmt.Errorf("PrependedSizeHashList4.Deserialize: length %d exceeds capacity", length)
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([][HashLength]byte, length)
|
||||||
|
for i := 0; i < int(length); i++ {
|
||||||
|
hashBytes, err := bb.ReadBytes(HashLength)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list[i] = [HashLength]byte(hashBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = PrependedSizeHashList4(list)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ serialize.Serializable = (*PrependedSizeHashList4)(nil)
|
||||||
Loading…
Reference in a new issue