github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/lib/btc/block.go (about)

     1  package btc
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"sync"
     8  
     9  	"github.com/piotrnar/gocoin/lib/others/sys"
    10  )
    11  
    12  type Block struct {
    13  	Raw               []byte
    14  	Hash              *Uint256
    15  	Txs               []*Tx
    16  	TxCount, TxOffset int          // Number of transactions and byte offset to the first one
    17  	Trusted           sys.SyncBool // if the block is trusted, we do not check signatures and some other things...
    18  	LastKnownHeight   uint32
    19  
    20  	BlockExtraInfo // If we cache block on disk (between downloading and comitting), this data has to be preserved
    21  
    22  	MedianPastTime uint32 // Set in PreCheckBlock() .. last used in PostCheckBlock()
    23  
    24  	// These flags are set in BuildTxList() used later (e.g. by script.VerifyTxScript):
    25  	NoWitnessSize int
    26  	BlockWeight   uint
    27  	PaidTxsVSize  uint
    28  	TotalInputs   int
    29  
    30  	OrbTxCnt    uint
    31  	OrbTxSize   uint
    32  	OrbTxWeight uint
    33  
    34  	NoWitnessData []byte // This is set by BuildNoWitnessData()
    35  }
    36  
    37  type BlockExtraInfo struct {
    38  	VerifyFlags uint32
    39  	Height      uint32
    40  }
    41  
    42  func NewBlockX(data []byte, hash *Uint256) (bl *Block, er error) {
    43  	bl = new(Block)
    44  	bl.Hash = hash
    45  	er = bl.UpdateContent(data)
    46  	return
    47  }
    48  
    49  // tha data may contain only the header (80 bytes)
    50  func NewBlock(data []byte) (bl *Block, er error) {
    51  	if data == nil {
    52  		er = errors.New("nil pointer")
    53  		return
    54  	}
    55  	return NewBlockX(data, NewSha2Hash(data[:80]))
    56  }
    57  
    58  // tha data may contain only the header (80 bytes)
    59  func (bl *Block) UpdateContent(data []byte) error {
    60  	if len(data) < 80 {
    61  		return errors.New("Block too short")
    62  	}
    63  	bl.Raw = data
    64  	if len(data) > 80 {
    65  		bl.TxCount, bl.TxOffset = VLen(data[80:])
    66  		if bl.TxOffset == 0 {
    67  			return errors.New("block's txn_count field corrupt - RPC_Result:bad-blk-length")
    68  		}
    69  		bl.TxOffset += 80
    70  	}
    71  	return nil
    72  }
    73  
    74  func (bl *Block) Version() uint32 {
    75  	return binary.LittleEndian.Uint32(bl.Raw[0:4])
    76  }
    77  
    78  func (bl *Block) ParentHash() []byte {
    79  	return bl.Raw[4:36]
    80  }
    81  
    82  func (bl *Block) MerkleRoot() []byte {
    83  	return bl.Raw[36:68]
    84  }
    85  
    86  func (bl *Block) BlockTime() uint32 {
    87  	return binary.LittleEndian.Uint32(bl.Raw[68:72])
    88  }
    89  
    90  func (bl *Block) Bits() uint32 {
    91  	return binary.LittleEndian.Uint32(bl.Raw[72:76])
    92  }
    93  
    94  // BuildTxListExt parses a block's transactions and adds them to the structure.
    95  //
    96  //	dohash - set it to false if you do not ened TxIDs (it is much faster then)
    97  //
    98  // returns error if block data inconsistent
    99  func (bl *Block) BuildTxListExt(dohash bool) (e error) {
   100  	if bl.TxCount == 0 {
   101  		bl.TxCount, bl.TxOffset = VLen(bl.Raw[80:])
   102  		if bl.TxCount == 0 || bl.TxOffset == 0 {
   103  			e = errors.New("block's txn_count field corrupt - RPC_Result:bad-blk-length")
   104  			return
   105  		}
   106  		bl.TxOffset += 80
   107  	}
   108  	bl.Txs = make([]*Tx, bl.TxCount)
   109  
   110  	// It would be more elegant to use bytes.Reader here, but this solution is ~20% faster.
   111  	offs := bl.TxOffset
   112  
   113  	var wg sync.WaitGroup
   114  	var data2hash, witness2hash []byte
   115  
   116  	bl.NoWitnessSize = 80 + VLenSize(uint64(bl.TxCount))
   117  	bl.BlockWeight = 4 * uint(bl.NoWitnessSize)
   118  
   119  	for i := 0; i < bl.TxCount; i++ {
   120  		var n int
   121  		bl.Txs[i], n = NewTx(bl.Raw[offs:])
   122  		if bl.Txs[i] == nil || n == 0 {
   123  			e = errors.New("NewTx failed")
   124  			bl.Txs = bl.Txs[:i] // make sure we don't leave any nil pointers in bl.Txs
   125  			break
   126  		}
   127  		bl.Txs[i].Raw = bl.Raw[offs : offs+n]
   128  		bl.Txs[i].Size = uint32(n)
   129  		if i == 0 {
   130  			for _, ou := range bl.Txs[0].TxOut {
   131  				ou.WasCoinbase = true
   132  			}
   133  		} else {
   134  			// Coinbase tx does not have an input
   135  			bl.TotalInputs += len(bl.Txs[i].TxIn)
   136  		}
   137  		if bl.Txs[i].SegWit != nil {
   138  			data2hash = bl.Txs[i].Serialize()
   139  			bl.Txs[i].NoWitSize = uint32(len(data2hash))
   140  			if i > 0 {
   141  				witness2hash = bl.Txs[i].Raw
   142  			}
   143  		} else {
   144  			data2hash = bl.Txs[i].Raw
   145  			bl.Txs[i].NoWitSize = bl.Txs[i].Size
   146  			witness2hash = nil
   147  		}
   148  		weight := uint(3*bl.Txs[i].NoWitSize + bl.Txs[i].Size)
   149  		bl.BlockWeight += weight
   150  		if i > 0 {
   151  			bl.PaidTxsVSize += uint(bl.Txs[i].VSize())
   152  		}
   153  		bl.NoWitnessSize += len(data2hash)
   154  		if i != 0 {
   155  			if yes, _ := bl.Txs[i].ContainsOrdFile(true); yes {
   156  				bl.OrbTxCnt++
   157  				bl.OrbTxSize += uint(n)
   158  				bl.OrbTxWeight += weight
   159  			}
   160  		}
   161  
   162  		if dohash {
   163  			wg.Add(1)
   164  			go func(tx *Tx, b, w []byte) {
   165  				tx.Hash.Calc(b) // Calculate tx hash in a background
   166  				if w != nil {
   167  					tx.wTxID.Calc(w)
   168  				}
   169  				wg.Done()
   170  			}(bl.Txs[i], data2hash, witness2hash)
   171  		}
   172  		offs += n
   173  	}
   174  
   175  	wg.Wait()
   176  
   177  	return
   178  }
   179  
   180  // BuildTxList parses a block's transactions and adds them to the structure, always calculating TX IDs.
   181  func (bl *Block) BuildTxList() (e error) {
   182  	return bl.BuildTxListExt(true)
   183  }
   184  
   185  // The block data in non-segwit format
   186  func (bl *Block) BuildNoWitnessData() (e error) {
   187  	if bl.TxCount == 0 {
   188  		e = bl.BuildTxList()
   189  		if e != nil {
   190  			return
   191  		}
   192  	}
   193  	old_format_block := new(bytes.Buffer)
   194  	old_format_block.Write(bl.Raw[:80])
   195  	WriteVlen(old_format_block, uint64(bl.TxCount))
   196  	for _, tx := range bl.Txs {
   197  		tx.WriteSerialized(old_format_block)
   198  	}
   199  	bl.NoWitnessData = old_format_block.Bytes()
   200  	if bl.NoWitnessSize == 0 {
   201  		bl.NoWitnessSize = len(bl.NoWitnessData)
   202  	} else if bl.NoWitnessSize != len(bl.NoWitnessData) {
   203  		panic("NoWitnessSize corrupt")
   204  	}
   205  	return
   206  }
   207  
   208  func GetBlockReward(height uint32) uint64 {
   209  	return 50e8 >> (height / 210000)
   210  }
   211  
   212  func (bl *Block) MerkleRootMatch() bool {
   213  	if bl.TxCount == 0 || len(bl.Txs) != bl.TxCount {
   214  		return false
   215  	}
   216  	merkle, mutated := bl.GetMerkle()
   217  	return !mutated && bytes.Equal(merkle, bl.MerkleRoot())
   218  }
   219  
   220  func (bl *Block) GetMerkle() (res []byte, mutated bool) {
   221  	mtr := make([][32]byte, len(bl.Txs), 3*len(bl.Txs)) // make the buffer 3 times longer as we use append() inside CalcMerkle
   222  	for i, tx := range bl.Txs {
   223  		if tx == nil {
   224  			println("GetMerkle(): tx missing", i)
   225  			mutated = true
   226  			return
   227  		}
   228  		mtr[i] = tx.Hash.Hash
   229  	}
   230  	res, mutated = CalcMerkle(mtr)
   231  	return
   232  }