diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f93a18912..0086409b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -186,10 +186,10 @@ jobs: parallel: true ######################## - # run integration tests + # run ubuntu integration tests ######################## - integration-test: - name: run itests + ubuntu-integration-test: + name: run ubuntu itests runs-on: ubuntu-latest if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' strategy: @@ -230,17 +230,18 @@ jobs: - name: Zip log files on failure if: ${{ failure() }} - timeout-minutes: 1 # timeout after 1 minute - run: 7z a logs-itest-${{ matrix.name }}.zip lntest/itest/**/*.log + timeout-minutes: 5 # timeout after 5 minute + run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log - name: Upload log files on failure - uses: actions/upload-artifact@v2.2.4 + uses: actions/upload-artifact@v3 if: ${{ failure() }} with: name: logs-itest-${{ matrix.name }} path: logs-itest-${{ matrix.name }}.zip retention-days: 5 + ######################## # run windows integration test ######################## @@ -258,102 +259,15 @@ jobs: go-version: '${{ env.GO_VERSION }}' - name: run itest - run: make itest-parallel windows=1 tranches=2 parallel=2 + run: make itest-parallel windows=1 - name: Zip log files on failure if: ${{ failure() }} - run: 7z a logs-itest-windows.zip lntest/itest/**/*.log + timeout-minutes: 5 # timeout after 5 minute + run: 7z a logs-itest-windows.zip itest/**/*.log - name: Upload log files on failure - uses: actions/upload-artifact@v2 - if: ${{ failure() }} - with: - name: logs-itest-windows - path: logs-itest-windows.zip - retention-days: 5 - - ######################## - # run new integration tests - ######################## - new-integration-test: - name: run new itests - runs-on: ubuntu-latest - if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' - strategy: - # Allow other tests in the matrix to continue if one fails. - fail-fast: false - matrix: - include: - - name: btcd - args: backend=btcd - - name: bitcoind - args: backend=bitcoind - - name: bitcoind-notxindex - args: backend="bitcoind notxindex" - - name: bitcoind-rpcpolling - args: backend="bitcoind rpcpolling" - - name: bitcoind-etcd - args: backend=bitcoind dbbackend=etcd - - name: bitcoind-postgres - args: backend=bitcoind dbbackend=postgres - - name: bitcoind-sqlite - args: backend=bitcoind dbbackend=sqlite - - name: neutrino - args: backend=neutrino - steps: - - name: git checkout - uses: actions/checkout@v3 - - - name: setup go ${{ env.GO_VERSION }} - uses: ./.github/actions/setup-go - with: - go-version: '${{ env.GO_VERSION }}' - - - name: install bitcoind - run: ./scripts/install_bitcoind.sh - - - name: run new ${{ matrix.name }} - run: make itest-parallel temptest=true ${{ matrix.args }} - - - name: Zip log files on failure - if: ${{ failure() }} - timeout-minutes: 1 # timeout after 1 minute - run: 7z a logs-itest-${{ matrix.name }}.zip lntest/itest/**/*.log - - - name: Upload log files on failure - uses: actions/upload-artifact@v2.2.4 - if: ${{ failure() }} - with: - name: logs-itest-${{ matrix.name }} - path: logs-itest-${{ matrix.name }}.zip - retention-days: 5 - - ######################## - # run new windows integration test - ######################## - new-windows-integration-test: - name: run new windows itest - runs-on: windows-latest - if: '!contains(github.event.pull_request.labels.*.name, ''no-itest'')' - steps: - - name: git checkout - uses: actions/checkout@v3 - - - name: setup go ${{ env.GO_VERSION }} - uses: ./.github/actions/setup-go - with: - go-version: '${{ env.GO_VERSION }}' - - - name: run new itest - run: make itest-parallel temptest=true windows=1 tranches=2 parallel=2 - - - name: Zip log files on failure - if: ${{ failure() }} - timeout-minutes: 1 # timeout after 1 minute - run: 7z a logs-itest-windows.zip lntest/itest/**/*.log - - - name: Upload log files on failure - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: ${{ failure() }} with: name: logs-itest-windows diff --git a/.gitignore b/.gitignore index 106af84c2..16d33c142 100644 --- a/.gitignore +++ b/.gitignore @@ -32,12 +32,12 @@ _testmain.go /lncli-itest # Integration test log files -lntest/itest/*.log -lntest/itest/.backendlogs -lntest/itest/.minerlogs -lntest/itest/lnd-itest -lntest/itest/btcd-itest -lntest/itest/.logs-* +itest/*.log +itest/.backendlogs +itest/.minerlogs +itest/lnd-itest +itest/btcd-itest +itest/.logs-* cmd/cmd *.key diff --git a/.golangci.yml b/.golangci.yml index 2c5092588..4bbce4669 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -27,6 +27,7 @@ run: - kvdb_etcd - kvdb_postgres - kvdb_sqlite + - integration linters-settings: govet: @@ -96,6 +97,7 @@ linters-settings: ignored-functions: - 'math.*' - 'strconv.ParseInt' + - 'errors.Wrap' linters: @@ -237,14 +239,9 @@ issues: linters: - forbidigo - # Fix false positives because of build flags in itest directory. - - path: lntest/itest/.* + - path: itest/.* linters: - - unused - - unparam - - govet - # itest case can be very long so we disable long function check. - - funlen + - paralleltest - path: lnmock/* linters: diff --git a/.travis.yml b/.travis.yml index c6a925020..312772f5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,31 +32,15 @@ sudo: required jobs: include: - - stage: Sanity Check - name: Lint and compile - script: - # Step 1: Make sure no diff is produced when compiling with the correct - # version. - - make rpc-check - - # Step 2: Make sure the unit tests compile, but don't run them. They run - # in a GitHub Workflow. - - make unit pkg=... case=_NONE_ - - # Step 3: Lint go code. Invoke GC more often to reduce memory usage. - - GOGC=30 make lint - - stage: Integration Test name: Bitcoind Integration ARM script: - bash ./scripts/install_bitcoind.sh - - GOARM=7 GOARCH=arm GOOS=linux make itest-parallel backend=bitcoind tranches=3 parallel=3 + - GOMEMLIMIT=500MiB GOARM=7 GOARCH=arm GOOS=linux travis_wait 30 make itest-parallel backend=bitcoind arch: arm64 - services: - - docker after_failure: - |- - LOG_FILES=$(find ./lntest/itest -name '*.log') + LOG_FILES=$(find ./itest -name '*.log') echo "Uploading to termbin.com..." && for f in $LOG_FILES; do echo -n $f; cat $f | nc termbin.com 9999 | xargs -r0 printf ' uploaded to %s'; done echo "Uploading to file.io..." && tar -zcvO $LOG_FILES | curl -s -F 'file=@-;filename=logs.tar.gz' https://file.io | xargs -r0 printf 'logs.tar.gz uploaded to %s\n' diff --git a/Makefile b/Makefile index 174fe0f03..a45e86817 100644 --- a/Makefile +++ b/Makefile @@ -93,19 +93,19 @@ build: build-itest: @$(call print, "Building itest btcd and lnd.") - CGO_ENABLED=0 $(GOBUILD) -tags="rpctest" -o lntest/itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG) - CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o lntest/itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd + CGO_ENABLED=0 $(GOBUILD) -tags="integration" -o itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG) + CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd @$(call print, "Building itest binary for ${backend} backend.") - CGO_ENABLED=0 $(GOTEST) -v ./lntest/itest -tags="$(DEV_TAGS) $(RPC_TAGS) rpctest $(backend)" -c -o lntest/itest/itest.test$(EXEC_SUFFIX) + CGO_ENABLED=0 $(GOTEST) -v ./itest -tags="$(DEV_TAGS) $(RPC_TAGS) integration $(backend)" -c -o itest/itest.test$(EXEC_SUFFIX) build-itest-race: @$(call print, "Building itest btcd and lnd with race detector.") - CGO_ENABLED=0 $(GOBUILD) -tags="rpctest" -o lntest/itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG) - CGO_ENABLED=1 $(GOBUILD) -race -tags="$(ITEST_TAGS)" -o lntest/itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd + CGO_ENABLED=0 $(GOBUILD) -tags="integration" -o itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG) + CGO_ENABLED=1 $(GOBUILD) -race -tags="$(ITEST_TAGS)" -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd @$(call print, "Building itest binary for ${backend} backend.") - CGO_ENABLED=0 $(GOTEST) -v ./lntest/itest -tags="$(DEV_TAGS) $(RPC_TAGS) rpctest $(backend)" -c -o lntest/itest/itest.test$(EXEC_SUFFIX) + CGO_ENABLED=0 $(GOTEST) -v ./itest -tags="$(DEV_TAGS) $(RPC_TAGS) integration $(backend)" -c -o itest/itest.test$(EXEC_SUFFIX) install: @$(call print, "Installing lnd and lncli.") @@ -164,7 +164,7 @@ endif itest-only: db-instance @$(call print, "Running integration tests with ${backend} backend.") - rm -rf lntest/itest/*.log lntest/itest/.logs-*; date + rm -rf itest/*.log itest/.logs-*; date EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_part.sh 0 1 $(TEST_FLAGS) $(ITEST_FLAGS) itest: build-itest itest-only @@ -173,7 +173,7 @@ itest-race: build-itest-race itest-only itest-parallel: build-itest db-instance @$(call print, "Running tests") - rm -rf lntest/itest/*.log lntest/itest/.logs-*; date + rm -rf itest/*.log itest/.logs-*; date EXEC_SUFFIX=$(EXEC_SUFFIX) echo "$$(seq 0 $$(expr $(ITEST_PARALLELISM) - 1))" | xargs -P $(ITEST_PARALLELISM) -n 1 -I {} scripts/itest_part.sh {} $(NUM_ITEST_TRANCHES) $(TEST_FLAGS) $(ITEST_FLAGS) itest-clean: diff --git a/aezeed/cipherseed_rpctest.go b/aezeed/cipherseed_integration.go similarity index 88% rename from aezeed/cipherseed_rpctest.go rename to aezeed/cipherseed_integration.go index 1fb25e31f..50d29cb79 100644 --- a/aezeed/cipherseed_rpctest.go +++ b/aezeed/cipherseed_integration.go @@ -1,5 +1,4 @@ -//go:build rpctest -// +build rpctest +//go:build integration package aezeed diff --git a/contractcourt/breacharbiter_test.go b/contractcourt/breacharbiter_test.go index 3dbbca3f9..263ab45ea 100644 --- a/contractcourt/breacharbiter_test.go +++ b/contractcourt/breacharbiter_test.go @@ -1,6 +1,3 @@ -//go:build !rpctest -// +build !rpctest - package contractcourt import ( diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 3939fef0b..99472e0c1 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -497,10 +497,10 @@ func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { "chan_point=%v", chanState.FundingOutpoint) } - log.Debugf("ChannelPoint(%v): local_commit_type=%v, local_commit=%v", + log.Tracef("ChannelPoint(%v): local_commit_type=%v, local_commit=%v", chanState.FundingOutpoint, chanState.ChanType, spew.Sdump(localCommit)) - log.Debugf("ChannelPoint(%v): remote_commit_type=%v, remote_commit=%v", + log.Tracef("ChannelPoint(%v): remote_commit_type=%v, remote_commit=%v", chanState.FundingOutpoint, chanState.ChanType, spew.Sdump(remoteCommit)) @@ -527,7 +527,7 @@ func newChainSet(chanState *channeldb.OpenChannel) (*chainSet, error) { var remotePendingCommit *channeldb.ChannelCommitment if remoteChainTip != nil { remotePendingCommit = &remoteChainTip.Commitment - log.Debugf("ChannelPoint(%v): remote_pending_commit_type=%v, "+ + log.Tracef("ChannelPoint(%v): remote_pending_commit_type=%v, "+ "remote_pending_commit=%v", chanState.FundingOutpoint, chanState.ChanType, spew.Sdump(remoteChainTip.Commitment)) diff --git a/contractcourt/nursery_store_test.go b/contractcourt/nursery_store_test.go index f82758901..ca537693c 100644 --- a/contractcourt/nursery_store_test.go +++ b/contractcourt/nursery_store_test.go @@ -1,6 +1,3 @@ -//go:build !rpctest -// +build !rpctest - package contractcourt import ( diff --git a/contractcourt/utxonursery_test.go b/contractcourt/utxonursery_test.go index 99b139ea4..3f5026ddc 100644 --- a/contractcourt/utxonursery_test.go +++ b/contractcourt/utxonursery_test.go @@ -1,6 +1,3 @@ -//go:build !rpctest -// +build !rpctest - package contractcourt import ( diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 15dfe796e..caa1c1637 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -316,6 +316,11 @@ type Config struct { // ChannelID. This is used to sign updates for them if the channel has // no AuthProof and the option-scid-alias feature bit was negotiated. GetAlias func(lnwire.ChannelID) (lnwire.ShortChannelID, error) + + // FindChannel allows the gossiper to find a channel that we're party + // to without iterating over the entire set of open channels. + FindChannel func(node *btcec.PublicKey, chanID lnwire.ChannelID) ( + *channeldb.OpenChannel, error) } // processedNetworkMsg is a wrapper around networkMsg and a boolean. It is @@ -3016,6 +3021,16 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg, ann.ShortChannelID, ) if err != nil { + _, err = d.cfg.FindChannel(nMsg.source, ann.ChannelID) + if err != nil { + err := fmt.Errorf("unable to store the proof for "+ + "short_chan_id=%v: %v", shortChanID, err) + log.Error(err) + nMsg.err <- err + + return nil, false + } + proof := channeldb.NewWaitingProof(nMsg.isRemote, ann) err := d.cfg.WaitingProofStore.Add(proof) if err != nil { diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index f215b77ae..37823469c 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -697,6 +697,12 @@ func createChannelAnnouncement(blockHeight uint32, key1, key2 *btcec.PrivateKey, return a, nil } +func mockFindChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) ( + *channeldb.OpenChannel, error) { + + return nil, nil +} + type testCtx struct { gossiper *AuthenticatedGossiper router *mockGraphSource @@ -792,6 +798,7 @@ func createTestCtx(t *testing.T, startHeight uint32) (*testCtx, error) { SignAliasUpdate: signAliasUpdate, FindBaseByAlias: findBaseByAlias, GetAlias: getAlias, + FindChannel: mockFindChannel, }, selfKeyDesc) if err := gossiper.Start(); err != nil { diff --git a/docs/code_contribution_guidelines.md b/docs/code_contribution_guidelines.md index 8a13d371d..79f03df65 100644 --- a/docs/code_contribution_guidelines.md +++ b/docs/code_contribution_guidelines.md @@ -155,7 +155,7 @@ A quick summary of test practices follows: or RPC's will need to be accompanied by integration tests which use the [`networkHarness`framework](https://github.com/lightningnetwork/lnd/blob/master/lntest/harness.go) contained within `lnd`. For example integration tests, see - [`lnd_test.go`](https://github.com/lightningnetwork/lnd/blob/master/lntest/itest/lnd_test.go). + [`lnd_test.go`](https://github.com/lightningnetwork/lnd/blob/master/itest/lnd_test.go). - The itest log files are automatically scanned for `[ERR]` lines. There shouldn't be any of those in the logs, see [Use of Log Levels](#use-of-log-levels). diff --git a/docs/musig2.md b/docs/musig2.md index e46398832..555dfd7ec 100644 --- a/docs/musig2.md +++ b/docs/musig2.md @@ -44,7 +44,7 @@ to test the RPCs and to showcase the different use cases. ### 3-of-3 Taproot key spend path (BIP-0086) See `testTaprootMuSig2KeySpendBip86` in -[`lntest/itest/lnd_taproot_test.go`](../lntest/itest/lnd_taproot_test.go) to see +[`itest/lnd_taproot_test.go`](../itest/lnd_taproot_test.go) to see the full code. This example uses combines the public keys of 3 participants into a shared @@ -73,7 +73,7 @@ the `MuSig2CreateSession` RPC call: ### 3-of-3 Taproot key spend path with root hash commitment See `testTaprootMuSig2KeySpendRootHash` in -[`lntest/itest/lnd_taproot_test.go`](../lntest/itest/lnd_taproot_test.go) to see +[`itest/lnd_taproot_test.go`](../itest/lnd_taproot_test.go) to see the full code. This is very similar to the above example but with the main difference that the @@ -101,7 +101,7 @@ the `MuSig2CreateSession` RPC call: ### 3-of-3 `OP_CHECKSIG` in Taproot script spend path See `testTaprootMuSig2CombinedLeafKeySpend` in -[`lntest/itest/lnd_taproot_test.go`](../lntest/itest/lnd_taproot_test.go) to see +[`itest/lnd_taproot_test.go`](../itest/lnd_taproot_test.go) to see the full code. This example is definitely the most involved one. To be able to use a MuSig2 diff --git a/docs/release-notes/release-notes-0.16.0.md b/docs/release-notes/release-notes-0.16.0.md index 20b74546b..73035ad6e 100644 --- a/docs/release-notes/release-notes-0.16.0.md +++ b/docs/release-notes/release-notes-0.16.0.md @@ -487,7 +487,8 @@ PRs([6776](https://github.com/lightningnetwork/lnd/pull/6776), [7242](https://github.com/lightningnetwork/lnd/pull/7242), [7245](https://github.com/lightningnetwork/lnd/pull/7245)), [6823](https://github.com/lightningnetwork/lnd/pull/6823), -[6824](https://github.com/lightningnetwork/lnd/pull/6824),) have been made to +[6824](https://github.com/lightningnetwork/lnd/pull/6824), +[6825](https://github.com/lightningnetwork/lnd/pull/6825)) have been made to refactor the itest for code health and maintenance. # Contributors (Alphabetical Order) diff --git a/funding/config_rpctest.go b/funding/config_integration.go similarity index 90% rename from funding/config_rpctest.go rename to funding/config_integration.go index 44a2c939c..06bb6e173 100644 --- a/funding/config_rpctest.go +++ b/funding/config_integration.go @@ -1,4 +1,4 @@ -//go:build rpctest +//go:build integration package funding diff --git a/funding/manager.go b/funding/manager.go index 0f65e920e..2d702ba48 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -108,6 +108,11 @@ const ( // for the funding transaction to be confirmed before forgetting // channels that aren't initiated by us. 2016 blocks is ~2 weeks. maxWaitNumBlocksFundingConf = 2016 + + // pendingChansLimit is the maximum number of pending channels that we + // can have. After this point, pending channel opens will start to be + // rejected. + pendingChansLimit = 1_000 ) var ( @@ -1320,6 +1325,25 @@ func (f *Manager) handleFundingOpen(peer lnpeer.Peer, return } + // Ensure that the pendingChansLimit is respected. + pendingChans, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels() + if err != nil { + f.failFundingFlow( + peer, msg.PendingChannelID, err, + ) + + return + } + + if len(pendingChans) > pendingChansLimit { + f.failFundingFlow( + peer, msg.PendingChannelID, + lnwire.ErrMaxPendingChannels, + ) + + return + } + // We'll also reject any requests to create channels until we're fully // synced to the network as we won't be able to properly validate the // confirmation of the funding transaction. diff --git a/funding/manager_test.go b/funding/manager_test.go index dedbb2efa..f386e3de5 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -1,6 +1,3 @@ -//go:build !rpctest -// +build !rpctest - package funding import ( diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 88476f33e..bb800f5e4 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -2743,6 +2743,15 @@ func (l *channelLink) handleSwitchPacket(pkt *htlcPacket) error { // // NOTE: Part of the ChannelLink interface. func (l *channelLink) HandleChannelUpdate(message lnwire.Message) { + select { + case <-l.quit: + // Return early if the link is already in the process of + // quitting. It doesn't make sense to hand the message to the + // mailbox here. + return + default: + } + l.mailBox.AddMessage(message) } diff --git a/itest/README.md b/itest/README.md new file mode 100644 index 000000000..6413a4e1f --- /dev/null +++ b/itest/README.md @@ -0,0 +1,106 @@ +# Integration Test + +`itest` is a package that houses the integration tests made for `lnd`. This +package builds test cases using the test framework `lntest`. + +## Add New Tests + +To add a new test case, create a `TestFunc` and add it in `list_on_test.go`. +Ideally, the `Name` should just be the snake case of the name used in +`TestFunc` without the leading `test` and underscores. For instance, to test +`lnd`'s exporting channel backup, we have, + +```go +{ + Name: "export channel backup", + TestFunc: testExportChannelBackup, +} +``` + +The place to put the code of the `TestFunc` is case-specific. `itest` package +has loosely defined a list of files to test different functionalities of `lnd`. +The new test needs to be put into one of these files, otherwise, a new file +needs to be created. + +## Run Tests + +#### Run a single test case + +To run a single test case, use `make itest icase=$case`, where `case` is the +name defined in `list_on_test.go`, with spaces replaced with underscores(`_`). + +```shell +# Run `testListChannels`. +make itest icase=list_channels +``` + +#### Run multiple test cases + +There are two ways to run multiple test cases. One way is to use `make itest +icase=$cases`, where `cases` has the format `cases='(case|case|...)'`. The +`case` is the name defined in `list_on_test.go`, with spaces replaced with +underscores(`_`). + +```shell +# Run `testListChannels` and `testListAddresses` together. +make itest icase='(list_channels|list_addresses)' +``` + +Another way to run multiple cases is similar to how Go runs its tests - by +simple regex matching. For instance, the following command will run three cases +since they all start with the word `list`, + +```shell +# Run `testListChannels`, `testListAddresses`, and `testListPayments` together. +make itest icase=list +``` + +#### Run all tests + +To run all tests, use `make itest` without `icase` flag. + +```shell +# Run all test cases. +make itest +``` + +#### Run tests in parallel + +To run tests in parallel, use `make itest-parallel`. This command takes two +special arguments, +- `tranches`, specifies the number of parts the test cases will be split into. +- `parallel`, specifies the number of threads to run in parallel. This value + must be smaller than or equal to `tranches`. + +```shell +# Split the tests into 4 parts, and run them using 2 threads. +make itest-parallel tranches=4 parallel=2 +``` + +By default, `itest-parallel` splits the tests into 4 parts and uses 4 threads +to run each of them. + +#### Additional arguments + +For both `make itest` and `make itest-parallel`, the following arguments are +allowed, +- `timeout`, specifies the timeout value used in testing. +- `dbbackend`, specifies the database backend. Must be `bbolt`, `etcd`, or + `postgres`, default to `bbolt`. +- `backend`, specifies the chain backend to be used. Must be one of, + - `btcd`, the default value. + - `neutrino` + - `bitcoind` + - `bitcoind notxindex` + - `bitcoind rpcpolling` + +```shell +# Run a single test case using bitcoind as the chain backend and etcd as the +# database backend, with a timeout of 5 minutes. +make itest icase=list_channels backend=bitcoind dbbackend=etcd timeout=5m + +# Run all test cases in parallel, using bitcoind notxindex as the chain backend +# and etcd as the database backend, with a timeout of 60 minutes for each +# parallel. +make itest-parallel backend="bitcoind notxindex" dbbackend=etcd timeout=60m +``` diff --git a/itest/list_off_test.go b/itest/list_off_test.go new file mode 100644 index 000000000..a510747e8 --- /dev/null +++ b/itest/list_off_test.go @@ -0,0 +1,7 @@ +//go:build !integration + +package itest + +import "github.com/lightningnetwork/lnd/lntest" + +var allTestCases = []*lntest.TestCase{} diff --git a/lntest/itest/list_on_test.go b/itest/list_on_test.go similarity index 98% rename from lntest/itest/list_on_test.go rename to itest/list_on_test.go index f2531203f..4a9b2baec 100644 --- a/lntest/itest/list_on_test.go +++ b/itest/list_on_test.go @@ -1,12 +1,10 @@ -//go:build rpctest -// +build rpctest +//go:build integration package itest -import "github.com/lightningnetwork/lnd/lntemp" +import "github.com/lightningnetwork/lnd/lntest" -// TODO(yy): remove the temp. -var allTestCasesTemp = []*lntemp.TestCase{ +var allTestCases = []*lntest.TestCase{ { Name: "update channel status", TestFunc: testUpdateChanStatus, @@ -509,4 +507,8 @@ var allTestCasesTemp = []*lntemp.TestCase{ Name: "zero conf reorg edge existence", TestFunc: testZeroConfReorg, }, + { + Name: "async bidirectional payments", + TestFunc: testBidirectionalAsyncPayments, + }, } diff --git a/lntest/itest/lnd_amp_test.go b/itest/lnd_amp_test.go similarity index 98% rename from lntest/itest/lnd_amp_test.go rename to itest/lnd_amp_test.go index 46dddbd08..a76aedd99 100644 --- a/lntest/itest/lnd_amp_test.go +++ b/itest/lnd_amp_test.go @@ -11,15 +11,15 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" ) // testSendPaymentAMPInvoice tests that we can send an AMP payment to a // specified AMP invoice using SendPaymentV2. -func testSendPaymentAMPInvoice(ht *lntemp.HarnessTest) { +func testSendPaymentAMPInvoice(ht *lntest.HarnessTest) { succeed := ht.Run("native payaddr", func(t *testing.T) { tt := ht.Subtest(t) testSendPaymentAMPInvoiceCase(tt, false) @@ -36,7 +36,7 @@ func testSendPaymentAMPInvoice(ht *lntemp.HarnessTest) { }) } -func testSendPaymentAMPInvoiceCase(ht *lntemp.HarnessTest, +func testSendPaymentAMPInvoiceCase(ht *lntest.HarnessTest, useExternalPayAddr bool) { mts := newMppTestScenario(ht) @@ -196,7 +196,7 @@ func testSendPaymentAMPInvoiceCase(ht *lntemp.HarnessTest, // testSendPaymentAMPInvoiceRepeat tests that it's possible to pay an AMP // invoice multiple times by having the client generate a new setID each time. -func testSendPaymentAMPInvoiceRepeat(ht *lntemp.HarnessTest) { +func testSendPaymentAMPInvoiceRepeat(ht *lntest.HarnessTest) { // In this basic test, we'll only need two nodes as we want to // primarily test the recurring payment feature. So we'll re-use the carol := ht.NewNode("Carol", nil) @@ -218,7 +218,7 @@ func testSendPaymentAMPInvoiceRepeat(ht *lntemp.HarnessTest) { // Establish a channel between Carol and Dave. chanAmt := btcutil.Amount(100_000) ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) // Create an AMP invoice of a trivial amount, that we'll pay repeatedly @@ -353,7 +353,7 @@ func testSendPaymentAMPInvoiceRepeat(ht *lntemp.HarnessTest) { // testSendPaymentAMP tests that we can send an AMP payment to a specified // destination using SendPaymentV2. -func testSendPaymentAMP(ht *lntemp.HarnessTest) { +func testSendPaymentAMP(ht *lntest.HarnessTest) { mts := newMppTestScenario(ht) const paymentAmt = btcutil.Amount(300000) @@ -460,7 +460,7 @@ func testSendPaymentAMP(ht *lntemp.HarnessTest) { mts.closeChannels() } -func testSendToRouteAMP(ht *lntemp.HarnessTest) { +func testSendToRouteAMP(ht *lntest.HarnessTest) { mts := newMppTestScenario(ht) const ( paymentAmt = btcutil.Amount(300000) diff --git a/lntest/itest/lnd_channel_backup_test.go b/itest/lnd_channel_backup_test.go similarity index 92% rename from lntest/itest/lnd_channel_backup_test.go rename to itest/lnd_channel_backup_test.go index d254bb9f2..c05a0adee 100644 --- a/lntest/itest/lnd_channel_backup_test.go +++ b/itest/lnd_channel_backup_test.go @@ -18,8 +18,8 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -34,7 +34,7 @@ type ( // that'll return the same node, but with its state restored via a // custom method. We use this to abstract away _how_ a node is restored // from our assertions once the node has been fully restored itself. - restoreMethodType func(ht *lntemp.HarnessTest, + restoreMethodType func(ht *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, mnemonic []string) nodeRestorer ) @@ -50,12 +50,12 @@ type chanRestoreScenario struct { dave *node.HarnessNode password []byte mnemonic []string - params lntemp.OpenChannelParams + params lntest.OpenChannelParams } // newChanRestoreScenario creates a new scenario that has two nodes, Carol and // Dave, connected and funded. -func newChanRestoreScenario(ht *lntemp.HarnessTest, ct lnrpc.CommitmentType, +func newChanRestoreScenario(ht *lntest.HarnessTest, ct lnrpc.CommitmentType, zeroConf bool) *chanRestoreScenario { const ( @@ -70,7 +70,7 @@ func newChanRestoreScenario(ht *lntemp.HarnessTest, ct lnrpc.CommitmentType, } if ct != lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE { - args := nodeArgsForCommitType(ct) + args := lntest.NodeArgsForCommitType(ct) nodeArgs = append(nodeArgs, args...) } @@ -99,7 +99,7 @@ func newChanRestoreScenario(ht *lntemp.HarnessTest, ct lnrpc.CommitmentType, // For the anchor output case we need two UTXOs for Carol so she can // sweep both the local and remote anchor. - if commitTypeHasAnchors(ct) { + if lntest.CommitTypeHasAnchors(ct) { ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) } @@ -112,7 +112,7 @@ func newChanRestoreScenario(ht *lntemp.HarnessTest, ct lnrpc.CommitmentType, dave: dave, mnemonic: mnemonic, password: password, - params: lntemp.OpenChannelParams{ + params: lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, ZeroConf: zeroConf, @@ -123,7 +123,7 @@ func newChanRestoreScenario(ht *lntemp.HarnessTest, ct lnrpc.CommitmentType, // restoreDave will call the `nodeRestorer` and asserts Dave is restored by // checking his wallet balance against zero. -func (c *chanRestoreScenario) restoreDave(ht *lntemp.HarnessTest, +func (c *chanRestoreScenario) restoreDave(ht *lntest.HarnessTest, restoredNodeFunc nodeRestorer) *node.HarnessNode { // Next, we'll make a new Dave and start the bulk of our recovery @@ -154,7 +154,7 @@ func (c *chanRestoreScenario) restoreDave(ht *lntemp.HarnessTest, // 4. validate pending channel state and check we cannot force close it. // 5. validate Carol's UTXOs. // 6. assert DLP is executed. -func (c *chanRestoreScenario) testScenario(ht *lntemp.HarnessTest, +func (c *chanRestoreScenario) testScenario(ht *lntest.HarnessTest, restoredNodeFunc nodeRestorer) { carol, dave := c.carol, c.dave @@ -212,7 +212,7 @@ func (c *chanRestoreScenario) testScenario(ht *lntemp.HarnessTest, // let's start up Carol again. require.NoError(ht, restartCarol(), "restart carol failed") - if commitTypeHasAnchors(c.params.CommitmentType) { + if lntest.CommitTypeHasAnchors(c.params.CommitmentType) { ht.AssertNumUTXOs(carol, 2) } else { ht.AssertNumUTXOs(carol, 1) @@ -232,7 +232,7 @@ func (c *chanRestoreScenario) testScenario(ht *lntemp.HarnessTest, // restoring from initial wallet creation. We'll also alternate between // restoring form the on disk file, and restoring from the exported RPC command // as well. -func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { +func testChannelBackupRestoreBasic(ht *lntest.HarnessTest) { var testCases = []struct { name string restoreMethod restoreMethodType @@ -241,7 +241,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // was the initiator, of the non-advertised channel. { name: "restore from RPC backup", - restoreMethod: func(st *lntemp.HarnessTest, + restoreMethod: func(st *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, @@ -269,7 +269,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // interface. { name: "restore from backup file", - restoreMethod: func(st *lntemp.HarnessTest, + restoreMethod: func(st *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, @@ -293,7 +293,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // prior mnemonic and new backup seed. { name: "restore during creation", - restoreMethod: func(st *lntemp.HarnessTest, + restoreMethod: func(st *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, @@ -325,7 +325,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // re-created, using the Unlock call. { name: "restore during unlock", - restoreMethod: func(st *lntemp.HarnessTest, + restoreMethod: func(st *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, @@ -362,7 +362,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // make sure imports can be canceled and later resumed. { name: "restore from backup file twice", - restoreMethod: func(st *lntemp.HarnessTest, + restoreMethod: func(st *lntest.HarnessTest, oldNode *node.HarnessNode, backupFilePath string, password []byte, @@ -376,6 +376,8 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // Now that we have Dave's backup file, we'll // create a new nodeRestorer that will restore // using the on-disk channel.backup. + // + //nolint:lll backup := &lnrpc.RestoreChanBackupRequest_MultiChanBackup{ MultiChanBackup: multi, } @@ -420,7 +422,7 @@ func testChannelBackupRestoreBasic(ht *lntemp.HarnessTest) { // ensuring that after Dave restores his channel state according to the // testCase, the DLP protocol is executed properly and both nodes are made // whole. -func runChanRestoreScenarioBasic(ht *lntemp.HarnessTest, +func runChanRestoreScenarioBasic(ht *lntest.HarnessTest, restoreMethod restoreMethodType) { // Create a new retore scenario. @@ -445,7 +447,7 @@ func runChanRestoreScenarioBasic(ht *lntemp.HarnessTest, // testChannelBackupRestoreUnconfirmed tests that we're able to restore from // disk file and the exported RPC command for unconfirmed channel. -func testChannelBackupRestoreUnconfirmed(ht *lntemp.HarnessTest) { +func testChannelBackupRestoreUnconfirmed(ht *lntest.HarnessTest) { // Use the channel backup file that contains an unconfirmed channel and // make sure recovery works as well. ht.Run("restore unconfirmed channel file", func(t *testing.T) { @@ -463,7 +465,7 @@ func testChannelBackupRestoreUnconfirmed(ht *lntemp.HarnessTest) { // runChanRestoreScenarioUnConfirmed checks that Dave is able to restore for an // unconfirmed channel. -func runChanRestoreScenarioUnConfirmed(ht *lntemp.HarnessTest, useFile bool) { +func runChanRestoreScenarioUnConfirmed(ht *lntest.HarnessTest, useFile bool) { // Create a new retore scenario. crs := newChanRestoreScenario( ht, lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE, false, @@ -527,7 +529,7 @@ func runChanRestoreScenarioUnConfirmed(ht *lntemp.HarnessTest, useFile bool) { // testChannelBackupRestoreCommitTypes tests that we're able to recover from, // and initiate the DLP protocol for different channel commitment types and // zero-conf channel. -func testChannelBackupRestoreCommitTypes(ht *lntemp.HarnessTest) { +func testChannelBackupRestoreCommitTypes(ht *lntest.HarnessTest) { var testCases = []struct { name string ct lnrpc.CommitmentType @@ -584,7 +586,7 @@ func testChannelBackupRestoreCommitTypes(ht *lntemp.HarnessTest) { // runChanRestoreScenarioCommitTypes tests that the DLP is applied for // different channel commitment types and zero-conf channel. -func runChanRestoreScenarioCommitTypes(ht *lntemp.HarnessTest, +func runChanRestoreScenarioCommitTypes(ht *lntest.HarnessTest, ct lnrpc.CommitmentType, zeroConf bool) { // Create a new retore scenario. @@ -606,7 +608,7 @@ func runChanRestoreScenarioCommitTypes(ht *lntemp.HarnessTest, _, minerHeight := ht.Miner.GetBestBlock() thawHeight := uint32(minerHeight + thawHeightDelta) - fundingShim, _, _ = deriveFundingShim( + fundingShim, _ = deriveFundingShim( ht, dave, carol, crs.params.Amt, thawHeight, true, ) crs.params.FundingShim = fundingShim @@ -639,7 +641,7 @@ func runChanRestoreScenarioCommitTypes(ht *lntemp.HarnessTest, // testChannelBackupRestoreLegacy checks a channel with the legacy revocation // producer format and makes sure old SCBs can still be recovered. -func testChannelBackupRestoreLegacy(ht *lntemp.HarnessTest) { +func testChannelBackupRestoreLegacy(ht *lntest.HarnessTest) { // Create a new retore scenario. crs := newChanRestoreScenario( ht, lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE, false, @@ -668,7 +670,7 @@ func testChannelBackupRestoreLegacy(ht *lntemp.HarnessTest) { // testChannelBackupRestoreForceClose checks that Dave can restore from force // closed channels. -func testChannelBackupRestoreForceClose(ht *lntemp.HarnessTest) { +func testChannelBackupRestoreForceClose(ht *lntest.HarnessTest) { // Restore a channel that was force closed by dave just before going // offline. success := ht.Run("from backup file anchors", func(t *testing.T) { @@ -691,7 +693,7 @@ func testChannelBackupRestoreForceClose(ht *lntemp.HarnessTest) { // runChanRestoreScenarioForceClose creates anchor-enabled force close channels // and checks that Dave is able to restore from them. -func runChanRestoreScenarioForceClose(ht *lntemp.HarnessTest, zeroConf bool) { +func runChanRestoreScenarioForceClose(ht *lntest.HarnessTest, zeroConf bool) { crs := newChanRestoreScenario( ht, lnrpc.CommitmentType_ANCHORS, zeroConf, ) @@ -792,7 +794,7 @@ func runChanRestoreScenarioForceClose(ht *lntemp.HarnessTest, zeroConf bool) { // testChannelBackupUpdates tests that both the streaming channel update RPC, // and the on-disk channel.backup are updated each time a channel is // opened/closed. -func testChannelBackupUpdates(ht *lntemp.HarnessTest) { +func testChannelBackupUpdates(ht *lntest.HarnessTest) { alice := ht.Alice // First, we'll make a temp directory that we'll use to store our @@ -850,7 +852,7 @@ func testChannelBackupUpdates(ht *lntemp.HarnessTest) { chanAmt := btcutil.Amount(1000000) for i := 0; i < numChans; i++ { chanPoint := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) chanPoints = append(chanPoints, chanPoint) } @@ -962,7 +964,7 @@ func testChannelBackupUpdates(ht *lntemp.HarnessTest) { // testExportChannelBackup tests that we're able to properly export either a // targeted channel's backup, or export backups of all the currents open // channels. -func testExportChannelBackup(ht *lntemp.HarnessTest) { +func testExportChannelBackup(ht *lntest.HarnessTest) { // First, we'll create our primary test node: Carol. We'll use Carol to // open channels and also export backups that we'll examine throughout // the test. @@ -979,7 +981,7 @@ func testExportChannelBackup(ht *lntemp.HarnessTest) { chanAmt := btcutil.Amount(1000000) for i := 0; i < numChans; i++ { chanPoint := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) chanPoints = append(chanPoints, chanPoint) } @@ -1022,17 +1024,20 @@ func testExportChannelBackup(ht *lntemp.HarnessTest) { require.NoError(ht, err, "timeout checking num single backup") } - assertMultiBackupFound := func() func(bool, map[wire.OutPoint]struct{}) { + assertMultiBackupFound := func() func(bool, + map[wire.OutPoint]struct{}) { + chanSnapshot := carol.RPC.ExportAllChanBackups() return func(found bool, chanPoints map[wire.OutPoint]struct{}) { + num := len(chanSnapshot.MultiChanBackup.MultiChanBackup) + switch { case found && chanSnapshot.MultiChanBackup == nil: require.Fail(ht, "multi-backup not present") case !found && chanSnapshot.MultiChanBackup != nil && - (len(chanSnapshot.MultiChanBackup.MultiChanBackup) != - chanbackup.NilMultiSizePacked): + num != chanbackup.NilMultiSizePacked: require.Fail(ht, "found multi-backup when "+ "non should be found") @@ -1086,7 +1091,7 @@ func testExportChannelBackup(ht *lntemp.HarnessTest) { // relationship lost state, they will detect this during channel sync, and the // up-to-date party will force close the channel, giving the outdated party the // opportunity to sweep its output. -func testDataLossProtection(ht *lntemp.HarnessTest) { +func testDataLossProtection(ht *lntest.HarnessTest) { const ( chanAmt = funding.MaxBtcFundingAmount paymentAmt = 10000 @@ -1107,22 +1112,20 @@ func testDataLossProtection(ht *lntemp.HarnessTest) { // directly from the miner. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) - // timeTravel is a method that will make Carol open a channel to the - // passed node, settle a series of payments, then reset the node back - // to the state before the payments happened. When this method returns - // the node will be unaware of the new state updates. The returned - // function can be used to restart the node in this state. - timeTravel := func(node *node.HarnessNode) (func() error, - *lnrpc.ChannelPoint, int64) { - + // timeTravelDave is a method that will make Carol open a channel to + // Dave, settle a series of payments, then Dave back to the state + // before the payments happened. When this method returns Dave will + // be unaware of the new state updates. The returned function can be + // used to restart Dave in this state. + timeTravelDave := func() (func() error, *lnrpc.ChannelPoint, int64) { // We must let the node communicate with Carol before they are // able to open channel, so we connect them. - ht.EnsureConnected(carol, node) + ht.EnsureConnected(carol, dave) // We'll first open up a channel between them with a 0.5 BTC // value. chanPoint := ht.OpenChannel( - carol, node, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -1132,17 +1135,17 @@ func testDataLossProtection(ht *lntemp.HarnessTest) { // the channel. // TODO(halseth): have dangling HTLCs on the commitment, able to // retrieve funds? - payReqs, _, _ := ht.CreatePayReqs(node, paymentAmt, numInvoices) + payReqs, _, _ := ht.CreatePayReqs(dave, paymentAmt, numInvoices) // Send payments from Carol using 3 of the payment hashes // generated above. ht.CompletePaymentRequests(carol, payReqs[:numInvoices/2]) - // Next query for the node's channel state, as we sent 3 - // payments of 10k satoshis each, it should now see his balance - // as being 30k satoshis. + // Next query for Dave's channel state, as we sent 3 payments + // of 10k satoshis each, it should now see his balance as being + // 30k satoshis. nodeChan := ht.AssertChannelLocalBalance( - node, chanPoint, 30_000, + dave, chanPoint, 30_000, ) // Grab the current commitment height (update number), we'll @@ -1153,39 +1156,46 @@ func testDataLossProtection(ht *lntemp.HarnessTest) { // With the temporary file created, copy the current state into // the temporary file we created above. Later after more // updates, we'll restore this state. - ht.BackupDB(node) + ht.BackupDB(dave) // Reconnect the peers after the restart that was needed for // the db backup. - ht.EnsureConnected(carol, node) + ht.EnsureConnected(carol, dave) - // Finally, send more payments from , using the remaining + // Finally, send more payments from Carol, using the remaining // payment hashes. ht.CompletePaymentRequests(carol, payReqs[numInvoices/2:]) - // Now we shutdown the node, copying over the its temporary + // TODO(yy): remove the sleep once the following bug is fixed. + // + // While the payment is reported as settled, the commitment + // dance may not be finished, which leaves several HTLCs in the + // commitment. Later on, when Carol force closes this channel, + // she would have HTLCs there and the test won't pass. + time.Sleep(2 * time.Second) + + // Now we shutdown Dave, copying over the its temporary // database state which has the *prior* channel state over his // current most up to date state. With this, we essentially - // force the node to travel back in time within the channel's + // force Dave to travel back in time within the channel's // history. - ht.RestartNodeAndRestoreDB(node) + ht.RestartNodeAndRestoreDB(dave) - // Make sure the channel is still there from the PoV of the - // node. - ht.AssertNodeNumChannels(node, 1) + // Make sure the channel is still there from the PoV of Dave. + ht.AssertNodeNumChannels(dave, 1) // Now query for the channel state, it should show that it's at // a state number in the past, not the *latest* state. - ht.AssertChannelNumUpdates(node, stateNumPreCopy, chanPoint) + ht.AssertChannelNumUpdates(dave, stateNumPreCopy, chanPoint) - balResp := node.RPC.WalletBalance() - restart := ht.SuspendNode(node) + balResp := dave.RPC.WalletBalance() + restart := ht.SuspendNode(dave) return restart, chanPoint, balResp.ConfirmedBalance } // Reset Dave to a state where he has an outdated channel state. - restartDave, _, daveStartingBalance := timeTravel(dave) + restartDave, _, daveStartingBalance := timeTravelDave() // We make a note of the nodes' current on-chain balances, to make sure // they are able to retrieve the channel funds eventually, @@ -1209,7 +1219,7 @@ func testDataLossProtection(ht *lntemp.HarnessTest) { // closed channel, such that Dave can retrieve his funds. // // We start by letting Dave time travel back to an outdated state. - restartDave, chanPoint2, daveStartingBalance := timeTravel(dave) + restartDave, chanPoint2, daveStartingBalance := timeTravelDave() carolBalResp = carol.RPC.WalletBalance() carolStartingBalance = carolBalResp.ConfirmedBalance @@ -1255,7 +1265,7 @@ func testDataLossProtection(ht *lntemp.HarnessTest) { // createLegacyRevocationChannel creates a single channel using the legacy // revocation producer format by using PSBT to signal a special pending channel // ID. -func createLegacyRevocationChannel(ht *lntemp.HarnessTest, +func createLegacyRevocationChannel(ht *lntest.HarnessTest, chanAmt, pushAmt btcutil.Amount, from, to *node.HarnessNode) { // We'll signal to the wallet that we also want to create a channel @@ -1277,7 +1287,7 @@ func createLegacyRevocationChannel(ht *lntemp.HarnessTest, }, }, } - openChannelReq := lntemp.OpenChannelParams{ + openChannelReq := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, FundingShim: shim, @@ -1295,8 +1305,9 @@ func createLegacyRevocationChannel(ht *lntemp.HarnessTest, } fundResp := from.RPC.FundPsbt(fundReq) - // We have a PSBT that has no witness data yet, which is exactly what we - // need for the next step of verifying the PSBT with the funding intents. + // We have a PSBT that has no witness data yet, which is exactly what + // we need for the next step of verifying the PSBT with the funding + // intents. msg := &lnrpc.FundingTransitionMsg{ Trigger: &lnrpc.FundingTransitionMsg_PsbtVerify{ PsbtVerify: &lnrpc.FundingPsbtVerify{ @@ -1345,7 +1356,7 @@ func createLegacyRevocationChannel(ht *lntemp.HarnessTest, // chanRestoreViaRPC is a helper test method that returns a nodeRestorer // instance which will restore the target node from a password+seed, then // trigger a SCB restore using the RPC interface. -func chanRestoreViaRPC(ht *lntemp.HarnessTest, password []byte, +func chanRestoreViaRPC(ht *lntest.HarnessTest, password []byte, mnemonic []string, multi []byte, oldNode *node.HarnessNode) nodeRestorer { @@ -1382,7 +1393,7 @@ func copyPorts(oldNode *node.HarnessNode) node.Option { // // Note: this function is only used in this test file and has been made // specifically for testChanRestoreScenario. -func assertTimeLockSwept(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode, +func assertTimeLockSwept(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, carolStartingBalance, daveStartingBalance int64) { // We expect Carol to sweep her funds and also the anchor tx. @@ -1450,11 +1461,13 @@ func assertTimeLockSwept(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode, // funds immediately, and Carol sweeping her fund after her CSV delay is up. If // the blankSlate value is true, then this means that Dave won't need to sweep // on chain as he has no funds in the channel. -func assertDLPExecuted(ht *lntemp.HarnessTest, +func assertDLPExecuted(ht *lntest.HarnessTest, carol *node.HarnessNode, carolStartingBalance int64, dave *node.HarnessNode, daveStartingBalance int64, commitType lnrpc.CommitmentType) { + ht.Helper() + // Increase the fee estimate so that the following force close tx will // be cpfp'ed. ht.SetFeeEstimate(30000) @@ -1467,7 +1480,7 @@ func assertDLPExecuted(ht *lntemp.HarnessTest, // Upon reconnection, the nodes should detect that Dave is out of sync. // Carol should force close the channel using her latest commitment. expectedTxes := 1 - if commitTypeHasAnchors(commitType) { + if lntest.CommitTypeHasAnchors(commitType) { expectedTxes = 2 } ht.Miner.AssertNumTxsInMempool(expectedTxes) diff --git a/lntest/itest/lnd_channel_balance_test.go b/itest/lnd_channel_balance_test.go similarity index 89% rename from lntest/itest/lnd_channel_balance_test.go rename to itest/lnd_channel_balance_test.go index 9f4d5715b..72dd16ea3 100644 --- a/lntest/itest/lnd_channel_balance_test.go +++ b/itest/lnd_channel_balance_test.go @@ -7,8 +7,8 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -16,7 +16,7 @@ import ( // testChannelBalance creates a new channel between Alice and Bob, then checks // channel balance to be equal amount specified while creation of channel. -func testChannelBalance(ht *lntemp.HarnessTest) { +func testChannelBalance(ht *lntest.HarnessTest) { // Open a channel with 0.16 BTC between Alice and Bob, ensuring the // channel has been opened properly. amount := funding.MaxBtcFundingAmount @@ -52,16 +52,16 @@ func testChannelBalance(ht *lntemp.HarnessTest) { ht.EnsureConnected(alice, bob) chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: amount}, + alice, bob, lntest.OpenChannelParams{Amt: amount}, ) cType := ht.GetChannelCommitType(alice, chanPoint) // As this is a single funder channel, Alice's balance should be // exactly 0.5 BTC since now state transitions have taken place yet. - checkChannelBalance(alice, amount-calcStaticFee(cType, 0), 0) + checkChannelBalance(alice, amount-lntest.CalcStaticFee(cType, 0), 0) // Ensure Bob currently has no available balance within the channel. - checkChannelBalance(bob, 0, amount-calcStaticFee(cType, 0)) + checkChannelBalance(bob, 0, amount-lntest.CalcStaticFee(cType, 0)) // Finally close the channel between Alice and Bob, asserting that the // channel has been properly closed on-chain. @@ -73,7 +73,7 @@ func testChannelBalance(ht *lntemp.HarnessTest) { // Alice will send Htlcs to Carol while she is in hodl mode. This will result // in a build of pending Htlcs. We expect the channels unsettled balance to // equal the sum of all the Pending Htlcs. -func testChannelUnsettledBalance(ht *lntemp.HarnessTest) { +func testChannelUnsettledBalance(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(1000000) // Creates a helper closure to be used below which asserts the proper @@ -123,17 +123,21 @@ func testChannelUnsettledBalance(ht *lntemp.HarnessTest) { // Open a channel between Alice and Carol. chanPointAlice := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) cType := ht.GetChannelCommitType(alice, chanPointAlice) // Check alice's channel balance, which should have zero remote and zero // pending balance. - checkChannelBalance(alice, chanAmt-calcStaticFee(cType, 0), 0, 0, 0) + checkChannelBalance( + alice, chanAmt-lntest.CalcStaticFee(cType, 0), 0, 0, 0, + ) // Check carol's channel balance, which should have zero local and zero // pending balance. - checkChannelBalance(carol, 0, chanAmt-calcStaticFee(cType, 0), 0, 0) + checkChannelBalance( + carol, 0, chanAmt-lntest.CalcStaticFee(cType, 0), 0, 0, + ) // Channel should be ready for payments. const ( @@ -195,7 +199,8 @@ func testChannelUnsettledBalance(ht *lntemp.HarnessTest) { // Check alice's channel balance, which should have a remote unsettled // balance that equals to the amount of invoices * payAmt. The remote // balance remains zero. - aliceLocal := chanAmt - calcStaticFee(cType, 0) - numInvoices*payAmt + fee := lntest.CalcStaticFee(cType, 0) + aliceLocal := chanAmt - fee - numInvoices*payAmt checkChannelBalance(alice, aliceLocal, 0, 0, numInvoices*payAmt) // Check carol's channel balance, which should have a local unsettled diff --git a/lntest/itest/lnd_channel_force_close_test.go b/itest/lnd_channel_force_close_test.go similarity index 92% rename from lntest/itest/lnd_channel_force_close_test.go rename to itest/lnd_channel_force_close_test.go index 0857751d4..a1c36e8d6 100644 --- a/lntest/itest/lnd_channel_force_close_test.go +++ b/itest/lnd_channel_force_close_test.go @@ -13,8 +13,8 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet/chainfee" @@ -30,7 +30,7 @@ import ( // // Note that whether the deadline is used or not is implicitly checked by its // corresponding fee rates. -func testCommitmentTransactionDeadline(ht *lntemp.HarnessTest) { +func testCommitmentTransactionDeadline(ht *lntest.HarnessTest) { // Get the default max fee rate used in sweeping the commitment // transaction. defaultMax := lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte @@ -72,7 +72,7 @@ func testCommitmentTransactionDeadline(ht *lntemp.HarnessTest) { setupNode := func(name string) *node.HarnessNode { // Create the node. args := []string{"--hodl.exit-settle"} - args = append(args, nodeArgsForCommitType( + args = append(args, lntest.NodeArgsForCommitType( lnrpc.CommitmentType_ANCHORS)..., ) node := ht.NewNode(name, args) @@ -104,7 +104,7 @@ func testCommitmentTransactionDeadline(ht *lntemp.HarnessTest) { // Open a channel between Alice and Bob. chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: 10e6, PushAmt: 5e6, }, @@ -194,7 +194,7 @@ func testCommitmentTransactionDeadline(ht *lntemp.HarnessTest) { // process. // // TODO(roasbeef): also add an unsettled HTLC before force closing. -func testChannelForceClosure(ht *lntemp.HarnessTest) { +func testChannelForceClosure(ht *lntest.HarnessTest) { // We'll test the scenario for some of the commitment types, to ensure // outputs can be swept. commitTypes := []lnrpc.CommitmentType{ @@ -209,7 +209,7 @@ func testChannelForceClosure(ht *lntemp.HarnessTest) { success := ht.Run(testName, func(t *testing.T) { st := ht.Subtest(t) - args := nodeArgsForCommitType(channelType) + args := lntest.NodeArgsForCommitType(channelType) alice := st.NewNode("Alice", args) defer st.Shutdown(alice) @@ -237,7 +237,7 @@ func testChannelForceClosure(ht *lntemp.HarnessTest) { } } -func channelForceClosureTest(ht *lntemp.HarnessTest, +func channelForceClosureTest(ht *lntest.HarnessTest, alice, carol *node.HarnessNode, channelType lnrpc.CommitmentType) { const ( @@ -268,7 +268,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, carolStartingBalance := carolBalResp.ConfirmedBalance chanPoint := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -600,6 +600,20 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, // Update current height _, curHeight = ht.Miner.GetBestBlock() + // checkForceClosedChannelNumHtlcs verifies that a force closed channel + // has the proper number of htlcs. + checkPendingChannelNumHtlcs := func( + forceClose lntest.PendingForceClose) error { + + if len(forceClose.PendingHtlcs) != numInvoices { + return fmt.Errorf("expected force closed channel to "+ + "have %d pending htlcs, found %d instead", + numInvoices, len(forceClose.PendingHtlcs)) + } + + return nil + } + err = wait.NoError(func() error { // Now that the commit output has been fully swept, check to // see that the channel remains open for the pending htlc @@ -611,7 +625,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, // The commitment funds will have been recovered after the // commit txn was included in the last block. The htlc funds // will be shown in limbo. - err := checkPendingChannelNumHtlcs(forceClose, numInvoices) + err := checkPendingChannelNumHtlcs(forceClose) if err != nil { return err } @@ -659,7 +673,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, // We should now be at the block just before the utxo nursery // will attempt to broadcast the htlc timeout transactions. - err = checkPendingChannelNumHtlcs(forceClose, numInvoices) + err = checkPendingChannelNumHtlcs(forceClose) if err != nil { return err } @@ -702,9 +716,9 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, // Retrieve each htlc timeout txn from the mempool, and ensure it is // well-formed. This entails verifying that each only spends from - // output, and that that output is from the commitment txn. In case - // this is an anchor channel, the transactions are aggregated by the - // sweeper into one. + // output, and that output is from the commitment txn. In case this is + // an anchor channel, the transactions are aggregated by the sweeper + // into one. numInputs := 1 if channelType == lnrpc.CommitmentType_ANCHORS { numInputs = numInvoices + 1 @@ -796,7 +810,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, // We record the htlc amount less fees here, so that we know // what value to expect for the second stage of our htlc - // htlc resolution. + // resolution. htlcLessFees = uint64(outputs[0].Value) } @@ -838,7 +852,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, return fmt.Errorf("htlc funds should still be in limbo") } - return checkPendingChannelNumHtlcs(forceClose, numInvoices) + return checkPendingChannelNumHtlcs(forceClose) }, defaultTimeout) require.NoError(ht, err, "timeout while checking force closed channel") @@ -913,7 +927,7 @@ func channelForceClosureTest(ht *lntemp.HarnessTest, forceClose := ht.AssertChannelPendingForceClose( alice, chanPoint, ) - err := checkPendingChannelNumHtlcs(forceClose, numInvoices) + err := checkPendingChannelNumHtlcs(forceClose) if err != nil { return err } @@ -986,7 +1000,7 @@ func padCLTV(cltv uint32) uint32 { // testFailingChannel tests that we will fail the channel by force closing it // in the case where a counterparty tries to settle an HTLC with the wrong // preimage. -func testFailingChannel(ht *lntemp.HarnessTest) { +func testFailingChannel(ht *lntest.HarnessTest) { const paymentAmt = 10000 chanAmt := lnd.MaxFundingAmount @@ -999,7 +1013,7 @@ func testFailingChannel(ht *lntemp.HarnessTest) { ht.ConnectNodes(alice, carol) // Let Alice connect and open a channel to Carol, - ht.OpenChannel(alice, carol, lntemp.OpenChannelParams{Amt: chanAmt}) + ht.OpenChannel(alice, carol, lntest.OpenChannelParams{Amt: chanAmt}) // With the channel open, we'll create a invoice for Carol that Alice // will attempt to pay. @@ -1058,7 +1072,7 @@ func testFailingChannel(ht *lntemp.HarnessTest) { // type matches a set of expected resolutions. // // NOTE: only used in current test file. -func assertReports(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func assertReports(ht *lntest.HarnessTest, hn *node.HarnessNode, chanPoint *lnrpc.ChannelPoint, expected map[string]*lnrpc.Resolution) { op := ht.OutPointFromChannelPoint(chanPoint) @@ -1087,3 +1101,54 @@ func assertReports(ht *lntemp.HarnessTest, hn *node.HarnessNode, require.Equal(ht, expected, res) } } + +// checkCommitmentMaturity checks that both the maturity height and blocks +// maturity height are as expected. +// +// NOTE: only used in current test file. +func checkCommitmentMaturity(forceClose lntest.PendingForceClose, + maturityHeight uint32, blocksTilMaturity int32) error { + + if forceClose.MaturityHeight != maturityHeight { + return fmt.Errorf("expected commitment maturity height to be "+ + "%d, found %d instead", maturityHeight, + forceClose.MaturityHeight) + } + if forceClose.BlocksTilMaturity != blocksTilMaturity { + return fmt.Errorf("expected commitment blocks til maturity to "+ + "be %d, found %d instead", blocksTilMaturity, + forceClose.BlocksTilMaturity) + } + + return nil +} + +// checkPendingHtlcStageAndMaturity uniformly tests all pending htlc's belonging +// to a force closed channel, testing for the expected stage number, blocks till +// maturity, and the maturity height. +// +// NOTE: only used in current test file. +func checkPendingHtlcStageAndMaturity( + forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, + stage, maturityHeight uint32, blocksTillMaturity int32) error { + + for _, pendingHtlc := range forceClose.PendingHtlcs { + if pendingHtlc.Stage != stage { + return fmt.Errorf("expected pending htlc to be stage "+ + "%d, found %d", stage, pendingHtlc.Stage) + } + if pendingHtlc.MaturityHeight != maturityHeight { + return fmt.Errorf("expected pending htlc maturity "+ + "height to be %d, instead has %d", + maturityHeight, pendingHtlc.MaturityHeight) + } + if pendingHtlc.BlocksTilMaturity != blocksTillMaturity { + return fmt.Errorf("expected pending htlc blocks til "+ + "maturity to be %d, instead has %d", + blocksTillMaturity, + pendingHtlc.BlocksTilMaturity) + } + } + + return nil +} diff --git a/lntest/itest/lnd_channel_graph_test.go b/itest/lnd_channel_graph_test.go similarity index 93% rename from lntest/itest/lnd_channel_graph_test.go rename to itest/lnd_channel_graph_test.go index e9e7ae648..ed4dfa25a 100644 --- a/lntest/itest/lnd_channel_graph_test.go +++ b/itest/lnd_channel_graph_test.go @@ -11,8 +11,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/peersrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -40,7 +40,7 @@ import ( // take more than 5 seconds to finish, the channel will be marked as disabled, // thus a following operation will fail if it relies on the channel being // enabled. -func testUpdateChanStatus(ht *lntemp.HarnessTest) { +func testUpdateChanStatus(ht *lntest.HarnessTest) { // Create two fresh nodes and open a channel between them. alice, bob := ht.Alice, ht.Bob args := []string{ @@ -57,7 +57,7 @@ func testUpdateChanStatus(ht *lntemp.HarnessTest) { // being the sole funder of the channel. chanAmt := btcutil.Amount(100000) chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) defer ht.CloseChannel(alice, chanPoint) @@ -141,7 +141,7 @@ func testUpdateChanStatus(ht *lntemp.HarnessTest) { FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate), TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta, MinHtlc: 1000, // default value - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), } // Manually disable the channel and ensure that a "Disabled = true" @@ -217,14 +217,14 @@ func testUpdateChanStatus(ht *lntemp.HarnessTest) { // testUnannouncedChannels checks unannounced channels are not returned by // describeGraph RPC request unless explicitly asked for. -func testUnannouncedChannels(ht *lntemp.HarnessTest) { +func testUnannouncedChannels(ht *lntest.HarnessTest) { amount := funding.MaxBtcFundingAmount alice, bob := ht.Alice, ht.Bob // Open a channel between Alice and Bob, ensuring the // channel has been opened properly. chanOpenUpdate := ht.OpenChannelAssertStream( - alice, bob, lntemp.OpenChannelParams{Amt: amount}, + alice, bob, lntest.OpenChannelParams{Amt: amount}, ) // Mine 2 blocks, and check that the channel is opened but not yet @@ -252,7 +252,7 @@ func testUnannouncedChannels(ht *lntemp.HarnessTest) { ht.CloseChannel(alice, fundingChanPoint) } -func testGraphTopologyNotifications(ht *lntemp.HarnessTest) { +func testGraphTopologyNotifications(ht *lntest.HarnessTest) { ht.Run("pinned", func(t *testing.T) { subT := ht.Subtest(t) testGraphTopologyNtfns(subT, true) @@ -263,7 +263,7 @@ func testGraphTopologyNotifications(ht *lntemp.HarnessTest) { }) } -func testGraphTopologyNtfns(ht *lntemp.HarnessTest, pinned bool) { +func testGraphTopologyNtfns(ht *lntest.HarnessTest, pinned bool) { const chanAmt = funding.MaxBtcFundingAmount // Spin up Bob first, since we will need to grab his pubkey when @@ -305,7 +305,7 @@ func testGraphTopologyNtfns(ht *lntemp.HarnessTest, pinned bool) { // Open a new channel between Alice and Bob. chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // The channel opening above should have triggered a few notifications @@ -335,17 +335,30 @@ func testGraphTopologyNtfns(ht *lntemp.HarnessTest, pinned bool) { // For the final portion of the test, we'll ensure that once a new node // appears in the network, the proper notification is dispatched. Note - // that a node that does not have any channels open is ignored, so first - // we disconnect Alice and Bob, open a channel between Bob and Carol, - // and finally connect Alice to Bob again. - ht.DisconnectNodes(alice, bob) + // that a node that does not have any channels open is ignored, so + // first we disconnect Alice and Bob, open a channel between Bob and + // Carol, and finally connect Alice to Bob again. + ht.DisconnectNodes(bob, alice) + + // Since Alice and Bob has a permanent connection, the above + // disconnection won't be enough as Alice will try to reconnect to Bob + // again. Atm, it seems nothing is stopping the reconnection. So we + // need to shutdown Alice here. + // + // TODO(yy): clearly define what `disconnectpeer` rpc is responsible + // for and its effect. If we disconnect a peer, we shouldn't allow the + // peer to connect to us again. + restartAlice := ht.SuspendNode(alice) carol := ht.NewNode("Carol", nil) ht.ConnectNodes(bob, carol) chanPoint = ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + bob, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) + // Restart Alice so she can receive the channel updates from Bob. + require.NoError(ht, restartAlice(), "failed to restart Alice") + // Reconnect Alice and Bob. This should result in the nodes syncing up // their respective graph state, with the new addition being the // existence of Carol in the graph, and also the channel between Bob @@ -366,7 +379,7 @@ func testGraphTopologyNtfns(ht *lntemp.HarnessTest, pinned bool) { // testNodeAnnouncement ensures that when a node is started with one or more // external IP addresses specified on the command line, that those addresses // announced to the network and reported in the network graph. -func testNodeAnnouncement(ht *lntemp.HarnessTest) { +func testNodeAnnouncement(ht *lntest.HarnessTest) { alice, bob := ht.Alice, ht.Bob advertisedAddrs := []string{ @@ -392,7 +405,7 @@ func testNodeAnnouncement(ht *lntemp.HarnessTest) { // ensures that Alice receives the node announcement from Bob as part of // the announcement broadcast. chanPoint := ht.OpenChannel( - bob, dave, lntemp.OpenChannelParams{Amt: 1000000}, + bob, dave, lntest.OpenChannelParams{Amt: 1000000}, ) assertAddrs := func(addrsFound []string, targetAddrs ...string) { @@ -421,7 +434,7 @@ func testNodeAnnouncement(ht *lntemp.HarnessTest) { // testUpdateNodeAnnouncement ensures that the RPC endpoint validates // the requests correctly and that the new node announcement is brodcasted // with the right information after updating our node. -func testUpdateNodeAnnouncement(ht *lntemp.HarnessTest) { +func testUpdateNodeAnnouncement(ht *lntest.HarnessTest) { alice, bob := ht.Alice, ht.Bob var lndArgs []string @@ -500,7 +513,7 @@ func testUpdateNodeAnnouncement(ht *lntemp.HarnessTest) { // ensures that Alice receives the node announcement from Bob as part of // the announcement broadcast. chanPoint := ht.OpenChannel( - bob, dave, lntemp.OpenChannelParams{ + bob, dave, lntest.OpenChannelParams{ Amt: 1000000, }, ) @@ -637,7 +650,7 @@ func testUpdateNodeAnnouncement(ht *lntemp.HarnessTest) { // assertSyncType asserts that the peer has an expected syncType. // // NOTE: only made for tests in this file. -func assertSyncType(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func assertSyncType(ht *lntest.HarnessTest, hn *node.HarnessNode, peer string, syncType lnrpc.Peer_SyncType) { resp := hn.RPC.ListPeers() @@ -693,7 +706,7 @@ func compareNodeAnns(n1, n2 *lnrpc.NodeUpdate) error { // the response expected values. // // NOTE: only used for tests in this file. -func assertUpdateNodeAnnouncementResponse(ht *lntemp.HarnessTest, +func assertUpdateNodeAnnouncementResponse(ht *lntest.HarnessTest, response *peersrpc.NodeAnnouncementUpdateResponse, expectedOps map[string]int) { diff --git a/lntest/itest/lnd_channel_policy_test.go b/itest/lnd_channel_policy_test.go similarity index 94% rename from lntest/itest/lnd_channel_policy_test.go rename to itest/lnd_channel_policy_test.go index faac28c39..85527a082 100644 --- a/lntest/itest/lnd_channel_policy_test.go +++ b/itest/lnd_channel_policy_test.go @@ -10,37 +10,22 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" ) -// assertPolicyUpdate checks that a given policy update has been received by a -// list of given nodes. -// TODO(yy): delete. -func assertPolicyUpdate(t *harnessTest, nodes []*lntest.HarnessNode, - advertisingNode string, policy *lnrpc.RoutingPolicy, - chanPoint *lnrpc.ChannelPoint) { - - for _, node := range nodes { - assertChannelPolicyUpdate( - t.t, node, advertisingNode, policy, chanPoint, false, - ) - } -} - // testUpdateChannelPolicy tests that policy updates made to a channel // gets propagated to other nodes in the network. -func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { +func testUpdateChannelPolicy(ht *lntest.HarnessTest) { const ( defaultFeeBase = 1000 defaultFeeRate = 1 defaultTimeLockDelta = chainreg.DefaultBitcoinTimeLockDelta defaultMinHtlc = 1000 ) - defaultMaxHtlc := calculateMaxHtlc(funding.MaxBtcFundingAmount) + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) chanAmt := funding.MaxBtcFundingAmount pushAmt := chanAmt / 2 @@ -49,7 +34,7 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { // Create a channel Alice->Bob. chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -102,7 +87,7 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { // part of his ChannelUpdate. const customMinHtlc = 5000 chanPoint2 := ht.OpenChannel( - carol, bob, lntemp.OpenChannelParams{ + carol, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, MinHtlc: customMinHtlc, @@ -182,9 +167,9 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { payAmt = btcutil.Amount(4) amtSat := int64(payAmt) amtMSat := int64(lnwire.NewMSatFromSatoshis(payAmt)) - routes.Routes[0].Hops[0].AmtToForward = amtSat // nolint:staticcheck + routes.Routes[0].Hops[0].AmtToForward = amtSat routes.Routes[0].Hops[0].AmtToForwardMsat = amtMSat - routes.Routes[0].Hops[1].AmtToForward = amtSat // nolint:staticcheck + routes.Routes[0].Hops[1].AmtToForward = amtSat routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat // Send the payment with the modified value. @@ -215,9 +200,9 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { payAmt = btcutil.Amount(5) amtSat = int64(payAmt) amtMSat = int64(lnwire.NewMSatFromSatoshis(payAmt)) - routes.Routes[0].Hops[0].AmtToForward = amtSat // nolint:staticcheck + routes.Routes[0].Hops[0].AmtToForward = amtSat routes.Routes[0].Hops[0].AmtToForwardMsat = amtMSat - routes.Routes[0].Hops[1].AmtToForward = amtSat // nolint:staticcheck + routes.Routes[0].Hops[1].AmtToForward = amtSat routes.Routes[0].Hops[1].AmtToForwardMsat = amtMSat // Manually set the MPP payload a new for each payment since @@ -298,7 +283,7 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { // We'll now open a channel from Alice directly to Carol. ht.ConnectNodes(alice, carol) chanPoint3 := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -439,7 +424,7 @@ func testUpdateChannelPolicy(ht *lntemp.HarnessTest) { // chan-disable-timeout flags here. For instance, if some operations take more // than 6 seconds to finish, the channel will be marked as disabled, thus a // following operation will fail if it relies on the channel being enabled. -func testSendUpdateDisableChannel(ht *lntemp.HarnessTest) { +func testSendUpdateDisableChannel(ht *lntest.HarnessTest) { const chanAmt = 100000 alice, bob := ht.Alice, ht.Bob @@ -482,8 +467,8 @@ func testSendUpdateDisableChannel(ht *lntemp.HarnessTest) { // We now proceed to open channels: Alice=>Bob, Alice=>Carol and // Eve=>Carol. - p := lntemp.OpenChannelParams{Amt: chanAmt} - reqs := []*lntemp.OpenChannelRequest{ + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ {Local: alice, Remote: bob, Param: p}, {Local: alice, Remote: carol, Param: p}, {Local: eve, Remote: carol, Param: p}, @@ -528,7 +513,7 @@ func testSendUpdateDisableChannel(ht *lntemp.HarnessTest) { FeeRateMilliMsat: int64(chainreg.DefaultBitcoinFeeRate), TimeLockDelta: chainreg.DefaultBitcoinTimeLockDelta, MinHtlc: 1000, // default value - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), Disabled: true, } @@ -670,7 +655,7 @@ func testSendUpdateDisableChannel(ht *lntemp.HarnessTest) { // Bob will update the base fee via UpdateChannelPolicy, we will test that // Alice will not fail the payment and send it using the updated channel // policy. -func testUpdateChannelPolicyForPrivateChannel(ht *lntemp.HarnessTest) { +func testUpdateChannelPolicyForPrivateChannel(ht *lntest.HarnessTest) { const ( chanAmt = btcutil.Amount(100000) paymentAmt = 20000 @@ -683,7 +668,7 @@ func testUpdateChannelPolicyForPrivateChannel(ht *lntemp.HarnessTest) { // Open a channel with 100k satoshis between Alice and Bob. chanPointAliceBob := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -696,7 +681,7 @@ func testUpdateChannelPolicyForPrivateChannel(ht *lntemp.HarnessTest) { // Open a channel with 100k satoshis between Bob and Carol. chanPointBobCarol := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{ + bob, carol, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -729,6 +714,19 @@ func testUpdateChannelPolicyForPrivateChannel(ht *lntemp.HarnessTest) { // Alice pays the invoices. She will use the updated baseFeeMSat in the // payment + // + // TODO(yy): we may get a flake saying the timeout checking the + // payment's state, which is due to slow round of HTLC settlement. An + // example log is shown below, where Alice sent RevokeAndAck to Bob, + // but it took Bob 7 seconds to reply back the final UpdateFulfillHTLC. + // + // 2022-11-14 06:23:59.774 PEER: Peer(Bob): Sending UpdateAddHTLC + // 2022-11-14 06:24:00.635 PEER: Peer(Bob): Sending CommitSig + // 2022-11-14 06:24:01.784 PEER: Peer(Bob): Sending RevokeAndAck + // 2022-11-14 06:24:08.464 PEER: Peer(Bob): Received UpdateFulfillHTLC + // + // 7 seconds is too long for a local test and this needs more + // investigation. payReqs := []string{resp.PaymentRequest} ht.CompletePaymentRequests(alice, payReqs) @@ -770,14 +768,14 @@ func testUpdateChannelPolicyForPrivateChannel(ht *lntemp.HarnessTest) { // testUpdateChannelPolicyFeeRateAccuracy tests that updating the channel policy // rounds fee rate values correctly as well as setting fee rate with ppm works // as expected. -func testUpdateChannelPolicyFeeRateAccuracy(ht *lntemp.HarnessTest) { +func testUpdateChannelPolicyFeeRateAccuracy(ht *lntest.HarnessTest) { chanAmt := funding.MaxBtcFundingAmount pushAmt := chanAmt / 2 // Create a channel Alice -> Bob. alice, bob := ht.Alice, ht.Bob chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -841,7 +839,7 @@ func testUpdateChannelPolicyFeeRateAccuracy(ht *lntemp.HarnessTest) { // assertNodesPolicyUpdate checks that a given policy update has been received // by a list of given nodes. -func assertNodesPolicyUpdate(ht *lntemp.HarnessTest, nodes []*node.HarnessNode, +func assertNodesPolicyUpdate(ht *lntest.HarnessTest, nodes []*node.HarnessNode, advertisingNode *node.HarnessNode, policy *lnrpc.RoutingPolicy, chanPoint *lnrpc.ChannelPoint) { diff --git a/lntest/itest/lnd_custom_message.go b/itest/lnd_custom_message.go similarity index 96% rename from lntest/itest/lnd_custom_message.go rename to itest/lnd_custom_message.go index 5faf425a1..14a4e7949 100644 --- a/lntest/itest/lnd_custom_message.go +++ b/itest/lnd_custom_message.go @@ -5,7 +5,7 @@ import ( "time" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" ) @@ -13,7 +13,7 @@ import ( // testCustomMessage tests sending and receiving of overridden custom message // types (within the message type range usually reserved for protocol messages) // via the send and subscribe custom message APIs. -func testCustomMessage(ht *lntemp.HarnessTest) { +func testCustomMessage(ht *lntest.HarnessTest) { alice, bob := ht.Alice, ht.Bob var ( @@ -82,7 +82,7 @@ func testCustomMessage(ht *lntemp.HarnessTest) { require.Equal(ht, bob.PubKey[:], msg.Peer, "first msg "+ "peer wrong") - case <-time.After(defaultTimeout): + case <-time.After(lntest.DefaultTimeout): ht.Fatalf("alice did not receive first custom message: %v", msgType) } @@ -151,7 +151,7 @@ func testCustomMessage(ht *lntemp.HarnessTest) { require.Equal(ht, bob.PubKey[:], msg.Peer, "second "+ "message peer") - case <-time.After(defaultTimeout): + case <-time.After(lntest.DefaultTimeout): ht.Fatalf("alice did not receive second custom message") } } diff --git a/lntest/itest/lnd_etcd_failover_test.go b/itest/lnd_etcd_failover_test.go similarity index 90% rename from lntest/itest/lnd_etcd_failover_test.go rename to itest/lnd_etcd_failover_test.go index 90bd5fbbe..81021572e 100644 --- a/lntest/itest/lnd_etcd_failover_test.go +++ b/itest/lnd_etcd_failover_test.go @@ -12,12 +12,12 @@ import ( "github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) -func assertLeader(ht *lntemp.HarnessTest, observer cluster.LeaderElector, +func assertLeader(ht *lntest.HarnessTest, observer cluster.LeaderElector, expected string) { leader, err := observer.Leader(ht.Context()) @@ -29,7 +29,7 @@ func assertLeader(ht *lntemp.HarnessTest, observer cluster.LeaderElector, // testEtcdFailover tests that in a cluster setup where two LND nodes form a // single cluster (sharing the same identity) one can hand over the leader role // to the other (failing over after graceful shutdown or forceful abort). -func testEtcdFailover(ht *lntemp.HarnessTest) { +func testEtcdFailover(ht *lntest.HarnessTest) { testCases := []struct { name string kill bool @@ -54,10 +54,10 @@ func testEtcdFailover(ht *lntemp.HarnessTest) { } } -func testEtcdFailoverCase(ht *lntemp.HarnessTest, kill bool) { +func testEtcdFailoverCase(ht *lntest.HarnessTest, kill bool) { etcdCfg, cleanup, err := kvdb.StartEtcdTestBackend( - ht.T.TempDir(), uint16(lntest.NextAvailablePort()), - uint16(lntest.NextAvailablePort()), "", + ht.T.TempDir(), uint16(node.NextAvailablePort()), + uint16(node.NextAvailablePort()), "", ) require.NoError(ht, err, "Failed to start etcd instance") defer cleanup() @@ -91,7 +91,7 @@ func testEtcdFailoverCase(ht *lntemp.HarnessTest, kill bool) { // Open a channel with 100k satoshis between Carol and Alice with Alice // being the sole funder of the channel. chanAmt := btcutil.Amount(100_000) - ht.OpenChannel(alice, carol1, lntemp.OpenChannelParams{Amt: chanAmt}) + ht.OpenChannel(alice, carol1, lntest.OpenChannelParams{Amt: chanAmt}) // At this point Carol-1 is the elected leader, while Carol-2 will wait // to become the leader when Carol-1 stops. diff --git a/lntest/itest/lnd_forward_interceptor_test.go b/itest/lnd_forward_interceptor_test.go similarity index 95% rename from lntest/itest/lnd_forward_interceptor_test.go rename to itest/lnd_forward_interceptor_test.go index 4f667552b..b5d08f5bf 100644 --- a/lntest/itest/lnd_forward_interceptor_test.go +++ b/itest/lnd_forward_interceptor_test.go @@ -9,8 +9,8 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" @@ -34,7 +34,7 @@ type interceptorTestCase struct { // testForwardInterceptorDedupHtlc tests that upon reconnection, duplicate // HTLCs aren't re-notified using the HTLC interceptor API. -func testForwardInterceptorDedupHtlc(ht *lntemp.HarnessTest) { +func testForwardInterceptorDedupHtlc(ht *lntest.HarnessTest) { // Initialize the test context with 3 connected nodes. ts := newInterceptorTestScenario(ht) @@ -42,8 +42,8 @@ func testForwardInterceptorDedupHtlc(ht *lntemp.HarnessTest) { // Open and wait for channels. const chanAmt = btcutil.Amount(300000) - p := lntemp.OpenChannelParams{Amt: chanAmt} - reqs := []*lntemp.OpenChannelRequest{ + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ {Local: alice, Remote: bob, Param: p}, {Local: bob, Remote: carol, Param: p}, } @@ -106,6 +106,7 @@ func testForwardInterceptorDedupHtlc(ht *lntemp.HarnessTest) { ht.EnsureConnected(bob, alice) // Here we wait for the channel to be active again. + ht.AssertChannelExists(alice, cpAB) ht.AssertChannelExists(bob, cpAB) // Now that the channel is active we make sure the test passes as @@ -185,15 +186,15 @@ func testForwardInterceptorDedupHtlc(ht *lntemp.HarnessTest) { // 3. Intercepted held htlcs result in no payment (invoice is not settled). // 4. When Interceptor disconnects it resumes all held htlcs, which result in // valid payment (invoice is settled). -func testForwardInterceptorBasic(ht *lntemp.HarnessTest) { +func testForwardInterceptorBasic(ht *lntest.HarnessTest) { ts := newInterceptorTestScenario(ht) alice, bob, carol := ts.alice, ts.bob, ts.carol // Open and wait for channels. const chanAmt = btcutil.Amount(300000) - p := lntemp.OpenChannelParams{Amt: chanAmt} - reqs := []*lntemp.OpenChannelRequest{ + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ {Local: alice, Remote: bob, Param: p}, {Local: bob, Remote: carol, Param: p}, } @@ -296,7 +297,8 @@ func testForwardInterceptorBasic(ht *lntemp.HarnessTest) { restartAlice := ht.SuspendNode(alice) require.NoError(ht, restartAlice(), "failed to restart alice") - // Make sure the channel is active from Bob's PoV. + // Make sure the channel is active from both Alice and Bob's PoV. + ht.AssertChannelExists(alice, cpAB) ht.AssertChannelExists(bob, cpAB) // Create a new interceptor as the old one has quit. @@ -345,7 +347,7 @@ func testForwardInterceptorBasic(ht *lntemp.HarnessTest) { // interceptorTestScenario is a helper struct to hold the test context and // provide the needed functionality. type interceptorTestScenario struct { - ht *lntemp.HarnessTest + ht *lntest.HarnessTest alice, bob, carol *node.HarnessNode } @@ -356,7 +358,7 @@ type interceptorTestScenario struct { // // Among them, Alice and Bob are standby nodes and Carol is a new node. func newInterceptorTestScenario( - ht *lntemp.HarnessTest) *interceptorTestScenario { + ht *lntest.HarnessTest) *interceptorTestScenario { alice, bob := ht.Alice, ht.Bob carol := ht.NewNode("carol", nil) @@ -416,6 +418,7 @@ func (c *interceptorTestScenario) prepareTestCases() []*interceptorTestCase { t.invoice = invoice t.payAddr = payReq.PaymentAddr } + return cases } diff --git a/lntest/itest/lnd_funding_test.go b/itest/lnd_funding_test.go similarity index 94% rename from lntest/itest/lnd_funding_test.go rename to itest/lnd_funding_test.go index d9e66d792..82ac7f153 100644 --- a/lntest/itest/lnd_funding_test.go +++ b/itest/lnd_funding_test.go @@ -13,8 +13,8 @@ import ( "github.com/lightningnetwork/lnd/labels" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" ) @@ -24,7 +24,7 @@ import ( // Bob, then immediately closes the channel after asserting some expected post // conditions. Finally, the chain itself is checked to ensure the closing // transaction was mined. -func testBasicChannelFunding(ht *lntemp.HarnessTest) { +func testBasicChannelFunding(ht *lntest.HarnessTest) { // Run through the test with combinations of all the different // commitment types. allTypes := []lnrpc.CommitmentType{ @@ -35,20 +35,20 @@ func testBasicChannelFunding(ht *lntemp.HarnessTest) { // testFunding is a function closure that takes Carol and Dave's // commitment types and test the funding flow. - testFunding := func(ht *lntemp.HarnessTest, carolCommitType, + testFunding := func(ht *lntest.HarnessTest, carolCommitType, daveCommitType lnrpc.CommitmentType) { // Based on the current tweak variable for Carol, we'll // preferentially signal the legacy commitment format. We do // the same for Dave shortly below. - carolArgs := nodeArgsForCommitType(carolCommitType) + carolArgs := lntest.NodeArgsForCommitType(carolCommitType) carol := ht.NewNode("Carol", carolArgs) // Each time, we'll send Carol a new set of coins in order to // fund the channel. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) - daveArgs := nodeArgsForCommitType(daveCommitType) + daveArgs := lntest.NodeArgsForCommitType(daveCommitType) dave := ht.NewNode("Dave", daveArgs) // Before we start the test, we'll ensure both sides are @@ -140,7 +140,7 @@ test: // test. Given two nodes: Alice and Bob, it'll assert proper channel creation, // then return a function closure that should be called to assert proper // channel closure. -func basicChannelFundingTest(ht *lntemp.HarnessTest, +func basicChannelFundingTest(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, fundingShim *lnrpc.FundingShim) (*lnrpc.Channel, *lnrpc.Channel, func()) { @@ -181,7 +181,7 @@ func basicChannelFundingTest(ht *lntemp.HarnessTest, // assertions will be executed to ensure the funding process completed // successfully. chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, FundingShim: fundingShim, @@ -193,7 +193,7 @@ func basicChannelFundingTest(ht *lntemp.HarnessTest, // With the channel open, ensure that the amount specified above has // properly been pushed to Bob. - aliceLocalBalance := chanAmt - pushAmt - calcStaticFee(cType, 0) + aliceLocalBalance := chanAmt - pushAmt - lntest.CalcStaticFee(cType, 0) checkChannelBalance( alice, aliceChannelBalance, aliceLocalBalance, pushAmt, ) @@ -216,7 +216,7 @@ func basicChannelFundingTest(ht *lntemp.HarnessTest, // testUnconfirmedChannelFunding tests that our unconfirmed change outputs can // be used to fund channels. -func testUnconfirmedChannelFunding(ht *lntemp.HarnessTest) { +func testUnconfirmedChannelFunding(ht *lntest.HarnessTest) { const ( chanAmt = funding.MaxBtcFundingAmount pushAmt = btcutil.Amount(100000) @@ -236,7 +236,7 @@ func testUnconfirmedChannelFunding(ht *lntemp.HarnessTest) { ht.ConnectNodes(carol, alice) chanOpenUpdate := ht.OpenChannelAssertStream( - carol, alice, lntemp.OpenChannelParams{ + carol, alice, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, SpendUnconfirmed: true, @@ -289,7 +289,7 @@ func testUnconfirmedChannelFunding(ht *lntemp.HarnessTest) { // Note that atm we haven't obtained the chanPoint yet, so we use the // type directly. cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY - carolLocalBalance := chanAmt - pushAmt - calcStaticFee(cType, 0) + carolLocalBalance := chanAmt - pushAmt - lntest.CalcStaticFee(cType, 0) checkChannelBalance(carol, 0, 0, carolLocalBalance, pushAmt) // For Alice, her local/remote balances should be zero, and the @@ -329,7 +329,12 @@ func testUnconfirmedChannelFunding(ht *lntemp.HarnessTest) { // testChannelFundingInputTypes tests that any type of supported input type can // be used to fund channels. -func testChannelFundingInputTypes(ht *lntemp.HarnessTest) { +func testChannelFundingInputTypes(ht *lntest.HarnessTest) { + const ( + chanAmt = funding.MaxBtcFundingAmount + burnAddr = "bcrt1qxsnqpdc842lu8c0xlllgvejt6rhy49u6fmpgyz" + ) + // We'll start off by creating a node for Carol. carol := ht.NewNode("Carol", nil) @@ -342,7 +347,7 @@ func testChannelFundingInputTypes(ht *lntemp.HarnessTest) { // runChannelFundingInputTypes tests that any type of supported input type can // be used to fund channels. -func runChannelFundingInputTypes(ht *lntemp.HarnessTest, alice, +func runChannelFundingInputTypes(ht *lntest.HarnessTest, alice, carol *node.HarnessNode) { const ( @@ -411,7 +416,7 @@ func runChannelFundingInputTypes(ht *lntemp.HarnessTest, alice, funder((chanAmt*11)/10, carol) chanOpenUpdate := ht.OpenChannelAssertStream( - carol, alice, lntemp.OpenChannelParams{ + carol, alice, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -423,7 +428,7 @@ func runChannelFundingInputTypes(ht *lntemp.HarnessTest, alice, // Note that atm we haven't obtained the chanPoint yet, so we // use the type directly. cType := lnrpc.CommitmentType_STATIC_REMOTE_KEY - carolLocalBalance := chanAmt - calcStaticFee(cType, 0) + carolLocalBalance := chanAmt - lntest.CalcStaticFee(cType, 0) checkChannelBalance(carol, 0, 0, carolLocalBalance, 0) // For Alice, her local/remote balances should be zero, and the @@ -463,7 +468,7 @@ func runChannelFundingInputTypes(ht *lntemp.HarnessTest, alice, // sendAllCoinsConfirm sends all coins of the node's wallet to the given address // and awaits one confirmation. -func sendAllCoinsConfirm(ht *lntemp.HarnessTest, node *node.HarnessNode, +func sendAllCoinsConfirm(ht *lntest.HarnessTest, node *node.HarnessNode, addr string) { sweepReq := &lnrpc.SendCoinsRequest{ @@ -477,7 +482,7 @@ func sendAllCoinsConfirm(ht *lntemp.HarnessTest, node *node.HarnessNode, // testExternalFundingChanPoint tests that we're able to carry out a normal // channel funding workflow given a channel point that was constructed outside // the main daemon. -func testExternalFundingChanPoint(ht *lntemp.HarnessTest) { +func testExternalFundingChanPoint(ht *lntest.HarnessTest) { // First, we'll create two new nodes that we'll use to open channel // between for this test. carol := ht.NewNode("carol", nil) @@ -496,11 +501,11 @@ func testExternalFundingChanPoint(ht *lntemp.HarnessTest) { // a transaction that will never be published. const thawHeight uint32 = 10 const chanSize = funding.MaxBtcFundingAmount - fundingShim1, chanPoint1, _ := deriveFundingShim( + fundingShim1, chanPoint1 := deriveFundingShim( ht, carol, dave, chanSize, thawHeight, false, ) ht.OpenChannelAssertPending( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: fundingShim1, }, @@ -512,7 +517,7 @@ func testExternalFundingChanPoint(ht *lntemp.HarnessTest) { // externally funded, we should still be able to open another one. Let's // do exactly that now. For this one we publish the transaction so we // can mine it later. - fundingShim2, chanPoint2, _ := deriveFundingShim( + fundingShim2, chanPoint2 := deriveFundingShim( ht, carol, dave, chanSize, thawHeight, true, ) @@ -591,7 +596,7 @@ func testExternalFundingChanPoint(ht *lntemp.HarnessTest) { // representation of channels if the system is restarted or disconnected. // testFundingPersistence mirrors testBasicChannelFunding, but adds restarts // and checks for the state of channels with unconfirmed funding transactions. -func testChannelFundingPersistence(ht *lntemp.HarnessTest) { +func testChannelFundingPersistence(ht *lntest.HarnessTest) { chanAmt := funding.MaxBtcFundingAmount pushAmt := btcutil.Amount(0) @@ -609,7 +614,7 @@ func testChannelFundingPersistence(ht *lntemp.HarnessTest) { // Create a new channel that requires 5 confs before it's considered // open, then broadcast the funding transaction - param := lntemp.OpenChannelParams{ + param := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, } @@ -676,7 +681,7 @@ func testChannelFundingPersistence(ht *lntemp.HarnessTest) { // The channel should be listed in the peer information returned by // both peers. - chanPoint := lntemp.ChanPointFromPendingUpdate(update) + chanPoint := lntest.ChanPointFromPendingUpdate(update) // Re-lookup our transaction in the block that it confirmed in. tx = ht.AssertTxAtHeight(alice, height, fundingTxID) @@ -685,6 +690,11 @@ func testChannelFundingPersistence(ht *lntemp.HarnessTest) { chanAlice := ht.AssertChannelExists(alice, chanPoint) ht.AssertChannelExists(carol, chanPoint) + // Make sure Alice and Carol have seen the channel in their network + // topology. + ht.AssertTopologyChannelOpen(alice, chanPoint) + ht.AssertTopologyChannelOpen(carol, chanPoint) + // Create an additional check for our channel assertion that will // check that our label is as expected. shortChanID := lnwire.NewShortChanIDFromInt(chanAlice.ChanId) @@ -699,7 +709,7 @@ func testChannelFundingPersistence(ht *lntemp.HarnessTest) { // testBatchChanFunding makes sure multiple channels can be opened in one batch // transaction in an atomic way. -func testBatchChanFunding(ht *lntemp.HarnessTest) { +func testBatchChanFunding(ht *lntest.HarnessTest) { // First, we'll create two new nodes that we'll use to open channels // to during this test. Carol has a high minimum funding amount that // we'll use to trigger an error during the batch channel open. @@ -793,10 +803,10 @@ func testBatchChanFunding(ht *lntemp.HarnessTest) { // deriveFundingShim creates a channel funding shim by deriving the necessary // keys on both sides. -func deriveFundingShim(ht *lntemp.HarnessTest, +func deriveFundingShim(ht *lntest.HarnessTest, carol, dave *node.HarnessNode, chanSize btcutil.Amount, thawHeight uint32, publish bool) (*lnrpc.FundingShim, - *lnrpc.ChannelPoint, *chainhash.Hash) { + *lnrpc.ChannelPoint) { keyLoc := &signrpc.KeyLocator{KeyFamily: 9999} carolFundingKey := carol.RPC.DeriveKey(keyLoc) @@ -882,5 +892,5 @@ func deriveFundingShim(ht *lntemp.HarnessTest, } fundingShim.GetChanPointShim().RemoteKey = daveFundingKey.RawKeyBytes - return fundingShim, chanPoint, txid + return fundingShim, chanPoint } diff --git a/lntest/itest/lnd_hold_invoice_force_test.go b/itest/lnd_hold_invoice_force_test.go similarity index 97% rename from lntest/itest/lnd_hold_invoice_force_test.go rename to itest/lnd_hold_invoice_force_test.go index 613281305..a3d832614 100644 --- a/lntest/itest/lnd_hold_invoice_force_test.go +++ b/itest/lnd_hold_invoice_force_test.go @@ -7,7 +7,7 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" @@ -15,11 +15,11 @@ import ( // testHoldInvoiceForceClose tests cancellation of accepted hold invoices which // would otherwise trigger force closes when they expire. -func testHoldInvoiceForceClose(ht *lntemp.HarnessTest) { +func testHoldInvoiceForceClose(ht *lntest.HarnessTest) { // Open a channel between alice and bob. alice, bob := ht.Alice, ht.Bob chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: 300000}, + alice, bob, lntest.OpenChannelParams{Amt: 300000}, ) // Create a non-dust hold invoice for bob. diff --git a/lntest/itest/lnd_hold_persistence_test.go b/itest/lnd_hold_persistence_test.go similarity index 96% rename from lntest/itest/lnd_hold_persistence_test.go rename to itest/lnd_hold_persistence_test.go index da5424ca3..c7cd5eac2 100644 --- a/lntest/itest/lnd_hold_persistence_test.go +++ b/itest/lnd_hold_persistence_test.go @@ -7,8 +7,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/rpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" @@ -17,7 +17,7 @@ import ( // testHoldInvoicePersistence tests that a sender to a hold-invoice, can be // restarted before the payment gets settled, and still be able to receive the // preimage. -func testHoldInvoicePersistence(ht *lntemp.HarnessTest) { +func testHoldInvoicePersistence(ht *lntest.HarnessTest) { const ( chanAmt = btcutil.Amount(1000000) numPayments = 10 @@ -34,7 +34,7 @@ func testHoldInvoicePersistence(ht *lntemp.HarnessTest) { // Open a channel between Alice and Carol which is private so that we // cover the addition of hop hints for hold invoices. chanPointAlice := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -45,7 +45,7 @@ func testHoldInvoicePersistence(ht *lntemp.HarnessTest) { // has at least one public channel in the graph. We open a public // channel from Alice -> Bob and wait for Carol to see it. chanPointBob := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, ) diff --git a/lntest/itest/lnd_macaroons_test.go b/itest/lnd_macaroons_test.go similarity index 98% rename from lntest/itest/lnd_macaroons_test.go rename to itest/lnd_macaroons_test.go index 01d576f8f..b5dad77bb 100644 --- a/lntest/itest/lnd_macaroons_test.go +++ b/itest/lnd_macaroons_test.go @@ -11,8 +11,8 @@ import ( "github.com/golang/protobuf/proto" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/macaroons" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,7 +23,7 @@ import ( // enabled on the gRPC interface, no requests with missing or invalid // macaroons are allowed. Further, the specific access rights (read/write, // entity based) and first-party caveats are tested as well. -func testMacaroonAuthentication(ht *lntemp.HarnessTest) { +func testMacaroonAuthentication(ht *lntest.HarnessTest) { var ( infoReq = &lnrpc.GetInfoRequest{} newAddrReq = &lnrpc.NewAddressRequest{ @@ -294,7 +294,7 @@ func testMacaroonAuthentication(ht *lntemp.HarnessTest) { // testBakeMacaroon checks that when creating macaroons, the permissions param // in the request must be set correctly, and the baked macaroon has the intended // permissions. -func testBakeMacaroon(ht *lntemp.HarnessTest) { +func testBakeMacaroon(ht *lntest.HarnessTest) { var testNode = ht.Alice testCases := []struct { @@ -518,7 +518,7 @@ func testBakeMacaroon(ht *lntemp.HarnessTest) { // specified ID and invalidates all macaroons derived from the key with that ID. // Also, it checks deleting the reserved marcaroon ID, DefaultRootKeyID or is // forbidden. -func testDeleteMacaroonID(ht *lntemp.HarnessTest) { +func testDeleteMacaroonID(ht *lntest.HarnessTest) { var ( ctxb = ht.Context() testNode = ht.Alice @@ -610,7 +610,7 @@ func testDeleteMacaroonID(ht *lntemp.HarnessTest) { // does not write any macaroon files to the daemon's file system and returns // the admin macaroon in the response. It then checks that the password // change of the wallet can also happen stateless. -func testStatelessInit(ht *lntemp.HarnessTest) { +func testStatelessInit(ht *lntest.HarnessTest) { var ( initPw = []byte("stateless") newPw = []byte("stateless-new") diff --git a/lntest/itest/lnd_max_channel_size_test.go b/itest/lnd_max_channel_size_test.go similarity index 87% rename from lntest/itest/lnd_max_channel_size_test.go rename to itest/lnd_max_channel_size_test.go index d795fb2e8..9959c7589 100644 --- a/lntest/itest/lnd_max_channel_size_test.go +++ b/itest/lnd_max_channel_size_test.go @@ -1,6 +1,3 @@ -//go:build rpctest -// +build rpctest - package itest import ( @@ -8,14 +5,14 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/funding" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnwallet" ) // testMaxChannelSize tests that lnd handles --maxchansize parameter correctly. // Wumbo nodes should enforce a default soft limit of 10 BTC by default. This // limit can be adjusted with --maxchansize config option. -func testMaxChannelSize(ht *lntemp.HarnessTest) { +func testMaxChannelSize(ht *lntest.HarnessTest) { // We'll make two new nodes, both wumbo but with the default limit on // maximum channel size (10 BTC) wumboNode := ht.NewNode( @@ -42,7 +39,7 @@ func testMaxChannelSize(ht *lntemp.HarnessTest) { ) ht.OpenChannelAssertErr( wumboNode, wumboNode2, - lntemp.OpenChannelParams{Amt: chanAmt}, expectedErr, + lntest.OpenChannelParams{Amt: chanAmt}, expectedErr, ) // We'll now make another wumbo node with appropriate maximum channel @@ -60,7 +57,7 @@ func testMaxChannelSize(ht *lntemp.HarnessTest) { // Creating a wumbo channel between these two nodes should succeed. ht.EnsureConnected(wumboNode, wumboNode3) chanPoint := ht.OpenChannel( - wumboNode, wumboNode3, lntemp.OpenChannelParams{Amt: chanAmt}, + wumboNode, wumboNode3, lntest.OpenChannelParams{Amt: chanAmt}, ) ht.CloseChannel(wumboNode, chanPoint) diff --git a/lntest/itest/lnd_max_htlcs_test.go b/itest/lnd_max_htlcs_test.go similarity index 94% rename from lntest/itest/lnd_max_htlcs_test.go rename to itest/lnd_max_htlcs_test.go index d457e452f..72d6ac031 100644 --- a/lntest/itest/lnd_max_htlcs_test.go +++ b/itest/lnd_max_htlcs_test.go @@ -4,8 +4,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" ) @@ -14,14 +14,14 @@ import ( // channel where we have already reached the limit of the number of htlcs that // we may add to the remote party's commitment. This test asserts that we do // not attempt to use the full channel at all in our pathfinding. -func testMaxHtlcPathfind(ht *lntemp.HarnessTest) { +func testMaxHtlcPathfind(ht *lntest.HarnessTest) { // Setup a channel between Alice and Bob where Alice will only allow // Bob to add a maximum of 5 htlcs to her commitment. maxHtlcs := 5 alice, bob := ht.Alice, ht.Bob chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: 1000000, PushAmt: 800000, RemoteMaxHtlcs: uint16(maxHtlcs), @@ -84,7 +84,7 @@ type holdSubscription struct { // cancel updates a hold invoice to cancel from the recipient and consumes // updates from the payer until it has reached a final, failed state. -func (h *holdSubscription) cancel(ht *lntemp.HarnessTest) { +func (h *holdSubscription) cancel(ht *lntest.HarnessTest) { h.recipient.RPC.CancelInvoice(h.hash[:]) invUpdate := ht.ReceiveSingleInvoice(h.invSubscription) @@ -110,7 +110,7 @@ func (h *holdSubscription) cancel(ht *lntemp.HarnessTest) { // acceptHoldInvoice adds a hold invoice to the recipient node, pays it from // the sender and asserts that we have reached the accepted state where htlcs // are locked in for the payment. -func acceptHoldInvoice(ht *lntemp.HarnessTest, idx int, sender, +func acceptHoldInvoice(ht *lntest.HarnessTest, idx int, sender, receiver *node.HarnessNode) *holdSubscription { hash := [lntypes.HashSize]byte{byte(idx + 1)} diff --git a/lntest/itest/lnd_misc_test.go b/itest/lnd_misc_test.go similarity index 96% rename from lntest/itest/lnd_misc_test.go rename to itest/lnd_misc_test.go index 04274fb35..577462afe 100644 --- a/lntest/itest/lnd_misc_test.go +++ b/itest/lnd_misc_test.go @@ -15,8 +15,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -28,7 +28,7 @@ import ( // disconnect at any point. // // TODO(yy): move to lnd_network_test. -func testDisconnectingTargetPeer(ht *lntemp.HarnessTest) { +func testDisconnectingTargetPeer(ht *lntest.HarnessTest) { // We'll start both nodes with a high backoff so that they don't // reconnect automatically during our test. args := []string{ @@ -49,7 +49,7 @@ func testDisconnectingTargetPeer(ht *lntemp.HarnessTest) { // Create a new channel that requires 1 confs before it's considered // open, then broadcast the funding transaction const numConfs = 1 - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, } @@ -119,7 +119,7 @@ func testDisconnectingTargetPeer(ht *lntemp.HarnessTest) { // configuration arguments to force Carol to replay the same sphinx packet // after reconnecting to Dave, and compare the returned failure message with // what we expect for replayed onion packets. -func testSphinxReplayPersistence(ht *lntemp.HarnessTest) { +func testSphinxReplayPersistence(ht *lntest.HarnessTest) { // Open a channel with 100k satoshis between Carol and Dave with Carol // being the sole funder of the channel. chanAmt := btcutil.Amount(100000) @@ -135,7 +135,7 @@ func testSphinxReplayPersistence(ht *lntemp.HarnessTest) { ht.ConnectNodes(carol, dave) chanPoint := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -150,7 +150,7 @@ func testSphinxReplayPersistence(ht *lntemp.HarnessTest) { ht.ConnectNodes(fred, carol) chanPointFC := ht.OpenChannel( - fred, carol, lntemp.OpenChannelParams{ + fred, carol, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -232,7 +232,7 @@ func testSphinxReplayPersistence(ht *lntemp.HarnessTest) { // tests the values in all ChannelConstraints are returned as expected. Once // ListChannels becomes mature, a test against all fields in ListChannels // should be performed. -func testListChannels(ht *lntemp.HarnessTest) { +func testListChannels(ht *lntest.HarnessTest) { const aliceRemoteMaxHtlcs = 50 const bobRemoteMaxHtlcs = 100 @@ -255,7 +255,7 @@ func testListChannels(ht *lntemp.HarnessTest) { chanAmt := btcutil.Amount(100000) pushAmt := btcutil.Amount(1000) - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, MinHtlc: customizedMinHtlc, @@ -337,7 +337,7 @@ func testListChannels(ht *lntemp.HarnessTest) { // testMaxPendingChannels checks that error is returned from remote peer if // max pending channel number was exceeded and that '--maxpendingchannels' flag // exists and works properly. -func testMaxPendingChannels(ht *lntemp.HarnessTest) { +func testMaxPendingChannels(ht *lntest.HarnessTest) { maxPendingChannels := lncfg.DefaultMaxPendingChannels + 1 amount := funding.MaxBtcFundingAmount @@ -362,7 +362,7 @@ func testMaxPendingChannels(ht *lntemp.HarnessTest) { ) for i := 0; i < maxPendingChannels; i++ { stream := ht.OpenChannelAssertStream( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: amount, }, ) @@ -372,7 +372,7 @@ func testMaxPendingChannels(ht *lntemp.HarnessTest) { // Carol exhausted available amount of pending channels, next open // channel request should cause ErrorGeneric to be sent back to Alice. ht.OpenChannelAssertErr( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: amount, }, lnwire.ErrMaxPendingChannels, ) @@ -415,7 +415,7 @@ func testMaxPendingChannels(ht *lntemp.HarnessTest) { // testGarbageCollectLinkNodes tests that we properly garbage collect link // nodes from the database and the set of persistent connections within the // server. -func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) { +func testGarbageCollectLinkNodes(ht *lntest.HarnessTest) { const chanAmt = 1000000 alice, bob := ht.Alice, ht.Bob @@ -423,7 +423,7 @@ func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) { // Open a channel between Alice and Bob which will later be // cooperatively closed. coopChanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -435,7 +435,7 @@ func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) { // Open a channel between Alice and Carol which will later be force // closed. forceCloseChanPoint := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -447,7 +447,7 @@ func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) { ht.ConnectNodes(alice, dave) persistentChanPoint := ht.OpenChannel( - alice, dave, lntemp.OpenChannelParams{ + alice, dave, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -527,7 +527,7 @@ func testGarbageCollectLinkNodes(ht *lntemp.HarnessTest) { // testRejectHTLC tests that a node can be created with the flag --rejecthtlc. // This means that the node will reject all forwarded HTLCs but can still // accept direct HTLCs as well as send HTLCs. -func testRejectHTLC(ht *lntemp.HarnessTest) { +func testRejectHTLC(ht *lntest.HarnessTest) { // RejectHTLC // Alice ------> Carol ------> Bob // @@ -548,14 +548,14 @@ func testRejectHTLC(ht *lntemp.HarnessTest) { // Open a channel between Alice and Carol. chanPointAlice := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, }, ) // Open a channel between Carol and Bob. chanPointCarol := ht.OpenChannel( - carol, bob, lntemp.OpenChannelParams{ + carol, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -623,14 +623,14 @@ func testRejectHTLC(ht *lntemp.HarnessTest) { // testNodeSignVerify checks that only connected nodes are allowed to perform // signing and verifying messages. -func testNodeSignVerify(ht *lntemp.HarnessTest) { +func testNodeSignVerify(ht *lntest.HarnessTest) { chanAmt := funding.MaxBtcFundingAmount pushAmt := btcutil.Amount(100000) alice, bob := ht.Alice, ht.Bob // Create a channel between alice and bob. aliceBobCh := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -670,11 +670,11 @@ func testNodeSignVerify(ht *lntemp.HarnessTest) { // testAbandonChannel abandons a channel and asserts that it is no longer open // and not in one of the pending closure states. It also verifies that the // abandoned channel is reported as closed with close type 'abandoned'. -func testAbandonChannel(ht *lntemp.HarnessTest) { +func testAbandonChannel(ht *lntest.HarnessTest) { alice, bob := ht.Alice, ht.Bob // First establish a channel between Alice and Bob. - channelParam := lntemp.OpenChannelParams{ + channelParam := lntest.OpenChannelParams{ Amt: funding.MaxBtcFundingAmount, PushAmt: btcutil.Amount(100000), } @@ -741,7 +741,7 @@ func testAbandonChannel(ht *lntemp.HarnessTest) { // wallet into a single target address at the specified fee rate. // // TODO(yy): expand this test to also use P2TR. -func testSweepAllCoins(ht *lntemp.HarnessTest) { +func testSweepAllCoins(ht *lntest.HarnessTest) { // First, we'll make a new node, ainz who'll we'll use to test wallet // sweeping. // @@ -917,7 +917,7 @@ func testSweepAllCoins(ht *lntemp.HarnessTest) { // testListAddresses tests that we get all the addresses and their // corresponding balance correctly. -func testListAddresses(ht *lntemp.HarnessTest) { +func testListAddresses(ht *lntest.HarnessTest) { // First, we'll make a new node - Alice, which will be generating // new addresses. alice := ht.NewNode("Alice", nil) @@ -964,7 +964,8 @@ func testListAddresses(ht *lntemp.HarnessTest) { }) generatedAddr[resp.Address] = addressDetails{ Balance: 400_000, - Type: walletrpc.AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, + Type: walletrpc. + AddressType_HYBRID_NESTED_WITNESS_PUBKEY_HASH, } for addr, addressDetail := range generatedAddr { @@ -985,7 +986,7 @@ func testListAddresses(ht *lntemp.HarnessTest) { foundAddresses := 0 for _, addressList := range addressLists.AccountWithAddresses { addresses := addressList.Addresses - derivationPath, err := lntemp.ParseDerivationPath( + derivationPath, err := lntest.ParseDerivationPath( addressList.DerivationPath, ) require.NoError(ht, err) @@ -1023,7 +1024,7 @@ func testListAddresses(ht *lntemp.HarnessTest) { for _, addressList := range addressLists.AccountWithAddresses { addresses := addressList.Addresses - derivationPath, err := lntemp.ParseDerivationPath( + derivationPath, err := lntest.ParseDerivationPath( addressList.DerivationPath, ) require.NoError(ht, err) @@ -1053,7 +1054,7 @@ func testListAddresses(ht *lntemp.HarnessTest) { require.Equal(ht, len(generatedAddr), foundAddresses) } -func assertChannelConstraintsEqual(ht *lntemp.HarnessTest, +func assertChannelConstraintsEqual(ht *lntest.HarnessTest, want, got *lnrpc.ChannelConstraints) { require.Equal(ht, want.CsvDelay, got.CsvDelay, "CsvDelay mismatched") @@ -1076,7 +1077,7 @@ func assertChannelConstraintsEqual(ht *lntemp.HarnessTest, // testSignVerifyMessageWithAddr tests signing and also verifying a signature // on a message with a provided address. -func testSignVerifyMessageWithAddr(ht *lntemp.HarnessTest) { +func testSignVerifyMessageWithAddr(ht *lntest.HarnessTest) { // Using different nodes to sign the message and verify the signature. alice, bob := ht.Alice, ht.Bob diff --git a/lntest/itest/lnd_mpp_test.go b/itest/lnd_mpp_test.go similarity index 94% rename from lntest/itest/lnd_mpp_test.go rename to itest/lnd_mpp_test.go index c9d8fd4c2..e54da0aa4 100644 --- a/lntest/itest/lnd_mpp_test.go +++ b/itest/lnd_mpp_test.go @@ -7,8 +7,8 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing/route" "github.com/stretchr/testify/require" @@ -16,7 +16,7 @@ import ( // testSendToRouteMultiPath tests that we are able to successfully route a // payment using multiple shards across different paths, by using SendToRoute. -func testSendToRouteMultiPath(ht *lntemp.HarnessTest) { +func testSendToRouteMultiPath(ht *lntest.HarnessTest) { mts := newMppTestScenario(ht) // To ensure the payment goes through separate paths, we'll set a @@ -162,7 +162,7 @@ func testSendToRouteMultiPath(ht *lntemp.HarnessTest) { // It has two standby nodes, alice and bob, and three new nodes, carol, dave, // and eve. type mppTestScenario struct { - ht *lntemp.HarnessTest + ht *lntest.HarnessTest alice, bob, carol, dave, eve *node.HarnessNode nodes []*node.HarnessNode @@ -179,7 +179,7 @@ type mppTestScenario struct { // Alice -- Carol ---- Bob // \ / // \__ Dave ____/ -func newMppTestScenario(ht *lntemp.HarnessTest) *mppTestScenario { +func newMppTestScenario(ht *lntest.HarnessTest) *mppTestScenario { alice, bob := ht.Alice, ht.Bob ht.RestartNodeWithExtraArgs(bob, []string{ "--maxpendingchannels=2", @@ -256,36 +256,36 @@ type mppOpenChannelRequest struct { // // NOTE: all the channels are open together to save blocks mined. func (m *mppTestScenario) openChannels(r *mppOpenChannelRequest) { - reqs := []*lntemp.OpenChannelRequest{ + reqs := []*lntest.OpenChannelRequest{ { Local: m.alice, Remote: m.carol, - Param: lntemp.OpenChannelParams{Amt: r.amtAliceCarol}, + Param: lntest.OpenChannelParams{Amt: r.amtAliceCarol}, }, { Local: m.alice, Remote: m.dave, - Param: lntemp.OpenChannelParams{Amt: r.amtAliceDave}, + Param: lntest.OpenChannelParams{Amt: r.amtAliceDave}, }, { Local: m.carol, Remote: m.bob, - Param: lntemp.OpenChannelParams{Amt: r.amtCarolBob}, + Param: lntest.OpenChannelParams{Amt: r.amtCarolBob}, }, { Local: m.carol, Remote: m.eve, - Param: lntemp.OpenChannelParams{Amt: r.amtCarolEve}, + Param: lntest.OpenChannelParams{Amt: r.amtCarolEve}, }, { Local: m.dave, Remote: m.bob, - Param: lntemp.OpenChannelParams{Amt: r.amtDaveBob}, + Param: lntest.OpenChannelParams{Amt: r.amtDaveBob}, }, { Local: m.eve, Remote: m.bob, - Param: lntemp.OpenChannelParams{Amt: r.amtEveBob}, + Param: lntest.OpenChannelParams{Amt: r.amtEveBob}, }, } @@ -313,7 +313,7 @@ func (m *mppTestScenario) closeChannels() { // active htlcs` or `link failed to shutdown` if we close the channel. // We need to investigate the order of settling the payments and // updating commitments to understand and fix . - time.Sleep(2 * time.Second) + time.Sleep(5 * time.Second) // Close all channels without mining the closing transactions. m.ht.CloseChannelAssertPending(m.alice, m.channelPoints[0], false) diff --git a/lntest/itest/lnd_multi-hop-error-propagation_test.go b/itest/lnd_multi-hop-error-propagation_test.go similarity index 92% rename from lntest/itest/lnd_multi-hop-error-propagation_test.go rename to itest/lnd_multi-hop-error-propagation_test.go index a5cca06aa..3941accb0 100644 --- a/lntest/itest/lnd_multi-hop-error-propagation_test.go +++ b/itest/lnd_multi-hop-error-propagation_test.go @@ -6,12 +6,12 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" ) -func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { +func testHtlcErrorPropagation(ht *lntest.HarnessTest) { // In this test we wish to exercise the daemon's correct parsing, // handling, and propagation of errors that occur while processing a // multi-hop payment. @@ -39,23 +39,22 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // and Bob. chanPointAlice := ht.OpenChannel( alice, bob, - lntemp.OpenChannelParams{Amt: chanAmt}, + lntest.OpenChannelParams{Amt: chanAmt}, ) // Next, we'll create a connection from Bob to Carol, and open a // channel between them so we have the topology: Alice -> Bob -> Carol. // The channel created will be of lower capacity that the one created // above. - const bobChanAmt = funding.MaxBtcFundingAmount chanPointBob := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + bob, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) // Ensure that Alice has Carol in her routing table before proceeding. ht.AssertTopologyChannelOpen(alice, chanPointBob) cType := ht.GetChannelCommitType(alice, chanPointAlice) - commitFee := calcStaticFee(cType, 0) + commitFee := lntest.CalcStaticFee(cType, 0) assertBaseBalance := func() { // Alice has opened a channel with Bob with zero push amount, @@ -153,24 +152,24 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { assertAliceAndBob := func() { ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForward, + lntest.HtlcEventForward, ) ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForwardFail, + lntest.HtlcEventForwardFail, ) ht.AssertHtlcEventTypes( bobEvents, routerrpc.HtlcEvent_FORWARD, - lntemp.HtlcEventForward, + lntest.HtlcEventForward, ) ht.AssertHtlcEventTypes( bobEvents, routerrpc.HtlcEvent_FORWARD, - lntemp.HtlcEventForwardFail, + lntest.HtlcEventForwardFail, ) ht.AssertHtlcEventTypes( bobEvents, routerrpc.HtlcEvent_UNKNOWN, - lntemp.HtlcEventFinal, + lntest.HtlcEventFinal, ) } @@ -188,7 +187,7 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // There's also a final htlc event that gives the final outcome of the // htlc. ht.AssertHtlcEventTypes( - carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal, + carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntest.HtlcEventFinal, ) // The balances of all parties should be the same as initially since @@ -228,7 +227,7 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // There's also a final htlc event that gives the final outcome of the // htlc. ht.AssertHtlcEventTypes( - carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal, + carolEvents, routerrpc.HtlcEvent_UNKNOWN, lntest.HtlcEventFinal, ) // The balances of all parties should be the same as initially since @@ -273,19 +272,19 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // settle event and a final htlc event for her receive. ht.AssertHtlcEventTypes( bobEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForward, + lntest.HtlcEventForward, ) ht.AssertHtlcEventTypes( bobEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventSettle, + lntest.HtlcEventSettle, ) ht.AssertHtlcEventTypes( carolEvents, routerrpc.HtlcEvent_RECEIVE, - lntemp.HtlcEventSettle, + lntest.HtlcEventSettle, ) ht.AssertHtlcEventTypes( carolEvents, routerrpc.HtlcEvent_UNKNOWN, - lntemp.HtlcEventFinal, + lntest.HtlcEventFinal, ) amtSent += toSend @@ -316,11 +315,11 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // Alice should have a forwarding event and a forwarding failure. ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForward, + lntest.HtlcEventForward, ) ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForwardFail, + lntest.HtlcEventForwardFail, ) // Bob should have a link failure because the htlc failed on his @@ -331,7 +330,7 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // There's also a final htlc event that gives the final outcome of the // htlc. ht.AssertHtlcEventTypes( - bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal, + bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntest.HtlcEventFinal, ) // Generate new invoice to not pay same invoice twice. @@ -362,11 +361,11 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // Alice should have a forwarding event and subsequent fail. ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForward, + lntest.HtlcEventForward, ) ht.AssertHtlcEventTypes( aliceEvents, routerrpc.HtlcEvent_SEND, - lntemp.HtlcEventForwardFail, + lntest.HtlcEventForwardFail, ) // Bob should have a link failure because he could not find the next @@ -377,7 +376,7 @@ func testHtlcErrorPropagation(ht *lntemp.HarnessTest) { // There's also a final htlc event that gives the final outcome of the // htlc. ht.AssertHtlcEventTypes( - bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntemp.HtlcEventFinal, + bobEvents, routerrpc.HtlcEvent_UNKNOWN, lntest.HtlcEventFinal, ) // Finally, immediately close the channel. This function will also diff --git a/lntest/itest/lnd_multi-hop-payments_test.go b/itest/lnd_multi-hop-payments_test.go similarity index 95% rename from lntest/itest/lnd_multi-hop-payments_test.go rename to itest/lnd_multi-hop-payments_test.go index 28c032061..30a446471 100644 --- a/lntest/itest/lnd_multi-hop-payments_test.go +++ b/itest/lnd_multi-hop-payments_test.go @@ -5,12 +5,12 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) -func testMultiHopPayments(ht *lntemp.HarnessTest) { +func testMultiHopPayments(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(100000) // As preliminary setup, we'll create two new nodes: Carol and Dave, @@ -43,21 +43,21 @@ func testMultiHopPayments(ht *lntemp.HarnessTest) { // Open a channel with 100k satoshis between Alice and Bob with Alice // being the sole funder of the channel. chanPointAlice := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // We'll create Dave and establish a channel to Alice. Dave will be // running an older node that requires the legacy onion payload. ht.FundCoins(btcutil.SatoshiPerBitcoin, dave) chanPointDave := ht.OpenChannel( - dave, alice, lntemp.OpenChannelParams{Amt: chanAmt}, + dave, alice, lntest.OpenChannelParams{Amt: chanAmt}, ) // Next, we'll create Carol and establish a channel to from her to // Dave. ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) chanPointCarol := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) // Create 5 invoices for Bob, which expect a payment from Carol for 1k @@ -69,7 +69,7 @@ func testMultiHopPayments(ht *lntemp.HarnessTest) { // Set the fee policies of the Alice -> Bob and the Dave -> Alice // channel edges to relatively large non default values. This makes it // possible to pick up more subtle fee calculation errors. - maxHtlc := calculateMaxHtlc(chanAmt) + maxHtlc := lntest.CalculateMaxHtlc(chanAmt) const aliceBaseFeeSat = 1 const aliceFeeRatePPM = 100000 updateChannelPolicy( @@ -222,7 +222,7 @@ func testMultiHopPayments(ht *lntemp.HarnessTest) { // policy update. // // NOTE: only used in current test. -func updateChannelPolicy(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func updateChannelPolicy(ht *lntest.HarnessTest, hn *node.HarnessNode, chanPoint *lnrpc.ChannelPoint, baseFee int64, feeRate int64, timeLockDelta uint32, maxHtlc uint64, listenerNode *node.HarnessNode) { diff --git a/lntest/itest/lnd_multi-hop_test.go b/itest/lnd_multi-hop_test.go similarity index 93% rename from lntest/itest/lnd_multi-hop_test.go rename to itest/lnd_multi-hop_test.go index 8f738dc7c..0f562ed9b 100644 --- a/lntest/itest/lnd_multi-hop_test.go +++ b/itest/lnd_multi-hop_test.go @@ -12,10 +12,9 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntemp/rpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/routing" "github.com/stretchr/testify/require" @@ -52,109 +51,13 @@ var commitWithZeroConf = []struct { }, } -// waitForInvoiceAccepted waits until the specified invoice moved to the -// accepted state by the node. -func waitForInvoiceAccepted(t *harnessTest, node *lntest.HarnessNode, - payHash lntypes.Hash) { - - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - invoiceUpdates, err := node.SubscribeSingleInvoice(ctx, - &invoicesrpc.SubscribeSingleInvoiceRequest{ - RHash: payHash[:], - }, - ) - if err != nil { - t.Fatalf("subscribe single invoice: %v", err) - } - - for { - update, err := invoiceUpdates.Recv() - if err != nil { - t.Fatalf("invoice update err: %v", err) - } - if update.State == lnrpc.Invoice_ACCEPTED { - break - } - } -} - -// checkPaymentStatus asserts that the given node list a payment with the given -// preimage has the expected status. -func checkPaymentStatus(node *lntest.HarnessNode, preimage lntypes.Preimage, - status lnrpc.Payment_PaymentStatus) error { - - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - req := &lnrpc.ListPaymentsRequest{ - IncludeIncomplete: true, - } - paymentsResp, err := node.ListPayments(ctxt, req) - if err != nil { - return fmt.Errorf("error when obtaining Alice payments: %v", - err) - } - - payHash := preimage.Hash() - var found bool - for _, p := range paymentsResp.Payments { - if p.PaymentHash != payHash.String() { - continue - } - - found = true - if p.Status != status { - return fmt.Errorf("expected payment status "+ - "%v, got %v", status, p.Status) - } - - switch status { - // If this expected status is SUCCEEDED, we expect the final preimage. - case lnrpc.Payment_SUCCEEDED: - if p.PaymentPreimage != preimage.String() { - return fmt.Errorf("preimage doesn't match: %v vs %v", - p.PaymentPreimage, preimage.String()) - } - - // Otherwise we expect an all-zero preimage. - default: - if p.PaymentPreimage != (lntypes.Preimage{}).String() { - return fmt.Errorf("expected zero preimage, got %v", - p.PaymentPreimage) - } - } - } - - if !found { - return fmt.Errorf("payment with payment hash %v not found "+ - "in response", payHash) - } - - return nil -} - -// assertAllTxesSpendFrom asserts that all txes in the list spend from the given -// tx. -func assertAllTxesSpendFrom(t *harnessTest, txes []*wire.MsgTx, - prevTxid chainhash.Hash) { - - for _, tx := range txes { - if tx.TxIn[0].PreviousOutPoint.Hash != prevTxid { - t.Fatalf("tx %v did not spend from %v", - tx.TxHash(), prevTxid) - } - } -} - // caseRunner defines a single test case runner. -type caseRunner func(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode, +type caseRunner func(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) // runMultiHopHtlcClaimTest is a helper method to build test cases based on // different commitment types and zero-conf config and run them. -func runMultiHopHtlcClaimTest(ht *lntemp.HarnessTest, tester caseRunner) { +func runMultiHopHtlcClaimTest(ht *lntest.HarnessTest, tester caseRunner) { for _, typeAndConf := range commitWithZeroConf { typeAndConf := typeAndConf name := fmt.Sprintf("zeroconf=%v/committype=%v", @@ -162,7 +65,7 @@ func runMultiHopHtlcClaimTest(ht *lntemp.HarnessTest, tester caseRunner) { // Create the nodes here so that separate logs will be created // for Alice and Bob. - args := nodeArgsForCommitType(typeAndConf.commitType) + args := lntest.NodeArgsForCommitType(typeAndConf.commitType) if typeAndConf.zeroConf { args = append( args, "--protocol.option-scid-alias", @@ -200,11 +103,11 @@ func runMultiHopHtlcClaimTest(ht *lntemp.HarnessTest, tester caseRunner) { // it using the HTLC timeout transaction. Any dust HTLC's should be immediately // canceled backwards. Once the timeout has been reached, then we should sweep // it on-chain, and cancel the HTLC backwards. -func testMultiHopHtlcLocalTimeout(ht *lntemp.HarnessTest) { +func testMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runMultiHopHtlcLocalTimeout) } -func runMultiHopHtlcLocalTimeout(ht *lntemp.HarnessTest, +func runMultiHopHtlcLocalTimeout(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -268,7 +171,7 @@ func runMultiHopHtlcLocalTimeout(ht *lntemp.HarnessTest, // Bob's force close transaction should now be found in the mempool. If // there are anchors, we also expect Bob's anchor sweep. expectedTxes := 1 - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) if hasAnchors { expectedTxes = 2 } @@ -369,11 +272,11 @@ func runMultiHopHtlcLocalTimeout(ht *lntemp.HarnessTest, // transaction. In this scenario, the node that sent the outgoing HTLC should // extract the preimage from the sweep transaction, and finish settling the // HTLC backwards into the route. -func testMultiHopReceiverChainClaim(ht *lntemp.HarnessTest) { +func testMultiHopReceiverChainClaim(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runMultiHopReceiverChainClaim) } -func runMultiHopReceiverChainClaim(ht *lntemp.HarnessTest, +func runMultiHopReceiverChainClaim(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -445,7 +348,7 @@ func runMultiHopReceiverChainClaim(ht *lntemp.HarnessTest, // transaction in order to go to the chain and sweep her HTLC. If there // are anchors, Carol also sweeps hers. expectedTxes := 1 - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) if hasAnchors { expectedTxes = 2 } @@ -567,13 +470,13 @@ func runMultiHopReceiverChainClaim(ht *lntemp.HarnessTest, // commitment on-chain early, then it eventually recognizes this HTLC as one // that's timed out. At this point, the node should timeout the HTLC using the // HTLC timeout transaction, then cancel it backwards as normal. -func testMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest) { +func testMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest( ht, runMultiHopLocalForceCloseOnChainHtlcTimeout, ) } -func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, +func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -614,7 +517,7 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, // Now that all parties have the HTLC locked in, we'll immediately // force close the Bob -> Carol channel. This should trigger contract // resolution mode for both of them. - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) stream, _ := ht.CloseChannelAssertPending(bob, bobChanPoint, true) closeTx := ht.AssertStreamChannelForceClosed( bob, bobChanPoint, hasAnchors, stream, @@ -723,13 +626,13 @@ func runMultiHopLocalForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, // channel, then we properly timeout the HTLC directly on *their* commitment // transaction once the timeout has expired. Once we sweep the transaction, we // should also cancel back the initial HTLC. -func testMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest) { +func testMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest( ht, runMultiHopRemoteForceCloseOnChainHtlcTimeout, ) } -func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, +func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -779,7 +682,7 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, // transaction. This will let us exercise that Bob is able to sweep the // expired HTLC on Carol's version of the commitment transaction. If // Carol has an anchor, it will be swept too. - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) closeStream, _ := ht.CloseChannelAssertPending( carol, bobChanPoint, true, ) @@ -879,11 +782,11 @@ func runMultiHopRemoteForceCloseOnChainHtlcTimeout(ht *lntemp.HarnessTest, // we force close a channel with an incoming HTLC, and later find out the // preimage via the witness beacon, we properly settle the HTLC on-chain using // the HTLC success transaction in order to ensure we don't lose any funds. -func testMultiHopHtlcLocalChainClaim(ht *lntemp.HarnessTest) { +func testMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runMultiHopHtlcLocalChainClaim) } -func runMultiHopHtlcLocalChainClaim(ht *lntemp.HarnessTest, +func runMultiHopHtlcLocalChainClaim(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -937,7 +840,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntemp.HarnessTest, // At this point, Bob decides that he wants to exit the channel // immediately, so he force closes his commitment transaction. - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) closeStream, _ := ht.CloseChannelAssertPending( bob, aliceChanPoint, true, ) @@ -985,7 +888,7 @@ func runMultiHopHtlcLocalChainClaim(ht *lntemp.HarnessTest, // Carol's commitment transaction should now be in the mempool. If // there is an anchor, Carol will sweep that too. - if commitTypeHasAnchors(c) { + if lntest.CommitTypeHasAnchors(c) { expectedTxes = 2 } ht.Miner.AssertNumTxsInMempool(expectedTxes) @@ -1173,11 +1076,11 @@ func runMultiHopHtlcLocalChainClaim(ht *lntemp.HarnessTest, // we found out the preimage via the witness beacon, we properly settle the // HTLC directly on-chain using the preimage in order to ensure that we don't // lose any funds. -func testMultiHopHtlcRemoteChainClaim(ht *lntemp.HarnessTest) { +func testMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runMultiHopHtlcRemoteChainClaim) } -func runMultiHopHtlcRemoteChainClaim(ht *lntemp.HarnessTest, +func runMultiHopHtlcRemoteChainClaim(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol, with @@ -1232,7 +1135,7 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntemp.HarnessTest, // Next, Alice decides that she wants to exit the channel, so she'll // immediately force close the channel by broadcast her commitment // transaction. - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) closeStream, _ := ht.CloseChannelAssertPending( alice, aliceChanPoint, true, ) @@ -1443,11 +1346,11 @@ func runMultiHopHtlcRemoteChainClaim(ht *lntemp.HarnessTest, // resolve them using the second level timeout and success transactions. In // case of anchor channels, the second-level spends can also be aggregated and // properly feebumped, so we'll check that as well. -func testMultiHopHtlcAggregation(ht *lntemp.HarnessTest) { +func testMultiHopHtlcAggregation(ht *lntest.HarnessTest) { runMultiHopHtlcClaimTest(ht, runMultiHopHtlcAggregation) } -func runMultiHopHtlcAggregation(ht *lntemp.HarnessTest, +func runMultiHopHtlcAggregation(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, c lnrpc.CommitmentType, zeroConf bool) { // First, we'll create a three hop network: Alice -> Bob -> Carol. @@ -1586,7 +1489,7 @@ func runMultiHopHtlcAggregation(ht *lntemp.HarnessTest, // Bob's force close transaction should now be found in the mempool. If // there are anchors, we also expect Bob's anchor sweep. - hasAnchors := commitTypeHasAnchors(c) + hasAnchors := lntest.CommitTypeHasAnchors(c) expectedTxes := 1 if hasAnchors { expectedTxes = 2 @@ -1825,7 +1728,7 @@ func runMultiHopHtlcAggregation(ht *lntemp.HarnessTest, } // createThreeHopNetwork creates a topology of `Alice -> Bob -> Carol`. -func createThreeHopNetwork(ht *lntemp.HarnessTest, +func createThreeHopNetwork(ht *lntest.HarnessTest, alice, bob *node.HarnessNode, carolHodl bool, c lnrpc.CommitmentType, zeroConf bool) (*lnrpc.ChannelPoint, *lnrpc.ChannelPoint, *node.HarnessNode) { @@ -1835,7 +1738,7 @@ func createThreeHopNetwork(ht *lntemp.HarnessTest, // We'll create a new node "carol" and have Bob connect to her. // If the carolHodl flag is set, we'll make carol always hold onto the // HTLC, this way it'll force Bob to go to chain to resolve the HTLC. - carolFlags := nodeArgsForCommitType(c) + carolFlags := lntest.NodeArgsForCommitType(c) if carolHodl { carolFlags = append(carolFlags, "--hodl.exit-settle") } @@ -1871,7 +1774,7 @@ func createThreeHopNetwork(ht *lntemp.HarnessTest, if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { _, minerHeight := ht.Miner.GetBestBlock() thawHeight = uint32(minerHeight + thawHeightDelta) - aliceFundingShim, _, _ = deriveFundingShim( + aliceFundingShim, _ = deriveFundingShim( ht, alice, bob, chanAmt, thawHeight, true, ) } @@ -1887,7 +1790,7 @@ func createThreeHopNetwork(ht *lntemp.HarnessTest, go acceptChannel(ht.T, true, acceptStream) } - aliceParams := lntemp.OpenChannelParams{ + aliceParams := lntest.OpenChannelParams{ Amt: chanAmt, CommitmentType: c, FundingShim: aliceFundingShim, @@ -1904,7 +1807,7 @@ func createThreeHopNetwork(ht *lntemp.HarnessTest, // open, our topology looks like: A -> B -> C. var bobFundingShim *lnrpc.FundingShim if c == lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE { - bobFundingShim, _, _ = deriveFundingShim( + bobFundingShim, _ = deriveFundingShim( ht, bob, carol, chanAmt, thawHeight, true, ) } @@ -1916,7 +1819,7 @@ func createThreeHopNetwork(ht *lntemp.HarnessTest, go acceptChannel(ht.T, true, acceptStream) } - bobParams := lntemp.OpenChannelParams{ + bobParams := lntest.OpenChannelParams{ Amt: chanAmt, CommitmentType: c, FundingShim: bobFundingShim, diff --git a/lntest/itest/lnd_network_test.go b/itest/lnd_network_test.go similarity index 94% rename from lntest/itest/lnd_network_test.go rename to itest/lnd_network_test.go index 6763a952e..4f0ff8986 100644 --- a/lntest/itest/lnd_network_test.go +++ b/itest/lnd_network_test.go @@ -3,12 +3,12 @@ package itest import ( "fmt" "net" + "time" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -16,7 +16,7 @@ import ( // testNetworkConnectionTimeout checks that the connectiontimeout is taking // effect. It creates a node with a small connection timeout value, and // connects it to a non-routable IP address. -func testNetworkConnectionTimeout(ht *lntemp.HarnessTest) { +func testNetworkConnectionTimeout(ht *lntest.HarnessTest) { var ( // testPub is a random public key for testing only. testPub = "0332bda7da70fefe4b6ab92f53b3c4f4ee7999" + @@ -75,7 +75,7 @@ func testNetworkConnectionTimeout(ht *lntemp.HarnessTest) { // testReconnectAfterIPChange verifies that if a persistent inbound node changes // its listening address then it's peer will still be able to reconnect to it. -func testReconnectAfterIPChange(ht *lntemp.HarnessTest) { +func testReconnectAfterIPChange(ht *lntest.HarnessTest) { // In this test, the following network will be set up. A single // dash line represents a peer connection and a double dash line // represents a channel. @@ -105,7 +105,7 @@ func testReconnectAfterIPChange(ht *lntemp.HarnessTest) { // We derive an extra port for Dave, and we initialise his node with // the port advertised as `--externalip` arguments. - ip2 := lntest.NextAvailablePort() + ip2 := node.NextAvailablePort() // Create a new node, Dave, which will initialize a P2P port for him. daveArgs := []string{fmt.Sprintf("--externalip=127.0.0.1:%d", ip2)} @@ -127,7 +127,7 @@ func testReconnectAfterIPChange(ht *lntemp.HarnessTest) { // ensures that Charlie receives the node announcement from Alice as // part of the announcement broadcast. chanPoint := ht.OpenChannel( - alice, dave, lntemp.OpenChannelParams{Amt: 1000000}, + alice, dave, lntest.OpenChannelParams{Amt: 1000000}, ) // waitForNodeAnnouncement is a closure used to wait on the given graph @@ -184,7 +184,7 @@ func testReconnectAfterIPChange(ht *lntemp.HarnessTest) { // address. // Change Dave's listening port and restart. - dave.Cfg.P2PPort = lntest.NextAvailablePort() + dave.Cfg.P2PPort = node.NextAvailablePort() dave.Cfg.ExtraArgs = []string{ fmt.Sprintf( "--externalip=127.0.0.1:%d", dave.Cfg.P2PPort, @@ -210,7 +210,7 @@ func testReconnectAfterIPChange(ht *lntemp.HarnessTest) { // testAddPeerConfig tests that the "--addpeer" config flag successfully adds // a new peer. -func testAddPeerConfig(ht *lntemp.HarnessTest) { +func testAddPeerConfig(ht *lntest.HarnessTest) { alice := ht.Alice info := alice.RPC.GetInfo() @@ -220,6 +220,9 @@ func testAddPeerConfig(ht *lntemp.HarnessTest) { args := []string{fmt.Sprintf("--addpeer=%v", alicePeerAddress)} carol := ht.NewNode("Carol", args) + // TODO(yy): remove this once the peer conn race is fixed. + time.Sleep(1 * time.Second) + ht.EnsureConnected(alice, carol) // If we list Carol's peers, Alice should already be diff --git a/lntest/itest/lnd_neutrino_test.go b/itest/lnd_neutrino_test.go similarity index 85% rename from lntest/itest/lnd_neutrino_test.go rename to itest/lnd_neutrino_test.go index 902eac430..2c551362a 100644 --- a/lntest/itest/lnd_neutrino_test.go +++ b/itest/lnd_neutrino_test.go @@ -2,15 +2,14 @@ package itest import ( "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" - "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) // testNeutrino checks that the neutrino sub-server can fetch compact // block filters, server status and connect to a connected peer. -func testNeutrino(ht *lntemp.HarnessTest) { - if ht.ChainBackendName() != lntest.NeutrinoBackendName { +func testNeutrino(ht *lntest.HarnessTest) { + if !ht.IsNeutrinoBackend() { ht.Skipf("skipping test for non neutrino backends") } diff --git a/lntest/itest/lnd_no_etcd_dummy_failover_test.go b/itest/lnd_no_etcd_dummy_failover_test.go similarity index 59% rename from lntest/itest/lnd_no_etcd_dummy_failover_test.go rename to itest/lnd_no_etcd_dummy_failover_test.go index 721b60cf6..2d731febc 100644 --- a/lntest/itest/lnd_no_etcd_dummy_failover_test.go +++ b/itest/lnd_no_etcd_dummy_failover_test.go @@ -3,8 +3,8 @@ package itest -import "github.com/lightningnetwork/lnd/lntemp" +import "github.com/lightningnetwork/lnd/lntest" // testEtcdFailover is an empty itest when LND is not compiled with etcd // support. -func testEtcdFailover(ht *lntemp.HarnessTest) {} +func testEtcdFailover(ht *lntest.HarnessTest) {} diff --git a/lntest/itest/lnd_nonstd_sweep_test.go b/itest/lnd_nonstd_sweep_test.go similarity index 94% rename from lntest/itest/lnd_nonstd_sweep_test.go rename to itest/lnd_nonstd_sweep_test.go index cc2ac7027..a85fbc786 100644 --- a/lntest/itest/lnd_nonstd_sweep_test.go +++ b/itest/lnd_nonstd_sweep_test.go @@ -5,11 +5,11 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) -func testNonstdSweep(ht *lntemp.HarnessTest) { +func testNonstdSweep(ht *lntest.HarnessTest) { p2shAddr, err := btcutil.NewAddressScriptHash( make([]byte, 1), harnessNetParams, ) @@ -74,7 +74,7 @@ func testNonstdSweep(ht *lntemp.HarnessTest) { } } -func testNonStdSweepInner(ht *lntemp.HarnessTest, address string) { +func testNonStdSweepInner(ht *lntest.HarnessTest, address string) { carol := ht.NewNode("carol", nil) // Give Carol a UTXO so SendCoins will behave as expected. diff --git a/lntest/itest/lnd_onchain_test.go b/itest/lnd_onchain_test.go similarity index 94% rename from lntest/itest/lnd_onchain_test.go rename to itest/lnd_onchain_test.go index 2b6b3200e..b9d50d5c6 100644 --- a/lntest/itest/lnd_onchain_test.go +++ b/itest/lnd_onchain_test.go @@ -11,8 +11,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/sweep" @@ -20,7 +20,7 @@ import ( ) // testChainKit tests ChainKit RPC endpoints. -func testChainKit(ht *lntemp.HarnessTest) { +func testChainKit(ht *lntest.HarnessTest) { // Test functions registered as test cases spin up separate nodes // during execution. By calling sub-test functions as seen below we // avoid the need to start separate nodes. @@ -30,7 +30,7 @@ func testChainKit(ht *lntemp.HarnessTest) { // testChainKitGetBlock ensures that given a block hash, the RPC endpoint // returns the correct target block. -func testChainKitGetBlock(ht *lntemp.HarnessTest) { +func testChainKitGetBlock(ht *lntest.HarnessTest) { // Get best block hash. bestBlockRes := ht.Alice.RPC.GetBestBlock(nil) @@ -58,7 +58,7 @@ func testChainKitGetBlock(ht *lntemp.HarnessTest) { // testChainKitGetBlockHash ensures that given a block height, the RPC endpoint // returns the correct target block hash. -func testChainKitGetBlockHash(ht *lntemp.HarnessTest) { +func testChainKitGetBlockHash(ht *lntest.HarnessTest) { // Get best block hash. bestBlockRes := ht.Alice.RPC.GetBestBlock(nil) @@ -78,13 +78,13 @@ func testChainKitGetBlockHash(ht *lntemp.HarnessTest) { // rate by broadcasting a Child-Pays-For-Parent (CPFP) transaction. // // TODO(wilmer): Add RBF case once btcd supports it. -func testCPFP(ht *lntemp.HarnessTest) { +func testCPFP(ht *lntest.HarnessTest) { runCPFP(ht, ht.Alice, ht.Bob) } // runCPFP ensures that the daemon can bump an unconfirmed transaction's fee // rate by broadcasting a Child-Pays-For-Parent (CPFP) transaction. -func runCPFP(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { +func runCPFP(ht *lntest.HarnessTest, alice, bob *node.HarnessNode) { // Skip this test for neutrino, as it's not aware of mempool // transactions. if ht.IsNeutrinoBackend() { @@ -172,6 +172,7 @@ func runCPFP(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { return fmt.Errorf("expected 0 pending sweeps, found %d", len(resp.PendingSweeps)) } + return nil }, defaultTimeout) require.NoError(ht, err, "timeout checking bob's pending sweeps") @@ -180,9 +181,9 @@ func runCPFP(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { // testAnchorReservedValue tests that we won't allow sending transactions when // that would take the value we reserve for anchor fee bumping out of our // wallet. -func testAnchorReservedValue(ht *lntemp.HarnessTest) { +func testAnchorReservedValue(ht *lntest.HarnessTest) { // Start two nodes supporting anchor channels. - args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) // NOTE: we cannot reuse the standby node here as the test requires the // node to start with no UTXOs. @@ -203,7 +204,7 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { // wallet, without a change output. This should not be allowed. ht.OpenChannelAssertErr( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, }, lnwallet.ErrReservedValueInvalidated, ) @@ -211,13 +212,13 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { // Alice opens a smaller channel. This works since it will have a // change output. chanPoint1 := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt / 4}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt / 4}, ) // If Alice tries to open another anchor channel to Bob, Bob should not // reject it as he is not contributing any funds. chanPoint2 := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt / 4}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt / 4}, ) // Similarly, if Alice tries to open a legacy channel to Bob, Bob @@ -229,7 +230,7 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { ht.EnsureConnected(alice, bob) chanPoint3 := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt / 4}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt / 4}, ) chanPoints := []*lnrpc.ChannelPoint{chanPoint1, chanPoint2, chanPoint3} @@ -276,6 +277,7 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { } balance = resp.TotalBalance + return nil }, defaultTimeout) require.NoError(ht, err, "timeout checking alice's balance") @@ -347,18 +349,15 @@ func testAnchorReservedValue(ht *lntemp.HarnessTest) { // testAnchorThirdPartySpend tests that if we force close a channel, but then // don't sweep the anchor in time and a 3rd party spends it, that we remove any // transactions that are a descendent of that sweep. -func testAnchorThirdPartySpend(ht *lntemp.HarnessTest) { +func testAnchorThirdPartySpend(ht *lntest.HarnessTest) { // First, we'll create two new nodes that both default to anchor // channels. // // NOTE: The itests differ here as anchors is default off vs the normal // lnd binary. - args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) alice := ht.NewNode("Alice", args) - defer ht.Shutdown(alice) - bob := ht.NewNode("Bob", args) - defer ht.Shutdown(bob) ht.EnsureConnected(alice, bob) @@ -374,7 +373,7 @@ func testAnchorThirdPartySpend(ht *lntemp.HarnessTest) { // Open the channel between the two nodes and wait for it to confirm // fully. aliceChanPoint1 := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: firstChanSize, }, ) @@ -493,7 +492,7 @@ func testAnchorThirdPartySpend(ht *lntemp.HarnessTest) { // assertAnchorOutputLost asserts that the anchor output for the given channel // has the state of being lost. -func assertAnchorOutputLost(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func assertAnchorOutputLost(ht *lntest.HarnessTest, hn *node.HarnessNode, chanPoint *lnrpc.ChannelPoint) { cp := ht.OutPointFromChannelPoint(chanPoint) @@ -527,8 +526,8 @@ func assertAnchorOutputLost(ht *lntemp.HarnessTest, hn *node.HarnessNode, // genAnchorSweep generates a "3rd party" anchor sweeping from an existing one. // In practice, we just re-use the existing witness, and track on our own // output producing a 1-in-1-out transaction. -func genAnchorSweep(ht *lntemp.HarnessTest, - aliceAnchor *lntemp.SweptOutput, anchorCsv uint32) *btcutil.Tx { +func genAnchorSweep(ht *lntest.HarnessTest, + aliceAnchor *lntest.SweptOutput, anchorCsv uint32) *btcutil.Tx { // At this point, we have the transaction that Alice used to try to // sweep her anchor. As this is actually just something anyone can diff --git a/lntest/itest/lnd_open_channel_test.go b/itest/lnd_open_channel_test.go similarity index 95% rename from lntest/itest/lnd_open_channel_test.go rename to itest/lnd_open_channel_test.go index b3c4a4aca..bfe80cbd1 100644 --- a/lntest/itest/lnd_open_channel_test.go +++ b/itest/lnd_open_channel_test.go @@ -10,9 +10,9 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntemp/rpc" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) @@ -20,7 +20,7 @@ import ( // testOpenChannelAfterReorg tests that in the case where we have an open // channel where the funding tx gets reorged out, the channel will no // longer be present in the node's routing table. -func testOpenChannelAfterReorg(ht *lntemp.HarnessTest) { +func testOpenChannelAfterReorg(ht *lntest.HarnessTest) { // Skip test for neutrino, as we cannot disconnect the miner at will. // TODO(halseth): remove when either can disconnect at will, or restart // node with connection to new miner. @@ -33,7 +33,7 @@ func testOpenChannelAfterReorg(ht *lntemp.HarnessTest) { // Set up a new miner that we can use to cause a reorg. tempLogDir := ".tempminerlogs" logFilename := "output-open_channel_reorg-temp_miner.log" - tempMiner := lntemp.NewTempMiner( + tempMiner := lntest.NewTempMiner( ht.Context(), ht.T, tempLogDir, logFilename, ) defer tempMiner.Stop() @@ -68,7 +68,7 @@ func testOpenChannelAfterReorg(ht *lntemp.HarnessTest) { // Create a new channel that requires 1 confs before it's considered // open, then broadcast the funding transaction - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: funding.MaxBtcFundingAmount, Private: true, } @@ -172,7 +172,7 @@ func testOpenChannelAfterReorg(ht *lntemp.HarnessTest) { // ChannelUpdate --> defaultBaseFee, provided FeeRate // 4.) baseFee and feeRate provided to OpenChannelRequest // ChannelUpdate --> provided baseFee, provided feeRate. -func testOpenChannelUpdateFeePolicy(ht *lntemp.HarnessTest) { +func testOpenChannelUpdateFeePolicy(ht *lntest.HarnessTest) { const ( defaultBaseFee = 1000 defaultFeeRate = 1 @@ -182,12 +182,12 @@ func testOpenChannelUpdateFeePolicy(ht *lntemp.HarnessTest) { optionalFeeRate = 1337 ) - defaultMaxHtlc := calculateMaxHtlc(funding.MaxBtcFundingAmount) + defaultMaxHtlc := lntest.CalculateMaxHtlc(funding.MaxBtcFundingAmount) chanAmt := funding.MaxBtcFundingAmount pushAmt := chanAmt / 2 - feeScenarios := []lntemp.OpenChannelParams{ + feeScenarios := []lntest.OpenChannelParams{ { Amt: chanAmt, PushAmt: pushAmt, @@ -259,8 +259,8 @@ func testOpenChannelUpdateFeePolicy(ht *lntemp.HarnessTest) { alice, bob := ht.Alice, ht.Bob - runTestCase := func(ht *lntemp.HarnessTest, - fs lntemp.OpenChannelParams, + runTestCase := func(ht *lntest.HarnessTest, + fs lntest.OpenChannelParams, alicePolicy, bobPolicy *lnrpc.RoutingPolicy) { // Create a channel Alice->Bob. @@ -305,14 +305,14 @@ func testOpenChannelUpdateFeePolicy(ht *lntemp.HarnessTest) { // testBasicChannelCreationAndUpdates tests multiple channel opening and // closing, and ensures that if a node is subscribed to channel updates they // will be received correctly for both cooperative and force closed channels. -func testBasicChannelCreationAndUpdates(ht *lntemp.HarnessTest) { +func testBasicChannelCreationAndUpdates(ht *lntest.HarnessTest) { runBasicChannelCreationAndUpdates(ht, ht.Alice, ht.Bob) } // runBasicChannelCreationAndUpdates tests multiple channel opening and closing, // and ensures that if a node is subscribed to channel updates they will be // received correctly for both cooperative and force closed channels. -func runBasicChannelCreationAndUpdates(ht *lntemp.HarnessTest, +func runBasicChannelCreationAndUpdates(ht *lntest.HarnessTest, alice, bob *node.HarnessNode) { const ( @@ -329,7 +329,7 @@ func runBasicChannelCreationAndUpdates(ht *lntemp.HarnessTest, chanPoints := make([]*lnrpc.ChannelPoint, numChannels) for i := 0; i < numChannels; i++ { chanPoints[i] = ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: amount, }, ) @@ -458,8 +458,8 @@ func runBasicChannelCreationAndUpdates(ht *lntemp.HarnessTest, // assertMinerBlockHeightDelta ensures that tempMiner is 'delta' blocks ahead // of miner. -func assertMinerBlockHeightDelta(ht *lntemp.HarnessTest, - miner, tempMiner *lntemp.HarnessMiner, delta int32) { +func assertMinerBlockHeightDelta(ht *lntest.HarnessTest, + miner, tempMiner *lntest.HarnessMiner, delta int32) { // Ensure the chain lengths are what we expect. err := wait.NoError(func() error { diff --git a/lntest/itest/lnd_payment_test.go b/itest/lnd_payment_test.go similarity index 76% rename from lntest/itest/lnd_payment_test.go rename to itest/lnd_payment_test.go index 590870285..631399326 100644 --- a/lntest/itest/lnd_payment_test.go +++ b/itest/lnd_payment_test.go @@ -1,7 +1,6 @@ package itest import ( - "context" "crypto/sha256" "encoding/hex" "fmt" @@ -11,14 +10,13 @@ import ( "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" ) -func testListPayments(ht *lntemp.HarnessTest) { +func testListPayments(ht *lntest.HarnessTest) { alice, bob := ht.Alice, ht.Bob // Check that there are no payments before test. @@ -28,7 +26,7 @@ func testListPayments(ht *lntemp.HarnessTest) { // being the sole funder of the channel. chanAmt := btcutil.Amount(100000) chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // Get the number of invoices Bob already has. @@ -171,13 +169,13 @@ func testListPayments(ht *lntemp.HarnessTest) { // subsystems trying to update the channel state in the db. We follow this // transition with a payment that updates the commitment state and verify that // the pending state is up to date. -func testPaymentFollowingChannelOpen(ht *lntemp.HarnessTest) { +func testPaymentFollowingChannelOpen(ht *lntest.HarnessTest) { const paymentAmt = btcutil.Amount(100) channelCapacity := paymentAmt * 1000 // We first establish a channel between Alice and Bob. alice, bob := ht.Alice, ht.Bob - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: channelCapacity, } pendingUpdate := ht.OpenChannelAssertPending(alice, bob, p) @@ -198,7 +196,7 @@ func testPaymentFollowingChannelOpen(ht *lntemp.HarnessTest) { ht.MineBlocksAndAssertNumTxes(6, 1) // We verify that the channel is open from both nodes point of view. - chanPoint := lntemp.ChanPointFromPendingUpdate(pendingUpdate) + chanPoint := lntest.ChanPointFromPendingUpdate(pendingUpdate) ht.AssertNodesNumPendingOpenChannels(alice, bob, 0) ht.AssertChannelExists(alice, chanPoint) ht.AssertChannelExists(bob, chanPoint) @@ -226,7 +224,7 @@ func testPaymentFollowingChannelOpen(ht *lntemp.HarnessTest) { } // testAsyncPayments tests the performance of the async payments. -func testAsyncPayments(ht *lntemp.HarnessTest) { +func testAsyncPayments(ht *lntest.HarnessTest) { // We use new nodes here as the benchmark test creates lots of data // which can be costly to be carried on. alice := ht.NewNode("Alice", []string{"--pending-commit-interval=3m"}) @@ -239,7 +237,7 @@ func testAsyncPayments(ht *lntemp.HarnessTest) { } // runAsyncPayments tests the performance of the async payments. -func runAsyncPayments(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { +func runAsyncPayments(ht *lntest.HarnessTest, alice, bob *node.HarnessNode) { const paymentAmt = 100 // First establish a channel with a capacity equals to the overall @@ -247,7 +245,7 @@ func runAsyncPayments(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { // Alice should send all money from her side to Bob. channelCapacity := btcutil.Amount(paymentAmt * 2000) chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: channelCapacity}, + alice, bob, lntest.OpenChannelParams{Amt: channelCapacity}, ) info := ht.QueryChannelByChanPoint(alice, chanPoint) @@ -273,7 +271,7 @@ func runAsyncPayments(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { settled := make(chan struct{}) defer close(settled) - timeout := lntest.AsyncBenchmarkTimeout * 2 + timeout := wait.AsyncBenchmarkTimeout for i := 0; i < numInvoices; i++ { payReq := bobPayReqs[i] go func() { @@ -304,46 +302,13 @@ func runAsyncPayments(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { // All payments have been sent, mark the finish time. timeTaken := time.Since(now) - // assertChannelState asserts the channel state by checking the values - // in fields, LocalBalance, RemoteBalance and num of PendingHtlcs. - assertChannelState := func(hn *node.HarnessNode, cp *lnrpc.ChannelPoint, - localBalance, remoteBalance int64, numPendingHtlcs int) { - - // Get the funding point. - err := wait.NoError(func() error { - // Find the target channel first. - target := ht.GetChannelByChanPoint(hn, cp) - - if len(target.PendingHtlcs) != numPendingHtlcs { - return fmt.Errorf("pending htlcs is "+ - "incorrect, got %v, expected %v", - len(target.PendingHtlcs), 0) - } - - if target.LocalBalance != localBalance { - return fmt.Errorf("local balance is "+ - "incorrect, got %v, expected %v", - target.LocalBalance, localBalance) - } - - if target.RemoteBalance != remoteBalance { - return fmt.Errorf("remote balance is "+ - "incorrect, got %v, expected %v", - target.RemoteBalance, remoteBalance) - } - - return nil - }, lntemp.DefaultTimeout) - require.NoError(ht, err, "timeout while chekcing for balance") - } - // Wait for the revocation to be received so alice no longer has // pending htlcs listed and has correct balances. This is needed due to // the fact that we now pipeline the settles. - assertChannelState(alice, chanPoint, aliceAmt, bobAmt, 0) + assertChannelState(ht, alice, chanPoint, aliceAmt, bobAmt) // Wait for Bob to receive revocation from Alice. - assertChannelState(bob, chanPoint, bobAmt, aliceAmt, 0) + assertChannelState(ht, bob, chanPoint, bobAmt, aliceAmt) ht.Log("\tBenchmark info: Elapsed time: ", timeTaken) ht.Log("\tBenchmark info: TPS: ", @@ -357,28 +322,41 @@ func runAsyncPayments(ht *lntemp.HarnessTest, alice, bob *node.HarnessNode) { // testBidirectionalAsyncPayments tests that nodes are able to send the // payments to each other in async manner without blocking. -func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) { - ctxb := context.Background() +func testBidirectionalAsyncPayments(ht *lntest.HarnessTest) { + const paymentAmt = 1000 - const ( - paymentAmt = 1000 - ) + // We use new nodes here as the benchmark test creates lots of data + // which can be costly to be carried on. + args := []string{ + // Increase the dust threshold to avoid the payments fail due + // to threshold limit reached. + "--dust-threshold=5000000", + + // Increase the pending commit interval since there are lots of + // commitment dances. + "--pending-commit-interval=5m", + + // Increase the mailbox delivery timeout as there are lots of + // ADDs going on. + "--htlcswitch.mailboxdeliverytimeout=2m", + } + alice := ht.NewNode("Alice", args) + bob := ht.NewNode("Bob", args) + + ht.EnsureConnected(alice, bob) + ht.FundCoins(btcutil.SatoshiPerBitcoin, alice) // First establish a channel with a capacity equals to the overall // amount of payments, between Alice and Bob, at the end of the test // Alice should send all money from her side to Bob. - chanPoint := openChannelAndAssert( - t, net, net.Alice, net.Bob, - lntest.OpenChannelParams{ + chanPoint := ht.OpenChannel( + alice, bob, lntest.OpenChannelParams{ Amt: paymentAmt * 2000, PushAmt: paymentAmt * 1000, }, ) - info, err := getChanInfo(net.Alice) - if err != nil { - t.Fatalf("unable to get alice channel info: %v", err) - } + info := ht.QueryChannelByChanPoint(alice, chanPoint) // We'll create a number of invoices equal the max number of HTLCs that // can be carried in one direction. The number on the commitment will @@ -394,158 +372,67 @@ func testBidirectionalAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) // With the channel open, we'll create invoices for Bob that Alice // will pay to in order to advance the state of the channel. - bobPayReqs, _, _, err := createPayReqs( - net.Bob, paymentAmt, numInvoices, - ) - if err != nil { - t.Fatalf("unable to create pay reqs: %v", err) - } + bobPayReqs, _, _ := ht.CreatePayReqs(bob, paymentAmt, numInvoices) // With the channel open, we'll create invoices for Alice that Bob // will pay to in order to advance the state of the channel. - alicePayReqs, _, _, err := createPayReqs( - net.Alice, paymentAmt, numInvoices, - ) - if err != nil { - t.Fatalf("unable to create pay reqs: %v", err) - } - - // Wait for Alice to receive the channel edge from the funding manager. - if err = net.Alice.WaitForNetworkChannelOpen(chanPoint); err != nil { - t.Fatalf("alice didn't see the alice->bob channel before "+ - "timeout: %v", err) - } - if err = net.Bob.WaitForNetworkChannelOpen(chanPoint); err != nil { - t.Fatalf("bob didn't see the bob->alice channel before "+ - "timeout: %v", err) - } + alicePayReqs, _, _ := ht.CreatePayReqs(alice, paymentAmt, numInvoices) // Reset mission control to prevent previous payment results from // interfering with this test. A new channel has been opened, but // mission control operates on node pairs. - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - _, err = net.Alice.RouterClient.ResetMissionControl( - ctxt, &routerrpc.ResetMissionControlRequest{}, - ) - if err != nil { - t.Fatalf("unable to reset mc for alice: %v", err) - } + alice.RPC.ResetMissionControl() // Send payments from Alice to Bob and from Bob to Alice in async // manner. - errChan := make(chan error) - statusChan := make(chan *lnrpc.Payment) + settled := make(chan struct{}) + defer close(settled) - send := func(node *lntest.HarnessNode, payReq string) { - go func() { - ctxt, _ = context.WithTimeout( - ctxb, lntest.AsyncBenchmarkTimeout, - ) - stream, err := node.RouterClient.SendPaymentV2( - ctxt, - &routerrpc.SendPaymentRequest{ - PaymentRequest: payReq, - TimeoutSeconds: 60, - FeeLimitMsat: noFeeLimitMsat, - }, - ) - if err != nil { - errChan <- err - } - result, err := getPaymentResult(stream) - if err != nil { - errChan <- err - } + timeout := wait.AsyncBenchmarkTimeout * 2 + send := func(node *node.HarnessNode, payReq string) { + req := &routerrpc.SendPaymentRequest{ + PaymentRequest: payReq, + TimeoutSeconds: int32(timeout.Seconds()), + FeeLimitMsat: noFeeLimitMsat, + } + // AssertPaymentStatusWithTimeout will assert that the + // payment is settled. + stream := node.RPC.SendPayment(req) + ht.AssertPaymentSucceedWithTimeout(stream, timeout) - statusChan <- result - }() + settled <- struct{}{} } for i := 0; i < numInvoices; i++ { - send(net.Bob, alicePayReqs[i]) - send(net.Alice, bobPayReqs[i]) + go send(bob, alicePayReqs[i]) + go send(alice, bobPayReqs[i]) } // Expect all payments to succeed. + timer := time.After(timeout) for i := 0; i < 2*numInvoices; i++ { select { - case result := <-statusChan: - if result.Status != lnrpc.Payment_SUCCEEDED { - t.Fatalf("payment error: %v", result.Status) - } - - case err := <-errChan: - t.Fatalf("payment error: %v", err) + case <-settled: + case <-timer: + require.Fail(ht, "timeout", "wait payment failed") } } // Wait for Alice and Bob to receive revocations messages, and update // states, i.e. balance info. - err = wait.NoError(func() error { - aliceInfo, err := getChanInfo(net.Alice) - if err != nil { - t.Fatalf("unable to get alice's channel info: %v", err) - } - - if aliceInfo.RemoteBalance != bobAmt { - return fmt.Errorf("alice's remote balance is incorrect, "+ - "got %v, expected %v", aliceInfo.RemoteBalance, - bobAmt) - } - - if aliceInfo.LocalBalance != aliceAmt { - return fmt.Errorf("alice's local balance is incorrect, "+ - "got %v, expected %v", aliceInfo.LocalBalance, - aliceAmt) - } - - if len(aliceInfo.PendingHtlcs) != 0 { - return fmt.Errorf("alice's pending htlcs is incorrect, "+ - "got %v expected %v", - len(aliceInfo.PendingHtlcs), 0) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) + assertChannelState(ht, alice, chanPoint, aliceAmt, bobAmt) // Next query for Bob's and Alice's channel states, in order to confirm // that all payment have been successful transmitted. - err = wait.NoError(func() error { - bobInfo, err := getChanInfo(net.Bob) - if err != nil { - t.Fatalf("unable to get bob's channel info: %v", err) - } - - if bobInfo.LocalBalance != bobAmt { - return fmt.Errorf("bob's local balance is incorrect, "+ - "got %v, expected %v", bobInfo.LocalBalance, - bobAmt) - } - - if bobInfo.RemoteBalance != aliceAmt { - return fmt.Errorf("bob's remote balance is incorrect, "+ - "got %v, expected %v", bobInfo.RemoteBalance, - aliceAmt) - } - - if len(bobInfo.PendingHtlcs) != 0 { - return fmt.Errorf("bob's pending htlcs is incorrect, "+ - "got %v, expected %v", - len(bobInfo.PendingHtlcs), 0) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) + assertChannelState(ht, bob, chanPoint, bobAmt, aliceAmt) // Finally, immediately close the channel. This function will also // block until the channel is closed and will additionally assert the // relevant channel closing post conditions. - closeChannelAndAssert(t, net, net.Alice, chanPoint, false) + ht.CloseChannel(alice, chanPoint) } -func testInvoiceSubscriptions(ht *lntemp.HarnessTest) { +func testInvoiceSubscriptions(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(500000) alice, bob := ht.Alice, ht.Bob @@ -558,7 +445,7 @@ func testInvoiceSubscriptions(ht *lntemp.HarnessTest) { // Open a channel with 500k satoshis between Alice and Bob with Alice // being the sole funder of the channel. chanPoint := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // Next create a new invoice for Bob requesting 1k satoshis. @@ -672,3 +559,36 @@ func testInvoiceSubscriptions(ht *lntemp.HarnessTest) { ht.CloseChannel(alice, chanPoint) } + +// assertChannelState asserts the channel state by checking the values in +// fields, LocalBalance, RemoteBalance and num of PendingHtlcs. +func assertChannelState(ht *lntest.HarnessTest, hn *node.HarnessNode, + cp *lnrpc.ChannelPoint, localBalance, remoteBalance int64) { + + // Get the funding point. + err := wait.NoError(func() error { + // Find the target channel first. + target := ht.GetChannelByChanPoint(hn, cp) + + if len(target.PendingHtlcs) != 0 { + return fmt.Errorf("pending htlcs is "+ + "incorrect, got %v, expected %v", + len(target.PendingHtlcs), 0) + } + + if target.LocalBalance != localBalance { + return fmt.Errorf("local balance is "+ + "incorrect, got %v, expected %v", + target.LocalBalance, localBalance) + } + + if target.RemoteBalance != remoteBalance { + return fmt.Errorf("remote balance is "+ + "incorrect, got %v, expected %v", + target.RemoteBalance, remoteBalance) + } + + return nil + }, lntest.DefaultTimeout) + require.NoError(ht, err, "timeout while chekcing for balance") +} diff --git a/lntest/itest/lnd_psbt_test.go b/itest/lnd_psbt_test.go similarity index 89% rename from lntest/itest/lnd_psbt_test.go rename to itest/lnd_psbt_test.go index 286abad6e..3d3b38c5f 100644 --- a/lntest/itest/lnd_psbt_test.go +++ b/itest/lnd_psbt_test.go @@ -2,8 +2,6 @@ package itest import ( "bytes" - "context" - "fmt" "time" "github.com/btcsuite/btcd/btcec/v2" @@ -20,16 +18,15 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) // testPsbtChanFunding makes sure a channel can be opened between carol and dave // by using a Partially Signed Bitcoin Transaction that funds the channel // multisig funding output. -func testPsbtChanFunding(ht *lntemp.HarnessTest) { +func testPsbtChanFunding(ht *lntest.HarnessTest) { // First, we'll create two new nodes that we'll use to open channels // between for this test. Dave gets some coins that will be used to // fund the PSBT, just to make sure that Carol has an empty wallet. @@ -42,7 +39,7 @@ func testPsbtChanFunding(ht *lntemp.HarnessTest) { // runPsbtChanFunding makes sure a channel can be opened between carol and dave // by using a Partially Signed Bitcoin Transaction that funds the channel // multisig funding output. -func runPsbtChanFunding(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode) { +func runPsbtChanFunding(ht *lntest.HarnessTest, carol, dave *node.HarnessNode) { const chanSize = funding.MaxBtcFundingAmount ht.FundCoins(btcutil.SatoshiPerBitcoin, dave) @@ -64,7 +61,7 @@ func runPsbtChanFunding(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode) { // by specifying a PSBT shim. We use the NoPublish flag here to avoid // publishing the whole batch TX too early. chanUpdates, tempPsbt := ht.OpenChannelPsbt( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -81,7 +78,7 @@ func runPsbtChanFunding(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode) { // Alice. We will publish the batch TX once this channel funding is // complete. chanUpdates2, psbtBytes2 := ht.OpenChannelPsbt( - carol, alice, lntemp.OpenChannelParams{ + carol, alice, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -231,7 +228,7 @@ func runPsbtChanFunding(ht *lntemp.HarnessTest, carol, dave *node.HarnessNode) { // and dave by using a Partially Signed Bitcoin Transaction that funds the // channel multisig funding output and is fully funded by an external third // party. -func testPsbtChanFundingExternal(ht *lntemp.HarnessTest) { +func testPsbtChanFundingExternal(ht *lntest.HarnessTest) { const chanSize = funding.MaxBtcFundingAmount // First, we'll create two new nodes that we'll use to open channels @@ -258,7 +255,7 @@ func testPsbtChanFundingExternal(ht *lntemp.HarnessTest) { // by specifying a PSBT shim. We use the NoPublish flag here to avoid // publishing the whole batch TX too early. chanUpdates, tempPsbt := ht.OpenChannelPsbt( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -275,7 +272,7 @@ func testPsbtChanFundingExternal(ht *lntemp.HarnessTest) { // Alice. We will publish the batch TX once this channel funding is // complete. chanUpdates2, psbtBytes2 := ht.OpenChannelPsbt( - carol, alice, lntemp.OpenChannelParams{ + carol, alice, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -413,19 +410,16 @@ func testPsbtChanFundingExternal(ht *lntemp.HarnessTest) { // the wallet of both nodes are empty and one of them uses PSBT and an external // wallet to fund the channel while creating reserve output in the same // transaction. -func testPsbtChanFundingSingleStep(ht *lntemp.HarnessTest) { +func testPsbtChanFundingSingleStep(ht *lntest.HarnessTest) { const chanSize = funding.MaxBtcFundingAmount - args := nodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) + args := lntest.NodeArgsForCommitType(lnrpc.CommitmentType_ANCHORS) // First, we'll create two new nodes that we'll use to open channels // between for this test. But in this case both nodes have an empty // wallet. carol := ht.NewNode("carol", args) - defer ht.Shutdown(carol) - dave := ht.NewNode("dave", args) - defer ht.Shutdown(dave) alice := ht.Alice ht.FundCoins(btcutil.SatoshiPerBitcoin, alice) @@ -454,7 +448,7 @@ func testPsbtChanFundingSingleStep(ht *lntemp.HarnessTest) { // Now that we have the pending channel ID, Carol will open the channel // by specifying a PSBT shim. chanUpdates, tempPsbt := ht.OpenChannelPsbt( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanSize, FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -576,7 +570,7 @@ func testPsbtChanFundingSingleStep(ht *lntemp.HarnessTest) { } // testSignPsbt tests that the SignPsbt RPC works correctly. -func testSignPsbt(ht *lntemp.HarnessTest) { +func testSignPsbt(ht *lntest.HarnessTest) { runSignPsbtSegWitV0P2WKH(ht, ht.Alice) runSignPsbtSegWitV0NP2WKH(ht, ht.Alice) runSignPsbtSegWitV1KeySpendBip86(ht, ht.Alice) @@ -591,7 +585,7 @@ func testSignPsbt(ht *lntemp.HarnessTest) { // runSignPsbtSegWitV0P2WKH tests that the SignPsbt RPC works correctly for a // SegWit v0 p2wkh input. -func runSignPsbtSegWitV0P2WKH(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func runSignPsbtSegWitV0P2WKH(ht *lntest.HarnessTest, alice *node.HarnessNode) { // We test that we can sign a PSBT that spends funds from an input that // the wallet doesn't know about. To set up that test case, we first // derive an address manually that the wallet won't be watching on @@ -670,7 +664,7 @@ func runSignPsbtSegWitV0P2WKH(ht *lntemp.HarnessTest, alice *node.HarnessNode) { // runSignPsbtSegWitV0NP2WKH tests that the SignPsbt RPC works correctly for a // SegWit v0 np2wkh input. -func runSignPsbtSegWitV0NP2WKH(ht *lntemp.HarnessTest, +func runSignPsbtSegWitV0NP2WKH(ht *lntest.HarnessTest, alice *node.HarnessNode) { // We test that we can sign a PSBT that spends funds from an input that @@ -759,7 +753,7 @@ func runSignPsbtSegWitV0NP2WKH(ht *lntemp.HarnessTest, // runSignPsbtSegWitV1KeySpendBip86 tests that the SignPsbt RPC works correctly // for a SegWit v1 p2tr key spend BIP-0086 input. -func runSignPsbtSegWitV1KeySpendBip86(ht *lntemp.HarnessTest, +func runSignPsbtSegWitV1KeySpendBip86(ht *lntest.HarnessTest, alice *node.HarnessNode) { // Derive a key we can use for signing. @@ -803,7 +797,7 @@ func runSignPsbtSegWitV1KeySpendBip86(ht *lntemp.HarnessTest, // runSignPsbtSegWitV1KeySpendRootHash tests that the SignPsbt RPC works // correctly for a SegWit v1 p2tr key spend that also commits to a script tree // root hash. -func runSignPsbtSegWitV1KeySpendRootHash(ht *lntemp.HarnessTest, +func runSignPsbtSegWitV1KeySpendRootHash(ht *lntest.HarnessTest, alice *node.HarnessNode) { // Derive a key we can use for signing. @@ -850,7 +844,7 @@ func runSignPsbtSegWitV1KeySpendRootHash(ht *lntemp.HarnessTest, // runSignPsbtSegWitV1ScriptSpend tests that the SignPsbt RPC works correctly // for a SegWit v1 p2tr script spend. -func runSignPsbtSegWitV1ScriptSpend(ht *lntemp.HarnessTest, +func runSignPsbtSegWitV1ScriptSpend(ht *lntest.HarnessTest, alice *node.HarnessNode) { // Derive a key we can use for signing. @@ -911,7 +905,7 @@ func runSignPsbtSegWitV1ScriptSpend(ht *lntemp.HarnessTest, // runFundAndSignPsbt makes sure we can sign PSBTs that were funded by our // internal wallet. -func runFundAndSignPsbt(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func runFundAndSignPsbt(ht *lntest.HarnessTest, alice *node.HarnessNode) { alice.AddToLogf("================ runFundAndSignPsbt ===============") // We'll be using a "main" address where we send the funds to and from @@ -965,7 +959,7 @@ func runFundAndSignPsbt(ht *lntemp.HarnessTest, alice *node.HarnessNode) { // assertPsbtSpend creates an output with the given pkScript on chain and then // attempts to create a sweep transaction that is signed using the SignPsbt RPC // that spends that output again. -func assertPsbtSpend(ht *lntemp.HarnessTest, alice *node.HarnessNode, +func assertPsbtSpend(ht *lntest.HarnessTest, alice *node.HarnessNode, pkScript []byte, decorateUnsigned func(*psbt.Packet), verifySigned func(*psbt.Packet)) { @@ -1074,7 +1068,7 @@ func assertPsbtSpend(ht *lntemp.HarnessTest, alice *node.HarnessNode, // assertPsbtFundSignSpend funds a PSBT from the internal wallet and then // attempts to sign it by using the SignPsbt or FinalizePsbt method. -func assertPsbtFundSignSpend(ht *lntemp.HarnessTest, alice *node.HarnessNode, +func assertPsbtFundSignSpend(ht *lntest.HarnessTest, alice *node.HarnessNode, fundOutputs map[string]uint64, changeType walletrpc.ChangeAddressType, useFinalize bool) { @@ -1149,7 +1143,7 @@ func assertPsbtFundSignSpend(ht *lntemp.HarnessTest, alice *node.HarnessNode, // assertChangeScriptType checks if the given script has the right type given // the change address type we used in FundPsbt. By default, the script should // be a P2WPKH one. -func assertChangeScriptType(ht *lntemp.HarnessTest, script []byte, +func assertChangeScriptType(ht *lntest.HarnessTest, script []byte, fundChangeType walletrpc.ChangeAddressType) { switch fundChangeType { @@ -1163,7 +1157,7 @@ func assertChangeScriptType(ht *lntemp.HarnessTest, script []byte, // deriveInternalKey derives a signing key and returns its descriptor, full // derivation path and parsed public key. -func deriveInternalKey(ht *lntemp.HarnessTest, +func deriveInternalKey(ht *lntest.HarnessTest, alice *node.HarnessNode) (*signrpc.KeyDescriptor, *btcec.PublicKey, []uint32) { @@ -1187,96 +1181,9 @@ func deriveInternalKey(ht *lntemp.HarnessTest, return keyDesc, parsedPubKey, fullDerivationPath } -// openChannelPsbt attempts to open a channel between srcNode and destNode with -// the passed channel funding parameters. If the passed context has a timeout, -// then if the timeout is reached before the channel pending notification is -// received, an error is returned. An error is returned if the expected step -// of funding the PSBT is not received from the source node. -func openChannelPsbt(ctx context.Context, srcNode, destNode *lntest.HarnessNode, - p lntest.OpenChannelParams) (lnrpc.Lightning_OpenChannelClient, []byte, - error) { - - // Wait until srcNode and destNode have the latest chain synced. - // Otherwise, we may run into a check within the funding manager that - // prevents any funding workflows from being kicked off if the chain - // isn't yet synced. - if err := srcNode.WaitForBlockchainSync(); err != nil { - return nil, nil, fmt.Errorf("unable to sync srcNode chain: %v", - err) - } - if err := destNode.WaitForBlockchainSync(); err != nil { - return nil, nil, fmt.Errorf("unable to sync destNode chain: %v", - err) - } - - // Send the request to open a channel to the source node now. This will - // open a long-lived stream where we'll receive status updates about the - // progress of the channel. - respStream, err := srcNode.OpenChannel(ctx, &lnrpc.OpenChannelRequest{ - NodePubkey: destNode.PubKey[:], - LocalFundingAmount: int64(p.Amt), - PushSat: int64(p.PushAmt), - Private: p.Private, - SpendUnconfirmed: p.SpendUnconfirmed, - MinHtlcMsat: int64(p.MinHtlc), - FundingShim: p.FundingShim, - }) - if err != nil { - return nil, nil, fmt.Errorf("unable to open channel between "+ - "source and dest: %v", err) - } - - // Consume the "PSBT funding ready" update. This waits until the node - // notifies us that the PSBT can now be funded. - resp, err := receiveChanUpdate(ctx, respStream) - if err != nil { - return nil, nil, fmt.Errorf("unable to consume channel update "+ - "message: %v", err) - } - upd, ok := resp.Update.(*lnrpc.OpenStatusUpdate_PsbtFund) - if !ok { - return nil, nil, fmt.Errorf("expected PSBT funding update, "+ - "instead got %v", resp) - } - return respStream, upd.PsbtFund.Psbt, nil -} - -// receiveChanUpdate waits until a message is received on the stream or the -// context is canceled. The context must have a timeout or must be canceled -// in case no message is received, otherwise this function will block forever. -func receiveChanUpdate(ctx context.Context, - stream lnrpc.Lightning_OpenChannelClient) (*lnrpc.OpenStatusUpdate, - error) { - - chanMsg := make(chan *lnrpc.OpenStatusUpdate) - errChan := make(chan error) - go func() { - // Consume one message. This will block until the message is - // received. - resp, err := stream.Recv() - if err != nil { - errChan <- err - return - } - chanMsg <- resp - }() - - select { - case <-ctx.Done(): - return nil, fmt.Errorf("timeout reached before chan pending " + - "update sent") - - case err := <-errChan: - return nil, err - - case updateMsg := <-chanMsg: - return updateMsg, nil - } -} - // sendAllCoinsToAddrType sweeps all coins from the wallet and sends them to a // new address of the given type. -func sendAllCoinsToAddrType(ht *lntemp.HarnessTest, +func sendAllCoinsToAddrType(ht *lntest.HarnessTest, hn *node.HarnessNode, addrType lnrpc.AddressType) { resp := hn.RPC.NewAddress(&lnrpc.NewAddressRequest{ diff --git a/lntest/itest/lnd_recovery_test.go b/itest/lnd_recovery_test.go similarity index 98% rename from lntest/itest/lnd_recovery_test.go rename to itest/lnd_recovery_test.go index 4ba521912..ef251f042 100644 --- a/lntest/itest/lnd_recovery_test.go +++ b/itest/lnd_recovery_test.go @@ -15,8 +15,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" @@ -24,7 +24,7 @@ import ( // testGetRecoveryInfo checks whether lnd gives the right information about // the wallet recovery process. -func testGetRecoveryInfo(ht *lntemp.HarnessTest) { +func testGetRecoveryInfo(ht *lntest.HarnessTest) { // First, create a new node with strong passphrase and grab the mnemonic // used for key derivation. This will bring up Carol with an empty // wallet, and such that she is synced up. @@ -94,7 +94,7 @@ func testGetRecoveryInfo(ht *lntemp.HarnessTest) { // when providing a valid aezeed that owns outputs on the chain. This test // performs multiple restorations using the same seed and various recovery // windows to ensure we detect funds properly. -func testOnchainFundRecovery(ht *lntemp.HarnessTest) { +func testOnchainFundRecovery(ht *lntest.HarnessTest) { // First, create a new node with strong passphrase and grab the mnemonic // used for key derivation. This will bring up Carol with an empty // wallet, and such that she is synced up. @@ -321,7 +321,7 @@ func testOnchainFundRecovery(ht *lntemp.HarnessTest) { // The fix in the wallet is simple: In step 6, don't detect addresses from // internal scopes while re-scanning to be in line with the logic in other areas // of the wallet code. -func testRescanAddressDetection(ht *lntemp.HarnessTest) { +func testRescanAddressDetection(ht *lntest.HarnessTest) { // We start off by creating a new node with the wallet re-scan flag // enabled. This won't have any effect on the first startup but will // come into effect after we re-start the node. diff --git a/lntest/itest/lnd_remote_signer_test.go b/itest/lnd_remote_signer_test.go similarity index 89% rename from lntest/itest/lnd_remote_signer_test.go rename to itest/lnd_remote_signer_test.go index 8b7a2b6a6..45f806cfd 100644 --- a/lntest/itest/lnd_remote_signer_test.go +++ b/itest/lnd_remote_signer_test.go @@ -11,8 +11,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) @@ -55,24 +55,24 @@ var ( // testRemoteSigner tests that a watch-only wallet can use a remote signing // wallet to perform any signing or ECDH operations. -func testRemoteSigner(ht *lntemp.HarnessTest) { +func testRemoteSigner(ht *lntest.HarnessTest) { type testCase struct { name string randomSeed bool sendCoins bool - fn func(tt *lntemp.HarnessTest, + fn func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) } subTests := []testCase{{ name: "random seed", randomSeed: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { // Nothing more to test here. }, }, { name: "account import", - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runWalletImportAccountScenario( tt, walletrpc.AddressType_WITNESS_PUBKEY_HASH, carol, wo, @@ -81,36 +81,36 @@ func testRemoteSigner(ht *lntemp.HarnessTest) { }, { name: "basic channel open close", sendCoins: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runBasicChannelCreationAndUpdates(tt, wo, carol) }, }, { name: "channel funding input types", sendCoins: false, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runChannelFundingInputTypes(tt, carol, wo) }, }, { name: "async payments", sendCoins: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runAsyncPayments(tt, wo, carol) }, }, { name: "shared key", - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runDeriveSharedKey(tt, wo) }, }, { name: "cpfp", sendCoins: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runCPFP(tt, wo, carol) }, }, { name: "psbt", randomSeed: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runPsbtChanFunding(tt, carol, wo) runSignPsbtSegWitV0P2WKH(tt, wo) runSignPsbtSegWitV1KeySpendBip86(tt, wo) @@ -125,20 +125,20 @@ func testRemoteSigner(ht *lntemp.HarnessTest) { }, { name: "sign output raw", sendCoins: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runSignOutputRaw(tt, wo) }, }, { name: "sign verify msg", sendCoins: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { runSignVerifyMessage(tt, wo) }, }, { name: "taproot", sendCoins: true, randomSeed: true, - fn: func(tt *lntemp.HarnessTest, wo, carol *node.HarnessNode) { + fn: func(tt *lntest.HarnessTest, wo, carol *node.HarnessNode) { testTaprootSendCoinsKeySpendBip86(tt, wo) testTaprootComputeInputScriptKeySpendBip86(tt, wo) testTaprootSignOutputRawScriptSpend(tt, wo) @@ -162,7 +162,7 @@ func testRemoteSigner(ht *lntemp.HarnessTest) { }, }} - prepareTest := func(st *lntemp.HarnessTest, + prepareTest := func(st *lntest.HarnessTest, subTest testCase) (*node.HarnessNode, *node.HarnessNode, *node.HarnessNode) { diff --git a/lntest/itest/lnd_res_handoff_test.go b/itest/lnd_res_handoff_test.go similarity index 95% rename from lntest/itest/lnd_res_handoff_test.go rename to itest/lnd_res_handoff_test.go index a18d2fce0..0a85cd77d 100644 --- a/lntest/itest/lnd_res_handoff_test.go +++ b/itest/lnd_res_handoff_test.go @@ -5,13 +5,13 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) // testResHandoff tests that the contractcourt is able to properly hand-off // resolution messages to the switch. -func testResHandoff(ht *lntemp.HarnessTest) { +func testResHandoff(ht *lntest.HarnessTest) { const ( chanAmt = btcutil.Amount(1000000) paymentAmt = 50000 @@ -22,14 +22,13 @@ func testResHandoff(ht *lntemp.HarnessTest) { // First we'll create a channel between Alice and Bob. ht.EnsureConnected(alice, bob) - params := lntemp.OpenChannelParams{Amt: chanAmt} + params := lntest.OpenChannelParams{Amt: chanAmt} chanPointAlice := ht.OpenChannel(alice, bob, params) // Create a new node Carol that will be in hodl mode. This is used to // trigger the behavior of checkRemoteDanglingActions in the // contractcourt. This will cause Bob to fail the HTLC back to Alice. carol := ht.NewNode("Carol", []string{"--hodl.commit"}) - defer ht.Shutdown(carol) // Connect Bob to Carol. ht.ConnectNodes(bob, carol) diff --git a/lntest/itest/lnd_rest_api_test.go b/itest/lnd_rest_api_test.go similarity index 97% rename from lntest/itest/lnd_rest_api_test.go rename to itest/lnd_rest_api_test.go index 43aff6110..3a9674a35 100644 --- a/lntest/itest/lnd_rest_api_test.go +++ b/itest/lnd_rest_api_test.go @@ -23,8 +23,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/verrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) @@ -56,7 +56,7 @@ var ( // testRestAPI tests that the most important features of the REST API work // correctly. -func testRestAPI(ht *lntemp.HarnessTest) { +func testRestAPI(ht *lntest.HarnessTest) { testCases := []struct { name string run func(*testing.T, *node.HarnessNode, *node.HarnessNode) @@ -201,7 +201,7 @@ func testRestAPI(ht *lntemp.HarnessTest) { }} wsTestCases := []struct { name string - run func(ht *lntemp.HarnessTest) + run func(ht *lntest.HarnessTest) }{{ name: "websocket subscription", run: wsTestCaseSubscription, @@ -243,7 +243,7 @@ func testRestAPI(ht *lntemp.HarnessTest) { } } -func wsTestCaseSubscription(ht *lntemp.HarnessTest) { +func wsTestCaseSubscription(ht *lntest.HarnessTest) { // Find out the current best block so we can subscribe to the next one. hash, height := ht.Miner.GetBestBlock() @@ -317,7 +317,7 @@ func wsTestCaseSubscription(ht *lntemp.HarnessTest) { } } -func wsTestCaseSubscriptionMacaroon(ht *lntemp.HarnessTest) { +func wsTestCaseSubscriptionMacaroon(ht *lntest.HarnessTest) { // Find out the current best block so we can subscribe to the next one. hash, height := ht.Miner.GetBestBlock() @@ -407,7 +407,7 @@ func wsTestCaseSubscriptionMacaroon(ht *lntemp.HarnessTest) { } } -func wsTestCaseBiDirectionalSubscription(ht *lntemp.HarnessTest) { +func wsTestCaseBiDirectionalSubscription(ht *lntest.HarnessTest) { initialRequest := &lnrpc.ChannelAcceptResponse{} url := "/v1/channels/acceptor" @@ -530,7 +530,7 @@ func wsTestCaseBiDirectionalSubscription(ht *lntemp.HarnessTest) { const numChannels = 3 for i := 0; i < numChannels; i++ { chanPoint := ht.OpenChannel( - bob, alice, lntemp.OpenChannelParams{Amt: 500000}, + bob, alice, lntest.OpenChannelParams{Amt: 500000}, ) defer ht.CloseChannel(bob, chanPoint) @@ -545,7 +545,7 @@ func wsTestCaseBiDirectionalSubscription(ht *lntemp.HarnessTest) { } } -func wsTestPingPongTimeout(ht *lntemp.HarnessTest) { +func wsTestPingPongTimeout(ht *lntest.HarnessTest) { initialRequest := &lnrpc.InvoiceSubscription{ AddIndex: 1, SettleIndex: 1, } diff --git a/lntest/itest/lnd_revocation_test.go b/itest/lnd_revocation_test.go similarity index 96% rename from lntest/itest/lnd_revocation_test.go rename to itest/lnd_revocation_test.go index bff6e6760..1ca9ee35e 100644 --- a/lntest/itest/lnd_revocation_test.go +++ b/itest/lnd_revocation_test.go @@ -12,7 +12,6 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc" - "github.com/lightningnetwork/lnd/lntemp" "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" @@ -21,7 +20,7 @@ import ( // testRevokedCloseRetribution tests that Carol is able carry out // retribution in the event that she fails immediately after detecting Bob's // breach txn in the mempool. -func testRevokedCloseRetribution(ht *lntemp.HarnessTest) { +func testRevokedCloseRetribution(ht *lntest.HarnessTest) { const ( chanAmt = funding.MaxBtcFundingAmount paymentAmt = 10000 @@ -51,7 +50,7 @@ func testRevokedCloseRetribution(ht *lntemp.HarnessTest) { // closure by Bob, we'll first open up a channel between them with a // 0.5 BTC value. chanPoint := ht.OpenChannel( - carol, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // With the channel open, we'll create a few invoices for Bob that @@ -158,7 +157,7 @@ func testRevokedCloseRetribution(ht *lntemp.HarnessTest) { // tx's confHeight+CSV-1 blocks and since we've already mined one that // included the justice tx we only need to mine extra DefaultCSV-2 // blocks to unlock it. - ht.MineBlocks(lntest.DefaultCSV - 2) + ht.MineBlocks(defaultCSV - 2) ht.AssertNumPendingForceClose(bob, 0) } @@ -166,7 +165,7 @@ func testRevokedCloseRetribution(ht *lntemp.HarnessTest) { // testRevokedCloseRetributionZeroValueRemoteOutput tests that Dave is able // carry out retribution in the event that he fails in state where the remote // commitment output has zero-value. -func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntemp.HarnessTest) { +func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntest.HarnessTest) { const ( chanAmt = funding.MaxBtcFundingAmount paymentAmt = 10000 @@ -199,7 +198,7 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntemp.HarnessTest) { // closure by Carol, we'll first open up a channel between them with a // 0.5 BTC value. chanPoint := ht.OpenChannel( - dave, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + dave, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) // With the channel open, we'll create a few invoices for Carol that @@ -224,6 +223,9 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntemp.HarnessTest) { // backup. ht.EnsureConnected(dave, carol) + // Once connected, give Dave some time to enable the channel again. + ht.AssertTopologyChannelOpen(dave, chanPoint) + // Finally, send payments from Dave to Carol, consuming Carol's // remaining payment hashes. ht.CompletePaymentRequestsNoWait(dave, carolPayReqs, chanPoint) @@ -298,7 +300,7 @@ func testRevokedCloseRetributionZeroValueRemoteOutput(ht *lntemp.HarnessTest) { // testRevokedCloseRetributionRemoteHodl tests that Dave properly responds to a // channel breach made by the remote party, specifically in the case that the // remote party breaches before settling extended HTLCs. -func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) { +func testRevokedCloseRetributionRemoteHodl(ht *lntest.HarnessTest) { const ( chanAmt = funding.MaxBtcFundingAmount pushAmt = 200000 @@ -333,7 +335,7 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) { // by Carol, we'll first open up a channel between them with a // funding.MaxBtcFundingAmount (2^24) satoshis value. chanPoint := ht.OpenChannel( - dave, carol, lntemp.OpenChannelParams{ + dave, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, }, @@ -401,6 +403,9 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) { // backup. ht.EnsureConnected(dave, carol) + // Once connected, give Dave some time to enable the channel again. + ht.AssertTopologyChannelOpen(dave, chanPoint) + // Finally, send payments from Dave to Carol, consuming Carol's // remaining payment hashes. ht.CompletePaymentRequestsNoWait( @@ -472,7 +477,14 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) { for _, txid := range mempool { // Check that the justice tx has the appropriate number // of inputs. - tx := ht.Miner.GetRawTransaction(txid) + // + // NOTE: We don't use `ht.Miner.GetRawTransaction` + // which asserts a txid must be found as the HTLC + // spending txes might be aggregated. + tx, err := ht.Miner.Client.GetRawTransaction(txid) + if err != nil { + return nil, err + } exNumInputs := 2 + numInvoices if len(tx.MsgTx().TxIn) == exNumInputs { @@ -578,7 +590,7 @@ func testRevokedCloseRetributionRemoteHodl(ht *lntemp.HarnessTest) { // trigger a breach. Carol is kept offline throughout the process and the test // asserts that Willy responds by broadcasting the justice transaction on // Carol's behalf sweeping her funds without a reward. -func testRevokedCloseRetributionAltruistWatchtower(ht *lntemp.HarnessTest) { +func testRevokedCloseRetributionAltruistWatchtower(ht *lntest.HarnessTest) { testCases := []struct { name string anchors bool @@ -592,7 +604,7 @@ func testRevokedCloseRetributionAltruistWatchtower(ht *lntemp.HarnessTest) { for _, tc := range testCases { tc := tc - testFunc := func(ht *lntemp.HarnessTest) { + testFunc := func(ht *lntest.HarnessTest) { testRevokedCloseRetributionAltruistWatchtowerCase( ht, tc.anchors, ) @@ -601,7 +613,7 @@ func testRevokedCloseRetributionAltruistWatchtower(ht *lntemp.HarnessTest) { success := ht.Run(tc.name, func(tt *testing.T) { st := ht.Subtest(tt) - st.RunTestCase(&lntemp.TestCase{ + st.RunTestCase(&lntest.TestCase{ Name: tc.name, TestFunc: testFunc, }) @@ -619,7 +631,7 @@ func testRevokedCloseRetributionAltruistWatchtower(ht *lntemp.HarnessTest) { } } -func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntemp.HarnessTest, +func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntest.HarnessTest, anchors bool) { const ( @@ -696,7 +708,7 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntemp.HarnessTest, // In order to test Dave's response to an uncooperative channel // closure by Carol, we'll first open up a channel between them with a // 0.5 BTC value. - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: 3 * (chanAmt / 4), PushAmt: chanAmt / 4, } @@ -727,6 +739,9 @@ func testRevokedCloseRetributionAltruistWatchtowerCase(ht *lntemp.HarnessTest, // backup. ht.EnsureConnected(dave, carol) + // Once connected, give Dave some time to enable the channel again. + ht.AssertTopologyChannelOpen(dave, chanPoint) + // Finally, send payments from Dave to Carol, consuming Carol's // remaining payment hashes. ht.CompletePaymentRequestsNoWait(dave, carolPayReqs, chanPoint) diff --git a/lntest/itest/lnd_routing_test.go b/itest/lnd_routing_test.go similarity index 95% rename from lntest/itest/lnd_routing_test.go rename to itest/lnd_routing_test.go index 53d9f2b97..a2bb4f4e8 100644 --- a/lntest/itest/lnd_routing_test.go +++ b/itest/lnd_routing_test.go @@ -10,8 +10,8 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -65,7 +65,7 @@ var singleHopSendToRouteCases = []singleHopSendToRouteCase{ // by feeding the route back into the various SendToRoute RPC methods. Here we // test all three SendToRoute endpoints, forcing each to perform both a regular // payment and an MPP payment. -func testSingleHopSendToRoute(ht *lntemp.HarnessTest) { +func testSingleHopSendToRoute(ht *lntest.HarnessTest) { for _, test := range singleHopSendToRouteCases { test := test @@ -76,7 +76,7 @@ func testSingleHopSendToRoute(ht *lntemp.HarnessTest) { } } -func testSingleHopSendToRouteCase(ht *lntemp.HarnessTest, +func testSingleHopSendToRouteCase(ht *lntest.HarnessTest, test singleHopSendToRouteCase) { const chanAmt = btcutil.Amount(100000) @@ -97,7 +97,7 @@ func testSingleHopSendToRouteCase(ht *lntemp.HarnessTest, // Open a channel with 100k satoshis between Carol and Dave with Carol // being the sole funder of the channel. chanPointCarol := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) defer ht.CloseChannel(carol, chanPointCarol) @@ -289,7 +289,7 @@ func testSingleHopSendToRouteCase(ht *lntemp.HarnessTest, // // We'll query the daemon for routes from Alice to Carol and then // send payments through the routes. -func testMultiHopSendToRoute(ht *lntemp.HarnessTest) { +func testMultiHopSendToRoute(ht *lntest.HarnessTest) { ht.Run("with cache", func(tt *testing.T) { st := ht.Subtest(tt) runMultiHopSendToRoute(st, true) @@ -309,7 +309,7 @@ func testMultiHopSendToRoute(ht *lntemp.HarnessTest) { // // We'll query the daemon for routes from Alice to Carol and then // send payments through the routes. -func runMultiHopSendToRoute(ht *lntemp.HarnessTest, useGraphCache bool) { +func runMultiHopSendToRoute(ht *lntest.HarnessTest, useGraphCache bool) { var opts []string if !useGraphCache { opts = append(opts, "--db.no-graph-cache") @@ -325,7 +325,7 @@ func runMultiHopSendToRoute(ht *lntemp.HarnessTest, useGraphCache bool) { // Open a channel with 100k satoshis between Alice and Bob with Alice // being the sole funder of the channel. chanPointAlice := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) defer ht.CloseChannel(alice, chanPointAlice) @@ -337,10 +337,13 @@ func runMultiHopSendToRoute(ht *lntemp.HarnessTest, useGraphCache bool) { ht.ConnectNodes(carol, bob) chanPointBob := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + bob, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) defer ht.CloseChannel(carol, chanPointBob) + // Make sure Alice knows the channel between Bob and Carol. + ht.AssertTopologyChannelOpen(alice, chanPointBob) + // Create 5 invoices for Carol, which expect a payment from Alice for // 1k satoshis with a different preimage each time. const ( @@ -407,14 +410,14 @@ func runMultiHopSendToRoute(ht *lntemp.HarnessTest, useGraphCache bool) { // testSendToRouteErrorPropagation tests propagation of errors that occur // while processing a multi-hop payment through an unknown route. -func testSendToRouteErrorPropagation(ht *lntemp.HarnessTest) { +func testSendToRouteErrorPropagation(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(100000) // Open a channel with 100k satoshis between Alice and Bob with Alice // being the sole funder of the channel. alice, bob := ht.Alice, ht.Bob chanPointAlice := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // Create a new nodes (Carol and Charlie), load her with some funds, @@ -432,7 +435,7 @@ func testSendToRouteErrorPropagation(ht *lntemp.HarnessTest) { ht.FundCoins(btcutil.SatoshiPerBitcoin, charlie) ht.ConnectNodes(carol, charlie) - ht.OpenChannel(carol, charlie, lntemp.OpenChannelParams{Amt: chanAmt}) + ht.OpenChannel(carol, charlie, lntest.OpenChannelParams{Amt: chanAmt}) // Query routes from Carol to Charlie which will be an invalid route // for Alice -> Bob. @@ -476,7 +479,9 @@ func testSendToRouteErrorPropagation(ht *lntemp.HarnessTest) { // testPrivateChannels tests that a private channel can be used for // routing by the two endpoints of the channel, but is not known by // the rest of the nodes in the graph. -func testPrivateChannels(ht *lntemp.HarnessTest) { +// +//nolint:dupword +func testPrivateChannels(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(100000) // We create the following topology: @@ -493,7 +498,7 @@ func testPrivateChannels(ht *lntemp.HarnessTest) { // Open a channel with 200k satoshis between Alice and Bob. alice, bob := ht.Alice, ht.Bob chanPointAlice := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt * 2}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt * 2}, ) // Create Dave, and a channel to Alice of 100k. @@ -502,7 +507,7 @@ func testPrivateChannels(ht *lntemp.HarnessTest) { ht.FundCoins(btcutil.SatoshiPerBitcoin, dave) chanPointDave := ht.OpenChannel( - dave, alice, lntemp.OpenChannelParams{Amt: chanAmt}, + dave, alice, lntest.OpenChannelParams{Amt: chanAmt}, ) // Next, we'll create Carol and establish a channel from her to @@ -512,14 +517,14 @@ func testPrivateChannels(ht *lntemp.HarnessTest) { ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) chanPointCarol := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) // Now create a _private_ channel directly between Carol and // Alice of 100k. ht.ConnectNodes(carol, alice) chanPointPrivate := ht.OpenChannel( - carol, alice, lntemp.OpenChannelParams{ + carol, alice, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -602,7 +607,7 @@ func testPrivateChannels(ht *lntemp.HarnessTest) { // testInvoiceRoutingHints tests that the routing hints for an invoice are // created properly. -func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { +func testInvoiceRoutingHints(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(100000) // Throughout this test, we'll be opening a channel between Alice and @@ -615,7 +620,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { // invoice's payment. alice, bob := ht.Alice, ht.Bob chanPointBob := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, Private: true, @@ -629,7 +634,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { ht.ConnectNodes(alice, carol) chanPointCarol := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{ + alice, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, }, @@ -642,7 +647,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { // that wish to stay unadvertised. ht.ConnectNodes(bob, carol) chanPointBobCarol := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{ + bob, carol, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, }, @@ -656,7 +661,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { ht.ConnectNodes(alice, dave) chanPointDave := ht.OpenChannel( - alice, dave, lntemp.OpenChannelParams{ + alice, dave, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -669,7 +674,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { eve := ht.NewNode("Eve", nil) ht.ConnectNodes(alice, eve) chanPointEve := ht.OpenChannel( - alice, eve, lntemp.OpenChannelParams{ + alice, eve, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, Private: true, @@ -744,7 +749,7 @@ func testInvoiceRoutingHints(ht *lntemp.HarnessTest) { // testMultiHopOverPrivateChannels tests that private channels can be used as // intermediate hops in a route for payments. -func testMultiHopOverPrivateChannels(ht *lntemp.HarnessTest) { +func testMultiHopOverPrivateChannels(ht *lntest.HarnessTest) { // We'll test that multi-hop payments over private channels work as // intended. To do so, we'll create the following topology: // private public private @@ -755,7 +760,7 @@ func testMultiHopOverPrivateChannels(ht *lntemp.HarnessTest) { // being the funder. alice, bob := ht.Alice, ht.Bob chanPointAlice := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -766,7 +771,7 @@ func testMultiHopOverPrivateChannels(ht *lntemp.HarnessTest) { carol := ht.NewNode("Carol", nil) ht.ConnectNodes(bob, carol) chanPointBob := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{ + bob, carol, lntest.OpenChannelParams{ Amt: chanAmt, }, ) @@ -781,7 +786,7 @@ func testMultiHopOverPrivateChannels(ht *lntemp.HarnessTest) { ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) chanPointCarol := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanAmt, Private: true, }, @@ -851,7 +856,7 @@ func testMultiHopOverPrivateChannels(ht *lntemp.HarnessTest) { // Alice --> Bob --> Carol --> Dave // // and query the daemon for routes from Alice to Dave. -func testQueryRoutes(ht *lntemp.HarnessTest) { +func testQueryRoutes(ht *lntest.HarnessTest) { const chanAmt = btcutil.Amount(100000) // Grab Alice and Bob from the standby nodes. @@ -869,8 +874,8 @@ func testQueryRoutes(ht *lntemp.HarnessTest) { // We now proceed to open channels: // Alice=>Bob, Bob=>Carol and Carol=>Dave. - p := lntemp.OpenChannelParams{Amt: chanAmt} - reqs := []*lntemp.OpenChannelRequest{ + p := lntest.OpenChannelParams{Amt: chanAmt} + reqs := []*lntest.OpenChannelRequest{ {Local: alice, Remote: bob, Param: p}, {Local: bob, Remote: carol, Param: p}, {Local: carol, Remote: dave, Param: p}, @@ -1086,7 +1091,7 @@ func testMissionControlCfg(t *testing.T, hn *node.HarnessNode) { // testMissionControlImport tests import of mission control results from an // external source. -func testMissionControlImport(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func testMissionControlImport(ht *lntest.HarnessTest, hn *node.HarnessNode, fromNode, toNode []byte) { // Reset mission control so that our query will return the default @@ -1138,7 +1143,7 @@ func testMissionControlImport(ht *lntemp.HarnessTest, hn *node.HarnessNode, // testRouteFeeCutoff tests that we are able to prevent querying routes and // sending payments that incur a fee higher than the fee limit. -func testRouteFeeCutoff(ht *lntemp.HarnessTest) { +func testRouteFeeCutoff(ht *lntest.HarnessTest) { // For this test, we'll create the following topology: // // --- Bob --- @@ -1155,7 +1160,7 @@ func testRouteFeeCutoff(ht *lntemp.HarnessTest) { // Open a channel between Alice and Bob. alice, bob := ht.Alice, ht.Bob chanPointAliceBob := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // Create Carol's node and open a channel between her and Alice with @@ -1165,7 +1170,7 @@ func testRouteFeeCutoff(ht *lntemp.HarnessTest) { ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) chanPointAliceCarol := ht.OpenChannel( - alice, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) // Create Dave's node and open a channel between him and Bob with Bob @@ -1173,13 +1178,13 @@ func testRouteFeeCutoff(ht *lntemp.HarnessTest) { dave := ht.NewNode("Dave", nil) ht.ConnectNodes(dave, bob) chanPointBobDave := ht.OpenChannel( - bob, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + bob, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) // Open a channel between Carol and Dave. ht.ConnectNodes(carol, dave) chanPointCarolDave := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{Amt: chanAmt}, + carol, dave, lntest.OpenChannelParams{Amt: chanAmt}, ) // Now that all the channels were set up, we'll wait for all the nodes @@ -1203,7 +1208,7 @@ func testRouteFeeCutoff(ht *lntemp.HarnessTest) { baseFee := int64(10000) feeRate := int64(5) timeLockDelta := uint32(chainreg.DefaultBitcoinTimeLockDelta) - maxHtlc := calculateMaxHtlc(chanAmt) + maxHtlc := lntest.CalculateMaxHtlc(chanAmt) expectedPolicy := &lnrpc.RoutingPolicy{ FeeBaseMsat: baseFee, @@ -1285,7 +1290,8 @@ func testRouteFeeCutoff(ht *lntemp.HarnessTest) { case *lnrpc.FeeLimit_Fixed: sendReq.FeeLimitMsat = 1000 * limit.Fixed case *lnrpc.FeeLimit_Percent: - sendReq.FeeLimitMsat = 1000 * paymentAmt * limit.Percent / 100 + sendReq.FeeLimitMsat = 1000 * paymentAmt * + limit.Percent / 100 } result := ht.SendPaymentAssertSettled(alice, sendReq) diff --git a/lntest/itest/lnd_rpc_middleware_interceptor_test.go b/itest/lnd_rpc_middleware_interceptor_test.go similarity index 99% rename from lntest/itest/lnd_rpc_middleware_interceptor_test.go rename to itest/lnd_rpc_middleware_interceptor_test.go index f9a793db4..7c99f6f20 100644 --- a/lntest/itest/lnd_rpc_middleware_interceptor_test.go +++ b/itest/lnd_rpc_middleware_interceptor_test.go @@ -8,8 +8,8 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/zpay32" "github.com/stretchr/testify/require" @@ -19,7 +19,7 @@ import ( // testRPCMiddlewareInterceptor tests that the RPC middleware interceptor can // be used correctly and in a safe way. -func testRPCMiddlewareInterceptor(ht *lntemp.HarnessTest) { +func testRPCMiddlewareInterceptor(ht *lntest.HarnessTest) { // Let's first enable the middleware interceptor. // // NOTE: we cannot use standby nodes here as the test messes with @@ -32,7 +32,7 @@ func testRPCMiddlewareInterceptor(ht *lntemp.HarnessTest) { // data to inspect when doing RPC calls to Alice later. ht.EnsureConnected(alice, bob) ht.FundCoins(btcutil.SatoshiPerBitcoin, alice) - ht.OpenChannel(alice, bob, lntemp.OpenChannelParams{Amt: 1_234_567}) + ht.OpenChannel(alice, bob, lntest.OpenChannelParams{Amt: 1_234_567}) // Load or bake the macaroons that the simulated users will use to // access the RPC. @@ -545,7 +545,7 @@ func middlewareRequestManipulationTest(t *testing.T, node *node.HarnessNode, // middlewareMandatoryTest tests that all RPC requests are blocked if there is // a mandatory middleware declared that's currently not registered. -func middlewareMandatoryTest(ht *lntemp.HarnessTest, node *node.HarnessNode) { +func middlewareMandatoryTest(ht *lntest.HarnessTest, node *node.HarnessNode) { // Let's declare our itest interceptor as mandatory but don't register // it just yet. That should cause all RPC requests to fail, except for // the registration itself. diff --git a/lntest/itest/lnd_send_multi_path_payment_test.go b/itest/lnd_send_multi_path_payment_test.go similarity index 96% rename from lntest/itest/lnd_send_multi_path_payment_test.go rename to itest/lnd_send_multi_path_payment_test.go index 0e5cfc24a..143cf496d 100644 --- a/lntest/itest/lnd_send_multi_path_payment_test.go +++ b/itest/lnd_send_multi_path_payment_test.go @@ -6,13 +6,13 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) // testSendMultiPathPayment tests that we are able to successfully route a // payment using multiple shards across different paths. -func testSendMultiPathPayment(ht *lntemp.HarnessTest) { +func testSendMultiPathPayment(ht *lntest.HarnessTest) { mts := newMppTestScenario(ht) const paymentAmt = btcutil.Amount(300000) diff --git a/lntest/itest/lnd_signer_test.go b/itest/lnd_signer_test.go similarity index 96% rename from lntest/itest/lnd_signer_test.go rename to itest/lnd_signer_test.go index ce16240a3..1372e4502 100644 --- a/lntest/itest/lnd_signer_test.go +++ b/itest/lnd_signer_test.go @@ -13,8 +13,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) @@ -22,7 +22,7 @@ import ( // DeriveSharedKey. It creates an ephemeral private key, performing an ECDH with // the node's pubkey and a customized public key to check the validity of the // result. -func testDeriveSharedKey(ht *lntemp.HarnessTest) { +func testDeriveSharedKey(ht *lntest.HarnessTest) { runDeriveSharedKey(ht, ht.Alice) } @@ -30,7 +30,7 @@ func testDeriveSharedKey(ht *lntemp.HarnessTest) { // DeriveSharedKey. It creates an ephemeral private key, performing an ECDH with // the node's pubkey and a customized public key to check the validity of the // result. -func runDeriveSharedKey(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func runDeriveSharedKey(ht *lntest.HarnessTest, alice *node.HarnessNode) { // Create an ephemeral key, extracts its public key, and make a // PrivKeyECDH using the ephemeral key. ephemeralPriv, err := btcec.NewPrivateKey() @@ -194,13 +194,13 @@ func runDeriveSharedKey(ht *lntemp.HarnessTest, alice *node.HarnessNode) { // testSignOutputRaw makes sure that the SignOutputRaw RPC can be used with all // custom ways of specifying the signing key in the key descriptor/locator. -func testSignOutputRaw(ht *lntemp.HarnessTest) { +func testSignOutputRaw(ht *lntest.HarnessTest) { runSignOutputRaw(ht, ht.Alice) } // runSignOutputRaw makes sure that the SignOutputRaw RPC can be used with all // custom ways of specifying the signing key in the key descriptor/locator. -func runSignOutputRaw(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func runSignOutputRaw(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family // for this. We want this to be an index of zero. const testCustomKeyFamily = 44 @@ -272,7 +272,7 @@ func runSignOutputRaw(ht *lntemp.HarnessTest, alice *node.HarnessNode) { // assertSignOutputRaw sends coins to a p2wkh address derived from the given // target public key and then tries to spend that output again by invoking the // SignOutputRaw RPC with the key descriptor provided. -func assertSignOutputRaw(ht *lntemp.HarnessTest, +func assertSignOutputRaw(ht *lntest.HarnessTest, alice *node.HarnessNode, targetPubKey *btcec.PublicKey, keyDesc *signrpc.KeyDescriptor, sigHash txscript.SigHashType) { @@ -373,14 +373,14 @@ func assertSignOutputRaw(ht *lntemp.HarnessTest, // testSignVerifyMessage makes sure that the SignMessage RPC can be used with // all custom flags by verifying with VerifyMessage. Tests both ECDSA and // Schnorr signatures. -func testSignVerifyMessage(ht *lntemp.HarnessTest) { +func testSignVerifyMessage(ht *lntest.HarnessTest) { runSignVerifyMessage(ht, ht.Alice) } // runSignVerifyMessage makes sure that the SignMessage RPC can be used with // all custom flags by verifying with VerifyMessage. Tests both ECDSA and // Schnorr signatures. -func runSignVerifyMessage(ht *lntemp.HarnessTest, alice *node.HarnessNode) { +func runSignVerifyMessage(ht *lntest.HarnessTest, alice *node.HarnessNode) { aliceMsg := []byte("alice msg") keyLoc := &signrpc.KeyLocator{ KeyFamily: int32(keychain.KeyFamilyNodeKey), diff --git a/lntest/itest/lnd_single_hop_invoice_test.go b/itest/lnd_single_hop_invoice_test.go similarity index 96% rename from lntest/itest/lnd_single_hop_invoice_test.go rename to itest/lnd_single_hop_invoice_test.go index ce75c42d0..8051f7bb8 100644 --- a/lntest/itest/lnd_single_hop_invoice_test.go +++ b/itest/lnd_single_hop_invoice_test.go @@ -7,20 +7,20 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/record" "github.com/stretchr/testify/require" ) -func testSingleHopInvoice(ht *lntemp.HarnessTest) { +func testSingleHopInvoice(ht *lntest.HarnessTest) { // Open a channel with 100k satoshis between Alice and Bob with Alice // being the sole funder of the channel. chanAmt := btcutil.Amount(100000) alice, bob := ht.Alice, ht.Bob cp := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // assertAmountPaid is a helper closure that asserts the amount paid by diff --git a/lntest/itest/lnd_switch_test.go b/itest/lnd_switch_test.go similarity index 95% rename from lntest/itest/lnd_switch_test.go rename to itest/lnd_switch_test.go index fd90be4a7..fc3a6cd5b 100644 --- a/lntest/itest/lnd_switch_test.go +++ b/itest/lnd_switch_test.go @@ -3,8 +3,8 @@ package itest import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) @@ -21,11 +21,11 @@ const ( // // The general flow of this test: // 1. Carol --> Dave --> Alice --> Bob forward payment -// 2. -------X X X Bob restart sender and intermediaries +// 2. X X X Bob restart sender and intermediaries // 3. Carol <-- Dave <-- Alice <-- Bob expect settle to propagate // //nolint:dupword -func testSwitchCircuitPersistence(ht *lntemp.HarnessTest) { +func testSwitchCircuitPersistence(ht *lntest.HarnessTest) { // Setup our test scenario. We should now have four nodes running with // three channels. s := setupScenarioFourNodes(ht) @@ -95,7 +95,7 @@ func testSwitchCircuitPersistence(ht *lntemp.HarnessTest) { // 2. Carol --- Dave X Alice --- Bob disconnect intermediaries // 3. Carol --- Dave X Alice <-- Bob settle last hop // 4. Carol <-- Dave <-- Alice --- Bob reconnect, expect settle to propagate -func testSwitchOfflineDelivery(ht *lntemp.HarnessTest) { +func testSwitchOfflineDelivery(ht *lntest.HarnessTest) { // Setup our test scenario. We should now have four nodes running with // three channels. s := setupScenarioFourNodes(ht) @@ -131,11 +131,6 @@ func testSwitchOfflineDelivery(ht *lntemp.HarnessTest) { // Wait until all outstanding htlcs in the network have been settled. s.assertHTLCs(ht, 0) - // When asserting the amount of satoshis moved, we'll factor in the - // default base fee, as we didn't modify the fee structure when - // creating the seed nodes in the network. - const baseFee = 1 - // At this point all the channels within our proto network should be // shifted by 5k satoshis in the direction of Carol, the sink within the // payment flow generated above. The order of asserts corresponds to @@ -174,7 +169,9 @@ func testSwitchOfflineDelivery(ht *lntemp.HarnessTest) { // 3. Carol --- Dave X Alice <-- Bob settle last hop // 4. Carol --- Dave X X Bob restart Alice // 5. Carol <-- Dave <-- Alice --- Bob expect settle to propagate -func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) { +// +//nolint:dupword +func testSwitchOfflineDeliveryPersistence(ht *lntest.HarnessTest) { // Setup our test scenario. We should now have four nodes running with // three channels. s := setupScenarioFourNodes(ht) @@ -260,7 +257,9 @@ func testSwitchOfflineDeliveryPersistence(ht *lntemp.HarnessTest) { // 3. Carol --- Dave X Alice <-- Bob settle last hop // 4. Carol --- Dave X X shutdown Bob, restart Alice // 5. Carol <-- Dave <-- Alice X expect settle to propagate -func testSwitchOfflineDeliveryOutgoingOffline(ht *lntemp.HarnessTest) { +// +//nolint:dupword +func testSwitchOfflineDeliveryOutgoingOffline(ht *lntest.HarnessTest) { // Setup our test scenario. We should now have four nodes running with // three channels. Note that we won't call the cleanUp function here as // we will manually stop the node Carol and her channel. @@ -371,13 +370,13 @@ type scenarioFourNodes struct { // // NOTE: caller needs to call cleanUp to clean the nodes and channels created // from this setup. -func setupScenarioFourNodes(ht *lntemp.HarnessTest) *scenarioFourNodes { +func setupScenarioFourNodes(ht *lntest.HarnessTest) *scenarioFourNodes { const ( chanAmt = btcutil.Amount(1000000) pushAmt = btcutil.Amount(900000) ) - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, } @@ -404,7 +403,7 @@ func setupScenarioFourNodes(ht *lntemp.HarnessTest) *scenarioFourNodes { ht.FundCoins(btcutil.SatoshiPerBitcoin, carol) // Open channels in batch to save blocks mined. - reqs := []*lntemp.OpenChannelRequest{ + reqs := []*lntest.OpenChannelRequest{ {Local: alice, Remote: bob, Param: params}, {Local: dave, Remote: alice, Param: params}, {Local: carol, Remote: dave, Param: params}, @@ -456,7 +455,7 @@ func setupScenarioFourNodes(ht *lntemp.HarnessTest) *scenarioFourNodes { // assertHTLCs is a helper function which asserts the desired num of // HTLCs has been seen in the nodes. -func (s *scenarioFourNodes) assertHTLCs(ht *lntemp.HarnessTest, num int) { +func (s *scenarioFourNodes) assertHTLCs(ht *lntest.HarnessTest, num int) { // Alice should have both the same number of outgoing and // incoming HTLCs. ht.AssertNumActiveHtlcs(s.alice, num*2) @@ -472,7 +471,7 @@ func (s *scenarioFourNodes) assertHTLCs(ht *lntemp.HarnessTest, num int) { // assertAmoutPaid is a helper method which takes a given paid amount // and number of payments and asserts the desired payments are made in // the four nodes. -func (s *scenarioFourNodes) assertAmoutPaid(ht *lntemp.HarnessTest, +func (s *scenarioFourNodes) assertAmoutPaid(ht *lntest.HarnessTest, amt int64, num int64) { ht.AssertAmountPaid( diff --git a/lntest/itest/lnd_taproot_test.go b/itest/lnd_taproot_test.go similarity index 95% rename from lntest/itest/lnd_taproot_test.go rename to itest/lnd_taproot_test.go index a04048195..c03c1b5e0 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/itest/lnd_taproot_test.go @@ -20,8 +20,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/chainrpc" "github.com/lightningnetwork/lnd/lnrpc/signrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/stretchr/testify/require" ) @@ -47,7 +47,7 @@ var ( // testTaproot ensures that the daemon can send to and spend from taproot (p2tr) // outputs. -func testTaproot(ht *lntemp.HarnessTest) { +func testTaproot(ht *lntest.HarnessTest) { testTaprootSendCoinsKeySpendBip86(ht, ht.Alice) testTaprootComputeInputScriptKeySpendBip86(ht, ht.Alice) testTaprootSignOutputRawScriptSpend(ht, ht.Alice) @@ -81,7 +81,7 @@ func testTaproot(ht *lntemp.HarnessTest) { // testTaprootSendCoinsKeySpendBip86 tests sending to and spending from // p2tr key spend only (BIP-0086) addresses through the SendCoins RPC which // internally uses the ComputeInputScript method for signing. -func testTaprootSendCoinsKeySpendBip86(ht *lntemp.HarnessTest, +func testTaprootSendCoinsKeySpendBip86(ht *lntest.HarnessTest, alice *node.HarnessNode) { // We'll start the test by sending Alice some coins, which she'll use to @@ -137,7 +137,7 @@ func testTaprootSendCoinsKeySpendBip86(ht *lntemp.HarnessTest, // testTaprootComputeInputScriptKeySpendBip86 tests sending to and spending from // p2tr key spend only (BIP-0086) addresses through the SendCoins RPC which // internally uses the ComputeInputScript method for signing. -func testTaprootComputeInputScriptKeySpendBip86(ht *lntemp.HarnessTest, +func testTaprootComputeInputScriptKeySpendBip86(ht *lntest.HarnessTest, alice *node.HarnessNode) { // We'll start the test by sending Alice some coins, which she'll use @@ -234,7 +234,7 @@ func testTaprootComputeInputScriptKeySpendBip86(ht *lntemp.HarnessTest, // testTaprootSignOutputRawScriptSpend tests sending to and spending from p2tr // script addresses using the script path with the SignOutputRaw RPC. -func testTaprootSignOutputRawScriptSpend(ht *lntemp.HarnessTest, +func testTaprootSignOutputRawScriptSpend(ht *lntest.HarnessTest, alice *node.HarnessNode, sigHashType ...txscript.SigHashType) { // For the next step, we need a public key. Let's use a special family @@ -260,9 +260,7 @@ func testTaprootSignOutputRawScriptSpend(ht *lntemp.HarnessTest, require.NoError(ht, err) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -392,7 +390,7 @@ func testTaprootSignOutputRawScriptSpend(ht *lntemp.HarnessTest, // testTaprootSignOutputRawKeySpendBip86 tests that a tapscript address can // also be spent using the key spend path through the SignOutputRaw RPC using a // BIP0086 key spend only commitment. -func testTaprootSignOutputRawKeySpendBip86(ht *lntemp.HarnessTest, +func testTaprootSignOutputRawKeySpendBip86(ht *lntest.HarnessTest, alice *node.HarnessNode, sigHashType ...txscript.SigHashType) { // For the next step, we need a public key. Let's use a special family @@ -413,9 +411,7 @@ func testTaprootSignOutputRawKeySpendBip86(ht *lntemp.HarnessTest, taprootKey := txscript.ComputeTaprootKeyNoScript(internalKey) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -490,7 +486,7 @@ func testTaprootSignOutputRawKeySpendBip86(ht *lntemp.HarnessTest, // testTaprootSignOutputRawKeySpendRootHash tests that a tapscript address can // also be spent using the key spend path through the SignOutputRaw RPC using a // tapscript root hash. -func testTaprootSignOutputRawKeySpendRootHash(ht *lntemp.HarnessTest, +func testTaprootSignOutputRawKeySpendRootHash(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family @@ -514,9 +510,7 @@ func testTaprootSignOutputRawKeySpendRootHash(ht *lntemp.HarnessTest, taprootKey := txscript.ComputeTaprootOutputKey(internalKey, rootHash[:]) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -583,7 +577,7 @@ func testTaprootSignOutputRawKeySpendRootHash(ht *lntemp.HarnessTest, // testTaprootMuSig2KeySpendBip86 tests that a combined MuSig2 key can also be // used as a BIP-0086 key spend only key. -func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, +func testTaprootMuSig2KeySpendBip86(ht *lntest.HarnessTest, alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're not going to commit to a script. So our taproot tweak will be @@ -601,9 +595,7 @@ func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, ) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -711,7 +703,7 @@ func testTaprootMuSig2KeySpendBip86(ht *lntemp.HarnessTest, // testTaprootMuSig2KeySpendRootHash tests that a tapscript address can also be // spent using a MuSig2 combined key. -func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, +func testTaprootMuSig2KeySpendRootHash(ht *lntest.HarnessTest, alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're going to commit to a script as well. This is a hash lock with a @@ -733,9 +725,7 @@ func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, ) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -843,7 +833,7 @@ func testTaprootMuSig2KeySpendRootHash(ht *lntemp.HarnessTest, // testTaprootMuSig2ScriptSpend tests that a tapscript address with an internal // key that is a MuSig2 combined key can also be spent using the script path. -func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, +func testTaprootMuSig2ScriptSpend(ht *lntest.HarnessTest, alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're going to commit to a script and spend the output using the @@ -870,9 +860,7 @@ func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, tapscript := input.TapscriptFullTree(internalKey, leaf1) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -926,7 +914,7 @@ func testTaprootMuSig2ScriptSpend(ht *lntemp.HarnessTest, // testTaprootMuSig2CombinedLeafKeySpend tests that a MuSig2 combined key can be // used for an OP_CHECKSIG inside a tap script leaf spend. -func testTaprootMuSig2CombinedLeafKeySpend(ht *lntemp.HarnessTest, +func testTaprootMuSig2CombinedLeafKeySpend(ht *lntest.HarnessTest, alice *node.HarnessNode, version signrpc.MuSig2Version) { // We're using the combined MuSig2 key in a script leaf. So we need to @@ -951,9 +939,7 @@ func testTaprootMuSig2CombinedLeafKeySpend(ht *lntemp.HarnessTest, require.NoError(ht, err) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) // Spend the output again, this time back to a p2wkh address. p2wkhAddr, p2wkhPkScript := newAddrWithScript( @@ -1102,7 +1088,7 @@ func testTaprootMuSig2CombinedLeafKeySpend(ht *lntemp.HarnessTest, // testTaprootImportTapscriptScriptSpend tests importing p2tr script addresses // using the script path with the full tree known. -func testTaprootImportTapscriptFullTree(ht *lntemp.HarnessTest, +func testTaprootImportTapscriptFullTree(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family @@ -1151,9 +1137,7 @@ func testTaprootImportTapscriptFullTree(ht *lntemp.HarnessTest, require.Equal(ht, calculatedAddr.String(), importResp.P2TrAddress) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) p2trOutputRPC := &lnrpc.OutPoint{ TxidBytes: p2trOutpoint.Hash[:], OutputIndex: p2trOutpoint.Index, @@ -1176,7 +1160,7 @@ func testTaprootImportTapscriptFullTree(ht *lntemp.HarnessTest, // testTaprootImportTapscriptPartialReveal tests importing p2tr script addresses // for which we only know part of the tree. -func testTaprootImportTapscriptPartialReveal(ht *lntemp.HarnessTest, +func testTaprootImportTapscriptPartialReveal(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family @@ -1221,9 +1205,7 @@ func testTaprootImportTapscriptPartialReveal(ht *lntemp.HarnessTest, require.Equal(ht, calculatedAddr.String(), importResp.P2TrAddress) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) p2trOutputRPC := &lnrpc.OutPoint{ TxidBytes: p2trOutpoint.Hash[:], @@ -1247,7 +1229,7 @@ func testTaprootImportTapscriptPartialReveal(ht *lntemp.HarnessTest, // testTaprootImportTapscriptRootHashOnly tests importing p2tr script addresses // for which we only know the root hash. -func testTaprootImportTapscriptRootHashOnly(ht *lntemp.HarnessTest, +func testTaprootImportTapscriptRootHashOnly(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family @@ -1280,9 +1262,7 @@ func testTaprootImportTapscriptRootHashOnly(ht *lntemp.HarnessTest, require.Equal(ht, calculatedAddr.String(), importResp.P2TrAddress) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) p2trOutputRPC := &lnrpc.OutPoint{ TxidBytes: p2trOutpoint.Hash[:], @@ -1306,7 +1286,7 @@ func testTaprootImportTapscriptRootHashOnly(ht *lntemp.HarnessTest, // testTaprootImportTapscriptFullKey tests importing p2tr script addresses for // which we only know the full Taproot key. -func testTaprootImportTapscriptFullKey(ht *lntemp.HarnessTest, +func testTaprootImportTapscriptFullKey(ht *lntest.HarnessTest, alice *node.HarnessNode) { // For the next step, we need a public key. Let's use a special family @@ -1339,9 +1319,7 @@ func testTaprootImportTapscriptFullKey(ht *lntemp.HarnessTest, require.Equal(ht, calculatedAddr.String(), importResp.P2TrAddress) // Send some coins to the generated tapscript address. - p2trOutpoint, p2trPkScript := sendToTaprootOutput( - ht, alice, taprootKey, testAmount, - ) + p2trOutpoint, p2trPkScript := sendToTaprootOutput(ht, alice, taprootKey) p2trOutputRPC := &lnrpc.OutPoint{ TxidBytes: p2trOutpoint.Hash[:], @@ -1365,7 +1343,7 @@ func testTaprootImportTapscriptFullKey(ht *lntemp.HarnessTest, // clearWalletImportedTapscriptBalance manually assembles and then attempts to // sign a TX to sweep funds from an imported tapscript address. -func clearWalletImportedTapscriptBalance(ht *lntemp.HarnessTest, +func clearWalletImportedTapscriptBalance(ht *lntest.HarnessTest, hn *node.HarnessNode, utxo *wire.TxOut, outPoint wire.OutPoint, internalKey *btcec.PublicKey, derivationPath []uint32, rootHash []byte) { @@ -1460,7 +1438,7 @@ func testScriptSchnorrSig(t *testing.T, } // newAddrWithScript returns a new address and its pkScript. -func newAddrWithScript(ht *lntemp.HarnessTest, node *node.HarnessNode, +func newAddrWithScript(ht *lntest.HarnessTest, node *node.HarnessNode, addrType lnrpc.AddressType) (btcutil.Address, []byte) { p2wkhResp := node.RPC.NewAddress(&lnrpc.NewAddressRequest{ @@ -1479,8 +1457,8 @@ func newAddrWithScript(ht *lntemp.HarnessTest, node *node.HarnessNode, // sendToTaprootOutput sends coins to a p2tr output of the given taproot key and // mines a block to confirm the coins. -func sendToTaprootOutput(ht *lntemp.HarnessTest, hn *node.HarnessNode, - taprootKey *btcec.PublicKey, amt int64) (wire.OutPoint, []byte) { +func sendToTaprootOutput(ht *lntest.HarnessTest, hn *node.HarnessNode, + taprootKey *btcec.PublicKey) (wire.OutPoint, []byte) { tapScriptAddr, err := btcutil.NewAddressTaproot( schnorr.SerializePubKey(taprootKey), harnessNetParams, @@ -1492,7 +1470,7 @@ func sendToTaprootOutput(ht *lntemp.HarnessTest, hn *node.HarnessNode, // Send some coins to the generated tapscript address. req := &lnrpc.SendCoinsRequest{ Addr: tapScriptAddr.String(), - Amount: amt, + Amount: testAmount, } hn.RPC.SendCoins(req) @@ -1542,7 +1520,7 @@ func sendToTaprootOutput(ht *lntemp.HarnessTest, hn *node.HarnessNode, // after checking its weight against an estimate. After asserting the given // spend request, the given sweep address' balance is verified to be seen as // funds belonging to the wallet. -func publishTxAndConfirmSweep(ht *lntemp.HarnessTest, node *node.HarnessNode, +func publishTxAndConfirmSweep(ht *lntest.HarnessTest, node *node.HarnessNode, tx *wire.MsgTx, estimatedWeight int64, spendRequest *chainrpc.SpendRequest, sweepAddr string) { @@ -1604,7 +1582,7 @@ func publishTxAndConfirmSweep(ht *lntemp.HarnessTest, node *node.HarnessNode, // confirmAddress makes sure that a transaction in the mempool spends funds to // the given address. It also checks that a confirmation notification for the // address is triggered when the transaction is mined. -func confirmAddress(ht *lntemp.HarnessTest, hn *node.HarnessNode, +func confirmAddress(ht *lntest.HarnessTest, hn *node.HarnessNode, addrString string) { // Wait until the tx that sends to the address is found. @@ -1654,7 +1632,7 @@ func confirmAddress(ht *lntemp.HarnessTest, hn *node.HarnessNode, // deriveSigningKeys derives three signing keys and returns their descriptors, // as well as the public keys in the Schnorr serialized format. -func deriveSigningKeys(ht *lntemp.HarnessTest, node *node.HarnessNode, +func deriveSigningKeys(ht *lntest.HarnessTest, node *node.HarnessNode, version signrpc.MuSig2Version) (*signrpc.KeyDescriptor, *signrpc.KeyDescriptor, *signrpc.KeyDescriptor, [][]byte) { @@ -1700,7 +1678,7 @@ func deriveSigningKeys(ht *lntemp.HarnessTest, node *node.HarnessNode, // combined into a single key. The same node is used for the three signing // participants but a separate key is generated for each session. So the result // should be the same as if it were three different nodes. -func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, +func createMuSigSessions(ht *lntest.HarnessTest, node *node.HarnessNode, taprootTweak *signrpc.TaprootTweakDesc, keyDesc1, keyDesc2, keyDesc3 *signrpc.KeyDescriptor, allPubKeys [][]byte, version signrpc.MuSig2Version) (*btcec.PublicKey, @@ -1838,7 +1816,7 @@ func createMuSigSessions(ht *lntemp.HarnessTest, node *node.HarnessNode, // testTaprootCoopClose asserts that if both peers signal ShutdownAnySegwit, // then a taproot closing addr is used. Otherwise, we shouldn't expect one to // be used. -func testTaprootCoopClose(ht *lntemp.HarnessTest) { +func testTaprootCoopClose(ht *lntest.HarnessTest) { // We'll start by making two new nodes, and funding a channel between // them. carol := ht.NewNode("Carol", nil) @@ -1853,7 +1831,7 @@ func testTaprootCoopClose(ht *lntemp.HarnessTest) { // We'll now open a channel between Carol and Dave. chanPoint := ht.OpenChannel( - carol, dave, lntemp.OpenChannelParams{ + carol, dave, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, SatPerVByte: satPerVbyte, @@ -1888,7 +1866,7 @@ func testTaprootCoopClose(ht *lntemp.HarnessTest) { // We'll now open up a chanel again between Carol and Eve. chanPoint = ht.OpenChannel( - carol, eve, lntemp.OpenChannelParams{ + carol, eve, lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: pushAmt, SatPerVByte: satPerVbyte, @@ -1904,7 +1882,7 @@ func testTaprootCoopClose(ht *lntemp.HarnessTest) { // testMuSig2CombineKey makes sure that combining a key with MuSig2 returns the // correct result according to the MuSig2 version specified. -func testMuSig2CombineKey(ht *lntemp.HarnessTest, alice *node.HarnessNode, +func testMuSig2CombineKey(ht *lntest.HarnessTest, alice *node.HarnessNode, version signrpc.MuSig2Version) { testVector040Key1 := hexDecode( diff --git a/lntest/itest/temp_lnd_test.go b/itest/lnd_test.go similarity index 75% rename from lntest/itest/temp_lnd_test.go rename to itest/lnd_test.go index 4b816b93b..cb9f60dbf 100644 --- a/lntest/itest/temp_lnd_test.go +++ b/itest/lnd_test.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "io" + "math" "os" "path/filepath" "runtime" @@ -11,9 +12,12 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/integration/rpctest" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/wait" "github.com/stretchr/testify/require" "google.golang.org/grpc/grpclog" ) @@ -26,9 +30,25 @@ const ( // defaultRunTranche is the default index of the test cases tranche that // we run. defaultRunTranche uint = 0 + + defaultTimeout = wait.DefaultTimeout + itestLndBinary = "../lnd-itest" + + // TODO(yy): remove the following defined constants and put them in the + // specific tests where they are used? + testFeeBase = 1e+6 + anchorSize = 330 + defaultCSV = node.DefaultCSV + noFeeLimitMsat = math.MaxInt64 + + AddrTypeWitnessPubkeyHash = lnrpc.AddressType_WITNESS_PUBKEY_HASH + AddrTypeNestedPubkeyHash = lnrpc.AddressType_NESTED_PUBKEY_HASH + AddrTypeTaprootPubkey = lnrpc.AddressType_TAPROOT_PUBKEY ) var ( + harnessNetParams = &chaincfg.RegressionNetParams + // testCasesSplitParts is the number of tranches the test cases should // be split into. By default this is set to 1, so no splitting happens. // If this value is increased, then the -runtranche flag must be @@ -49,31 +69,32 @@ var ( // dbBackendFlag specifies the backend to use. dbBackendFlag = flag.String("dbbackend", "bbolt", "Database backend "+ - "(bbolt, etcd, postgres, sqlite)") + "(bbolt, etcd, postgres)") + + // lndExecutable is the full path to the lnd binary. + lndExecutable = flag.String( + "lndexec", itestLndBinary, "full path to lnd binary", + ) ) -// TestLightningNetworkDaemonTemp performs a series of integration tests -// amongst a programmatically driven network of lnd nodes. -func TestLightningNetworkDaemonTemp(t *testing.T) { - if !*tempTest { - t.Skip("Running old tests, new tests are skipped") - } - +// TestLightningNetworkDaemon performs a series of integration tests amongst a +// programmatically driven network of lnd nodes. +func TestLightningNetworkDaemon(t *testing.T) { // If no tests are registered, then we can exit early. - if len(allTestCasesTemp) == 0 { - t.Skip("integration tests not selected with flag 'rpctest'") + if len(allTestCases) == 0 { + t.Skip("integration tests not selected with flag 'integration'") } // Get the test cases to be run in this tranche. testCases, trancheIndex, trancheOffset := getTestCaseSplitTranche() - lntest.ApplyPortOffset(uint32(trancheIndex) * 1000) + node.ApplyPortOffset(uint32(trancheIndex) * 1000) // Create a simple fee service. - feeService := lntemp.NewFeeService(t) + feeService := lntest.NewFeeService(t) // Get the binary path and setup the harness test. binary := getLndBinary(t) - harnessTest := lntemp.SetupHarness( + harnessTest := lntest.SetupHarness( t, binary, *dbBackendFlag, feeService, ) defer harnessTest.Stop() @@ -126,12 +147,16 @@ func TestLightningNetworkDaemonTemp(t *testing.T) { break } } + + _, height := harnessTest.Miner.GetBestBlock() + t.Logf("=========> tests finished for tranche: %v, tested %d "+ + "cases, end height: %d\n", trancheIndex, len(testCases), height) } // getTestCaseSplitTranche returns the sub slice of the test cases that should // be run as the current split tranche as well as the index and slice offset of // the tranche. -func getTestCaseSplitTranche() ([]*lntemp.TestCase, uint, uint) { +func getTestCaseSplitTranche() ([]*lntest.TestCase, uint, uint) { numTranches := defaultSplitTranches if testCasesSplitTranches != nil { numTranches = *testCasesSplitTranches @@ -150,7 +175,7 @@ func getTestCaseSplitTranche() ([]*lntemp.TestCase, uint, uint) { runTranche = 0 } - numCases := uint(len(allTestCasesTemp)) + numCases := uint(len(allTestCases)) testsPerTranche := numCases / numTranches trancheOffset := runTranche * testsPerTranche trancheEnd := trancheOffset + testsPerTranche @@ -158,7 +183,7 @@ func getTestCaseSplitTranche() ([]*lntemp.TestCase, uint, uint) { trancheEnd = numCases } - return allTestCasesTemp[trancheOffset:trancheEnd], threadID, + return allTestCases[trancheOffset:trancheEnd], threadID, trancheOffset } @@ -188,7 +213,7 @@ func init() { // Before we start any node, we need to make sure that any btcd node // that is started through the RPC harness uses a unique port as well // to avoid any port collisions. - rpctest.ListenAddressGenerator = lntest.GenerateBtcdListenerAddresses + rpctest.ListenAddressGenerator = node.GenerateBtcdListenerAddresses // Swap out grpc's default logger with out fake logger which drops the // statements on the floor. diff --git a/lntest/itest/lnd_trackpayments_test.go b/itest/lnd_trackpayments_test.go similarity index 95% rename from lntest/itest/lnd_trackpayments_test.go rename to itest/lnd_trackpayments_test.go index 2554471ef..55224b87b 100644 --- a/lntest/itest/lnd_trackpayments_test.go +++ b/itest/lnd_trackpayments_test.go @@ -7,17 +7,17 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) // testTrackPayments tests whether a client that calls the TrackPayments api // receives payment updates. -func testTrackPayments(ht *lntemp.HarnessTest) { +func testTrackPayments(ht *lntest.HarnessTest) { // Open a channel between alice and bob. alice, bob := ht.Alice, ht.Bob channel := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{ + alice, bob, lntest.OpenChannelParams{ Amt: btcutil.Amount(300000), }, ) diff --git a/lntest/itest/lnd_wallet_import_test.go b/itest/lnd_wallet_import_test.go similarity index 96% rename from lntest/itest/lnd_wallet_import_test.go rename to itest/lnd_wallet_import_test.go index 818ff311d..afd96a352 100644 --- a/lntest/itest/lnd_wallet_import_test.go +++ b/itest/lnd_wallet_import_test.go @@ -16,8 +16,8 @@ import ( "github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" + "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lnwallet" "github.com/stretchr/testify/require" ) @@ -51,7 +51,7 @@ func walletToLNAddrType(t *testing.T, // newExternalAddr generates a new external address of an imported account for a // pair of nodes, where one acts as the funder and the other as the signer. -func newExternalAddr(ht *lntemp.HarnessTest, funder, signer *node.HarnessNode, +func newExternalAddr(ht *lntest.HarnessTest, funder, signer *node.HarnessNode, importedAccount string, addrType walletrpc.AddressType) string { // We'll generate a new address for Carol from Dave's node to receive @@ -125,7 +125,7 @@ func assertOutputScriptType(t *testing.T, expType txscript.ScriptClass, // psbtSendFromImportedAccount attempts to fund a PSBT from the given imported // account, originating from the source node to the destination. -func psbtSendFromImportedAccount(ht *lntemp.HarnessTest, srcNode, destNode, +func psbtSendFromImportedAccount(ht *lntest.HarnessTest, srcNode, destNode, signer *node.HarnessNode, account string, accountAddrType walletrpc.AddressType) { @@ -236,7 +236,7 @@ func psbtSendFromImportedAccount(ht *lntemp.HarnessTest, srcNode, destNode, // node. To ensure the channel is operational before closing it, a test payment // is made. Several balance assertions are made along the way for the sake of // correctness. -func fundChanAndCloseFromImportedAccount(ht *lntemp.HarnessTest, srcNode, +func fundChanAndCloseFromImportedAccount(ht *lntest.HarnessTest, srcNode, destNode, signer *node.HarnessNode, account string, accountAddrType walletrpc.AddressType, utxoAmt, chanSize int64) { @@ -256,7 +256,7 @@ func fundChanAndCloseFromImportedAccount(ht *lntemp.HarnessTest, srcNode, // The source node will then fund the channel through a PSBT shim. pendingChanID := ht.Random32Bytes() chanUpdates, rawPsbt := ht.OpenChannelPsbt( - srcNode, destNode, lntemp.OpenChannelParams{ + srcNode, destNode, lntest.OpenChannelParams{ Amt: btcutil.Amount(chanSize), FundingShim: &lnrpc.FundingShim{ Shim: &lnrpc.FundingShim_PsbtShim{ @@ -463,7 +463,7 @@ func fundChanAndCloseFromImportedAccount(ht *lntemp.HarnessTest, srcNode, // testWalletImportAccount tests that an imported account can fund transactions // and channels through PSBTs, by having one node (the one with the imported // account) craft the transactions and another node act as the signer. -func testWalletImportAccount(ht *lntemp.HarnessTest) { +func testWalletImportAccount(ht *lntest.HarnessTest) { testCases := []struct { name string addrType walletrpc.AddressType @@ -491,7 +491,7 @@ func testWalletImportAccount(ht *lntemp.HarnessTest) { for _, tc := range testCases { tc := tc success := ht.Run(tc.name, func(tt *testing.T) { - testFunc := func(ht *lntemp.HarnessTest) { + testFunc := func(ht *lntest.HarnessTest) { testWalletImportAccountScenario( ht, tc.addrType, ) @@ -499,7 +499,7 @@ func testWalletImportAccount(ht *lntemp.HarnessTest) { st := ht.Subtest(tt) - st.RunTestCase(&lntemp.TestCase{ + st.RunTestCase(&lntest.TestCase{ Name: tc.name, TestFunc: testFunc, }) @@ -515,7 +515,7 @@ func testWalletImportAccount(ht *lntemp.HarnessTest) { } } -func testWalletImportAccountScenario(ht *lntemp.HarnessTest, +func testWalletImportAccountScenario(ht *lntest.HarnessTest, addrType walletrpc.AddressType) { // We'll start our test by having two nodes, Carol and Dave. Carol's @@ -529,7 +529,7 @@ func testWalletImportAccountScenario(ht *lntemp.HarnessTest, runWalletImportAccountScenario(ht, addrType, carol, dave) } -func runWalletImportAccountScenario(ht *lntemp.HarnessTest, +func runWalletImportAccountScenario(ht *lntest.HarnessTest, addrType walletrpc.AddressType, carol, dave *node.HarnessNode) { const utxoAmt int64 = btcutil.SatoshiPerBitcoin @@ -619,7 +619,7 @@ func runWalletImportAccountScenario(ht *lntemp.HarnessTest, // testWalletImportPubKey tests that an imported public keys can fund // transactions and channels through PSBTs, by having one node (the one with the // imported account) craft the transactions and another node act as the signer. -func testWalletImportPubKey(ht *lntemp.HarnessTest) { +func testWalletImportPubKey(ht *lntest.HarnessTest) { testCases := []struct { name string addrType walletrpc.AddressType @@ -642,7 +642,7 @@ func testWalletImportPubKey(ht *lntemp.HarnessTest) { for _, tc := range testCases { tc := tc success := ht.Run(tc.name, func(tt *testing.T) { - testFunc := func(ht *lntemp.HarnessTest) { + testFunc := func(ht *lntest.HarnessTest) { testWalletImportPubKeyScenario( ht, tc.addrType, ) @@ -650,7 +650,7 @@ func testWalletImportPubKey(ht *lntemp.HarnessTest) { st := ht.Subtest(tt) - st.RunTestCase(&lntemp.TestCase{ + st.RunTestCase(&lntest.TestCase{ Name: tc.name, TestFunc: testFunc, }) @@ -666,7 +666,7 @@ func testWalletImportPubKey(ht *lntemp.HarnessTest) { } } -func testWalletImportPubKeyScenario(ht *lntemp.HarnessTest, +func testWalletImportPubKeyScenario(ht *lntest.HarnessTest, addrType walletrpc.AddressType) { const utxoAmt int64 = btcutil.SatoshiPerBitcoin diff --git a/lntest/itest/lnd_wipe_fwdpkgs_test.go b/itest/lnd_wipe_fwdpkgs_test.go similarity index 94% rename from lntest/itest/lnd_wipe_fwdpkgs_test.go rename to itest/lnd_wipe_fwdpkgs_test.go index 0f74d4fab..0e3a74d7b 100644 --- a/lntest/itest/lnd_wipe_fwdpkgs_test.go +++ b/itest/lnd_wipe_fwdpkgs_test.go @@ -5,12 +5,10 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/stretchr/testify/require" ) -type pendingChan *lnrpc.PendingChannelsResponse_PendingChannel - // testWipeForwardingPackagesLocal tests that when a channel is closed, either // through local force close, remote close, or coop close, all the forwarding // packages of that particular channel are deleted. The test creates a @@ -21,7 +19,7 @@ type pendingChan *lnrpc.PendingChannelsResponse_PendingChannel // packages are wiped. // - Bob coop closes the channel Bob->Carol, and checks from both Bob PoVs // that the forwarding packages are wiped. -func testWipeForwardingPackages(ht *lntemp.HarnessTest) { +func testWipeForwardingPackages(ht *lntest.HarnessTest) { const ( chanAmt = 10e6 paymentAmt = 10e4 @@ -41,12 +39,12 @@ func testWipeForwardingPackages(ht *lntemp.HarnessTest) { // Open a channel between Alice and Bob. chanPointAB := ht.OpenChannel( - alice, bob, lntemp.OpenChannelParams{Amt: chanAmt}, + alice, bob, lntest.OpenChannelParams{Amt: chanAmt}, ) // Open a channel between Bob and Carol. chanPointBC := ht.OpenChannel( - bob, carol, lntemp.OpenChannelParams{Amt: chanAmt}, + bob, carol, lntest.OpenChannelParams{Amt: chanAmt}, ) // Before we continue, make sure Alice has seen the channel between Bob diff --git a/lntest/itest/lnd_wumbo_channels_test.go b/itest/lnd_wumbo_channels_test.go similarity index 88% rename from lntest/itest/lnd_wumbo_channels_test.go rename to itest/lnd_wumbo_channels_test.go index 5a5ba6627..18d170acc 100644 --- a/lntest/itest/lnd_wumbo_channels_test.go +++ b/itest/lnd_wumbo_channels_test.go @@ -3,7 +3,7 @@ package itest import ( "github.com/btcsuite/btcd/btcutil" "github.com/lightningnetwork/lnd/funding" - "github.com/lightningnetwork/lnd/lntemp" + "github.com/lightningnetwork/lnd/lntest" "github.com/lightningnetwork/lnd/lnwallet" ) @@ -11,7 +11,7 @@ import ( // acceptances will allow a wumbo channel to be created. Additionally, if a // node is running with mini channels only enabled, then they should reject any // inbound wumbo channel requests. -func testWumboChannels(ht *lntemp.HarnessTest) { +func testWumboChannels(ht *lntest.HarnessTest) { // With all the channel types exercised, we'll now make sure the wumbo // signalling support works properly. // @@ -33,7 +33,7 @@ func testWumboChannels(ht *lntemp.HarnessTest) { // The test should indicate a failure due to the channel being too // large. ht.OpenChannelAssertErr( - wumboNode, miniNode, lntemp.OpenChannelParams{Amt: chanAmt}, + wumboNode, miniNode, lntest.OpenChannelParams{Amt: chanAmt}, lnwallet.ErrChanTooLarge(chanAmt, funding.MaxBtcFundingAmount), ) @@ -46,7 +46,7 @@ func testWumboChannels(ht *lntemp.HarnessTest) { // Creating a wumbo channel between these two nodes should succeed. ht.EnsureConnected(wumboNode, wumboNode2) chanPoint := ht.OpenChannel( - wumboNode, wumboNode2, lntemp.OpenChannelParams{Amt: chanAmt}, + wumboNode, wumboNode2, lntest.OpenChannelParams{Amt: chanAmt}, ) ht.CloseChannel(wumboNode, chanPoint) } diff --git a/lntest/itest/lnd_zero_conf_test.go b/itest/lnd_zero_conf_test.go similarity index 92% rename from lntest/itest/lnd_zero_conf_test.go rename to itest/lnd_zero_conf_test.go index bf5de369a..ae3ca0ec7 100644 --- a/lntest/itest/lnd_zero_conf_test.go +++ b/itest/lnd_zero_conf_test.go @@ -1,7 +1,7 @@ package itest import ( - "context" + "fmt" "testing" "time" @@ -13,10 +13,9 @@ import ( "github.com/lightningnetwork/lnd/chainreg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntemp" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntemp/rpc" "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwire" "github.com/stretchr/testify/require" @@ -24,7 +23,7 @@ import ( // testZeroConfChannelOpen tests that opening a zero-conf channel works and // sending payments also works. -func testZeroConfChannelOpen(ht *lntemp.HarnessTest) { +func testZeroConfChannelOpen(ht *lntest.HarnessTest) { // Since option-scid-alias is opt-in, the provided harness nodes will // not have the feature bit set. Also need to set anchors as those are // default-off in itests. @@ -40,7 +39,7 @@ func testZeroConfChannelOpen(ht *lntemp.HarnessTest) { // We'll open a regular public channel between Bob and Carol here. chanAmt := btcutil.Amount(1_000_000) - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, } chanPoint := ht.OpenChannel(bob, carol, p) @@ -59,7 +58,7 @@ func testZeroConfChannelOpen(ht *lntemp.HarnessTest) { go acceptChannel(ht.T, true, acceptStream) // Open a private zero-conf anchors channel of 1M satoshis. - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: chanAmt, Private: true, CommitmentType: lnrpc.CommitmentType_ANCHORS, @@ -192,7 +191,7 @@ func testZeroConfChannelOpen(ht *lntemp.HarnessTest) { // testOptionScidAlias checks that opening an option_scid_alias channel-type // channel or w/o the channel-type works properly. -func testOptionScidAlias(ht *lntemp.HarnessTest) { +func testOptionScidAlias(ht *lntest.HarnessTest) { type scidTestCase struct { name string @@ -235,7 +234,7 @@ func testOptionScidAlias(ht *lntemp.HarnessTest) { } } -func optionScidAliasScenario(ht *lntemp.HarnessTest, chantype, private bool) { +func optionScidAliasScenario(ht *lntest.HarnessTest, chantype, private bool) { // Option-scid-alias is opt-in, as is anchors. scidAliasArgs := []string{ "--protocol.option-scid-alias", @@ -257,7 +256,7 @@ func optionScidAliasScenario(ht *lntemp.HarnessTest, chantype, private bool) { chanAmt := btcutil.Amount(1_000_000) - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: chanAmt, Private: private, CommitmentType: lnrpc.CommitmentType_ANCHORS, @@ -283,7 +282,7 @@ func optionScidAliasScenario(ht *lntemp.HarnessTest, chantype, private bool) { // We'll now open a regular public channel between Bob and Carol and // assert that Bob can pay Dave. We'll also assert that the invoice // Dave issues has the startingAlias as a hop hint. - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, } fundingPoint2 := ht.OpenChannel(bob, carol, p) @@ -313,6 +312,7 @@ func optionScidAliasScenario(ht *lntemp.HarnessTest, chantype, private bool) { require.Len(ht, decodedReq.RouteHints, 0) payReq := daveInvoiceResp2.PaymentRequest ht.CompletePaymentRequests(bob, []string{payReq}) + return } @@ -410,7 +410,7 @@ func waitForZeroConfGraphChange(hn *node.HarnessNode, // testUpdateChannelPolicyScidAlias checks that option-scid-alias, zero-conf // channel-types, and option-scid-alias feature-bit-only channels have the // expected graph and that payments work when updating the channel policy. -func testUpdateChannelPolicyScidAlias(ht *lntemp.HarnessTest) { +func testUpdateChannelPolicyScidAlias(ht *lntest.HarnessTest) { tests := []struct { name string @@ -462,7 +462,7 @@ func testUpdateChannelPolicyScidAlias(ht *lntemp.HarnessTest) { } } -func testPrivateUpdateAlias(ht *lntemp.HarnessTest, +func testPrivateUpdateAlias(ht *lntest.HarnessTest, zeroConf, scidAliasType, private bool) { // We'll create a new node Eve that will not have option-scid-alias @@ -495,7 +495,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, chanAmt := btcutil.Amount(1_000_000) - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, } @@ -509,7 +509,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, go acceptChannel(ht.T, zeroConf, acceptStream) // Open a private channel, optionally specifying a channel-type. - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: chanAmt, Private: private, CommitmentType: lnrpc.CommitmentType_ANCHORS, @@ -542,7 +542,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, FeeRateMilliMsat: testFeeBase * feeRate, TimeLockDelta: timeLockDelta, MinHtlc: 1000, // default value - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), } // Assert that Dave receives Carol's policy update. @@ -568,7 +568,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, FeeRateMilliMsat: testFeeBase * feeRate, TimeLockDelta: timeLockDelta, MinHtlc: 1000, - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), } // Assert that Carol receives Dave's policy update. @@ -655,7 +655,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, FeeRateMilliMsat: testFeeBase * feeRate, TimeLockDelta: timeLockDelta, MinHtlc: 1000, - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), } // Assert Dave receives Carol's policy update. @@ -744,7 +744,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, FeeRateMilliMsat: testFeeBase * feeRate, TimeLockDelta: timeLockDelta, MinHtlc: 1000, - MaxHtlcMsat: calculateMaxHtlc(chanAmt), + MaxHtlcMsat: lntest.CalculateMaxHtlc(chanAmt), } // Assert Dave and optionally Eve receives Carol's update. @@ -764,7 +764,7 @@ func testPrivateUpdateAlias(ht *lntemp.HarnessTest, // testOptionScidUpgrade tests that toggling the option-scid-alias feature bit // correctly upgrades existing channels. -func testOptionScidUpgrade(ht *lntemp.HarnessTest) { +func testOptionScidUpgrade(ht *lntest.HarnessTest) { bob := ht.Bob // Start carol with anchors only. @@ -788,7 +788,7 @@ func testOptionScidUpgrade(ht *lntemp.HarnessTest) { chanAmt := btcutil.Amount(1_000_000) - p := lntemp.OpenChannelParams{ + p := lntest.OpenChannelParams{ Amt: chanAmt, PushAmt: chanAmt / 2, Private: true, @@ -798,7 +798,7 @@ func testOptionScidUpgrade(ht *lntemp.HarnessTest) { // Bob will open a channel to Carol now. ht.EnsureConnected(bob, carol) - p = lntemp.OpenChannelParams{ + p = lntest.OpenChannelParams{ Amt: chanAmt, } fundingPoint2 := ht.OpenChannel(bob, carol, p) @@ -822,25 +822,34 @@ func testOptionScidUpgrade(ht *lntemp.HarnessTest) { var startingAlias lnwire.ShortChannelID startingAlias.BlockHeight = 16_000_000 - err := wait.Predicate(func() bool { + // TODO(yy): Carol and Dave will attempt to connect to each other + // during restart. However, due to the race condition in peer + // connection, they may both fail. Thus we need to ensure the + // connection here. Once the race is fixed, we can remove this line. + ht.EnsureConnected(dave, carol) + + err := wait.NoError(func() error { invoiceResp := dave.RPC.AddInvoice(daveParams) decodedReq := dave.RPC.DecodePayReq(invoiceResp.PaymentRequest) if len(decodedReq.RouteHints) != 1 { - return false + return fmt.Errorf("expected 1 route hint, got %v", + decodedReq.RouteHints) } if len(decodedReq.RouteHints[0].HopHints) != 1 { - return false + return fmt.Errorf("expected 1 hop hint, got %v", + len(decodedReq.RouteHints[0].HopHints)) } hopHint := decodedReq.RouteHints[0].HopHints[0].ChanId if startingAlias.ToUint64() == hopHint { daveInvoice = invoiceResp - return true + return nil } - return false + return fmt.Errorf("unmatched alias, expected %v, got %v", + startingAlias.ToUint64(), hopHint) }, defaultTimeout) require.NoError(ht, err) @@ -892,15 +901,12 @@ func acceptChannel(t *testing.T, zeroConf bool, stream rpc.AcceptorClient) { // testZeroConfReorg tests that a reorg does not cause a zero-conf channel to // be deleted from the channel graph. This was previously the case due to logic // in the function DisconnectBlockAtHeight. -func testZeroConfReorg(ht *lntemp.HarnessTest) { - if ht.ChainBackendName() == lntest.NeutrinoBackendName { +func testZeroConfReorg(ht *lntest.HarnessTest) { + if ht.IsNeutrinoBackend() { ht.Skipf("skipping zero-conf reorg test for neutrino backend") } - var ( - ctxb = context.Background() - temp = "temp" - ) + var temp = "temp" // Since zero-conf is opt in, the harness nodes provided won't be able // to open zero-conf channels. In that case, we just spin up new nodes. @@ -926,7 +932,7 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { go acceptChannel(ht.T, true, acceptStream) // Open a private zero-conf anchors channel of 1M satoshis. - params := lntemp.OpenChannelParams{ + params := lntest.OpenChannelParams{ Amt: btcutil.Amount(1_000_000), CommitmentType: lnrpc.CommitmentType_ANCHORS, ZeroConf: true, @@ -949,10 +955,9 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { // We will now attempt to query for the alias SCID in Carol's graph. // We will query for the starting alias, which is exported by the // aliasmgr package. - _, err := carol.RPC.LN.GetChanInfo(ctxb, &lnrpc.ChanInfoRequest{ + carol.RPC.GetChanInfo(&lnrpc.ChanInfoRequest{ ChanId: aliasmgr.StartingAlias.ToUint64(), }) - require.NoError(ht.T, err) // Now we will trigger a reorg and we'll assert that the edge still // exists in the graph. @@ -960,7 +965,7 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { // First, we'll setup a new miner that we can use to cause a reorg. tempLogDir := ".tempminerlogs" logFilename := "output-open_channel_reorg-temp_miner.log" - tempMiner := lntemp.NewTempMiner( + tempMiner := lntest.NewTempMiner( ht.Context(), ht.T, tempLogDir, logFilename, ) defer tempMiner.Stop() @@ -971,7 +976,7 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { // We start by connecting the new miner to our original miner, such // that it will sync to our original chain. - err = ht.Miner.Client.Node( + err := ht.Miner.Client.Node( btcjson.NConnect, tempMiner.P2PAddress(), &temp, ) require.NoError(ht.T, err, "unable to connect node") @@ -991,18 +996,16 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { require.NoError(ht.T, err, "unable to remove node") // We now cause a fork, by letting our original miner mine 1 block and - // our new miner will mine 2. - ht.MineBlocks(1) - _, err = tempMiner.Client.Generate(2) - require.NoError(ht.T, err, "unable to generate blocks") + // our new miner will mine 2. We also expect the funding transition to + // be mined. + ht.MineBlocksAndAssertNumTxes(1, 1) + tempMiner.MineEmptyBlocks(2) // Ensure the temp miner is one block ahead. assertMinerBlockHeightDelta(ht, ht.Miner, tempMiner, 1) // Wait for Carol to sync to the original miner's chain. - _, minerHeight, err := ht.Miner.Client.GetBestBlock() - require.NoError(ht.T, err, "unable to get current blockheight") - + _, minerHeight := ht.Miner.GetBestBlock() ht.WaitForNodeBlockHeight(carol, minerHeight) // Now we'll disconnect Carol's chain backend from the original miner @@ -1033,24 +1036,19 @@ func testZeroConfReorg(ht *lntemp.HarnessTest) { ht.ConnectMiner() - // This should have caused a reorg and Alice should sync to the new + // This should have caused a reorg and Carol should sync to the new // chain. - _, tempMinerHeight, err := tempMiner.Client.GetBestBlock() - require.NoError(ht.T, err, "unable to get current blockheight") - + _, tempMinerHeight := tempMiner.GetBestBlock() ht.WaitForNodeBlockHeight(carol, tempMinerHeight) - err = wait.Predicate(func() bool { - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) + // Make sure all active nodes are synced. + ht.AssertActiveNodesSynced() - _, err = carol.RPC.LN.GetChanInfo(ctxt, &lnrpc.ChanInfoRequest{ - ChanId: aliasmgr.StartingAlias.ToUint64(), - }) - - return err == nil - }, defaultTimeout) - require.NoError(ht.T, err, "carol doesn't have zero-conf edge") + // Carol should have the channel once synced. + carol.RPC.GetChanInfo(&lnrpc.ChanInfoRequest{ + ChanId: aliasmgr.StartingAlias.ToUint64(), + }) // Mine the zero-conf funding transaction so the test doesn't fail. - ht.MineBlocks(1) + ht.MineBlocksAndAssertNumTxes(1, 1) } diff --git a/lntest/itest/log_check_errors.sh b/itest/log_check_errors.sh similarity index 100% rename from lntest/itest/log_check_errors.sh rename to itest/log_check_errors.sh diff --git a/lntest/itest/log_substitutions.txt b/itest/log_substitutions.txt similarity index 100% rename from lntest/itest/log_substitutions.txt rename to itest/log_substitutions.txt diff --git a/lncfg/address_test.go b/lncfg/address_test.go index a738523a8..2066aecc3 100644 --- a/lncfg/address_test.go +++ b/lncfg/address_test.go @@ -1,6 +1,3 @@ -//go:build !rpctest -// +build !rpctest - package lncfg import ( diff --git a/lncfg/protocol.go b/lncfg/protocol.go index 238e8c485..876b009c1 100644 --- a/lncfg/protocol.go +++ b/lncfg/protocol.go @@ -1,5 +1,4 @@ -//go:build !rpctest -// +build !rpctest +//go:build !integration package lncfg diff --git a/lncfg/protocol_rpctest.go b/lncfg/protocol_integration.go similarity index 97% rename from lncfg/protocol_rpctest.go rename to lncfg/protocol_integration.go index 7fca60f10..b2c64cb47 100644 --- a/lncfg/protocol_rpctest.go +++ b/lncfg/protocol_integration.go @@ -1,5 +1,4 @@ -//go:build rpctest -// +build rpctest +//go:build integration package lncfg @@ -7,6 +6,8 @@ package lncfg // compatibility of protocol additions, while defaulting to the latest within // lnd, or to enable experimental protocol changes. // +// TODO(yy): delete this build flag to unify with `lncfg/protocol.go`. +// //nolint:lll type ProtocolOptions struct { // LegacyProtocol is a sub-config that houses all the legacy protocol diff --git a/lntemp/fee_service.go b/lntemp/fee_service.go deleted file mode 100644 index aac4cbd1d..000000000 --- a/lntemp/fee_service.go +++ /dev/null @@ -1,140 +0,0 @@ -package lntemp - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "sync" - "testing" - - "github.com/lightningnetwork/lnd/lntest" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" - "github.com/stretchr/testify/require" -) - -// WebFeeService defines an interface that's used to provide fee estimation -// service used in the integration tests. It must provide an URL so that a lnd -// node can be started with the flag `--feeurl` and uses the customized fee -// estimator. -type WebFeeService interface { - // Start starts the service. - Start() error - - // Stop stops the service. - Stop() error - - // URL returns the service's endpoint. - URL() string - - // SetFeeRate sets the estimated fee rate for a given confirmation - // target. - SetFeeRate(feeRate chainfee.SatPerKWeight, conf uint32) -} - -const ( - // feeServiceTarget is the confirmation target for which a fee estimate - // is returned. Requests for higher confirmation targets will fall back - // to this. - feeServiceTarget = 1 - - // DefaultFeeRateSatPerKw specifies the default fee rate used in the - // tests. - DefaultFeeRateSatPerKw = 12500 -) - -// FeeService runs a web service that provides fee estimation information. -type FeeService struct { - *testing.T - - feeRateMap map[uint32]uint32 - url string - - srv *http.Server - wg sync.WaitGroup - lock sync.Mutex -} - -// Compile-time check for the WebFeeService interface. -var _ WebFeeService = (*FeeService)(nil) - -// Start spins up a go-routine to serve fee estimates. -func NewFeeService(t *testing.T) *FeeService { - port := lntest.NextAvailablePort() - f := FeeService{ - T: t, - url: fmt.Sprintf( - "http://localhost:%v/fee-estimates.json", port, - ), - } - - // Initialize default fee estimate. - f.feeRateMap = map[uint32]uint32{ - feeServiceTarget: DefaultFeeRateSatPerKw, - } - - listenAddr := fmt.Sprintf(":%v", port) - mux := http.NewServeMux() - mux.HandleFunc("/fee-estimates.json", f.handleRequest) - - f.srv = &http.Server{ - Addr: listenAddr, - Handler: mux, - } - return &f -} - -// Start starts the web server. -func (f *FeeService) Start() error { - f.wg.Add(1) - go func() { - defer f.wg.Done() - - if err := f.srv.ListenAndServe(); err != http.ErrServerClosed { - require.NoErrorf(f, err, "cannot start fee api") - } - }() - - return nil -} - -// handleRequest handles a client request for fee estimates. -func (f *FeeService) handleRequest(w http.ResponseWriter, r *http.Request) { - f.lock.Lock() - defer f.lock.Unlock() - - bytes, err := json.Marshal( - struct { - Fees map[uint32]uint32 `json:"fee_by_block_target"` - }{ - Fees: f.feeRateMap, - }, - ) - require.NoErrorf(f, err, "cannot serialize estimates") - - _, err = io.WriteString(w, string(bytes)) - require.NoError(f, err, "cannot send estimates") -} - -// Stop stops the web server. -func (f *FeeService) Stop() error { - err := f.srv.Shutdown(context.Background()) - require.NoError(f, err, "cannot stop fee api") - - f.wg.Wait() - return nil -} - -// SetFeeRate sets a fee for the given confirmation target. -func (f *FeeService) SetFeeRate(fee chainfee.SatPerKWeight, conf uint32) { - f.lock.Lock() - defer f.lock.Unlock() - - f.feeRateMap[conf] = uint32(fee.FeePerKVByte()) -} - -// URL returns the service endpoint. -func (f *FeeService) URL() string { - return f.url -} diff --git a/lntemp/harness_miner.go b/lntemp/harness_miner.go deleted file mode 100644 index 9c9b758f2..000000000 --- a/lntemp/harness_miner.go +++ /dev/null @@ -1,438 +0,0 @@ -package lntemp - -import ( - "bytes" - "context" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/btcsuite/btcd/btcjson" - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/integration/rpctest" - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd/lntest" - "github.com/lightningnetwork/lnd/lntest/wait" - "github.com/stretchr/testify/require" -) - -const ( - // minerLogFilename is the default log filename for the miner node. - minerLogFilename = "output_btcd_miner.log" - - // minerLogDir is the default log dir for the miner node. - minerLogDir = ".minerlogs" - - // slowMineDelay defines a wait period between mining new blocks. - slowMineDelay = 100 * time.Millisecond -) - -var harnessNetParams = &chaincfg.RegressionNetParams - -type HarnessMiner struct { - *testing.T - *rpctest.Harness - - // runCtx is a context with cancel method. It's used to signal when the - // node needs to quit, and used as the parent context when spawning - runCtx context.Context - cancel context.CancelFunc - - // logPath is the directory path of the miner's logs. - logPath string - - // logFilename is the saved log filename of the miner node. - logFilename string -} - -// NewMiner creates a new miner using btcd backend with the default log file -// dir and name. -func NewMiner(ctxt context.Context, t *testing.T) *HarnessMiner { - return newMiner(ctxt, t, minerLogDir, minerLogFilename) -} - -// NewTempMiner creates a new miner using btcd backend with the specified log -// file dir and name. -func NewTempMiner(ctxt context.Context, t *testing.T, - tempDir, tempLogFilename string) *HarnessMiner { - - t.Helper() - - return newMiner(ctxt, t, tempDir, tempLogFilename) -} - -// newMiner creates a new miner using btcd's rpctest. -func newMiner(ctxb context.Context, t *testing.T, minerDirName, - logFilename string) *HarnessMiner { - - handler := &rpcclient.NotificationHandlers{} - btcdBinary := lntest.GetBtcdBinary() - baseLogPath := fmt.Sprintf("%s/%s", lntest.GetLogDir(), minerDirName) - - args := []string{ - "--rejectnonstd", - "--txindex", - "--nowinservice", - "--nobanning", - "--debuglevel=debug", - "--logdir=" + baseLogPath, - "--trickleinterval=100ms", - // Don't disconnect if a reply takes too long. - "--nostalldetect", - } - - miner, err := rpctest.New(harnessNetParams, handler, args, btcdBinary) - require.NoError(t, err, "unable to create mining node") - - ctxt, cancel := context.WithCancel(ctxb) - return &HarnessMiner{ - T: t, - Harness: miner, - runCtx: ctxt, - cancel: cancel, - logPath: baseLogPath, - logFilename: logFilename, - } -} - -// saveLogs copies the node logs and save it to the file specified by -// h.logFilename. -func (h *HarnessMiner) saveLogs() { - // After shutting down the miner, we'll make a copy of the log files - // before deleting the temporary log dir. - path := fmt.Sprintf("%s/%s", h.logPath, harnessNetParams.Name) - files, err := ioutil.ReadDir(path) - require.NoError(h, err, "unable to read log directory") - - for _, file := range files { - newFilename := strings.Replace( - file.Name(), "btcd.log", h.logFilename, 1, - ) - copyPath := fmt.Sprintf("%s/../%s", h.logPath, newFilename) - - logFile := fmt.Sprintf("%s/%s", path, file.Name()) - err := CopyFile(filepath.Clean(copyPath), logFile) - require.NoError(h, err, "unable to copy file") - } - - err = os.RemoveAll(h.logPath) - require.NoErrorf(h, err, "cannot remove dir %s", h.logPath) -} - -// Stop shuts down the miner and saves its logs. -func (h *HarnessMiner) Stop() { - h.cancel() - require.NoError(h, h.TearDown(), "tear down miner got error") - h.saveLogs() -} - -// GetBestBlock makes a RPC request to miner and asserts. -func (h *HarnessMiner) GetBestBlock() (*chainhash.Hash, int32) { - blockHash, height, err := h.Client.GetBestBlock() - require.NoError(h, err, "failed to GetBestBlock") - return blockHash, height -} - -// GetRawMempool makes a RPC call to the miner's GetRawMempool and -// asserts. -func (h *HarnessMiner) GetRawMempool() []*chainhash.Hash { - mempool, err := h.Client.GetRawMempool() - require.NoError(h, err, "unable to get mempool") - return mempool -} - -// GenerateBlocks mine 'num' of blocks and returns them. -func (h *HarnessMiner) GenerateBlocks(num uint32) []*chainhash.Hash { - blockHashes, err := h.Client.Generate(num) - require.NoError(h, err, "unable to generate blocks") - require.Len(h, blockHashes, int(num), "wrong num of blocks generated") - - return blockHashes -} - -// GetBlock gets a block using its block hash. -func (h *HarnessMiner) GetBlock(blockHash *chainhash.Hash) *wire.MsgBlock { - block, err := h.Client.GetBlock(blockHash) - require.NoError(h, err, "unable to get block") - return block -} - -// MineBlocks mine 'num' of blocks and check that blocks are present in -// node blockchain. -func (h *HarnessMiner) MineBlocks(num uint32) []*wire.MsgBlock { - blocks := make([]*wire.MsgBlock, num) - - blockHashes := h.GenerateBlocks(num) - - for i, blockHash := range blockHashes { - block := h.GetBlock(blockHash) - blocks[i] = block - } - - return blocks -} - -// AssertNumTxsInMempool polls until finding the desired number of transactions -// in the provided miner's mempool. It will asserrt if this number is not met -// after the given timeout. -func (h *HarnessMiner) AssertNumTxsInMempool(n int) []*chainhash.Hash { - var ( - mem []*chainhash.Hash - err error - ) - - err = wait.NoError(func() error { - // We require the RPC call to be succeeded and won't wait for - // it as it's an unexpected behavior. - mem = h.GetRawMempool() - if len(mem) == n { - return nil - } - - return fmt.Errorf("want %v, got %v in mempool: %v", - n, len(mem), mem) - }, lntest.MinerMempoolTimeout) - require.NoError(h, err, "assert tx in mempool timeout") - - return mem -} - -// AssertTxInBlock asserts that a given txid can be found in the passed block. -func (h *HarnessMiner) AssertTxInBlock(block *wire.MsgBlock, - txid *chainhash.Hash) { - - blockTxes := make([]chainhash.Hash, 0) - - for _, tx := range block.Transactions { - sha := tx.TxHash() - blockTxes = append(blockTxes, sha) - - if bytes.Equal(txid[:], sha[:]) { - return - } - } - - require.Failf(h, "tx was not included in block", "tx:%v, block has:%v", - txid, blockTxes) -} - -// MineBlocksAndAssertNumTxes mine 'num' of blocks and check that blocks are -// present in node blockchain. numTxs should be set to the number of -// transactions (excluding the coinbase) we expect to be included in the first -// mined block. -func (h *HarnessMiner) MineBlocksAndAssertNumTxes(num uint32, - numTxs int) []*wire.MsgBlock { - - // If we expect transactions to be included in the blocks we'll mine, - // we wait here until they are seen in the miner's mempool. - txids := h.AssertNumTxsInMempool(numTxs) - - // Mine blocks. - blocks := h.MineBlocks(num) - - // Finally, assert that all the transactions were included in the first - // block. - for _, txid := range txids { - h.AssertTxInBlock(blocks[0], txid) - } - - return blocks -} - -// GetRawTransaction makes a RPC call to the miner's GetRawTransaction and -// asserts. -func (h *HarnessMiner) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx { - tx, err := h.Client.GetRawTransaction(txid) - require.NoErrorf(h, err, "failed to get raw tx: %v", txid) - return tx -} - -// GetRawTransactionVerbose makes a RPC call to the miner's -// GetRawTransactionVerbose and asserts. -func (h *HarnessMiner) GetRawTransactionVerbose( - txid *chainhash.Hash) *btcjson.TxRawResult { - - tx, err := h.Client.GetRawTransactionVerbose(txid) - require.NoErrorf(h, err, "failed to get raw tx verbose: %v", txid) - return tx -} - -// AssertTxInMempool asserts a given transaction can be found in the mempool. -func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx { - var msgTx *wire.MsgTx - - err := wait.NoError(func() error { - // We require the RPC call to be succeeded and won't wait for - // it as it's an unexpected behavior. - mempool := h.GetRawMempool() - - if len(mempool) == 0 { - return fmt.Errorf("empty mempool") - } - - for _, memTx := range mempool { - // Check the values are equal. - if *memTx == *txid { - return nil - } - } - - return fmt.Errorf("txid %v not found in mempool: %v", txid, - mempool) - }, lntest.MinerMempoolTimeout) - - require.NoError(h, err, "timeout checking mempool") - return msgTx -} - -// SendOutputsWithoutChange uses the miner to send the given outputs using the -// specified fee rate and returns the txid. -func (h *HarnessMiner) SendOutputsWithoutChange(outputs []*wire.TxOut, - feeRate btcutil.Amount) *chainhash.Hash { - - txid, err := h.Harness.SendOutputsWithoutChange( - outputs, feeRate, - ) - require.NoErrorf(h, err, "failed to send output") - - return txid -} - -// CreateTransaction uses the miner to create a transaction using the given -// outputs using the specified fee rate and returns the transaction. -func (h *HarnessMiner) CreateTransaction(outputs []*wire.TxOut, - feeRate btcutil.Amount) *wire.MsgTx { - - tx, err := h.Harness.CreateTransaction(outputs, feeRate, false) - require.NoErrorf(h, err, "failed to create transaction") - - return tx -} - -// SendOutput creates, signs, and finally broadcasts a transaction spending -// the harness' available mature coinbase outputs to create the new output. -func (h *HarnessMiner) SendOutput(newOutput *wire.TxOut, - feeRate btcutil.Amount) *chainhash.Hash { - - hash, err := h.Harness.SendOutputs([]*wire.TxOut{newOutput}, feeRate) - require.NoErrorf(h, err, "failed to send outputs") - - return hash -} - -// MineBlocksSlow mines 'num' of blocks. Between each mined block an artificial -// delay is introduced to give all network participants time to catch up. -func (h *HarnessMiner) MineBlocksSlow(num uint32) []*wire.MsgBlock { - blocks := make([]*wire.MsgBlock, num) - blockHashes := make([]*chainhash.Hash, 0, num) - - for i := uint32(0); i < num; i++ { - generatedHashes := h.GenerateBlocks(1) - blockHashes = append(blockHashes, generatedHashes...) - - time.Sleep(slowMineDelay) - } - - for i, blockHash := range blockHashes { - block, err := h.Client.GetBlock(blockHash) - require.NoError(h, err, "get blocks") - - blocks[i] = block - } - - return blocks -} - -// AssertOutpointInMempool asserts a given outpoint can be found in the mempool. -func (h *HarnessMiner) AssertOutpointInMempool(op wire.OutPoint) *wire.MsgTx { - var msgTx *wire.MsgTx - - err := wait.NoError(func() error { - // We require the RPC call to be succeeded and won't wait for - // it as it's an unexpected behavior. - mempool := h.GetRawMempool() - - if len(mempool) == 0 { - return fmt.Errorf("empty mempool") - } - - for _, txid := range mempool { - // We require the RPC call to be succeeded and won't - // wait for it as it's an unexpected behavior. - tx := h.GetRawTransaction(txid) - - msgTx = tx.MsgTx() - for _, txIn := range msgTx.TxIn { - if txIn.PreviousOutPoint == op { - return nil - } - } - } - - return fmt.Errorf("outpoint %v not found in mempool", op) - }, lntest.MinerMempoolTimeout) - - require.NoError(h, err, "timeout checking mempool") - - return msgTx -} - -// GetNumTxsFromMempool polls until finding the desired number of transactions -// in the miner's mempool and returns the full transactions to the caller. -func (h *HarnessMiner) GetNumTxsFromMempool(n int) []*wire.MsgTx { - txids := h.AssertNumTxsInMempool(n) - - var txes []*wire.MsgTx - for _, txid := range txids { - tx := h.GetRawTransaction(txid) - txes = append(txes, tx.MsgTx()) - } - - return txes -} - -// NewMinerAddress creates a new address for the miner and asserts. -func (h *HarnessMiner) NewMinerAddress() btcutil.Address { - addr, err := h.NewAddress() - require.NoError(h, err, "failed to create new miner address") - return addr -} - -// MineBlocksWithTxes mines a single block to include the specifies -// transactions only. -func (h *HarnessMiner) MineBlockWithTxes(txes []*btcutil.Tx) *wire.MsgBlock { - var emptyTime time.Time - - // Generate a block. - b, err := h.GenerateAndSubmitBlock(txes, -1, emptyTime) - require.NoError(h, err, "unable to mine block") - - block, err := h.Client.GetBlock(b.Hash()) - require.NoError(h, err, "unable to get block") - - return block -} - -// MineEmptyBlocks mines a given number of empty blocks. -func (h *HarnessMiner) MineEmptyBlocks(num int) []*wire.MsgBlock { - var emptyTime time.Time - - blocks := make([]*wire.MsgBlock, num) - for i := 0; i < num; i++ { - // Generate an empty block. - b, err := h.GenerateAndSubmitBlock(nil, -1, emptyTime) - require.NoError(h, err, "unable to mine empty block") - - block := h.GetBlock(b.Hash()) - blocks[i] = block - } - - return blocks -} diff --git a/lntemp/README.md b/lntest/README.md similarity index 75% rename from lntemp/README.md rename to lntest/README.md index b4c7e65aa..be28b218f 100644 --- a/lntemp/README.md +++ b/lntest/README.md @@ -93,3 +93,24 @@ making a new test case using `Subtest`, there’s a cleanup function which further validates the current test case has no dangling uncleaned states, such as transactions left in mempool, open channels, etc. +### Different Code Used in `lntest` + +Since the miner in `lntest` uses regtest, it has a very fast block production +rate, which is the greatest difference between the conditions it simulates and +the real-world has. Aside from that, `lnd` has several places that use +different code, which is triggered by the build flag `integration`, to speed up +the tests. They are summarized as followings, + +1. `funding.checkPeerFundingLockInterval`, which is used when we wait for the + peer to send us `FundingLocked`. This value is 1 second in `lnd`, and 10 + milliseconds in `lntest`. +2. `lncfg.ProtocolOptions`, which is used to specify protocol flags. In `lnd`, + anchor and script enforced lease are enabled by default, while in `lntest`, + they are disabled by default. +3. Reduced scrypt parameters are used in `lntest`. In `lnd`, the parameters N, + R, and P are imported from `snacl`, while in `lntest` they are replaced with + `waddrmgr.FastScryptOptions`. Both `macaroon` and `aezeed` are affected. +4. The method, `nextRevocationProducer`, defined in `LightningWallet` is + slightly different. For `lnwallet`, it will check a special pre-defined + channel ID to test restoring channel backups created with the old revocation + root derivation method. diff --git a/lntest/bitcoind_common.go b/lntest/bitcoind_common.go index d9bf102f6..8cac2e5af 100644 --- a/lntest/bitcoind_common.go +++ b/lntest/bitcoind_common.go @@ -14,6 +14,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/rpcclient" + "github.com/lightningnetwork/lnd/lntest/node" ) // logDirPattern is the pattern of the name of the temporary log directory. @@ -37,7 +38,7 @@ type BitcoindBackendConfig struct { // A compile time assertion to ensure BitcoindBackendConfig meets the // BackendConfig interface. -var _ BackendConfig = (*BitcoindBackendConfig)(nil) +var _ node.BackendConfig = (*BitcoindBackendConfig)(nil) // GenArgs returns the arguments needed to be passed to LND at startup for // using this node as a chain backend. @@ -89,7 +90,7 @@ func (b BitcoindBackendConfig) Name() string { func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, rpcPolling bool) (*BitcoindBackendConfig, func() error, error) { - baseLogDir := fmt.Sprintf(logDirPattern, GetLogDir()) + baseLogDir := fmt.Sprintf(logDirPattern, node.GetLogDir()) if netParams != &chaincfg.RegressionNetParams { return nil, nil, fmt.Errorf("only regtest supported") } @@ -109,10 +110,12 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, fmt.Errorf("unable to create temp directory: %v", err) } - zmqBlockAddr := fmt.Sprintf("tcp://127.0.0.1:%d", NextAvailablePort()) - zmqTxAddr := fmt.Sprintf("tcp://127.0.0.1:%d", NextAvailablePort()) - rpcPort := NextAvailablePort() - p2pPort := NextAvailablePort() + zmqBlockAddr := fmt.Sprintf("tcp://127.0.0.1:%d", + node.NextAvailablePort()) + zmqTxAddr := fmt.Sprintf("tcp://127.0.0.1:%d", + node.NextAvailablePort()) + rpcPort := node.NextAvailablePort() + p2pPort := node.NextAvailablePort() cmdArgs := []string{ "-datadir=" + tempBitcoindDir, @@ -146,9 +149,9 @@ func newBackend(miner string, netParams *chaincfg.Params, extraArgs []string, // After shutting down the chain backend, we'll make a copy of // the log file before deleting the temporary log dir. logDestination := fmt.Sprintf( - "%s/output_bitcoind_chainbackend.log", GetLogDir(), + "%s/output_bitcoind_chainbackend.log", node.GetLogDir(), ) - err := CopyFile(logDestination, logFile) + err := node.CopyFile(logDestination, logFile) if err != nil { errStr += fmt.Sprintf("unable to copy file: %v\n", err) } diff --git a/lntest/btcd.go b/lntest/btcd.go index 200faa9bf..3ba613539 100644 --- a/lntest/btcd.go +++ b/lntest/btcd.go @@ -15,6 +15,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/integration/rpctest" "github.com/btcsuite/btcd/rpcclient" + "github.com/lightningnetwork/lnd/lntest/node" ) // logDirPattern is the pattern of the name of the temporary log directory. @@ -41,7 +42,7 @@ type BtcdBackendConfig struct { // A compile time assertion to ensure BtcdBackendConfig meets the BackendConfig // interface. -var _ BackendConfig = (*BtcdBackendConfig)(nil) +var _ node.BackendConfig = (*BtcdBackendConfig)(nil) // GenArgs returns the arguments needed to be passed to LND at startup for // using this node as a chain backend. @@ -83,7 +84,7 @@ func (b BtcdBackendConfig) Name() string { func NewBackend(miner string, netParams *chaincfg.Params) ( *BtcdBackendConfig, func() error, error) { - baseLogDir := fmt.Sprintf(logDirPattern, GetLogDir()) + baseLogDir := fmt.Sprintf(logDirPattern, node.GetLogDir()) args := []string{ "--rejectnonstd", "--txindex", @@ -98,9 +99,12 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( // Don't disconnect if a reply takes too long. "--nostalldetect", } - chainBackend, err := rpctest.New(netParams, nil, args, GetBtcdBinary()) + chainBackend, err := rpctest.New( + netParams, nil, args, node.GetBtcdBinary(), + ) if err != nil { - return nil, nil, fmt.Errorf("unable to create btcd node: %v", err) + return nil, nil, fmt.Errorf("unable to create btcd node: %w", + err) } // We want to overwrite some of the connection settings to make the @@ -108,11 +112,17 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( // are already blocks present, which will take a bit longer than the // 1 second the default settings amount to. Doubling both values will // give us retries up to 4 seconds. - chainBackend.MaxConnRetries = rpctest.DefaultMaxConnectionRetries * 2 - chainBackend.ConnectionRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2 + const ( + maxConnRetries = rpctest.DefaultMaxConnectionRetries * 2 + connRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2 + ) + + chainBackend.MaxConnRetries = maxConnRetries + chainBackend.ConnectionRetryTimeout = connRetryTimeout if err := chainBackend.SetUp(false, 0); err != nil { - return nil, nil, fmt.Errorf("unable to set up btcd backend: %v", err) + return nil, nil, fmt.Errorf("unable to set up btcd backend: %w", + err) } bd := &BtcdBackendConfig{ @@ -141,14 +151,16 @@ func NewBackend(miner string, netParams *chaincfg.Params) ( for _, file := range files { logFile := fmt.Sprintf("%s/%s", logDir, file.Name()) newFilename := strings.Replace( - file.Name(), "btcd.log", "output_btcd_chainbackend.log", 1, + file.Name(), "btcd.log", + "output_btcd_chainbackend.log", 1, ) logDestination := fmt.Sprintf( - "%s/%s", GetLogDir(), newFilename, + "%s/%s", node.GetLogDir(), newFilename, ) - err := CopyFile(logDestination, logFile) + err := node.CopyFile(logDestination, logFile) if err != nil { - errStr += fmt.Sprintf("unable to copy file: %v\n", err) + errStr += fmt.Sprintf("unable to copy file: "+ + "%v\n", err) } } diff --git a/lntest/fee_service.go b/lntest/fee_service.go index e2c1df361..40128b68d 100644 --- a/lntest/fee_service.go +++ b/lntest/fee_service.go @@ -9,45 +9,72 @@ import ( "sync" "testing" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/stretchr/testify/require" ) +// WebFeeService defines an interface that's used to provide fee estimation +// service used in the integration tests. It must provide an URL so that a lnd +// node can be started with the flag `--feeurl` and uses the customized fee +// estimator. +type WebFeeService interface { + // Start starts the service. + Start() error + + // Stop stops the service. + Stop() error + + // URL returns the service's endpoint. + URL() string + + // SetFeeRate sets the estimated fee rate for a given confirmation + // target. + SetFeeRate(feeRate chainfee.SatPerKWeight, conf uint32) +} + const ( // feeServiceTarget is the confirmation target for which a fee estimate // is returned. Requests for higher confirmation targets will fall back // to this. feeServiceTarget = 1 + + // DefaultFeeRateSatPerKw specifies the default fee rate used in the + // tests. + DefaultFeeRateSatPerKw = 12500 ) -// feeService runs a web service that provides fee estimation information. -type feeService struct { - feeEstimates +// FeeService runs a web service that provides fee estimation information. +type FeeService struct { + *testing.T - t *testing.T - - srv *http.Server - wg sync.WaitGroup - - url string + feeRateMap map[uint32]uint32 + url string + srv *http.Server + wg sync.WaitGroup lock sync.Mutex } -// feeEstimates contains the current fee estimates. -type feeEstimates struct { - Fees map[uint32]uint32 `json:"fee_by_block_target"` -} +// Compile-time check for the WebFeeService interface. +var _ WebFeeService = (*FeeService)(nil) -// startFeeService spins up a go-routine to serve fee estimates. -func startFeeService(t *testing.T) *feeService { - port := NextAvailablePort() - f := feeService{ - t: t, - url: fmt.Sprintf("http://localhost:%v/fee-estimates.json", port), +// Start spins up a go-routine to serve fee estimates. +func NewFeeService(t *testing.T) *FeeService { + t.Helper() + + port := node.NextAvailablePort() + f := FeeService{ + T: t, + url: fmt.Sprintf( + "http://localhost:%v/fee-estimates.json", port, + ), } // Initialize default fee estimate. - f.Fees = map[uint32]uint32{feeServiceTarget: 50000} + f.feeRateMap = map[uint32]uint32{ + feeServiceTarget: DefaultFeeRateSatPerKw, + } listenAddr := fmt.Sprintf(":%v", port) mux := http.NewServeMux() @@ -58,57 +85,60 @@ func startFeeService(t *testing.T) *feeService { Handler: mux, } + return &f +} + +// Start starts the web server. +func (f *FeeService) Start() error { f.wg.Add(1) go func() { defer f.wg.Done() if err := f.srv.ListenAndServe(); err != http.ErrServerClosed { - f.t.Errorf("error: cannot start fee api: %v", err) + require.NoErrorf(f, err, "cannot start fee api") } }() - return &f + return nil } // handleRequest handles a client request for fee estimates. -func (f *feeService) handleRequest(w http.ResponseWriter, r *http.Request) { +func (f *FeeService) handleRequest(w http.ResponseWriter, r *http.Request) { f.lock.Lock() defer f.lock.Unlock() - bytes, err := json.Marshal(f.feeEstimates) - if err != nil { - f.t.Errorf("error: cannot serialize estimates: %v", err) - - return - } + bytes, err := json.Marshal( + struct { + Fees map[uint32]uint32 `json:"fee_by_block_target"` + }{ + Fees: f.feeRateMap, + }, + ) + require.NoErrorf(f, err, "cannot serialize estimates") _, err = io.WriteString(w, string(bytes)) - if err != nil { - f.t.Errorf("error: cannot send estimates: %v", err) - } + require.NoError(f, err, "cannot send estimates") } -// stop stops the web server. -func (f *feeService) stop() { - if err := f.srv.Shutdown(context.Background()); err != nil { - f.t.Errorf("error: cannot stop fee api: %v", err) - } +// Stop stops the web server. +func (f *FeeService) Stop() error { + err := f.srv.Shutdown(context.Background()) + require.NoError(f, err, "cannot stop fee api") f.wg.Wait() + + return nil } -// setFee changes the current fee estimate for the fixed confirmation target. -func (f *feeService) setFee(fee chainfee.SatPerKWeight) { +// SetFeeRate sets a fee for the given confirmation target. +func (f *FeeService) SetFeeRate(fee chainfee.SatPerKWeight, conf uint32) { f.lock.Lock() defer f.lock.Unlock() - f.Fees[feeServiceTarget] = uint32(fee.FeePerKVByte()) + f.feeRateMap[conf] = uint32(fee.FeePerKVByte()) } -// setFeeWithConf sets a fee for the given confirmation target. -func (f *feeService) setFeeWithConf(fee chainfee.SatPerKWeight, conf uint32) { - f.lock.Lock() - defer f.lock.Unlock() - - f.Fees[conf] = uint32(fee.FeePerKVByte()) +// URL returns the service endpoint. +func (f *FeeService) URL() string { + return f.url } diff --git a/lntest/fee_service_test.go b/lntest/fee_service_test.go deleted file mode 100644 index 228d7c30e..000000000 --- a/lntest/fee_service_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package lntest - -import ( - "io/ioutil" - "net/http" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -// TestFeeService tests the itest fee estimating web service. -func TestFeeService(t *testing.T) { - service := startFeeService(t) - defer service.stop() - - service.setFee(5000) - - // Wait for service to start accepting connections. - var resp *http.Response - require.Eventually( - t, - func() bool { - var err error - resp, err = http.Get(service.url) // nolint:bodyclose - return err == nil - }, - 10*time.Second, time.Second, - ) - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - require.NoError(t, err) - - require.Equal( - t, "{\"fee_by_block_target\":{\"1\":20000}}", string(body), - ) -} diff --git a/lntemp/harness.go b/lntest/harness.go similarity index 97% rename from lntemp/harness.go rename to lntest/harness.go index 781a5ccf3..963826a1e 100644 --- a/lntemp/harness.go +++ b/lntest/harness.go @@ -1,4 +1,4 @@ -package lntemp +package lntest import ( "context" @@ -17,9 +17,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntemp/rpc" - "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/lnwire" @@ -38,6 +37,10 @@ const ( // numBlocksOpenChannel specifies the number of blocks mined when // opening a channel. numBlocksOpenChannel = 6 + + // lndErrorChanSize specifies the buffer size used to receive errors + // from lnd process. + lndErrorChanSize = 10 ) // TestCase defines a test case that's been used in the integration test. @@ -85,7 +88,7 @@ type HarnessTest struct { // runCtx is a context with cancel method. It's used to signal when the // node needs to quit, and used as the parent context when spawning // children contexts for RPC requests. - runCtx context.Context + runCtx context.Context //nolint:containedctx cancel context.CancelFunc // stopChainBackend points to the cleanup function returned by the @@ -100,12 +103,15 @@ type HarnessTest struct { // NewHarnessTest creates a new instance of a harnessTest from a regular // testing.T instance. func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService, - dbBackend lntest.DatabaseBackend) *HarnessTest { + dbBackend node.DatabaseBackend) *HarnessTest { + + t.Helper() // Create the run context. ctxt, cancel := context.WithCancel(context.Background()) manager := newNodeManager(lndBinary, dbBackend) + return &HarnessTest{ T: t, manager: manager, @@ -114,7 +120,7 @@ func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService, cancel: cancel, // We need to use buffered channel here as we don't want to // block sending errors. - lndErrorChan: make(chan error, 10), + lndErrorChan: make(chan error, lndErrorChanSize), } } @@ -180,21 +186,18 @@ func (h *HarnessTest) SetupStandbyNodes() { h.Alice = h.NewNode("Alice", lndArgs) h.Bob = h.NewNode("Bob", lndArgs) - // First, make a connection between the two nodes. This will wait until - // both nodes are fully started since the Connect RPC is guarded behind - // the server.Started() flag that waits for all subsystems to be ready. - h.ConnectNodes(h.Alice, h.Bob) - addrReq := &lnrpc.NewAddressRequest{ Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH, } - // Load up the wallets of the seeder nodes with 10 outputs of 10 BTC + const initialFund = 1 * btcutil.SatoshiPerBitcoin + + // Load up the wallets of the seeder nodes with 100 outputs of 1 BTC // each. nodes := []*node.HarnessNode{h.Alice, h.Bob} for _, hn := range nodes { h.manager.standbyNodes[hn.Cfg.NodeID] = hn - for i := 0; i < 10; i++ { + for i := 0; i < 100; i++ { resp := hn.RPC.NewAddress(addrReq) addr, err := btcutil.DecodeAddress( @@ -207,7 +210,7 @@ func (h *HarnessTest) SetupStandbyNodes() { output := &wire.TxOut{ PkScript: addrScript, - Value: 10 * btcutil.SatoshiPerBitcoin, + Value: initialFund, } h.Miner.SendOutput(output, defaultMinerFeeRate) } @@ -215,7 +218,7 @@ func (h *HarnessTest) SetupStandbyNodes() { // We generate several blocks in order to give the outputs created // above a good number of confirmations. - const totalTxes = 20 + const totalTxes = 200 h.MineBlocksAndAssertNumTxes(numBlocksSendOutput, totalTxes) // Now we want to wait for the nodes to catch up. @@ -223,7 +226,7 @@ func (h *HarnessTest) SetupStandbyNodes() { h.WaitForBlockchainSync(h.Bob) // Now block until both wallets have fully synced up. - expectedBalance := int64(btcutil.SatoshiPerBitcoin * 100) + const expectedBalance = 100 * initialFund err := wait.NoError(func() error { aliceResp := h.Alice.RPC.WalletBalance() bobResp := h.Bob.RPC.WalletBalance() @@ -286,6 +289,8 @@ func (h *HarnessTest) RunTestCase(testCase *TestCase) { // resetStandbyNodes resets all standby nodes by attaching the new testing.T // and restarting them with the original config. func (h *HarnessTest) resetStandbyNodes(t *testing.T) { + t.Helper() + for _, hn := range h.manager.standbyNodes { // Inherit the testing.T. h.T = t @@ -313,7 +318,7 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { Miner: h.Miner, standbyNodes: h.standbyNodes, feeService: h.feeService, - lndErrorChan: make(chan error, 10), + lndErrorChan: make(chan error, lndErrorChanSize), } // Inherit context from the main test. @@ -341,6 +346,7 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { // Don't bother run the cleanups if the test is failed. if st.Failed() { st.Log("test failed, skipped cleanup") + st.shutdownAllNodes() return } @@ -381,10 +387,21 @@ func (h *HarnessTest) Subtest(t *testing.T) *HarnessTest { // shutdownNonStandbyNodes will shutdown any non-standby nodes. func (h *HarnessTest) shutdownNonStandbyNodes() { + h.shutdownNodes(true) +} + +// shutdownAllNodes will shutdown all running nodes. +func (h *HarnessTest) shutdownAllNodes() { + h.shutdownNodes(false) +} + +// shutdownNodes will shutdown any non-standby nodes. If skipStandby is false, +// all the standby nodes will be shutdown too. +func (h *HarnessTest) shutdownNodes(skipStandby bool) { for nid, node := range h.manager.activeNodes { // If it's a standby node, skip. _, ok := h.manager.standbyNodes[nid] - if ok { + if ok && skipStandby { continue } @@ -394,7 +411,11 @@ func (h *HarnessTest) shutdownNonStandbyNodes() { err := wait.NoError(func() error { return h.manager.shutdownNode(node) }, DefaultTimeout) - require.NoErrorf(h, err, "unable to shutdown %s", node.Name()) + + // Instead of returning the error, we will log it instead. This + // is needed so other nodes can continue their shutdown + // processes. + h.Logf("unable to shutdown %s, got err: %v", node.Name(), err) } } @@ -636,7 +657,7 @@ func (h *HarnessTest) NewNodeEtcd(name string, etcdCfg *etcd.Config, leaderSessionTTL int) *node.HarnessNode { // We don't want to use the embedded etcd instance. - h.manager.dbBackend = lntest.BackendBbolt + h.manager.dbBackend = node.BackendBbolt extraArgs := node.ExtraArgsEtcd( etcdCfg, name, cluster, leaderSessionTTL, @@ -659,7 +680,7 @@ func (h *HarnessTest) NewNodeWithSeedEtcd(name string, etcdCfg *etcd.Config, leaderSessionTTL int) (*node.HarnessNode, []string, []byte) { // We don't want to use the embedded etcd instance. - h.manager.dbBackend = lntest.BackendBbolt + h.manager.dbBackend = node.BackendBbolt // Create a request to generate a new aezeed. The new seed will have // the same password as the internal wallet. @@ -1082,7 +1103,19 @@ func (h *HarnessTest) CloseChannelAssertPending(hn *node.HarnessNode, // transaction to be broadcast, then wait for the closing tx to be seen // within the network. event, err := h.ReceiveCloseChannelUpdate(stream) - require.NoError(h, err) + if err != nil { + // TODO(yy): remove the sleep once the following bug is fixed. + // We may receive the error `cannot co-op close channel with + // active htlcs` or `link failed to shutdown` if we close the + // channel. We need to investigate the order of settling the + // payments and updating commitments to properly fix it. + time.Sleep(2 * time.Second) + + // Give it another chance. + stream = hn.RPC.CloseChannel(closeReq) + event, err = h.ReceiveCloseChannelUpdate(stream) + require.NoError(h, err) + } pendingClose, ok := event.Update.(*lnrpc.CloseStatusUpdate_ClosePending) require.Truef(h, ok, "expected channel close update, instead got %v", @@ -1277,7 +1310,7 @@ func (h *HarnessTest) completePaymentRequestsAssertStatus(hn *node.HarnessNode, send := func(payReq string) { req := &routerrpc.SendPaymentRequest{ PaymentRequest: payReq, - TimeoutSeconds: defaultPaymentTimeout, + TimeoutSeconds: int32(wait.PaymentTimeout.Seconds()), FeeLimitMsat: noFeeLimitMsat, } stream := hn.RPC.SendPayment(req) @@ -1293,7 +1326,7 @@ func (h *HarnessTest) completePaymentRequestsAssertStatus(hn *node.HarnessNode, } // Wait for all payments to report the expected status. - timer := time.After(DefaultTimeout) + timer := time.After(wait.PaymentTimeout) select { case stream := <-results: h.AssertPaymentStatusFromStream(stream, status) @@ -1402,7 +1435,7 @@ func (h *HarnessTest) CleanupForceClose(hn *node.HarnessNode, // // The commit sweep resolver is able to broadcast the sweep tx up to // one block before the CSV elapses, so wait until defaulCSV-1. - h.MineBlocks(lntest.DefaultCSV - 1) + h.MineBlocks(node.DefaultCSV - 1) // The node should now sweep the funds, clean up by mining the sweeping // tx. @@ -1686,7 +1719,7 @@ func (h *HarnessTest) OpenMultiChannelsAsync( case cp := <-r.result: channelPoints = append(channelPoints, cp) - case <-time.After(lntest.ChannelOpenTimeout): + case <-time.After(wait.ChannelOpenTimeout): require.Failf(h, "timeout", "wait channel point "+ "timeout for channel %s=>%s", r.Local.Name(), r.Remote.Name()) diff --git a/lntemp/harness_assertion.go b/lntest/harness_assertion.go similarity index 98% rename from lntemp/harness_assertion.go rename to lntest/harness_assertion.go index 9b61c9186..f6f1ee96e 100644 --- a/lntemp/harness_assertion.go +++ b/lntest/harness_assertion.go @@ -1,4 +1,4 @@ -package lntemp +package lntest import ( "bytes" @@ -23,8 +23,8 @@ import ( "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntemp/rpc" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/rpc" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" @@ -38,6 +38,7 @@ func (h *HarnessTest) WaitForBlockchainSync(hn *node.HarnessNode) { if resp.SyncedToChain { return nil } + return fmt.Errorf("%s is not synced to chain", hn.Name()) }, DefaultTimeout) @@ -145,6 +146,12 @@ func (h *HarnessTest) EnsureConnected(a, b *node.HarnessNode) { // connected to the peer. errConnectionRequested := "connection request in progress" + // windowsErr is an error we've seen from windows build where + // connecting to an already connected node gives such error from the + // receiver side. + windowsErr := "An established connection was aborted by the software " + + "in your host machine." + tryConnect := func(a, b *node.HarnessNode) error { bInfo := b.RPC.GetInfo() @@ -165,17 +172,21 @@ func (h *HarnessTest) EnsureConnected(a, b *node.HarnessNode) { return nil } - // If the connection is in process, we return no error. - if strings.Contains(err.Error(), errConnectionRequested) { - return nil - } - // If the two are already connected, we return early with no // error. if strings.Contains(err.Error(), "already connected to peer") { return nil } + // Otherwise we log the error to console. + h.Logf("EnsureConnected %s=>%s got err: %v", a.Name(), + b.Name(), err) + + // If the connection is in process, we return no error. + if strings.Contains(err.Error(), errConnectionRequested) { + return nil + } + // We may get connection refused error if we happens to be in // the middle of a previous node disconnection, e.g., a restart // from one of the nodes. @@ -183,6 +194,16 @@ func (h *HarnessTest) EnsureConnected(a, b *node.HarnessNode) { return nil } + // Check for windows error. If Alice connects to Bob, Alice + // will throw "i/o timeout" and Bob will give windowsErr. + if strings.Contains(err.Error(), windowsErr) { + return nil + } + + if strings.Contains(err.Error(), "i/o timeout") { + return nil + } + return err } @@ -231,8 +252,10 @@ func (h *HarnessTest) AssertNumEdges(hn *node.HarnessNode, // slice. edges = chanGraph.Edges[old:] } + return nil } + return errNumNotMatched(hn.Name(), "num of channel edges", expected, total-old, total, old) }, DefaultTimeout) @@ -925,7 +948,9 @@ func (h *HarnessTest) AssertNodesNumPendingOpenChannels(a, b *node.HarnessNode, func (h *HarnessTest) AssertPaymentStatusFromStream(stream rpc.PaymentClient, status lnrpc.Payment_PaymentStatus) *lnrpc.Payment { - return h.assertPaymentStatusWithTimeout(stream, status, DefaultTimeout) + return h.assertPaymentStatusWithTimeout( + stream, status, wait.PaymentTimeout, + ) } // AssertPaymentSucceedWithTimeout asserts that a payment is succeeded within @@ -1166,7 +1191,9 @@ func (h *HarnessTest) AssertActiveHtlcs(hn *node.HarnessNode, h := hex.EncodeToString(htlc.HashLock) _, ok := htlcHashes[h] if ok { - return fmt.Errorf("duplicate HashLock") + return fmt.Errorf("duplicate HashLock "+ + "in PendingHtlcs: %v", + ch.PendingHtlcs) } htlcHashes[h] = struct{}{} } @@ -1732,7 +1759,8 @@ func (h *HarnessTest) AssertNumPayments(hn *node.HarnessNode, return errNumNotMatched(hn.Name(), "num of payments", num, len(payments), have+len(payments), have) }, DefaultTimeout) - require.NoError(h, err, "timeout checking num of payments") + require.NoError(h, err, "%s: timeout checking num of payments", + hn.Name()) return payments } @@ -1745,7 +1773,8 @@ func (h *HarnessTest) AssertNumNodeAnns(hn *node.HarnessNode, // We will get the current number of channel updates first and add it // to our expected number of newly created channel updates. anns, err := hn.Watcher.WaitForNumNodeUpdates(pubkey, num) - require.NoError(h, err, "failed to assert num of channel updates") + require.NoError(h, err, "%s: failed to assert num of node anns", + hn.Name()) return anns } @@ -1757,7 +1786,8 @@ func (h *HarnessTest) AssertNumChannelUpdates(hn *node.HarnessNode, op := h.OutPointFromChannelPoint(chanPoint) err := hn.Watcher.WaitForNumChannelUpdates(op, num) - require.NoError(h, err, "failed to assert num of channel updates") + require.NoError(h, err, "%s: failed to assert num of channel updates", + hn.Name()) } // CreateBurnAddr creates a random burn address of the given type. diff --git a/lntest/harness_miner.go b/lntest/harness_miner.go index b9f54619a..bdbabe35b 100644 --- a/lntest/harness_miner.go +++ b/lntest/harness_miner.go @@ -1,18 +1,26 @@ package lntest import ( + "bytes" "context" "fmt" "io/ioutil" "os" "path/filepath" "strings" + "testing" "time" + "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/integration/rpctest" "github.com/btcsuite/btcd/rpcclient" + "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/lntest/node" + "github.com/lightningnetwork/lnd/lntest/wait" + "github.com/stretchr/testify/require" ) const ( @@ -21,16 +29,21 @@ const ( // minerLogDir is the default log dir for the miner node. minerLogDir = ".minerlogs" + + // slowMineDelay defines a wait period between mining new blocks. + slowMineDelay = 100 * time.Millisecond ) var harnessNetParams = &chaincfg.RegressionNetParams type HarnessMiner struct { + *testing.T *rpctest.Harness // runCtx is a context with cancel method. It's used to signal when the // node needs to quit, and used as the parent context when spawning - runCtx context.Context + // children contexts for RPC requests. + runCtx context.Context //nolint:containedctx cancel context.CancelFunc // logPath is the directory path of the miner's logs. @@ -42,21 +55,30 @@ type HarnessMiner struct { // NewMiner creates a new miner using btcd backend with the default log file // dir and name. -func NewMiner() (*HarnessMiner, error) { - return newMiner(minerLogDir, minerLogFilename) +func NewMiner(ctxt context.Context, t *testing.T) *HarnessMiner { + t.Helper() + return newMiner(ctxt, t, minerLogDir, minerLogFilename) } // NewTempMiner creates a new miner using btcd backend with the specified log // file dir and name. -func NewTempMiner(tempDir, tempLogFilename string) (*HarnessMiner, error) { - return newMiner(tempDir, tempLogFilename) +func NewTempMiner(ctxt context.Context, t *testing.T, + tempDir, tempLogFilename string) *HarnessMiner { + + t.Helper() + + return newMiner(ctxt, t, tempDir, tempLogFilename) } // newMiner creates a new miner using btcd's rpctest. -func newMiner(minerDirName, logFilename string) (*HarnessMiner, error) { +func newMiner(ctxb context.Context, t *testing.T, minerDirName, + logFilename string) *HarnessMiner { + + t.Helper() + handler := &rpcclient.NotificationHandlers{} - btcdBinary := GetBtcdBinary() - baseLogPath := fmt.Sprintf("%s/%s", GetLogDir(), minerDirName) + btcdBinary := node.GetBtcdBinary() + baseLogPath := fmt.Sprintf("%s/%s", node.GetLogDir(), minerDirName) args := []string{ "--rejectnonstd", @@ -71,42 +93,28 @@ func newMiner(minerDirName, logFilename string) (*HarnessMiner, error) { } miner, err := rpctest.New(harnessNetParams, handler, args, btcdBinary) - if err != nil { - return nil, fmt.Errorf("unable to create mining node: %v", err) - } + require.NoError(t, err, "unable to create mining node") - ctxt, cancel := context.WithCancel(context.Background()) - m := &HarnessMiner{ + ctxt, cancel := context.WithCancel(ctxb) + + return &HarnessMiner{ + T: t, Harness: miner, runCtx: ctxt, cancel: cancel, logPath: baseLogPath, logFilename: logFilename, } - return m, nil -} - -// Stop shuts down the miner and saves its logs. -func (h *HarnessMiner) Stop() error { - h.cancel() - - if err := h.TearDown(); err != nil { - return fmt.Errorf("tear down miner got error: %s", err) - } - - return h.saveLogs() } // saveLogs copies the node logs and save it to the file specified by // h.logFilename. -func (h *HarnessMiner) saveLogs() error { +func (h *HarnessMiner) saveLogs() { // After shutting down the miner, we'll make a copy of the log files // before deleting the temporary log dir. path := fmt.Sprintf("%s/%s", h.logPath, harnessNetParams.Name) files, err := ioutil.ReadDir(path) - if err != nil { - return fmt.Errorf("unable to read log directory: %v", err) - } + require.NoError(h, err, "unable to read log directory") for _, file := range files { newFilename := strings.Replace( @@ -116,46 +124,332 @@ func (h *HarnessMiner) saveLogs() error { logFile := fmt.Sprintf("%s/%s", path, file.Name()) err := CopyFile(filepath.Clean(copyPath), logFile) - if err != nil { - return fmt.Errorf("unable to copy file: %v", err) + require.NoError(h, err, "unable to copy file") + } + + err = os.RemoveAll(h.logPath) + require.NoErrorf(h, err, "cannot remove dir %s", h.logPath) +} + +// Stop shuts down the miner and saves its logs. +func (h *HarnessMiner) Stop() { + h.cancel() + require.NoError(h, h.TearDown(), "tear down miner got error") + h.saveLogs() +} + +// GetBestBlock makes a RPC request to miner and asserts. +func (h *HarnessMiner) GetBestBlock() (*chainhash.Hash, int32) { + blockHash, height, err := h.Client.GetBestBlock() + require.NoError(h, err, "failed to GetBestBlock") + + return blockHash, height +} + +// GetRawMempool makes a RPC call to the miner's GetRawMempool and +// asserts. +func (h *HarnessMiner) GetRawMempool() []*chainhash.Hash { + mempool, err := h.Client.GetRawMempool() + require.NoError(h, err, "unable to get mempool") + + return mempool +} + +// GenerateBlocks mine 'num' of blocks and returns them. +func (h *HarnessMiner) GenerateBlocks(num uint32) []*chainhash.Hash { + blockHashes, err := h.Client.Generate(num) + require.NoError(h, err, "unable to generate blocks") + require.Len(h, blockHashes, int(num), "wrong num of blocks generated") + + return blockHashes +} + +// GetBlock gets a block using its block hash. +func (h *HarnessMiner) GetBlock(blockHash *chainhash.Hash) *wire.MsgBlock { + block, err := h.Client.GetBlock(blockHash) + require.NoError(h, err, "unable to get block") + + return block +} + +// MineBlocks mine 'num' of blocks and check that blocks are present in +// node blockchain. +func (h *HarnessMiner) MineBlocks(num uint32) []*wire.MsgBlock { + blocks := make([]*wire.MsgBlock, num) + + blockHashes := h.GenerateBlocks(num) + + for i, blockHash := range blockHashes { + block := h.GetBlock(blockHash) + blocks[i] = block + } + + return blocks +} + +// AssertNumTxsInMempool polls until finding the desired number of transactions +// in the provided miner's mempool. It will asserrt if this number is not met +// after the given timeout. +func (h *HarnessMiner) AssertNumTxsInMempool(n int) []*chainhash.Hash { + var ( + mem []*chainhash.Hash + err error + ) + + err = wait.NoError(func() error { + // We require the RPC call to be succeeded and won't wait for + // it as it's an unexpected behavior. + mem = h.GetRawMempool() + if len(mem) == n { + return nil + } + + return fmt.Errorf("want %v, got %v in mempool: %v", + n, len(mem), mem) + }, wait.MinerMempoolTimeout) + require.NoError(h, err, "assert tx in mempool timeout") + + return mem +} + +// AssertTxInBlock asserts that a given txid can be found in the passed block. +func (h *HarnessMiner) AssertTxInBlock(block *wire.MsgBlock, + txid *chainhash.Hash) { + + blockTxes := make([]chainhash.Hash, 0) + + for _, tx := range block.Transactions { + sha := tx.TxHash() + blockTxes = append(blockTxes, sha) + + if bytes.Equal(txid[:], sha[:]) { + return } } - if err = os.RemoveAll(h.logPath); err != nil { - return fmt.Errorf("cannot remove dir %s: %v", h.logPath, err) - } - - return nil + require.Failf(h, "tx was not included in block", "tx:%v, block has:%v", + txid, blockTxes) } -// waitForTxInMempool blocks until the target txid is seen in the mempool. If -// the transaction isn't seen within the network before the passed timeout, -// then an error is returned. -func (h *HarnessMiner) waitForTxInMempool(txid chainhash.Hash) error { - ticker := time.NewTicker(50 * time.Millisecond) - defer ticker.Stop() +// MineBlocksAndAssertNumTxes mine 'num' of blocks and check that blocks are +// present in node blockchain. numTxs should be set to the number of +// transactions (excluding the coinbase) we expect to be included in the first +// mined block. +func (h *HarnessMiner) MineBlocksAndAssertNumTxes(num uint32, + numTxs int) []*wire.MsgBlock { - var mempool []*chainhash.Hash - for { - select { - case <-h.runCtx.Done(): - return fmt.Errorf("NetworkHarness has been torn down") - case <-time.After(DefaultTimeout): - return fmt.Errorf("wanted %v, found %v txs "+ - "in mempool: %v", txid, len(mempool), mempool) + // If we expect transactions to be included in the blocks we'll mine, + // we wait here until they are seen in the miner's mempool. + txids := h.AssertNumTxsInMempool(numTxs) - case <-ticker.C: - var err error - mempool, err = h.Client.GetRawMempool() + // Mine blocks. + blocks := h.MineBlocks(num) + + // Finally, assert that all the transactions were included in the first + // block. + for _, txid := range txids { + h.AssertTxInBlock(blocks[0], txid) + } + + return blocks +} + +// GetRawTransaction makes a RPC call to the miner's GetRawTransaction and +// asserts. +func (h *HarnessMiner) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx { + tx, err := h.Client.GetRawTransaction(txid) + require.NoErrorf(h, err, "failed to get raw tx: %v", txid) + return tx +} + +// GetRawTransactionVerbose makes a RPC call to the miner's +// GetRawTransactionVerbose and asserts. +func (h *HarnessMiner) GetRawTransactionVerbose( + txid *chainhash.Hash) *btcjson.TxRawResult { + + tx, err := h.Client.GetRawTransactionVerbose(txid) + require.NoErrorf(h, err, "failed to get raw tx verbose: %v", txid) + return tx +} + +// AssertTxInMempool asserts a given transaction can be found in the mempool. +func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx { + var msgTx *wire.MsgTx + + err := wait.NoError(func() error { + // We require the RPC call to be succeeded and won't wait for + // it as it's an unexpected behavior. + mempool := h.GetRawMempool() + + if len(mempool) == 0 { + return fmt.Errorf("empty mempool") + } + + for _, memTx := range mempool { + // Check the values are equal. + if *memTx == *txid { + return nil + } + } + + return fmt.Errorf("txid %v not found in mempool: %v", txid, + mempool) + }, wait.MinerMempoolTimeout) + + require.NoError(h, err, "timeout checking mempool") + + return msgTx +} + +// SendOutputsWithoutChange uses the miner to send the given outputs using the +// specified fee rate and returns the txid. +func (h *HarnessMiner) SendOutputsWithoutChange(outputs []*wire.TxOut, + feeRate btcutil.Amount) *chainhash.Hash { + + txid, err := h.Harness.SendOutputsWithoutChange( + outputs, feeRate, + ) + require.NoErrorf(h, err, "failed to send output") + + return txid +} + +// CreateTransaction uses the miner to create a transaction using the given +// outputs using the specified fee rate and returns the transaction. +func (h *HarnessMiner) CreateTransaction(outputs []*wire.TxOut, + feeRate btcutil.Amount) *wire.MsgTx { + + tx, err := h.Harness.CreateTransaction(outputs, feeRate, false) + require.NoErrorf(h, err, "failed to create transaction") + + return tx +} + +// SendOutput creates, signs, and finally broadcasts a transaction spending +// the harness' available mature coinbase outputs to create the new output. +func (h *HarnessMiner) SendOutput(newOutput *wire.TxOut, + feeRate btcutil.Amount) *chainhash.Hash { + + hash, err := h.Harness.SendOutputs([]*wire.TxOut{newOutput}, feeRate) + require.NoErrorf(h, err, "failed to send outputs") + + return hash +} + +// MineBlocksSlow mines 'num' of blocks. Between each mined block an artificial +// delay is introduced to give all network participants time to catch up. +func (h *HarnessMiner) MineBlocksSlow(num uint32) []*wire.MsgBlock { + blocks := make([]*wire.MsgBlock, num) + blockHashes := make([]*chainhash.Hash, 0, num) + + for i := uint32(0); i < num; i++ { + generatedHashes := h.GenerateBlocks(1) + blockHashes = append(blockHashes, generatedHashes...) + + time.Sleep(slowMineDelay) + } + + for i, blockHash := range blockHashes { + block, err := h.Client.GetBlock(blockHash) + require.NoError(h, err, "get blocks") + + blocks[i] = block + } + + return blocks +} + +// AssertOutpointInMempool asserts a given outpoint can be found in the mempool. +func (h *HarnessMiner) AssertOutpointInMempool(op wire.OutPoint) *wire.MsgTx { + var msgTx *wire.MsgTx + + err := wait.NoError(func() error { + // We require the RPC call to be succeeded and won't wait for + // it as it's an unexpected behavior. + mempool := h.GetRawMempool() + + if len(mempool) == 0 { + return fmt.Errorf("empty mempool") + } + + for _, txid := range mempool { + // We don't use `ht.Miner.GetRawTransaction` which + // asserts a txid must be found. While iterating here, + // the actual mempool state might have been changed, + // causing a given txid being removed and cannot be + // found. For instance, the aggregation logic used in + // sweeping HTLC outputs will update the mempool by + // replacing the HTLC spending txes with a single one. + tx, err := h.Client.GetRawTransaction(txid) if err != nil { return err } - for _, mempoolTx := range mempool { - if *mempoolTx == txid { + msgTx = tx.MsgTx() + for _, txIn := range msgTx.TxIn { + if txIn.PreviousOutPoint == op { return nil } } } - } + + return fmt.Errorf("outpoint %v not found in mempool", op) + }, wait.MinerMempoolTimeout) + + require.NoError(h, err, "timeout checking mempool") + + return msgTx +} + +// GetNumTxsFromMempool polls until finding the desired number of transactions +// in the miner's mempool and returns the full transactions to the caller. +func (h *HarnessMiner) GetNumTxsFromMempool(n int) []*wire.MsgTx { + txids := h.AssertNumTxsInMempool(n) + + var txes []*wire.MsgTx + for _, txid := range txids { + tx := h.GetRawTransaction(txid) + txes = append(txes, tx.MsgTx()) + } + + return txes +} + +// NewMinerAddress creates a new address for the miner and asserts. +func (h *HarnessMiner) NewMinerAddress() btcutil.Address { + addr, err := h.NewAddress() + require.NoError(h, err, "failed to create new miner address") + return addr +} + +// MineBlocksWithTxes mines a single block to include the specifies +// transactions only. +func (h *HarnessMiner) MineBlockWithTxes(txes []*btcutil.Tx) *wire.MsgBlock { + var emptyTime time.Time + + // Generate a block. + b, err := h.GenerateAndSubmitBlock(txes, -1, emptyTime) + require.NoError(h, err, "unable to mine block") + + block, err := h.Client.GetBlock(b.Hash()) + require.NoError(h, err, "unable to get block") + + return block +} + +// MineEmptyBlocks mines a given number of empty blocks. +func (h *HarnessMiner) MineEmptyBlocks(num int) []*wire.MsgBlock { + var emptyTime time.Time + + blocks := make([]*wire.MsgBlock, num) + for i := 0; i < num; i++ { + // Generate an empty block. + b, err := h.GenerateAndSubmitBlock(nil, -1, emptyTime) + require.NoError(h, err, "unable to mine empty block") + + block := h.GetBlock(b.Hash()) + blocks[i] = block + } + + return blocks } diff --git a/lntest/harness_net.go b/lntest/harness_net.go deleted file mode 100644 index 358a1c0b3..000000000 --- a/lntest/harness_net.go +++ /dev/null @@ -1,1759 +0,0 @@ -package lntest - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - "testing" - "time" - - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/lightningnetwork/lnd" - "github.com/lightningnetwork/lnd/kvdb/etcd" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntest/wait" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" - "github.com/lightningnetwork/lnd/lnwire" - "github.com/stretchr/testify/require" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc/grpclog" -) - -// DefaultCSV is the CSV delay (remotedelay) we will start our test nodes with. -const DefaultCSV = 4 - -// NodeOption is a function for updating a node's configuration. -type NodeOption func(*BaseNodeConfig) - -// NetworkHarness is an integration testing harness for the lightning network. -// Building on top of HarnessNode, it is responsible for handling interactions -// among different nodes. The harness by default is created with two active -// nodes on the network: -// Alice and Bob. -type NetworkHarness struct { - netParams *chaincfg.Params - - // currentTestCase holds the name for the currently run test case. - currentTestCase string - - // lndBinary is the full path to the lnd binary that was specifically - // compiled with all required itest flags. - lndBinary string - - // Miner is a reference to a running full node that can be used to - // create new blocks on the network. - Miner *HarnessMiner - - // BackendCfg houses the information necessary to use a node as LND - // chain backend, such as rpc configuration, P2P information etc. - BackendCfg BackendConfig - - activeNodes map[int]*HarnessNode - - nodesByPub map[string]*HarnessNode - - // Alice and Bob are the initial seeder nodes that are automatically - // created to be the initial participants of the test network. - Alice *HarnessNode - Bob *HarnessNode - - // dbBackend sets the database backend to use. - dbBackend DatabaseBackend - - // Channel for transmitting stderr output from failed lightning node - // to main process. - lndErrorChan chan error - - // feeService is a web service that provides external fee estimates to - // lnd. - feeService *feeService - - // runCtx is a context with cancel method. It's used to signal when the - // node needs to quit, and used as the parent context when spawning - // children contexts for RPC requests. - runCtx context.Context - cancel context.CancelFunc - - mtx sync.Mutex -} - -// NewNetworkHarness creates a new network test harness. -// TODO(roasbeef): add option to use golang's build library to a binary of the -// current repo. This will save developers from having to manually `go install` -// within the repo each time before changes. -func NewNetworkHarness(m *HarnessMiner, b BackendConfig, lndBinary string, - dbBackend DatabaseBackend) (*NetworkHarness, error) { - - ctxt, cancel := context.WithCancel(context.Background()) - - n := NetworkHarness{ - activeNodes: make(map[int]*HarnessNode), - nodesByPub: make(map[string]*HarnessNode), - lndErrorChan: make(chan error), - netParams: m.ActiveNet, - Miner: m, - BackendCfg: b, - runCtx: ctxt, - cancel: cancel, - lndBinary: lndBinary, - dbBackend: dbBackend, - } - return &n, nil -} - -// LookUpNodeByPub queries the set of active nodes to locate a node according -// to its public key. The error is returned if the node was not found. -func (n *NetworkHarness) LookUpNodeByPub(pubStr string) (*HarnessNode, error) { - n.mtx.Lock() - defer n.mtx.Unlock() - - node, ok := n.nodesByPub[pubStr] - if !ok { - return nil, fmt.Errorf("unable to find node") - } - - return node, nil -} - -// ProcessErrors returns a channel used for reporting any fatal process errors. -// If any of the active nodes within the harness' test network incur a fatal -// error, that error is sent over this channel. -func (n *NetworkHarness) ProcessErrors() <-chan error { - return n.lndErrorChan -} - -// SetUp starts the initial seeder nodes within the test harness. The initial -// node's wallets will be funded wallets with ten 1 BTC outputs each. Finally -// rpc clients capable of communicating with the initial seeder nodes are -// created. Nodes are initialized with the given extra command line flags, which -// should be formatted properly - "--arg=value". -func (n *NetworkHarness) SetUp(t *testing.T, - testCase string, lndArgs []string) error { - - // Swap out grpc's default logger with out fake logger which drops the - // statements on the floor. - fakeLogger := grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard) - grpclog.SetLoggerV2(fakeLogger) - n.currentTestCase = testCase - n.feeService = startFeeService(t) - - // Start the initial seeder nodes within the test network, then connect - // their respective RPC clients. - eg := errgroup.Group{} - eg.Go(func() error { - var err error - n.Alice, err = n.newNode( - "Alice", lndArgs, false, nil, n.dbBackend, true, - ) - return err - }) - eg.Go(func() error { - var err error - n.Bob, err = n.newNode( - "Bob", lndArgs, false, nil, n.dbBackend, true, - ) - return err - }) - require.NoError(t, eg.Wait()) - - // First, make a connection between the two nodes. This will wait until - // both nodes are fully started since the Connect RPC is guarded behind - // the server.Started() flag that waits for all subsystems to be ready. - n.ConnectNodes(t, n.Alice, n.Bob) - - // Load up the wallets of the seeder nodes with 10 outputs of 1 BTC - // each. - addrReq := &lnrpc.NewAddressRequest{ - Type: lnrpc.AddressType_WITNESS_PUBKEY_HASH, - } - clients := []lnrpc.LightningClient{n.Alice, n.Bob} - for _, client := range clients { - for i := 0; i < 10; i++ { - resp, err := client.NewAddress(n.runCtx, addrReq) - if err != nil { - return err - } - addr, err := btcutil.DecodeAddress(resp.Address, n.netParams) - if err != nil { - return err - } - addrScript, err := txscript.PayToAddrScript(addr) - if err != nil { - return err - } - - output := &wire.TxOut{ - PkScript: addrScript, - Value: btcutil.SatoshiPerBitcoin, - } - _, err = n.Miner.SendOutputs([]*wire.TxOut{output}, 7500) - if err != nil { - return err - } - } - } - - // We generate several blocks in order to give the outputs created - // above a good number of confirmations. - if _, err := n.Miner.Client.Generate(10); err != nil { - return err - } - - // Now we want to wait for the nodes to catch up. - if err := n.Alice.WaitForBlockchainSync(); err != nil { - return err - } - if err := n.Bob.WaitForBlockchainSync(); err != nil { - return err - } - - // Now block until both wallets have fully synced up. - expectedBalance := int64(btcutil.SatoshiPerBitcoin * 10) - balReq := &lnrpc.WalletBalanceRequest{} - balanceTicker := time.NewTicker(time.Millisecond * 200) - defer balanceTicker.Stop() - balanceTimeout := time.After(DefaultTimeout) -out: - for { - select { - case <-balanceTicker.C: - aliceResp, err := n.Alice.WalletBalance(n.runCtx, balReq) - if err != nil { - return err - } - bobResp, err := n.Bob.WalletBalance(n.runCtx, balReq) - if err != nil { - return err - } - - if aliceResp.ConfirmedBalance == expectedBalance && - bobResp.ConfirmedBalance == expectedBalance { - - break out - } - case <-balanceTimeout: - return fmt.Errorf("balances not synced after deadline") - } - } - - return nil -} - -// TearDown tears down all active nodes within the test lightning network. -func (n *NetworkHarness) TearDown() error { - for _, node := range n.activeNodes { - if err := n.ShutdownNode(node); err != nil { - return err - } - } - - return nil -} - -// Stop stops the test harness. -func (n *NetworkHarness) Stop() { - close(n.lndErrorChan) - n.cancel() - - // feeService may not be created. For instance, running a non-exist - // test case. - if n.feeService != nil { - n.feeService.stop() - } -} - -// extraArgsEtcd returns extra args for configuring LND to use an external etcd -// database (for remote channel DB and wallet DB). -func extraArgsEtcd(etcdCfg *etcd.Config, name string, cluster bool, - leaderSessionTTL int) []string { - - extraArgs := []string{ - "--db.backend=etcd", - fmt.Sprintf("--db.etcd.host=%v", etcdCfg.Host), - fmt.Sprintf("--db.etcd.user=%v", etcdCfg.User), - fmt.Sprintf("--db.etcd.pass=%v", etcdCfg.Pass), - fmt.Sprintf("--db.etcd.namespace=%v", etcdCfg.Namespace), - } - - if etcdCfg.InsecureSkipVerify { - extraArgs = append(extraArgs, "--db.etcd.insecure_skip_verify") - } - - if cluster { - clusterArgs := []string{ - "--cluster.enable-leader-election", - fmt.Sprintf("--cluster.id=%v", name), - fmt.Sprintf("--cluster.leader-session-ttl=%v", - leaderSessionTTL), - } - extraArgs = append(extraArgs, clusterArgs...) - } - - return extraArgs -} - -// NewNodeWithSeedEtcd starts a new node with seed that'll use an external -// etcd database as its (remote) channel and wallet DB. The passsed cluster -// flag indicates that we'd like the node to join the cluster leader election. -func (n *NetworkHarness) NewNodeWithSeedEtcd(name string, etcdCfg *etcd.Config, - password []byte, entropy []byte, statelessInit, cluster bool, - leaderSessionTTL int) (*HarnessNode, []string, []byte, error) { - - // We don't want to use the embedded etcd instance. - const dbBackend = BackendBbolt - - extraArgs := extraArgsEtcd(etcdCfg, name, cluster, leaderSessionTTL) - return n.newNodeWithSeed( - name, extraArgs, password, entropy, statelessInit, dbBackend, - ) -} - -// NewNodeWithSeedEtcd starts a new node with seed that'll use an external -// etcd database as its (remote) channel and wallet DB. The passsed cluster -// flag indicates that we'd like the node to join the cluster leader election. -// If the wait flag is false then we won't wait until RPC is available (this is -// useful when the node is not expected to become the leader right away). -func (n *NetworkHarness) NewNodeEtcd(name string, etcdCfg *etcd.Config, - password []byte, cluster, wait bool, leaderSessionTTL int) ( - *HarnessNode, error) { - - // We don't want to use the embedded etcd instance. - const dbBackend = BackendBbolt - - extraArgs := extraArgsEtcd(etcdCfg, name, cluster, leaderSessionTTL) - return n.newNode(name, extraArgs, true, password, dbBackend, wait) -} - -// NewNode fully initializes a returns a new HarnessNode bound to the -// current instance of the network harness. The created node is running, but -// not yet connected to other nodes within the network. -func (n *NetworkHarness) NewNode(t *testing.T, - name string, extraArgs []string, opts ...NodeOption) *HarnessNode { - - node, err := n.newNode( - name, extraArgs, false, nil, n.dbBackend, true, opts..., - ) - require.NoErrorf(t, err, "unable to create new node for %s", name) - - return node -} - -// NewNodeWithSeed fully initializes a new HarnessNode after creating a fresh -// aezeed. The provided password is used as both the aezeed password and the -// wallet password. The generated mnemonic is returned along with the -// initialized harness node. -func (n *NetworkHarness) NewNodeWithSeed(name string, extraArgs []string, - password []byte, statelessInit bool) (*HarnessNode, []string, []byte, - error) { - - return n.newNodeWithSeed( - name, extraArgs, password, nil, statelessInit, n.dbBackend, - ) -} - -func (n *NetworkHarness) newNodeWithSeed(name string, extraArgs []string, - password, entropy []byte, statelessInit bool, dbBackend DatabaseBackend) ( - *HarnessNode, []string, []byte, error) { - - node, err := n.newNode( - name, extraArgs, true, password, dbBackend, true, - ) - if err != nil { - return nil, nil, nil, err - } - - // Create a request to generate a new aezeed. The new seed will have the - // same password as the internal wallet. - genSeedReq := &lnrpc.GenSeedRequest{ - AezeedPassphrase: password, - SeedEntropy: entropy, - } - - ctxt, cancel := context.WithTimeout(n.runCtx, DefaultTimeout) - defer cancel() - - var genSeedResp *lnrpc.GenSeedResponse - if err := wait.NoError(func() error { - genSeedResp, err = node.GenSeed(ctxt, genSeedReq) - return err - }, DefaultTimeout); err != nil { - return nil, nil, nil, err - } - - // With the seed created, construct the init request to the node, - // including the newly generated seed. - initReq := &lnrpc.InitWalletRequest{ - WalletPassword: password, - CipherSeedMnemonic: genSeedResp.CipherSeedMnemonic, - AezeedPassphrase: password, - StatelessInit: statelessInit, - } - - // Pass the init request via rpc to finish unlocking the node. This will - // also initialize the macaroon-authenticated LightningClient. - response, err := node.Init(initReq) - if err != nil { - return nil, nil, nil, err - } - - // With the node started, we can now record its public key within the - // global mapping. - n.RegisterNode(node) - - // In stateless initialization mode we get a macaroon back that we have - // to return to the test, otherwise gRPC calls won't be possible since - // there are no macaroon files created in that mode. - // In stateful init the admin macaroon will just be nil. - return node, genSeedResp.CipherSeedMnemonic, response.AdminMacaroon, nil -} - -func (n *NetworkHarness) NewNodeRemoteSigner(name string, extraArgs []string, - password []byte, watchOnly *lnrpc.WatchOnly) (*HarnessNode, error) { - - node, err := n.newNode( - name, extraArgs, true, password, n.dbBackend, true, - ) - if err != nil { - return nil, err - } - - // With the seed created, construct the init request to the node, - // including the newly generated seed. - initReq := &lnrpc.InitWalletRequest{ - WalletPassword: password, - WatchOnly: watchOnly, - } - - // Pass the init request via rpc to finish unlocking the node. This will - // also initialize the macaroon-authenticated LightningClient. - _, err = node.Init(initReq) - if err != nil { - return nil, err - } - - // With the node started, we can now record its public key within the - // global mapping. - n.RegisterNode(node) - - return node, nil -} - -// RestoreNodeWithSeed fully initializes a HarnessNode using a chosen mnemonic, -// password, recovery window, and optionally a set of static channel backups. -// After providing the initialization request to unlock the node, this method -// will finish initializing the LightningClient such that the HarnessNode can -// be used for regular rpc operations. -func (n *NetworkHarness) RestoreNodeWithSeed(name string, extraArgs []string, - password []byte, mnemonic []string, rootKey string, recoveryWindow int32, - chanBackups *lnrpc.ChanBackupSnapshot, - opts ...NodeOption) (*HarnessNode, error) { - - node, err := n.newNode( - name, extraArgs, true, password, n.dbBackend, true, opts..., - ) - if err != nil { - return nil, err - } - - initReq := &lnrpc.InitWalletRequest{ - WalletPassword: password, - CipherSeedMnemonic: mnemonic, - AezeedPassphrase: password, - ExtendedMasterKey: rootKey, - RecoveryWindow: recoveryWindow, - ChannelBackups: chanBackups, - } - - _, err = node.Init(initReq) - if err != nil { - return nil, err - } - - // With the node started, we can now record its public key within the - // global mapping. - n.RegisterNode(node) - - return node, nil -} - -// newNode initializes a new HarnessNode, supporting the ability to initialize a -// wallet with or without a seed. If hasSeed is false, the returned harness node -// can be used immediately. Otherwise, the node will require an additional -// initialization phase where the wallet is either created or restored. -func (n *NetworkHarness) newNode(name string, extraArgs []string, hasSeed bool, - password []byte, dbBackend DatabaseBackend, wait bool, opts ...NodeOption) ( - *HarnessNode, error) { - - cfg := &BaseNodeConfig{ - Name: name, - LogFilenamePrefix: n.currentTestCase, - HasSeed: hasSeed, - Password: password, - BackendCfg: n.BackendCfg, - NetParams: n.netParams, - ExtraArgs: extraArgs, - FeeURL: n.feeService.url, - DbBackend: dbBackend, - } - for _, opt := range opts { - opt(cfg) - } - - node, err := newNode(cfg) - if err != nil { - return nil, err - } - - // Put node in activeNodes to ensure Shutdown is called even if Start - // returns an error. - n.mtx.Lock() - n.activeNodes[node.NodeID] = node - n.mtx.Unlock() - - err = node.start(n.lndBinary, n.lndErrorChan, wait) - if err != nil { - return nil, err - } - - // If this node is to have a seed, it will need to be unlocked or - // initialized via rpc. Delay registering it with the network until it - // can be driven via an unlocked rpc connection. - if node.Cfg.HasSeed { - return node, nil - } - - // With the node started, we can now record its public key within the - // global mapping. - n.RegisterNode(node) - - return node, nil -} - -// RegisterNode records a new HarnessNode in the NetworkHarnesses map of known -// nodes. This method should only be called with nodes that have successfully -// retrieved their public keys via FetchNodeInfo. -func (n *NetworkHarness) RegisterNode(node *HarnessNode) { - n.mtx.Lock() - n.nodesByPub[node.PubKeyStr] = node - n.mtx.Unlock() -} - -func (n *NetworkHarness) connect(ctx context.Context, - req *lnrpc.ConnectPeerRequest, a *HarnessNode) error { - - syncTimeout := time.After(DefaultTimeout) -tryconnect: - if _, err := a.ConnectPeer(ctx, req); err != nil { - // If the chain backend is still syncing, retry. - if strings.Contains(err.Error(), lnd.ErrServerNotActive.Error()) || - strings.Contains(err.Error(), "i/o timeout") { - - select { - case <-time.After(100 * time.Millisecond): - goto tryconnect - case <-syncTimeout: - return fmt.Errorf("chain backend did not " + - "finish syncing") - } - } - return err - } - - return nil -} - -// EnsureConnected will try to connect to two nodes, returning no error if they -// are already connected. If the nodes were not connected previously, this will -// behave the same as ConnectNodes. If a pending connection request has already -// been made, the method will block until the two nodes appear in each other's -// peers list, or until the 15s timeout expires. -func (n *NetworkHarness) EnsureConnected(t *testing.T, a, b *HarnessNode) { - ctx, cancel := context.WithTimeout(n.runCtx, DefaultTimeout*2) - defer cancel() - - // errConnectionRequested is used to signal that a connection was - // requested successfully, which is distinct from already being - // connected to the peer. - errConnectionRequested := errors.New("connection request in progress") - - tryConnect := func(a, b *HarnessNode) error { - bInfo, err := b.GetInfo(ctx, &lnrpc.GetInfoRequest{}) - if err != nil { - return err - } - - req := &lnrpc.ConnectPeerRequest{ - Addr: &lnrpc.LightningAddress{ - Pubkey: bInfo.IdentityPubkey, - Host: b.Cfg.P2PAddr(), - }, - } - - var predErr error - err = wait.Predicate(func() bool { - ctx, cancel := context.WithTimeout(ctx, DefaultTimeout) - defer cancel() - - err := n.connect(ctx, req, a) - switch { - // Request was successful, wait for both to display the - // connection. - case err == nil: - predErr = errConnectionRequested - return true - - // If the two are already connected, we return early - // with no error. - case strings.Contains( - err.Error(), "already connected to peer", - ): - predErr = nil - return true - - default: - predErr = err - return false - } - }, DefaultTimeout) - if err != nil { - return fmt.Errorf("connection not succeeded within 15 "+ - "seconds: %v", predErr) - } - - return predErr - } - - aErr := tryConnect(a, b) - bErr := tryConnect(b, a) - switch { - // If both reported already being connected to each other, we can exit - // early. - case aErr == nil && bErr == nil: - - // Return any critical errors returned by either alice. - case aErr != nil && aErr != errConnectionRequested: - t.Fatalf( - "ensure connection between %s and %s failed "+ - "with error from %s: %v", - a.Cfg.Name, b.Cfg.Name, a.Cfg.Name, aErr, - ) - - // Return any critical errors returned by either bob. - case bErr != nil && bErr != errConnectionRequested: - t.Fatalf("ensure connection between %s and %s failed "+ - "with error from %s: %v", - a.Cfg.Name, b.Cfg.Name, b.Cfg.Name, bErr, - ) - - // Otherwise one or both requested a connection, so we wait for the - // peers lists to reflect the connection. - default: - } - - findSelfInPeerList := func(a, b *HarnessNode) bool { - // If node B is seen in the ListPeers response from node A, - // then we can exit early as the connection has been fully - // established. - resp, err := b.ListPeers(ctx, &lnrpc.ListPeersRequest{}) - if err != nil { - return false - } - - for _, peer := range resp.Peers { - if peer.PubKey == a.PubKeyStr { - return true - } - } - - return false - } - - err := wait.Predicate(func() bool { - return findSelfInPeerList(a, b) && findSelfInPeerList(b, a) - }, DefaultTimeout) - - require.NoErrorf( - t, err, "unable to connect %s to %s, "+ - "got error: peers not connected within %v seconds", - a.Cfg.Name, b.Cfg.Name, DefaultTimeout, - ) -} - -// ConnectNodes attempts to create a connection between nodes a and b. -func (n *NetworkHarness) ConnectNodes(t *testing.T, a, b *HarnessNode) { - n.connectNodes(t, a, b, false) -} - -// ConnectNodesPerm attempts to connect nodes a and b and sets node b as -// a peer that node a should persistently attempt to reconnect to if they -// become disconnected. -func (n *NetworkHarness) ConnectNodesPerm(t *testing.T, - a, b *HarnessNode) { - - n.connectNodes(t, a, b, true) -} - -// connectNodes establishes an encrypted+authenticated p2p connection from node -// a towards node b. The function will return a non-nil error if the connection -// was unable to be established. If the perm parameter is set to true then -// node a will persistently attempt to reconnect to node b if they get -// disconnected. -// -// NOTE: This function may block for up to 15-seconds as it will not return -// until the new connection is detected as being known to both nodes. -func (n *NetworkHarness) connectNodes(t *testing.T, a, b *HarnessNode, - perm bool) { - - ctx, cancel := context.WithTimeout(n.runCtx, DefaultTimeout) - defer cancel() - - bobInfo, err := b.GetInfo(ctx, &lnrpc.GetInfoRequest{}) - require.NoErrorf( - t, err, "unable to connect %s to %s, got error: %v", - a.Cfg.Name, b.Cfg.Name, err, - ) - - req := &lnrpc.ConnectPeerRequest{ - Addr: &lnrpc.LightningAddress{ - Pubkey: bobInfo.IdentityPubkey, - Host: b.Cfg.P2PAddr(), - }, - Perm: perm, - } - - err = n.connect(ctx, req, a) - require.NoErrorf( - t, err, "unable to connect %s to %s, got error: %v", - a.Cfg.Name, b.Cfg.Name, err, - ) - - err = wait.Predicate(func() bool { - // If node B is seen in the ListPeers response from node A, - // then we can exit early as the connection has been fully - // established. - resp, err := a.ListPeers(ctx, &lnrpc.ListPeersRequest{}) - if err != nil { - return false - } - - for _, peer := range resp.Peers { - if peer.PubKey == b.PubKeyStr { - return true - } - } - - return false - }, DefaultTimeout) - - require.NoErrorf( - t, err, "unable to connect %s to %s, "+ - "got error: peers not connected within %v seconds", - a.Cfg.Name, b.Cfg.Name, DefaultTimeout, - ) -} - -// DisconnectNodes disconnects node a from node b by sending RPC message -// from a node to b node. -func (n *NetworkHarness) DisconnectNodes(a, b *HarnessNode) error { - ctx, cancel := context.WithTimeout(n.runCtx, DefaultTimeout) - defer cancel() - - bobInfo, err := b.GetInfo(ctx, &lnrpc.GetInfoRequest{}) - if err != nil { - return err - } - - req := &lnrpc.DisconnectPeerRequest{ - PubKey: bobInfo.IdentityPubkey, - } - - if _, err := a.DisconnectPeer(ctx, req); err != nil { - return err - } - - return nil -} - -// RestartNode attempts to restart a lightning node by shutting it down -// cleanly, then restarting the process. This function is fully blocking. Upon -// restart, the RPC connection to the node will be re-attempted, continuing iff -// the connection attempt is successful. If the callback parameter is non-nil, -// then the function will be executed after the node shuts down, but *before* -// the process has been started up again. -// -// This method can be useful when testing edge cases such as a node broadcast -// and invalidated prior state, or persistent state recovery, simulating node -// crashes, etc. Additionally, each time the node is restarted, the caller can -// pass a set of SCBs to pass in via the Unlock method allowing them to restore -// channels during restart. -func (n *NetworkHarness) RestartNode(node *HarnessNode, callback func() error, - chanBackups ...*lnrpc.ChanBackupSnapshot) error { - - err := n.RestartNodeNoUnlock(node, callback, true) - if err != nil { - return err - } - - // If the node doesn't have a password set, then we can exit here as we - // don't need to unlock it. - if len(node.Cfg.Password) == 0 { - return nil - } - - // Otherwise, we'll unlock the wallet, then complete the final steps - // for the node initialization process. - unlockReq := &lnrpc.UnlockWalletRequest{ - WalletPassword: node.Cfg.Password, - } - if len(chanBackups) != 0 { - unlockReq.ChannelBackups = chanBackups[0] - unlockReq.RecoveryWindow = 1000 - } - - if err := node.Unlock(unlockReq); err != nil { - return err - } - - // Give the node some time to catch up with the chain before we - // continue with the tests. - return node.WaitForBlockchainSync() -} - -// RestartNodeNoUnlock attempts to restart a lightning node by shutting it down -// cleanly, then restarting the process. In case the node was setup with a seed, -// it will be left in the unlocked state. This function is fully blocking. If -// the callback parameter is non-nil, then the function will be executed after -// the node shuts down, but *before* the process has been started up again. -func (n *NetworkHarness) RestartNodeNoUnlock(node *HarnessNode, - callback func() error, wait bool) error { - - if err := node.stop(); err != nil { - return err - } - - if callback != nil { - if err := callback(); err != nil { - return err - } - } - - return node.start(n.lndBinary, n.lndErrorChan, wait) -} - -// SuspendNode stops the given node and returns a callback that can be used to -// start it again. -func (n *NetworkHarness) SuspendNode(node *HarnessNode) (func() error, error) { - if err := node.stop(); err != nil { - return nil, err - } - - restart := func() error { - return node.start(n.lndBinary, n.lndErrorChan, true) - } - - return restart, nil -} - -// ShutdownNode stops an active lnd process and returns when the process has -// exited and any temporary directories have been cleaned up. -func (n *NetworkHarness) ShutdownNode(node *HarnessNode) error { - if err := node.shutdown(); err != nil { - return err - } - - delete(n.activeNodes, node.NodeID) - return nil -} - -// KillNode kills the node (but won't wait for the node process to stop). -func (n *NetworkHarness) KillNode(node *HarnessNode) error { - if err := node.kill(); err != nil { - return err - } - - delete(n.activeNodes, node.NodeID) - return nil -} - -// StopNode stops the target node, but doesn't yet clean up its directories. -// This can be used to temporarily bring a node down during a test, to be later -// started up again. -func (n *NetworkHarness) StopNode(node *HarnessNode) error { - return node.stop() -} - -// SaveProfilesPages hits profiles pages of all active nodes and writes it to -// disk using a similar naming scheme as to the regular set of logs. -func (n *NetworkHarness) SaveProfilesPages(t *testing.T) { - // Only write gorutine dumps if flag is active. - if !(*goroutineDump) { - return - } - - for _, node := range n.activeNodes { - if err := saveProfilesPage(node); err != nil { - t.Logf("Logging follow-up error only, see rest of "+ - "the log for actual cause: %v\n", err) - } - } -} - -// saveProfilesPage saves the profiles page for the given node to file. -func saveProfilesPage(node *HarnessNode) error { - resp, err := http.Get( - fmt.Sprintf( - "http://localhost:%d/debug/pprof/goroutine?debug=1", - node.Cfg.ProfilePort, - ), - ) - if err != nil { - return fmt.Errorf("failed to get profile page "+ - "(node_id=%d, name=%s): %v", - node.NodeID, node.Cfg.Name, err) - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("failed to read profile page "+ - "(node_id=%d, name=%s): %v", - node.NodeID, node.Cfg.Name, err) - } - - fileName := fmt.Sprintf( - "pprof-%d-%s-%s.log", node.NodeID, node.Cfg.Name, - hex.EncodeToString(node.PubKey[:logPubKeyBytes]), - ) - - logFile, err := os.Create(fileName) - if err != nil { - return fmt.Errorf("failed to create file for profile page "+ - "(node_id=%d, name=%s): %v", - node.NodeID, node.Cfg.Name, err) - } - defer logFile.Close() - - _, err = logFile.Write(body) - if err != nil { - return fmt.Errorf("failed to save profile page "+ - "(node_id=%d, name=%s): %v", - node.NodeID, node.Cfg.Name, err) - } - return nil -} - -// OpenChannelParams houses the params to specify when opening a new channel. -type OpenChannelParams struct { - // Amt is the local amount being put into the channel. - Amt btcutil.Amount - - // PushAmt is the amount that should be pushed to the remote when the - // channel is opened. - PushAmt btcutil.Amount - - // Private is a boolan indicating whether the opened channel should be - // private. - Private bool - - // SpendUnconfirmed is a boolean indicating whether we can utilize - // unconfirmed outputs to fund the channel. - SpendUnconfirmed bool - - // MinHtlc is the htlc_minimum_msat value set when opening the channel. - MinHtlc lnwire.MilliSatoshi - - // RemoteMaxHtlcs is the remote_max_htlcs value set when opening the - // channel, restricting the number of concurrent HTLCs the remote party - // can add to a commitment. - RemoteMaxHtlcs uint16 - - // FundingShim is an optional funding shim that the caller can specify - // in order to modify the channel funding workflow. - FundingShim *lnrpc.FundingShim - - // SatPerVByte is the amount of satoshis to spend in chain fees per virtual - // byte of the transaction. - SatPerVByte btcutil.Amount - - // CommitmentType is the commitment type that should be used for the - // channel to be opened. - CommitmentType lnrpc.CommitmentType - - // ZeroConf is used to determine if the channel will be a zero-conf - // channel. This only works if the explicit negotiation is used with - // anchors or script enforced leases. - ZeroConf bool - - // ScidAlias denotes whether the channel will be an option-scid-alias - // channel type negotiation. - ScidAlias bool - - // BaseFee is the channel base fee applied during the channel - // announcement phase. - BaseFee uint64 - - // FeeRate is the channel fee rate in ppm applied during the channel - // announcement phase. - FeeRate uint64 - - // UseBaseFee, if set, instructs the downstream logic to apply the - // user-specified channel base fee to the channel update announcement. - // If set to false it avoids applying a base fee of 0 and instead - // activates the default configured base fee. - UseBaseFee bool - - // UseFeeRate, if set, instructs the downstream logic to apply the - // user-specified channel fee rate to the channel update announcement. - // If set to false it avoids applying a fee rate of 0 and instead - // activates the default configured fee rate. - UseFeeRate bool -} - -// OpenChannel attempts to open a channel between srcNode and destNode with the -// passed channel funding parameters. If the passed context has a timeout, then -// if the timeout is reached before the channel pending notification is -// received, an error is returned. The confirmed boolean determines whether we -// should fund the channel with confirmed outputs or not. -func (n *NetworkHarness) OpenChannel(srcNode, destNode *HarnessNode, - p OpenChannelParams) (lnrpc.Lightning_OpenChannelClient, error) { - - // Wait until srcNode and destNode have the latest chain synced. - // Otherwise, we may run into a check within the funding manager that - // prevents any funding workflows from being kicked off if the chain - // isn't yet synced. - if err := srcNode.WaitForBlockchainSync(); err != nil { - return nil, fmt.Errorf("unable to sync srcNode chain: %v", err) - } - if err := destNode.WaitForBlockchainSync(); err != nil { - return nil, fmt.Errorf("unable to sync destNode chain: %v", err) - } - - minConfs := int32(1) - if p.SpendUnconfirmed { - minConfs = 0 - } - - openReq := &lnrpc.OpenChannelRequest{ - NodePubkey: destNode.PubKey[:], - LocalFundingAmount: int64(p.Amt), - PushSat: int64(p.PushAmt), - Private: p.Private, - MinConfs: minConfs, - SpendUnconfirmed: p.SpendUnconfirmed, - MinHtlcMsat: int64(p.MinHtlc), - RemoteMaxHtlcs: uint32(p.RemoteMaxHtlcs), - FundingShim: p.FundingShim, - SatPerByte: int64(p.SatPerVByte), - CommitmentType: p.CommitmentType, - ZeroConf: p.ZeroConf, - ScidAlias: p.ScidAlias, - BaseFee: p.BaseFee, - FeeRate: p.FeeRate, - UseBaseFee: p.UseBaseFee, - UseFeeRate: p.UseFeeRate, - } - - // We need to use n.runCtx here to keep the response stream alive after - // the function is returned. - respStream, err := srcNode.OpenChannel(n.runCtx, openReq) - if err != nil { - return nil, fmt.Errorf("unable to open channel between "+ - "alice and bob: %v", err) - } - - chanOpen := make(chan struct{}) - errChan := make(chan error) - go func() { - // Consume the "channel pending" update. This waits until the - // node notifies us that the final message in the channel - // funding workflow has been sent to the remote node. - resp, err := respStream.Recv() - if err != nil { - errChan <- err - return - } - _, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) - if !ok { - errChan <- fmt.Errorf("expected channel pending: "+ - "update, instead got %v", resp) - return - } - - close(chanOpen) - }() - - select { - case <-time.After(ChannelOpenTimeout): - return nil, fmt.Errorf("timeout reached before chan pending "+ - "update sent: %v", err) - case err := <-errChan: - return nil, err - case <-chanOpen: - return respStream, nil - } -} - -// OpenPendingChannel attempts to open a channel between srcNode and destNode -// with the passed channel funding parameters. If the passed context has a -// timeout, then if the timeout is reached before the channel pending -// notification is received, an error is returned. -func (n *NetworkHarness) OpenPendingChannel(srcNode, destNode *HarnessNode, - amt btcutil.Amount, - pushAmt btcutil.Amount) (*lnrpc.PendingUpdate, error) { - - // Wait until srcNode and destNode have blockchain synced - if err := srcNode.WaitForBlockchainSync(); err != nil { - return nil, fmt.Errorf("unable to sync srcNode chain: %v", err) - } - if err := destNode.WaitForBlockchainSync(); err != nil { - return nil, fmt.Errorf("unable to sync destNode chain: %v", err) - } - - openReq := &lnrpc.OpenChannelRequest{ - NodePubkey: destNode.PubKey[:], - LocalFundingAmount: int64(amt), - PushSat: int64(pushAmt), - Private: false, - } - - // We need to use n.runCtx here to keep the response stream alive after - // the function is returned. - respStream, err := srcNode.OpenChannel(n.runCtx, openReq) - if err != nil { - return nil, fmt.Errorf("unable to open channel between "+ - "alice and bob: %v", err) - } - - chanPending := make(chan *lnrpc.PendingUpdate) - errChan := make(chan error) - go func() { - // Consume the "channel pending" update. This waits until the - // node notifies us that the final message in the channel - // funding workflow has been sent to the remote node. - resp, err := respStream.Recv() - if err != nil { - errChan <- err - return - } - pendingResp, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanPending) - if !ok { - errChan <- fmt.Errorf("expected channel pending "+ - "update, instead got %v", resp) - return - } - - chanPending <- pendingResp.ChanPending - }() - - select { - case <-time.After(ChannelOpenTimeout): - return nil, fmt.Errorf("timeout reached before chan pending " + - "update sent") - case err := <-errChan: - return nil, err - case pendingChan := <-chanPending: - return pendingChan, nil - } -} - -// WaitForChannelOpen waits for a notification that a channel is open by -// consuming a message from the past open channel stream. If the passed context -// has a timeout, then if the timeout is reached before the channel has been -// opened, then an error is returned. -func (n *NetworkHarness) WaitForChannelOpen( - openChanStream lnrpc.Lightning_OpenChannelClient) ( - *lnrpc.ChannelPoint, error) { - - ctx, cancel := context.WithTimeout(n.runCtx, ChannelOpenTimeout) - defer cancel() - - errChan := make(chan error) - respChan := make(chan *lnrpc.ChannelPoint) - go func() { - resp, err := openChanStream.Recv() - if err != nil { - errChan <- fmt.Errorf("unable to read rpc resp: %v", err) - return - } - fundingResp, ok := resp.Update.(*lnrpc.OpenStatusUpdate_ChanOpen) - if !ok { - errChan <- fmt.Errorf("expected channel open update, "+ - "instead got %v", resp) - return - } - - respChan <- fundingResp.ChanOpen.ChannelPoint - }() - - select { - case <-ctx.Done(): - return nil, fmt.Errorf("timeout reached while waiting for " + - "channel open") - case err := <-errChan: - return nil, err - case chanPoint := <-respChan: - return chanPoint, nil - } -} - -// CloseChannel attempts to close the channel indicated by the -// passed channel point, initiated by the passed lnNode. If the passed context -// has a timeout, an error is returned if that timeout is reached before the -// channel close is pending. -func (n *NetworkHarness) CloseChannel(lnNode *HarnessNode, - cp *lnrpc.ChannelPoint, force bool) (lnrpc.Lightning_CloseChannelClient, - *chainhash.Hash, error) { - - // The cancel is intentionally left out here because the returned - // item(close channel client) relies on the context being active. This - // will be fixed once we finish refactoring the NetworkHarness. - ctxt, cancel := context.WithTimeout(n.runCtx, ChannelCloseTimeout) - defer cancel() - - // Create a channel outpoint that we can use to compare to channels - // from the ListChannelsResponse. - txidHash, err := getChanPointFundingTxid(cp) - if err != nil { - return nil, nil, err - } - fundingTxID, err := chainhash.NewHash(txidHash) - if err != nil { - return nil, nil, err - } - chanPoint := wire.OutPoint{ - Hash: *fundingTxID, - Index: cp.OutputIndex, - } - - // We'll wait for *both* nodes to read the channel as active if we're - // performing a cooperative channel closure. - if !force { - timeout := DefaultTimeout - listReq := &lnrpc.ListChannelsRequest{} - - // We define two helper functions, one two locate a particular - // channel, and the other to check if a channel is active or - // not. - filterChannel := func(node *HarnessNode, - op wire.OutPoint) (*lnrpc.Channel, error) { - - listResp, err := node.ListChannels(ctxt, listReq) - if err != nil { - return nil, err - } - - for _, c := range listResp.Channels { - if c.ChannelPoint == op.String() { - return c, nil - } - } - - return nil, fmt.Errorf("unable to find channel") - } - activeChanPredicate := func(node *HarnessNode) func() bool { - return func() bool { - channel, err := filterChannel(node, chanPoint) - if err != nil { - return false - } - - return channel.Active - } - } - - // Next, we'll fetch the target channel in order to get the - // harness node that will be receiving the channel close - // request. - targetChan, err := filterChannel(lnNode, chanPoint) - if err != nil { - return nil, nil, err - } - receivingNode, err := n.LookUpNodeByPub(targetChan.RemotePubkey) - if err != nil { - return nil, nil, err - } - - // Before proceeding, we'll ensure that the channel is active - // for both nodes. - err = wait.Predicate(activeChanPredicate(lnNode), timeout) - if err != nil { - return nil, nil, fmt.Errorf("channel of closing " + - "node not active in time") - } - err = wait.Predicate( - activeChanPredicate(receivingNode), timeout, - ) - if err != nil { - return nil, nil, fmt.Errorf("channel of receiving " + - "node not active in time") - } - } - - var ( - closeRespStream lnrpc.Lightning_CloseChannelClient - closeTxid *chainhash.Hash - ) - - err = wait.NoError(func() error { - closeReq := &lnrpc.CloseChannelRequest{ - ChannelPoint: cp, Force: force, - } - // We need to use n.runCtx to keep the client stream alive - // after the function has returned. - closeRespStream, err = lnNode.CloseChannel(n.runCtx, closeReq) - if err != nil { - return fmt.Errorf("unable to close channel: %v", err) - } - - // Consume the "channel close" update in order to wait for the - // closing transaction to be broadcast, then wait for the - // closing tx to be seen within the network. - closeResp, err := closeRespStream.Recv() - if err != nil { - return fmt.Errorf("unable to recv() from close "+ - "stream: %v", err) - } - pendingClose, ok := closeResp.Update.(*lnrpc.CloseStatusUpdate_ClosePending) - if !ok { - return fmt.Errorf("expected channel close update, "+ - "instead got %v", pendingClose) - } - - closeTxid, err = chainhash.NewHash( - pendingClose.ClosePending.Txid, - ) - if err != nil { - return fmt.Errorf("unable to decode closeTxid: "+ - "%v", err) - } - if err := n.Miner.waitForTxInMempool(*closeTxid); err != nil { - return fmt.Errorf("error while waiting for "+ - "broadcast tx: %v", err) - } - return nil - }, ChannelCloseTimeout) - if err != nil { - return nil, nil, err - } - - return closeRespStream, closeTxid, nil -} - -// WaitForChannelClose waits for a notification from the passed channel close -// stream that the node has deemed the channel has been fully closed. If the -// passed context has a timeout, then if the timeout is reached before the -// notification is received then an error is returned. -func (n *NetworkHarness) WaitForChannelClose( - closeChanStream lnrpc.Lightning_CloseChannelClient) ( - *chainhash.Hash, error) { - - errChan := make(chan error) - updateChan := make(chan *lnrpc.CloseStatusUpdate_ChanClose) - go func() { - closeResp, err := closeChanStream.Recv() - if err != nil { - errChan <- err - return - } - - closeFin, ok := closeResp.Update.(*lnrpc.CloseStatusUpdate_ChanClose) - if !ok { - errChan <- fmt.Errorf("expected channel close update, "+ - "instead got %v", closeFin) - return - } - - updateChan <- closeFin - }() - - // Wait until either the deadline for the context expires, an error - // occurs, or the channel close update is received. - select { - case <-time.After(ChannelCloseTimeout): - return nil, fmt.Errorf("timeout reached before update sent") - case err := <-errChan: - return nil, err - case update := <-updateChan: - return chainhash.NewHash(update.ChanClose.ClosingTxid) - } -} - -// AssertChannelExists asserts that an active channel identified by the -// specified channel point exists from the point-of-view of the node. It takes -// an optional set of check functions which can be used to make further -// assertions using channel's values. These functions are responsible for -// failing the test themselves if they do not pass. -func (n *NetworkHarness) AssertChannelExists(node *HarnessNode, - chanPoint *wire.OutPoint, checks ...func(*lnrpc.Channel)) error { - - ctx, cancel := context.WithTimeout(n.runCtx, ChannelCloseTimeout) - defer cancel() - - req := &lnrpc.ListChannelsRequest{} - - return wait.NoError(func() error { - resp, err := node.ListChannels(ctx, req) - if err != nil { - return fmt.Errorf("unable fetch node's channels: %v", err) - } - - for _, channel := range resp.Channels { - if channel.ChannelPoint == chanPoint.String() { - // First check whether our channel is active, - // failing early if it is not. - if !channel.Active { - return fmt.Errorf("channel %s inactive", - chanPoint) - } - - // Apply any additional checks that we would - // like to verify. - for _, check := range checks { - check(channel) - } - - return nil - } - } - - return fmt.Errorf("channel %s not found", chanPoint) - }, DefaultTimeout) -} - -// DumpLogs reads the current logs generated by the passed node, and returns -// the logs as a single string. This function is useful for examining the logs -// of a particular node in the case of a test failure. -// Logs from lightning node being generated with delay - you should -// add time.Sleep() in order to get all logs. -func (n *NetworkHarness) DumpLogs(node *HarnessNode) (string, error) { - logFile := fmt.Sprintf("%v/simnet/lnd.log", node.Cfg.LogDir) - - buf, err := ioutil.ReadFile(logFile) - if err != nil { - return "", err - } - - return string(buf), nil -} - -// SendCoins attempts to send amt satoshis from the internal mining node to the -// targeted lightning node using a P2WKH address. 6 blocks are mined after in -// order to confirm the transaction. -func (n *NetworkHarness) SendCoins(t *testing.T, amt btcutil.Amount, - target *HarnessNode) { - - err := n.SendCoinsOfType( - amt, target, lnrpc.AddressType_WITNESS_PUBKEY_HASH, true, - ) - require.NoErrorf(t, err, "unable to send coins for %s", target.Cfg.Name) -} - -// SendCoinsUnconfirmed sends coins from the internal mining node to the target -// lightning node using a P2WPKH address. No blocks are mined after, so the -// transaction remains unconfirmed. -func (n *NetworkHarness) SendCoinsUnconfirmed(t *testing.T, amt btcutil.Amount, - target *HarnessNode) { - - err := n.SendCoinsOfType( - amt, target, lnrpc.AddressType_WITNESS_PUBKEY_HASH, false, - ) - require.NoErrorf( - t, err, "unable to send unconfirmed coins for %s", - target.Cfg.Name, - ) -} - -// SendCoinsNP2WKH attempts to send amt satoshis from the internal mining node -// to the targeted lightning node using a NP2WKH address. -func (n *NetworkHarness) SendCoinsNP2WKH(t *testing.T, amt btcutil.Amount, - target *HarnessNode) { - - err := n.SendCoinsOfType( - amt, target, lnrpc.AddressType_NESTED_PUBKEY_HASH, true, - ) - require.NoErrorf( - t, err, "unable to send NP2WKH coins for %s", - target.Cfg.Name, - ) -} - -// SendCoinsP2TR attempts to send amt satoshis from the internal mining node -// to the targeted lightning node using a P2TR address. -func (n *NetworkHarness) SendCoinsP2TR(t *testing.T, amt btcutil.Amount, - target *HarnessNode) { - - err := n.SendCoinsOfType( - amt, target, lnrpc.AddressType_TAPROOT_PUBKEY, true, - ) - require.NoErrorf( - t, err, "unable to send P2TR coins for %s", target.Cfg.Name, - ) -} - -// SendCoinsOfType attempts to send amt satoshis from the internal mining node -// to the targeted lightning node. The confirmed boolean indicates whether the -// transaction that pays to the target should confirm. -func (n *NetworkHarness) SendCoinsOfType(amt btcutil.Amount, target *HarnessNode, - addrType lnrpc.AddressType, confirmed bool) error { - - ctx, cancel := context.WithTimeout(n.runCtx, DefaultTimeout) - defer cancel() - - balReq := &lnrpc.WalletBalanceRequest{} - initialBalance, err := target.WalletBalance(ctx, balReq) - if err != nil { - return err - } - - // First, obtain an address from the target lightning node, preferring - // to receive a p2wkh address s.t the output can immediately be used as - // an input to a funding transaction. - addrReq := &lnrpc.NewAddressRequest{ - Type: addrType, - } - resp, err := target.NewAddress(ctx, addrReq) - if err != nil { - return err - } - addr, err := btcutil.DecodeAddress(resp.Address, n.netParams) - if err != nil { - return err - } - addrScript, err := txscript.PayToAddrScript(addr) - if err != nil { - return err - } - - // Generate a transaction which creates an output to the target - // pkScript of the desired amount. - output := &wire.TxOut{ - PkScript: addrScript, - Value: int64(amt), - } - _, err = n.Miner.SendOutputs([]*wire.TxOut{output}, 7500) - if err != nil { - return err - } - - // Encode the pkScript in hex as this the format that it will be - // returned via rpc. - expPkScriptStr := hex.EncodeToString(addrScript) - - // Now, wait for ListUnspent to show the unconfirmed transaction - // containing the correct pkscript. - err = wait.NoError(func() error { - // Since neutrino doesn't support unconfirmed outputs, skip - // this check. - if target.Cfg.BackendCfg.Name() == "neutrino" { - return nil - } - - req := &lnrpc.ListUnspentRequest{} - resp, err := target.ListUnspent(ctx, req) - if err != nil { - return err - } - - // When using this method, there should only ever be on - // unconfirmed transaction. - if len(resp.Utxos) != 1 { - return fmt.Errorf("number of unconfirmed utxos "+ - "should be 1, found %d", len(resp.Utxos)) - } - - // Assert that the lone unconfirmed utxo contains the same - // pkscript as the output generated above. - pkScriptStr := resp.Utxos[0].PkScript - if strings.Compare(pkScriptStr, expPkScriptStr) != 0 { - return fmt.Errorf("pkscript mismatch, want: %s, "+ - "found: %s", expPkScriptStr, pkScriptStr) - } - - return nil - }, DefaultTimeout) - if err != nil { - return fmt.Errorf("unconfirmed utxo was not found in "+ - "ListUnspent: %v", err) - } - - // If the transaction should remain unconfirmed, then we'll wait until - // the target node's unconfirmed balance reflects the expected balance - // and exit. - if !confirmed { - expectedBalance := btcutil.Amount(initialBalance.UnconfirmedBalance) + amt - return target.WaitForBalance(expectedBalance, false) - } - - // Otherwise, we'll generate 6 new blocks to ensure the output gains a - // sufficient number of confirmations and wait for the balance to - // reflect what's expected. - if _, err := n.Miner.Client.Generate(6); err != nil { - return err - } - - fullInitialBalance := initialBalance.ConfirmedBalance + - initialBalance.UnconfirmedBalance - expectedBalance := btcutil.Amount(fullInitialBalance) + amt - return target.WaitForBalance(expectedBalance, true) -} - -func (n *NetworkHarness) SetFeeEstimate(fee chainfee.SatPerKWeight) { - n.feeService.setFee(fee) -} - -func (n *NetworkHarness) SetFeeEstimateWithConf( - fee chainfee.SatPerKWeight, conf uint32) { - - n.feeService.setFeeWithConf(fee, conf) -} - -// copyAll copies all files and directories from srcDir to dstDir recursively. -// Note that this function does not support links. -func copyAll(dstDir, srcDir string) error { - entries, err := ioutil.ReadDir(srcDir) - if err != nil { - return err - } - - for _, entry := range entries { - srcPath := filepath.Join(srcDir, entry.Name()) - dstPath := filepath.Join(dstDir, entry.Name()) - - info, err := os.Stat(srcPath) - if err != nil { - return err - } - - if info.IsDir() { - err := os.Mkdir(dstPath, info.Mode()) - if err != nil && !os.IsExist(err) { - return err - } - - err = copyAll(dstPath, srcPath) - if err != nil { - return err - } - } else if err := CopyFile(dstPath, srcPath); err != nil { - return err - } - } - - return nil -} - -// BackupDb creates a backup of the current database. -func (n *NetworkHarness) BackupDb(hn *HarnessNode) error { - if hn.backupDbDir != "" { - return errors.New("backup already created") - } - - restart, err := n.SuspendNode(hn) - if err != nil { - return err - } - - if hn.postgresDbName != "" { - // Backup database. - backupDbName := hn.postgresDbName + "_backup" - err := executePgQuery( - "CREATE DATABASE " + backupDbName + " WITH TEMPLATE " + - hn.postgresDbName, - ) - if err != nil { - return err - } - } else { - // Backup files. - tempDir, err := ioutil.TempDir("", "past-state") - if err != nil { - return fmt.Errorf("unable to create temp db folder: %v", - err) - } - - if err := copyAll(tempDir, hn.DBDir()); err != nil { - return fmt.Errorf("unable to copy database files: %v", - err) - } - - hn.backupDbDir = tempDir - } - - err = restart() - if err != nil { - return err - } - - return nil -} - -// RestoreDb restores a database backup. -func (n *NetworkHarness) RestoreDb(hn *HarnessNode) error { - if hn.postgresDbName != "" { - // Restore database. - backupDbName := hn.postgresDbName + "_backup" - err := executePgQuery( - "DROP DATABASE " + hn.postgresDbName, - ) - if err != nil { - return err - } - err = executePgQuery( - "ALTER DATABASE " + backupDbName + " RENAME TO " + hn.postgresDbName, - ) - if err != nil { - return err - } - } else { - // Restore files. - if hn.backupDbDir == "" { - return errors.New("no database backup created") - } - - if err := copyAll(hn.DBDir(), hn.backupDbDir); err != nil { - return fmt.Errorf("unable to copy database files: %v", err) - } - - if err := os.RemoveAll(hn.backupDbDir); err != nil { - return fmt.Errorf("unable to remove backup dir: %v", err) - } - hn.backupDbDir = "" - } - - return nil -} - -// getChanPointFundingTxid returns the given channel point's funding txid in -// raw bytes. -func getChanPointFundingTxid(chanPoint *lnrpc.ChannelPoint) ([]byte, error) { - var txid []byte - - // A channel point's funding txid can be get/set as a byte slice or a - // string. In the case it is a string, decode it. - switch chanPoint.GetFundingTxid().(type) { - case *lnrpc.ChannelPoint_FundingTxidBytes: - txid = chanPoint.GetFundingTxidBytes() - case *lnrpc.ChannelPoint_FundingTxidStr: - s := chanPoint.GetFundingTxidStr() - h, err := chainhash.NewHashFromStr(s) - if err != nil { - return nil, err - } - - txid = h[:] - } - - return txid, nil -} diff --git a/lntest/harness_node.go b/lntest/harness_node.go deleted file mode 100644 index db9ad7fab..000000000 --- a/lntest/harness_node.go +++ /dev/null @@ -1,1923 +0,0 @@ -package lntest - -import ( - "bytes" - "context" - "crypto/rand" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/jackc/pgx/v4/pgxpool" - "github.com/lightningnetwork/lnd/chanbackup" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lnrpc/chainrpc" - "github.com/lightningnetwork/lnd/lnrpc/invoicesrpc" - "github.com/lightningnetwork/lnd/lnrpc/neutrinorpc" - "github.com/lightningnetwork/lnd/lnrpc/peersrpc" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lnrpc/signrpc" - "github.com/lightningnetwork/lnd/lnrpc/walletrpc" - "github.com/lightningnetwork/lnd/lnrpc/watchtowerrpc" - "github.com/lightningnetwork/lnd/lnrpc/wtclientrpc" - "github.com/lightningnetwork/lnd/lntest/wait" - "github.com/lightningnetwork/lnd/macaroons" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/status" - "gopkg.in/macaroon.v2" -) - -const ( - // logPubKeyBytes is the number of bytes of the node's PubKey that will - // be appended to the log file name. The whole PubKey is too long and - // not really necessary to quickly identify what node produced which - // log file. - logPubKeyBytes = 4 - - // trickleDelay is the amount of time in milliseconds between each - // release of announcements by AuthenticatedGossiper to the network. - trickleDelay = 50 - - postgresDsn = "postgres://postgres:postgres@localhost:6432/%s?sslmode=disable" - - // commitInterval specifies the maximum interval the graph database - // will wait between attempting to flush a batch of modifications to - // disk(db.batch-commit-interval). - commitInterval = 10 * time.Millisecond -) - -var ( - // numActiveNodes is the number of active nodes within the test network. - numActiveNodes = 0 - numActiveNodesMtx sync.Mutex -) - -func postgresDatabaseDsn(dbName string) string { - return fmt.Sprintf(postgresDsn, dbName) -} - -// BackendConfig is an interface that abstracts away the specific chain backend -// node implementation. -type BackendConfig interface { - // GenArgs returns the arguments needed to be passed to LND at startup - // for using this node as a chain backend. - GenArgs() []string - - // ConnectMiner is called to establish a connection to the test miner. - ConnectMiner() error - - // DisconnectMiner is called to disconnect the miner. - DisconnectMiner() error - - // Name returns the name of the backend type. - Name() string - - // Credentials returns the rpc username, password and host for the - // backend. - Credentials() (string, string, string, error) -} - -// NodeConfig is the basic interface a node configuration must implement. -type NodeConfig interface { - // BaseConfig returns the base node configuration struct. - BaseConfig() *BaseNodeConfig - - // GenerateListeningPorts generates the ports to listen on designated - // for the current lightning network test. - GenerateListeningPorts() - - // GenArgs generates a slice of command line arguments from the - // lightning node config struct. - GenArgs() []string -} - -// BaseNodeConfig is the base node configuration. -type BaseNodeConfig struct { - Name string - - // LogFilenamePrefix is used to prefix node log files. Can be used - // to store the current test case for simpler postmortem debugging. - LogFilenamePrefix string - - BackendCfg BackendConfig - NetParams *chaincfg.Params - BaseDir string - ExtraArgs []string - - DataDir string - LogDir string - TLSCertPath string - TLSKeyPath string - AdminMacPath string - ReadMacPath string - InvoiceMacPath string - - HasSeed bool - Password []byte - - P2PPort int - RPCPort int - RESTPort int - ProfilePort int - - AcceptKeySend bool - AcceptAMP bool - - FeeURL string - - DbBackend DatabaseBackend - PostgresDsn string -} - -func (cfg BaseNodeConfig) P2PAddr() string { - return fmt.Sprintf(ListenerFormat, cfg.P2PPort) -} - -func (cfg BaseNodeConfig) RPCAddr() string { - return fmt.Sprintf(ListenerFormat, cfg.RPCPort) -} - -func (cfg BaseNodeConfig) RESTAddr() string { - return fmt.Sprintf(ListenerFormat, cfg.RESTPort) -} - -// DBDir returns the holding directory path of the graph database. -func (cfg BaseNodeConfig) DBDir() string { - return filepath.Join(cfg.DataDir, "graph", cfg.NetParams.Name) -} - -func (cfg BaseNodeConfig) DBPath() string { - return filepath.Join(cfg.DBDir(), "channel.db") -} - -func (cfg BaseNodeConfig) ChanBackupPath() string { - return filepath.Join( - cfg.DataDir, "chain", "bitcoin", - fmt.Sprintf( - "%v/%v", cfg.NetParams.Name, - chanbackup.DefaultBackupFileName, - ), - ) -} - -// GenerateListeningPorts generates the ports to listen on designated for the -// current lightning network test. -func (cfg *BaseNodeConfig) GenerateListeningPorts() { - if cfg.P2PPort == 0 { - cfg.P2PPort = NextAvailablePort() - } - if cfg.RPCPort == 0 { - cfg.RPCPort = NextAvailablePort() - } - if cfg.RESTPort == 0 { - cfg.RESTPort = NextAvailablePort() - } - if cfg.ProfilePort == 0 { - cfg.ProfilePort = NextAvailablePort() - } -} - -// BaseConfig returns the base node configuration struct. -func (cfg *BaseNodeConfig) BaseConfig() *BaseNodeConfig { - return cfg -} - -// GenArgs generates a slice of command line arguments from the lightning node -// config struct. -func (cfg *BaseNodeConfig) GenArgs() []string { - var args []string - - switch cfg.NetParams { - case &chaincfg.TestNet3Params: - args = append(args, "--bitcoin.testnet") - case &chaincfg.SimNetParams: - args = append(args, "--bitcoin.simnet") - case &chaincfg.RegressionNetParams: - args = append(args, "--bitcoin.regtest") - } - - backendArgs := cfg.BackendCfg.GenArgs() - args = append(args, backendArgs...) - - nodeArgs := []string{ - "--bitcoin.active", - "--nobootstrap", - "--debuglevel=debug", - "--bitcoin.defaultchanconfs=1", - "--keep-failed-payment-attempts", - fmt.Sprintf("--db.batch-commit-interval=%v", commitInterval), - fmt.Sprintf("--bitcoin.defaultremotedelay=%v", DefaultCSV), - fmt.Sprintf("--rpclisten=%v", cfg.RPCAddr()), - fmt.Sprintf("--restlisten=%v", cfg.RESTAddr()), - fmt.Sprintf("--restcors=https://%v", cfg.RESTAddr()), - fmt.Sprintf("--listen=%v", cfg.P2PAddr()), - fmt.Sprintf("--externalip=%v", cfg.P2PAddr()), - fmt.Sprintf("--lnddir=%v", cfg.BaseDir), - fmt.Sprintf("--adminmacaroonpath=%v", cfg.AdminMacPath), - fmt.Sprintf("--readonlymacaroonpath=%v", cfg.ReadMacPath), - fmt.Sprintf("--invoicemacaroonpath=%v", cfg.InvoiceMacPath), - fmt.Sprintf("--trickledelay=%v", trickleDelay), - fmt.Sprintf("--profile=%d", cfg.ProfilePort), - fmt.Sprintf("--caches.rpc-graph-cache-duration=%d", 0), - } - args = append(args, nodeArgs...) - - if !cfg.HasSeed { - args = append(args, "--noseedbackup") - } - - if cfg.ExtraArgs != nil { - args = append(args, cfg.ExtraArgs...) - } - - if cfg.AcceptKeySend { - args = append(args, "--accept-keysend") - } - - if cfg.AcceptAMP { - args = append(args, "--accept-amp") - } - - switch cfg.DbBackend { - case BackendEtcd: - args = append(args, "--db.backend=etcd") - args = append(args, "--db.etcd.embedded") - args = append( - args, fmt.Sprintf( - "--db.etcd.embedded_client_port=%v", - NextAvailablePort(), - ), - ) - args = append( - args, fmt.Sprintf( - "--db.etcd.embedded_peer_port=%v", - NextAvailablePort(), - ), - ) - args = append( - args, fmt.Sprintf( - "--db.etcd.embedded_log_file=%v", - path.Join(cfg.LogDir, "etcd.log"), - ), - ) - - case BackendPostgres: - args = append(args, "--db.backend=postgres") - args = append(args, "--db.postgres.dsn="+cfg.PostgresDsn) - - case BackendSqlite: - args = append(args, "--db.backend=sqlite") - args = append(args, fmt.Sprintf("--db.sqlite.busytimeout=%v", - SqliteBusyTimeout)) - } - - if cfg.FeeURL != "" { - args = append(args, "--feeurl="+cfg.FeeURL) - } - - return args -} - -// policyUpdateMap defines a type to store channel policy updates. It has the -// format, -// -// { -// "chanPoint1": { -// "advertisingNode1": [ -// policy1, policy2, ... -// ], -// "advertisingNode2": [ -// policy1, policy2, ... -// ] -// }, -// "chanPoint2": ... -// }. -type policyUpdateMap map[string]map[string][]*lnrpc.RoutingPolicy - -// HarnessNode represents an instance of lnd running within our test network -// harness. Each HarnessNode instance also fully embeds an RPC client in -// order to pragmatically drive the node. -type HarnessNode struct { - Cfg *BaseNodeConfig - - // NodeID is a unique identifier for the node within a NetworkHarness. - NodeID int - - // PubKey is the serialized compressed identity public key of the node. - // This field will only be populated once the node itself has been - // started via the start() method. - PubKey [33]byte - PubKeyStr string - - // rpc holds a list of RPC clients. - rpc *RPCClients - - // chanWatchRequests receives a request for watching a particular event - // for a given channel. - chanWatchRequests chan *chanWatchRequest - - // For each outpoint, we'll track an integer which denotes the number of - // edges seen for that channel within the network. When this number - // reaches 2, then it means that both edge advertisements has propagated - // through the network. - openChans map[wire.OutPoint]int - openChanWatchers map[wire.OutPoint][]chan struct{} - - closedChans map[wire.OutPoint]struct{} - closeChanWatchers map[wire.OutPoint][]chan struct{} - - // policyUpdates stores a slice of seen polices by each advertising - // node and the outpoint. - policyUpdates policyUpdateMap - - // backupDbDir is the path where a database backup is stored, if any. - backupDbDir string - - // postgresDbName is the name of the postgres database where lnd data is - // stored in. - postgresDbName string - - // runCtx is a context with cancel method. It's used to signal when the - // node needs to quit, and used as the parent context when spawning - // children contexts for RPC requests. - runCtx context.Context - cancel context.CancelFunc - - wg sync.WaitGroup - cmd *exec.Cmd - logFile *os.File - - // TODO(yy): remove - lnrpc.LightningClient - lnrpc.WalletUnlockerClient - invoicesrpc.InvoicesClient - peersrpc.PeersClient - SignerClient signrpc.SignerClient - RouterClient routerrpc.RouterClient - WalletKitClient walletrpc.WalletKitClient - Watchtower watchtowerrpc.WatchtowerClient - WatchtowerClient wtclientrpc.WatchtowerClientClient - StateClient lnrpc.StateClient - ChainClient chainrpc.ChainNotifierClient - ChainKit chainrpc.ChainKitClient - NeutrinoClient neutrinorpc.NeutrinoKitClient -} - -// RPCClients wraps a list of RPC clients into a single struct for easier -// access. -type RPCClients struct { - // conn is the underlying connection to the grpc endpoint of the node. - conn *grpc.ClientConn - - LN lnrpc.LightningClient - WalletUnlocker lnrpc.WalletUnlockerClient - Invoice invoicesrpc.InvoicesClient - Signer signrpc.SignerClient - Router routerrpc.RouterClient - WalletKit walletrpc.WalletKitClient - Watchtower watchtowerrpc.WatchtowerClient - WatchtowerClient wtclientrpc.WatchtowerClientClient - State lnrpc.StateClient - ChainClient chainrpc.ChainNotifierClient - ChainKit chainrpc.ChainKitClient - NeutrinoClient neutrinorpc.NeutrinoKitClient -} - -// Assert *HarnessNode implements the lnrpc.LightningClient interface. -var _ lnrpc.LightningClient = (*HarnessNode)(nil) -var _ lnrpc.WalletUnlockerClient = (*HarnessNode)(nil) -var _ invoicesrpc.InvoicesClient = (*HarnessNode)(nil) -var _ peersrpc.PeersClient = (*HarnessNode)(nil) - -// nextNodeID generates a unique sequence to be used as the node's ID. -func nextNodeID() int { - numActiveNodesMtx.Lock() - defer numActiveNodesMtx.Unlock() - nodeNum := numActiveNodes - numActiveNodes++ - - return nodeNum -} - -// newNode creates a new test lightning node instance from the passed config. -func newNode(cfg *BaseNodeConfig) (*HarnessNode, error) { - if cfg.BaseDir == "" { - var err error - cfg.BaseDir, err = ioutil.TempDir("", "lndtest-node") - if err != nil { - return nil, err - } - } - cfg.DataDir = filepath.Join(cfg.BaseDir, "data") - cfg.LogDir = filepath.Join(cfg.BaseDir, "logs") - cfg.TLSCertPath = filepath.Join(cfg.BaseDir, "tls.cert") - cfg.TLSKeyPath = filepath.Join(cfg.BaseDir, "tls.key") - - networkDir := filepath.Join( - cfg.DataDir, "chain", "bitcoin", cfg.NetParams.Name, - ) - cfg.AdminMacPath = filepath.Join(networkDir, "admin.macaroon") - cfg.ReadMacPath = filepath.Join(networkDir, "readonly.macaroon") - cfg.InvoiceMacPath = filepath.Join(networkDir, "invoice.macaroon") - - cfg.GenerateListeningPorts() - - // Run all tests with accept keysend. The keysend code is very isolated - // and it is highly unlikely that it would affect regular itests when - // enabled. - cfg.AcceptKeySend = true - - // Create temporary database. - var dbName string - if cfg.DbBackend == BackendPostgres { - var err error - dbName, err = createTempPgDb() - if err != nil { - return nil, err - } - cfg.PostgresDsn = postgresDatabaseDsn(dbName) - } - - return &HarnessNode{ - Cfg: cfg, - NodeID: nextNodeID(), - chanWatchRequests: make(chan *chanWatchRequest), - openChans: make(map[wire.OutPoint]int), - openChanWatchers: make(map[wire.OutPoint][]chan struct{}), - - closedChans: make(map[wire.OutPoint]struct{}), - closeChanWatchers: make(map[wire.OutPoint][]chan struct{}), - - policyUpdates: policyUpdateMap{}, - - postgresDbName: dbName, - }, nil -} - -func createTempPgDb() (string, error) { - // Create random database name. - randBytes := make([]byte, 8) - _, err := rand.Read(randBytes) - if err != nil { - return "", err - } - dbName := "itest_" + hex.EncodeToString(randBytes) - - // Create database. - err = executePgQuery("CREATE DATABASE " + dbName) - if err != nil { - return "", err - } - - return dbName, nil -} - -func executePgQuery(query string) error { - pool, err := pgxpool.Connect( - context.Background(), - postgresDatabaseDsn("postgres"), - ) - if err != nil { - return fmt.Errorf("unable to connect to database: %w", err) - } - defer pool.Close() - - _, err = pool.Exec(context.Background(), query) - return err -} - -// String gives the internal state of the node which is useful for debugging. -func (hn *HarnessNode) String() string { - type nodeCfg struct { - LogFilenamePrefix string - ExtraArgs []string - HasSeed bool - P2PPort int - RPCPort int - RESTPort int - ProfilePort int - AcceptKeySend bool - AcceptAMP bool - FeeURL string - } - - nodeState := struct { - NodeID int - Name string - PubKey string - OpenChans map[string]int - ClosedChans map[string]struct{} - NodeCfg nodeCfg - }{ - NodeID: hn.NodeID, - Name: hn.Cfg.Name, - PubKey: hn.PubKeyStr, - OpenChans: make(map[string]int), - ClosedChans: make(map[string]struct{}), - NodeCfg: nodeCfg{ - LogFilenamePrefix: hn.Cfg.LogFilenamePrefix, - ExtraArgs: hn.Cfg.ExtraArgs, - HasSeed: hn.Cfg.HasSeed, - P2PPort: hn.Cfg.P2PPort, - RPCPort: hn.Cfg.RPCPort, - RESTPort: hn.Cfg.RESTPort, - AcceptKeySend: hn.Cfg.AcceptKeySend, - AcceptAMP: hn.Cfg.AcceptAMP, - FeeURL: hn.Cfg.FeeURL, - }, - } - - for outpoint, count := range hn.openChans { - nodeState.OpenChans[outpoint.String()] = count - } - for outpoint, count := range hn.closedChans { - nodeState.ClosedChans[outpoint.String()] = count - } - - stateBytes, err := json.MarshalIndent(nodeState, "", "\t") - if err != nil { - return fmt.Sprintf("\n encode node state with err: %v", err) - } - - return fmt.Sprintf("\nnode state: %s", stateBytes) -} - -// DBPath returns the filepath to the channeldb database file for this node. -func (hn *HarnessNode) DBPath() string { - return hn.Cfg.DBPath() -} - -// DBDir returns the path for the directory holding channeldb file(s). -func (hn *HarnessNode) DBDir() string { - return hn.Cfg.DBDir() -} - -// Name returns the name of this node set during initialization. -func (hn *HarnessNode) Name() string { - return hn.Cfg.Name -} - -// TLSCertStr returns the path where the TLS certificate is stored. -func (hn *HarnessNode) TLSCertStr() string { - return hn.Cfg.TLSCertPath -} - -// TLSKeyStr returns the path where the TLS key is stored. -func (hn *HarnessNode) TLSKeyStr() string { - return hn.Cfg.TLSKeyPath -} - -// ChanBackupPath returns the fielpath to the on-disk channel.backup file for -// this node. -func (hn *HarnessNode) ChanBackupPath() string { - return hn.Cfg.ChanBackupPath() -} - -// AdminMacPath returns the filepath to the admin.macaroon file for this node. -func (hn *HarnessNode) AdminMacPath() string { - return hn.Cfg.AdminMacPath -} - -// ReadMacPath returns the filepath to the readonly.macaroon file for this node. -func (hn *HarnessNode) ReadMacPath() string { - return hn.Cfg.ReadMacPath -} - -// InvoiceMacPath returns the filepath to the invoice.macaroon file for this -// node. -func (hn *HarnessNode) InvoiceMacPath() string { - return hn.Cfg.InvoiceMacPath -} - -// startLnd handles the startup of lnd, creating log files, and possibly kills -// the process when needed. -func (hn *HarnessNode) startLnd(lndBinary string, lndError chan<- error) error { - args := hn.Cfg.GenArgs() - hn.cmd = exec.Command(lndBinary, args...) - - // Redirect stderr output to buffer - var errb bytes.Buffer - hn.cmd.Stderr = &errb - - // If the logoutput flag is passed, redirect output from the nodes to - // log files. - var ( - fileName string - err error - ) - if *LogOutput { - fileName, err = addLogFile(hn) - if err != nil { - return err - } - } - - if err := hn.cmd.Start(); err != nil { - return err - } - - // Launch a new goroutine which that bubbles up any potential fatal - // process errors to the goroutine running the tests. - hn.wg.Add(1) - go func() { - defer hn.wg.Done() - - err := hn.cmd.Wait() - if err != nil { - lndError <- fmt.Errorf("%v\n%v", err, errb.String()) - } - - // Make sure log file is closed and renamed if necessary. - finalizeLogfile(hn, fileName) - - // Rename the etcd.log file if the node was running on embedded - // etcd. - finalizeEtcdLog(hn) - }() - - return nil -} - -// Start launches a new process running lnd. Additionally, the PID of the -// launched process is saved in order to possibly kill the process forcibly -// later. -// -// This may not clean up properly if an error is returned, so the caller should -// call shutdown() regardless of the return value. -func (hn *HarnessNode) start(lndBinary string, lndError chan<- error, - wait bool) error { - - // Init the runCtx. - ctxt, cancel := context.WithCancel(context.Background()) - hn.runCtx = ctxt - hn.cancel = cancel - - // Start lnd and prepare logs. - if err := hn.startLnd(lndBinary, lndError); err != nil { - return err - } - - // We may want to skip waiting for the node to come up (eg. the node - // is waiting to become the leader). - if !wait { - return nil - } - - // Since Stop uses the LightningClient to stop the node, if we fail to - // get a connected client, we have to kill the process. - useMacaroons := !hn.Cfg.HasSeed - conn, err := hn.ConnectRPC(useMacaroons) - if err != nil { - err = fmt.Errorf("ConnectRPC err: %w", err) - cmdErr := hn.cmd.Process.Kill() - if cmdErr != nil { - err = fmt.Errorf("kill process got err: %w: %v", - cmdErr, err) - } - return err - } - - // Init all the RPC clients. - hn.InitRPCClients(conn) - - if err := hn.WaitUntilStarted(); err != nil { - return err - } - - // If the node was created with a seed, we will need to perform an - // additional step to unlock the wallet. The connection returned will - // only use the TLS certs, and can only perform operations necessary to - // unlock the daemon. - if hn.Cfg.HasSeed { - // TODO(yy): remove - hn.WalletUnlockerClient = lnrpc.NewWalletUnlockerClient(conn) - return nil - } - - return hn.initLightningClient() -} - -// WaitUntilStarted waits until the wallet state flips from "WAITING_TO_START". -func (hn *HarnessNode) WaitUntilStarted() error { - return hn.waitTillServerState(func(s lnrpc.WalletState) bool { - return s != lnrpc.WalletState_WAITING_TO_START - }) -} - -// WaitUntilStateReached waits until the given wallet state (or one of the -// states following it) has been reached. -func (hn *HarnessNode) WaitUntilStateReached( - desiredState lnrpc.WalletState) error { - - return hn.waitTillServerState(func(s lnrpc.WalletState) bool { - return s >= desiredState - }) -} - -// WaitUntilServerActive waits until the lnd daemon is fully started. -func (hn *HarnessNode) WaitUntilServerActive() error { - return hn.waitTillServerState(func(s lnrpc.WalletState) bool { - return s == lnrpc.WalletState_SERVER_ACTIVE - }) -} - -// WaitUntilLeader attempts to finish the start procedure by initiating an RPC -// connection and setting up the wallet unlocker client. This is needed when -// a node that has recently been started was waiting to become the leader and -// we're at the point when we expect that it is the leader now (awaiting -// unlock). -func (hn *HarnessNode) WaitUntilLeader(timeout time.Duration) error { - var ( - conn *grpc.ClientConn - connErr error - ) - - if err := wait.NoError(func() error { - conn, connErr = hn.ConnectRPC(!hn.Cfg.HasSeed) - return connErr - }, timeout); err != nil { - return err - } - - // Init all the RPC clients. - hn.InitRPCClients(conn) - - if err := hn.WaitUntilStarted(); err != nil { - return err - } - - // If the node was created with a seed, we will need to perform an - // additional step to unlock the wallet. The connection returned will - // only use the TLS certs, and can only perform operations necessary to - // unlock the daemon. - if hn.Cfg.HasSeed { - // TODO(yy): remove - hn.WalletUnlockerClient = lnrpc.NewWalletUnlockerClient(conn) - - return nil - } - - return hn.initLightningClient() -} - -// initClientWhenReady waits until the main gRPC server is detected as active, -// then complete the normal HarnessNode gRPC connection creation. If the node -// is initialized stateless, the macaroon is returned so that the client can -// use it. -func (hn *HarnessNode) initClientWhenReady(stateless bool, - macBytes []byte) error { - - // Wait for the wallet to finish unlocking, such that we can connect to - // it via a macaroon-authenticated rpc connection. - var ( - conn *grpc.ClientConn - err error - ) - if err = wait.NoError(func() error { - // If the node has been initialized stateless, we need to pass - // the macaroon to the client. - if stateless { - adminMac := &macaroon.Macaroon{} - err := adminMac.UnmarshalBinary(macBytes) - if err != nil { - return fmt.Errorf("unmarshal failed: %w", err) - } - conn, err = hn.ConnectRPCWithMacaroon(adminMac) - return err - } - - // Normal initialization, we expect a macaroon to be in the - // file system. - conn, err = hn.ConnectRPC(true) - return err - }, DefaultTimeout); err != nil { - return fmt.Errorf("timeout while init client: %w", err) - } - - // Init all the RPC clients. - hn.InitRPCClients(conn) - - return hn.initLightningClient() -} - -// Init initializes a harness node by passing the init request via rpc. After -// the request is submitted, this method will block until a -// macaroon-authenticated RPC connection can be established to the harness -// node. Once established, the new connection is used to initialize the -// LightningClient and subscribes the HarnessNode to topology changes. -func (hn *HarnessNode) Init( - initReq *lnrpc.InitWalletRequest) (*lnrpc.InitWalletResponse, error) { - - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - response, err := hn.rpc.WalletUnlocker.InitWallet(ctxt, initReq) - if err != nil { - return nil, fmt.Errorf("failed to init wallet: %w", err) - } - - err = hn.initClientWhenReady( - initReq.StatelessInit, response.AdminMacaroon, - ) - if err != nil { - return nil, fmt.Errorf("failed to init: %w", err) - } - - return response, nil -} - -// InitChangePassword initializes a harness node by passing the change password -// request via RPC. After the request is submitted, this method will block until -// a macaroon-authenticated RPC connection can be established to the harness -// node. Once established, the new connection is used to initialize the -// LightningClient and subscribes the HarnessNode to topology changes. -func (hn *HarnessNode) InitChangePassword( - chngPwReq *lnrpc.ChangePasswordRequest) (*lnrpc.ChangePasswordResponse, - error) { - - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - response, err := hn.rpc.WalletUnlocker.ChangePassword(ctxt, chngPwReq) - if err != nil { - return nil, err - } - err = hn.initClientWhenReady( - chngPwReq.StatelessInit, response.AdminMacaroon, - ) - if err != nil { - return nil, err - } - - return response, nil -} - -// Unlock attempts to unlock the wallet of the target HarnessNode. This method -// should be called after the restart of a HarnessNode that was created with a -// seed+password. Once this method returns, the HarnessNode will be ready to -// accept normal gRPC requests and harness command. -func (hn *HarnessNode) Unlock(unlockReq *lnrpc.UnlockWalletRequest) error { - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - // Otherwise, we'll need to unlock the node before it's able to start - // up properly. - _, err := hn.rpc.WalletUnlocker.UnlockWallet(ctxt, unlockReq) - if err != nil { - return err - } - - // Now that the wallet has been unlocked, we'll wait for the RPC client - // to be ready, then establish the normal gRPC connection. - return hn.initClientWhenReady(false, nil) -} - -// waitTillServerState makes a subscription to the server's state change and -// blocks until the server is in the targeted state. -func (hn *HarnessNode) waitTillServerState( - predicate func(state lnrpc.WalletState) bool) error { - - ctxt, cancel := context.WithTimeout(hn.runCtx, NodeStartTimeout) - defer cancel() - - client, err := hn.rpc.State.SubscribeState( - ctxt, &lnrpc.SubscribeStateRequest{}, - ) - if err != nil { - return fmt.Errorf("failed to subscribe to state: %w", err) - } - - errChan := make(chan error, 1) - done := make(chan struct{}) - go func() { - for { - resp, err := client.Recv() - if err != nil { - errChan <- err - return - } - - if predicate(resp.State) { - close(done) - return - } - } - }() - - var lastErr error - for { - select { - case err := <-errChan: - lastErr = err - - case <-done: - return nil - - case <-time.After(NodeStartTimeout): - return fmt.Errorf("timeout waiting for state, "+ - "got err from stream: %v", lastErr) - } - } -} - -// InitRPCClients initializes a list of RPC clients for the node. -func (hn *HarnessNode) InitRPCClients(c *grpc.ClientConn) { - hn.rpc = &RPCClients{ - conn: c, - LN: lnrpc.NewLightningClient(c), - Invoice: invoicesrpc.NewInvoicesClient(c), - Router: routerrpc.NewRouterClient(c), - WalletKit: walletrpc.NewWalletKitClient(c), - WalletUnlocker: lnrpc.NewWalletUnlockerClient(c), - Watchtower: watchtowerrpc.NewWatchtowerClient(c), - WatchtowerClient: wtclientrpc.NewWatchtowerClientClient(c), - Signer: signrpc.NewSignerClient(c), - State: lnrpc.NewStateClient(c), - ChainClient: chainrpc.NewChainNotifierClient(c), - ChainKit: chainrpc.NewChainKitClient(c), - NeutrinoClient: neutrinorpc.NewNeutrinoKitClient(c), - } -} - -// initLightningClient blocks until the lnd server is fully started and -// subscribes the harness node to graph topology updates. This method also -// spawns a lightning network watcher for this node, which watches for topology -// changes. -func (hn *HarnessNode) initLightningClient() error { - // TODO(yy): remove - // Construct the LightningClient that will allow us to use the - // HarnessNode directly for normal rpc operations. - conn := hn.rpc.conn - hn.LightningClient = lnrpc.NewLightningClient(conn) - hn.InvoicesClient = invoicesrpc.NewInvoicesClient(conn) - hn.RouterClient = routerrpc.NewRouterClient(conn) - hn.WalletKitClient = walletrpc.NewWalletKitClient(conn) - hn.Watchtower = watchtowerrpc.NewWatchtowerClient(conn) - hn.WatchtowerClient = wtclientrpc.NewWatchtowerClientClient(conn) - hn.SignerClient = signrpc.NewSignerClient(conn) - hn.PeersClient = peersrpc.NewPeersClient(conn) - hn.StateClient = lnrpc.NewStateClient(conn) - hn.ChainClient = chainrpc.NewChainNotifierClient(conn) - hn.ChainKit = chainrpc.NewChainKitClient(conn) - hn.NeutrinoClient = neutrinorpc.NewNeutrinoKitClient(conn) - - // Wait until the server is fully started. - if err := hn.WaitUntilServerActive(); err != nil { - return err - } - - // Set the harness node's pubkey to what the node claims in GetInfo. - // The RPC must have been started at this point. - if err := hn.FetchNodeInfo(); err != nil { - return err - } - - // Launch the watcher that will hook into graph related topology change - // from the PoV of this node. - hn.wg.Add(1) - go hn.lightningNetworkWatcher() - - return nil -} - -// FetchNodeInfo queries an unlocked node to retrieve its public key. -func (hn *HarnessNode) FetchNodeInfo() error { - // Obtain the lnid of this node for quick identification purposes. - info, err := hn.rpc.LN.GetInfo(hn.runCtx, &lnrpc.GetInfoRequest{}) - if err != nil { - return err - } - - hn.PubKeyStr = info.IdentityPubkey - - pubkey, err := hex.DecodeString(info.IdentityPubkey) - if err != nil { - return err - } - copy(hn.PubKey[:], pubkey) - - return nil -} - -// AddToLogf adds a line of choice to the node's logfile. This is useful -// to interleave test output with output from the node. -func (hn *HarnessNode) AddToLogf(format string, a ...interface{}) { - // If this node was not set up with a log file, just return early. - if hn.logFile == nil { - return - } - - desc := fmt.Sprintf("itest: %s\n", fmt.Sprintf(format, a...)) - if _, err := hn.logFile.WriteString(desc); err != nil { - hn.PrintErrf("write to log err: %v", err) - } -} - -// ReadMacaroon waits a given duration for the macaroon file to be created. If -// the file is readable within the timeout, its content is de-serialized as a -// macaroon and returned. -func (hn *HarnessNode) ReadMacaroon(macPath string, timeout time.Duration) ( - *macaroon.Macaroon, error) { - - // Wait until macaroon file is created and has valid content before - // using it. - var mac *macaroon.Macaroon - err := wait.NoError(func() error { - macBytes, err := ioutil.ReadFile(macPath) - if err != nil { - return fmt.Errorf("error reading macaroon file: %v", - err) - } - - newMac := &macaroon.Macaroon{} - if err = newMac.UnmarshalBinary(macBytes); err != nil { - return fmt.Errorf("error unmarshalling macaroon "+ - "file: %v", err) - } - mac = newMac - - return nil - }, timeout) - - return mac, err -} - -// ConnectRPCWithMacaroon uses the TLS certificate and given macaroon to -// create a gRPC client connection. -func (hn *HarnessNode) ConnectRPCWithMacaroon(mac *macaroon.Macaroon) ( - *grpc.ClientConn, error) { - - // Wait until TLS certificate is created and has valid content before - // using it, up to 30 sec. - var tlsCreds credentials.TransportCredentials - err := wait.NoError(func() error { - var err error - tlsCreds, err = credentials.NewClientTLSFromFile( - hn.Cfg.TLSCertPath, "", - ) - return err - }, DefaultTimeout) - if err != nil { - return nil, fmt.Errorf("error reading TLS cert: %v", err) - } - - opts := []grpc.DialOption{ - grpc.WithBlock(), - grpc.WithTransportCredentials(tlsCreds), - } - - ctx, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - if mac == nil { - return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...) - } - macCred, err := macaroons.NewMacaroonCredential(mac) - if err != nil { - return nil, fmt.Errorf("error cloning mac: %v", err) - } - opts = append(opts, grpc.WithPerRPCCredentials(macCred)) - - return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...) -} - -// ConnectRPC uses the TLS certificate and admin macaroon files written by the -// lnd node to create a gRPC client connection. -func (hn *HarnessNode) ConnectRPC(useMacs bool) (*grpc.ClientConn, error) { - // If we don't want to use macaroons, just pass nil, the next method - // will handle it correctly. - if !useMacs { - return hn.ConnectRPCWithMacaroon(nil) - } - - // If we should use a macaroon, always take the admin macaroon as a - // default. - mac, err := hn.ReadMacaroon(hn.Cfg.AdminMacPath, DefaultTimeout) - if err != nil { - return nil, err - } - return hn.ConnectRPCWithMacaroon(mac) -} - -// SetExtraArgs assigns the ExtraArgs field for the node's configuration. The -// changes will take effect on restart. -func (hn *HarnessNode) SetExtraArgs(extraArgs []string) { - hn.Cfg.ExtraArgs = extraArgs -} - -// cleanup cleans up all the temporary files created by the node's process. -func (hn *HarnessNode) cleanup() error { - if hn.backupDbDir != "" { - err := os.RemoveAll(hn.backupDbDir) - if err != nil { - return fmt.Errorf("unable to remove backup dir: %v", - err) - } - } - - return os.RemoveAll(hn.Cfg.BaseDir) -} - -// Stop attempts to stop the active lnd process. -func (hn *HarnessNode) stop() error { - // Do nothing if the process is not running. - if hn.runCtx == nil { - return nil - } - - // If start() failed before creating clients, we will just wait for the - // child process to die. - if hn.rpc != nil && hn.rpc.LN != nil { - // Don't watch for error because sometimes the RPC connection - // gets closed before a response is returned. - req := lnrpc.StopRequest{} - - err := wait.NoError(func() error { - _, err := hn.rpc.LN.StopDaemon(hn.runCtx, &req) - switch { - case err == nil: - return nil - - // Try again if a recovery/rescan is in progress. - case strings.Contains( - err.Error(), "recovery in progress", - ): - return err - - default: - return nil - } - }, DefaultTimeout) - if err != nil { - return err - } - } - - // Stop the runCtx and wait for goroutines to finish. - hn.cancel() - - // Wait for lnd process to exit. - err := wait.NoError(func() error { - if hn.cmd.ProcessState == nil { - return fmt.Errorf("process did not exit") - } - - if !hn.cmd.ProcessState.Exited() { - return fmt.Errorf("process did not exit") - } - - // Wait for goroutines to be finished. - hn.wg.Wait() - - return nil - }, DefaultTimeout*2) - if err != nil { - return err - } - - hn.LightningClient = nil - hn.WalletUnlockerClient = nil - hn.Watchtower = nil - hn.WatchtowerClient = nil - hn.NeutrinoClient = nil - - // Close any attempts at further grpc connections. - if hn.rpc.conn != nil { - err := status.Code(hn.rpc.conn.Close()) - switch err { - case codes.OK: - return nil - - // When the context is canceled above, we might get the - // following error as the context is no longer active. - case codes.Canceled: - return nil - - case codes.Unknown: - return fmt.Errorf("unknown error attempting to stop "+ - "grpc client: %v", err) - - default: - return fmt.Errorf("error attempting to stop "+ - "grpc client: %v", err) - } - } - - return nil -} - -// shutdown stops the active lnd process and cleans up any temporary -// directories created along the way. -func (hn *HarnessNode) shutdown() error { - if err := hn.stop(); err != nil { - return err - } - if err := hn.cleanup(); err != nil { - return err - } - return nil -} - -// kill kills the lnd process. -func (hn *HarnessNode) kill() error { - return hn.cmd.Process.Kill() -} - -type chanWatchType uint8 - -const ( - // watchOpenChannel specifies that this is a request to watch an open - // channel event. - watchOpenChannel chanWatchType = iota - - // watchCloseChannel specifies that this is a request to watch a close - // channel event. - watchCloseChannel - - // watchPolicyUpdate specifies that this is a request to watch a policy - // update event. - watchPolicyUpdate -) - -// closeChanWatchRequest is a request to the lightningNetworkWatcher to be -// notified once it's detected within the test Lightning Network, that a -// channel has either been added or closed. -type chanWatchRequest struct { - chanPoint wire.OutPoint - - chanWatchType chanWatchType - - eventChan chan struct{} - - advertisingNode string - policy *lnrpc.RoutingPolicy - includeUnannounced bool -} - -func (hn *HarnessNode) checkChanPointInGraph(chanPoint wire.OutPoint) bool { - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - chanGraph, err := hn.DescribeGraph(ctxt, &lnrpc.ChannelGraphRequest{}) - if err != nil { - return false - } - - targetChanPoint := chanPoint.String() - for _, chanEdge := range chanGraph.Edges { - candidateChanPoint := chanEdge.ChanPoint - if targetChanPoint == candidateChanPoint { - return true - } - } - - return false -} - -// lightningNetworkWatcher is a goroutine which is able to dispatch -// notifications once it has been observed that a target channel has been -// closed or opened within the network. In order to dispatch these -// notifications, the GraphTopologySubscription client exposed as part of the -// gRPC interface is used. -func (hn *HarnessNode) lightningNetworkWatcher() { - defer hn.wg.Done() - - graphUpdates := make(chan *lnrpc.GraphTopologyUpdate) - - // Start a goroutine to receive graph updates. - hn.wg.Add(1) - go func() { - defer hn.wg.Done() - err := hn.receiveTopologyClientStream(graphUpdates) - - if err != nil { - hn.PrintErrf("receive topology client stream "+ - "got err:%v", err) - } - }() - - for { - select { - // A new graph update has just been received, so we'll examine - // the current set of registered clients to see if we can - // dispatch any requests. - case graphUpdate := <-graphUpdates: - hn.handleChannelEdgeUpdates(graphUpdate.ChannelUpdates) - hn.handleClosedChannelUpdate(graphUpdate.ClosedChans) - // TODO(yy): handle node updates too - - // A new watch request, has just arrived. We'll either be able - // to dispatch immediately, or need to add the client for - // processing later. - case watchRequest := <-hn.chanWatchRequests: - switch watchRequest.chanWatchType { - case watchOpenChannel: - // TODO(roasbeef): add update type also, checks - // for multiple of 2 - hn.handleOpenChannelWatchRequest(watchRequest) - - case watchCloseChannel: - hn.handleCloseChannelWatchRequest(watchRequest) - - case watchPolicyUpdate: - hn.handlePolicyUpdateWatchRequest(watchRequest) - } - - case <-hn.runCtx.Done(): - return - } - } -} - -// WaitForNetworkChannelOpen will block until a channel with the target -// outpoint is seen as being fully advertised within the network. A channel is -// considered "fully advertised" once both of its directional edges have been -// advertised within the test Lightning Network. -func (hn *HarnessNode) WaitForNetworkChannelOpen( - chanPoint *lnrpc.ChannelPoint) error { - - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - eventChan := make(chan struct{}) - - op, err := MakeOutpoint(chanPoint) - if err != nil { - return fmt.Errorf("failed to create outpoint for %v "+ - "got err: %v", chanPoint, err) - } - - hn.chanWatchRequests <- &chanWatchRequest{ - chanPoint: op, - eventChan: eventChan, - chanWatchType: watchOpenChannel, - } - - select { - case <-eventChan: - return nil - case <-ctxt.Done(): - return fmt.Errorf("channel:%s not opened before timeout: %s", - op, hn) - } -} - -// WaitForNetworkChannelClose will block until a channel with the target -// outpoint is seen as closed within the network. A channel is considered -// closed once a transaction spending the funding outpoint is seen within a -// confirmed block. -func (hn *HarnessNode) WaitForNetworkChannelClose( - chanPoint *lnrpc.ChannelPoint) error { - - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - eventChan := make(chan struct{}) - - op, err := MakeOutpoint(chanPoint) - if err != nil { - return fmt.Errorf("failed to create outpoint for %v "+ - "got err: %v", chanPoint, err) - } - - hn.chanWatchRequests <- &chanWatchRequest{ - chanPoint: op, - eventChan: eventChan, - chanWatchType: watchCloseChannel, - } - - select { - case <-eventChan: - return nil - case <-ctxt.Done(): - return fmt.Errorf("channel:%s not closed before timeout: "+ - "%s", op, hn) - } -} - -// WaitForChannelPolicyUpdate will block until a channel policy with the target -// outpoint and advertisingNode is seen within the network. -func (hn *HarnessNode) WaitForChannelPolicyUpdate( - advertisingNode string, policy *lnrpc.RoutingPolicy, - chanPoint *lnrpc.ChannelPoint, includeUnannounced bool) error { - - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - eventChan := make(chan struct{}) - - op, err := MakeOutpoint(chanPoint) - if err != nil { - return fmt.Errorf("failed to create outpoint for %v"+ - "got err: %v", chanPoint, err) - } - - ticker := time.NewTicker(wait.PollInterval) - defer ticker.Stop() - - for { - select { - // Send a watch request every second. - case <-ticker.C: - // Did the event can close in the meantime? We want to - // avoid a "close of closed channel" panic since we're - // re-using the same event chan for multiple requests. - select { - case <-eventChan: - return nil - default: - } - - hn.chanWatchRequests <- &chanWatchRequest{ - chanPoint: op, - eventChan: eventChan, - chanWatchType: watchPolicyUpdate, - policy: policy, - advertisingNode: advertisingNode, - includeUnannounced: includeUnannounced, - } - - case <-eventChan: - return nil - - case <-ctxt.Done(): - return fmt.Errorf("channel:%s policy not updated "+ - "before timeout: [%s:%v] %s", op, - advertisingNode, policy, hn.String()) - } - } -} - -// WaitForBlockchainSync waits for the target node to be fully synchronized -// with the blockchain. If the passed context object has a set timeout, it will -// continually poll until the timeout has elapsed. In the case that the chain -// isn't synced before the timeout is up, this function will return an error. -func (hn *HarnessNode) WaitForBlockchainSync() error { - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - ticker := time.NewTicker(time.Millisecond * 100) - defer ticker.Stop() - - for { - resp, err := hn.rpc.LN.GetInfo(ctxt, &lnrpc.GetInfoRequest{}) - if err != nil { - return err - } - if resp.SyncedToChain { - return nil - } - - select { - case <-ctxt.Done(): - return fmt.Errorf("timeout while waiting for " + - "blockchain sync") - case <-hn.runCtx.Done(): - return nil - case <-ticker.C: - } - } -} - -// WaitForBalance waits until the node sees the expected confirmed/unconfirmed -// balance within their wallet. -func (hn *HarnessNode) WaitForBalance(expectedBalance btcutil.Amount, - confirmed bool) error { - - req := &lnrpc.WalletBalanceRequest{} - - var lastBalance btcutil.Amount - doesBalanceMatch := func() bool { - balance, err := hn.rpc.LN.WalletBalance(hn.runCtx, req) - if err != nil { - return false - } - - if confirmed { - lastBalance = btcutil.Amount(balance.ConfirmedBalance) - return btcutil.Amount(balance.ConfirmedBalance) == - expectedBalance - } - - lastBalance = btcutil.Amount(balance.UnconfirmedBalance) - return btcutil.Amount(balance.UnconfirmedBalance) == - expectedBalance - } - - err := wait.Predicate(doesBalanceMatch, DefaultTimeout) - if err != nil { - return fmt.Errorf("balances not synced after deadline: "+ - "expected %v, only have %v", expectedBalance, - lastBalance) - } - - return nil -} - -// PrintErrf prints an error to the console. -func (hn *HarnessNode) PrintErrf(format string, a ...interface{}) { - fmt.Printf("itest error from [node:%s]: %s\n", // nolint:forbidigo - hn.Cfg.Name, fmt.Sprintf(format, a...)) -} - -// handleChannelEdgeUpdates takes a series of channel edge updates, extracts -// the outpoints, and saves them to harness node's internal state. -func (hn *HarnessNode) handleChannelEdgeUpdates( - updates []*lnrpc.ChannelEdgeUpdate) { - - // For each new channel, we'll increment the number of - // edges seen by one. - for _, newChan := range updates { - op, err := MakeOutpoint(newChan.ChanPoint) - if err != nil { - hn.PrintErrf("failed to create outpoint for %v "+ - "got err: %v", newChan.ChanPoint, err) - return - } - hn.openChans[op]++ - - // For this new channel, if the number of edges seen is less - // than two, then the channel hasn't been fully announced yet. - if numEdges := hn.openChans[op]; numEdges < 2 { - return - } - - // Otherwise, we'll notify all the registered watchers and - // remove the dispatched watchers. - for _, eventChan := range hn.openChanWatchers[op] { - close(eventChan) - } - delete(hn.openChanWatchers, op) - - // Check whether there's a routing policy update. If so, save - // it to the node state. - if newChan.RoutingPolicy == nil { - continue - } - - // Append the policy to the slice. - node := newChan.AdvertisingNode - policies := hn.policyUpdates[op.String()] - - // If the map[op] is nil, we need to initialize the map first. - if policies == nil { - policies = make(map[string][]*lnrpc.RoutingPolicy) - } - policies[node] = append( - policies[node], newChan.RoutingPolicy, - ) - hn.policyUpdates[op.String()] = policies - } -} - -// handleOpenChannelWatchRequest processes a watch open channel request by -// checking the number of the edges seen for a given channel point. If the -// number is no less than 2 then the channel is considered open. Otherwise, we -// will attempt to find it in its channel graph. If neither can be found, the -// request is added to a watch request list than will be handled by -// handleChannelEdgeUpdates. -func (hn *HarnessNode) handleOpenChannelWatchRequest(req *chanWatchRequest) { - targetChan := req.chanPoint - - // If this is an open request, then it can be dispatched if the number - // of edges seen for the channel is at least two. - if numEdges := hn.openChans[targetChan]; numEdges >= 2 { - close(req.eventChan) - return - } - - // Before we add the channel to our set of open clients, we'll check to - // see if the channel is already in the channel graph of the target - // node. This lets us handle the case where a node has already seen a - // channel before a notification has been requested, causing us to miss - // it. - chanFound := hn.checkChanPointInGraph(targetChan) - if chanFound { - close(req.eventChan) - return - } - - // Otherwise, we'll add this to the list of open channel watchers for - // this out point. - hn.openChanWatchers[targetChan] = append( - hn.openChanWatchers[targetChan], - req.eventChan, - ) -} - -// handleClosedChannelUpdate takes a series of closed channel updates, extracts -// the outpoints, saves them to harness node's internal state, and notifies all -// registered clients. -func (hn *HarnessNode) handleClosedChannelUpdate( - updates []*lnrpc.ClosedChannelUpdate) { - - // For each channel closed, we'll mark that we've detected a channel - // closure while lnd was pruning the channel graph. - for _, closedChan := range updates { - op, err := MakeOutpoint(closedChan.ChanPoint) - if err != nil { - hn.PrintErrf("failed to create outpoint for %v "+ - "got err: %v", closedChan.ChanPoint, err) - return - } - - hn.closedChans[op] = struct{}{} - - // As the channel has been closed, we'll notify all register - // watchers. - for _, eventChan := range hn.closeChanWatchers[op] { - close(eventChan) - } - delete(hn.closeChanWatchers, op) - } -} - -// handleCloseChannelWatchRequest processes a watch close channel request by -// checking whether the given channel point can be found in the node's internal -// state. If not, the request is added to a watch request list than will be -// handled by handleCloseChannelWatchRequest. -func (hn *HarnessNode) handleCloseChannelWatchRequest(req *chanWatchRequest) { - targetChan := req.chanPoint - - // If this is a close request, then it can be immediately dispatched if - // we've already seen a channel closure for this channel. - if _, ok := hn.closedChans[targetChan]; ok { - close(req.eventChan) - return - } - - // Otherwise, we'll add this to the list of close channel watchers for - // this out point. - hn.closeChanWatchers[targetChan] = append( - hn.closeChanWatchers[targetChan], - req.eventChan, - ) -} - -type topologyClient lnrpc.Lightning_SubscribeChannelGraphClient - -// newTopologyClient creates a topology client. -func (hn *HarnessNode) newTopologyClient( - ctx context.Context) (topologyClient, error) { - - req := &lnrpc.GraphTopologySubscription{} - client, err := hn.rpc.LN.SubscribeChannelGraph(ctx, req) - if err != nil { - return nil, fmt.Errorf("%s(%d): unable to create topology "+ - "client: %v (%s)", hn.Name(), hn.NodeID, err, - time.Now().String()) - } - - return client, nil -} - -// receiveTopologyClientStream initializes a topologyClient to subscribe -// topology update events. Due to a race condition between the ChannelRouter -// starting and us making the subscription request, it's possible for our graph -// subscription to fail. In that case, we will retry the subscription until it -// succeeds or fail after 10 seconds. -// -// NOTE: must be run as a goroutine. -func (hn *HarnessNode) receiveTopologyClientStream( - receiver chan *lnrpc.GraphTopologyUpdate) error { - - // Create a topology client to receive graph updates. - client, err := hn.newTopologyClient(hn.runCtx) - if err != nil { - return fmt.Errorf("create topologyClient failed: %w", err) - } - - // We use the context to time out when retrying graph subscription. - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - for { - update, err := client.Recv() - - switch { - case err == nil: - // Good case. We will send the update to the receiver. - - case strings.Contains(err.Error(), "router not started"): - // If the router hasn't been started, we will retry - // every 200 ms until it has been started or fail - // after the ctxt is timed out. - select { - case <-ctxt.Done(): - return fmt.Errorf("graph subscription: " + - "router not started before timeout") - case <-time.After(wait.PollInterval): - case <-hn.runCtx.Done(): - return nil - } - - // Re-create the topology client. - client, err = hn.newTopologyClient(hn.runCtx) - if err != nil { - return fmt.Errorf("create topologyClient "+ - "failed: %v", err) - } - - continue - - case strings.Contains(err.Error(), "EOF"): - // End of subscription stream. Do nothing and quit. - return nil - - case strings.Contains(err.Error(), context.Canceled.Error()): - // End of subscription stream. Do nothing and quit. - return nil - - default: - // An expected error is returned, return and leave it - // to be handled by the caller. - return fmt.Errorf("graph subscription err: %w", err) - } - - // Send the update or quit. - select { - case receiver <- update: - case <-hn.runCtx.Done(): - return nil - } - } -} - -// handlePolicyUpdateWatchRequest checks that if the expected policy can be -// found either in the node's interval state or describe graph response. If -// found, it will signal the request by closing the event channel. Otherwise it -// does nothing but returns nil. -func (hn *HarnessNode) handlePolicyUpdateWatchRequest(req *chanWatchRequest) { - op := req.chanPoint - - // Get a list of known policies for this chanPoint+advertisingNode - // combination. Start searching in the node state first. - policies, ok := hn.policyUpdates[op.String()][req.advertisingNode] - - if !ok { - // If it cannot be found in the node state, try searching it - // from the node's DescribeGraph. - policyMap := hn.getChannelPolicies(req.includeUnannounced) - policies, ok = policyMap[op.String()][req.advertisingNode] - if !ok { - return - } - } - - // Check if there's a matched policy. - for _, policy := range policies { - if CheckChannelPolicy(policy, req.policy) == nil { - close(req.eventChan) - return - } - } -} - -// getChannelPolicies queries the channel graph and formats the policies into -// the format defined in type policyUpdateMap. -func (hn *HarnessNode) getChannelPolicies(include bool) policyUpdateMap { - ctxt, cancel := context.WithTimeout(hn.runCtx, DefaultTimeout) - defer cancel() - - graph, err := hn.rpc.LN.DescribeGraph(ctxt, &lnrpc.ChannelGraphRequest{ - IncludeUnannounced: include, - }) - if err != nil { - hn.PrintErrf("DescribeGraph got err: %v", err) - return nil - } - - policyUpdates := policyUpdateMap{} - - for _, e := range graph.Edges { - policies := policyUpdates[e.ChanPoint] - - // If the map[op] is nil, we need to initialize the map first. - if policies == nil { - policies = make(map[string][]*lnrpc.RoutingPolicy) - } - - if e.Node1Policy != nil { - policies[e.Node1Pub] = append( - policies[e.Node1Pub], e.Node1Policy, - ) - } - - if e.Node2Policy != nil { - policies[e.Node2Pub] = append( - policies[e.Node2Pub], e.Node2Policy, - ) - } - - policyUpdates[e.ChanPoint] = policies - } - - return policyUpdates -} - -// renameFile is a helper to rename (log) files created during integration -// tests. -func renameFile(fromFileName, toFileName string) { - err := os.Rename(fromFileName, toFileName) - if err != nil { - fmt.Printf("could not rename %s to %s: %v\n", // nolint:forbidigo - fromFileName, toFileName, err) - } -} - -// getFinalizedLogFilePrefix returns the finalize log filename. -func getFinalizedLogFilePrefix(hn *HarnessNode) string { - pubKeyHex := hex.EncodeToString( - hn.PubKey[:logPubKeyBytes], - ) - - return fmt.Sprintf("%s/%d-%s-%s-%s", - GetLogDir(), hn.NodeID, - hn.Cfg.LogFilenamePrefix, - hn.Cfg.Name, pubKeyHex) -} - -// finalizeLogfile makes sure the log file cleanup function is initialized, -// even if no log file is created. -func finalizeLogfile(hn *HarnessNode, fileName string) { - if hn.logFile != nil { - hn.logFile.Close() - - // If logoutput flag is not set, return early. - if !*LogOutput { - return - } - - newFileName := fmt.Sprintf("%v.log", - getFinalizedLogFilePrefix(hn), - ) - - renameFile(fileName, newFileName) - } -} - -func finalizeEtcdLog(hn *HarnessNode) { - if hn.Cfg.DbBackend != BackendEtcd { - return - } - - etcdLogFileName := fmt.Sprintf("%s/etcd.log", hn.Cfg.LogDir) - newEtcdLogFileName := fmt.Sprintf("%v-etcd.log", - getFinalizedLogFilePrefix(hn), - ) - - renameFile(etcdLogFileName, newEtcdLogFileName) -} - -func addLogFile(hn *HarnessNode) (string, error) { - var fileName string - - dir := GetLogDir() - fileName = fmt.Sprintf("%s/%d-%s-%s-%s.log", dir, hn.NodeID, - hn.Cfg.LogFilenamePrefix, hn.Cfg.Name, - hex.EncodeToString(hn.PubKey[:logPubKeyBytes])) - - // If the node's PubKey is not yet initialized, create a - // temporary file name. Later, after the PubKey has been - // initialized, the file can be moved to its final name with - // the PubKey included. - if bytes.Equal(hn.PubKey[:4], []byte{0, 0, 0, 0}) { - fileName = fmt.Sprintf("%s/%d-%s-%s-tmp__.log", dir, - hn.NodeID, hn.Cfg.LogFilenamePrefix, - hn.Cfg.Name) - } - - // Create file if not exists, otherwise append. - file, err := os.OpenFile(fileName, - os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) - if err != nil { - return fileName, err - } - - // Pass node's stderr to both errb and the file. - w := io.MultiWriter(hn.cmd.Stderr, file) - hn.cmd.Stderr = w - - // Pass the node's stdout only to the file. - hn.cmd.Stdout = file - - // Let the node keep a reference to this file, such - // that we can add to it if necessary. - hn.logFile = file - - return fileName, nil -} diff --git a/lntemp/harness_node_manager.go b/lntest/harness_node_manager.go similarity index 96% rename from lntemp/harness_node_manager.go rename to lntest/harness_node_manager.go index 1ee502354..9cb69a54f 100644 --- a/lntemp/harness_node_manager.go +++ b/lntest/harness_node_manager.go @@ -1,4 +1,4 @@ -package lntemp +package lntest import ( "context" @@ -8,8 +8,7 @@ import ( "testing" "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lntemp/node" - "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/lightningnetwork/lnd/lntest/wait" ) @@ -30,7 +29,7 @@ type nodeManager struct { lndBinary string // dbBackend sets the database backend to use. - dbBackend lntest.DatabaseBackend + dbBackend node.DatabaseBackend // activeNodes is a map of all running nodes, format: // {pubkey: *HarnessNode}. @@ -50,7 +49,7 @@ type nodeManager struct { // newNodeManager creates a new node manager instance. func newNodeManager(lndBinary string, - dbBackend lntest.DatabaseBackend) *nodeManager { + dbBackend node.DatabaseBackend) *nodeManager { return &nodeManager{ lndBinary: lndBinary, @@ -80,7 +79,7 @@ func (nm *nodeManager) newNode(t *testing.T, name string, extraArgs []string, BackendCfg: nm.chainBackend, ExtraArgs: extraArgs, FeeURL: nm.feeServiceURL, - DbBackend: nm.dbBackend, + DBBackend: nm.dbBackend, NodeID: nm.nextNodeID(), LndBinary: nm.lndBinary, NetParams: harnessNetParams, diff --git a/lntemp/harness_setup.go b/lntest/harness_setup.go similarity index 85% rename from lntemp/harness_setup.go rename to lntest/harness_setup.go index 8a08c03c3..20442f9c2 100644 --- a/lntemp/harness_setup.go +++ b/lntest/harness_setup.go @@ -1,4 +1,4 @@ -package lntemp +package lntest import ( "context" @@ -6,7 +6,7 @@ import ( "testing" "github.com/btcsuite/btcd/integration/rpctest" - "github.com/lightningnetwork/lnd/lntest" + "github.com/lightningnetwork/lnd/lntest/node" "github.com/stretchr/testify/require" ) @@ -23,11 +23,11 @@ func SetupHarness(t *testing.T, binaryPath, dbBackendName string, t.Log("Setting up HarnessTest...") // Parse testing flags that influence our test execution. - logDir := lntest.GetLogDir() + logDir := node.GetLogDir() require.NoError(t, os.MkdirAll(logDir, 0700), "create log dir failed") // Parse database backend - dbBackend := prepareDbBackend(t, dbBackendName) + dbBackend := prepareDBBackend(t, dbBackendName) // Create a new HarnessTest. ht := NewHarnessTest(t, binaryPath, feeService, dbBackend) @@ -82,9 +82,9 @@ func prepareMiner(ctxt context.Context, t *testing.T) *HarnessMiner { // prepareChainBackend creates a new chain backend. func prepareChainBackend(t *testing.T, - minerAddr string) (lntest.BackendConfig, func()) { + minerAddr string) (node.BackendConfig, func()) { - chainBackend, cleanUp, err := lntest.NewBackend( + chainBackend, cleanUp, err := NewBackend( minerAddr, harnessNetParams, ) require.NoError(t, err, "new backend") @@ -94,23 +94,23 @@ func prepareChainBackend(t *testing.T, } } -// prepareDbBackend parses a DatabaseBackend based on the name given. -func prepareDbBackend(t *testing.T, - dbBackendName string) lntest.DatabaseBackend { +// prepareDBBackend parses a DatabaseBackend based on the name given. +func prepareDBBackend(t *testing.T, + dbBackendName string) node.DatabaseBackend { - var dbBackend lntest.DatabaseBackend + var dbBackend node.DatabaseBackend switch dbBackendName { case "bbolt": - dbBackend = lntest.BackendBbolt + dbBackend = node.BackendBbolt case "etcd": - dbBackend = lntest.BackendEtcd + dbBackend = node.BackendEtcd case "postgres": - dbBackend = lntest.BackendPostgres + dbBackend = node.BackendPostgres case "sqlite": - dbBackend = lntest.BackendSqlite + dbBackend = node.BackendSqlite default: require.Fail(t, "unknown db backend") diff --git a/lntest/itest/assertions.go b/lntest/itest/assertions.go deleted file mode 100644 index a917c2c1a..000000000 --- a/lntest/itest/assertions.go +++ /dev/null @@ -1,1357 +0,0 @@ -package itest - -import ( - "context" - "encoding/hex" - "fmt" - "math" - "sync/atomic" - "testing" - "time" - - "github.com/btcsuite/btcd/btcutil" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/rpcclient" - "github.com/btcsuite/btcd/wire" - "github.com/go-errors/errors" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/lnrpc" - "github.com/lightningnetwork/lnd/lnrpc/routerrpc" - "github.com/lightningnetwork/lnd/lntest" - "github.com/lightningnetwork/lnd/lntest/wait" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" -) - -// openChannelStream blocks until an OpenChannel request for a channel funding -// by alice succeeds. If it does, a stream client is returned to receive events -// about the opening channel. -func openChannelStream(t *harnessTest, net *lntest.NetworkHarness, - alice, bob *lntest.HarnessNode, - p lntest.OpenChannelParams) lnrpc.Lightning_OpenChannelClient { - - t.t.Helper() - - // Wait until we are able to fund a channel successfully. This wait - // prevents us from erroring out when trying to create a channel while - // the node is starting up. - var chanOpenUpdate lnrpc.Lightning_OpenChannelClient - err := wait.NoError(func() error { - var err error - chanOpenUpdate, err = net.OpenChannel(alice, bob, p) - return err - }, defaultTimeout) - require.NoError(t.t, err, "unable to open channel") - - return chanOpenUpdate -} - -// openChannelAndAssert attempts to open a channel with the specified -// parameters extended from Alice to Bob. Additionally, two items are asserted -// after the channel is considered open: the funding transaction should be -// found within a block, and that Alice can report the status of the new -// channel. -func openChannelAndAssert(t *harnessTest, net *lntest.NetworkHarness, - alice, bob *lntest.HarnessNode, - p lntest.OpenChannelParams) *lnrpc.ChannelPoint { - - t.t.Helper() - - chanOpenUpdate := openChannelStream(t, net, alice, bob, p) - - // Mine 6 blocks, then wait for Alice's node to notify us that the - // channel has been opened. The funding transaction should be found - // within the first newly mined block. We mine 6 blocks so that in the - // case that the channel is public, it is announced to the network. - block := mineBlocks(t, net, 6, 1)[0] - - fundingChanPoint, err := net.WaitForChannelOpen(chanOpenUpdate) - require.NoError(t.t, err, "error while waiting for channel open") - - fundingTxID, err := lnrpc.GetChanPointFundingTxid(fundingChanPoint) - require.NoError(t.t, err, "unable to get txid") - - assertTxInBlock(t, block, fundingTxID) - - // The channel should be listed in the peer information returned by - // both peers. - chanPoint := wire.OutPoint{ - Hash: *fundingTxID, - Index: fundingChanPoint.OutputIndex, - } - require.NoError( - t.t, net.AssertChannelExists(alice, &chanPoint), - "unable to assert channel existence", - ) - require.NoError( - t.t, net.AssertChannelExists(bob, &chanPoint), - "unable to assert channel existence", - ) - - // They should also notice this channel from topology subscription. - err = alice.WaitForNetworkChannelOpen(fundingChanPoint) - require.NoError(t.t, err) - - err = bob.WaitForNetworkChannelOpen(fundingChanPoint) - require.NoError(t.t, err) - - return fundingChanPoint -} - -func waitForGraphSync(t *harnessTest, node *lntest.HarnessNode) { - t.t.Helper() - - err := wait.Predicate(func() bool { - ctxb := context.Background() - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - resp, err := node.GetInfo(ctxt, &lnrpc.GetInfoRequest{}) - require.NoError(t.t, err) - - return resp.SyncedToGraph - }, defaultTimeout) - require.NoError(t.t, err) -} - -// closeChannelAndAssert attempts to close a channel identified by the passed -// channel point owned by the passed Lightning node. A fully blocking channel -// closure is attempted, therefore the passed context should be a child derived -// via timeout from a base parent. Additionally, once the channel has been -// detected as closed, an assertion checks that the transaction is found within -// a block. Finally, this assertion verifies that the node always sends out a -// disable update when closing the channel if the channel was previously -// enabled. -// -// NOTE: This method assumes that the provided funding point is confirmed -// on-chain AND that the edge exists in the node's channel graph. If the funding -// transactions was reorged out at some point, use closeReorgedChannelAndAssert. -func closeChannelAndAssert(t *harnessTest, net *lntest.NetworkHarness, - node *lntest.HarnessNode, fundingChanPoint *lnrpc.ChannelPoint, - force bool) *chainhash.Hash { - - return closeChannelAndAssertType( - t, net, node, fundingChanPoint, false, force, - ) -} - -func closeChannelAndAssertType(t *harnessTest, - net *lntest.NetworkHarness, node *lntest.HarnessNode, - fundingChanPoint *lnrpc.ChannelPoint, - anchors, force bool) *chainhash.Hash { - - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, channelCloseTimeout) - defer cancel() - - // Fetch the current channel policy. If the channel is currently - // enabled, we will register for graph notifications before closing to - // assert that the node sends out a disabling update as a result of the - // channel being closed. - curPolicy := getChannelPolicies( - t, node, node.PubKeyStr, fundingChanPoint, - )[0] - expectDisable := !curPolicy.Disabled - - closeUpdates, _, err := net.CloseChannel(node, fundingChanPoint, force) - require.NoError(t.t, err, "unable to close channel") - - // If the channel policy was enabled prior to the closure, wait until we - // received the disabled update. - if expectDisable { - curPolicy.Disabled = true - assertChannelPolicyUpdate( - t.t, node, node.PubKeyStr, - curPolicy, fundingChanPoint, false, - ) - } - - return assertChannelClosed( - ctxt, t, net, node, fundingChanPoint, anchors, closeUpdates, - ) -} - -// closeReorgedChannelAndAssert attempts to close a channel identified by the -// passed channel point owned by the passed Lightning node. A fully blocking -// channel closure is attempted, therefore the passed context should be a child -// derived via timeout from a base parent. Additionally, once the channel has -// been detected as closed, an assertion checks that the transaction is found -// within a block. -// -// NOTE: This method does not verify that the node sends a disable update for -// the closed channel. -func closeReorgedChannelAndAssert(t *harnessTest, - net *lntest.NetworkHarness, node *lntest.HarnessNode, - fundingChanPoint *lnrpc.ChannelPoint, force bool) *chainhash.Hash { - - ctxb := context.Background() - ctx, cancel := context.WithTimeout(ctxb, channelCloseTimeout) - defer cancel() - - closeUpdates, _, err := net.CloseChannel(node, fundingChanPoint, force) - require.NoError(t.t, err, "unable to close channel") - - return assertChannelClosed( - ctx, t, net, node, fundingChanPoint, false, closeUpdates, - ) -} - -// assertChannelClosed asserts that the channel is properly cleaned up after -// initiating a cooperative or local close. -func assertChannelClosed(ctx context.Context, t *harnessTest, - net *lntest.NetworkHarness, node *lntest.HarnessNode, - fundingChanPoint *lnrpc.ChannelPoint, anchors bool, - closeUpdates lnrpc.Lightning_CloseChannelClient) *chainhash.Hash { - - txid, err := lnrpc.GetChanPointFundingTxid(fundingChanPoint) - require.NoError(t.t, err, "unable to get txid") - chanPointStr := fmt.Sprintf("%v:%v", txid, fundingChanPoint.OutputIndex) - - // If the channel appears in list channels, ensure that its state - // contains ChanStatusCoopBroadcasted. - listChansRequest := &lnrpc.ListChannelsRequest{} - listChansResp, err := node.ListChannels(ctx, listChansRequest) - require.NoError(t.t, err, "unable to query for list channels") - - for _, channel := range listChansResp.Channels { - // Skip other channels. - if channel.ChannelPoint != chanPointStr { - continue - } - - // Assert that the channel is in coop broadcasted. - require.Contains( - t.t, channel.ChanStatusFlags, - channeldb.ChanStatusCoopBroadcasted.String(), - "channel not coop broadcasted", - ) - } - - // At this point, the channel should now be marked as being in the - // state of "waiting close". - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - pendingChanResp, err := node.PendingChannels(ctx, pendingChansRequest) - require.NoError(t.t, err, "unable to query for pending channels") - - var found bool - for _, pendingClose := range pendingChanResp.WaitingCloseChannels { - if pendingClose.Channel.ChannelPoint == chanPointStr { - found = true - break - } - } - require.True(t.t, found, "channel not marked as waiting close") - - // We'll now, generate a single block, wait for the final close status - // update, then ensure that the closing transaction was included in the - // block. If there are anchors, we also expect an anchor sweep. - expectedTxes := 1 - if anchors { - expectedTxes = 2 - } - - block := mineBlocks(t, net, 1, expectedTxes)[0] - - closingTxid, err := net.WaitForChannelClose(closeUpdates) - require.NoError(t.t, err, "error while waiting for channel close") - - assertTxInBlock(t, block, closingTxid) - - // Finally, the transaction should no longer be in the waiting close - // state as we've just mined a block that should include the closing - // transaction. - err = wait.Predicate(func() bool { - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - pendingChanResp, err := node.PendingChannels( - ctx, pendingChansRequest, - ) - if err != nil { - return false - } - - for _, pendingClose := range pendingChanResp.WaitingCloseChannels { - if pendingClose.Channel.ChannelPoint == chanPointStr { - return false - } - } - - return true - }, defaultTimeout) - require.NoError( - t.t, err, "closing transaction not marked as fully closed", - ) - - return closingTxid -} - -// findForceClosedChannel searches a pending channel response for a particular -// channel, returning the force closed channel upon success. -func findForceClosedChannel(pendingChanResp *lnrpc.PendingChannelsResponse, - op fmt.Stringer) (*lnrpc.PendingChannelsResponse_ForceClosedChannel, - error) { - - for _, forceClose := range pendingChanResp.PendingForceClosingChannels { - if forceClose.Channel.ChannelPoint == op.String() { - return forceClose, nil - } - } - - return nil, errors.New("channel not marked as force closed") -} - -// findWaitingCloseChannel searches a pending channel response for a particular -// channel, returning the waiting close channel upon success. -func findWaitingCloseChannel(pendingChanResp *lnrpc.PendingChannelsResponse, - op fmt.Stringer) (*lnrpc.PendingChannelsResponse_WaitingCloseChannel, - error) { - - for _, waitingClose := range pendingChanResp.WaitingCloseChannels { - if waitingClose.Channel.ChannelPoint == op.String() { - return waitingClose, nil - } - } - - return nil, errors.New("channel not marked as waiting close") -} - -// waitForChannelPendingForceClose waits for the node to report that the -// channel is pending force close, and that the UTXO nursery is aware of it. -func waitForChannelPendingForceClose(node *lntest.HarnessNode, - fundingChanPoint *lnrpc.ChannelPoint) error { - - ctxb := context.Background() - ctx, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - txid, err := lnrpc.GetChanPointFundingTxid(fundingChanPoint) - if err != nil { - return err - } - - op := wire.OutPoint{ - Hash: *txid, - Index: fundingChanPoint.OutputIndex, - } - - return wait.NoError(func() error { - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - pendingChanResp, err := node.PendingChannels( - ctx, pendingChansRequest, - ) - if err != nil { - return fmt.Errorf("unable to get pending channels: %v", - err) - } - - forceClose, err := findForceClosedChannel(pendingChanResp, &op) - if err != nil { - return err - } - - // We must wait until the UTXO nursery has received the channel - // and is aware of its maturity height. - if forceClose.MaturityHeight == 0 { - return fmt.Errorf("channel had maturity height of 0") - } - - return nil - }, defaultTimeout) -} - -// lnrpcForceCloseChannel is a short type alias for a ridiculously long type -// name in the lnrpc package. -type lnrpcForceCloseChannel = lnrpc.PendingChannelsResponse_ForceClosedChannel - -// waitForNumChannelPendingForceClose waits for the node to report a certain -// number of channels in state pending force close. -func waitForNumChannelPendingForceClose(node *lntest.HarnessNode, - expectedNum int, - perChanCheck func(channel *lnrpcForceCloseChannel) error) error { - - ctxb := context.Background() - ctx, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - return wait.NoError(func() error { - resp, err := node.PendingChannels( - ctx, &lnrpc.PendingChannelsRequest{}, - ) - if err != nil { - return fmt.Errorf("unable to get pending channels: %v", - err) - } - - forceCloseChans := resp.PendingForceClosingChannels - if len(forceCloseChans) != expectedNum { - return fmt.Errorf("%v should have %d pending "+ - "force close channels but has %d", - node.Cfg.Name, expectedNum, - len(forceCloseChans)) - } - - if perChanCheck != nil { - for _, forceCloseChan := range forceCloseChans { - err := perChanCheck(forceCloseChan) - if err != nil { - return err - } - } - } - - return nil - }, defaultTimeout) -} - -// cleanupForceClose mines a force close commitment found in the mempool and -// the following sweep transaction from the force closing node. -func cleanupForceClose(t *harnessTest, net *lntest.NetworkHarness, - node *lntest.HarnessNode, chanPoint *lnrpc.ChannelPoint) { - - // Wait for the channel to be marked pending force close. - err := waitForChannelPendingForceClose(node, chanPoint) - require.NoError(t.t, err, "channel not pending force close") - - // Mine enough blocks for the node to sweep its funds from the force - // closed channel. - // - // The commit sweep resolver is able to broadcast the sweep tx up to - // one block before the CSV elapses, so wait until defaulCSV-1. - _, err = net.Miner.Client.Generate(defaultCSV - 1) - require.NoError(t.t, err, "unable to generate blocks") - - // The node should now sweep the funds, clean up by mining the sweeping - // tx. - mineBlocks(t, net, 1, 1) -} - -// numOpenChannelsPending sends an RPC request to a node to get a count of the -// node's channels that are currently in a pending state (with a broadcast, but -// not confirmed funding transaction). -func numOpenChannelsPending(ctxt context.Context, - node *lntest.HarnessNode) (int, error) { - - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - resp, err := node.PendingChannels(ctxt, pendingChansRequest) - if err != nil { - return 0, err - } - return len(resp.PendingOpenChannels), nil -} - -// assertNumOpenChannelsPending asserts that a pair of nodes have the expected -// number of pending channels between them. -func assertNumOpenChannelsPending(t *harnessTest, - alice, bob *lntest.HarnessNode, expected int) { - - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - err := wait.NoError(func() error { - aliceNumChans, err := numOpenChannelsPending(ctxt, alice) - if err != nil { - return fmt.Errorf("error fetching alice's node (%v) "+ - "pending channels %v", alice.NodeID, err) - } - bobNumChans, err := numOpenChannelsPending(ctxt, bob) - if err != nil { - return fmt.Errorf("error fetching bob's node (%v) "+ - "pending channels %v", bob.NodeID, err) - } - - aliceStateCorrect := aliceNumChans == expected - if !aliceStateCorrect { - return fmt.Errorf("number of pending channels for "+ - "alice incorrect. expected %v, got %v", - expected, aliceNumChans) - } - - bobStateCorrect := bobNumChans == expected - if !bobStateCorrect { - return fmt.Errorf("number of pending channels for bob "+ - "incorrect. expected %v, got %v", expected, - bobNumChans) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) -} - -// checkPeerInPeersList returns true if Bob appears in Alice's peer list. -func checkPeerInPeersList(ctx context.Context, alice, - bob *lntest.HarnessNode) (bool, error) { - - peers, err := alice.ListPeers(ctx, &lnrpc.ListPeersRequest{}) - if err != nil { - return false, fmt.Errorf( - "error listing %s's node (%v) peers: %v", - alice.Name(), alice.NodeID, err, - ) - } - - for _, peer := range peers.Peers { - if peer.PubKey == bob.PubKeyStr { - return true, nil - } - } - - return false, nil -} - -// assertConnected asserts that two peers are connected. -func assertConnected(t *harnessTest, alice, bob *lntest.HarnessNode) { - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - err := wait.NoError(func() error { - bobIsAlicePeer, err := checkPeerInPeersList(ctxt, alice, bob) - if err != nil { - return err - } - - if !bobIsAlicePeer { - return fmt.Errorf( - "expected %s and %s to be connected "+ - "but %s is not in %s's peer list", - alice.Name(), bob.Name(), - bob.Name(), alice.Name(), - ) - } - - aliceIsBobPeer, err := checkPeerInPeersList(ctxt, bob, alice) - if err != nil { - return err - } - - if !aliceIsBobPeer { - return fmt.Errorf( - "expected %s and %s to be connected "+ - "but %s is not in %s's peer list", - alice.Name(), bob.Name(), - alice.Name(), bob.Name(), - ) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) -} - -// assertNotConnected asserts that two peers are not connected. -func assertNotConnected(t *harnessTest, alice, bob *lntest.HarnessNode) { - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - err := wait.NoError(func() error { - bobIsAlicePeer, err := checkPeerInPeersList(ctxt, alice, bob) - if err != nil { - return err - } - - if bobIsAlicePeer { - return fmt.Errorf( - "expected %s and %s not to be "+ - "connected but %s is in %s's "+ - "peer list", - alice.Name(), bob.Name(), - bob.Name(), alice.Name(), - ) - } - - aliceIsBobPeer, err := checkPeerInPeersList(ctxt, bob, alice) - if err != nil { - return err - } - - if aliceIsBobPeer { - return fmt.Errorf( - "expected %s and %s not to be "+ - "connected but %s is in %s's "+ - "peer list", - alice.Name(), bob.Name(), - alice.Name(), bob.Name(), - ) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) -} - -// shutdownAndAssert shuts down the given node and asserts that no errors -// occur. -func shutdownAndAssert(net *lntest.NetworkHarness, t *harnessTest, - node *lntest.HarnessNode) { - - // The process may not be in a state to always shutdown immediately, so - // we'll retry up to a hard limit to ensure we eventually shutdown. - err := wait.NoError(func() error { - return net.ShutdownNode(node) - }, defaultTimeout) - require.NoErrorf(t.t, err, "unable to shutdown %v", node.Name()) -} - -// assertChannelBalanceResp makes a ChannelBalance request and checks the -// returned response matches the expected. -func assertChannelBalanceResp(t *harnessTest, node *lntest.HarnessNode, - expected *lnrpc.ChannelBalanceResponse) { - - resp := getChannelBalance(t, node) - require.True(t.t, proto.Equal(expected, resp), "balance is incorrect") -} - -// getChannelBalance gets the channel balance. -func getChannelBalance(t *harnessTest, - node *lntest.HarnessNode) *lnrpc.ChannelBalanceResponse { - - t.t.Helper() - - ctxt, _ := context.WithTimeout(context.Background(), defaultTimeout) - req := &lnrpc.ChannelBalanceRequest{} - resp, err := node.ChannelBalance(ctxt, req) - - require.NoError(t.t, err, "unable to get node's balance") - return resp -} - -// txStr returns the string representation of the channel's funding transaction. -func txStr(chanPoint *lnrpc.ChannelPoint) string { - fundingTxID, err := lnrpc.GetChanPointFundingTxid(chanPoint) - if err != nil { - return "" - } - cp := wire.OutPoint{ - Hash: *fundingTxID, - Index: chanPoint.OutputIndex, - } - return cp.String() -} - -// getChannelPolicies queries the channel graph and retrieves the current edge -// policies for the provided channel points. -func getChannelPolicies(t *harnessTest, node *lntest.HarnessNode, - advertisingNode string, - chanPoints ...*lnrpc.ChannelPoint) []*lnrpc.RoutingPolicy { - - ctxb := context.Background() - - descReq := &lnrpc.ChannelGraphRequest{ - IncludeUnannounced: true, - } - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - chanGraph, err := node.DescribeGraph(ctxt, descReq) - require.NoError(t.t, err, "unable to query for alice's graph") - - var policies []*lnrpc.RoutingPolicy - err = wait.NoError(func() error { - out: - for _, chanPoint := range chanPoints { - for _, e := range chanGraph.Edges { - if e.ChanPoint != txStr(chanPoint) { - continue - } - - if e.Node1Pub == advertisingNode { - policies = append(policies, - e.Node1Policy) - } else { - policies = append(policies, - e.Node2Policy) - } - - continue out - } - - // If we've iterated over all the known edges and we weren't - // able to find this specific one, then we'll fail. - return fmt.Errorf("did not find edge %v", txStr(chanPoint)) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) - - return policies -} - -// assertChannelPolicy asserts that the passed node's known channel policy for -// the passed chanPoint is consistent with the expected policy values. -func assertChannelPolicy(t *harnessTest, node *lntest.HarnessNode, - advertisingNode string, expectedPolicy *lnrpc.RoutingPolicy, - chanPoints ...*lnrpc.ChannelPoint) { - - policies := getChannelPolicies(t, node, advertisingNode, chanPoints...) - for _, policy := range policies { - err := lntest.CheckChannelPolicy(policy, expectedPolicy) - if err != nil { - t.Fatalf(fmt.Sprintf("%v: %s", err.Error(), node)) - } - } -} - -func checkCommitmentMaturity( - forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, - maturityHeight uint32, blocksTilMaturity int32) error { - - if forceClose.MaturityHeight != maturityHeight { - return fmt.Errorf("expected commitment maturity height to be "+ - "%d, found %d instead", maturityHeight, - forceClose.MaturityHeight) - } - if forceClose.BlocksTilMaturity != blocksTilMaturity { - return fmt.Errorf("expected commitment blocks til maturity to "+ - "be %d, found %d instead", blocksTilMaturity, - forceClose.BlocksTilMaturity) - } - - return nil -} - -// checkForceClosedChannelNumHtlcs verifies that a force closed channel has the -// proper number of htlcs. -func checkPendingChannelNumHtlcs( - forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, - expectedNumHtlcs int) error { - - if len(forceClose.PendingHtlcs) != expectedNumHtlcs { - return fmt.Errorf("expected force closed channel to have %d "+ - "pending htlcs, found %d instead", expectedNumHtlcs, - len(forceClose.PendingHtlcs)) - } - - return nil -} - -// checkNumForceClosedChannels checks that a pending channel response has the -// expected number of force closed channels. -func checkNumForceClosedChannels(pendingChanResp *lnrpc.PendingChannelsResponse, - expectedNumChans int) error { - - if len(pendingChanResp.PendingForceClosingChannels) != expectedNumChans { - return fmt.Errorf("expected to find %d force closed channels, "+ - "got %d", expectedNumChans, - len(pendingChanResp.PendingForceClosingChannels)) - } - - return nil -} - -// checkNumWaitingCloseChannels checks that a pending channel response has the -// expected number of channels waiting for closing tx to confirm. -func checkNumWaitingCloseChannels(pendingChanResp *lnrpc.PendingChannelsResponse, - expectedNumChans int) error { - - if len(pendingChanResp.WaitingCloseChannels) != expectedNumChans { - return fmt.Errorf("expected to find %d channels waiting "+ - "closure, got %d", expectedNumChans, - len(pendingChanResp.WaitingCloseChannels)) - } - - return nil -} - -// checkPendingHtlcStageAndMaturity uniformly tests all pending htlc's belonging -// to a force closed channel, testing for the expected stage number, blocks till -// maturity, and the maturity height. -func checkPendingHtlcStageAndMaturity( - forceClose *lnrpc.PendingChannelsResponse_ForceClosedChannel, - stage, maturityHeight uint32, blocksTillMaturity int32) error { - - for _, pendingHtlc := range forceClose.PendingHtlcs { - if pendingHtlc.Stage != stage { - return fmt.Errorf("expected pending htlc to be stage "+ - "%d, found %d", stage, pendingHtlc.Stage) - } - if pendingHtlc.MaturityHeight != maturityHeight { - return fmt.Errorf("expected pending htlc maturity "+ - "height to be %d, instead has %d", - maturityHeight, pendingHtlc.MaturityHeight) - } - if pendingHtlc.BlocksTilMaturity != blocksTillMaturity { - return fmt.Errorf("expected pending htlc blocks til "+ - "maturity to be %d, instead has %d", - blocksTillMaturity, - pendingHtlc.BlocksTilMaturity) - } - } - - return nil -} - -// assertAmountSent generates a closure which queries listchannels for sndr and -// rcvr, and asserts that sndr sent amt satoshis, and that rcvr received amt -// satoshis. -// -// NOTE: This method assumes that each node only has one channel, and it is the -// channel used to send the payment. -func assertAmountSent(amt btcutil.Amount, sndr, rcvr *lntest.HarnessNode) func() error { - return func() error { - // Both channels should also have properly accounted from the - // amount that has been sent/received over the channel. - listReq := &lnrpc.ListChannelsRequest{} - ctxb := context.Background() - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - sndrListChannels, err := sndr.ListChannels(ctxt, listReq) - if err != nil { - return fmt.Errorf("unable to query for %s's channel "+ - "list: %v", sndr.Name(), err) - } - sndrSatoshisSent := sndrListChannels.Channels[0].TotalSatoshisSent - if sndrSatoshisSent != int64(amt) { - return fmt.Errorf("%s's satoshis sent is incorrect "+ - "got %v, expected %v", sndr.Name(), - sndrSatoshisSent, amt) - } - - ctxt, _ = context.WithTimeout(ctxb, defaultTimeout) - rcvrListChannels, err := rcvr.ListChannels(ctxt, listReq) - if err != nil { - return fmt.Errorf("unable to query for %s's channel "+ - "list: %v", rcvr.Name(), err) - } - rcvrSatoshisReceived := rcvrListChannels.Channels[0].TotalSatoshisReceived - if rcvrSatoshisReceived != int64(amt) { - return fmt.Errorf("%s's satoshis received is "+ - "incorrect got %v, expected %v", rcvr.Name(), - rcvrSatoshisReceived, amt) - } - - return nil - } -} - -// assertLastHTLCError checks that the last sent HTLC of the last payment sent -// by the given node failed with the expected failure code. -func assertLastHTLCError(t *harnessTest, node *lntest.HarnessNode, - code lnrpc.Failure_FailureCode) { - - req := &lnrpc.ListPaymentsRequest{ - IncludeIncomplete: true, - } - ctxt, _ := context.WithTimeout(context.Background(), defaultTimeout) - paymentsResp, err := node.ListPayments(ctxt, req) - require.NoError(t.t, err, "error when obtaining payments") - - payments := paymentsResp.Payments - require.NotZero(t.t, len(payments), "no payments found") - - payment := payments[len(payments)-1] - htlcs := payment.Htlcs - require.NotZero(t.t, len(htlcs), "no htlcs") - - htlc := htlcs[len(htlcs)-1] - require.NotNil(t.t, htlc.Failure, "expected failure") - - require.Equal(t.t, code, htlc.Failure.Code, "unexpected failure code") -} - -// assertAmountPaid checks that the ListChannels command of the provided -// node list the total amount sent and received as expected for the -// provided channel. -func assertAmountPaid(t *harnessTest, channelName string, - node *lntest.HarnessNode, chanPoint wire.OutPoint, amountSent, - amountReceived int64) { - - ctxb := context.Background() - - checkAmountPaid := func() error { - listReq := &lnrpc.ListChannelsRequest{} - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - resp, err := node.ListChannels(ctxt, listReq) - if err != nil { - return fmt.Errorf("unable to for node's "+ - "channels: %v", err) - } - for _, channel := range resp.Channels { - if channel.ChannelPoint != chanPoint.String() { - continue - } - - if channel.TotalSatoshisSent != amountSent { - return fmt.Errorf("%v: incorrect amount"+ - " sent: %v != %v", channelName, - channel.TotalSatoshisSent, - amountSent) - } - if channel.TotalSatoshisReceived != - amountReceived { - - return fmt.Errorf("%v: incorrect amount"+ - " received: %v != %v", - channelName, - channel.TotalSatoshisReceived, - amountReceived) - } - - return nil - } - return fmt.Errorf("channel not found") - } - - // As far as HTLC inclusion in commitment transaction might be - // postponed we will try to check the balance couple of times, - // and then if after some period of time we receive wrong - // balance return the error. - // TODO(roasbeef): remove sleep after invoice notification hooks - // are in place - var timeover uint32 - go func() { - <-time.After(defaultTimeout) - atomic.StoreUint32(&timeover, 1) - }() - - for { - isTimeover := atomic.LoadUint32(&timeover) == 1 - if err := checkAmountPaid(); err != nil { - require.Falsef( - t.t, isTimeover, - "Check amount Paid failed: %v", err, - ) - } else { - break - } - } -} - -// assertNumPendingChannels checks that a PendingChannels response from the -// node reports the expected number of pending channels. -func assertNumPendingChannels(t *harnessTest, node *lntest.HarnessNode, - expWaitingClose, expPendingForceClose int) { - - ctxb := context.Background() - - var predErr error - err := wait.Predicate(func() bool { - pendingChansRequest := &lnrpc.PendingChannelsRequest{} - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - pendingChanResp, err := node.PendingChannels(ctxt, - pendingChansRequest) - if err != nil { - predErr = fmt.Errorf("unable to query for pending "+ - "channels: %v", err) - return false - } - n := len(pendingChanResp.WaitingCloseChannels) - if n != expWaitingClose { - predErr = fmt.Errorf("expected to find %d channels "+ - "waiting close, found %d", expWaitingClose, n) - return false - } - n = len(pendingChanResp.PendingForceClosingChannels) - if n != expPendingForceClose { - predErr = fmt.Errorf("expected to find %d channel "+ - "pending force close, found %d", expPendingForceClose, n) - return false - } - return true - }, defaultTimeout) - require.NoErrorf(t.t, err, "got err: %v", predErr) -} - -// assertNodeNumChannels polls the provided node's list channels rpc until it -// reaches the desired number of total channels. -func assertNodeNumChannels(t *harnessTest, node *lntest.HarnessNode, - numChannels int) { - - ctxb := context.Background() - - // Poll node for its list of channels. - req := &lnrpc.ListChannelsRequest{} - - var predErr error - pred := func() bool { - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - chanInfo, err := node.ListChannels(ctxt, req) - if err != nil { - predErr = fmt.Errorf("unable to query for node's "+ - "channels: %v", err) - return false - } - - // Return true if the query returned the expected number of - // channels. - num := len(chanInfo.Channels) - if num != numChannels { - predErr = fmt.Errorf("expected %v channels, got %v", - numChannels, num) - return false - } - return true - } - - require.NoErrorf( - t.t, wait.Predicate(pred, defaultTimeout), - "node has incorrect number of channels: %v", predErr, - ) -} - -// assertActiveHtlcs makes sure all the passed nodes have the _exact_ HTLCs -// matching payHashes on _all_ their channels. -func assertActiveHtlcs(nodes []*lntest.HarnessNode, payHashes ...[]byte) error { - ctxb := context.Background() - - req := &lnrpc.ListChannelsRequest{} - for _, node := range nodes { - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - nodeChans, err := node.ListChannels(ctxt, req) - if err != nil { - return fmt.Errorf("unable to get node chans: %v", err) - } - - for _, channel := range nodeChans.Channels { - // Record all payment hashes active for this channel. - htlcHashes := make(map[string]struct{}) - for _, htlc := range channel.PendingHtlcs { - h := hex.EncodeToString(htlc.HashLock) - _, ok := htlcHashes[h] - if ok { - return fmt.Errorf("duplicate HashLock") - } - htlcHashes[h] = struct{}{} - } - - // Channel should have exactly the payHashes active. - if len(payHashes) != len(htlcHashes) { - return fmt.Errorf("node [%s:%x] had %v "+ - "htlcs active, expected %v", - node.Cfg.Name, node.PubKey[:], - len(htlcHashes), len(payHashes)) - } - - // Make sure all the payHashes are active. - for _, payHash := range payHashes { - h := hex.EncodeToString(payHash) - if _, ok := htlcHashes[h]; ok { - continue - } - return fmt.Errorf("node [%s:%x] didn't have: "+ - "the payHash %v active", node.Cfg.Name, - node.PubKey[:], h) - } - } - } - - return nil -} - -func assertNumActiveHtlcsChanPoint(node *lntest.HarnessNode, - chanPoint wire.OutPoint, numHtlcs int) error { - - ctxb := context.Background() - - req := &lnrpc.ListChannelsRequest{} - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - nodeChans, err := node.ListChannels(ctxt, req) - if err != nil { - return err - } - - for _, channel := range nodeChans.Channels { - if channel.ChannelPoint != chanPoint.String() { - continue - } - - if len(channel.PendingHtlcs) != numHtlcs { - return fmt.Errorf("expected %v active HTLCs, got %v", - numHtlcs, len(channel.PendingHtlcs)) - } - return nil - } - - return fmt.Errorf("channel point %v not found", chanPoint) -} - -func assertNumActiveHtlcs(nodes []*lntest.HarnessNode, numHtlcs int) error { - ctxb := context.Background() - - req := &lnrpc.ListChannelsRequest{} - for _, node := range nodes { - ctxt, _ := context.WithTimeout(ctxb, defaultTimeout) - nodeChans, err := node.ListChannels(ctxt, req) - if err != nil { - return err - } - - for _, channel := range nodeChans.Channels { - if len(channel.PendingHtlcs) != numHtlcs { - return fmt.Errorf("expected %v HTLCs, got %v", - numHtlcs, len(channel.PendingHtlcs)) - } - } - } - - return nil -} - -func assertSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client, - timeout time.Duration, inputs ...wire.OutPoint) chainhash.Hash { - - tx := getSpendingTxInMempool(t, miner, timeout, inputs...) - return tx.TxHash() -} - -// getSpendingTxInMempool waits for a transaction spending the given outpoint to -// appear in the mempool and returns that tx in full. -func getSpendingTxInMempool(t *harnessTest, miner *rpcclient.Client, - timeout time.Duration, inputs ...wire.OutPoint) *wire.MsgTx { - - inputSet := make(map[wire.OutPoint]struct{}, len(inputs)) - breakTimeout := time.After(timeout) - ticker := time.NewTicker(50 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-breakTimeout: - t.Fatalf("didn't find tx in mempool") - case <-ticker.C: - mempool, err := miner.GetRawMempool() - require.NoError(t.t, err, "unable to get mempool") - - if len(mempool) == 0 { - continue - } - - for _, txid := range mempool { - tx, err := miner.GetRawTransaction(txid) - require.NoError(t.t, err, "unable to fetch tx") - msgTx := tx.MsgTx() - - // Include the inputs again in case they were - // removed in a previous iteration. - for _, input := range inputs { - inputSet[input] = struct{}{} - } - - for _, txIn := range msgTx.TxIn { - input := txIn.PreviousOutPoint - delete(inputSet, input) - } - - if len(inputSet) > 0 { - // Missing input, check next transaction - // or try again. - continue - } - - // Transaction spends all expected inputs, - // return. - return msgTx - } - } - } -} - -// assertTxLabel is a helper function which finds a target tx in our set -// of transactions and checks that it has the desired label. -func assertTxLabel(t *harnessTest, node *lntest.HarnessNode, - targetTx, label string) { - - // List all transactions relevant to our wallet, and find the tx so that - // we can check the correct label has been set. - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - txResp, err := node.GetTransactions( - ctxt, &lnrpc.GetTransactionsRequest{}, - ) - require.NoError(t.t, err, "could not get transactions") - - // Find our transaction in the set of transactions returned and check - // its label. - for _, txn := range txResp.Transactions { - if txn.TxHash == targetTx { - require.Equal(t.t, label, txn.Label, "labels not match") - } - } -} - -// sendAndAssertSuccess sends the given payment requests and asserts that the -// payment completes successfully. -func sendAndAssertSuccess(t *harnessTest, node *lntest.HarnessNode, - req *routerrpc.SendPaymentRequest) *lnrpc.Payment { - - ctxb := context.Background() - ctx, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - - var result *lnrpc.Payment - err := wait.NoError(func() error { - stream, err := node.RouterClient.SendPaymentV2(ctx, req) - if err != nil { - return fmt.Errorf("unable to send payment: %v", err) - } - - result, err = getPaymentResult(stream) - if err != nil { - return fmt.Errorf("unable to get payment result: %v", - err) - } - - if result.Status != lnrpc.Payment_SUCCEEDED { - return fmt.Errorf("payment failed: %v", result.Status) - } - - return nil - }, defaultTimeout) - require.NoError(t.t, err) - - return result -} - -// sendAndAssertFailure sends the given payment requests and asserts that the -// payment fails with the expected reason. -func sendAndAssertFailure(t *harnessTest, node *lntest.HarnessNode, - req *routerrpc.SendPaymentRequest, - failureReason lnrpc.PaymentFailureReason) *lnrpc.Payment { - - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - - stream, err := node.RouterClient.SendPaymentV2(ctx, req) - require.NoError(t.t, err, "unable to send payment") - - result, err := getPaymentResult(stream) - require.NoError(t.t, err, "unable to get payment result") - - require.Equal( - t.t, lnrpc.Payment_FAILED, result.Status, - "payment was expected to fail, but succeeded", - ) - - require.Equal( - t.t, failureReason, result.FailureReason, - "payment failureReason not matched", - ) - - return result -} - -// getPaymentResult reads a final result from the stream and returns it. -func getPaymentResult(stream routerrpc.Router_SendPaymentV2Client) ( - *lnrpc.Payment, error) { - - for { - payment, err := stream.Recv() - if err != nil { - return nil, err - } - - if payment.Status != lnrpc.Payment_IN_FLIGHT { - return payment, nil - } - } -} - -// assertNumUTXOs waits for the given number of UTXOs to be available or fails -// if that isn't the case before the default timeout. -func assertNumUTXOs(t *testing.T, node *lntest.HarnessNode, expectedUtxos int) { - ctxb := context.Background() - ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout) - defer cancel() - err := wait.NoError(func() error { - resp, err := node.ListUnspent( // nolint:staticcheck - ctxt, &lnrpc.ListUnspentRequest{ - MinConfs: 1, - MaxConfs: math.MaxInt32, - }, - ) - if err != nil { - return fmt.Errorf("error listing unspent: %v", err) - } - - if len(resp.Utxos) != expectedUtxos { - return fmt.Errorf("not enough UTXOs, got %d wanted %d", - len(resp.Utxos), expectedUtxos) - } - - return nil - }, defaultTimeout) - require.NoError(t, err, "wait for listunspent") -} - -// assertChannelPolicyUpdate checks that the required policy update has -// happened on the given node. -func assertChannelPolicyUpdate(t *testing.T, node *lntest.HarnessNode, - advertisingNode string, policy *lnrpc.RoutingPolicy, - chanPoint *lnrpc.ChannelPoint, includeUnannounced bool) { - - require.NoError( - t, node.WaitForChannelPolicyUpdate( - advertisingNode, policy, - chanPoint, includeUnannounced, - ), "error while waiting for channel update", - ) -} - -func transactionInWallet(node *lntest.HarnessNode, txid chainhash.Hash) bool { - txStr := txid.String() - - txResp, err := node.GetTransactions( - context.Background(), &lnrpc.GetTransactionsRequest{}, - ) - if err != nil { - return false - } - - for _, txn := range txResp.Transactions { - if txn.TxHash == txStr { - return true - } - } - - return false -} - -func assertTransactionInWallet(t *testing.T, node *lntest.HarnessNode, txID chainhash.Hash) { - t.Helper() - - err := wait.Predicate(func() bool { - return transactionInWallet(node, txID) - }, defaultTimeout) - require.NoError( - t, err, fmt.Sprintf("transaction %v not found in wallet", txID), - ) -} - -func assertTransactionNotInWallet(t *testing.T, node *lntest.HarnessNode, - txID chainhash.Hash) { - - t.Helper() - - err := wait.Predicate(func() bool { - return !transactionInWallet(node, txID) - }, defaultTimeout) - require.NoError( - t, err, fmt.Sprintf("transaction %v found in wallet", txID), - ) -} - -// assertNodeAnnouncement compares that two node announcements match. -func assertNodeAnnouncement(t *harnessTest, n1, n2 *lnrpc.NodeUpdate) { - // Alias should match. - require.Equal(t.t, n1.Alias, n2.Alias, "alias don't match") - - // Color should match. - require.Equal(t.t, n1.Color, n2.Color, "color don't match") - - // NodeAddresses should match. - require.Equal( - t.t, len(n1.NodeAddresses), len(n2.NodeAddresses), - "node addresses don't match", - ) - - addrs := make(map[string]struct{}, len(n1.NodeAddresses)) - for _, nodeAddr := range n1.NodeAddresses { - addrs[nodeAddr.Addr] = struct{}{} - } - - for _, nodeAddr := range n2.NodeAddresses { - if _, ok := addrs[nodeAddr.Addr]; !ok { - t.Fatalf("address %v not found in node announcement", - nodeAddr.Addr) - } - } -} diff --git a/lntest/itest/list_off_test.go b/lntest/itest/list_off_test.go deleted file mode 100644 index e34443092..000000000 --- a/lntest/itest/list_off_test.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !rpctest -// +build !rpctest - -package itest - -import "github.com/lightningnetwork/lnd/lntemp" - -// TODO(yy): remove the temp. -var allTestCasesTemp = []*lntemp.TestCase{} diff --git a/lntest/itest/lnd_test.go b/lntest/itest/lnd_test.go deleted file mode 100644 index 1f0dbd4cb..000000000 --- a/lntest/itest/lnd_test.go +++ /dev/null @@ -1,241 +0,0 @@ -package itest - -import ( - "flag" - "fmt" - "os" - "strings" - "testing" - "time" - - "github.com/btcsuite/btcd/integration/rpctest" - "github.com/lightningnetwork/lnd/lntest" - "github.com/stretchr/testify/require" -) - -var ( - // tempTest is a flag used to mark whether we should run the old or the - // new test cases. Used here so we can transit smoothly during our new - // itest construction. - // - // TODO(yy): remove temp flag. - tempTest = flag.Bool("temptest", false, "run the new tests(temp)") -) - -// getTestCaseSplitTranche returns the sub slice of the test cases that should -// be run as the current split tranche as well as the index and slice offset of -// the tranche. -func getTestCaseSplitTrancheOld() ([]*testCase, uint, uint) { - numTranches := defaultSplitTranches - if testCasesSplitTranches != nil { - numTranches = *testCasesSplitTranches - } - runTranche := defaultRunTranche - if testCasesRunTranche != nil { - runTranche = *testCasesRunTranche - } - - // There's a special flake-hunt mode where we run the same test multiple - // times in parallel. In that case the tranche index is equal to the - // thread ID, but we need to actually run all tests for the regex - // selection to work. - threadID := runTranche - if numTranches == 1 { - runTranche = 0 - } - - numCases := uint(len(allTestCases)) - testsPerTranche := numCases / numTranches - trancheOffset := runTranche * testsPerTranche - trancheEnd := trancheOffset + testsPerTranche - if trancheEnd > numCases || runTranche == numTranches-1 { - trancheEnd = numCases - } - - return allTestCases[trancheOffset:trancheEnd], threadID, trancheOffset -} - -// TestLightningNetworkDaemon performs a series of integration tests amongst a -// programmatically driven network of lnd nodes. -func TestLightningNetworkDaemon(t *testing.T) { - if *tempTest { - t.Skip("Running new tests, old tests are skipped") - } - - // If no tests are registered, then we can exit early. - if len(allTestCases) == 0 { - t.Skip("integration tests not selected with flag 'rpctest'") - } - - // Parse testing flags that influence our test execution. - logDir := lntest.GetLogDir() - require.NoError(t, os.MkdirAll(logDir, 0700)) - testCases, trancheIndex, trancheOffset := getTestCaseSplitTrancheOld() - lntest.ApplyPortOffset(uint32(trancheIndex) * 1000) - - // Before we start any node, we need to make sure that any btcd node - // that is started through the RPC harness uses a unique port as well to - // avoid any port collisions. - rpctest.ListenAddressGenerator = lntest.GenerateBtcdListenerAddresses - - // Declare the network harness here to gain access to its - // 'OnTxAccepted' call back. - var lndHarness *lntest.NetworkHarness - - // Create an instance of the btcd's rpctest.Harness that will act as - // the miner for all tests. This will be used to fund the wallets of - // the nodes within the test network and to drive blockchain related - // events within the network. Revert the default setting of accepting - // non-standard transactions on simnet to reject them. Transactions on - // the lightning network should always be standard to get better - // guarantees of getting included in to blocks. - // - // We will also connect it to our chain backend. - miner, err := lntest.NewMiner() - require.NoError(t, err, "failed to create new miner") - defer func() { - require.NoError(t, miner.Stop(), "failed to stop miner") - }() - - // Start a chain backend. - chainBackend, cleanUp, err := lntest.NewBackend( - miner.P2PAddress(), harnessNetParams, - ) - require.NoError(t, err, "new backend") - defer func() { - require.NoError(t, cleanUp(), "cleanup") - }() - - // Before we start anything, we want to overwrite some of the connection - // settings to make the tests more robust. We might need to restart the - // miner while there are already blocks present, which will take a bit - // longer than the 1 second the default settings amount to. Doubling - // both values will give us retries up to 4 seconds. - miner.MaxConnRetries = rpctest.DefaultMaxConnectionRetries * 2 - miner.ConnectionRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2 - - // Set up miner and connect chain backend to it. - require.NoError(t, miner.SetUp(true, 50)) - require.NoError(t, miner.Client.NotifyNewTransactions(false)) - require.NoError(t, chainBackend.ConnectMiner(), "connect miner") - - // Parse database backend - var dbBackend lntest.DatabaseBackend - switch *dbBackendFlag { - case "bbolt": - dbBackend = lntest.BackendBbolt - - case "etcd": - dbBackend = lntest.BackendEtcd - - case "postgres": - dbBackend = lntest.BackendPostgres - - case "sqlite": - dbBackend = lntest.BackendSqlite - - default: - require.Fail(t, "unknown db backend") - } - - // Now we can set up our test harness (LND instance), with the chain - // backend we just created. - ht := newHarnessTest(t, nil) - binary := ht.getLndBinary() - lndHarness, err = lntest.NewNetworkHarness( - miner, chainBackend, binary, dbBackend, - ) - if err != nil { - ht.Fatalf("unable to create lightning network harness: %v", err) - } - defer lndHarness.Stop() - - // Spawn a new goroutine to watch for any fatal errors that any of the - // running lnd processes encounter. If an error occurs, then the test - // case should naturally as a result and we log the server error here to - // help debug. - go func() { - for { - select { - case err, more := <-lndHarness.ProcessErrors(): - if !more { - return - } - ht.Logf("lnd finished with error (stderr):\n%v", - err) - } - } - }() - - // Next mine enough blocks in order for segwit and the CSV package - // soft-fork to activate on SimNet. - numBlocks := harnessNetParams.MinerConfirmationWindow * 2 - if _, err := miner.Client.Generate(numBlocks); err != nil { - ht.Fatalf("unable to generate blocks: %v", err) - } - - // With the btcd harness created, we can now complete the - // initialization of the network. args - list of lnd arguments, - // example: "--debuglevel=debug" - // TODO(roasbeef): create master balanced channel with all the monies? - aliceBobArgs := []string{ - "--default-remote-max-htlcs=483", - "--dust-threshold=5000000", - } - - // Run the subset of the test cases selected in this tranche. - for idx, testCase := range testCases { - testCase := testCase - name := fmt.Sprintf("tranche%02d/%02d-of-%d/%s/%s", - trancheIndex, trancheOffset+uint(idx)+1, - len(allTestCases), chainBackend.Name(), testCase.name) - - success := t.Run(name, func(t1 *testing.T) { - cleanTestCaseName := strings.ReplaceAll( - testCase.name, " ", "_", - ) - - err = lndHarness.SetUp( - t1, cleanTestCaseName, aliceBobArgs, - ) - require.NoError(t1, - err, "unable to set up test lightning network", - ) - defer func() { - require.NoError(t1, lndHarness.TearDown()) - }() - - lndHarness.EnsureConnected( - t1, lndHarness.Alice, lndHarness.Bob, - ) - - logLine := fmt.Sprintf( - "STARTING ============ %v ============\n", - testCase.name, - ) - - lndHarness.Alice.AddToLogf(logLine) - lndHarness.Bob.AddToLogf(logLine) - - // Start every test with the default static fee estimate. - lndHarness.SetFeeEstimate(12500) - - // Create a separate harness test for the testcase to - // avoid overwriting the external harness test that is - // tied to the parent test. - ht := newHarnessTest(t1, lndHarness) - ht.RunTestCase(testCase) - }) - - // Stop at the first failure. Mimic behavior of original test - // framework. - if !success { - // Log failure time to help relate the lnd logs to the - // failure. - t.Logf("Failure time: %v", time.Now().Format( - "2006-01-02 15:04:05.000", - )) - break - } - } -} diff --git a/lntest/itest/lnd_test_list_off_test.go b/lntest/itest/lnd_test_list_off_test.go deleted file mode 100644 index bf21584f3..000000000 --- a/lntest/itest/lnd_test_list_off_test.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build !rpctest -// +build !rpctest - -package itest - -var allTestCases = []*testCase{} diff --git a/lntest/itest/lnd_test_list_on_test.go b/lntest/itest/lnd_test_list_on_test.go deleted file mode 100644 index 0783688bd..000000000 --- a/lntest/itest/lnd_test_list_on_test.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build rpctest -// +build rpctest - -package itest - -var allTestCases = []*testCase{ - { - name: "async bidirectional payments", - test: testBidirectionalAsyncPayments, - }, -} diff --git a/lntest/itest/log_error_whitelist.txt b/lntest/itest/log_error_whitelist.txt deleted file mode 100644 index 389694dca..000000000 --- a/lntest/itest/log_error_whitelist.txt +++ /dev/null @@ -1,302 +0,0 @@ -