github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/uspv/mblock.go (about)

     1  package uspv
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/mit-dci/lit/logging"
     7  
     8  	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
     9  	"github.com/mit-dci/lit/wire"
    10  )
    11  
    12  // MakeMerkleParent ...
    13  func MakeMerkleParent(left, right *chainhash.Hash) *chainhash.Hash {
    14  	// dupes can screw things up; CVE-2012-2459. check for them
    15  	if left != nil && right != nil && left.IsEqual(right) {
    16  		logging.Infof("DUP HASH CRASH")
    17  		return nil
    18  	}
    19  	// if left child is nil, output nil.  Need this for hard mode.
    20  	if left == nil {
    21  		return nil
    22  	}
    23  	// if right is nil, hash left with itself
    24  	if right == nil {
    25  		right = left
    26  	}
    27  
    28  	// Concatenate the left and right nodes
    29  	var sha [64]byte
    30  	copy(sha[:32], left[:])
    31  	copy(sha[32:], right[:])
    32  
    33  	newSha := chainhash.DoubleHashH(sha[:])
    34  	return &newSha
    35  }
    36  
    37  type merkleNode struct {
    38  	p uint32          // position in the binary tree
    39  	h *chainhash.Hash // hash
    40  }
    41  
    42  // given n merkle leaves, how deep is the tree?
    43  // iterate shifting left until greater than n
    44  func treeDepth(n uint32) (e uint8) {
    45  	for ; (1 << e) < n; e++ {
    46  	}
    47  	return
    48  }
    49  
    50  // smallest power of 2 that can contain n
    51  func nextPowerOfTwo(n uint32) uint32 {
    52  	return 1 << treeDepth(n) // 2^exponent
    53  }
    54  
    55  // check if a node is populated based on node position and size of tree
    56  func inDeadZone(pos, size uint32) bool {
    57  	msb := nextPowerOfTwo(size)
    58  	last := size - 1      // last valid position is 1 less than size
    59  	if pos > (msb<<1)-2 { // greater than root; not even in the tree
    60  		logging.Infof(" ?? greater than root ")
    61  		return true
    62  	}
    63  	h := msb
    64  	for pos >= h {
    65  		h = h>>1 | msb
    66  		last = last>>1 | msb
    67  	}
    68  	return pos > last
    69  }
    70  
    71  // take in a merkle block, parse through it, and return txids indicated
    72  // If there's any problem return an error.  Checks self-consistency only.
    73  // doing it with a stack instead of recursion.  Because...
    74  // OK I don't know why I'm just not in to recursion OK?
    75  func checkMBlock(m *wire.MsgMerkleBlock) ([]*chainhash.Hash, error) {
    76  	if m.Transactions == 0 {
    77  		return nil, fmt.Errorf("No transactions in merkleblock")
    78  	}
    79  	if len(m.Flags) == 0 {
    80  		return nil, fmt.Errorf("No flag bits")
    81  	}
    82  	var s []merkleNode      // the stack
    83  	var r []*chainhash.Hash // slice to return; txids we care about
    84  
    85  	// set initial position to root of merkle tree
    86  	msb := nextPowerOfTwo(m.Transactions) // most significant bit possible
    87  	pos := (msb << 1) - 2                 // current position in tree
    88  
    89  	var i uint8 // position in the current flag byte
    90  	var tip int
    91  	// main loop
    92  	for {
    93  		tip = len(s) - 1 // slice position of stack tip
    94  		// First check if stack operations can be performed
    95  		// is stack one filled item?  that's complete.
    96  		if tip == 0 && s[0].h != nil {
    97  			if s[0].h.IsEqual(&m.Header.MerkleRoot) {
    98  				return r, nil
    99  			}
   100  			return nil, fmt.Errorf("computed root %s but expect %s\n",
   101  				s[0].h.String(), m.Header.MerkleRoot.String())
   102  		}
   103  		// is current position in the tree's dead zone? partial parent
   104  		if inDeadZone(pos, m.Transactions) {
   105  			// create merkle parent from single side (left)
   106  			s[tip-1].h = MakeMerkleParent(s[tip].h, nil)
   107  			s = s[:tip]          // remove 1 from stack
   108  			pos = s[tip-1].p | 1 // move position to parent's sibling
   109  			continue
   110  		}
   111  		// does stack have 3+ items? and are last 2 items filled?
   112  		if tip > 1 && s[tip-1].h != nil && s[tip].h != nil {
   113  			//logging.Infof("nodes %d and %d combine into %d\n",
   114  			//	s[tip-1].p, s[tip].p, s[tip-2].p)
   115  			// combine two filled nodes into parent node
   116  			s[tip-2].h = MakeMerkleParent(s[tip-1].h, s[tip].h)
   117  			// remove children
   118  			s = s[:tip-1]
   119  			// move position to parent's sibling
   120  			pos = s[tip-2].p | 1
   121  			continue
   122  		}
   123  
   124  		// no stack ops to perform, so make new node from message hashes
   125  		if len(m.Hashes) == 0 {
   126  			return nil, fmt.Errorf("Ran out of hashes at position %d.", pos)
   127  		}
   128  		if len(m.Flags) == 0 {
   129  			return nil, fmt.Errorf("Ran out of flag bits.")
   130  		}
   131  		var n merkleNode // make new node
   132  		n.p = pos        // set current position for new node
   133  
   134  		if pos&msb != 0 { // upper non-txid hash
   135  			if m.Flags[0]&(1<<i) == 0 { // flag bit says fill node
   136  				n.h = m.Hashes[0]       // copy hash from message
   137  				m.Hashes = m.Hashes[1:] // pop off message
   138  				if pos&1 != 0 {         // right side; ascend
   139  					pos = pos>>1 | msb
   140  				} else { // left side, go to sibling
   141  					pos |= 1
   142  				}
   143  			} else { // flag bit says skip; put empty on stack and descend
   144  				pos = (pos ^ msb) << 1 // descend to left
   145  			}
   146  			s = append(s, n) // push new node on stack
   147  		} else { // bottom row txid; flag bit indicates tx of interest
   148  			if pos >= m.Transactions {
   149  				// this can't happen because we check deadzone above...
   150  				return nil, fmt.Errorf("got into an invalid txid node")
   151  			}
   152  			n.h = m.Hashes[0]           // copy hash from message
   153  			m.Hashes = m.Hashes[1:]     // pop off message
   154  			if m.Flags[0]&(1<<i) != 0 { //txid of interest
   155  				r = append(r, n.h)
   156  			}
   157  			if pos&1 == 0 { // left side, go to sibling
   158  				pos |= 1
   159  			} // if on right side we don't move; stack ops will move next
   160  			s = append(s, n) // push new node onto the stack
   161  		}
   162  
   163  		// done with pushing onto stack; advance flag bit
   164  		i++
   165  		if i == 8 { // move to next byte
   166  			i = 0
   167  			m.Flags = m.Flags[1:]
   168  		}
   169  	}
   170  }