github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/block/block.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package block 5 6 import ( 7 "errors" 8 "fmt" 9 "time" 10 11 "github.com/MetalBlockchain/metalgo/ids" 12 "github.com/MetalBlockchain/metalgo/staking" 13 "github.com/MetalBlockchain/metalgo/utils/hashing" 14 "github.com/MetalBlockchain/metalgo/utils/wrappers" 15 ) 16 17 var ( 18 _ SignedBlock = (*statelessBlock)(nil) 19 20 errUnexpectedSignature = errors.New("signature provided when none was expected") 21 errInvalidCertificate = errors.New("invalid certificate") 22 ) 23 24 type Block interface { 25 ID() ids.ID 26 ParentID() ids.ID 27 Block() []byte 28 Bytes() []byte 29 30 initialize(bytes []byte) error 31 verify(chainID ids.ID) error 32 } 33 34 type SignedBlock interface { 35 Block 36 37 PChainHeight() uint64 38 Timestamp() time.Time 39 40 // Proposer returns the ID of the node that proposed this block. If no node 41 // signed this block, [ids.EmptyNodeID] will be returned. 42 Proposer() ids.NodeID 43 } 44 45 type statelessUnsignedBlock struct { 46 ParentID ids.ID `serialize:"true"` 47 Timestamp int64 `serialize:"true"` 48 PChainHeight uint64 `serialize:"true"` 49 Certificate []byte `serialize:"true"` 50 Block []byte `serialize:"true"` 51 } 52 53 type statelessBlock struct { 54 StatelessBlock statelessUnsignedBlock `serialize:"true"` 55 Signature []byte `serialize:"true"` 56 57 id ids.ID 58 timestamp time.Time 59 cert *staking.Certificate 60 proposer ids.NodeID 61 bytes []byte 62 } 63 64 func (b *statelessBlock) ID() ids.ID { 65 return b.id 66 } 67 68 func (b *statelessBlock) ParentID() ids.ID { 69 return b.StatelessBlock.ParentID 70 } 71 72 func (b *statelessBlock) Block() []byte { 73 return b.StatelessBlock.Block 74 } 75 76 func (b *statelessBlock) Bytes() []byte { 77 return b.bytes 78 } 79 80 func (b *statelessBlock) initialize(bytes []byte) error { 81 b.bytes = bytes 82 83 // The serialized form of the block is the unsignedBytes followed by the 84 // signature, which is prefixed by a uint32. So, we need to strip off the 85 // signature as well as it's length prefix to get the unsigned bytes. 86 lenUnsignedBytes := len(bytes) - wrappers.IntLen - len(b.Signature) 87 unsignedBytes := bytes[:lenUnsignedBytes] 88 b.id = hashing.ComputeHash256Array(unsignedBytes) 89 90 b.timestamp = time.Unix(b.StatelessBlock.Timestamp, 0) 91 if len(b.StatelessBlock.Certificate) == 0 { 92 return nil 93 } 94 95 var err error 96 b.cert, err = staking.ParseCertificate(b.StatelessBlock.Certificate) 97 if err != nil { 98 return fmt.Errorf("%w: %w", errInvalidCertificate, err) 99 } 100 101 b.proposer = ids.NodeIDFromCert(b.cert) 102 return nil 103 } 104 105 func (b *statelessBlock) verify(chainID ids.ID) error { 106 if len(b.StatelessBlock.Certificate) == 0 { 107 if len(b.Signature) > 0 { 108 return errUnexpectedSignature 109 } 110 return nil 111 } 112 113 header, err := BuildHeader(chainID, b.StatelessBlock.ParentID, b.id) 114 if err != nil { 115 return err 116 } 117 118 headerBytes := header.Bytes() 119 return staking.CheckSignature( 120 b.cert, 121 headerBytes, 122 b.Signature, 123 ) 124 } 125 126 func (b *statelessBlock) PChainHeight() uint64 { 127 return b.StatelessBlock.PChainHeight 128 } 129 130 func (b *statelessBlock) Timestamp() time.Time { 131 return b.timestamp 132 } 133 134 func (b *statelessBlock) Proposer() ids.NodeID { 135 return b.proposer 136 }