Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor BlockHeader #228

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
Expand All @@ -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
}
}
Expand All @@ -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
}
}
Expand All @@ -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
}
}
Expand Down
44 changes: 7 additions & 37 deletions gateway/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down
48 changes: 6 additions & 42 deletions gateway/outline.go
Original file line number Diff line number Diff line change
@@ -1,50 +1,13 @@
package gateway

import (
"encoding/binary"
"time"

"go.sia.tech/core/consensus"
"go.sia.tech/core/internal/blake2b"
"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 {
Expand Down Expand Up @@ -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.
Expand Down
44 changes: 44 additions & 0 deletions types/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,18 @@ func EncodePtr[T any, P interface {
}
}

// EncodePtrCast encodes a pointer to an object by casting it to V.
func EncodePtrCast[V interface {
Cast() T
EncoderTo
}, T any](e *Encoder, p *T) {
e.WriteBool(p != nil)
if p != nil {
vp := *(*V)(unsafe.Pointer(p))
vp.EncodeTo(e)
}
}

// EncodeSlice encodes a slice of objects that implement EncoderTo.
func EncodeSlice[T EncoderTo](e *Encoder, s []T) {
e.WriteUint64(uint64(len(s)))
Expand Down Expand Up @@ -254,6 +266,22 @@ func DecodePtr[T any, TP interface {
}
}

// DecodePtrCast decodes a pointer to an object by casting it to V.
func DecodePtrCast[T interface {
Cast() V
}, TP interface {
*T
DecoderFrom
}, V any](d *Decoder, p **V) {
tp := (**T)(unsafe.Pointer(p))
if d.ReadBool() {
*tp = new(T)
TP(*tp).DecodeFrom(d)
} else {
*tp = nil
}
}

// DecodeSlice decodes a length-prefixed slice of type T, containing values read
// from the decoder.
func DecodeSlice[T any, DF interface {
Expand Down Expand Up @@ -852,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

Expand Down Expand Up @@ -1329,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)
Expand Down
20 changes: 20 additions & 0 deletions types/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ import (
"lukechampine.com/frand"
)

func TestEncodePtrCast(t *testing.T) {
var buf bytes.Buffer
e := types.NewEncoder(&buf)
c := types.Siacoins(1)
types.EncodePtrCast[types.V1Currency](e, &c)
types.EncodePtrCast[types.V2Currency](e, &c)
types.EncodePtrCast[types.V2Currency](e, nil)
e.Flush()
var c1, c2, c3 *types.Currency
d := types.NewBufDecoder(buf.Bytes())
types.DecodePtrCast[types.V1Currency](d, &c1)
types.DecodePtrCast[types.V2Currency](d, &c2)
types.DecodePtrCast[types.V2Currency](d, &c3)
if err := d.Err(); err != nil {
t.Fatal(err)
} else if *c1 != c || *c2 != c || c3 != nil {
t.Fatal("mismatch:", c1, c2, c3)
}
}

func TestEncodeSlice(t *testing.T) {
txns := multiproofTxns(10, 10)
var buf bytes.Buffer
Expand Down
57 changes: 38 additions & 19 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down