decred.org/dcrdex@v1.0.5/dex/networks/zec/block.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org
     3  
     4  package zec
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"time"
    11  
    12  	"decred.org/dcrdex/dex/utils"
    13  	"github.com/btcsuite/btcd/wire"
    14  )
    15  
    16  // Block extends a wire.MsgBlock to specify Zcash specific fields, or in the
    17  // case of the Nonce, a type-variant.
    18  type Block struct {
    19  	wire.MsgBlock
    20  	// Transactions and MsgBlock.Transactions should both be populated. Each
    21  	// *Tx.MsgTx will be the same as the *MsgTx in MsgBlock.Transactions.
    22  	Transactions         []*Tx
    23  	HashBlockCommitments [32]byte // Using NU5 name
    24  	Nonce                [32]byte // Bitcoin uses uint32
    25  	Solution             []byte   // length 1344 on main and testnet, 36 on regtest
    26  }
    27  
    28  // DeserializeBlock deserializes the Zcash-encoded block.
    29  func DeserializeBlock(b []byte) (*Block, error) {
    30  	zecBlock := &Block{}
    31  
    32  	// https://zips.z.cash/protocol/protocol.pdf section 7.6
    33  	r := bytes.NewReader(b)
    34  
    35  	if err := zecBlock.decodeBlockHeader(r); err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	txCount, err := wire.ReadVarInt(r, pver)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	// TODO: Limit txCount based on block size, header size, min tx size.
    45  
    46  	zecBlock.MsgBlock.Transactions = make([]*wire.MsgTx, 0, txCount)
    47  	zecBlock.Transactions = make([]*Tx, 0, txCount)
    48  	for i := uint64(0); i < txCount; i++ {
    49  		tx := &Tx{MsgTx: new(wire.MsgTx)}
    50  
    51  		if err := tx.ZecDecode(r); err != nil {
    52  			blockHash := zecBlock.BlockHash()
    53  			return nil, fmt.Errorf("error decoding tx at index %d in block %s: %w", i, blockHash, err)
    54  		}
    55  		zecBlock.MsgBlock.Transactions = append(zecBlock.MsgBlock.Transactions, tx.MsgTx)
    56  		zecBlock.Transactions = append(zecBlock.Transactions, tx)
    57  	}
    58  
    59  	return zecBlock, nil
    60  }
    61  
    62  func DeserializeBlockHeader(b []byte) (*wire.BlockHeader, error) {
    63  	zecBlock := &Block{}
    64  
    65  	// https://zips.z.cash/protocol/protocol.pdf section 7.6
    66  	r := bytes.NewReader(b)
    67  
    68  	if err := zecBlock.decodeBlockHeader(r); err != nil {
    69  		return nil, err
    70  	}
    71  	return &zecBlock.Header, nil
    72  }
    73  
    74  // See github.com/zcash/zcash CBlockHeader -> SerializeOp
    75  func (z *Block) decodeBlockHeader(r io.Reader) error {
    76  	hdr := &z.MsgBlock.Header
    77  
    78  	nVersion, err := readUint32(r)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	hdr.Version = int32(nVersion)
    83  
    84  	if _, err = io.ReadFull(r, hdr.PrevBlock[:]); err != nil {
    85  		return err
    86  	}
    87  
    88  	if _, err := io.ReadFull(r, hdr.MerkleRoot[:]); err != nil {
    89  		return err
    90  	}
    91  
    92  	_, err = io.ReadFull(r, z.HashBlockCommitments[:])
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	nTime, err := readUint32(r)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	hdr.Timestamp = time.Unix(int64(nTime), 0)
   102  
   103  	hdr.Bits, err = readUint32(r)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	err = readInternalByteOrder(r, z.Nonce[:])
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	solSize, err := wire.ReadVarInt(r, pver)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	if solSize != 1344 && solSize != 36 && solSize != 400 /* zclassic */ {
   118  		return fmt.Errorf("wrong solution size %d", solSize)
   119  	}
   120  	z.Solution = make([]byte, solSize)
   121  
   122  	_, err = io.ReadFull(r, z.Solution)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func readInternalByteOrder(r io.Reader, b []byte) error {
   131  	if _, err := io.ReadFull(r, b); err != nil {
   132  		return err
   133  	}
   134  	// Reverse the bytes
   135  	utils.ReverseSlice(b)
   136  	return nil
   137  }