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 }