Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for utreexo blocks with ublock.go #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ go 1.14

require (
github.com/aead/siphash v1.0.1
github.com/btcsuite/btcd v0.20.1-beta
github.com/davecgh/go-spew v1.1.0
github.com/btcsuite/btcd v0.21.0-beta.0.20201124191514-610bb55ae85c
github.com/davecgh/go-spew v1.1.1
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
)

replace github.com/btcsuite/btcd => github.com/mit-dci/utcd v0.21.0-beta.0.20201218053343-e551088caf70
38 changes: 38 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,41 +1,78 @@
github.com/adiabat/bech32 v0.0.0-20170505011816-6289d404861d/go.mod h1:NW+G+E7qQb191ngeVCFjpvrWHIYANKkWJYxekITaulc=
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.21.0-beta.0.20201124191514-610bb55ae85c h1:r38ojjb2+4etXdjZV8F4aYp6ctswV1Z1ndWKOQMklxU=
github.com/btcsuite/btcd v0.21.0-beta.0.20201124191514-610bb55ae85c/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/mit-dci/utcd v0.21.0-beta.0.20201218053343-e551088caf70 h1:44LOb7N0DeQ6sIrICdACDABaR2f7suO6LmUTzWyb9jA=
github.com/mit-dci/utcd v0.21.0-beta.0.20201218053343-e551088caf70/go.mod h1:o9bdYwVAJ6CDeypx/a3qklUWlw6meljBw/KZbCtnM2Y=
github.com/mit-dci/utreexo v0.0.0-20201116141530-092f6c9b8e67 h1:U5Om3SY8g7Kwd2yWZqfooGZA7dWWJrL159oxSxDuSII=
github.com/mit-dci/utreexo v0.0.0-20201116141530-092f6c9b8e67/go.mod h1:sOjTy1VT+krgEwk71W/PztXnYkOM47r7IagMkhh/ioU=
github.com/mit-dci/utreexo v0.0.0-20201203041934-1a03a98e9424 h1:n4T8YjFWUrVdZtRcydjPh7lY+BSdZuViIMoJQirLD4M=
github.com/mit-dci/utreexo v0.0.0-20201203041934-1a03a98e9424/go.mod h1:Fkwf5QjCAkJxTCWWARKviyBWIX4v2NRcFsd0f0eZZjs=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/rjected/btcd v0.0.0-20201126072033-294365f87de0 h1:TUxklTFHm54fNFAsXrR3BPeOArKAjWeGm+yjsdfoWnU=
github.com/rjected/btcd v0.0.0-20201126072033-294365f87de0/go.mod h1:EezbYF75lMxhU8xHAUGavfx3bWMOdoeqpfl6XvefjPg=
github.com/rjected/btcd v0.0.0-20201126073131-223cc4289c8a h1:qugoi2BMK9I6CTB94SfQDyGIdr8Es/fEXz5wylpc/cI=
github.com/rjected/btcd v0.0.0-20201126073131-223cc4289c8a/go.mod h1:j8dtpaFXvu9EkyWZ7sWCmESLnll1puOtC3zgbQJLJ3c=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
Expand All @@ -47,6 +84,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
231 changes: 231 additions & 0 deletions ublock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package btcutil

import (
"bytes"
"fmt"
"io"

"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
)

// UBlock represents a utreexo block. It mimicks the behavior of Block in block.go
type UBlock struct {
msgUBlock *wire.MsgUBlock

serializedUBlock []byte // Serialized bytes for the block
serializedUBlockNoWitness []byte // Serialized bytes for block w/o witness data
blockHash *chainhash.Hash // Cached block hash
blockHeight int32 // Height in the main block chain
transactions []*Tx // Transactions
txnsGenerated bool // ALL wrapped transactions generated
}

// MsgBlock returns the underlying wire.MsgUBlock for the Block.
func (ub *UBlock) MsgUBlock() *wire.MsgUBlock {
return ub.msgUBlock
}

func (ub *UBlock) Bytes() ([]byte, error) {
// Return the cached serialized bytes if it has already been generated.
if len(ub.serializedUBlock) != 0 {
return ub.serializedUBlock, nil
}

// Serialize the MsgBlock.
w := bytes.NewBuffer(make([]byte, 0, ub.msgUBlock.SerializeSize()))
err := ub.msgUBlock.Serialize(w)
if err != nil {
return nil, err
}
serializedUBlock := w.Bytes()

// Cache the serialized bytes and return them.
ub.serializedUBlock = serializedUBlock
return serializedUBlock, nil
}

// BytesNoWitness returns the serialized bytes for the block with transactions
// encoded without any witness data.
func (ub *UBlock) BytesNoWitness() ([]byte, error) {
// Return the cached serialized bytes if it has already been generated.
if len(ub.serializedUBlockNoWitness) != 0 {
return ub.serializedUBlockNoWitness, nil
}

// Serialize the MsgBlock.
var w bytes.Buffer
err := ub.msgUBlock.SerializeNoWitness(&w)
if err != nil {
return nil, err
}
serializedUBlock := w.Bytes()

// Cache the serialized bytes and return them.
ub.serializedUBlockNoWitness = serializedUBlock
return serializedUBlock, nil
}

// Hash returns the block identifier hash for the Block. This is equivalent to
// calling BlockHash on the underlying wire.MsgBlock, however it caches the
// result so subsequent calls are more efficient.
func (ub *UBlock) Hash() *chainhash.Hash {
// Return the cached block hash if it has already been generated.
if ub.blockHash != nil {
return ub.blockHash
}

// Cache the block hash and return it.
hash := ub.msgUBlock.BlockHash()
ub.blockHash = &hash
return &hash
}

// Transactions returns a slice of wrapped transactions (btcutil.Tx) for all
// transactions in the Block. This is nearly equivalent to accessing the raw
// transactions (wire.MsgTx) in the underlying wire.MsgBlock, however it
// instead provides easy access to wrapped versions (btcutil.Tx) of them.
func (ub *UBlock) Transactions() []*Tx {
// Return transactions if they have ALL already been generated. This
// flag is necessary because the wrapped transactions are lazily
// generated in a sparse fashion.
if ub.txnsGenerated {
return ub.transactions
}

// Generate slice to hold all of the wrapped transactions if needed.
if len(ub.transactions) == 0 {
ub.transactions = make([]*Tx, len(ub.msgUBlock.MsgBlock.Transactions))
}

// Generate and cache the wrapped transactions for all that haven't
// already ubeen done.
for i, tx := range ub.transactions {
if tx == nil {
newTx := NewTx(ub.msgUBlock.MsgBlock.Transactions[i])
newTx.SetIndex(i)
ub.transactions[i] = newTx
}
}

ub.txnsGenerated = true

return ub.transactions
}

// Height returns the saved height of the block in the block chain. This value
// will be BlockHeightUnknown if it hasn't already explicitly been set.
func (b *UBlock) Height() int32 {
return b.blockHeight
}

// SetHeight sets the height of the block in the block chain.
func (b *UBlock) SetHeight(height int32) {
b.blockHeight = height
}

// NewUBlock returns a new instance of a bitcoin block given an underlying
// wire.MsgUBlock. See UBlock.
func NewBUlock(msgUBlock *wire.MsgUBlock) *UBlock {
return &UBlock{
msgUBlock: msgUBlock,
blockHeight: BlockHeightUnknown,
}
}

// NewUBlockFromReader returns a new instance of a utreexo block given a
// Reader to deserialize the ublock. See UBlock.
func NewUBlockFromReader(r io.Reader) (*UBlock, error) {
// Deserialize the bytes into a MsgBlock.
var msgUBlock wire.MsgUBlock
err := msgUBlock.Deserialize(r)
if err != nil {
return nil, err
}

ub := UBlock{
msgUBlock: &msgUBlock,
blockHeight: BlockHeightUnknown,
}
return &ub, nil
}

// NewUBlockFromBlockAndBytes returns a new instance of a utreexo block given
// an underlying wire.MsgUBlock and the serialized bytes for it. See UBlock.
func NewUBlockFromBlockAndBytes(msgUBlock *wire.MsgUBlock, serializedUBlock []byte) *UBlock {
return &UBlock{
msgUBlock: msgUBlock,
serializedUBlock: serializedUBlock,
blockHeight: BlockHeightUnknown,
}
}

// Block builds a block from the UBlock. For compatibility with some functions
// that want a block
func (ub *UBlock) Block() *Block {
block := Block{
msgBlock: &ub.msgUBlock.MsgBlock,
blockHash: ub.blockHash,
blockHeight: ub.blockHeight,
transactions: ub.transactions,
txnsGenerated: ub.txnsGenerated,
}
return &block
}

// ProofSanity checks the consistency of a UBlock
func (ub *UBlock) ProofSanity(inputSkipList []uint32, nl uint64, h uint8) error {
// get the outpoints that need proof
proveOPs := BlockToDelOPs(&ub.msgUBlock.MsgBlock, inputSkipList)

// ensure that all outpoints are provided in the extradata
if len(proveOPs) != len(ub.msgUBlock.UtreexoData.Stxos) {
err := fmt.Errorf("height %d %d outpoints need proofs but only %d proven\n",
ub.msgUBlock.UtreexoData.Height, len(proveOPs), len(ub.msgUBlock.UtreexoData.Stxos))
return err
}
for i, _ := range ub.msgUBlock.UtreexoData.Stxos {
if chainhash.Hash(proveOPs[i].Hash) != chainhash.Hash(ub.msgUBlock.UtreexoData.Stxos[i].TxHash) ||
proveOPs[i].Index != ub.msgUBlock.UtreexoData.Stxos[i].Index {
err := fmt.Errorf("block/utxoData mismatch %s v %s\n",
proveOPs[i].String(), ub.msgUBlock.UtreexoData.Stxos[i].OPString())
return err
}
}
// derive leafHashes from leafData
if !ub.msgUBlock.UtreexoData.ProofSanity(nl, h) {
return fmt.Errorf("height %d LeafData / Proof mismatch", ub.msgUBlock.UtreexoData.Height)
}

return nil
}

// BlockToDelOPs gives all the UTXOs in a block that need proofs in order to be
// deleted. All txinputs except for the coinbase input and utxos created
// within the same block (on the skiplist)
func BlockToDelOPs(
blk *wire.MsgBlock, skiplist []uint32) (delOPs []wire.OutPoint) {

var blockInIdx uint32
for txinblock, tx := range blk.Transactions {
if txinblock == 0 {
blockInIdx++ // coinbase tx always has 1 input
continue
}

// loop through inputs
for _, txin := range tx.TxIn {
// check if on skiplist. If so, don't make leaf
if len(skiplist) > 0 && skiplist[0] == blockInIdx {
// fmt.Printf("skip %s\n", txin.PreviousOutPoint.String())
skiplist = skiplist[1:]
blockInIdx++
continue
}

delOPs = append(delOPs, txin.PreviousOutPoint)
blockInIdx++
}
}
return
}