From dae65a3de0ea41d950ccb00e84f85dafac308610 Mon Sep 17 00:00:00 2001 From: lukechampine Date: Fri, 8 Nov 2024 20:50:50 -0500 Subject: [PATCH] types,gateway: Refactor BlockHeader --- consensus/state.go | 10 ++++---- gateway/encoding.go | 44 ++++++---------------------------- gateway/outline.go | 48 +++++--------------------------------- types/encoding.go | 16 +++++++++++++ types/types.go | 57 ++++++++++++++++++++++++++++++--------------- 5 files changed, 72 insertions(+), 103 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index 85757d8f..2db710f4 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -609,7 +609,7 @@ type MidState struct { } func (ms *MidState) siacoinElement(ts V1TransactionSupplement, id types.SiacoinOutputID) (types.SiacoinElement, bool) { - if i, ok := ms.created[types.Hash256(id)]; ok { + if i, ok := ms.created[id]; ok { return ms.sces[i], true } return ts.siacoinElement(id) @@ -714,7 +714,7 @@ func (ts *V1TransactionSupplement) DecodeFrom(d *types.Decoder) { func (ts V1TransactionSupplement) siacoinElement(id types.SiacoinOutputID) (sce types.SiacoinElement, ok bool) { for _, sce := range ts.SiacoinInputs { - if types.SiacoinOutputID(sce.ID) == id { + if sce.ID == id { return sce, true } } @@ -723,7 +723,7 @@ func (ts V1TransactionSupplement) siacoinElement(id types.SiacoinOutputID) (sce func (ts V1TransactionSupplement) siafundElement(id types.SiafundOutputID) (sfe types.SiafundElement, ok bool) { for _, sfe := range ts.SiafundInputs { - if types.SiafundOutputID(sfe.ID) == id { + if sfe.ID == id { return sfe, true } } @@ -732,7 +732,7 @@ func (ts V1TransactionSupplement) siafundElement(id types.SiafundOutputID) (sfe func (ts V1TransactionSupplement) revision(id types.FileContractID) (fce types.FileContractElement, ok bool) { for _, fce := range ts.RevisedFileContracts { - if types.FileContractID(fce.ID) == id { + if fce.ID == id { return fce, true } } @@ -741,7 +741,7 @@ func (ts V1TransactionSupplement) revision(id types.FileContractID) (fce types.F func (ts V1TransactionSupplement) storageProof(id types.FileContractID) (sps V1StorageProofSupplement, ok bool) { for _, sps := range ts.StorageProofs { - if types.FileContractID(sps.FileContract.ID) == id { + if sps.FileContract.ID == id { return sps, true } } diff --git a/gateway/encoding.go b/gateway/encoding.go index 31b44fa8..997029e1 100644 --- a/gateway/encoding.go +++ b/gateway/encoding.go @@ -53,36 +53,6 @@ func (h *Header) decodeFrom(d *types.Decoder) { h.NetAddress = d.ReadString() } -func (h *BlockHeader) encodeTo(e *types.Encoder) { - h.ParentID.EncodeTo(e) - e.WriteUint64(h.Nonce) - e.WriteTime(h.Timestamp) - h.MerkleRoot.EncodeTo(e) -} - -func (h *BlockHeader) decodeFrom(d *types.Decoder) { - h.ParentID.DecodeFrom(d) - h.Nonce = d.ReadUint64() - h.Timestamp = d.ReadTime() - h.MerkleRoot.DecodeFrom(d) -} - -func (h *V2BlockHeader) encodeTo(e *types.Encoder) { - h.Parent.EncodeTo(e) - e.WriteUint64(h.Nonce) - e.WriteTime(h.Timestamp) - h.TransactionsRoot.EncodeTo(e) - h.MinerAddress.EncodeTo(e) -} - -func (h *V2BlockHeader) decodeFrom(d *types.Decoder) { - h.Parent.DecodeFrom(d) - h.Nonce = d.ReadUint64() - h.Timestamp = d.ReadTime() - h.TransactionsRoot.DecodeFrom(d) - h.MinerAddress.DecodeFrom(d) -} - func (ob *V2BlockOutline) encodeTo(e *types.Encoder) { e.WriteUint64(ob.Height) ob.ParentID.EncodeTo(e) @@ -261,12 +231,12 @@ func (r *RPCSendBlk) maxResponseLen() int { return 5e6 } // RPCRelayHeader relays a header. type RPCRelayHeader struct { - Header BlockHeader + Header types.BlockHeader emptyResponse } -func (r *RPCRelayHeader) encodeRequest(e *types.Encoder) { r.Header.encodeTo(e) } -func (r *RPCRelayHeader) decodeRequest(d *types.Decoder) { r.Header.decodeFrom(d) } +func (r *RPCRelayHeader) encodeRequest(e *types.Encoder) { r.Header.EncodeTo(e) } +func (r *RPCRelayHeader) decodeRequest(d *types.Decoder) { r.Header.DecodeFrom(d) } func (r *RPCRelayHeader) maxRequestLen() int { return 32 + 8 + 8 + 32 } // RPCRelayTransactionSet relays a transaction set. @@ -364,13 +334,13 @@ func (r *RPCSendCheckpoint) maxResponseLen() int { return 5e6 + 4e3 } // RPCRelayV2Header relays a v2 block header. type RPCRelayV2Header struct { - Header V2BlockHeader + Header types.BlockHeader emptyResponse } -func (r *RPCRelayV2Header) encodeRequest(e *types.Encoder) { r.Header.encodeTo(e) } -func (r *RPCRelayV2Header) decodeRequest(d *types.Decoder) { r.Header.decodeFrom(d) } -func (r *RPCRelayV2Header) maxRequestLen() int { return 8 + 32 + 8 + 8 + 32 + 32 } +func (r *RPCRelayV2Header) encodeRequest(e *types.Encoder) { r.Header.EncodeTo(e) } +func (r *RPCRelayV2Header) decodeRequest(d *types.Decoder) { r.Header.DecodeFrom(d) } +func (r *RPCRelayV2Header) maxRequestLen() int { return 8 + 32 + 32 + 8 } // RPCRelayV2BlockOutline relays a v2 block outline. type RPCRelayV2BlockOutline struct { diff --git a/gateway/outline.go b/gateway/outline.go index 9bf9873c..15ab8b1a 100644 --- a/gateway/outline.go +++ b/gateway/outline.go @@ -1,7 +1,6 @@ package gateway import ( - "encoding/binary" "time" "go.sia.tech/core/consensus" @@ -9,42 +8,6 @@ import ( "go.sia.tech/core/types" ) -// A BlockHeader contains a Block's non-transaction data. -type BlockHeader struct { - ParentID types.BlockID - Nonce uint64 - Timestamp time.Time - MerkleRoot types.Hash256 -} - -// ID returns a hash that uniquely identifies the block. -func (h BlockHeader) ID() types.BlockID { - buf := make([]byte, 32+8+8+32) - copy(buf[:32], h.ParentID[:]) - binary.LittleEndian.PutUint64(buf[32:], h.Nonce) - binary.LittleEndian.PutUint64(buf[40:], uint64(h.Timestamp.Unix())) - copy(buf[48:], h.MerkleRoot[:]) - return types.BlockID(types.HashBytes(buf)) -} - -// A V2BlockHeader contains a V2Block's non-transaction data. -type V2BlockHeader struct { - Parent types.ChainIndex - Nonce uint64 - Timestamp time.Time - TransactionsRoot types.Hash256 - MinerAddress types.Address -} - -// ID returns a hash that uniquely identifies the block. -func (h V2BlockHeader) ID(cs consensus.State) types.BlockID { - return (&types.Block{ - Nonce: h.Nonce, - Timestamp: h.Timestamp, - V2: &types.V2BlockData{Commitment: cs.Commitment(h.TransactionsRoot, h.MinerAddress)}, - }).ID() -} - // An OutlineTransaction identifies a transaction by its full hash. The actual // transaction data may or may not be present. type OutlineTransaction struct { @@ -75,11 +38,12 @@ func (bo V2BlockOutline) commitment(cs consensus.State) types.Hash256 { // ID returns a hash that uniquely identifies the block. func (bo V2BlockOutline) ID(cs consensus.State) types.BlockID { - return (&types.Block{ - Nonce: bo.Nonce, - Timestamp: bo.Timestamp, - V2: &types.V2BlockData{Commitment: bo.commitment(cs)}, - }).ID() + return types.BlockHeader{ + ParentID: bo.ParentID, + Nonce: bo.Nonce, + Timestamp: bo.Timestamp, + Commitment: bo.commitment(cs), + }.ID() } // Missing returns the hashes of transactions that are missing from the block. diff --git a/types/encoding.go b/types/encoding.go index 0ecddf54..58586630 100644 --- a/types/encoding.go +++ b/types/encoding.go @@ -880,6 +880,14 @@ func (b V2BlockData) EncodeTo(e *Encoder) { V2TransactionsMultiproof(b.Transactions).EncodeTo(e) } +// EncodeTo implements types.EncoderTo. +func (h BlockHeader) EncodeTo(e *Encoder) { + h.ParentID.EncodeTo(e) + e.WriteUint64(h.Nonce) + e.WriteTime(h.Timestamp) + h.Commitment.EncodeTo(e) +} + // V1Block provides v1 encoding for Block. type V1Block Block @@ -1357,6 +1365,14 @@ func (b *V2BlockData) DecodeFrom(d *Decoder) { (*V2TransactionsMultiproof)(&b.Transactions).DecodeFrom(d) } +// DecodeFrom implements types.DecoderFrom. +func (h *BlockHeader) DecodeFrom(d *Decoder) { + h.ParentID.DecodeFrom(d) + h.Nonce = d.ReadUint64() + h.Timestamp = d.ReadTime() + h.Commitment.DecodeFrom(d) +} + // DecodeFrom implements types.DecoderFrom. func (b *V1Block) DecodeFrom(d *Decoder) { b.ParentID.DecodeFrom(d) diff --git a/types/types.go b/types/types.go index 981721f9..94bfbd11 100644 --- a/types/types.go +++ b/types/types.go @@ -804,7 +804,26 @@ type V2BlockData struct { Transactions []V2Transaction `json:"transactions"` } -// A Block is a set of transactions grouped under a header. +// A BlockHeader is the preimage of a Block's ID. +type BlockHeader struct { + ParentID BlockID `json:"parentID"` + Nonce uint64 `json:"nonce"` + Timestamp time.Time `json:"timestamp"` + Commitment Hash256 `json:"commitment"` +} + +// ID returns the hash of the header data. +func (bh BlockHeader) ID() BlockID { + buf := make([]byte, 32+8+8+32) + copy(buf[:32], bh.ParentID[:]) + binary.LittleEndian.PutUint64(buf[32:], bh.Nonce) + binary.LittleEndian.PutUint64(buf[40:], uint64(bh.Timestamp.Unix())) + copy(buf[48:], bh.Commitment[:]) + return BlockID(HashBytes(buf)) +} + +// A Block is a timestamped set of transactions, immutably linked to a previous +// block, secured by proof-of-work. type Block struct { ParentID BlockID `json:"parentID"` Nonce uint64 `json:"nonce"` @@ -815,12 +834,6 @@ type Block struct { V2 *V2BlockData `json:"v2,omitempty"` } -// MerkleRoot returns the Merkle root of the block's miner payouts and -// transactions. -func (b *Block) MerkleRoot() Hash256 { - return blockMerkleRoot(b.MinerPayouts, b.Transactions) -} - // V2Transactions returns the block's v2 transactions, if present. func (b *Block) V2Transactions() []V2Transaction { if b.V2 != nil { @@ -829,20 +842,26 @@ func (b *Block) V2Transactions() []V2Transaction { return nil } -// ID returns a hash that uniquely identifies a block. -func (b *Block) ID() BlockID { - buf := make([]byte, 32+8+8+32) - binary.LittleEndian.PutUint64(buf[32:], b.Nonce) - binary.LittleEndian.PutUint64(buf[40:], uint64(b.Timestamp.Unix())) - if b.V2 != nil { - copy(buf[:32], "sia/id/block|") - copy(buf[48:], b.V2.Commitment[:]) +// Header returns the block's header. +func (b *Block) Header() BlockHeader { + var commitment Hash256 + if b.V2 == nil { + // NOTE: expensive! + commitment = blockMerkleRoot(b.MinerPayouts, b.Transactions) } else { - root := b.MerkleRoot() // NOTE: expensive! - copy(buf[:32], b.ParentID[:]) - copy(buf[48:], root[:]) + commitment = b.V2.Commitment } - return BlockID(HashBytes(buf)) + return BlockHeader{ + ParentID: b.ParentID, + Nonce: b.Nonce, + Timestamp: b.Timestamp, + Commitment: commitment, + } +} + +// ID returns a hash that uniquely identifies a block. +func (b *Block) ID() BlockID { + return b.Header().ID() } func unmarshalHex(dst []byte, data []byte) error {