mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-20 02:27:21 +01:00
709b8a05cd
When there's only the coinbase tx, the merkle root = the coinbase txid. Needless complication in bitcoin == job security?
170 lines
5.0 KiB
Go
170 lines
5.0 KiB
Go
package uspv
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
)
|
|
|
|
func MakeMerkleParent(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash {
|
|
// this can screw things up; CVE-2012-2459
|
|
if left != nil && right != nil && left.IsEqual(right) {
|
|
fmt.Printf("DUP HASH CRASH")
|
|
return nil
|
|
}
|
|
// if left chils is nil, output nil. Shouldn't need this?
|
|
if left == nil {
|
|
fmt.Printf("L CRASH")
|
|
return nil
|
|
}
|
|
// if right is nil, has left with itself
|
|
if right == nil {
|
|
right = left
|
|
}
|
|
|
|
// Concatenate the left and right nodes
|
|
var sha [wire.HashSize * 2]byte
|
|
copy(sha[:wire.HashSize], left[:])
|
|
copy(sha[wire.HashSize:], right[:])
|
|
|
|
newSha := wire.DoubleSha256SH(sha[:])
|
|
return &newSha
|
|
}
|
|
|
|
type merkleNode struct {
|
|
p uint32 // position in the binary tree
|
|
h *wire.ShaHash // hash
|
|
}
|
|
|
|
// given n merkle leaves, how deep is the tree?
|
|
// iterate shifting left until greater than n
|
|
func treeDepth(n uint32) (e uint8) {
|
|
for ; (1 << e) < n; e++ {
|
|
}
|
|
return
|
|
}
|
|
|
|
// smallest power of 2 that can contain n
|
|
func nextPowerOfTwo(n uint32) uint32 {
|
|
return 1 << treeDepth(n) // 2^exponent
|
|
}
|
|
|
|
// check if a node is populated based on node position and size of tree
|
|
func inDeadZone(pos, size uint32) bool {
|
|
msb := nextPowerOfTwo(size)
|
|
last := size - 1 // last valid position is 1 less than size
|
|
if pos > (msb<<1)-2 { // greater than root; not even in the tree
|
|
fmt.Printf(" ?? greater than root ")
|
|
return true
|
|
}
|
|
h := msb
|
|
for pos >= h {
|
|
h = h>>1 | msb
|
|
last = last>>1 | msb
|
|
}
|
|
return pos > last
|
|
}
|
|
|
|
// take in a merkle block, parse through it, and return the
|
|
// txids that they're trying to tell us about. If there's any problem
|
|
// return an error.
|
|
// doing it with a stack instead of recursion. Because...
|
|
// OK I don't know why I'm just not in to recursion OK?
|
|
func checkMBlock(m *wire.MsgMerkleBlock) ([]*wire.ShaHash, error) {
|
|
if m.Transactions == 0 {
|
|
return nil, fmt.Errorf("No transactions in merkleblock")
|
|
}
|
|
if len(m.Flags) == 0 {
|
|
return nil, fmt.Errorf("No flag bits")
|
|
}
|
|
var s []merkleNode // the stack
|
|
var r []*wire.ShaHash // slice to return; txids we care about
|
|
|
|
// set initial position to root of merkle tree
|
|
msb := nextPowerOfTwo(m.Transactions) // most significant bit possible
|
|
pos := (msb << 1) - 2 // current position in tree
|
|
|
|
var i uint8 // position in the current flag byte
|
|
var tip int
|
|
// main loop
|
|
for {
|
|
tip = len(s) - 1 // slice position of stack tip
|
|
// First check if stack operations can be performed
|
|
// is stack one filled item? that's complete.
|
|
if tip == 0 && s[0].h != nil {
|
|
if s[0].h.IsEqual(&m.Header.MerkleRoot) {
|
|
return r, nil
|
|
}
|
|
return nil, fmt.Errorf("computed root %s but expect %s\n",
|
|
s[0].h.String(), m.Header.MerkleRoot.String())
|
|
}
|
|
// is current position in the tree's dead zone? partial parent
|
|
if inDeadZone(pos, m.Transactions) {
|
|
// create merkle parent from single side (left)
|
|
s[tip-1].h = MakeMerkleParent(s[tip].h, nil)
|
|
s = s[:tip] // remove 1 from stack
|
|
pos = s[tip-1].p | 1 // move position to parent's sibling
|
|
continue
|
|
}
|
|
// does stack have 3+ items? and are last 2 items filled?
|
|
if tip > 1 && s[tip-1].h != nil && s[tip].h != nil {
|
|
//fmt.Printf("nodes %d and %d combine into %d\n",
|
|
// s[tip-1].p, s[tip].p, s[tip-2].p)
|
|
// combine two filled nodes into parent node
|
|
s[tip-2].h = MakeMerkleParent(s[tip-1].h, s[tip].h)
|
|
// remove children
|
|
s = s[:tip-1]
|
|
// move position to parent's sibling
|
|
pos = s[tip-2].p | 1
|
|
continue
|
|
}
|
|
|
|
// no stack ops to perform, so make new node from message hashes
|
|
if len(m.Hashes) == 0 {
|
|
return nil, fmt.Errorf("Ran out of hashes at position %d.", pos)
|
|
}
|
|
if len(m.Flags) == 0 {
|
|
return nil, fmt.Errorf("Ran out of flag bits.")
|
|
}
|
|
var n merkleNode // make new node
|
|
n.p = pos // set current position for new node
|
|
|
|
if pos&msb != 0 { // upper non-txid hash
|
|
if m.Flags[0]&(1<<i) == 0 { // flag bit says fill node
|
|
n.h = m.Hashes[0] // copy hash from message
|
|
m.Hashes = m.Hashes[1:] // pop off message
|
|
if pos&1 != 0 { // right side; ascend
|
|
pos = pos>>1 | msb
|
|
} else { // left side, go to sibling
|
|
pos |= 1
|
|
}
|
|
} else { // flag bit says skip; put empty on stack and descend
|
|
pos = (pos ^ msb) << 1 // descend to left
|
|
}
|
|
s = append(s, n) // push new node on stack
|
|
} else { // bottom row txid; flag bit indicates tx of interest
|
|
if pos >= m.Transactions {
|
|
// this can't happen because we check deadzone above...
|
|
return nil, fmt.Errorf("got into an invalid txid node")
|
|
}
|
|
n.h = m.Hashes[0] // copy hash from message
|
|
m.Hashes = m.Hashes[1:] // pop off message
|
|
if m.Flags[0]&(1<<i) != 0 { //txid of interest
|
|
r = append(r, n.h)
|
|
}
|
|
if pos&1 == 0 { // left side, go to sibling
|
|
pos |= 1
|
|
} // if on right side we don't move; stack ops will move next
|
|
s = append(s, n) // push new node onto the stack
|
|
}
|
|
|
|
// done with pushing onto stack; advance flag bit
|
|
i++
|
|
if i == 8 { // move to next byte
|
|
i = 0
|
|
m.Flags = m.Flags[1:]
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("ran out of things to do?")
|
|
}
|