From d7145ac81a06c3199ab8df8381a9e79f741215ec Mon Sep 17 00:00:00 2001 From: Calvin Kim Date: Wed, 25 Nov 2020 01:21:18 +0900 Subject: [PATCH] Add support for utreexo blocks with ublock.go ublock.go: Enable utreexo support --- go.mod | 8 +- go.sum | 38 +++++++++ ublock.go | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 3 deletions(-) create mode 100644 ublock.go diff --git a/go.mod b/go.mod index 11583b4b8..f5b6f653a 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 2678e6530..199b3923c 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/ublock.go b/ublock.go new file mode 100644 index 000000000..cbd035b5c --- /dev/null +++ b/ublock.go @@ -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 +}