decred.org/dcrdex@v1.0.5/dex/networks/ltc/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 ltc
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  
    11  	"github.com/btcsuite/btcd/wire"
    12  )
    13  
    14  const (
    15  	// mwebVer is the bit of the block header's version that indicates the
    16  	// presence of a MWEB.
    17  	mwebVer = 0x20000000 // 1 << 29
    18  )
    19  
    20  func parseMWEB(blk io.Reader) error {
    21  	dec := newDecoder(blk)
    22  	// src/mweb/mweb_models.h - struct Block
    23  	// "A convenience wrapper around a possibly-null extension block.""
    24  	// OptionalPtr around a mw::Block. Read the option byte:
    25  	hasMWEB, err := dec.readByte()
    26  	if err != nil {
    27  		return fmt.Errorf("failed to check MWEB option byte: %w", err)
    28  	}
    29  	if hasMWEB == 0 {
    30  		return nil
    31  	}
    32  
    33  	// src/libmw/include/mw/models/block/Block.h - class Block
    34  	// (1) Header and (2) TxBody
    35  
    36  	// src/libmw/include/mw/models/block/Header.h - class Header
    37  	// height
    38  	if _, err = dec.readVLQ(); err != nil {
    39  		return fmt.Errorf("failed to decode MWEB height: %w", err)
    40  	}
    41  
    42  	// 3x Hash + 2x BlindingFactor
    43  	if err = dec.discardBytes(32*3 + 32*2); err != nil {
    44  		return fmt.Errorf("failed to decode MWEB junk: %w", err)
    45  	}
    46  
    47  	// Number of TXOs: outputMMRSize
    48  	if _, err = dec.readVLQ(); err != nil {
    49  		return fmt.Errorf("failed to decode TXO count: %w", err)
    50  	}
    51  
    52  	// Number of kernels: kernelMMRSize
    53  	if _, err = dec.readVLQ(); err != nil {
    54  		return fmt.Errorf("failed to decode kernel count: %w", err)
    55  	}
    56  
    57  	// TxBody
    58  	_, err = dec.readMWTXBody()
    59  	if err != nil {
    60  		return fmt.Errorf("failed to decode MWEB tx: %w", err)
    61  	}
    62  	// if len(kern0) > 0 {
    63  	// 	mwebTxID := chainhash.Hash(blake3.Sum256(kern0))
    64  	// 	fmt.Println(mwebTxID.String())
    65  	// }
    66  
    67  	return nil
    68  }
    69  
    70  // DeserializeBlock decodes the bytes of a serialized Litecoin block. This
    71  // function exists because MWEB changes both the block and transaction
    72  // serializations. Blocks may have a MW "extension block" for "peg-out"
    73  // transactions, and this EB is after all the transactions in the regular LTC
    74  // block. After the canonical transactions in the regular block, there may be
    75  // zero or more "peg-in" transactions followed by one integration transaction
    76  // (also known as a HogEx transaction), all still in the regular LTC block. The
    77  // peg-in txns decode correctly, but the integration tx is a special transaction
    78  // with the witness tx flag with bit 3 set (8), which prevents correct
    79  // wire.MsgTx deserialization.
    80  // Refs:
    81  // https://github.com/litecoin-project/lips/blob/master/lip-0002.mediawiki#PegOut_Transactions
    82  // https://github.com/litecoin-project/lips/blob/master/lip-0003.mediawiki#Specification
    83  // https://github.com/litecoin-project/litecoin/commit/9d1f530a5fa6d16871fdcc3b506be42b593d3ce4
    84  // https://github.com/litecoin-project/litecoin/commit/8c82032f45e644f413ec5c91e121a31c993aa831
    85  // (src/libmw/include/mw/models/tx/Transaction.h for the `mweb_tx` field of the
    86  // `CTransaction` in the "primitives" commit).
    87  func DeserializeBlock(blk io.Reader) (*wire.MsgBlock, error) {
    88  	// Block header
    89  	hdr := &wire.BlockHeader{}
    90  	err := hdr.Deserialize(blk)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("failed to deserialize block header: %w", err)
    93  	}
    94  
    95  	// This block's transactions
    96  	txnCount, err := wire.ReadVarInt(blk, 0)
    97  	if err != nil {
    98  		return nil, fmt.Errorf("failed to parse transaction count: %w", err)
    99  	}
   100  
   101  	// We can only decode the canonical txns, not the mw peg-in txs in the EB.
   102  	var hasHogEx bool
   103  	txns := make([]*wire.MsgTx, 0, int(txnCount))
   104  	for i := 0; i < cap(txns); i++ {
   105  		msgTx, err := DeserializeTx(blk)
   106  		if err != nil {
   107  			return nil, fmt.Errorf("failed to deserialize transaction %d of %d in block %v: %w",
   108  				i+1, txnCount, hdr.BlockHash(), err)
   109  		}
   110  		txns = append(txns, msgTx.MsgTx) // txns = append(txns, msgTx)
   111  		hasHogEx = msgTx.IsHogEx         // hogex is the last txn
   112  	}
   113  
   114  	// The mwebVer mask indicates it may contain a MWEB after a HogEx.
   115  	// src/primitives/block.h: SERIALIZE_NO_MWEB
   116  	if hdr.Version&mwebVer != 0 && hasHogEx {
   117  		if err = parseMWEB(blk); err != nil {
   118  			return nil, err
   119  		}
   120  	}
   121  
   122  	return &wire.MsgBlock{
   123  		Header:       *hdr,
   124  		Transactions: txns,
   125  	}, nil
   126  }
   127  
   128  // DeserializeBlockBytes wraps DeserializeBlock using bytes.NewReader for
   129  // convenience.
   130  func DeserializeBlockBytes(blk []byte) (*wire.MsgBlock, error) {
   131  	return DeserializeBlock(bytes.NewReader(blk))
   132  }