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 }