Skip to content

Commit

Permalink
Merge pull request #228 from SiaFoundation/v2-header
Browse files Browse the repository at this point in the history
Refactor BlockHeader
  • Loading branch information
n8maninger authored Nov 22, 2024
2 parents 51068a3 + dae65a3 commit 611d481
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 103 deletions.
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

0 comments on commit 611d481

Please sign in to comment.