github.com/mavryk-network/mvgo@v1.19.9/codec/block.go (about)

     1  // Copyright (c) 2020-2022 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package codec
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/mavryk-network/mvgo/mavryk"
    13  )
    14  
    15  // BlockHeader represents a Tenderbake compatible block header
    16  type BlockHeader struct {
    17  	Level            int32                 `json:"level"`
    18  	Proto            byte                  `json:"proto"`
    19  	Predecessor      mavryk.BlockHash      `json:"predecessor"`
    20  	Timestamp        time.Time             `json:"timestamp"`
    21  	ValidationPass   byte                  `json:"validation_pass"`
    22  	OperationsHash   mavryk.OpListListHash `json:"operations_hash"`
    23  	Fitness          []mavryk.HexBytes     `json:"fitness"`
    24  	Context          mavryk.ContextHash    `json:"context"`
    25  	PayloadHash      mavryk.PayloadHash    `json:"payload_hash"`
    26  	PayloadRound     int                   `json:"payload_round"`
    27  	ProofOfWorkNonce mavryk.HexBytes       `json:"proof_of_work_nonce"`
    28  	SeedNonceHash    mavryk.NonceHash      `json:"seed_nonce_hash"`
    29  	LbVote           mavryk.FeatureVote    `json:"liquidity_baking_toggle_vote"`
    30  	AiVote           mavryk.FeatureVote    `json:"adaptive_issuance_vote"`
    31  	Signature        mavryk.Signature      `json:"signature"`
    32  	ChainId          *mavryk.ChainIdHash   `json:"-"` // remote signer use only
    33  }
    34  
    35  // Bytes serializes the block header into binary form. When no signature is set, the
    36  // result can be used as input for signing, if a signature is set the result is
    37  // ready for broadcast.
    38  func (h BlockHeader) Bytes() []byte {
    39  	buf := bytes.NewBuffer(nil)
    40  	_ = h.EncodeBuffer(buf)
    41  	return buf.Bytes()
    42  }
    43  
    44  // WatermarkedBytes serializes the block header and prefixes it with a watermark.
    45  // This format is only used for signing.
    46  func (h BlockHeader) WatermarkedBytes() []byte {
    47  	buf := bytes.NewBuffer(nil)
    48  	buf.WriteByte(TenderbakeBlockWatermark)
    49  	buf.Write(h.ChainId.Bytes())
    50  	_ = h.EncodeBuffer(buf)
    51  	return buf.Bytes()
    52  }
    53  
    54  // Digest returns a 32 byte blake2b hash for signing the block header. The pre-image
    55  // is binary serialized (without signature) and prefixed with a watermark byte.
    56  func (h BlockHeader) Digest() []byte {
    57  	d := mavryk.Digest(h.WatermarkedBytes())
    58  	return d[:]
    59  }
    60  
    61  // Hash calculates the block hash. For the hash to be correct, the block
    62  // must contain a valid signature.
    63  func (h *BlockHeader) Hash() (s mavryk.BlockHash) {
    64  	d := mavryk.Digest(h.Bytes())
    65  	copy(s[:], d[:])
    66  	return
    67  }
    68  
    69  // Sign signs the block header using a private key and generates a generic signature.
    70  // If a valid signature already exists, this function is a noop.
    71  func (h *BlockHeader) Sign(key mavryk.PrivateKey) error {
    72  	if h.Signature.IsValid() {
    73  		return nil
    74  	}
    75  	sig, err := key.Sign(h.Digest())
    76  	sig.Type = mavryk.SignatureTypeGeneric
    77  	if err != nil {
    78  		return err
    79  	}
    80  	h.Signature = sig
    81  	return nil
    82  }
    83  
    84  // WithChainId sets chain_id for this block to id. Use this only for remote signing
    85  // of blocks as it creates an invalid binary encoding otherwise.
    86  func (h *BlockHeader) WithChainId(id mavryk.ChainIdHash) *BlockHeader {
    87  	clone := id.Clone()
    88  	h.ChainId = &clone
    89  	return h
    90  }
    91  
    92  // WithSignature adds an externally created signature to the block header. Converts
    93  // any non-generic signature first. No signature validation is performed, it is
    94  // assumed the signature is correct.
    95  func (h *BlockHeader) WithSignature(sig mavryk.Signature) *BlockHeader {
    96  	sig = sig.Clone()
    97  	sig.Type = mavryk.SignatureTypeGeneric
    98  	h.Signature = sig
    99  	return h
   100  }
   101  
   102  func (h BlockHeader) MarshalJSON() ([]byte, error) {
   103  	buf := bytes.NewBuffer(nil)
   104  	buf.WriteByte('{')
   105  	buf.WriteString(`"level":`)
   106  	buf.WriteString(strconv.FormatInt(int64(h.Level), 10))
   107  	buf.WriteString(`,"proto":`)
   108  	buf.WriteString(strconv.Itoa(int(h.Proto)))
   109  	buf.WriteString(`,"predecessor":`)
   110  	buf.WriteString(strconv.Quote(h.Predecessor.String()))
   111  	buf.WriteString(`,"timestamp":`)
   112  	buf.WriteString(strconv.Quote(h.Timestamp.UTC().Format("2006-01-02T15:04:05Z")))
   113  	buf.WriteString(`,"validation_pass":`)
   114  	buf.WriteString(strconv.Itoa(int(h.ValidationPass)))
   115  	buf.WriteString(`,"operations_hash":`)
   116  	buf.WriteString(strconv.Quote(h.OperationsHash.String()))
   117  	buf.WriteString(`,"fitness":[`)
   118  	for i, v := range h.Fitness {
   119  		if i > 0 {
   120  			buf.WriteByte(',')
   121  		}
   122  		buf.WriteString(strconv.Quote(v.String()))
   123  	}
   124  	buf.WriteString(`],"context":`)
   125  	buf.WriteString(strconv.Quote(h.Context.String()))
   126  	buf.WriteString(`,"payload_hash":`)
   127  	buf.WriteString(strconv.Quote(h.PayloadHash.String()))
   128  	buf.WriteString(`,"payload_round":`)
   129  	buf.WriteString(strconv.Itoa(h.PayloadRound))
   130  	buf.WriteString(`,"proof_of_work_nonce":`)
   131  	buf.WriteString(strconv.Quote(h.ProofOfWorkNonce.String()))
   132  	if h.SeedNonceHash.IsValid() {
   133  		buf.WriteString(`,"seed_nonce_hash":`)
   134  		buf.WriteString(strconv.Quote(h.SeedNonceHash.String()))
   135  	}
   136  	buf.WriteString(`,"liquidity_baking_toggle_vote":`)
   137  	buf.WriteString(strconv.Quote(h.LbVote.String()))
   138  	buf.WriteString(`,"adaptive_issuance_vote":`)
   139  	buf.WriteString(strconv.Quote(h.AiVote.String()))
   140  	if h.Signature.IsValid() {
   141  		buf.WriteString(`,"signature":`)
   142  		buf.WriteString(strconv.Quote(h.Signature.String()))
   143  	}
   144  	buf.WriteByte('}')
   145  	return buf.Bytes(), nil
   146  }
   147  
   148  func (h *BlockHeader) EncodeBuffer(buf *bytes.Buffer) error {
   149  	binary.Write(buf, enc, h.Level)
   150  	buf.WriteByte(h.Proto)
   151  	buf.Write(h.Predecessor.Bytes())
   152  	binary.Write(buf, enc, h.Timestamp.Unix())
   153  	buf.WriteByte(h.ValidationPass)
   154  	buf.Write(h.OperationsHash.Bytes())
   155  	var fitnessLen int
   156  	for _, v := range h.Fitness {
   157  		fitnessLen += len(v)
   158  	}
   159  	binary.Write(buf, enc, uint32(fitnessLen+4*len(h.Fitness)))
   160  	for _, v := range h.Fitness {
   161  		binary.Write(buf, enc, uint32(len(v)))
   162  		buf.Write(v)
   163  	}
   164  	buf.Write(h.Context.Bytes())
   165  	buf.Write(h.PayloadHash.Bytes())
   166  	binary.Write(buf, enc, uint32(h.PayloadRound))
   167  	buf.Write(h.ProofOfWorkNonce)
   168  	if h.SeedNonceHash.IsValid() {
   169  		buf.WriteByte(0xff)
   170  		buf.Write(h.SeedNonceHash.Bytes())
   171  	} else {
   172  		buf.WriteByte(0x0)
   173  	}
   174  	// BROKEN: merging multiple vote flags is undocumented
   175  	buf.WriteByte(h.LbVote.Tag() | (h.AiVote.Tag() << 2))
   176  	if h.Signature.IsValid() {
   177  		buf.Write(h.Signature.Data) // raw, no tag!
   178  	}
   179  	return nil
   180  }
   181  
   182  func (h *BlockHeader) DecodeBuffer(buf *bytes.Buffer) (err error) {
   183  	h.Level, err = readInt32(buf.Next(4))
   184  	if err != nil {
   185  		return
   186  	}
   187  	h.Proto, err = readByte(buf.Next(1))
   188  	if err != nil {
   189  		return
   190  	}
   191  	if err = h.Predecessor.UnmarshalBinary(buf.Next(32)); err != nil {
   192  		return
   193  	}
   194  	var i64 int64
   195  	i64, err = readInt64(buf.Next(8))
   196  	if err != nil {
   197  		return
   198  	}
   199  	h.Timestamp = time.Unix(i64, 0).UTC()
   200  	h.ValidationPass, err = readByte(buf.Next(1))
   201  	if err != nil {
   202  		return
   203  	}
   204  	if err = h.OperationsHash.UnmarshalBinary(buf.Next(32)); err != nil {
   205  		return
   206  	}
   207  	var l int32
   208  	l, err = readInt32(buf.Next(4))
   209  	if err != nil {
   210  		return
   211  	}
   212  	h.Fitness = make([]mavryk.HexBytes, 0)
   213  	for l > 0 {
   214  		var n int32
   215  		n, err = readInt32(buf.Next(4))
   216  		if err != nil {
   217  			return
   218  		}
   219  		b := make([]byte, int(n))
   220  		copy(b, buf.Next(int(n)))
   221  		h.Fitness = append(h.Fitness, b)
   222  		l -= n + 4
   223  	}
   224  	if err = h.Context.UnmarshalBinary(buf.Next(32)); err != nil {
   225  		return
   226  	}
   227  	if err = h.PayloadHash.UnmarshalBinary(buf.Next(32)); err != nil {
   228  		return
   229  	}
   230  	l, err = readInt32(buf.Next(4))
   231  	if err != nil {
   232  		return
   233  	}
   234  	h.PayloadRound = int(l)
   235  	h.ProofOfWorkNonce = make([]byte, 8)
   236  	copy(h.ProofOfWorkNonce[:], buf.Next(8))
   237  	var ok bool
   238  	ok, err = readBool(buf.Next(1))
   239  	if err != nil {
   240  		return
   241  	}
   242  	if ok {
   243  		if err = h.SeedNonceHash.UnmarshalBinary(buf.Next(32)); err != nil {
   244  			return
   245  		}
   246  	}
   247  	// BROKEN: merging multiple vote flags is undocumented
   248  	b := buf.Next(1)
   249  	if len(b) > 0 {
   250  		if err = h.LbVote.UnmarshalBinary([]byte{b[0] & 3}); err != nil {
   251  			return
   252  		}
   253  		if err = h.AiVote.UnmarshalBinary([]byte{(b[0] >> 2) & 3}); err != nil {
   254  			return
   255  		}
   256  	}
   257  	// conditionally read signature
   258  	if buf.Len() > 0 {
   259  		err = h.Signature.UnmarshalBinary(buf.Next(64))
   260  		if err != nil {
   261  			return
   262  		}
   263  	}
   264  	return nil
   265  }
   266  
   267  func (h BlockHeader) MarshalBinary() ([]byte, error) {
   268  	buf := bytes.NewBuffer(nil)
   269  	err := h.EncodeBuffer(buf)
   270  	return buf.Bytes(), err
   271  }
   272  
   273  func (h *BlockHeader) UnmarshalBinary(data []byte) error {
   274  	return h.DecodeBuffer(bytes.NewBuffer(data))
   275  }