github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/block/block.go (about) 1 package block 2 3 import ( 4 "encoding/json" 5 "errors" 6 "math" 7 "math/big" 8 9 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 10 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 11 "github.com/nspcc-dev/neo-go/pkg/io" 12 "github.com/nspcc-dev/neo-go/pkg/util" 13 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 14 ) 15 16 const ( 17 // MaxTransactionsPerBlock is the maximum number of transactions per block. 18 MaxTransactionsPerBlock = math.MaxUint16 19 ) 20 21 // ErrMaxContentsPerBlock is returned when the maximum number of contents per block is reached. 22 var ErrMaxContentsPerBlock = errors.New("the number of contents exceeds the maximum number of contents per block") 23 24 var expectedHeaderSizeWithEmptyWitness int 25 26 func init() { 27 expectedHeaderSizeWithEmptyWitness = io.GetVarSize(new(Header)) 28 } 29 30 // Block represents one block in the chain. 31 type Block struct { 32 // The base of the block. 33 Header 34 35 // Transaction list. 36 Transactions []*transaction.Transaction 37 38 // True if this block is created from trimmed data. 39 Trimmed bool 40 } 41 42 // auxBlockOut is used for JSON i/o. 43 type auxBlockOut struct { 44 Transactions []*transaction.Transaction `json:"tx"` 45 } 46 47 // auxBlockIn is used for JSON i/o. 48 type auxBlockIn struct { 49 Transactions []json.RawMessage `json:"tx"` 50 } 51 52 // ComputeMerkleRoot computes Merkle tree root hash based on actual block's data. 53 func (b *Block) ComputeMerkleRoot() util.Uint256 { 54 hashes := make([]util.Uint256, len(b.Transactions)) 55 for i, tx := range b.Transactions { 56 hashes[i] = tx.Hash() 57 } 58 59 return hash.CalcMerkleRoot(hashes) 60 } 61 62 // RebuildMerkleRoot rebuilds the merkleroot of the block. 63 func (b *Block) RebuildMerkleRoot() { 64 b.MerkleRoot = b.ComputeMerkleRoot() 65 } 66 67 // NewTrimmedFromReader returns a new block from trimmed data. 68 // This is commonly used to create a block from stored data. 69 // Blocks created from trimmed data will have their Trimmed field 70 // set to true. 71 func NewTrimmedFromReader(stateRootEnabled bool, br *io.BinReader) (*Block, error) { 72 block := &Block{ 73 Header: Header{ 74 StateRootEnabled: stateRootEnabled, 75 }, 76 Trimmed: true, 77 } 78 79 block.Header.DecodeBinary(br) 80 lenHashes := br.ReadVarUint() 81 if lenHashes > MaxTransactionsPerBlock { 82 return nil, ErrMaxContentsPerBlock 83 } 84 if lenHashes > 0 { 85 block.Transactions = make([]*transaction.Transaction, lenHashes) 86 for i := 0; i < int(lenHashes); i++ { 87 var hash util.Uint256 88 hash.DecodeBinary(br) 89 block.Transactions[i] = transaction.NewTrimmedTX(hash) 90 } 91 } 92 93 return block, br.Err 94 } 95 96 // New creates a new blank block with proper state root setting. 97 func New(stateRootEnabled bool) *Block { 98 return &Block{ 99 Header: Header{ 100 StateRootEnabled: stateRootEnabled, 101 }, 102 } 103 } 104 105 // EncodeTrimmed writes trimmed representation of the block data into w. Trimmed blocks 106 // do not store complete transactions, instead they only store their hashes. 107 func (b *Block) EncodeTrimmed(w *io.BinWriter) { 108 b.Header.EncodeBinary(w) 109 110 w.WriteVarUint(uint64(len(b.Transactions))) 111 for _, tx := range b.Transactions { 112 h := tx.Hash() 113 h.EncodeBinary(w) 114 } 115 } 116 117 // DecodeBinary decodes the block from the given BinReader, implementing 118 // Serializable interface. 119 func (b *Block) DecodeBinary(br *io.BinReader) { 120 b.Header.DecodeBinary(br) 121 contentsCount := br.ReadVarUint() 122 if contentsCount > MaxTransactionsPerBlock { 123 br.Err = ErrMaxContentsPerBlock 124 return 125 } 126 txes := make([]*transaction.Transaction, contentsCount) 127 for i := 0; i < int(contentsCount); i++ { 128 tx := &transaction.Transaction{} 129 tx.DecodeBinary(br) 130 txes[i] = tx 131 } 132 b.Transactions = txes 133 if br.Err != nil { 134 return 135 } 136 } 137 138 // EncodeBinary encodes the block to the given BinWriter, implementing 139 // Serializable interface. 140 func (b *Block) EncodeBinary(bw *io.BinWriter) { 141 b.Header.EncodeBinary(bw) 142 bw.WriteVarUint(uint64(len(b.Transactions))) 143 for i := 0; i < len(b.Transactions); i++ { 144 b.Transactions[i].EncodeBinary(bw) 145 } 146 } 147 148 // MarshalJSON implements the json.Marshaler interface. 149 func (b Block) MarshalJSON() ([]byte, error) { 150 abo := auxBlockOut{ 151 Transactions: b.Transactions, 152 } 153 // `"tx": []` (C#) vs `"tx": null` (default Go when missing any transactions) 154 if abo.Transactions == nil { 155 abo.Transactions = []*transaction.Transaction{} 156 } 157 auxb, err := json.Marshal(abo) 158 if err != nil { 159 return nil, err 160 } 161 baseBytes, err := json.Marshal(b.Header) 162 if err != nil { 163 return nil, err 164 } 165 166 // Stitch them together. 167 if baseBytes[len(baseBytes)-1] != '}' || auxb[0] != '{' { 168 return nil, errors.New("can't merge internal jsons") 169 } 170 baseBytes[len(baseBytes)-1] = ',' 171 baseBytes = append(baseBytes, auxb[1:]...) 172 return baseBytes, nil 173 } 174 175 // UnmarshalJSON implements the json.Unmarshaler interface. 176 func (b *Block) UnmarshalJSON(data []byte) error { 177 // As Base and auxb are at the same level in json, 178 // do unmarshalling separately for both structs. 179 auxb := new(auxBlockIn) 180 err := json.Unmarshal(data, auxb) 181 if err != nil { 182 return err 183 } 184 err = json.Unmarshal(data, &b.Header) 185 if err != nil { 186 return err 187 } 188 if len(auxb.Transactions) != 0 { 189 b.Transactions = make([]*transaction.Transaction, 0, len(auxb.Transactions)) 190 for _, txBytes := range auxb.Transactions { 191 tx := &transaction.Transaction{} 192 err = tx.UnmarshalJSON(txBytes) 193 if err != nil { 194 return err 195 } 196 b.Transactions = append(b.Transactions, tx) 197 } 198 } 199 return nil 200 } 201 202 // GetExpectedBlockSize returns the expected block size which should be equal to io.GetVarSize(b). 203 func (b *Block) GetExpectedBlockSize() int { 204 var transactionsSize int 205 for _, tx := range b.Transactions { 206 transactionsSize += tx.Size() 207 } 208 return b.GetExpectedBlockSizeWithoutTransactions(len(b.Transactions)) + transactionsSize 209 } 210 211 // GetExpectedBlockSizeWithoutTransactions returns the expected block size without transactions size. 212 func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int { 213 size := expectedHeaderSizeWithEmptyWitness - 1 - 1 + // 1 is for the zero-length (new(Header)).Script.Invocation/Verification 214 io.GetVarSize(&b.Script) + 215 io.GetVarSize(txCount) 216 if b.StateRootEnabled { 217 size += util.Uint256Size 218 } 219 return size 220 } 221 222 // ToStackItem converts Block to stackitem.Item. 223 func (b *Block) ToStackItem() stackitem.Item { 224 items := []stackitem.Item{ 225 stackitem.NewByteArray(b.Hash().BytesBE()), 226 stackitem.NewBigInteger(big.NewInt(int64(b.Version))), 227 stackitem.NewByteArray(b.PrevHash.BytesBE()), 228 stackitem.NewByteArray(b.MerkleRoot.BytesBE()), 229 stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))), 230 stackitem.NewBigInteger(new(big.Int).SetUint64(b.Nonce)), 231 stackitem.NewBigInteger(big.NewInt(int64(b.Index))), 232 stackitem.NewByteArray(b.NextConsensus.BytesBE()), 233 stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))), 234 } 235 if b.StateRootEnabled { 236 items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE())) 237 } 238 239 return stackitem.NewArray(items) 240 }