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

MWEB Light Client #10

Open
wants to merge 75 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
29dd25e
Mempool transaction notification
hectorchu Jan 6, 2024
5e46c24
Get mweb header and leafset
hectorchu Jan 9, 2024
4e18a4a
Verify mweb header and leafset
hectorchu Jan 10, 2024
27ec42e
Verify mwebheader merkle block
hectorchu Jan 10, 2024
0c4882c
Verify that the hogex is the final transaction
hectorchu Jan 10, 2024
5177004
Restart with new tip if we detect the empty leafset message
hectorchu Jan 10, 2024
24d02f8
First cut of code to verify mweb utxos
hectorchu Jan 12, 2024
66d1725
Switch to queryAllPeers
hectorchu Jan 13, 2024
490ea90
Fixed bugs
hectorchu Jan 14, 2024
032d443
Added code to download all mweb utxos
hectorchu Jan 14, 2024
123e038
Save mweb utxos in database
hectorchu Jan 15, 2024
9bb868d
Fix build
hectorchu Jan 17, 2024
0036932
Add mweb utxos notification
hectorchu Jan 21, 2024
b51ea19
Continually fetch mweb utxos
hectorchu Jan 21, 2024
ac9bec8
Rewrote the leaf span selection logic
hectorchu Jan 22, 2024
c05e78b
Store leaf to utxo mapping
hectorchu Jan 22, 2024
1fb18a3
Fix bug when mmr peak has no unspent leaves
hectorchu Jan 22, 2024
246a4bb
Purge spent coins from db
hectorchu Jan 22, 2024
0a3a3a5
Notify new utxos at startup
hectorchu Jan 22, 2024
49a10bd
Fix bug - bytes.Buffer cannot be reused
hectorchu Jan 22, 2024
9aa29b7
Fill in BlockMeta at source
hectorchu Jan 23, 2024
8aee05a
Moved const to wire package
hectorchu Jan 27, 2024
dc6700e
Encodings
hectorchu Jan 28, 2024
12283d0
Purge spent coins even if no new coins are added
hectorchu Jan 28, 2024
fe8ae47
Atomically update leafset and purge spents
hectorchu Jan 28, 2024
0a9debd
Block meta comes from utxo height
hectorchu Jan 29, 2024
4dae5ae
Fix looping bug?
hectorchu Jan 30, 2024
f89fe7c
Notify new leafset after purge
hectorchu Jan 31, 2024
8b38d14
Fix test
hectorchu Feb 3, 2024
d4eb1c3
Fixed closing a closed channel in queryAllPeers
hectorchu Feb 3, 2024
f5e6f59
Stop rebroadcasting confirmed transactions
hectorchu Feb 6, 2024
83263bf
Fix crash
hectorchu Feb 14, 2024
0e9156e
Use bytes.Reader
hectorchu Feb 15, 2024
0d61133
Add ConnectPeer method
hectorchu Feb 17, 2024
d0a394c
Fix build
hectorchu Feb 18, 2024
7dcf165
Fix test
hectorchu Feb 18, 2024
e9357a6
Remove unused param
hectorchu Feb 20, 2024
273ddc8
Notify unconfirmed mweb utxos
hectorchu Feb 21, 2024
7c9f679
Split verifyMwebHeader function
hectorchu Feb 22, 2024
34c155e
Reorg for clarity
hectorchu Feb 22, 2024
4a76f15
Fetch mweb headers
hectorchu Feb 23, 2024
1be256b
Store height to leaf count mapping in db
hectorchu Feb 23, 2024
0eb808a
Calculate utxo heights from headers
hectorchu Feb 23, 2024
2034e8a
Fix query timing out for large batches
hectorchu Feb 23, 2024
de80950
Small cosmetic change
hectorchu Feb 23, 2024
3277928
Only load the leaves at height mapping once
hectorchu Feb 24, 2024
9138ec4
Handle reorgs
hectorchu Feb 25, 2024
800859f
Make sure rollback height is earlier
hectorchu Feb 25, 2024
f54c93c
Fix small oversight
hectorchu Feb 25, 2024
efcfe3d
Combine leafset with height
hectorchu Feb 26, 2024
bc490e4
Minor tidy-up
hectorchu Feb 26, 2024
6e3710f
Fill in leafset block header
hectorchu Feb 27, 2024
5df2155
Only clear rollback height if unchanged
hectorchu Feb 27, 2024
f301b6e
Rollback leaves at height
hectorchu Feb 27, 2024
d7191ac
Switching to go workspaces
hectorchu Feb 28, 2024
a89dfc4
Fix underflow
hectorchu Feb 28, 2024
b7a025c
Move mweb handler to separate file
hectorchu Feb 29, 2024
6203880
Ensure we're not rolling back before notifying utxos
hectorchu Mar 1, 2024
8294325
Only validate witness commitment if segwit is active
hectorchu Mar 1, 2024
681bb79
Fix double close
hectorchu Mar 2, 2024
48aa4d5
Work manager stability fixes
hectorchu Mar 5, 2024
914d20b
Fix test
hectorchu Mar 5, 2024
0cd9ef5
Fix batch timeout
hectorchu Mar 6, 2024
92cc54f
Allow multiple connections to an IP
hectorchu Mar 7, 2024
b6e22da
Remove unneeded method
hectorchu Mar 7, 2024
d53d13f
Fix more syncing issues
hectorchu Apr 1, 2024
2e8a94f
Also reset timeout
hectorchu Apr 1, 2024
8e37b25
Reduce lock hold time
hectorchu Apr 3, 2024
f732421
Don't fail utxo lookup if leaf not found
hectorchu Apr 3, 2024
9229dd0
Error if no peers when sending
hectorchu Apr 27, 2024
73f2c1a
Update module name
hectorchu May 10, 2024
90f5e66
go mod tidy
hectorchu May 11, 2024
a4a65dd
go mod tidy
hectorchu May 11, 2024
5bd91a4
go mod tidy
hectorchu May 11, 2024
e76acf7
go mod tidy
hectorchu Sep 3, 2024
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PKG := github.com/ltcsuite/neutrino
PKG := github.com/ltcmweb/neutrino
TOOLS_DIR := tools

LTCD_PKG := github.com/ltcsuite/ltcd
LTCD_PKG := github.com/ltcmweb/ltcd
LINT_PKG := github.com/golangci/golangci-lint/cmd/golangci-lint
GOACC_PKG := github.com/ory/go-acc
GOIMPORTS_PKG := github.com/rinchsan/gosimports/cmd/gosimports
Expand Down
14 changes: 7 additions & 7 deletions bamboozle_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
"sort"
"testing"

"github.com/ltcsuite/ltcd/chaincfg"
"github.com/ltcsuite/ltcd/chaincfg/chainhash"
"github.com/ltcsuite/ltcd/ltcutil/gcs"
"github.com/ltcsuite/ltcd/ltcutil/gcs/builder"
"github.com/ltcsuite/ltcd/txscript"
"github.com/ltcsuite/ltcd/wire"
"github.com/ltcmweb/ltcd/chaincfg"
"github.com/ltcmweb/ltcd/chaincfg/chainhash"
"github.com/ltcmweb/ltcd/ltcutil/gcs"
"github.com/ltcmweb/ltcd/ltcutil/gcs/builder"
"github.com/ltcmweb/ltcd/txscript"
"github.com/ltcmweb/ltcd/wire"
"github.com/ltcmweb/neutrino/headerfs"
"github.com/ltcsuite/ltcwallet/walletdb"
"github.com/ltcsuite/neutrino/headerfs"
)

func decodeHashNoError(str string) *chainhash.Hash {
Expand Down
14 changes: 14 additions & 0 deletions banman/reason.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ const (

// InvalidBlock signals that a peer served us a bad block.
InvalidBlock Reason = 5

// InvalidMwebHeader signals that a peer served us an invalid
// mweb header message.
InvalidMwebHeader Reason = 20

// InvalidMwebUtxos signals that a peer served us an invalid
// mweb utxos message.
InvalidMwebUtxos Reason = 21
)

// String returns a human-readable description for the reason a peer was banned.
Expand All @@ -43,6 +51,12 @@ func (r Reason) String() string {
case InvalidBlock:
return "peer served an invalid block"

case InvalidMwebHeader:
return "peer served invalid mweb header message"

case InvalidMwebUtxos:
return "peer served invalid mweb utxos message"

default:
return "unknown reason"
}
Expand Down
2 changes: 1 addition & 1 deletion banman/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"testing"
"time"

"github.com/ltcmweb/neutrino/banman"
"github.com/ltcsuite/ltcwallet/walletdb"
_ "github.com/ltcsuite/ltcwallet/walletdb/bdb"
"github.com/ltcsuite/neutrino/banman"
)

// createTestBanStore creates a test Store backed by a boltdb instance.
Expand Down
4 changes: 2 additions & 2 deletions batch_spend_reporter.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package neutrino

import (
"github.com/ltcsuite/ltcd/chaincfg/chainhash"
"github.com/ltcsuite/ltcd/wire"
"github.com/ltcmweb/ltcd/chaincfg/chainhash"
"github.com/ltcmweb/ltcd/wire"
)

// batchSpendReporter orchestrates the delivery of spend reports to
Expand Down
128 changes: 113 additions & 15 deletions blockmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,21 @@ import (
"sync/atomic"
"time"

"github.com/ltcsuite/ltcd/blockchain"
"github.com/ltcsuite/ltcd/chaincfg"
"github.com/ltcsuite/ltcd/chaincfg/chainhash"
"github.com/ltcsuite/ltcd/ltcutil"
"github.com/ltcsuite/ltcd/ltcutil/gcs"
"github.com/ltcsuite/ltcd/ltcutil/gcs/builder"
"github.com/ltcsuite/ltcd/wire"
"github.com/ltcsuite/neutrino/banman"
"github.com/ltcsuite/neutrino/blockntfns"
"github.com/ltcsuite/neutrino/chainsync"
"github.com/ltcsuite/neutrino/headerfs"
"github.com/ltcsuite/neutrino/headerlist"
"github.com/ltcsuite/neutrino/query"
"github.com/ltcmweb/ltcd/blockchain"
"github.com/ltcmweb/ltcd/chaincfg"
"github.com/ltcmweb/ltcd/chaincfg/chainhash"
"github.com/ltcmweb/ltcd/ltcutil"
"github.com/ltcmweb/ltcd/ltcutil/gcs"
"github.com/ltcmweb/ltcd/ltcutil/gcs/builder"
"github.com/ltcmweb/ltcd/ltcutil/mweb"
"github.com/ltcmweb/ltcd/wire"
"github.com/ltcmweb/neutrino/banman"
"github.com/ltcmweb/neutrino/blockntfns"
"github.com/ltcmweb/neutrino/chainsync"
"github.com/ltcmweb/neutrino/headerfs"
"github.com/ltcmweb/neutrino/headerlist"
"github.com/ltcmweb/neutrino/mwebdb"
"github.com/ltcmweb/neutrino/query"
)

const (
Expand Down Expand Up @@ -72,6 +74,13 @@ type donePeerMsg struct {
peer *ServerPeer
}

// txMsg packages a bitcoin tx message and the peer it came from together
// so the block handler has access to that information.
type txMsg struct {
tx *ltcutil.Tx
peer *ServerPeer
}

// blockManagerCfg holds options and dependencies needed by the blockManager
// during operation.
type blockManagerCfg struct {
Expand All @@ -86,6 +95,9 @@ type blockManagerCfg struct {
// compact filters are persistently stored.
RegFilterHeaders *headerfs.FilterHeaderStore

// MwebCoins is the store where mweb coins are persistently stored.
MwebCoins mwebdb.CoinDatabase

// TimeSource is used to access a time estimate based on the clocks of
// the connected peers.
TimeSource blockchain.MedianTimeSource
Expand All @@ -109,6 +121,8 @@ type blockManagerCfg struct {
checkResponse func(sp *ServerPeer, resp wire.Message,
quit chan<- struct{}, peerQuit chan<- struct{}),
options ...QueryOption)

mempool *Mempool
}

// blockManager provides a concurrency safe block manager for handling all
Expand Down Expand Up @@ -206,6 +220,12 @@ type blockManager struct { // nolint:maligned
minRetargetTimespan int64 // target timespan / adjustment factor
maxRetargetTimespan int64 // target timespan * adjustment factor
blocksPerRetarget int32 // target timespan / target time per block

requestedTxns map[chainhash.Hash]struct{}

mwebUtxosCallbacksMtx sync.Mutex
mwebUtxosCallbacks []func(*mweb.Leafset, []*wire.MwebNetUtxo)
mwebRollbackSignal *sync.Cond
}

// newBlockManager returns a new bitcoin block manager. Use Start to begin
Expand Down Expand Up @@ -235,13 +255,15 @@ func newBlockManager(cfg *blockManagerCfg) (*blockManager, error) {
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
minRetargetTimespan: targetTimespan / adjustmentFactor,
maxRetargetTimespan: targetTimespan * adjustmentFactor,
requestedTxns: make(map[chainhash.Hash]struct{}),
}

// Next we'll create the two signals that goroutines will use to wait
// on a particular header chain height before starting their normal
// duties.
bm.newHeadersSignal = sync.NewCond(&bm.newHeadersMtx)
bm.newFilterHeadersSignal = sync.NewCond(&bm.newFilterHeadersMtx)
bm.mwebRollbackSignal = sync.NewCond(&bm.mwebUtxosCallbacksMtx)

// We fetch the genesis header to use for verifying the first received
// interval.
Expand Down Expand Up @@ -290,7 +312,7 @@ func (b *blockManager) Start() {
}

log.Trace("Starting block manager")
b.wg.Add(2)
b.wg.Add(3)
go b.blockHandler()
go func() {
defer b.wg.Done()
Expand All @@ -302,10 +324,12 @@ func (b *blockManager) Start() {
select {
case <-b.cfg.firstPeerSignal:
case <-b.quit:
b.wg.Done()
return
}

log.Debug("Peer connected, starting cfHandler.")
go b.mwebHandler()
b.cfHandler()
}()
}
Expand Down Expand Up @@ -335,6 +359,7 @@ func (b *blockManager) Stop() error {

b.newHeadersSignal.Broadcast()
b.newFilterHeadersSignal.Broadcast()
b.mwebRollbackSignal.Broadcast()
}
}()

Expand Down Expand Up @@ -1282,6 +1307,11 @@ func (b *blockManager) rollBackToHeight(height uint32) error {
return err
}

err = b.cfg.MwebCoins.PutRollbackHeight(height)
if err != nil {
return err
}

for uint32(bs.Height) > height {
header, headerHeight, err := b.cfg.BlockHeaders.FetchHeader(&bs.Hash)
if err != nil {
Expand Down Expand Up @@ -1994,6 +2024,9 @@ out:
case *invMsg:
b.handleInvMsg(msg)

case *txMsg:
b.handleTxMsg(msg)

case *headersMsg:
b.handleHeadersMsg(msg)

Expand Down Expand Up @@ -2260,13 +2293,51 @@ func (b *blockManager) QueueInv(inv *wire.MsgInv, sp *ServerPeer) {
}
}

// QueueTx adds the passed transaction message and peer to the block handling
// queue. Responds to the done channel argument after the tx message is
// processed.
func (b *blockManager) QueueTx(tx *ltcutil.Tx, sp *ServerPeer) {
// No channel handling here because peers do not need to block on inv
// messages.
if atomic.LoadInt32(&b.shutdown) != 0 {
return
}

select {
case b.peerChan <- &txMsg{tx: tx, peer: sp}:
case <-b.quit:
return
}
}

// handleInvMsg handles inv messages from all peers.
// We examine the inventory advertised by the remote peer and act accordingly.
func (b *blockManager) handleInvMsg(imsg *invMsg) {
invVects := imsg.inv.InvList
if b.BlockHeadersSynced() {
gdmsg := wire.NewMsgGetData()
for _, iv := range invVects {
if iv.Type == wire.InvTypeTx {
if b.cfg.mempool.HaveTransaction(&iv.Hash) {
continue
}
if _, exists := b.requestedTxns[iv.Hash]; !exists {
b.requestedTxns[iv.Hash] = struct{}{}
gdmsg.AddInvVect(&wire.InvVect{
Type: wire.InvTypeMwebTx,
Hash: iv.Hash,
})
}
}
}
if len(gdmsg.InvList) > 0 {
imsg.peer.QueueMessage(gdmsg, nil)
}
}

// Attempt to find the final block in the inventory list. There may
// not be one.
lastBlock := -1
invVects := imsg.inv.InvList
for i := len(invVects) - 1; i >= 0; i-- {
if invVects[i].Type == wire.InvTypeBlock {
lastBlock = i
Expand Down Expand Up @@ -2344,6 +2415,17 @@ func (b *blockManager) handleInvMsg(imsg *invMsg) {
}
}

// handleTxMsg handles transaction messages from all peers.
func (b *blockManager) handleTxMsg(tmsg *txMsg) {
txHash := tmsg.tx.Hash()
if _, exists := b.requestedTxns[*txHash]; !exists {
log.Warnf("Peer %s sent us a transaction we didn't request", tmsg.peer.Addr())
return
}
b.cfg.mempool.AddTransaction(tmsg.tx)
delete(b.requestedTxns, *txHash)
}

// QueueHeaders adds the passed headers message and peer to the block handling
// queue.
func (b *blockManager) QueueHeaders(headers *wire.MsgHeaders, sp *ServerPeer) {
Expand Down Expand Up @@ -2729,6 +2811,12 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
b.headerTipHash = *finalHash
b.newHeadersMtx.Unlock()
b.newHeadersSignal.Broadcast()

// Clear the mempool to free up memory. This may mean we might receive
// transactions we've previously downloaded but this is rather unlikely.
if b.cfg.mempool != nil {
b.cfg.mempool.Clear()
}
}

// areHeadersConnected returns true if the passed block headers are connected to
Expand Down Expand Up @@ -3044,3 +3132,13 @@ func (l *lightHeaderCtx) RelativeAncestorCtx(
ancestorHeight, ancestor, l.store, l.headerList,
)
}

// RegisterMwebUtxosCallback will register a callback that will fire when
// new mweb utxos are received.
func (b *blockManager) RegisterMwebUtxosCallback(
onMwebUtxos func(*mweb.Leafset, []*wire.MwebNetUtxo)) {

b.mwebUtxosCallbacksMtx.Lock()
defer b.mwebUtxosCallbacksMtx.Unlock()
b.mwebUtxosCallbacks = append(b.mwebUtxosCallbacks, onMwebUtxos)
}
26 changes: 13 additions & 13 deletions blockmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ import (
"testing"
"time"

"github.com/ltcsuite/ltcd/blockchain"
"github.com/ltcsuite/ltcd/chaincfg"
"github.com/ltcsuite/ltcd/chaincfg/chainhash"
"github.com/ltcsuite/ltcd/integration/rpctest"
"github.com/ltcsuite/ltcd/ltcutil/gcs"
"github.com/ltcsuite/ltcd/ltcutil/gcs/builder"
"github.com/ltcsuite/ltcd/peer"
"github.com/ltcsuite/ltcd/txscript"
"github.com/ltcsuite/ltcd/wire"
"github.com/ltcmweb/ltcd/blockchain"
"github.com/ltcmweb/ltcd/chaincfg"
"github.com/ltcmweb/ltcd/chaincfg/chainhash"
"github.com/ltcmweb/ltcd/integration/rpctest"
"github.com/ltcmweb/ltcd/ltcutil/gcs"
"github.com/ltcmweb/ltcd/ltcutil/gcs/builder"
"github.com/ltcmweb/ltcd/peer"
"github.com/ltcmweb/ltcd/txscript"
"github.com/ltcmweb/ltcd/wire"
"github.com/ltcmweb/neutrino/banman"
"github.com/ltcmweb/neutrino/blockntfns"
"github.com/ltcmweb/neutrino/headerfs"
"github.com/ltcmweb/neutrino/query"
"github.com/ltcsuite/ltcwallet/walletdb"
"github.com/ltcsuite/neutrino/banman"
"github.com/ltcsuite/neutrino/blockntfns"
"github.com/ltcsuite/neutrino/headerfs"
"github.com/ltcsuite/neutrino/query"
"github.com/stretchr/testify/require"
)

Expand Down
4 changes: 2 additions & 2 deletions blockntfns/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"testing"
"time"

"github.com/ltcsuite/ltcd/wire"
"github.com/ltcsuite/neutrino/blockntfns"
"github.com/ltcmweb/ltcd/wire"
"github.com/ltcmweb/neutrino/blockntfns"
)

var emptyHeader wire.BlockHeader
Expand Down
2 changes: 1 addition & 1 deletion blockntfns/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package blockntfns
import (
"fmt"

"github.com/ltcsuite/ltcd/wire"
"github.com/ltcmweb/ltcd/wire"
)

// BlockNtfn is an interface that coalesces all the different types of block
Expand Down
Loading