Compare commits

..

No commits in common. "master" and "v0.33.0-beta" have entirely different histories.

408 changed files with 3507 additions and 42512 deletions

View file

@ -1,3 +1,2 @@
#### Pull Request Checklist
- [ ] Add an entry to `docs/release-notes/release-notes-next.md`, or apply the
`no-changelog` label (required by CI)
- [ ] Update `release_notes.md` if your PR contains major features, breaking changes or bugfixes

View file

@ -5,6 +5,8 @@ on:
types: [created]
pull_request:
types: [labeled, synchronize]
pull_request_target:
types: [labeled, synchronize]
jobs:
claude-review:
@ -53,3 +55,75 @@ jobs:
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
claude-approve:
if: |
github.event_name == 'pull_request_target' &&
contains(github.event.pull_request.labels.*.name, 'low-risk') &&
(github.event.action == 'labeled' || github.event.action == 'synchronize')
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code Review and Approve
id: claude-approve
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
Review this pull request thoroughly, checking for:
- Code quality and best practices
- Potential bugs or issues
- Performance considerations
- Security concerns
- Test coverage
Use the repository's AGENTS.md for guidance on style and conventions.
Classify the PR's intrinsic risk severity using one of:
- critical: must block merge
- high: large blast radius or very sensitive code paths
- medium: meaningful production or maintainability risk
- low: constrained blast radius and straightforward rollback
Intrinsic risk is about the impact and sensitivity of changed code,
even when no concrete bug is found.
Any changes touching fund movement, signing/sweeping, swap state
transitions, security/authz/authn logic, secrets handling, or DB
schema/migrations are at least medium risk.
Also classify each finding using one of:
- critical
- high
- medium
- low
- nit
Decision rule:
- Approve ONLY if intrinsic PR risk is low AND highest finding
severity is low or nit.
- If intrinsic PR risk is medium/high/critical, DO NOT approve and
submit a changes-requested review.
- If any finding is medium/high/critical, DO NOT approve and submit
a changes-requested review.
- If uncertain, treat as medium risk.
If approving:
gh pr review ${{ github.event.pull_request.number }} --approve --body "Claude review: intrinsic PR risk low and findings low/nit; safe for low-risk path."
If NOT approving:
gh pr review ${{ github.event.pull_request.number }} --request-changes --body "Claude review: not eligible for low-risk auto-approval. Include intrinsic PR risk and findings with severities."
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*),Bash(gh pr review:*)"'

View file

@ -1,66 +0,0 @@
name: gateway
# Opt-in code-review bot. Triggered by a `/gateway <command>` comment on a PR
# (e.g. `/gateway review`); review/approve commands are gated to maintainers.
# Comment-commands only — no pull_request triggers — so fork PRs (which receive
# no secrets) never spawn failing runs. v0.5.0 added the
# pull_request_review_comment trigger: /gateway dismiss, promote, and explain
# now also work as replies on a finding's inline thread (finding id inferred
# from the thread when omitted). Also a comment event — same fork-PR safety
# profile as issue_comment.
#
# Thin shim: the public lightninglabs/gateway-action mints an App token and
# checks out the private gateway runtime at execution time. The runtime stays
# private; only this entry point is public.
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
permissions:
# The action mints an App installation token internally; the GITHUB_TOKEN
# handed to this shim is unused, so we minimise it.
contents: read
jobs:
review:
# issue_comment fires for all issues and every PR comment. Filter to PR
# comments that look like a /gateway command so unrelated comments don't
# spin up a no-op runner. `contains` (not `startsWith`) because the runtime
# accepts the command at column 0 of any line, including multi-line bodies.
if: >-
${{
(github.event_name == 'issue_comment'
&& github.event.issue.pull_request != null
&& contains(github.event.comment.body, '/gateway')) ||
(github.event_name == 'pull_request_review_comment'
&& contains(github.event.comment.body, '/gateway'))
}}
runs-on: ubuntu-latest
timeout-minutes: 15
env:
GATEWAY_REVIEW_MODE: multi
steps:
- uses: lightninglabs/gateway-action@334a8455ee316e40668ae3ac85249150c62704ec # v0.6.0
with:
# Pin the private runtime to an immutable commit (matches the action
# SHA-pin above) so runtime upgrades go through a loop PR, not a moved
# tag. Without this, runtime_ref defaults to the v0.6.0 tag.
runtime_ref: 75f6e67deac362bdcfc10d10629ddcf69c0e2615 # gateway v0.6.0
event_name: ${{ github.event_name }}
event_action: ${{ github.event.action }}
repo: ${{ github.repository }}
pr_number: ${{ github.event.issue.number || github.event.pull_request.number }}
actor: ${{ github.event.sender.login }}
comment_body: ${{ github.event.comment.body }}
comment_id: ${{ github.event.comment.id }}
comment_in_reply_to: ${{ github.event.comment.in_reply_to_id }}
# installation_id intentionally omitted: as of gateway v0.4.4 the
# runtime resolves the App installation covering this repo from
# app_id/private_key, so a hardcoded (and easily wrong-org) id is no
# longer needed.
app_id: ${{ secrets.GATEWAY_APP_ID }}
private_key: ${{ secrets.GATEWAY_PRIVATE_KEY }}
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

View file

@ -24,37 +24,6 @@ env:
LITD_ITEST_BRANCH: master
jobs:
########################
# commit message lint
########################
commit-message:
name: Commit Message
runs-on: ubuntu-latest
steps:
- name: git checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Lint commit messages
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BEFORE_SHA: ${{ github.event.before }}
GITHUB_SHA: ${{ github.sha }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
range="$BASE_SHA..$HEAD_SHA"
elif [ "$EVENT_NAME" = "push" ] &&
[ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
range="$BEFORE_SHA..$GITHUB_SHA"
else
range="$(git rev-parse HEAD^ 2>/dev/null)..HEAD"
fi
make commitmsg-lint range="$range"
########################
# RPC compile and check
########################
@ -155,9 +124,6 @@ jobs:
- name: check
run: make docs-check
- name: check generated FSM diagrams
run: make fsm-check
########################
# run unit-test sqlite3 race
########################

View file

@ -1,58 +0,0 @@
name: Release Notes
on:
pull_request:
branches:
- "*"
types:
- opened
- reopened
- synchronize
- labeled
- unlabeled
permissions:
contents: read
defaults:
run:
shell: bash
jobs:
release-notes:
name: Release Notes
runs-on: ubuntu-latest
steps:
- name: Skip release notes check
if: contains(github.event.pull_request.labels.*.name, 'no-changelog')
run: echo "Release notes check skipped by the no-changelog label."
- name: git checkout
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }}
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check for a release note
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }}
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
RELEASE_NOTES_FILE: docs/release-notes/release-notes-next.md
run: |
if git diff --unified=0 "$BASE_SHA...$HEAD_SHA" -- \
"$RELEASE_NOTES_FILE" |
awk '
/^@@/ { in_hunk = 1; next }
in_hunk && /^\+/ {
line = substr($0, 2)
if (line ~ /[^[:space:]]/) found = 1
}
END { exit found ? 0 : 1 }
'
then
exit 0
fi
echo "::error file=$RELEASE_NOTES_FILE::Add a release note or apply the no-changelog label."
exit 1

View file

@ -3,7 +3,7 @@ run:
go: "1.26"
# timeout for analysis
timeout: 6m
timeout: 4m
linters:
default: all
@ -55,16 +55,14 @@ linters:
- wsl_v5
- noinlineerr
settings:
staticcheck:
checks:
- all
- -QF*
- -ST*
gosec:
excludes:
- G402
- G306
- G115
staticcheck:
checks:
- -SA1019
tagliatelle:
case:
rules:

View file

@ -51,33 +51,3 @@ The project is a client daemon (`loopd`) that connects to a user's `lnd` node an
* **Cryptography:** Extensively uses Taproot and MuSig2 for efficiency, privacy, and complex spending conditions, especially in the `instantout` and `staticaddr` features.
* **Labeling (`labels/`):** A utility to create and validate labels for swaps, which helps distinguish between user-initiated and automated swaps (e.g., `[reserved]: autoloop-out`).
* **Assets (`assets/`):** Contains logic for interacting with `tapd` (Taproot Assets Protocol Daemon), allowing Loop to facilitate swaps involving assets other than Bitcoin.
**5. Minimum `lnd` Version (`loopd/run.go`):**
`loopd` enforces a minimum `lnd` version at startup through
`LoopMinRequiredLndVersion` in `loopd/run.go` (handed to `lndclient` as
`CheckVersion`). This is a hard gate: loopd refuses to start against an older
`lnd` node.
**Maintenance rule:** whenever you start using an `lnd` gRPC method or message
field that does not exist in older `lnd`, bump `LoopMinRequiredLndVersion` to the
`lnd` release that introduced that API, and record which API drove the bump in the
comment above the variable. Pin it to the *real* floor of the APIs the client uses
— do **not** just track the `go.mod` dependency. Historically this value only
tracked `go.mod` and drifted out of sync with the APIs actually called (it sat at
0.17.0 while the client already depended on 0.18.4 APIs). To find the introducing
release, grep the field/method across `lnd` version tags, e.g.
`git grep <field> <tag> -- <proto-file>`. As of the current floor (**v0.18.4-beta**)
the binding dependencies are the asset loop-out fields
`routerrpc.SendPaymentRequest.first_hop_custom_records` and
`lnrpc.Route.custom_channel_data` (lnd v0.18.4-beta), plus the sweep-batcher fee
floor `walletrpc.EstimateFeeResponse.min_relay_fee_sat_per_kw` (lnd v0.18.3-beta).
**6. Pull Request Release Notes:**
Every pull request must either add at least one non-empty line to
`docs/release-notes/release-notes-next.md` or carry the `no-changelog` label.
Before finishing any change intended for a pull request, add a concise entry
under the appropriate section (`New Features`, `Breaking Changes`, `Bug Fixes`,
or `Maintenance`). For a change that does not warrant a release note, ensure the
pull request uses the `no-changelog` label instead. This is enforced by CI.

View file

@ -15,15 +15,6 @@ GOBUILD := CGO_ENABLED=0 GO111MODULE=on go build -v
GOINSTALL := CGO_ENABLED=0 GO111MODULE=on go install -v
GOMOD := GO111MODULE=on go mod
# GitLab-backed module sources can reject CI runner traffic. Seed GitLab and
# modernc.org modules through the public Go proxy before the direct check.
MOD_CHECK_FILES := go.mod go.sum swapserverrpc/go.mod swapserverrpc/go.sum \
looprpc/go.mod looprpc/go.sum tools/go.mod tools/go.sum
MOD_CHECK_PREFETCH := $(shell awk '($$1 ~ /^(gitlab\.com\/|modernc\.org\/)/) { \
version=$$2; sub("/go.mod$$", "", version); \
if (version ~ /^v/) print $$1 "@" version; \
}' $(MOD_CHECK_FILES) | sort -u)
COMMIT := $(shell git describe --abbrev=40 --dirty --tags)
COMMIT_HASH := $(shell git rev-parse HEAD)
DIRTY := $(shell git diff-index --quiet HEAD -- || echo dirty)
@ -165,52 +156,12 @@ mod-tidy:
mod-check:
@$(call print, "Checking modules.")
ifneq ($(MOD_CHECK_PREFETCH),)
@GOPROXY=https://proxy.golang.org $(GOMOD) download $(MOD_CHECK_PREFETCH)
endif
GOPROXY=direct $(GOMOD) tidy
cd swapserverrpc/ && GOPROXY=direct $(GOMOD) tidy
cd looprpc/ && GOPROXY=direct $(GOMOD) tidy
cd tools/ && GOPROXY=direct $(GOMOD) tidy
if test -n "$$(git status --porcelain)"; then echo "Running go mod tidy changes go.mod/go.sum"; git status; git diff; exit 1; fi
commitmsg-lint:
@$(call print, "Linting commit message(s).")
@if [ -n "$(range)" ]; then \
./scripts/commit_message.py lint --range "$(range)"; \
elif [ -n "$(commit)" ]; then \
./scripts/commit_message.py lint --commit "$(commit)"; \
elif [ -n "$(file)" ]; then \
./scripts/commit_message.py lint --file "$(file)"; \
else \
./scripts/commit_message.py lint --commit HEAD; \
fi
commitmsg-fmt:
@$(call print, "Formatting commit message.")
@if [ -n "$(file)" ]; then \
if [ "$(inplace)" = "1" ]; then \
./scripts/commit_message.py fmt --file "$(file)" --in-place \
$(if $(filter 1,$(decode)),--decode-escaped-newlines,); \
else \
./scripts/commit_message.py fmt --file "$(file)" \
$(if $(filter 1,$(decode)),--decode-escaped-newlines,); \
fi; \
elif [ -n "$(commit)" ]; then \
./scripts/commit_message.py fmt --commit "$(commit)" \
$(if $(filter 1,$(decode)),--decode-escaped-newlines,); \
else \
echo "Error: provide file=<path> or commit=<rev>"; \
exit 1; \
fi
commitmsg-reword:
@$(call print, "Rewording commit with formatted message.")
@./scripts/commit_message.py reword \
--commit "$(if $(commit),$(commit),HEAD)" \
$(if $(filter 1,$(decode)),--decode-escaped-newlines,) \
$(if $(filter 1,$(dryrun)),--dry-run,)
sqlc:
@$(call print, "Generating sql models and queries in Go")
./scripts/gen_sqlc_docker.sh
@ -231,20 +182,4 @@ docs-check: docs
fsm:
@$(call print, "Generating state machine docs")
./scripts/fsm-generate.sh;
FSM_FILES := \
fsm/example_fsm.md \
instantout/fsm.md \
instantout/reservation/fsm.md \
staticaddr/deposit/fsm.md \
staticaddr/loopin/fsm.md
fsm-check: fsm
@$(call print, "Verifying generated state machine docs")
if test -n "$$(git status --porcelain -- $(FSM_FILES))"; then \
echo "Generated FSM diagrams are not up-to-date!"; \
git status --porcelain -- $(FSM_FILES); \
git diff -- $(FSM_FILES); \
exit 1; \
fi
.PHONY: fsm fsm-check
.PHONY: fsm

View file

@ -85,8 +85,6 @@ loopd --network=testnet
### Submit feature requests
The [GitHub issue tracker](https://github.com/lightninglabs/loop/issues) can be
used to request specific improvements or report bugs.
Do not use public issues for security disclosures.
Follow the instructions in [SECURITY.md](./SECURITY.md).
### Join us on Slack
Join us on the
@ -133,4 +131,4 @@ go install ./...
## Reproducible builds
If you want to build release files yourself, follow
[the guide](./docs/reproducible_release.md).
[the guide](./docs/release.md).

View file

@ -1,9 +0,0 @@
# Security Policy
## Reporting a Vulnerability
If you believe you have found a security or privacy issue in Lightning Loop,
please do not open a public GitHub issue.
Instead, disclose it responsibly by following the instructions in the
[lnd security policy](https://github.com/lightningnetwork/lnd/blob/master/SECURITY.md).

View file

@ -4,15 +4,14 @@ import (
"context"
"encoding/hex"
"fmt"
"math"
"math/big"
"os"
"path/filepath"
"sync"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/taproot-assets/rfqmath"
"github.com/lightninglabs/taproot-assets/rpcutils"
"github.com/lightninglabs/taproot-assets/tapcfg"
"github.com/lightninglabs/taproot-assets/taprpc"
"github.com/lightninglabs/taproot-assets/taprpc/priceoraclerpc"
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
@ -50,24 +49,13 @@ type TapdConfig struct {
// DefaultTapdConfig returns a default configuration to connect to a taproot
// assets daemon.
func DefaultTapdConfig() *TapdConfig {
return DefaultTapdConfigForNetwork(
btcutil.AppDataDir("tapd", false), "mainnet",
)
}
// DefaultTapdConfigForNetwork returns the default tapd configuration rooted in
// defaultTapdDir for the given Bitcoin network. Passing the directory
// explicitly keeps path construction testable without accessing the user's
// real tapd data.
func DefaultTapdConfigForNetwork(defaultTapdDir, network string) *TapdConfig {
defaultConf := tapcfg.DefaultConfig()
return &TapdConfig{
Activate: false,
Host: "localhost:10029",
MacaroonPath: filepath.Join(
defaultTapdDir, "data", network, "admin.macaroon",
),
TLSPath: filepath.Join(defaultTapdDir, "tls.cert"),
RFQtimeout: defaultRfqTimeout,
Activate: false,
Host: "localhost:10029",
MacaroonPath: defaultConf.RpcConf.MacaroonPath,
TLSPath: defaultConf.RpcConf.TLSCertPath,
RFQtimeout: defaultRfqTimeout,
}
}
@ -79,19 +67,14 @@ type TapdClient struct {
rfqrpc.RfqClient
universerpc.UniverseClient
rfqTimeoutSeconds uint32
assetNameCache map[string]string
assetNameMutex sync.RWMutex
cc *grpc.ClientConn
cfg *TapdConfig
assetNameCache map[string]string
assetNameMutex sync.Mutex
cc *grpc.ClientConn
}
// NewTapdClient returns a new taproot assets client.
func NewTapdClient(config *TapdConfig) (*TapdClient, error) {
rfqTimeoutSeconds, err := getRfqTimeoutSeconds(config.RFQtimeout)
if err != nil {
return nil, err
}
// Create the client connection to the server.
conn, err := getClientConn(config)
if err != nil {
@ -102,7 +85,7 @@ func NewTapdClient(config *TapdConfig) (*TapdClient, error) {
client := &TapdClient{
assetNameCache: make(map[string]string),
cc: conn,
rfqTimeoutSeconds: rfqTimeoutSeconds,
cfg: config,
TaprootAssetsClient: taprpc.NewTaprootAssetsClient(conn),
TaprootAssetChannelsClient: tapchannelrpc.NewTaprootAssetChannelsClient(conn),
PriceOracleClient: priceoraclerpc.NewPriceOracleClient(conn),
@ -145,7 +128,7 @@ func (c *TapdClient) GetRfqForAsset(ctx context.Context,
PeerPubKey: peerPubkey,
PaymentMaxAmt: uint64(paymentMaxAmt),
Expiry: uint64(expiry),
TimeoutSeconds: c.rfqTimeoutSeconds,
TimeoutSeconds: uint32(c.cfg.RFQtimeout.Seconds()),
})
if err != nil {
return nil, err
@ -158,26 +141,21 @@ func (c *TapdClient) GetRfqForAsset(ctx context.Context,
rfq.GetRejectedQuote())
}
acceptedQuote := rfq.GetAcceptedQuote()
if acceptedQuote == nil {
return nil, fmt.Errorf("no accepted quote")
if rfq.GetAcceptedQuote() != nil {
return rfq.GetAcceptedQuote(), nil
}
_, err = unmarshalAssetRate(acceptedQuote.BidAssetRate)
if err != nil {
return nil, fmt.Errorf("invalid accepted quote asset rate: %w",
err)
}
return acceptedQuote, nil
return nil, fmt.Errorf("no accepted quote")
}
// GetAssetName returns the human-readable name of the asset.
func (c *TapdClient) GetAssetName(ctx context.Context,
assetId []byte) (string, error) {
c.assetNameMutex.Lock()
defer c.assetNameMutex.Unlock()
assetIdStr := hex.EncodeToString(assetId)
if name, ok := c.getCachedAssetName(assetIdStr); ok {
if name, ok := c.assetNameCache[assetIdStr]; ok {
return name, nil
}
@ -203,28 +181,11 @@ func (c *TapdClient) GetAssetName(ctx context.Context,
assetName = assetStats.AssetStats[0].Asset.AssetName
}
c.cacheAssetName(assetIdStr, assetName)
c.assetNameCache[assetIdStr] = assetName
return assetName, nil
}
// getCachedAssetName returns an asset name from the cache.
func (c *TapdClient) getCachedAssetName(assetID string) (string, bool) {
c.assetNameMutex.RLock()
defer c.assetNameMutex.RUnlock()
name, ok := c.assetNameCache[assetID]
return name, ok
}
// cacheAssetName adds an asset name to the cache.
func (c *TapdClient) cacheAssetName(assetID, name string) {
c.assetNameMutex.Lock()
defer c.assetNameMutex.Unlock()
c.assetNameCache[assetID] = name
}
// GetAssetPrice returns the price of an asset in satoshis. NOTE: this currently
// uses the rfq process for the asset price. A future implementation should
// use a price oracle to not spam a peer.
@ -248,7 +209,7 @@ func (c *TapdClient) GetAssetPrice(ctx context.Context, assetID string,
},
PaymentMaxAmt: uint64(msatAmt),
Expiry: uint64(rfqExpiry),
TimeoutSeconds: c.rfqTimeoutSeconds,
TimeoutSeconds: uint32(c.cfg.RFQtimeout.Seconds()),
PeerPubKey: peerPubkey,
})
if err != nil {
@ -282,7 +243,7 @@ func (c *TapdClient) GetAssetPrice(ctx context.Context, assetID string,
func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) (
btcutil.Amount, error) {
rateFP, err := unmarshalAssetRate(assetRate)
rateFP, err := rpcutils.UnmarshalRfqFixedPoint(assetRate)
if err != nil {
return 0, fmt.Errorf("cannot unmarshal asset rate: %w", err)
}
@ -294,33 +255,6 @@ func getSatsFromAssetAmt(assetAmt uint64, assetRate *rfqrpc.FixedPoint) (
return msatAmt.ToSatoshis(), nil
}
// unmarshalAssetRate validates and converts an RPC asset rate to the fixed
// point representation used for RFQ arithmetic.
func unmarshalAssetRate(assetRate *rfqrpc.FixedPoint) (
*rfqmath.BigIntFixedPoint, error) {
if assetRate == nil {
return nil, fmt.Errorf("asset rate cannot be nil")
}
if assetRate.Scale > math.MaxUint8 {
return nil, fmt.Errorf("scale value overflow: %v", assetRate.Scale)
}
coefficient, ok := new(big.Int).SetString(assetRate.Coefficient, 10)
if !ok {
return nil, fmt.Errorf("invalid asset rate coefficient: %q",
assetRate.Coefficient)
}
if coefficient.Sign() <= 0 {
return nil, fmt.Errorf("asset rate coefficient must be positive")
}
return &rfqmath.BigIntFixedPoint{
Coefficient: rfqmath.NewBigInt(coefficient),
Scale: uint8(assetRate.Scale),
}, nil
}
// getPaymentMaxAmount returns the milisat amount we are willing to pay for the
// payment.
func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) (
@ -343,26 +277,6 @@ func getPaymentMaxAmount(satAmount btcutil.Amount, feeLimitMultiplier float64) (
)
}
// getRfqTimeoutSeconds converts the configured RFQ timeout to the whole
// seconds accepted by tapd. Fractional seconds are rounded up so tapd's
// timeout is never shorter than the configured duration.
func getRfqTimeoutSeconds(timeout time.Duration) (uint32, error) {
if timeout <= 0 {
return 0, fmt.Errorf("RFQ timeout must be greater than zero")
}
seconds := timeout / time.Second
if timeout%time.Second != 0 {
seconds++
}
if seconds > time.Duration(math.MaxUint32) {
return 0, fmt.Errorf("RFQ timeout exceeds maximum of %v seconds",
uint64(math.MaxUint32))
}
return uint32(seconds), nil
}
func getClientConn(config *TapdConfig) (*grpc.ClientConn, error) {
// Load the specified TLS certificate and build transport credentials.
creds, err := credentials.NewClientTLSFromFile(config.TLSPath, "")

View file

@ -1,269 +1,14 @@
package assets
import (
"context"
"encoding/hex"
"encoding/pem"
"math"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/taproot-assets/taprpc/rfqrpc"
"github.com/lightninglabs/taproot-assets/taprpc/universerpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"gopkg.in/macaroon.v2"
)
type blockingUniverseClient struct {
universerpc.UniverseClient
queryStarted chan struct{}
releaseQuery chan struct{}
}
func (b *blockingUniverseClient) QueryAssetStats(context.Context,
*universerpc.AssetStatsQuery, ...grpc.CallOption) (
*universerpc.UniverseAssetStats, error) {
close(b.queryStarted)
<-b.releaseQuery
return &universerpc.UniverseAssetStats{
AssetStats: []*universerpc.AssetStatsSnapshot{
{
Asset: &universerpc.AssetStatsAsset{
AssetName: "queried asset",
},
},
},
}, nil
}
type staticRfqClient struct {
rfqrpc.RfqClient
response *rfqrpc.AddAssetSellOrderResponse
}
func (s *staticRfqClient) AddAssetSellOrder(context.Context,
*rfqrpc.AddAssetSellOrderRequest, ...grpc.CallOption) (
*rfqrpc.AddAssetSellOrderResponse, error) {
return s.response, nil
}
// TestDefaultTapdConfig tests that the default tapd connection paths match
// tapd's mainnet defaults.
func TestDefaultTapdConfig(t *testing.T) {
defaultTapdDir := btcutil.AppDataDir("tapd", false)
config := DefaultTapdConfig()
require.Equal(t, filepath.Join(
defaultTapdDir, "data", "mainnet", "admin.macaroon",
), config.MacaroonPath)
require.Equal(
t, filepath.Join(defaultTapdDir, "tls.cert"), config.TLSPath,
)
}
// TestTapdConfigClientConn tests that the default tapd file layout can be used
// to construct a client connection.
func TestTapdConfigClientConn(t *testing.T) {
// Use an isolated tapd root so the test never reads from or writes to a
// user's real tapd data directory.
defaultTapdDir := t.TempDir()
network := "regtest"
macaroonPath := filepath.Join(
defaultTapdDir, "data", network, "admin.macaroon",
)
require.NoError(t, os.MkdirAll(filepath.Dir(macaroonPath), 0o700))
// NewTapdClient parses the configured TLS certificate before creating
// its gRPC client. An httptest server provides a valid certificate
// without requiring a running tapd instance.
tlsServer := httptest.NewTLSServer(http.NotFoundHandler())
t.Cleanup(tlsServer.Close)
cert := tlsServer.Certificate()
certBytes := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE", Bytes: cert.Raw,
})
require.NoError(t, os.WriteFile(
filepath.Join(defaultTapdDir, "tls.cert"), certBytes, 0o600,
))
// Store a valid serialized macaroon at tapd's production path. This
// ensures connection setup tests the path itself rather than failing on
// malformed credentials.
mac, err := macaroon.New(
[]byte("root-key"), []byte("id"), "tapd",
macaroon.LatestVersion,
)
require.NoError(t, err)
macBytes, err := mac.MarshalBinary()
require.NoError(t, err)
require.NoError(t, os.WriteFile(macaroonPath, macBytes, 0o600))
// grpc.NewClient connects lazily, so constructing the tapd client verifies
// that both credentials can be loaded and parsed without needing a live
// tapd server.
config := DefaultTapdConfigForNetwork(defaultTapdDir, network)
client, err := NewTapdClient(config)
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, client.cc.Close())
})
require.Equal(t, macaroonPath, config.MacaroonPath)
require.Equal(
t, filepath.Join(defaultTapdDir, "tls.cert"), config.TLSPath,
)
}
// TestGetAssetNameCachedLookupNotBlocked verifies that a slow universe query
// for one asset does not prevent another caller from reading a cached name.
func TestGetAssetNameCachedLookupNotBlocked(t *testing.T) {
const cachedName = "cached asset"
cachedAssetID := []byte{1}
queryStarted := make(chan struct{})
releaseQuery := make(chan struct{})
client := &TapdClient{
UniverseClient: &blockingUniverseClient{
queryStarted: queryStarted,
releaseQuery: releaseQuery,
},
assetNameCache: map[string]string{
hex.EncodeToString(cachedAssetID): cachedName,
},
}
queryResult := make(chan error, 1)
go func() {
_, err := client.GetAssetName(context.Background(), []byte{2})
queryResult <- err
}()
select {
case <-queryStarted:
case <-time.After(time.Second):
t.Fatal("universe query did not start")
}
type nameResult struct {
name string
err error
}
cachedResult := make(chan nameResult, 1)
go func() {
name, err := client.GetAssetName(
context.Background(), cachedAssetID,
)
cachedResult <- nameResult{name: name, err: err}
}()
select {
case result := <-cachedResult:
require.NoError(t, result.err)
require.Equal(t, cachedName, result.name)
case <-time.After(time.Second):
close(releaseQuery)
t.Fatal("cached lookup blocked behind universe query")
}
close(releaseQuery)
require.NoError(t, <-queryResult)
}
// TestGetRfqForAssetValidatesRate verifies that malformed accepted quote rates
// are rejected before they reach downstream RFQ arithmetic.
func TestGetRfqForAssetValidatesRate(t *testing.T) {
tests := []struct {
name string
assetRate *rfqrpc.FixedPoint
expectError bool
}{
{
name: "valid",
assetRate: &rfqrpc.FixedPoint{
Coefficient: "100000", Scale: 0,
},
},
{
name: "nil",
assetRate: nil,
expectError: true,
},
{
name: "malformed coefficient",
assetRate: &rfqrpc.FixedPoint{
Coefficient: "not-a-number", Scale: 0,
},
expectError: true,
},
{
name: "zero coefficient",
assetRate: &rfqrpc.FixedPoint{
Coefficient: "0", Scale: 0,
},
expectError: true,
},
{
name: "negative coefficient",
assetRate: &rfqrpc.FixedPoint{
Coefficient: "-1", Scale: 0,
},
expectError: true,
},
{
name: "scale overflow",
assetRate: &rfqrpc.FixedPoint{
Coefficient: "1", Scale: 256,
},
expectError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
acceptedQuote := &rfqrpc.PeerAcceptedSellQuote{
BidAssetRate: test.assetRate,
}
acceptedResponse :=
&rfqrpc.AddAssetSellOrderResponse_AcceptedQuote{
AcceptedQuote: acceptedQuote,
}
client := &TapdClient{
RfqClient: &staticRfqClient{
response: &rfqrpc.AddAssetSellOrderResponse{
Response: acceptedResponse,
},
},
rfqTimeoutSeconds: 60,
}
quote, err := client.GetRfqForAsset(
context.Background(), 1000, []byte{1}, []byte{2},
time.Now().Add(time.Minute).Unix(), 1,
)
if test.expectError {
require.Error(t, err)
require.Nil(t, quote)
return
}
require.NoError(t, err)
require.Same(t, acceptedQuote, quote)
})
}
}
func TestGetPaymentMaxAmount(t *testing.T) {
tests := []struct {
satAmount btcutil.Amount
@ -323,62 +68,6 @@ func TestGetPaymentMaxAmount(t *testing.T) {
}
}
// TestGetRfqTimeoutSeconds verifies that configured durations are safely
// converted to tapd's whole-second timeout field.
func TestGetRfqTimeoutSeconds(t *testing.T) {
tests := []struct {
name string
timeout time.Duration
expectedSeconds uint32
expectError bool
}{
{
name: "whole seconds",
timeout: 60 * time.Second,
expectedSeconds: 60,
},
{
name: "sub-second rounded up",
timeout: time.Millisecond,
expectedSeconds: 1,
},
{
name: "fractional second rounded up",
timeout: time.Second + time.Nanosecond,
expectedSeconds: 2,
},
{
name: "zero",
timeout: 0,
expectError: true,
},
{
name: "negative",
timeout: -time.Second,
expectError: true,
},
{
name: "overflow",
timeout: time.Duration(math.MaxUint32)*time.Second +
time.Nanosecond,
expectError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
seconds, err := getRfqTimeoutSeconds(test.timeout)
if test.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, test.expectedSeconds, seconds)
})
}
}
func TestGetSatsFromAssetAmt(t *testing.T) {
tests := []struct {
assetAmt uint64
@ -404,39 +93,6 @@ func TestGetSatsFromAssetAmt(t *testing.T) {
expected: btcutil.Amount(0),
expectError: false,
},
{
assetAmt: 1000,
assetRate: nil,
expectError: true,
},
{
assetAmt: 1000,
assetRate: &rfqrpc.FixedPoint{
Coefficient: "not-a-number", Scale: 0,
},
expectError: true,
},
{
assetAmt: 1000,
assetRate: &rfqrpc.FixedPoint{
Coefficient: "0", Scale: 0,
},
expectError: true,
},
{
assetAmt: 1000,
assetRate: &rfqrpc.FixedPoint{
Coefficient: "-1", Scale: 0,
},
expectError: true,
},
{
assetAmt: 1000,
assetRate: &rfqrpc.FixedPoint{
Coefficient: "1", Scale: 256,
},
expectError: true,
},
}
for _, test := range tests {

View file

@ -43,10 +43,16 @@ func forceAutoloop(ctx context.Context, cmd *cli.Command) error {
}
func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, func(), error) {
conn, cleanup, err := sessionTransport.Dial(cmd)
rpcServer := cmd.String("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
if err != nil {
return nil, nil, err
}
conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath)
if err != nil {
return nil, nil, err
}
cleanup := func() { conn.Close() }
debugClient := looprpc.NewDebugClient(conn)
return debugClient, cleanup, nil

View file

@ -1,82 +0,0 @@
package main
import (
"errors"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
// TestDefaultPathText verifies HOME-based path elision behavior used for help
// defaults without relying on the real environment.
func TestDefaultPathText(t *testing.T) {
sep := string(filepath.Separator)
home := filepath.Clean(sep + "home" + sep + "alice")
homeDir := func() (string, error) { return home, nil }
tests := []struct {
name string
value string
homeFn func() (string, error)
want string
}{
{
name: "empty value",
value: "",
homeFn: func() (string, error) {
return home, nil
},
want: "",
},
{
name: "nil homedir func",
value: home + sep + "data",
homeFn: nil,
want: home + sep + "data",
},
{
name: "homedir error",
value: home + sep + "data",
homeFn: func() (string, error) {
return "", errors.New("homedir error")
},
want: home + sep + "data",
},
{
name: "exact home",
value: home,
homeFn: homeDir,
want: "~",
},
{
name: "home prefix",
value: home + sep + "dir" + sep + "file",
homeFn: homeDir,
want: "~" + sep + "dir" + sep + "file",
},
{
name: "non-home path",
value: filepath.Clean(sep + "var" + sep + "tmp"),
homeFn: homeDir,
want: filepath.Clean(sep + "var" + sep + "tmp"),
},
{
name: "prefix but not path segment",
value: home + "x" + sep + "dir",
homeFn: homeDir,
want: home + "x" + sep + "dir",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := defaultPathText(test.value, test.homeFn)
require.Equalf(
t, test.want, got,
"defaultPathText(%q)", test.value,
)
})
}
}

View file

@ -88,12 +88,6 @@ func instantOut(ctx context.Context, cmd *cli.Command) error {
fmt.Printf("Available reservations: \n\n")
for _, res := range confirmedReservations {
idx++
if len(res.ReservationId) != reservation.IdLength {
return fmt.Errorf("invalid reservation id length: "+
"got %d, expected %d", len(res.ReservationId),
reservation.IdLength)
}
fmt.Printf("Reservation %v: shortid %x, amt %v, expiry "+
"height %v \n", idx, res.ReservationId[:3], res.Amount,
res.Expiry)
@ -137,7 +131,7 @@ func instantOut(ctx context.Context, cmd *cli.Command) error {
if err != nil {
return err
}
if idx <= 0 {
if idx < 0 {
return fmt.Errorf("invalid index %v", idx)
}
@ -183,8 +177,6 @@ func instantOut(ctx context.Context, cmd *cli.Command) error {
fmt.Println("Starting instant swap out")
maxSwapFee := quote.ServiceFeeSat
// Now we can request the instant out swap.
instantOutRes, err := client.InstantOut(
ctx,
@ -192,9 +184,6 @@ func instantOut(ctx context.Context, cmd *cli.Command) error {
ReservationIds: selectedReservations,
OutgoingChanSet: outgoingChanSet,
DestAddr: cmd.String("addr"),
MaxSwapFee: &looprpc.InstantOutRequest_MaxSwapFeeSat{
MaxSwapFeeSat: maxSwapFee,
},
},
)
if err != nil {

View file

@ -340,12 +340,6 @@ var setParamsCommand = &cli.Command{
Usage: "the confirmation target for loop in on-chain " +
"htlcs.",
},
&cli.StringFlag{
Name: "loopinsource",
Usage: "the loop-in source to use for autoloop rules: " +
"wallet or static-address. Static-address " +
"requires loopd --experimental.",
},
&cli.BoolFlag{
Name: "easyautoloop",
Usage: "set to true to enable easy autoloop, which " +
@ -561,25 +555,6 @@ func setParams(ctx context.Context, cmd *cli.Command) error {
flagSet = true
}
if cmd.IsSet("loopinsource") {
switch cmd.String("loopinsource") {
case "wallet":
params.LoopInSource =
looprpc.LoopInSource_LOOP_IN_SOURCE_WALLET
case "static-address", "static_address", "static":
params.LoopInSource =
looprpc.LoopInSource_LOOP_IN_SOURCE_STATIC_ADDRESS
default:
return fmt.Errorf("unknown loopinsource value %q "+
"(use \"wallet\" or \"static-address\")",
cmd.String("loopinsource"))
}
flagSet = true
}
// If we are setting easy autoloop parameters, we need to ensure that
// the asset ID is set, and that we have a valid entry in our params
// map.

View file

@ -207,7 +207,7 @@ func loopIn(ctx context.Context, cmd *cli.Command) error {
}
fmt.Printf("Swap initiated\n")
fmt.Printf("ID: %x\n", resp.IdBytes)
fmt.Printf("ID: %v\n", resp.Id)
if resp.HtlcAddressP2Tr != "" {
fmt.Printf("HTLC address (P2TR): %v\n", resp.HtlcAddressP2Tr)

View file

@ -248,9 +248,9 @@ func loopOut(ctx context.Context, cmd *cli.Command) error {
// Set our maximum swap wait time. If a fast swap is requested we set
// it to now, otherwise to 30 minutes in the future.
fast := cmd.Bool("fast")
swapDeadline := cliClock.Now()
swapDeadline := time.Now()
if !fast {
swapDeadline = cliClock.Now().Add(defaultSwapWaitTime)
swapDeadline = time.Now().Add(defaultSwapWaitTime)
}
sweepConfTarget := int32(cmd.Uint64("conf_target"))

View file

@ -6,12 +6,11 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/btcsuite/btcd/btcutil"
@ -20,7 +19,6 @@ import (
"github.com/lightninglabs/loop/loopd"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
@ -54,11 +52,10 @@ var (
defaultInitiator = "loop-cli"
loopDirFlag = &cli.StringFlag{
Name: "loopdir",
Value: loopd.LoopDirBase,
DefaultText: defaultPathText(loopd.LoopDirBase, os.UserHomeDir),
Usage: "path to loop's base directory",
Sources: cli.EnvVars(envVarLoopDir),
Name: "loopdir",
Value: loopd.LoopDirBase,
Usage: "path to loop's base directory",
Sources: cli.EnvVars(envVarLoopDir),
}
networkFlag = &cli.StringFlag{
Name: "network",
@ -69,21 +66,15 @@ var (
}
tlsCertFlag = &cli.StringFlag{
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
DefaultText: defaultPathText(
loopd.DefaultTLSCertPath, os.UserHomeDir,
),
Name: "tlscertpath",
Usage: "path to loop's TLS certificate",
Value: loopd.DefaultTLSCertPath,
Sources: cli.EnvVars(envVarTLSCertPath),
}
macaroonPathFlag = &cli.StringFlag{
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
DefaultText: defaultPathText(
loopd.DefaultMacaroonPath, os.UserHomeDir,
),
Name: "macaroonpath",
Usage: "path to macaroon file",
Value: loopd.DefaultMacaroonPath,
Sources: cli.EnvVars(envVarMacaroonPath),
}
verboseFlag = &cli.BoolFlag{
@ -101,53 +92,8 @@ var (
instantOutCommand, listInstantOutsCommand, stopCommand,
printManCommand, printMarkdownCommand,
}
// cliClock provides the time source used by CLI commands.
cliClock clock.Clock = clock.NewDefaultClock()
// sessionRec is the active recorder when session capture is enabled.
sessionRec *sessionRecorder
// forceDeterministicJSON is enabled by tests to obtain stable JSON
// output.
forceDeterministicJSON bool
)
// installSessionSignalHandler records signals and cancels the root context.
func installSessionSignalHandler(cancel context.CancelFunc) func() {
if sessionRec == nil {
return func() {}
}
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
done := make(chan struct{})
go func() {
defer close(done)
interrupted := false
for sig := range sigCh {
sessionRec.LogSignal(sig)
if !interrupted {
interrupted = true
cancel()
continue
}
// A second signal requests immediate shutdown. Finalize the
// session first because os.Exit skips deferred cleanup.
_ = sessionRec.Finalize(fmt.Errorf("signal: %s", sig))
os.Exit(130)
}
}()
return func() {
signal.Stop(sigCh)
close(sigCh)
<-done
}
}
const (
// satAmtFmt formats a satoshi value into a one line string, intended to
@ -186,37 +132,6 @@ const (
envVarMacaroonPath = "LOOPCLI_MACAROONPATH"
)
// defaultPathText returns a help-friendly path string that replaces the user's
// home directory with "~". The homeDir function is injected so callers can
// control environment-dependent behavior in tests.
func defaultPathText(value string, homeDir func() (string, error)) string {
if value == "" {
return value
}
if homeDir == nil {
return value
}
home, err := homeDir()
if err != nil || home == "" {
return value
}
cleanHome := filepath.Clean(home)
cleanValue := filepath.Clean(value)
if cleanValue == cleanHome {
return "~"
}
prefix := cleanHome + string(filepath.Separator)
if suffix, ok := strings.CutPrefix(cleanValue, prefix); ok {
return "~" + string(filepath.Separator) + suffix
}
return value
}
func printJSON(resp any) {
b, err := json.Marshal(resp)
if err != nil {
@ -229,8 +144,7 @@ func printJSON(resp any) {
fatal(err)
}
out.WriteString("\n")
printBytes := maybeNormalizeJSON(out.Bytes())
_, _ = os.Stdout.Write(printBytes)
_, _ = out.WriteTo(os.Stdout)
}
func printRespJSON(resp proto.Message) {
@ -240,76 +154,16 @@ func printRespJSON(resp proto.Message) {
return
}
fmt.Println(string(maybeNormalizeJSON(jsonBytes)))
fmt.Println(string(jsonBytes))
}
func fatal(err error) {
fmt.Fprintf(os.Stderr, "[loop] %v\n", err)
if sessionRec != nil {
if finalizeErr := sessionRec.Finalize(err); finalizeErr != nil {
fmt.Fprintf(os.Stderr, "[loop] unable to finalize "+
"session: %v\n", finalizeErr)
}
}
os.Exit(1)
}
func main() {
var err error
sessionRec, err = newSessionRecorder(os.Args)
if err != nil {
fatal(err)
}
// Intercept clock and stdio if needed.
if sessionRec != nil {
restoreClock := hookClock(
clock.NewTestClock(sessionRec.ClockStart()),
)
defer restoreClock()
if err := sessionRec.Start(nil, nil, nil); err != nil {
fatal(err)
}
}
// Intercept clock calls if needed.
var restoreTransport func()
if sessionRec != nil {
restoreTransport = hookGrpc(sessionRec)
defer restoreTransport()
}
rootCmd := newRootCommand()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if sessionRec != nil {
ctx = sessionRec.InjectContext(ctx)
}
if sessionRec != nil {
signalStop := installSessionSignalHandler(cancel)
defer signalStop()
}
if err := rootCmd.Run(ctx, os.Args); err != nil {
fatal(err)
}
if sessionRec != nil {
if err := sessionRec.Finalize(nil); err != nil {
fmt.Fprintf(os.Stderr, "[loop] unable to finalize "+
"session: %v\n", err)
}
}
}
// newRootCommand constructs the CLI root command for loop.
func newRootCommand() *cli.Command {
return &cli.Command{
rootCmd := &cli.Command{
Name: "loop",
Usage: "control plane for your loopd",
Version: loop.RichVersion(),
@ -330,11 +184,17 @@ func newRootCommand() *cli.Command {
return cli.ShowRootCommandHelp(cmd)
},
}
if err := rootCmd.Run(context.Background(), os.Args); err != nil {
fatal(err)
}
}
// getClient establishes a SwapClient RPC connection and returns the client and
// a cleanup handler.
func getClient(cmd *cli.Command) (looprpc.SwapClientClient, func(), error) {
func getClient(cmd *cli.Command) (looprpc.SwapClientClient,
func(), error) {
client, _, cleanup, err := getClientWithConn(cmd)
if err != nil {
return nil, nil, err
@ -346,54 +206,23 @@ func getClient(cmd *cli.Command) (looprpc.SwapClientClient, func(), error) {
// getClientWithConn returns both the SwapClient RPC client and the underlying
// gRPC connection so callers can perform connection-aware actions.
func getClientWithConn(cmd *cli.Command) (looprpc.SwapClientClient,
daemonConn, func(), error) {
*grpc.ClientConn, func(), error) {
conn, cleanup, err := sessionTransport.Dial(cmd)
rpcServer := cmd.String("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
if err != nil {
return nil, nil, nil, err
}
conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath)
if err != nil {
return nil, nil, nil, err
}
cleanup := func() { conn.Close() }
loopClient := looprpc.NewSwapClientClient(conn)
return loopClient, conn, cleanup, nil
}
// hookClock overrides cliClock until the returned callback is called.
func hookClock(c clock.Clock) func() {
prev := cliClock
cliClock = c
return func() {
cliClock = prev
}
}
// maybeNormalizeJSON rewrites JSON output to avoid the build-dependent spacing
// introduced by google.golang.org/protobuf/internal/encoding/json (see
// WriteName in protobuf-go-hex-display/internal/encoding/json/encode.go, which
// uses internal/detrand.Bool). When recording or replaying sessions we ensure
// stable output by re-encoding with the standard library.
func maybeNormalizeJSON(raw []byte) []byte {
if sessionRec == nil && !forceDeterministicJSON {
return raw
}
var parsed any
if err := json.Unmarshal(raw, &parsed); err != nil {
return raw
}
normalized, err := json.MarshalIndent(parsed, "", " ")
if err != nil {
return raw
}
if len(raw) > 0 && raw[len(raw)-1] == '\n' {
normalized = append(normalized, '\n')
}
return normalized
}
func getMaxRoutingFee(amt btcutil.Amount) btcutil.Amount {
return swap.CalcFee(amt, maxRoutingFeeBase, maxRoutingFeeRate)
}
@ -500,6 +329,12 @@ func displayInDetails(req *looprpc.QuoteRequest,
"wallet.\n\n")
}
if req.DepositOutpoints != nil {
fmt.Printf("On-chain fees for static address loop-ins are not " +
"included.\nThey were already paid when the deposits " +
"were created.\n\n")
}
printQuoteInResp(req, resp, verbose)
fmt.Printf("\nCONTINUE SWAP? (y/n): ")
@ -560,7 +395,10 @@ func parseAmt(text string) (btcutil.Amount, error) {
func logSwap(swap *looprpc.SwapStatus) {
// If our swap failed, we add our failure reason to the state.
swapState := monitorSwapState(swap)
swapState := fmt.Sprintf("%v", swap.State)
if swap.State == looprpc.SwapState_FAILED {
swapState = fmt.Sprintf("%v (%v)", swapState, swap.FailureReason)
}
if swap.Type == looprpc.SwapType_LOOP_OUT {
fmt.Printf("%v %v %v %v - %v",
@ -582,32 +420,10 @@ func logSwap(swap *looprpc.SwapStatus) {
}
}
showCost := shouldShowSwapCost(swap.GetState())
if swap.Type == looprpc.SwapType_STATIC_LOOP_IN {
staticState := swap.GetStaticLoopInState()
// Static loop-ins key cost visibility off the dedicated FSM state, not
// the generic SwapState lifecycle used by traditional swaps.
switch staticState {
case looprpc.StaticAddressLoopInSwapState_INIT_HTLC,
looprpc.StaticAddressLoopInSwapState_SIGN_HTLC_TX,
looprpc.StaticAddressLoopInSwapState_MONITOR_INVOICE_HTLC_TX,
looprpc.StaticAddressLoopInSwapState_SWEEP_STATIC_ADDRESS_HTLC_TIMEOUT,
looprpc.StaticAddressLoopInSwapState_MONITOR_HTLC_TIMEOUT_SWEEP,
looprpc.StaticAddressLoopInSwapState_UNLOCK_DEPOSITS:
if swap.State != looprpc.SwapState_INITIATED &&
swap.State != looprpc.SwapState_HTLC_PUBLISHED &&
swap.State != looprpc.SwapState_PREIMAGE_REVEALED {
showCost = false
case looprpc.StaticAddressLoopInSwapState_PAYMENT_RECEIVED,
looprpc.StaticAddressLoopInSwapState_HTLC_STATIC_ADDRESS_TIMEOUT_SWEPT,
looprpc.StaticAddressLoopInSwapState_SUCCEEDED,
looprpc.StaticAddressLoopInSwapState_SUCCEEDED_TRANSITIONING_FAILED,
looprpc.StaticAddressLoopInSwapState_FAILED_STATIC_ADDRESS_SWAP:
showCost = true
}
}
if showCost {
fmt.Printf(" (cost: server %v, onchain %v, offchain %v)",
swap.CostServer, swap.CostOnchain, swap.CostOffchain,
)
@ -616,46 +432,13 @@ func logSwap(swap *looprpc.SwapStatus) {
fmt.Println()
}
// monitorSwapState returns the static loop-in FSM label for static swaps and
// the shared swap-state label for all others.
func monitorSwapState(swap *looprpc.SwapStatus) string {
if swap.Type == looprpc.SwapType_STATIC_LOOP_IN {
return swap.GetStaticLoopInState().String()
}
return genericMonitorSwapState(swap)
}
// shouldShowSwapCost reports whether a swap's generic state is terminal enough
// to include the persisted cost summary in monitor output.
func shouldShowSwapCost(loopState looprpc.SwapState) bool {
return loopState != looprpc.SwapState_INITIATED &&
loopState != looprpc.SwapState_HTLC_PUBLISHED &&
loopState != looprpc.SwapState_PREIMAGE_REVEALED
}
// genericMonitorSwapState formats the shared swap-state label and failure
// reason used by non-static swaps.
func genericMonitorSwapState(swap *looprpc.SwapStatus) string {
loopState := swap.GetState()
swapState := fmt.Sprintf("%v", loopState)
if loopState == looprpc.SwapState_FAILED {
swapState = fmt.Sprintf(
"%v (%v)", swapState, swap.FailureReason,
)
}
return swapState
}
// getClientConn dials the loopd gRPC server with TLS and macaroon auth.
func getClientConn(address, tlsCertPath, macaroonPath string) (daemonConn,
func(), error) {
func getClientConn(address, tlsCertPath, macaroonPath string) (*grpc.ClientConn,
error) {
// We always need to send a macaroon.
macOption, err := readMacaroon(macaroonPath)
if err != nil {
return nil, nil, err
return nil, err
}
opts := []grpc.DialOption{
@ -663,40 +446,28 @@ func getClientConn(address, tlsCertPath, macaroonPath string) (daemonConn,
macOption,
}
// Install gRPC interceptors for session recording if needed.
if unary := sessionTransport.UnaryInterceptor(); unary != nil {
opts = append(opts, grpc.WithChainUnaryInterceptor(unary))
}
if stream := sessionTransport.StreamInterceptor(); stream != nil {
opts = append(opts, grpc.WithChainStreamInterceptor(stream))
}
// Since TLS cannot be disabled, we'll always have a cert file to read.
creds, err := credentials.NewClientTLSFromFile(tlsCertPath, "")
if err != nil {
return nil, nil, err
return nil, err
}
opts = append(opts, grpc.WithTransportCredentials(creds))
conn, err := grpc.NewClient(address, opts...)
if err != nil {
return nil, nil, fmt.Errorf("unable to create RPC client: %v",
return nil, fmt.Errorf("unable to create RPC client: %v",
err)
}
cleanup := func() {
_ = conn.Close()
}
return conn, cleanup, nil
return conn, nil
}
// readMacaroon tries to read the macaroon file at the specified path and create
// gRPC dial options from it.
func readMacaroon(macPath string) (grpc.DialOption, error) {
// Load the specified macaroon file.
macBytes, err := os.ReadFile(macPath)
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
return nil, fmt.Errorf("unable to read macaroon path : %v", err)
}

View file

@ -1,275 +0,0 @@
package main
import (
"bytes"
"io"
"os"
"strings"
"testing"
"time"
"github.com/lightninglabs/loop/looprpc"
"github.com/stretchr/testify/require"
)
// TestMonitorSwapStateKeepsRegularLoopInFailureReason preserves the generic
// failure suffix for regular loop-in swaps so terminal errors stay visible.
func TestMonitorSwapStateKeepsRegularLoopInFailureReason(t *testing.T) {
swap := &looprpc.SwapStatus{
Type: looprpc.SwapType_LOOP_IN,
State: looprpc.SwapState_FAILED,
FailureReason: looprpc.
FailureReason_FAILURE_REASON_TIMEOUT,
}
got := monitorSwapState(swap)
require.Equal(t, "FAILED (FAILURE_REASON_TIMEOUT)", got)
}
// TestMonitorSwapStateLabelsStaticLoopInStages locks the precise static loop-in
// stage names so monitor output stays stable as the FSM evolves.
func TestMonitorSwapStateLabelsStaticLoopInStages(t *testing.T) {
tests := []struct {
name string
staticState looprpc.StaticAddressLoopInSwapState
want string
}{
{
name: "init htlc",
staticState: looprpc.StaticAddressLoopInSwapState_INIT_HTLC,
want: "INIT_HTLC",
},
{
name: "sign htlc",
staticState: looprpc.
StaticAddressLoopInSwapState_SIGN_HTLC_TX,
want: "SIGN_HTLC_TX",
},
{
name: "monitor invoice and htlc",
staticState: looprpc.
StaticAddressLoopInSwapState_MONITOR_INVOICE_HTLC_TX,
want: "MONITOR_INVOICE_HTLC_TX",
},
{
name: "unlock deposits",
staticState: looprpc.
StaticAddressLoopInSwapState_UNLOCK_DEPOSITS,
want: "UNLOCK_DEPOSITS",
},
{
name: "payment received",
staticState: looprpc.StaticAddressLoopInSwapState_PAYMENT_RECEIVED,
want: "PAYMENT_RECEIVED",
},
{
name: "timeout sweep",
staticState: looprpc.
StaticAddressLoopInSwapState_SWEEP_STATIC_ADDRESS_HTLC_TIMEOUT,
want: "SWEEP_STATIC_ADDRESS_HTLC_TIMEOUT",
},
{
name: "monitor timeout sweep",
staticState: looprpc.
StaticAddressLoopInSwapState_MONITOR_HTLC_TIMEOUT_SWEEP,
want: "MONITOR_HTLC_TIMEOUT_SWEEP",
},
{
name: "timeout swept",
staticState: looprpc.
StaticAddressLoopInSwapState_HTLC_STATIC_ADDRESS_TIMEOUT_SWEPT,
want: "HTLC_STATIC_ADDRESS_TIMEOUT_SWEPT",
},
{
name: "success",
staticState: looprpc.StaticAddressLoopInSwapState_SUCCEEDED,
want: "SUCCEEDED",
},
{
name: "succeeded transitioning failed",
staticState: looprpc.
StaticAddressLoopInSwapState_SUCCEEDED_TRANSITIONING_FAILED,
want: "SUCCEEDED_TRANSITIONING_FAILED",
},
{
name: "failed",
staticState: looprpc.
StaticAddressLoopInSwapState_FAILED_STATIC_ADDRESS_SWAP,
want: "FAILED_STATIC_ADDRESS_SWAP",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
swap := &looprpc.SwapStatus{
Type: looprpc.SwapType_STATIC_LOOP_IN,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: test.staticState,
},
}
got := monitorSwapState(swap)
require.Equal(t, test.want, got)
})
}
initSwap := &looprpc.SwapStatus{
Type: looprpc.SwapType_STATIC_LOOP_IN,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_INIT_HTLC,
},
}
signSwap := &looprpc.SwapStatus{
Type: looprpc.SwapType_STATIC_LOOP_IN,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_SIGN_HTLC_TX,
},
}
require.NotEqual(t, monitorSwapState(initSwap), monitorSwapState(signSwap))
}
// TestMonitorSwapStateLabelsUnknownStaticLoopIn ensures the absent static
// oneof maps to the UNKNOWN static loop-in label.
func TestMonitorSwapStateLabelsUnknownStaticLoopIn(t *testing.T) {
swap := &looprpc.SwapStatus{
Type: looprpc.SwapType_STATIC_LOOP_IN,
}
got := monitorSwapState(swap)
require.Equal(t, "UNKNOWN_STATIC_ADDRESS_SWAP_STATE", got)
}
// TestLogSwapHidesStaticLoopInCostForInFlightState proves in-flight static FSM
// state suppresses cost even with generic SUCCESS set deliberately.
func TestLogSwapHidesStaticLoopInCostForInFlightState(t *testing.T) {
swap := &looprpc.SwapStatus{
LastUpdateTime: time.Unix(1, 0).UnixNano(),
Type: looprpc.SwapType_STATIC_LOOP_IN,
State: looprpc.SwapState_SUCCESS,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_INIT_HTLC,
},
Amt: 50_000,
CostServer: 11,
CostOnchain: 22,
CostOffchain: 33,
HtlcAddressP2Wsh: "bc1qstaticinflighttestaddress",
}
output := captureStdout(t, func() {
logSwap(swap)
})
require.Contains(t, output, "STATIC_LOOP_IN INIT_HTLC 0.00050000 BTC")
require.NotContains(t, output, "(cost:")
}
// TestLogSwapHidesCostForUnknownStaticLoopInState ensures unknown static
// states print the UNKNOWN label without a cost summary.
func TestLogSwapHidesCostForUnknownStaticLoopInState(t *testing.T) {
swap := &looprpc.SwapStatus{
LastUpdateTime: time.Unix(3, 0).UnixNano(),
Type: looprpc.SwapType_STATIC_LOOP_IN,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_UNKNOWN_STATIC_ADDRESS_SWAP_STATE,
},
Amt: 50_000,
CostServer: 11,
CostOnchain: 22,
CostOffchain: 33,
HtlcAddressP2Wsh: "bc1qunknownstaticterminaltestaddress",
}
output := captureStdout(t, func() {
logSwap(swap)
})
require.Contains(
t, output, "STATIC_LOOP_IN UNKNOWN_STATIC_ADDRESS_SWAP_STATE 0.00050000 BTC",
)
require.NotContains(t, output, "(cost:")
}
// TestLogSwapShowsStaticLoopInCostForTerminalState preserves terminal cost
// output, including the P2WSH address, once the static loop-in is done.
func TestLogSwapShowsStaticLoopInCostForTerminalState(t *testing.T) {
swap := &looprpc.SwapStatus{
LastUpdateTime: time.Unix(2, 0).UnixNano(),
Type: looprpc.SwapType_STATIC_LOOP_IN,
State: looprpc.SwapState_INITIATED,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_SUCCEEDED,
},
Amt: 50_000,
CostServer: 11,
CostOnchain: 22,
CostOffchain: 33,
HtlcAddressP2Wsh: "bc1qstaticterminaltestaddress",
}
output := captureStdout(t, func() {
logSwap(swap)
})
require.Contains(
t, output,
"STATIC_LOOP_IN SUCCEEDED 0.00050000 BTC - P2WSH: bc1qstaticterminaltestaddress",
)
require.Contains(
t, output,
"(cost: server 11, onchain 22, offchain 33)",
)
}
// TestLogSwapDisplaysStaticLoopInTypeAndStage ensures the monitor output names
// the static loop-in type and active FSM stage instead of collapsing them.
func TestLogSwapDisplaysStaticLoopInTypeAndStage(t *testing.T) {
swap := &looprpc.SwapStatus{
LastUpdateTime: time.Unix(1, 0).UnixNano(),
Type: looprpc.SwapType_STATIC_LOOP_IN,
StaticLoopInStateOptional: &looprpc.SwapStatus_StaticLoopInState{
StaticLoopInState: looprpc.
StaticAddressLoopInSwapState_SIGN_HTLC_TX,
},
Amt: 50_000,
HtlcAddressP2Wsh: "tb1q5cyxnuxmeuwuvkwfem96llyxf8duyshm56t8k8",
}
output := captureStdout(t, func() {
logSwap(swap)
})
require.Contains(
t, output, "STATIC_LOOP_IN SIGN_HTLC_TX 0.00050000 BTC",
)
require.Contains(
t, output, "P2WSH: tb1q5cyxnuxmeuwuvkwfem96llyxf8duyshm56t8k8",
)
require.NotContains(t, output, "STATIC_LOOP_IN INIT_HTLC")
}
func captureStdout(t *testing.T, fn func()) string {
t.Helper()
originalStdout := os.Stdout
reader, writer, err := os.Pipe()
require.NoError(t, err)
os.Stdout = writer
fn()
require.NoError(t, writer.Close())
os.Stdout = originalStdout
var buf bytes.Buffer
_, err = io.Copy(&buf, reader)
require.NoError(t, err)
require.NoError(t, reader.Close())
return strings.TrimSpace(buf.String())
}

View file

@ -7,22 +7,24 @@ import (
"strconv"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightningnetwork/lnd"
lndcommands "github.com/lightningnetwork/lnd/cmd/commands"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/urfave/cli/v3"
)
const (
defaultUtxoMinConf = 1
defaultUtxoMinConf = 1
)
var (
channelTypeTweakless = "tweakless"
channelTypeAnchors = "anchors"
channelTypeSimpleTaproot = "simple-taproot"
channelTypeTaproot = "taproot"
channelTypeSimpleTaproot = "taproot"
)
var openChannelCommand = &cli.Command{
Name: "openchannel",
Usage: "Open a channel to an existing peer.",
Usage: "Open a channel to a an existing peer.",
Description: `
Attempt to open a new channel to an existing peer with the key
node-key.
@ -135,9 +137,9 @@ var openChannelCommand = &cli.Command{
&cli.StringFlag{
Name: "channel_type",
Usage: fmt.Sprintf("(optional) the type of channel to "+
"propose to the remote peer (%q, %q, %q, %q)",
"propose to the remote peer (%q, %q, %q)",
channelTypeTweakless, channelTypeAnchors,
channelTypeSimpleTaproot, channelTypeTaproot),
channelTypeSimpleTaproot),
},
&cli.BoolFlag{
Name: "zero_conf",
@ -158,11 +160,11 @@ var openChannelCommand = &cli.Command{
},
&cli.StringFlag{
Name: "memo",
Usage: "(optional) a note-to-self containing some useful " +
"information about the channel. This is stored " +
"locally only, and is purely for reference. It " +
"has no bearing on the channel's operation. Max " +
"allowed length is 500 characters",
Usage: `(optional) a note-to-self containing some useful
information about the channel. This is stored
locally only, and is purely for reference. It
has no bearing on the channel's operation. Max
allowed length is 500 characters`,
},
&cli.BoolFlag{
Name: "fundmax",
@ -269,7 +271,7 @@ func openChannel(ctx context.Context, cmd *cli.Command) error {
if cmd.IsSet("utxo") {
utxos := cmd.StringSlice("utxo")
outpoints, err := lnd.UtxosToOutpoints(utxos)
outpoints, err := lndcommands.UtxosToOutpoints(utxos)
if err != nil {
return fmt.Errorf("unable to decode utxos: %w", err)
}
@ -320,7 +322,6 @@ func openChannel(ctx context.Context, cmd *cli.Command) error {
switch channelType {
case "":
break
case channelTypeTweakless:
req.CommitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
@ -329,10 +330,6 @@ func openChannel(ctx context.Context, cmd *cli.Command) error {
case channelTypeSimpleTaproot:
req.CommitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
case channelTypeTaproot:
req.CommitmentType = lnrpc.CommitmentType_TAPROOT
default:
return fmt.Errorf("unsupported channel type %v", channelType)
}

View file

@ -206,9 +206,9 @@ func quoteOut(ctx context.Context, cmd *cli.Command) error {
defer cleanup()
fast := cmd.Bool("fast")
swapDeadline := cliClock.Now()
swapDeadline := time.Now()
if !fast {
swapDeadline = cliClock.Now().Add(defaultSwapWaitTime)
swapDeadline = time.Now().Add(defaultSwapWaitTime)
}
quoteReq := &looprpc.QuoteRequest{

View file

@ -1,619 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"slices"
"strconv"
"testing"
"github.com/stretchr/testify/require"
)
// updateRecordedSessionsEnvVar enables replay bless mode for text-only fixture
// updates.
const updateRecordedSessionsEnvVar = "LOOP_UPDATE_RECORDED_SESSIONS"
// replayedSessionOutput captures the user-visible output produced by an
// offline session replay.
type replayedSessionOutput struct {
stdout string
stderr string
stdoutChunks []string
stderrChunks []string
runError *string
}
// updateRecordedSessionsEnabled reports whether replay should bless text-only
// fixture updates.
func updateRecordedSessionsEnabled() (bool, error) {
raw, ok := os.LookupEnv(updateRecordedSessionsEnvVar)
if !ok {
return false, nil
}
enabled, err := strconv.ParseBool(raw)
if err != nil {
return false, fmt.Errorf("invalid %s value %q",
updateRecordedSessionsEnvVar, raw)
}
return enabled, nil
}
// sessionFixturePath resolves a relative fixture path under
// cmd/loop/testdata/sessions to an absolute path.
func sessionFixturePath(rel string) (string, error) {
_, filename, _, ok := runtime.Caller(0)
if !ok {
return "", fmt.Errorf("locate session fixture dir")
}
loopDir := filepath.Dir(filename)
return filepath.Join(
loopDir, "testdata", "sessions", filepath.FromSlash(rel),
), nil
}
// loadSessionFilePath reads and decodes a session fixture from disk.
func loadSessionFilePath(path string) (sessionFile, error) {
blob, err := os.ReadFile(path)
if err != nil {
return sessionFile{}, err
}
var fixture sessionFile
if err := json.Unmarshal(blob, &fixture); err != nil {
return sessionFile{}, err
}
return fixture, nil
}
// writeSessionFilePath writes a session fixture using the recorder's JSON
// formatting.
func writeSessionFilePath(path string, fixture sessionFile) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
return encoder.Encode(fixture)
}
// maybeUpdateSessionFixture rewrites the text-only portions of a recorded
// session if the replay output changed.
func maybeUpdateSessionFixture(path string, fixture sessionFile,
output replayedSessionOutput) (bool, error) {
updated, changed, err := rewriteSessionFixture(fixture, output)
if err != nil || !changed {
return changed, err
}
if err := writeSessionFilePath(path, updated); err != nil {
return false, err
}
return true, nil
}
// rewriteSessionFixture rewrites stdout, stderr and run_error while leaving
// the rest of the recorded interaction unchanged.
func rewriteSessionFixture(fixture sessionFile,
output replayedSessionOutput) (sessionFile, bool, error) {
updated := fixture
changed := false
if !optionalTextEqual(updated.Metadata.RunError, output.runError) {
updated.Metadata.RunError = cloneOptionalString(output.runError)
changed = true
}
var err error
updated.Events, changed, err = rewriteExitEventRunError(
updated.Events, output.runError, changed,
)
if err != nil {
return sessionFile{}, false, err
}
updated.Events, changed, err = rewriteTextEvents(
updated.Events, eventStdout, fixtureStdout(fixture),
output.stdout, output.stdoutChunks, changed,
)
if err != nil {
return sessionFile{}, false, err
}
updated.Events, changed, err = rewriteTextEvents(
updated.Events, eventStderr, fixtureStderr(fixture),
output.stderr, output.stderrChunks, changed,
)
if err != nil {
return sessionFile{}, false, err
}
return updated, changed, nil
}
// rewriteExitEventRunError updates the exit payload to match the replayed run
// error.
func rewriteExitEventRunError(events []sessionEvent, runError *string,
changed bool) ([]sessionEvent, bool, error) {
data, err := marshalSessionJSON(exitPayload{
RunError: cloneOptionalString(runError),
})
if err != nil {
return nil, changed, err
}
updated := append([]sessionEvent(nil), events...)
for i, event := range slices.Backward(updated) {
if event.Kind != eventExit {
continue
}
if bytes.Equal(event.Data, data) {
return updated, changed, nil
}
updated[i].Data = data
return updated, true, nil
}
updated = append(updated, sessionEvent{
Kind: eventExit,
Data: data,
})
return updated, true, nil
}
// rewriteTextEvents updates one text stream when the normalized aggregate text
// changed.
func rewriteTextEvents(events []sessionEvent, kind, recorded, actual string,
chunks []string, changed bool) ([]sessionEvent, bool, error) {
normalizedEqual := normalizeTimestamps(recorded) ==
normalizeTimestamps(actual)
sourceChunks := chunks
sourceCombined := actual
if normalizedEqual {
sourceChunks = fixtureTextChunksByKind(events, kind)
sourceCombined = recorded
}
replacement, err := canonicalTextEvents(
events, kind, sourceChunks, sourceCombined,
)
if err != nil {
return nil, changed, err
}
if normalizedEqual && textEventsMatch(events, kind, replacement) {
return events, changed, nil
}
updated, err := replaceTextEvents(events, kind, replacement)
if err != nil {
return nil, changed, err
}
return updated, true, nil
}
// canonicalTextEvents builds the canonical replacement events for one text
// stream.
func canonicalTextEvents(events []sessionEvent, kind string, chunks []string,
combined string) ([]sessionEvent, error) {
indices := textEventIndices(events, kind)
replacement := replacementTextChunks(chunks, combined)
if len(indices) != len(replacement) {
replacement = replacementTextChunks(nil, combined)
}
return buildReplacementTextEvents(events, indices, kind, replacement)
}
// replaceTextEvents rewrites the recorded events for a text stream using the
// provided canonical replacement events.
func replaceTextEvents(events []sessionEvent, kind string,
replacements []sessionEvent) ([]sessionEvent, error) {
indices := textEventIndices(events, kind)
if len(indices) == 0 && len(replacements) == 0 {
return append([]sessionEvent(nil), events...), nil
}
if replacements == nil {
replacements = []sessionEvent{}
}
return replaceTextEventsWithCanonical(
events, kind, indices, replacements,
)
}
// replaceTextEventsWithCanonical splices canonical replacement events into the
// event stream.
func replaceTextEventsWithCanonical(events []sessionEvent, kind string,
indices []int, replacements []sessionEvent) ([]sessionEvent, error) {
if len(indices) == len(replacements) {
updated := append([]sessionEvent(nil), events...)
for i, idx := range indices {
updated[idx].Data = replacements[i].Data
}
return updated, nil
}
if len(indices) > 0 {
firstIdx := indices[0]
updated := make([]sessionEvent, 0,
len(events)-len(indices)+len(replacements))
inserted := false
for i, event := range events {
if event.Kind == kind {
if !inserted && i == firstIdx {
updated = append(updated, replacements...)
inserted = true
}
continue
}
updated = append(updated, event)
}
return updated, nil
}
if len(replacements) == 0 {
return append([]sessionEvent(nil), events...), nil
}
insertAt := len(events)
for i, event := range events {
if event.Kind == eventExit {
insertAt = i
break
}
}
updated := make([]sessionEvent, 0, len(events)+len(replacements))
updated = append(updated, events[:insertAt]...)
updated = append(updated, replacements...)
updated = append(updated, events[insertAt:]...)
return updated, nil
}
// textEventsMatch reports whether the existing events already use the canonical
// representation for a text stream.
func textEventsMatch(events []sessionEvent, kind string,
replacements []sessionEvent) bool {
indices := textEventIndices(events, kind)
if len(indices) != len(replacements) {
return false
}
for i, idx := range indices {
if events[idx].TimeMS != replacements[i].TimeMS {
return false
}
if !bytes.Equal(events[idx].Data, replacements[i].Data) {
return false
}
}
return true
}
// buildReplacementTextEvents creates replacement text events while reusing the
// closest recorded timestamps when possible.
func buildReplacementTextEvents(events []sessionEvent, indices []int,
kind string, chunks []string) ([]sessionEvent, error) {
if len(chunks) == 0 {
return nil, nil
}
replacements := make([]sessionEvent, 0, len(chunks))
for i, chunk := range chunks {
data, err := marshalSessionJSON(newTextPayload(chunk))
if err != nil {
return nil, err
}
replacements = append(replacements, sessionEvent{
TimeMS: replacementEventTime(events, indices, i),
Kind: kind,
Data: data,
})
}
return replacements, nil
}
// replacementEventTime picks a stable timestamp for a replacement text event.
func replacementEventTime(events []sessionEvent, indices []int, idx int) int64 {
if len(indices) == 0 {
return 0
}
if idx < len(indices) {
return events[indices[idx]].TimeMS
}
return events[indices[len(indices)-1]].TimeMS
}
// textEventIndices returns the indexes of text events with the given kind.
func textEventIndices(events []sessionEvent, kind string) []int {
var indices []int
for i, event := range events {
if event.Kind == kind {
indices = append(indices, i)
}
}
return indices
}
// replacementTextChunks returns the recorded write chunks to persist. If the
// hook callbacks did not observe any chunks, fall back to the aggregate text so
// the fixture still captures the visible output.
func replacementTextChunks(chunks []string, combined string) []string {
if len(chunks) > 0 {
return append([]string(nil), chunks...)
}
if combined == "" {
return nil
}
return []string{combined}
}
// fixtureStdout returns the concatenated stdout text stored in a fixture.
func fixtureStdout(fixture sessionFile) string {
return fixtureTextByKind(fixture.Events, eventStdout)
}
// fixtureStderr returns the concatenated stderr text stored in a fixture.
func fixtureStderr(fixture sessionFile) string {
return fixtureTextByKind(fixture.Events, eventStderr)
}
// fixtureTextByKind concatenates all text payloads for a given event kind.
func fixtureTextByKind(events []sessionEvent, kind string) string {
var out bytes.Buffer
for _, text := range fixtureTextChunksByKind(events, kind) {
out.WriteString(text)
}
return out.String()
}
// fixtureTextChunksByKind returns the decoded text for each event of a given
// kind.
func fixtureTextChunksByKind(events []sessionEvent, kind string) []string {
var chunks []string
for _, event := range events {
if event.Kind != kind {
continue
}
var payload textPayload
if err := json.Unmarshal(event.Data, &payload); err != nil {
continue
}
chunks = append(chunks, payload.text())
}
return chunks
}
// optionalTextEqual compares optional text values using the same timestamp
// normalization rules as replay assertions.
func optionalTextEqual(a, b *string) bool {
switch {
case a == nil && b == nil:
return true
case a == nil || b == nil:
return false
}
return normalizeTimestamps(*a) == normalizeTimestamps(*b)
}
// cloneOptionalString copies an optional string.
func cloneOptionalString(value *string) *string {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
// TestUpdateRecordedSessionsEnabled verifies update-mode env parsing.
func TestUpdateRecordedSessionsEnabled(t *testing.T) {
if current, ok := os.LookupEnv(updateRecordedSessionsEnvVar); ok {
t.Setenv(updateRecordedSessionsEnvVar, current)
} else {
t.Setenv(updateRecordedSessionsEnvVar, "false")
}
_ = os.Unsetenv(updateRecordedSessionsEnvVar)
enabled, err := updateRecordedSessionsEnabled()
require.NoError(t, err)
require.False(t, enabled)
t.Setenv(updateRecordedSessionsEnvVar, "true")
enabled, err = updateRecordedSessionsEnabled()
require.NoError(t, err)
require.True(t, enabled)
t.Setenv(updateRecordedSessionsEnvVar, "invalid")
enabled, err = updateRecordedSessionsEnabled()
require.False(t, enabled)
require.EqualError(t, err,
"invalid LOOP_UPDATE_RECORDED_SESSIONS value \"invalid\"")
}
// TestRewriteSessionFixtureUpdatesMatchingChunks verifies that bless mode
// rewrites text and run errors in place when the write counts are unchanged.
func TestRewriteSessionFixtureUpdatesMatchingChunks(t *testing.T) {
oldErr := "old error"
newErr := "new error"
fixture := sessionFile{
Metadata: sessionMetadata{
RunError: &oldErr,
},
Events: []sessionEvent{
textEvent(t, 5, eventStdout, "old stdout\n"),
textEvent(t, 6, eventStderr, "old stderr\n"),
exitEvent(t, 7, &oldErr),
},
}
updated, changed, err := rewriteSessionFixture(fixture,
replayedSessionOutput{
stdout: "new stdout\n",
stderr: "new stderr\n",
stdoutChunks: []string{"new stdout\n"},
stderrChunks: []string{"new stderr\n"},
runError: &newErr,
},
)
require.NoError(t, err)
require.True(t, changed)
require.Equal(t, &newErr, updated.Metadata.RunError)
require.Equal(t, int64(5), updated.Events[0].TimeMS)
require.Equal(t, "new stdout\n", textEventText(t, updated.Events[0]))
require.Equal(t, int64(6), updated.Events[1].TimeMS)
require.Equal(t, "new stderr\n", textEventText(t, updated.Events[1]))
require.Equal(t, &newErr, exitEventRunError(t, updated.Events[2]))
}
// TestRewriteSessionFixtureReplacesChangedChunkCount verifies that bless mode
// collapses mismatched write counts to one text event to keep fixture diffs
// small.
func TestRewriteSessionFixtureReplacesChangedChunkCount(t *testing.T) {
fixture := sessionFile{
Events: []sessionEvent{
textEvent(t, 10, eventStdout, "old "),
textEvent(t, 11, eventStdout, "text\n"),
exitEvent(t, 12, nil),
},
}
updated, changed, err := rewriteSessionFixture(fixture,
replayedSessionOutput{
stdout: "new output\n",
stdoutChunks: []string{"new ", "output", "\n"},
},
)
require.NoError(t, err)
require.True(t, changed)
require.Len(t, updated.Events, 2)
require.Equal(t, "new output\n", textEventText(t, updated.Events[0]))
require.Equal(t, int64(10), updated.Events[0].TimeMS)
require.Equal(t, eventExit, updated.Events[1].Kind)
}
// TestRewriteSessionFixtureSkipsNormalizedTimestampNoise verifies that bless
// mode does not churn fixtures when only timestamp formatting differs.
func TestRewriteSessionFixtureSkipsNormalizedTimestampNoise(t *testing.T) {
fixture := sessionFile{
Events: []sessionEvent{
textEvent(t, 3, eventStdout,
"2026-05-04T10:00:00-05:00\n"),
exitEvent(t, 4, nil),
},
}
updated, changed, err := rewriteSessionFixture(fixture,
replayedSessionOutput{
stdout: "2026-05-04T15:00:00Z\n",
stdoutChunks: []string{"2026-05-04T15:00:00Z\n"},
},
)
require.NoError(t, err)
require.False(t, changed)
require.Equal(t, fixture, updated)
}
// textEvent builds a text session event for tests.
func textEvent(t *testing.T, timeMS int64, kind, text string) sessionEvent {
t.Helper()
data, err := marshalSessionJSON(newTextPayload(text))
require.NoError(t, err)
return sessionEvent{
TimeMS: timeMS,
Kind: kind,
Data: data,
}
}
// exitEvent builds an exit session event for tests.
func exitEvent(t *testing.T, timeMS int64, runError *string) sessionEvent {
t.Helper()
data, err := marshalSessionJSON(exitPayload{
RunError: cloneOptionalString(runError),
})
require.NoError(t, err)
return sessionEvent{
TimeMS: timeMS,
Kind: eventExit,
Data: data,
}
}
// textEventText decodes a text payload for assertions.
func textEventText(t *testing.T, event sessionEvent) string {
t.Helper()
var payload textPayload
require.NoError(t, json.Unmarshal(event.Data, &payload))
return payload.text()
}
// exitEventRunError decodes an exit payload for assertions.
func exitEventRunError(t *testing.T, event sessionEvent) *string {
t.Helper()
var payload exitPayload
require.NoError(t, json.Unmarshal(event.Data, &payload))
return payload.RunError
}

View file

@ -1,712 +0,0 @@
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/lightninglabs/loop"
"github.com/urfave/cli/v3"
statuspb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
grpcstatus "google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
const (
sessionEnvVar = "LOOP_SESSION_RECORD"
sessionDefaultDir = "cmd/loop/testdata/sessions"
sessionFileExt = ".json"
defaultSessionSlug = "session"
)
// Event identifiers in recorded sessions.
const (
eventStdout = "stdout"
eventStderr = "stderr"
eventStdin = "stdin"
eventGrpc = "grpc"
eventExit = "exit"
eventSignal = "signal"
)
// grpcMarshalOptions is marshalling options to encode gRPC messages in recorded
// sessions.
var grpcMarshalOptions = protojson.MarshalOptions{
UseProtoNames: true,
EmitUnpopulated: true,
}
// marshalSessionJSON encodes nested session payloads without HTML escaping so
// recorded fixture strings stay byte-for-byte close to the CLI text.
func marshalSessionJSON(value any) ([]byte, error) {
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetEscapeHTML(false)
if err := encoder.Encode(value); err != nil {
return nil, err
}
return bytes.TrimSuffix(buf.Bytes(), []byte("\n")), nil
}
// sessionRecorder captures CLI IO and gRPC traffic for replay.
type sessionRecorder struct {
mu sync.Mutex
started time.Time
filePath string
slug string
metadata sessionMetadata
events []sessionEvent
eventErr error
finalizeOnce sync.Once
hooksMu sync.Mutex
hooksStarted bool
stdoutUnhook func() error
stderrUnhook func() error
stdinUnhook func() error
}
// sessionFile is a single recorded CLI session in JSON format. They are
// stored inside sessionDefaultDir.
type sessionFile struct {
Metadata sessionMetadata `json:"metadata"`
Events []sessionEvent `json:"events"`
}
// sessionMetadata stores static session details and runtime metadata.
type sessionMetadata struct {
Args []string `json:"args"`
Env map[string]string `json:"env"`
Version string `json:"version"`
RunError *string `json:"run_error,omitempty"`
Duration *time.Duration `json:"duration,omitempty"`
ClockStartUnix int64 `json:"clock_start_unix"`
}
// sessionEvent records a single timestamped payload entry.
type sessionEvent struct {
TimeMS int64 `json:"time_ms"`
Kind string `json:"kind"`
Data json.RawMessage `json:"data"`
}
// textPayload records stdout/stderr text chunks as newline-preserving lines.
type textPayload struct {
Lines []string `json:"lines"`
}
// newTextPayload splits text into newline-preserving lines for fixture output.
func newTextPayload(text string) textPayload {
return textPayload{
Lines: splitTextLines(text),
}
}
// text joins a recorded text payload back into the original output.
func (p textPayload) text() string {
return joinTextLines(p.Lines)
}
// stdinPayload records stdin chunks.
type stdinPayload struct {
Text string `json:"text"`
}
// splitTextLines preserves line endings while splitting text for fixture
// storage.
func splitTextLines(text string) []string {
if text == "" {
return nil
}
lines := strings.SplitAfter(text, "\n")
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
return lines
}
// joinTextLines reconstructs text stored as newline-preserving lines.
func joinTextLines(lines []string) string {
return strings.Join(lines, "")
}
// grpcPayload records a gRPC message or error event.
type grpcPayload struct {
Method string `json:"method"`
Event string `json:"event"`
MessageType string `json:"message_type,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
Error string `json:"error,omitempty"`
Status *statuspb.Status `json:"status,omitempty"`
}
// exitPayload records the final run error, if any.
type exitPayload struct {
RunError *string `json:"run_error,omitempty"`
}
// signalPayload records handled signals.
type signalPayload struct {
Signal string `json:"signal"`
}
// newSessionRecorder creates a recorder when session recording is enabled.
func newSessionRecorder(args []string) (*sessionRecorder, error) {
// Session recording is disabled unless the env var is set.
envValue, ok := os.LookupEnv(sessionEnvVar)
if !ok {
return nil, nil
}
// Allow explicit disable by setting the env var to false.
enabled, err := strconv.ParseBool(envValue)
if err != nil {
return nil, fmt.Errorf("invalid %s value %q", sessionEnvVar,
envValue)
}
if !enabled {
return nil, nil
}
// Initialize the recorder before collecting metadata.
recorder := &sessionRecorder{
started: time.Now(),
}
// Derive the slug before resolving the output file path.
recorder.slug = deriveSessionSlug(args)
// Capture metadata that remains stable for the session.
metadata := sessionMetadata{
Args: append([]string(nil), args...),
Env: collectSessionEnv(),
Version: loop.RichVersion(),
ClockStartUnix: recorder.started.Unix(),
}
recorder.metadata = metadata
// Resolve the session file location.
baseDir, fileName, err := recorder.resolveFilePath()
if err != nil {
return nil, err
}
recorder.filePath = filepath.Join(baseDir, fileName)
return recorder, nil
}
// collectSessionEnv extracts the environment variables recorded in sessions.
func collectSessionEnv() map[string]string {
env := make(map[string]string)
// Record only LOOPCLI_ variables, excluding the recording toggle.
for _, kv := range os.Environ() {
parts := strings.SplitN(kv, "=", 2)
if len(parts) != 2 {
continue
}
key := parts[0]
value := parts[1]
if key == sessionEnvVar {
continue
}
if strings.HasPrefix(key, "LOOPCLI_") {
env[key] = value
}
}
return env
}
// resolveFilePath chooses the output directory and filename.
func (r *sessionRecorder) resolveFilePath() (string, string, error) {
if err := ensureSessionBaseDir(sessionDefaultDir); err != nil {
return "", "", err
}
counter, err := nextSessionCounter(sessionDefaultDir)
if err != nil {
return "", "", err
}
slug := r.slug
if slug == "" {
slug = defaultSessionSlug
}
name := fmt.Sprintf("%02d_%s%s", counter, slug, sessionFileExt)
return sessionDefaultDir, name, nil
}
// ensureSessionBaseDir verifies that recording is running from the repo root.
func ensureSessionBaseDir(baseDir string) error {
info, err := os.Stat(baseDir)
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("%s does not exist; run session recording "+
"from the repository root", baseDir)
}
if err != nil {
return err
}
if !info.IsDir() {
return fmt.Errorf("%s is not a directory", baseDir)
}
return nil
}
// logEvent records a new event with the elapsed timestamp.
func (r *sessionRecorder) logEvent(kind string, payload any) {
data, err := marshalSessionJSON(payload)
if err != nil {
r.mu.Lock()
if r.eventErr == nil {
r.eventErr = fmt.Errorf("marshal session %s event: %w",
kind, err)
}
r.mu.Unlock()
return
}
event := sessionEvent{
TimeMS: time.Since(r.started).Milliseconds(),
Kind: kind,
Data: data,
}
r.mu.Lock()
defer r.mu.Unlock()
r.events = append(r.events, event)
}
// Start attaches stdin/stdout/stderr hooks for session recording.
func (r *sessionRecorder) Start(stdinSource io.Reader,
stdoutForward, stderrForward io.Writer) error {
r.hooksMu.Lock()
defer r.hooksMu.Unlock()
if r.hooksStarted {
return nil
}
// Capture stdout and stderr first, then stdin.
origStdout := os.Stdout
if stdoutForward == nil {
stdoutForward = origStdout
}
outHook, err := hookStdout(origStdout, stdoutForward, func(p []byte) {
r.logEvent(eventStdout, newTextPayload(string(p)))
})
if err != nil {
return err
}
origStderr := os.Stderr
if stderrForward == nil {
stderrForward = origStderr
}
errHook, err := hookStderr(origStderr, stderrForward, func(p []byte) {
r.logEvent(eventStderr, newTextPayload(string(p)))
})
if err != nil {
_ = outHook()
return err
}
origStdin := os.Stdin
if stdinSource == nil {
stdinSource = origStdin
}
stdinHook, err := hookStdin(origStdin, stdinSource, func(p []byte) {
r.logEvent(eventStdin, stdinPayload{Text: string(p)})
})
if err != nil {
_ = errHook()
_ = outHook()
return err
}
r.stdoutUnhook = outHook
r.stderrUnhook = errHook
r.stdinUnhook = stdinHook
r.hooksStarted = true
return nil
}
// stopHooks detaches any active IO hooks.
func (r *sessionRecorder) stopHooks() error {
r.hooksMu.Lock()
defer r.hooksMu.Unlock()
var firstErr error
if r.stdoutUnhook != nil {
if err := r.stdoutUnhook(); err != nil && firstErr == nil {
firstErr = err
}
r.stdoutUnhook = nil
}
if r.stderrUnhook != nil {
if err := r.stderrUnhook(); err != nil && firstErr == nil {
firstErr = err
}
r.stderrUnhook = nil
}
if r.stdinUnhook != nil {
if err := r.stdinUnhook(); err != nil && firstErr == nil {
firstErr = err
}
r.stdinUnhook = nil
}
r.hooksStarted = false
return firstErr
}
// logExit records the final outcome and duration.
func (r *sessionRecorder) logExit(runErr error) {
var payload exitPayload
if runErr != nil {
msg := runErr.Error()
payload.RunError = &msg
}
// Store the exit event first for the event stream.
r.logEvent(eventExit, payload)
// Update metadata with the final run state.
duration := time.Since(r.started)
r.mu.Lock()
defer r.mu.Unlock()
if runErr != nil {
msg := runErr.Error()
r.metadata.RunError = &msg
} else {
r.metadata.RunError = nil
}
r.metadata.Duration = &duration
}
// finalize writes the recorded session to disk once.
func (r *sessionRecorder) finalize(runErr error) error {
var finalizeErr error
r.finalizeOnce.Do(func() {
hookErr := r.stopHooks()
r.logExit(runErr)
r.mu.Lock()
metadata := r.metadata
events := append([]sessionEvent(nil), r.events...)
eventErr := r.eventErr
r.mu.Unlock()
fileContent := sessionFile{
Metadata: metadata,
Events: events,
}
err := os.MkdirAll(filepath.Dir(r.filePath), 0o755)
if err != nil {
finalizeErr = errors.Join(err, eventErr, hookErr)
return
}
file, err := os.Create(r.filePath)
if err != nil {
finalizeErr = errors.Join(err, eventErr, hookErr)
return
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")
if err := encoder.Encode(fileContent); err != nil {
finalizeErr = errors.Join(err, eventErr, hookErr)
return
}
finalizeErr = errors.Join(eventErr, hookErr)
})
return finalizeErr
}
// Finalize records the exit event and flushes the session to disk.
func (r *sessionRecorder) Finalize(runErr error) error {
return r.finalize(runErr)
}
// ClockStart returns the fixed session clock used while recording CLI actions.
func (r *sessionRecorder) ClockStart() time.Time {
return time.Unix(r.metadata.ClockStartUnix, 0)
}
// Dial uses the direct gRPC connection for recording.
func (r *sessionRecorder) Dial(cmd *cli.Command) (daemonConn, func(), error) {
return dialDirectConn(cmd)
}
// UnaryInterceptor captures unary RPCs for session playback.
func (r *sessionRecorder) UnaryInterceptor() grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply any,
cc *grpc.ClientConn, invoker grpc.UnaryInvoker,
opts ...grpc.CallOption) error {
r.logGRPCMessage(method, "request", req, nil)
err := invoker(ctx, method, req, reply, cc, opts...)
if err != nil {
r.logGRPCMessage(method, "error", nil, err)
return err
}
r.logGRPCMessage(method, "response", reply, nil)
return nil
}
}
// StreamInterceptor captures stream RPCs for session playback.
func (r *sessionRecorder) StreamInterceptor() grpc.StreamClientInterceptor {
return func(ctx context.Context, desc *grpc.StreamDesc,
cc *grpc.ClientConn, method string, streamer grpc.Streamer,
opts ...grpc.CallOption) (grpc.ClientStream, error) {
clientStream, err := streamer(ctx, desc, cc, method, opts...)
if err != nil {
r.logGRPCMessage(method, "error", nil, err)
return nil, err
}
return &recordingClientStream{
ClientStream: clientStream,
recorder: r,
method: method,
}, nil
}
}
// recordingClientStream wraps a gRPC stream and logs message events.
type recordingClientStream struct {
grpc.ClientStream
recorder *sessionRecorder
method string
}
// SendMsg records the outgoing stream message.
func (s *recordingClientStream) SendMsg(m any) error {
s.recorder.logGRPCMessage(s.method, "send", m, nil)
err := s.ClientStream.SendMsg(m)
if err != nil {
s.recorder.logGRPCMessage(s.method, "error", nil, err)
}
return err
}
// RecvMsg records the incoming stream message.
func (s *recordingClientStream) RecvMsg(m any) error {
err := s.ClientStream.RecvMsg(m)
if err != nil {
s.recorder.logGRPCMessage(s.method, "error", nil, err)
return err
}
s.recorder.logGRPCMessage(s.method, "recv", m, nil)
return nil
}
// logGRPCMessage captures gRPC request/response data in the event stream.
func (r *sessionRecorder) logGRPCMessage(method, event string, msg any,
receptionErr error) {
payload := grpcPayload{Method: method, Event: event}
if receptionErr != nil {
payload.Error = receptionErr.Error()
if grpcStatus, ok := grpcstatus.FromError(receptionErr); ok {
payload.Status = grpcStatus.Proto()
}
r.logEvent(eventGrpc, payload)
return
}
if msg != nil {
if protoMsg, ok := msg.(proto.Message); ok {
payload.MessageType = string(
proto.MessageName(protoMsg),
)
data, err := grpcMarshalOptions.Marshal(protoMsg)
if err == nil {
payload.Payload = data
}
} else {
data, err := marshalSessionJSON(msg)
if err == nil {
payload.Payload = data
}
}
}
r.logEvent(eventGrpc, payload)
}
// LogSignal records an incoming signal event.
func (r *sessionRecorder) LogSignal(sig os.Signal) {
r.logEvent(eventSignal, signalPayload{Signal: sig.String()})
}
// InjectContext tags the outgoing context with the session name.
func (r *sessionRecorder) InjectContext(ctx context.Context) context.Context {
return metadata.AppendToOutgoingContext(
ctx, "loop-session", filepath.Base(r.filePath),
)
}
// deriveSessionSlug builds a stable slug from the command arguments.
func deriveSessionSlug(args []string) string {
if len(args) == 0 {
return ""
}
base := filepath.Base(args[0])
tokens := []string{base}
for _, arg := range args[1:] {
if strings.HasPrefix(arg, "-") {
break
}
if arg == "" {
continue
}
tokens = append(tokens, arg)
}
return sanitizeSlug(strings.Join(tokens, "-"))
}
// sanitizeSlug normalizes a session slug to a safe filename.
func sanitizeSlug(value string) string {
value = strings.ToLower(value)
var builder strings.Builder
lastDash := false
for _, r := range value {
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
builder.WriteRune(r)
lastDash = false
continue
}
if !lastDash {
builder.WriteRune('-')
lastDash = true
}
}
slug := strings.Trim(builder.String(), "-")
if slug == "" {
return defaultSessionSlug
}
return slug
}
// nextSessionCounter finds the next available session number.
func nextSessionCounter(baseDir string) (int, error) {
maxCounter := 0
entries, err := os.ReadDir(baseDir)
if errors.Is(err, fs.ErrNotExist) {
return 1, nil
}
if err != nil {
return 0, err
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
if filepath.Ext(entry.Name()) != sessionFileExt {
continue
}
counter, ok := parseSessionCounter(entry.Name())
if !ok {
continue
}
if counter > maxCounter {
maxCounter = counter
}
}
return maxCounter + 1, nil
}
// parseSessionCounter extracts the numeric prefix from a session filename.
func parseSessionCounter(name string) (int, bool) {
base := strings.TrimSuffix(name, filepath.Ext(name))
if base == "" {
return 0, false
}
parts := strings.SplitN(base, "_", 2)
if parts[0] == "" {
return 0, false
}
for _, r := range parts[0] {
if r < '0' || r > '9' {
return 0, false
}
}
value, err := strconv.Atoi(parts[0])
if err != nil {
return 0, false
}
if value < 0 {
return 0, false
}
return value, true
}

View file

@ -1,343 +0,0 @@
package main
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
)
const (
testLoopArg = "loop"
testOutArg = "out"
testLoopOutSlug = "loop-out"
testVersion = "test-version"
testClockStart = int64(1769407086)
)
// TestDeriveSessionSlug verifies slug derivation from CLI arguments.
func TestDeriveSessionSlug(t *testing.T) {
tests := []struct {
name string
args []string
want string
}{
{
name: "empty_args",
want: "",
},
{
name: "binary_only",
args: []string{"/usr/local/bin/loop"},
want: testLoopArg,
},
{
name: "with_subcommand",
args: []string{testLoopArg, testOutArg},
want: testLoopOutSlug,
},
{
name: "stops_at_flag",
args: []string{testLoopArg, testOutArg, "--network", "regtest"},
want: testLoopOutSlug,
},
{
name: "skips_empty_args",
args: []string{testLoopArg, "", "quote", testOutArg},
want: "loop-quote-out",
},
{
name: "sanitizes_tokens",
args: []string{testLoopArg, "Quote", "Out"},
want: "loop-quote-out",
},
{
name: "sanitizes_path_base",
args: []string{"/tmp/loop-cli", testOutArg},
want: "loop-cli-out",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
slug := deriveSessionSlug(test.args)
require.Equal(t, test.want, slug)
})
}
}
// TestSanitizeSlug verifies slug normalization behavior.
func TestSanitizeSlug(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "already_clean",
input: testLoopOutSlug,
want: testLoopOutSlug,
},
{
name: "upper_and_spaces",
input: "Loop Out",
want: testLoopOutSlug,
},
{
name: "symbols_collapsed",
input: "loop@@@out",
want: testLoopOutSlug,
},
{
name: "trims_dashes",
input: "--loop-out--",
want: testLoopOutSlug,
},
{
name: "empty_to_default",
input: "!!!",
want: defaultSessionSlug,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
slug := sanitizeSlug(test.input)
require.Equal(t, test.want, slug)
})
}
}
// TestParseSessionCounter verifies session counter parsing.
func TestParseSessionCounter(t *testing.T) {
tests := []struct {
name string
input string
want int
ok bool
}{
{
name: "with_suffix",
input: "01_loop-out.json",
want: 1,
ok: true,
},
{
name: "with_extra_underscores",
input: "12_loop_in.json",
want: 12,
ok: true,
},
{
name: "no_suffix",
input: "99",
want: 99,
ok: true,
},
{
name: "no_extension",
input: "7_loop-out",
want: 7,
ok: true,
},
{
name: "empty_name",
input: "",
ok: false,
},
{
name: "missing_prefix",
input: "_loop.json",
ok: false,
},
{
name: "non_numeric_prefix",
input: "loop.json",
ok: false,
},
{
name: "mixed_prefix",
input: "1a_loop.json",
ok: false,
},
{
name: "negative_prefix",
input: "-1_loop.json",
ok: false,
},
{
name: "plus_prefix",
input: "+1_loop.json",
ok: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
value, ok := parseSessionCounter(test.input)
require.Equal(t, test.ok, ok)
if test.ok {
require.Equal(t, test.want, value)
}
})
}
}
// TestSessionFinalizeWritesFileWhenHookRestoreFails verifies that session
// data is still written to disk when hook restoration returns an error.
func TestSessionFinalizeWritesFileWhenHookRestoreFails(t *testing.T) {
recorder := &sessionRecorder{
started: time.Now().Add(-time.Second),
filePath: filepath.Join(t.TempDir(), "session.json"),
metadata: sessionMetadata{
Args: []string{testLoopArg, testOutArg},
Env: map[string]string{},
Version: testVersion,
ClockStartUnix: testClockStart,
},
hooksStarted: true,
stdoutUnhook: func() error {
return errors.New("stdout restore failed")
},
}
err := recorder.Finalize(nil)
require.EqualError(t, err, "stdout restore failed")
blob, err := os.ReadFile(recorder.filePath)
require.NoError(t, err)
var data sessionFile
require.NoError(t, json.Unmarshal(blob, &data))
require.Equal(t, recorder.metadata.Args, data.Metadata.Args)
require.Equal(t, recorder.metadata.Version, data.Metadata.Version)
require.NotNil(t, data.Metadata.Duration)
require.Nil(t, data.Metadata.RunError)
require.Len(t, data.Events, 1)
require.Equal(t, eventExit, data.Events[0].Kind)
}
// TestSessionFinalizeReportsWriteAndHookErrors verifies that a write failure
// and hook restoration failure are both returned.
func TestSessionFinalizeReportsWriteAndHookErrors(t *testing.T) {
recorder := &sessionRecorder{
started: time.Now().Add(-time.Second),
filePath: t.TempDir(),
metadata: sessionMetadata{
Args: []string{testLoopArg, testOutArg},
Env: map[string]string{},
Version: testVersion,
ClockStartUnix: testClockStart,
},
hooksStarted: true,
stdoutUnhook: func() error {
return errors.New("stdout restore failed")
},
}
err := recorder.Finalize(nil)
require.ErrorContains(t, err, "is a directory")
require.ErrorContains(t, err, "stdout restore failed")
}
// TestSessionFinalizeReportsDeferredMarshalError verifies that marshal
// failures encountered while recording are reported from Finalize while the
// session file is still written to disk.
func TestSessionFinalizeReportsDeferredMarshalError(t *testing.T) {
recorder := &sessionRecorder{
started: time.Now().Add(-time.Second),
filePath: filepath.Join(t.TempDir(), "session.json"),
metadata: sessionMetadata{
Args: []string{testLoopArg, testOutArg},
Env: map[string]string{},
Version: testVersion,
ClockStartUnix: testClockStart,
},
}
// This logEvent will fail, because chan int is not JSON marshallable.
recorder.logEvent(eventStdout, make(chan int))
err := recorder.Finalize(nil)
require.ErrorContains(t, err, "marshal session stdout event")
require.ErrorContains(t, err, "unsupported type: chan int")
blob, err := os.ReadFile(recorder.filePath)
require.NoError(t, err)
var data sessionFile
require.NoError(t, json.Unmarshal(blob, &data))
require.Equal(t, recorder.metadata.Args, data.Metadata.Args)
require.Equal(t, recorder.metadata.Version, data.Metadata.Version)
require.NotNil(t, data.Metadata.Duration)
require.Nil(t, data.Metadata.RunError)
require.Len(t, data.Events, 1)
require.Equal(t, eventExit, data.Events[0].Kind)
}
// TestNewSessionRecorderRequiresRepoRoot verifies that recording fails clearly
// when the session fixture directory is not available from the current working
// directory.
func TestNewSessionRecorderRequiresRepoRoot(t *testing.T) {
t.Chdir(t.TempDir())
t.Setenv(sessionEnvVar, "true")
recorder, err := newSessionRecorder([]string{testLoopArg, testOutArg})
require.Nil(t, recorder)
require.EqualError(t, err, "cmd/loop/testdata/sessions does not "+
"exist; run session recording from the repository root")
}
// TestNewSessionRecorderCapturesClockStart verifies that new recordings store
// the clock start used for deterministic replay.
func TestNewSessionRecorderCapturesClockStart(t *testing.T) {
repoRoot := t.TempDir()
require.NoError(
t, os.MkdirAll(filepath.Join(repoRoot, sessionDefaultDir), 0o755),
)
t.Chdir(repoRoot)
t.Setenv(sessionEnvVar, "true")
before := time.Now().Unix()
recorder, err := newSessionRecorder([]string{testLoopArg, testOutArg})
after := time.Now().Unix()
require.NoError(t, err)
require.NotNil(t, recorder)
require.GreaterOrEqual(t, recorder.metadata.ClockStartUnix, before)
require.LessOrEqual(t, recorder.metadata.ClockStartUnix, after)
require.Equal(
t, time.Unix(recorder.metadata.ClockStartUnix, 0),
recorder.ClockStart(),
)
}
// TestLogGRPCMessageRecordsStatus verifies that gRPC status errors retain
// their structured status details in recorded sessions.
func TestLogGRPCMessageRecordsStatus(t *testing.T) {
recorder := &sessionRecorder{
started: time.Now(),
}
statusErr := grpcstatus.Error(
codes.FailedPrecondition, "no rules set",
)
recorder.logGRPCMessage(
"/looprpc.SwapClient/SuggestSwaps", "error", nil, statusErr,
)
require.Len(t, recorder.events, 1)
require.Equal(t, eventGrpc, recorder.events[0].Kind)
var payload grpcPayload
require.NoError(t, json.Unmarshal(recorder.events[0].Data, &payload))
require.Equal(t, statusErr.Error(), payload.Error)
require.NotNil(t, payload.Status)
require.Equal(t, int32(codes.FailedPrecondition), payload.Status.Code)
require.Equal(t, "no rules set", payload.Status.Message)
}

File diff suppressed because it is too large Load diff

View file

@ -1,143 +0,0 @@
package main
import (
"io"
"os"
"sync"
)
// hookStdout redirects stdout and returns a function to restore it.
func hookStdout(orig *os.File, forward io.Writer,
onChunk func([]byte)) (func() error, error) {
return hookOutput(
func(f *os.File) { os.Stdout = f },
orig,
forward,
onChunk,
)
}
// hookStderr redirects stderr and returns a function to restore it.
func hookStderr(orig *os.File, forward io.Writer,
onChunk func([]byte)) (func() error, error) {
return hookOutput(
func(f *os.File) { os.Stderr = f },
orig,
forward,
onChunk,
)
}
// hookOutput redirects an output stream and returns a restore function.
func hookOutput(setDest func(*os.File), orig *os.File, forward io.Writer,
onChunk func([]byte)) (func() error, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
setDest(w)
var wg sync.WaitGroup
wg.Go(func() {
defer r.Close()
writer := composeWriter(forward, onChunk)
// Restoring the original descriptor closes the pipe from the
// other side, so io.Copy can fail as part of normal teardown.
_, _ = io.Copy(writer, r)
})
return func() error {
setDest(orig)
_ = w.Close()
wg.Wait()
return nil
}, nil
}
// hookStdin redirects stdin and returns a function to restore it.
func hookStdin(orig *os.File, source io.Reader,
onChunk func([]byte)) (func() error, error) {
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
useOrig := false
if source == nil {
source = orig
useOrig = true
} else if source == orig {
useOrig = true
}
if onChunk != nil {
source = io.TeeReader(source, chunkWriter(onChunk))
}
os.Stdin = r
var wg sync.WaitGroup
wg.Go(func() {
defer w.Close()
// Closing the pipe during hook restoration can terminate the
// copy loop with an expected error.
_, _ = io.Copy(w, source)
})
return func() error {
os.Stdin = orig
_ = r.Close()
_ = w.Close()
// When stdin is still backed by the original terminal, the copy
// goroutine can block indefinitely on user input. In that case
// we rely on closing the pipe or process exit to end the copy
// loop instead of waiting here and hanging the CLI shutdown
// path.
if !useOrig {
wg.Wait()
}
return nil
}, nil
}
// composeWriter builds a writer that forwards to the provided sinks.
func composeWriter(forward io.Writer, onChunk func([]byte)) io.Writer {
switch {
case forward != nil && onChunk != nil:
return io.MultiWriter(forward, chunkWriter(onChunk))
case forward != nil:
return forward
case onChunk != nil:
return chunkWriter(onChunk)
default:
return io.Discard
}
}
// chunkWriter writes copy-safe chunks to a callback.
type chunkWriter func([]byte)
// Write copies p before forwarding to the callback.
func (w chunkWriter) Write(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
copyBuf := make([]byte, len(p))
copy(copyBuf, p)
w(copyBuf)
return len(p), nil
}

View file

@ -1,83 +0,0 @@
package main
import (
"context"
"github.com/urfave/cli/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
)
// grpcTransport customizes gRPC dialing and interceptors for sessions.
type grpcTransport interface {
// Dial returns a gRPC connection for the CLI.
Dial(cmd *cli.Command) (daemonConn, func(), error)
// UnaryInterceptor returns the unary interceptor to apply for session
// flows.
UnaryInterceptor() grpc.UnaryClientInterceptor
// StreamInterceptor returns the stream interceptor to apply for session
// flows.
StreamInterceptor() grpc.StreamClientInterceptor
}
// daemonConn is the client connection interface required by stop/wait flows.
type daemonConn interface {
grpc.ClientConnInterface
// GetState reports the current connectivity state of the client
// channel.
GetState() connectivity.State
// WaitForStateChange blocks until the state changes or the context
// expires.
WaitForStateChange(ctx context.Context,
sourceState connectivity.State) bool
// Connect forces the channel out of idle mode.
Connect()
}
// directGrpcTransport establishes real gRPC connections to loopd.
type directGrpcTransport struct{}
// Dial opens a direct gRPC connection.
func (t *directGrpcTransport) Dial(
cmd *cli.Command) (daemonConn, func(), error) {
return dialDirectConn(cmd)
}
// UnaryInterceptor returns nil because direct connections do not need wrapping.
func (t *directGrpcTransport) UnaryInterceptor() grpc.UnaryClientInterceptor {
return nil
}
// StreamInterceptor returns nil because direct connections do not need
// wrapping.
func (t *directGrpcTransport) StreamInterceptor() grpc.StreamClientInterceptor {
return nil
}
// sessionTransport defines the active gRPC transport for CLI commands.
var sessionTransport grpcTransport = &directGrpcTransport{}
// hookGrpc installs the active gRPC session transport hook.
func hookGrpc(transport grpcTransport) func() {
prev := sessionTransport
sessionTransport = transport
return func() { sessionTransport = prev }
}
// dialDirectConn returns the standard CLI gRPC connection.
func dialDirectConn(cmd *cli.Command) (daemonConn, func(), error) {
rpcServer := cmd.String("rpcserver")
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
if err != nil {
return nil, nil, err
}
return getClientConn(rpcServer, tlsCertPath, macaroonPath)
}

View file

@ -4,17 +4,14 @@ import (
"context"
"errors"
"fmt"
"sort"
"strings"
"github.com/lightninglabs/loop/labels"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/staticaddr/deposit"
"github.com/lightninglabs/loop/staticaddr/loopin"
"github.com/lightninglabs/loop/swapserverrpc"
"github.com/lightningnetwork/lnd"
"github.com/lightningnetwork/lnd/input"
lndcommands "github.com/lightningnetwork/lnd/cmd/commands"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/routing/route"
"github.com/urfave/cli/v3"
)
@ -195,7 +192,7 @@ func withdraw(ctx context.Context, cmd *cli.Command) error {
case isAllSelected:
case isUtxoSelected:
utxos := cmd.StringSlice("utxo")
outpoints, err = lnd.UtxosToOutpoints(utxos)
outpoints, err = lndcommands.UtxosToOutpoints(utxos)
if err != nil {
return err
}
@ -352,7 +349,7 @@ func listWithdrawals(ctx context.Context, cmd *cli.Command) error {
var listStaticAddressSwapsCommand = &cli.Command{
Name: "listswaps",
Usage: "Shows a list of static address swaps.",
Usage: "Shows a list of finalized static address swaps.",
Description: `
`,
Action: listStaticAddressSwaps,
@ -556,7 +553,11 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
allDeposits := depositList.FilteredDeposits
if len(allDeposits) == 0 {
return errors.New("no deposited outputs available")
errString := fmt.Sprintf("no confirmed deposits available, "+
"deposits need at least %v confirmations",
deposit.MinConfs)
return errors.New(errString)
}
var depositOutpoints []string
@ -613,28 +614,6 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
return err
}
// Warn the user if any selected deposits have fewer than 6
// confirmations, as the swap payment won't be received immediately
// for those.
summary, err := client.GetStaticAddressSummary(
ctx, &looprpc.StaticAddressSummaryRequest{},
)
if err != nil {
return err
}
depositsToCheck := warningDepositOutpoints(
allDeposits, depositOutpoints, autoSelectDepositsForQuote,
quoteReq.Amt,
)
warning := lowConfDepositWarning(
allDeposits, depositsToCheck,
int64(summary.RelativeExpiryBlocks),
)
if warning != "" {
fmt.Println(warning)
}
if !(cmd.Bool("force") || cmd.Bool("f")) {
err = displayInDetails(quoteReq, quote, cmd.Bool("verbose"))
if err != nil {
@ -690,162 +669,6 @@ func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
return outpoints
}
var warningSelectionDustLimit = int64(lnwallet.DustLimitForSize(input.P2TRSize))
// warningDepositOutpoints returns the deposit outpoints to check for
// low-confirmation warnings.
func warningDepositOutpoints(allDeposits []*looprpc.Deposit,
selectedOutpoints []string, autoSelect bool, targetAmount int64) []string {
if !autoSelect {
return selectedOutpoints
}
return autoSelectedWarningOutpoints(allDeposits, targetAmount)
}
// autoSelectedWarningOutpoints returns the outpoints selected by the same
// ordering used for automatic static loop-in deposit selection.
func autoSelectedWarningOutpoints(allDeposits []*looprpc.Deposit,
targetAmount int64) []string {
if targetAmount <= 0 {
return nil
}
// KEEP IN SYNC with staticaddr/loopin.SelectDeposits.
deposits := filterSwappableWarningDeposits(allDeposits)
sort.Slice(deposits, func(i, j int) bool {
iConfirmed := deposits[i].ConfirmationHeight > 0
jConfirmed := deposits[j].ConfirmationHeight > 0
if iConfirmed != jConfirmed {
return iConfirmed
}
if deposits[i].Value == deposits[j].Value {
return deposits[i].BlocksUntilExpiry <
deposits[j].BlocksUntilExpiry
}
return deposits[i].Value > deposits[j].Value
})
selectedOutpoints := make([]string, 0, len(deposits))
var selectedAmount int64
for _, deposit := range deposits {
selectedOutpoints = append(selectedOutpoints, deposit.Outpoint)
selectedAmount += deposit.Value
if selectedAmount == targetAmount {
return selectedOutpoints
}
if selectedAmount > targetAmount &&
selectedAmount-targetAmount >= warningSelectionDustLimit {
return selectedOutpoints
}
}
return nil
}
// filterSwappableWarningDeposits filters deposits for CLI warning selection.
func filterSwappableWarningDeposits(
allDeposits []*looprpc.Deposit) []*looprpc.Deposit {
swappable := make([]*looprpc.Deposit, 0, len(allDeposits))
minBlocksUntilExpiry := int64(
loopin.DefaultLoopInOnChainCltvDelta + loopin.DepositHtlcDelta,
)
for _, deposit := range allDeposits {
// Unconfirmed deposits remain swappable because their CSV timeout has
// not started yet. This mirrors loopin.IsSwappable.
if deposit.ConfirmationHeight > 0 &&
deposit.BlocksUntilExpiry < minBlocksUntilExpiry {
continue
}
swappable = append(swappable, deposit)
}
return swappable
}
// conservativeWarningConfs is the highest default confirmation tier used by
// the server's dynamic confirmation-risk policy.
//
// The CLI does not currently know the server's exact policy, so we use this
// conservative threshold for warnings without promising immediate execution.
const conservativeWarningConfs = 6
// lowConfDepositWarning checks the selected deposits against a conservative
// confirmation threshold and returns a warning string if any are found.
func lowConfDepositWarning(allDeposits []*looprpc.Deposit,
selectedOutpoints []string, csvExpiry int64) string {
depositMap := make(map[string]*looprpc.Deposit, len(allDeposits))
for _, d := range allDeposits {
depositMap[d.Outpoint] = d
}
var lowConfEntries []string
for _, op := range selectedOutpoints {
d, ok := depositMap[op]
if !ok {
continue
}
var confs int64
switch {
case d.ConfirmationHeight <= 0:
confs = 0
case csvExpiry > 0:
// For confirmed deposits we can compute
// confirmations as CSVExpiry - BlocksUntilExpiry + 1.
confs = csvExpiry - d.BlocksUntilExpiry + 1
default:
// Can't determine confirmations without the CSV expiry.
continue
}
if confs >= conservativeWarningConfs {
continue
}
if confs == 0 {
lowConfEntries = append(
lowConfEntries,
fmt.Sprintf(" - %s (unconfirmed)", op),
)
} else {
lowConfEntries = append(
lowConfEntries,
fmt.Sprintf(
" - %s (%d confirmations)", op,
confs,
),
)
}
}
if len(lowConfEntries) == 0 {
return ""
}
return fmt.Sprintf(
"\nWARNING: The following deposits are below the "+
"conservative %d-confirmation threshold:\n%s\n"+
"The swap payment for these deposits may wait for "+
"more confirmations depending on the server's "+
"confirmation-risk policy.\n",
conservativeWarningConfs,
strings.Join(lowConfEntries, "\n"),
)
}
func displayNewAddressWarning() error {
fmt.Printf("\nWARNING: Be aware that loosing your l402.token file in " +
".loop under your home directory will take your ability to " +

View file

@ -1,220 +0,0 @@
package main
import (
"strings"
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/looprpc"
"github.com/lightninglabs/loop/staticaddr/deposit"
"github.com/lightninglabs/loop/staticaddr/loopin"
"github.com/stretchr/testify/require"
)
// TestLowConfDepositWarningConfirmedOnly verifies confirmed deposits below the
// conservative warning threshold are included in the warning text.
func TestLowConfDepositWarningConfirmedOnly(t *testing.T) {
t.Parallel()
deposits := []*looprpc.Deposit{
{
Outpoint: "confirmed-low",
ConfirmationHeight: 100,
BlocksUntilExpiry: 140,
},
{
Outpoint: "confirmed-high",
ConfirmationHeight: 95,
BlocksUntilExpiry: 139,
},
}
warning := lowConfDepositWarning(
deposits, []string{"confirmed-low", "confirmed-high"}, 144,
)
require.Contains(t, warning, "confirmed-low (5 confirmations)")
require.NotContains(t, warning, "confirmed-high")
}
// TestLowConfDepositWarningUnconfirmed verifies unconfirmed deposits get a
// warning that the swap may wait for confirmation-risk acceptance.
func TestLowConfDepositWarningUnconfirmed(t *testing.T) {
t.Parallel()
deposits := []*looprpc.Deposit{
{
Outpoint: "mempool",
ConfirmationHeight: 0,
BlocksUntilExpiry: 144,
},
}
warning := lowConfDepositWarning(deposits, []string{"mempool"}, 144)
require.Contains(t, warning, "mempool (unconfirmed)")
require.True(
t,
strings.Contains(
warning,
"conservative 6-confirmation threshold",
),
)
require.NotContains(t, warning, "executed immediately")
}
// TestWarningDepositOutpointsAutoSelectPrefersConfirmed verifies automatic
// warning selection keeps the loop-in preference for confirmed outputs.
func TestWarningDepositOutpointsAutoSelectPrefersConfirmed(t *testing.T) {
t.Parallel()
const csvExpiry = 1100
deposits := []*looprpc.Deposit{
{
Outpoint: "mempool-large",
Value: 2_000_000,
ConfirmationHeight: 0,
BlocksUntilExpiry: csvExpiry,
},
{
Outpoint: "confirmed",
Value: 1_500_000,
ConfirmationHeight: 100,
BlocksUntilExpiry: csvExpiry - 5,
},
}
selected := warningDepositOutpoints(deposits, nil, true, 1_000_000)
require.Equal(t, []string{"confirmed"}, selected)
require.Empty(t, lowConfDepositWarning(deposits, selected, csvExpiry))
}
// TestWarningDepositOutpointsAutoSelectIncludesNeededUnconfirmed verifies the
// warning path includes mempool deposits when they are needed for the target.
func TestWarningDepositOutpointsAutoSelectIncludesNeededUnconfirmed(t *testing.T) {
t.Parallel()
const csvExpiry = 1100
deposits := []*looprpc.Deposit{
{
Outpoint: "confirmed-small",
Value: 500_000,
ConfirmationHeight: 100,
BlocksUntilExpiry: csvExpiry - 5,
},
{
Outpoint: "mempool-large",
Value: 2_000_000,
ConfirmationHeight: 0,
BlocksUntilExpiry: csvExpiry,
},
}
selected := warningDepositOutpoints(deposits, nil, true, 1_000_000)
require.Equal(
t, []string{"confirmed-small", "mempool-large"}, selected,
)
warning := lowConfDepositWarning(deposits, selected, csvExpiry)
require.Contains(t, warning, "mempool-large (unconfirmed)")
require.NotContains(t, warning, "confirmed-small")
}
// TestWarningDepositSelectionMatchesLoopInSelection verifies CLI warning
// selection matches the loop-in selector.
func TestWarningDepositSelectionMatchesLoopInSelection(t *testing.T) {
t.Parallel()
const (
blockHeight = uint32(10_000)
csvExpiry = uint32(1_200)
targetAmount = int64(2_500_000)
)
type fixture struct {
name string
value int64
confirmationHeight int64
}
fixtures := []fixture{
{
name: "mempool-huge",
value: 3_000_000,
confirmationHeight: 0,
},
{
name: "confirmed-later-expiry",
value: 2_000_000,
confirmationHeight: 9_900,
},
{
name: "confirmed-earlier-expiry",
value: 2_000_000,
confirmationHeight: 9_890,
},
{
name: "confirmed-small",
value: 600_000,
confirmationHeight: 9_900,
},
{
name: "confirmed-too-close-to-expiry",
value: 5_000_000,
confirmationHeight: 9_849,
},
}
rpcDeposits := make([]*looprpc.Deposit, 0, len(fixtures))
loopInDeposits := make([]*deposit.Deposit, 0, len(fixtures))
for idx, fixture := range fixtures {
hash := chainhash.Hash{byte(idx + 1)}
outpoint := wire.OutPoint{
Hash: hash,
Index: uint32(idx),
}
blocksUntilExpiry := int64(0)
if fixture.confirmationHeight > 0 {
blocksUntilExpiry = fixture.confirmationHeight +
int64(csvExpiry) - int64(blockHeight)
}
rpcDeposits = append(rpcDeposits, &looprpc.Deposit{
Outpoint: outpoint.String(),
Value: fixture.value,
ConfirmationHeight: fixture.confirmationHeight,
BlocksUntilExpiry: blocksUntilExpiry,
})
loopInDeposits = append(loopInDeposits, &deposit.Deposit{
OutPoint: outpoint,
Value: btcutil.Amount(fixture.value),
ConfirmationHeight: fixture.confirmationHeight,
})
}
cliSelected := autoSelectedWarningOutpoints(
rpcDeposits, targetAmount,
)
loopInSelected, err := loopin.SelectDeposits(
btcutil.Amount(targetAmount), loopInDeposits, csvExpiry,
blockHeight,
)
require.NoError(t, err)
loopInSelectedOutpoints := make([]string, 0, len(loopInSelected))
for _, selected := range loopInSelected {
loopInSelectedOutpoints = append(
loopInSelectedOutpoints, selected.OutPoint.String(),
)
}
require.Equal(t, loopInSelectedOutpoints, cliSelected)
}

View file

@ -6,6 +6,7 @@ import (
"github.com/lightninglabs/loop/looprpc"
"github.com/urfave/cli/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
)
@ -62,7 +63,7 @@ func stopDaemon(ctx context.Context, cmd *cli.Command) error {
// waitForDaemonShutdown monitors the gRPC connectivity state until the daemon
// disappears. To avoid getting stuck in idle mode we nudge the connection to
// reconnect when needed.
func waitForDaemonShutdown(ctx context.Context, conn daemonConn) error {
func waitForDaemonShutdown(ctx context.Context, conn *grpc.ClientConn) error {
for {
state := conn.GetState()

View file

@ -15,10 +15,9 @@ import (
var listSwapsCommand = &cli.Command{
Name: "listswaps",
Usage: "list traditional Loop In and Loop Out swaps",
Description: "Lists traditional Loop In and Loop Out swaps that are " +
"currently stored in the database. Static address loop-ins are not " +
"included; use `loop static listswaps` to view them.",
Usage: "list all swaps in the local database",
Description: "Allows the user to get a list of all swaps that are " +
"currently stored in the database",
Action: listSwaps,
Flags: []cli.Flag{
&cli.BoolFlag{
@ -129,12 +128,10 @@ func listSwaps(ctx context.Context, cmd *cli.Command) error {
var swapInfoCommand = &cli.Command{
Name: "swapinfo",
Usage: "show the status of a traditional swap",
Usage: "show the status of a swap",
ArgsUsage: "id",
Description: "Shows the status of a traditional Loop In or Loop Out " +
"swap currently stored in the database. Static address loop-ins " +
"must be viewed with `loop static listswaps`; there is no generic " +
"per-swap static lookup command.",
Description: "Allows the user to get the status of a single swap " +
"currently stored in the database",
Flags: []cli.Flag{
&cli.Uint64Flag{
Name: "id",
@ -209,6 +206,9 @@ func abandonSwap(ctx context.Context, cmd *cli.Command) error {
var id string
switch {
case cmd.IsSet("id"):
id = cmd.String("id")
case cmd.NArg() > 0:
id = args.First()

View file

@ -1,81 +0,0 @@
# Session Recording Notes (Loop CLI)
## How to record sessions
- Use the local CLI binary: `/home/user/bin/loop`.
- Always include `--network regtest` **after** the main command and subcommands so it does not become part of the session filename.
- Record with `LOOP_SESSION_RECORD=true`, e.g.:
- `LOOP_SESSION_RECORD=true /home/user/bin/loop quote out --network regtest 500000`
- After recording several related commands, group the resulting JSON files into a subdir under `cmd/loop/testdata/sessions/`.
## Control panel HTTP server (regtest helpers)
Base URL: `http://127.0.0.1:12345`
- Implementation: [httpcmd](https://github.com/starius/httpcmd/)
- Config: [control panel config](https://gist.github.com/starius/6604ffe27f51d55f4cf715b4202637dd)
- `/mine`:
- Mines a block. Returns JSON with `exit_code`, `stdout`, `stderr`, `duration`, `timed_out`.
- `/deposit`:
- Sends a deposit to the client static address. Returns JSON with a `txid` on success.
- May fail with insufficient funds unless blocks have been mined first.
- `/reservation`:
- Opens a reservation for instant out. Returns JSON with reservation details (id, amount, state, etc.).
- `/loop-log`:
- Returns loopd logs (text). Use a tail filter when inspecting.
- `/loop-server-log`:
- Returns loop server logs (text). Use a tail filter when inspecting.
- Tip: The response is JSON with a large `stdout` field; extract first, then tail:
- `curl -s http://127.0.0.1:12345/loop-server-log | jq -r '.stdout' > /tmp/loop-server-log.txt`
- `tail -n 50 /tmp/loop-server-log.txt`
## Useful regtest flow observations
- Deposits may be unconfirmed for a few blocks; mine multiple blocks to move a deposit into `DEPOSITED`.
- After making a deposit, `loop static summary` reflects unconfirmed + confirmed values; `loop static listdeposits` lists confirmed deposits.
- Reservations should be opened (via `/reservation`) before `instantout`; list them with `loop reservations list`.
- `instantout` uses interactive selection; `ALL` then `y` works for a simple scenario:
- `printf "ALL\ny\n" | LOOP_SESSION_RECORD=true /home/user/bin/loop instantout --network regtest`
## Coverage notes (high-level)
- Sessions were recorded for: terms, getinfo, quote in/out, listauth, fetchl402, getparams, setrule (error + success), suggestswaps (error + success), reservations list, instantout, listinstantouts, static withdraw/listwithdrawals/listswaps, listswaps, swapinfo, loop out (forced), abandon swap (help path), plus existing static-loop-in/basic-swaps.
- Some paths are intentionally skipped for now:
- `stop` (would shut down loopd; replay expects a real gRPC conn).
- Asset quote paths (`getAssetAmt`, `unmarshalFixedPoint`) require tapd/asset quotes.
- Real dialer/macaroon path handling (`extractPathArgs`, `readMacaroon`, `getClientConn`) arent exercised by replay.
## Replay stability notes
- Session replay now clones the CLI command tree per run, so flag state (`IsSet`) does not leak between sessions.
- Historical warning: earlier replays could have sticky flags across runs; if you see odd ordering-dependent failures, re-check that the replay uses the cloned command path.
## Bless mode for CLI text changes
- Use `LOOP_UPDATE_RECORDED_SESSIONS=true` to let `TestRecordedSessions` rewrite recorded `stdout`, `stderr`, and `run_error` values after a successful offline replay.
- Always run bless mode with `-count=1` so the Go test cache does not skip the update:
- `LOOP_UPDATE_RECORDED_SESSIONS=true go test ./cmd/loop -run TestRecordedSessions -count=1 -v`
- You can target a narrower subset with `-run`, for example:
- `LOOP_UPDATE_RECORDED_SESSIONS=true go test ./cmd/loop -run 'TestRecordedSessions/static-openchannel' -count=1 -v`
- Bless mode is intentionally narrow:
- it reuses the recorded gRPC stream, stdin, and env,
- it refuses to bless a session if the command changes from success to failure or vice versa,
- and it refuses to bless a session if replay no longer consumes the same recorded gRPC interaction.
- Use live recording, not bless mode, when a commands behavior or RPC flow changed.
## Session coverage map
| Subdir | Commands / scenarios |
| --- | --- |
| `basic-swaps/` | `loop out` (success), `loop in` (success), `loop monitor` |
| `getinfo/` | `loop getinfo` |
| `instantout/` | `loop reservations list`, `loop instantout` (ALL + confirm), `loop instantout` (channel flag), `loop instantout` (select index), `loop instantout` (no confirmed reservations), `loop instantout` (cancel), `loop instantout` (invalid selection), `loop listinstantouts` |
| `l402/` | `loop listauth`, `loop fetchl402` |
| `liquidity/` | `loop getparams`, `loop setparams` (no flags, feepercent, conflict, many flags/categories, destaddr, account, includeallpeers; includes error cases), `loop setrule` (missing threshold, no args, incoming/outgoing/type in/out, clear), `loop suggestswaps` (error + success) |
| `loopin/` | `loop in` (invalid amount), `loop in` (external + conf_target error), `loop in` (route_hints + private error), `loop in` (external cancel + verbose, last_hop/amt flag), `loop in` (external force) |
| `loopout/` | `loop out` (forced success), `loop out` (invalid amount), `loop out` (addr + account error), `loop out` (invalid account address type), `loop out` (amt flag + channel + max routing fee + payment timeout), `loop out` (addr flag), `loop out` (positional addr), `loop out` (account + account_addr_type) |
| `misc/` | `loop terms` |
| `quote/` | `loop quote out` (success + verbose), `loop quote in` (help + verbose), `loop quote out` (help), `loop quote in` (deposit_outpoint success), `loop quote in` (positional + last_hop) |
| `static/` | `loop static withdraw` (no selection error), `loop static withdraw` (invalid utxo), `loop static withdraw` (all success), `loop static withdraw` (utxo + dest_addr success), `loop static listwithdrawals`, `loop static listswaps` |
| `static-autoloop/` | `loop setparams --loopinsource static-address` (success + no-experimental error), `loop getparams` (static-address loop-in source), `loop suggestswaps` (static loop-in suggestion) |
| `static-loop-in/` | `loop static new`, `loop static` (help), `loop static listunspent` (incl alias), `loop static listdeposits`, `loop static summary`, `loop static in` (multiple args/flags cases), `loop static in` (duplicate outpoints), `loop static in` (positional low amount error), `loop static in` (positional + last_hop + payment_timeout), `loop static in` (all cancel), `loop static in` (explicit and automatically selected low-confirmation warnings) |
| `static-filters/` | `loop static listdeposits --filter ...` for each state (deposited/withdrawing/withdrawn/looping_in/looped_in/publish_expired_deposit/sweep_htlc_timeout/htlc_timeout_swept/wait_for_expiry_sweep/expired/failed) |
| `swaps/` | `loop listswaps` (success + conflicting filters + loop_out_only filters + loop_in_only), `loop swapinfo` (success + invalid id + id flag errors), `loop abandonswap` (help + invalid id + success) |
## Missing / not covered yet
- `loop stop` (would terminate loopd; replay expects a live gRPC server).
- Docs generators: `loop man`, `loop markdown` (handled in a separate docs pipeline).
- Asset/tapd scenarios (explicitly out of scope for now).

View file

@ -1,146 +0,0 @@
{
"metadata": {
"args": [
"loop",
"out",
"--network",
"regtest",
"500000",
"--fast"
],
"env": {},
"version": "0.31.7-beta commit=vbump-lndclient-64-g3b735173216470e023b3b72949dd82af9adbebe1 commit_hash=3b735173216470e023b3b72949dd82af9adbebe1",
"duration": 1332472129,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769407086",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 22,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3875",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3793",
"swap_payment_dest": "AhbcGtFvFiW2ru6gxtm66Xv9DpxBdSBXK4a7EX7jbSWd",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 23,
"kind": "stdout",
"data": {
"lines": [
"Send off-chain: 500000 sat\n",
"Receive on-chain: 492332 sat\n",
"Estimated total fee: 7668 sat\n",
"\n",
"Fast swap requested.\n",
"\n",
"CONTINUE SWAP? (y/n): "
]
}
},
{
"time_ms": 1240,
"kind": "stdin",
"data": {
"text": "y\n"
}
},
{
"time_ms": 1241,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "",
"max_swap_routing_fee": "10010",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3875",
"max_prepay_amt": "1337",
"max_miner_fee": "948250",
"loop_out_channel": "0",
"outgoing_chan_set": [],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769407086",
"label": "",
"initiator": "loop-cli",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"is_external_addr": false,
"reservation_ids": [],
"payment_timeout": 0,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 1327,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "e5f3a1d5cee33e239571d0c1e8cbc6926cbc1d784b9fe185ff6589e949db4549",
"id_bytes": "5fOh1c7jPiOVcdDB6MvGkmy8HXhLn+GF/2WJ6UnbRUk=",
"htlc_address": "bcrt1p69ekm32epfcy8ymkqdj5clvh37vmlgw76k9x5nfs76c7qgghnpqqmtf80x",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1p69ekm32epfcy8ymkqdj5clvh37vmlgw76k9x5nfs76c7qgghnpqqmtf80x",
"server_message": ""
}
}
},
{
"time_ms": 1329,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: e5f3a1d5cee33e239571d0c1e8cbc6926cbc1d784b9fe185ff6589e949db4549\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 1332,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,131 +0,0 @@
{
"metadata": {
"args": [
"loop",
"in",
"--network",
"regtest",
"500000"
],
"env": {},
"version": "0.31.7-beta commit=vbump-lndclient-69-g057ef82340ab279a5659ab6ae7edab61448a2acd commit_hash=057ef82340ab279a5659ab6ae7edab61448a2acd",
"duration": 2146386627,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 5,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 0,
"external_htlc": false,
"swap_publication_deadline": "0",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 444,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1825",
"htlc_publish_fee_sat": "3875",
"cltv_delta": 0,
"conf_target": 6,
"quoted_amt": "500000"
}
}
},
{
"time_ms": 445,
"kind": "stdout",
"data": {
"lines": [
"Send on-chain: 500000 sat\n",
"Receive off-chain: 494300 sat\n",
"Estimated total fee: 5700 sat\n",
"\n",
"CONTINUE SWAP? (y/n): "
]
}
},
{
"time_ms": 1682,
"kind": "stdin",
"data": {
"text": "y\n"
}
},
{
"time_ms": 1682,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopIn",
"event": "request",
"message_type": "looprpc.LoopInRequest",
"payload": {
"amt": "500000",
"max_swap_fee": "1825",
"max_miner_fee": "11625",
"last_hop": "",
"external_htlc": false,
"htlc_conf_target": 0,
"label": "",
"initiator": "loop-cli",
"route_hints": [],
"private": false
}
}
},
{
"time_ms": 2140,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopIn",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "33b431ce62ab7f8f3ff5e2aee9452ec9aa18713e734ecd79fc9cfb4bd77cc7d8",
"id_bytes": "M7QxzmKrf48/9eKu6UUuyaoYcT5zTs15/Jz7S9d8x9g=",
"htlc_address": "bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu",
"server_message": ""
}
}
},
{
"time_ms": 2141,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: 33b431ce62ab7f8f3ff5e2aee9452ec9aa18713e734ecd79fc9cfb4bd77cc7d8\n",
"HTLC address (P2TR): bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 2146,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,428 +0,0 @@
{
"metadata": {
"args": [
"loop",
"monitor",
"--network",
"regtest"
],
"env": {},
"version": "0.31.7-beta commit=vbump-lndclient-70-g352a68cd43f1976a937faaf76041bc078fdd16f6 commit_hash=352a68cd43f1976a937faaf76041bc078fdd16f6",
"run_error": "recv: rpc error: code = Canceled desc = context canceled",
"duration": 58532880576,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 8,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "send",
"message_type": "looprpc.MonitorRequest",
"payload": {}
}
},
{
"time_ms": 8,
"kind": "stdout",
"data": {
"lines": [
"Note: offchain cost may report as 0 after loopd restart during swap\n"
]
}
},
{
"time_ms": 13,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "33b431ce62ab7f8f3ff5e2aee9452ec9aa18713e734ecd79fc9cfb4bd77cc7d8",
"id_bytes": "M7QxzmKrf48/9eKu6UUuyaoYcT5zTs15/Jz7S9d8x9g=",
"type": "LOOP_IN",
"state": "INVOICE_SETTLED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769413263544127799",
"last_update_time": "1769413312288899857",
"htlc_address": "bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu",
"cost_server": "0",
"cost_onchain": "3850",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 13,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "4a8f1dd97addf8222c7613f6a41e3942f4bf9ca64026107c484c1f7e551df658",
"id_bytes": "So8d2Xrd+CIsdhP2pB45QvS/nKZAJhB8SEwfflUd9lg=",
"type": "LOOP_OUT",
"state": "SUCCESS",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769407041704702192",
"last_update_time": "1769413318432807159",
"htlc_address": "bcrt1pmvvj28wnuwe70xfy6kjzwxcg7cx0kc2dy8l6armutegs8y3p0j7qg0atua",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pmvvj28wnuwe70xfy6kjzwxcg7cx0kc2dy8l6armutegs8y3p0j7qg0atua",
"cost_server": "3875",
"cost_onchain": "2775",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 14,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "e5f3a1d5cee33e239571d0c1e8cbc6926cbc1d784b9fe185ff6589e949db4549",
"id_bytes": "5fOh1c7jPiOVcdDB6MvGkmy8HXhLn+GF/2WJ6UnbRUk=",
"type": "LOOP_OUT",
"state": "SUCCESS",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769407087874686997",
"last_update_time": "1769413322775449292",
"htlc_address": "bcrt1p69ekm32epfcy8ymkqdj5clvh37vmlgw76k9x5nfs76c7qgghnpqqmtf80x",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1p69ekm32epfcy8ymkqdj5clvh37vmlgw76k9x5nfs76c7qgghnpqqmtf80x",
"cost_server": "3875",
"cost_onchain": "2775",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 14,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "39424d7e6cf6d9c613c4026f5e6da48fd2b65989007895a0db7b937b7f8563e4",
"id_bytes": "OUJNfmz22cYTxAJvXm2kj9K2WYkAeJWg23uTe3+FY+Q=",
"type": "LOOP_OUT",
"state": "SUCCESS",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769413204269210604",
"last_update_time": "1769413329442159169",
"htlc_address": "bcrt1prz80rm04fhf5y73ps9txxslnhj8e3nqcsnmz6ca6vs6jkrce2l9svtd8sl",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1prz80rm04fhf5y73ps9txxslnhj8e3nqcsnmz6ca6vs6jkrce2l9svtd8sl",
"cost_server": "3875",
"cost_onchain": "2774",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 14,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T02:41:52-05:00 LOOP_IN INVOICE_SETTLED 0.00500000 BTC - P2TR: bcrt1pmtw7fch8de39wxfqunuucudgulkvyxm59epz9p0ph673squ7l2ns3gpcsu (cost: server 0, onchain 3850, offchain 0)\n",
"2026-01-26T02:41:58-05:00 LOOP_OUT SUCCESS 0.00500000 BTC - (cost: server 3875, onchain 2775, offchain 0)\n",
"2026-01-26T02:42:02-05:00 LOOP_OUT SUCCESS 0.00500000 BTC - (cost: server 3875, onchain 2775, offchain 0)\n",
"2026-01-26T02:42:09-05:00 LOOP_OUT SUCCESS 0.00500000 BTC - (cost: server 3875, onchain 2774, offchain 0)\n"
]
}
},
{
"time_ms": 19978,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "13da0be986d8a1f095794f78c5e716150e93c92834e3be52f6eb2fbfc84b8eb0",
"id_bytes": "E9oL6YbYofCVeU94xecWFQ6TySg0475S9usvv8hLjrA=",
"type": "LOOP_OUT",
"state": "INITIATED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414430311530071",
"last_update_time": "1769414430311530071",
"htlc_address": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 19978,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:30-05:00 LOOP_OUT INITIATED 0.00500000 BTC - \n"
]
}
},
{
"time_ms": 32802,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "3f4ec343ba4087eec3cdf501b4e19733d9685cc151db360bb67d2c924677ce71",
"id_bytes": "P07DQ7pAh+7DzfUBtOGXM9loXMFR2zYLtn0skkZ3znE=",
"type": "LOOP_IN",
"state": "INITIATED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414443188873986",
"last_update_time": "1769414443188873986",
"htlc_address": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 32802,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:43-05:00 LOOP_IN INITIATED 0.00500000 BTC - P2TR: bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch\n"
]
}
},
{
"time_ms": 32829,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "3f4ec343ba4087eec3cdf501b4e19733d9685cc151db360bb67d2c924677ce71",
"id_bytes": "P07DQ7pAh+7DzfUBtOGXM9loXMFR2zYLtn0skkZ3znE=",
"type": "LOOP_IN",
"state": "HTLC_PUBLISHED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414443188873986",
"last_update_time": "1769414443212089572",
"htlc_address": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 32829,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:43-05:00 LOOP_IN HTLC_PUBLISHED 0.00500000 BTC - P2TR: bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch\n"
]
}
},
{
"time_ms": 39513,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "3f4ec343ba4087eec3cdf501b4e19733d9685cc151db360bb67d2c924677ce71",
"id_bytes": "P07DQ7pAh+7DzfUBtOGXM9loXMFR2zYLtn0skkZ3znE=",
"type": "LOOP_IN",
"state": "INVOICE_SETTLED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414443188873986",
"last_update_time": "1769414449902523194",
"htlc_address": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch",
"cost_server": "0",
"cost_onchain": "3850",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 39513,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:49-05:00 LOOP_IN INVOICE_SETTLED 0.00500000 BTC - P2TR: bcrt1ppuw9wtl65avd026t9nkxux8whz3gyhkjh6n9cryp8a5xz9jacf9qlgxsch (cost: server 0, onchain 3850, offchain 0)\n"
]
}
},
{
"time_ms": 40341,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "13da0be986d8a1f095794f78c5e716150e93c92834e3be52f6eb2fbfc84b8eb0",
"id_bytes": "E9oL6YbYofCVeU94xecWFQ6TySg0475S9usvv8hLjrA=",
"type": "LOOP_OUT",
"state": "PREIMAGE_REVEALED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414430311530071",
"last_update_time": "1769414450735780318",
"htlc_address": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 40341,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:50-05:00 LOOP_OUT PREIMAGE_REVEALED 0.00500000 BTC - \n"
]
}
},
{
"time_ms": 44110,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "13da0be986d8a1f095794f78c5e716150e93c92834e3be52f6eb2fbfc84b8eb0",
"id_bytes": "E9oL6YbYofCVeU94xecWFQ6TySg0475S9usvv8hLjrA=",
"type": "LOOP_OUT",
"state": "SUCCESS",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1769414430311530071",
"last_update_time": "1769414454503356191",
"htlc_address": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1ps8vvwey05ezrmhe4mpz60nre256ggl2uyazs7fayh83z7ptnclnstaxvu2",
"cost_server": "3873",
"cost_onchain": "2774",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 44110,
"kind": "stdout",
"data": {
"lines": [
"2026-01-26T03:00:54-05:00 LOOP_OUT SUCCESS 0.00500000 BTC - (cost: server 3873, onchain 2774, offchain 0)\n"
]
}
},
{
"time_ms": 58531,
"kind": "signal",
"data": {
"signal": "interrupt"
}
},
{
"time_ms": 58531,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "error",
"error": "rpc error: code = Canceled desc = context canceled",
"status": {
"code": 1,
"message": "context canceled"
}
}
},
{
"time_ms": 58532,
"kind": "stderr",
"data": {
"lines": [
"[loop] recv: rpc error: code = Canceled desc = context canceled\n"
]
}
},
{
"time_ms": 58532,
"kind": "exit",
"data": {
"run_error": "recv: rpc error: code = Canceled desc = context canceled"
}
}
]
}

View file

@ -1,267 +0,0 @@
{
"metadata": {
"args": [
"loop",
"monitor",
"--network",
"regtest"
],
"env": {},
"version": "0.33.3-beta commit= commit_hash=",
"run_error": "recv: rpc error: code = Canceled desc = context canceled",
"duration": 25407490958,
"clock_start_unix": 1784150305
},
"events": [
{
"time_ms": 13,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "send",
"message_type": "looprpc.MonitorRequest",
"payload": {}
}
},
{
"time_ms": 13,
"kind": "stdout",
"data": {
"lines": [
"Note: offchain cost may report as 0 after loopd restart during swap\n"
]
}
},
{
"time_ms": 16479,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "bec3777d45337f652d96136e5714ed90a5d4f7cda87b5842110ecc38c9a65826",
"id_bytes": "vsN3fUUzf2UtlhNuVxTtkKXU982oe1hCEQ7MOMmmWCY=",
"type": "STATIC_LOOP_IN",
"state": "INITIATED",
"static_loop_in_state": "INIT_HTLC",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1784150320647961000",
"last_update_time": "1784150321494895000",
"htlc_address": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2wsh": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2tr": "",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 16479,
"kind": "stdout",
"data": {
"lines": [
"2026-07-15T18:18:41-03:00 STATIC_LOOP_IN INIT_HTLC 0.00500000 BTC - P2WSH: bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv\n"
]
}
},
{
"time_ms": 16482,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "bec3777d45337f652d96136e5714ed90a5d4f7cda87b5842110ecc38c9a65826",
"id_bytes": "vsN3fUUzf2UtlhNuVxTtkKXU982oe1hCEQ7MOMmmWCY=",
"type": "STATIC_LOOP_IN",
"state": "INITIATED",
"static_loop_in_state": "SIGN_HTLC_TX",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1784150320647961000",
"last_update_time": "1784150321499639000",
"htlc_address": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2wsh": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2tr": "",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 16482,
"kind": "stdout",
"data": {
"lines": [
"2026-07-15T18:18:41-03:00 STATIC_LOOP_IN SIGN_HTLC_TX 0.00500000 BTC - P2WSH: bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv\n"
]
}
},
{
"time_ms": 16530,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "bec3777d45337f652d96136e5714ed90a5d4f7cda87b5842110ecc38c9a65826",
"id_bytes": "vsN3fUUzf2UtlhNuVxTtkKXU982oe1hCEQ7MOMmmWCY=",
"type": "STATIC_LOOP_IN",
"state": "INITIATED",
"static_loop_in_state": "MONITOR_INVOICE_HTLC_TX",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1784150320647961000",
"last_update_time": "1784150321548450000",
"htlc_address": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2wsh": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2tr": "",
"cost_server": "0",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 16530,
"kind": "stdout",
"data": {
"lines": [
"2026-07-15T18:18:41-03:00 STATIC_LOOP_IN MONITOR_INVOICE_HTLC_TX 0.00500000 BTC - P2WSH: bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv\n"
]
}
},
{
"time_ms": 17027,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "bec3777d45337f652d96136e5714ed90a5d4f7cda87b5842110ecc38c9a65826",
"id_bytes": "vsN3fUUzf2UtlhNuVxTtkKXU982oe1hCEQ7MOMmmWCY=",
"type": "STATIC_LOOP_IN",
"state": "INITIATED",
"static_loop_in_state": "PAYMENT_RECEIVED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1784150320647961000",
"last_update_time": "1784150322044521000",
"htlc_address": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2wsh": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2tr": "",
"cost_server": "1828",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 17027,
"kind": "stdout",
"data": {
"lines": [
"2026-07-15T18:18:42-03:00 STATIC_LOOP_IN PAYMENT_RECEIVED 0.00500000 BTC - P2WSH: bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv (cost: server 1828, onchain 0, offchain 0)\n"
]
}
},
{
"time_ms": 17036,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "recv",
"message_type": "looprpc.SwapStatus",
"payload": {
"amt": "500000",
"id": "bec3777d45337f652d96136e5714ed90a5d4f7cda87b5842110ecc38c9a65826",
"id_bytes": "vsN3fUUzf2UtlhNuVxTtkKXU982oe1hCEQ7MOMmmWCY=",
"type": "STATIC_LOOP_IN",
"state": "INITIATED",
"static_loop_in_state": "SUCCEEDED",
"failure_reason": "FAILURE_REASON_NONE",
"initiation_time": "1784150320647961000",
"last_update_time": "1784150322052572000",
"htlc_address": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2wsh": "bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv",
"htlc_address_p2tr": "",
"cost_server": "1828",
"cost_onchain": "0",
"cost_offchain": "0",
"last_hop": "",
"outgoing_chan_set": [],
"label": "",
"asset_info": null
}
}
},
{
"time_ms": 17036,
"kind": "stdout",
"data": {
"lines": [
"2026-07-15T18:18:42-03:00 STATIC_LOOP_IN SUCCEEDED 0.00500000 BTC - P2WSH: bcrt1qjsl6tmv2vf3p5wwv28jh6xwettds934k02c3l5w6zyjsyrsd3lfskgdjmv (cost: server 1828, onchain 0, offchain 0)\n"
]
}
},
{
"time_ms": 25406,
"kind": "signal",
"data": {
"signal": "interrupt"
}
},
{
"time_ms": 25407,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/Monitor",
"event": "error",
"error": "rpc error: code = Canceled desc = context canceled",
"status": {
"code": 1,
"message": "context canceled"
}
}
},
{
"time_ms": 25407,
"kind": "stderr",
"data": {
"lines": [
"[loop] recv: rpc error: code = Canceled desc = context canceled\n"
]
}
},
{
"time_ms": 25407,
"kind": "exit",
"data": {
"run_error": "recv: rpc error: code = Canceled desc = context canceled"
}
}
]
}

View file

@ -1,96 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"getinfo",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 200590737,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 4,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetInfo",
"event": "request",
"message_type": "looprpc.GetInfoRequest",
"payload": {}
}
},
{
"time_ms": 200,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetInfo",
"event": "response",
"message_type": "looprpc.GetInfoResponse",
"payload": {
"version": "0.31.7-beta",
"network": "regtest",
"rpc_listen": "localhost:11010",
"rest_listen": "localhost:8081",
"macaroon_path": "/home/user/.loop/regtest/loop.macaroon",
"tls_cert_path": "/home/user/.loop/regtest/tls.cert",
"loop_out_stats": {
"pending_count": "0",
"success_count": "0",
"fail_count": "0",
"sum_pending_amt": "0",
"sum_succeeded_amt": "0"
},
"loop_in_stats": {
"pending_count": "0",
"success_count": "0",
"fail_count": "0",
"sum_pending_amt": "0",
"sum_succeeded_amt": "0"
},
"commit_hash": "579ea31979e0cdb1ee6049ab97cc2768888eac40"
}
}
},
{
"time_ms": 200,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"commit_hash\": \"579ea31979e0cdb1ee6049ab97cc2768888eac40\",\n",
" \"loop_in_stats\": {\n",
" \"fail_count\": \"0\",\n",
" \"pending_count\": \"0\",\n",
" \"success_count\": \"0\",\n",
" \"sum_pending_amt\": \"0\",\n",
" \"sum_succeeded_amt\": \"0\"\n",
" },\n",
" \"loop_out_stats\": {\n",
" \"fail_count\": \"0\",\n",
" \"pending_count\": \"0\",\n",
" \"success_count\": \"0\",\n",
" \"sum_pending_amt\": \"0\",\n",
" \"sum_succeeded_amt\": \"0\"\n",
" },\n",
" \"macaroon_path\": \"/home/user/.loop/regtest/loop.macaroon\",\n",
" \"network\": \"regtest\",\n",
" \"rest_listen\": \"localhost:8081\",\n",
" \"rpc_listen\": \"localhost:11010\",\n",
" \"tls_cert_path\": \"/home/user/.loop/regtest/tls.cert\",\n",
" \"version\": \"0.31.7-beta\"\n",
"}\n"
]
}
},
{
"time_ms": 200,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,107 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"reservations",
"list",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 177945855,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 177,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
}
]
}
}
},
{
"time_ms": 177,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"reservations\": [\n",
" {\n",
" \"amount\": \"800000\",\n",
" \"expiry\": 1619,\n",
" \"reservation_id\": \"b3f443e8b8ff797766b450ad77fe80beb250acfd7887562b6e5c9964c5884d87\",\n",
" \"state\": \"Confirmed\",\n",
" \"tx_id\": \"8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17\",\n",
" \"vout\": 0\n",
" },\n",
" {\n",
" \"amount\": \"800000\",\n",
" \"expiry\": 1623,\n",
" \"reservation_id\": \"5a5f824ec63e576cd4be42b96ecf5622db64bca2eb28cf00d64a59e5567f307b\",\n",
" \"state\": \"Confirmed\",\n",
" \"tx_id\": \"beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836\",\n",
" \"vout\": 0\n",
" },\n",
" {\n",
" \"amount\": \"800000\",\n",
" \"expiry\": 1626,\n",
" \"reservation_id\": \"32eeb97db85ac84b51ce8ba028b067a1e44df1ffad10cd7e3bd42e36f5087db2\",\n",
" \"state\": \"Confirmed\",\n",
" \"tx_id\": \"5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952\",\n",
" \"vout\": 0\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 177,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,190 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1574538838,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 0,
"kind": "stdin",
"data": {
"text": "ALL\ny\n"
}
},
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 152,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
}
]
}
}
},
{
"time_ms": 152,
"kind": "stdout",
"data": {
"lines": [
"Available reservations: \n",
"\n",
"Reservation 1: shortid b3f443, amt 800000, expiry height 1619 \n",
"Reservation 2: shortid 5a5f82, amt 800000, expiry height 1623 \n",
"Reservation 3: shortid 32eeb9, amt 800000, expiry height 1626 \n",
"\n",
"Max amount to instant out: 2400000\n",
"\n",
"Select reservations for instantout (e.g. '1,2,3')\n"
]
}
},
{
"time_ms": 152,
"kind": "stdout",
"data": {
"lines": [
"Type 'ALL' to use all available reservations.\n"
]
}
},
{
"time_ms": 152,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "request",
"message_type": "looprpc.InstantOutQuoteRequest",
"payload": {
"amt": "2400000",
"num_reservations": 0,
"reservation_ids": [
"s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI="
]
}
}
},
{
"time_ms": 177,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "response",
"message_type": "looprpc.InstantOutQuoteResponse",
"payload": {
"service_fee_sat": "4800",
"sweep_fee_sat": "5650"
}
}
},
{
"time_ms": 177,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "request",
"message_type": "looprpc.InstantOutRequest",
"payload": {
"reservation_ids": [
"s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI="
],
"outgoing_chan_set": [],
"dest_addr": "",
"max_swap_fee_sat": "4800"
}
}
},
{
"time_ms": 177,
"kind": "stdout",
"data": {
"lines": [
"\n",
"Estimated on-chain fee: 5650 sat\n",
"Service fee: 4800 sat\n",
"\n",
"CONTINUE SWAP? (y/n): Starting instant swap out\n"
]
}
},
{
"time_ms": 1574,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "response",
"message_type": "looprpc.InstantOutResponse",
"payload": {
"instant_out_hash": "uxbiybKoHjn4B4b+yHRFGI9e12jMMtTLEvNnF7wYriU=",
"sweep_tx_id": "a1650cb339118e58f317bbbbd8cff720e0cc02a9fe159c9286724d6f5722fef2",
"state": "PushPreimage"
}
}
},
{
"time_ms": 1574,
"kind": "stdout",
"data": {
"lines": [
"Instant out swap initiated with ID: bb16e2c9b2a81e39f80786fec87445188f5ed768cc32d4cb12f36717bc18ae25, State: PushPreimage \n",
"Sweepless sweep tx id: a1650cb339118e58f317bbbbd8cff720e0cc02a9fe159c9286724d6f5722fef2 \n"
]
}
},
{
"time_ms": 1574,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,80 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"listinstantouts",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 142800927,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListInstantOuts",
"event": "request",
"message_type": "looprpc.ListInstantOutsRequest",
"payload": {}
}
},
{
"time_ms": 142,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListInstantOuts",
"event": "response",
"message_type": "looprpc.ListInstantOutsResponse",
"payload": {
"swaps": [
{
"swap_hash": "uxbiybKoHjn4B4b+yHRFGI9e12jMMtTLEvNnF7wYriU=",
"state": "WaitForSweeplessSweepConfirmed",
"amount": "2400000",
"reservation_ids": [
"s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI="
],
"sweep_tx_id": "a1650cb339118e58f317bbbbd8cff720e0cc02a9fe159c9286724d6f5722fef2"
}
]
}
}
},
{
"time_ms": 142,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"swaps\": [\n",
" {\n",
" \"amount\": \"2400000\",\n",
" \"reservation_ids\": [\n",
" \"b3f443e8b8ff797766b450ad77fe80beb250acfd7887562b6e5c9964c5884d87\",\n",
" \"5a5f824ec63e576cd4be42b96ecf5622db64bca2eb28cf00d64a59e5567f307b\",\n",
" \"32eeb97db85ac84b51ce8ba028b067a1e44df1ffad10cd7e3bd42e36f5087db2\"\n",
" ],\n",
" \"state\": \"WaitForSweeplessSweepConfirmed\",\n",
" \"swap_hash\": \"bb16e2c9b2a81e39f80786fec87445188f5ed768cc32d4cb12f36717bc18ae25\",\n",
" \"sweep_tx_id\": \"a1650cb339118e58f317bbbbd8cff720e0cc02a9fe159c9286724d6f5722fef2\"\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 142,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,94 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 271911644,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "stdin",
"data": {
"text": "ALL\nn\n"
}
},
{
"time_ms": 9,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 270,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Spent",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Spent",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Spent",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
},
{
"reservation_id": "/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"state": "WaitForConfirmation",
"amount": "800000",
"tx_id": "",
"vout": 0,
"expiry": 1632
}
]
}
}
},
{
"time_ms": 271,
"kind": "stdout",
"data": {
"lines": [
"No confirmed reservations found \n"
]
}
},
{
"time_ms": 271,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,155 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "swap canceled",
"duration": 229272887,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stdin",
"data": {
"text": "ALL\nn\n"
}
},
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 188,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Spent",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Spent",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Spent",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
},
{
"reservation_id": "/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "322d031e9ab55e0bcdac421c57d2d5d7705b80d01d3217a672aa1bf73edfc963",
"vout": 0,
"expiry": 1632
}
]
}
}
},
{
"time_ms": 188,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "request",
"message_type": "looprpc.InstantOutQuoteRequest",
"payload": {
"amt": "800000",
"num_reservations": 0,
"reservation_ids": [
"/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U="
]
}
}
},
{
"time_ms": 188,
"kind": "stdout",
"data": {
"lines": [
"Available reservations: \n",
"\n",
"Reservation 1: shortid fc0b25, amt 800000, expiry height 1632 \n",
"\n",
"Max amount to instant out: 800000\n",
"\n",
"Select reservations for instantout (e.g. '1,2,3')\n",
"Type 'ALL' to use all available reservations.\n"
]
}
},
{
"time_ms": 228,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "response",
"message_type": "looprpc.InstantOutQuoteResponse",
"payload": {
"service_fee_sat": "1600",
"sweep_fee_sat": "2773"
}
}
},
{
"time_ms": 228,
"kind": "stdout",
"data": {
"lines": [
"\n",
"Estimated on-chain fee: 2773 sat\n",
"Service fee: 1600 sat\n",
"\n",
"CONTINUE SWAP? (y/n): "
]
}
},
{
"time_ms": 229,
"kind": "stderr",
"data": {
"lines": [
"[loop] swap canceled\n"
]
}
},
{
"time_ms": 229,
"kind": "exit",
"data": {
"run_error": "swap canceled"
}
}
]
}

View file

@ -1,113 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "invalid index 99",
"duration": 203873152,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 0,
"kind": "stdin",
"data": {
"text": "99\n"
}
},
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 202,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Spent",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Spent",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Spent",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
},
{
"reservation_id": "/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "322d031e9ab55e0bcdac421c57d2d5d7705b80d01d3217a672aa1bf73edfc963",
"vout": 0,
"expiry": 1632
}
]
}
}
},
{
"time_ms": 202,
"kind": "stdout",
"data": {
"lines": [
"Available reservations: \n",
"\n",
"Reservation 1: shortid fc0b25, amt 800000, expiry height 1632 \n",
"\n",
"Max amount to instant out: 800000\n",
"\n",
"Select reservations for instantout (e.g. '1,2,3')\n",
"Type 'ALL' to use all available reservations.\n"
]
}
},
{
"time_ms": 203,
"kind": "stderr",
"data": {
"lines": [
"[loop] invalid index 99\n"
]
}
},
{
"time_ms": 203,
"kind": "exit",
"data": {
"run_error": "invalid index 99"
}
}
]
}

View file

@ -1,207 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest",
"--channel",
"125344325763072"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1716733605,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stdin",
"data": {
"text": "ALL\ny\n"
}
},
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 317,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Spent",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Spent",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Spent",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
},
{
"reservation_id": "/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "322d031e9ab55e0bcdac421c57d2d5d7705b80d01d3217a672aa1bf73edfc963",
"vout": 0,
"expiry": 1632
},
{
"reservation_id": "0JK1m4RmC5iKV9dR/SlixC0IEGmobPMTlVgmk13thx8=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "081eed1a78e87498ca29af380aaecc64e949c47aeb400375e662fdf630554ace",
"vout": 0,
"expiry": 1653
}
]
}
}
},
{
"time_ms": 317,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "request",
"message_type": "looprpc.InstantOutQuoteRequest",
"payload": {
"amt": "1600000",
"num_reservations": 0,
"reservation_ids": [
"/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"0JK1m4RmC5iKV9dR/SlixC0IEGmobPMTlVgmk13thx8="
]
}
}
},
{
"time_ms": 317,
"kind": "stdout",
"data": {
"lines": [
"Available reservations: \n",
"\n",
"Reservation 1: shortid fc0b25, amt 800000, expiry height 1632 \n",
"Reservation 2: shortid d092b5, amt 800000, expiry height 1653 \n",
"\n",
"Max amount to instant out: 1600000\n",
"\n",
"Select reservations for instantout (e.g. '1,2,3')\n",
"Type 'ALL' to use all available reservations.\n"
]
}
},
{
"time_ms": 359,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "response",
"message_type": "looprpc.InstantOutQuoteResponse",
"payload": {
"service_fee_sat": "3200",
"sweep_fee_sat": "4210"
}
}
},
{
"time_ms": 359,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "request",
"message_type": "looprpc.InstantOutRequest",
"payload": {
"reservation_ids": [
"/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"0JK1m4RmC5iKV9dR/SlixC0IEGmobPMTlVgmk13thx8="
],
"outgoing_chan_set": [
"125344325763072"
],
"dest_addr": "",
"max_swap_fee_sat": "3200"
}
}
},
{
"time_ms": 360,
"kind": "stdout",
"data": {
"lines": [
"\n",
"Estimated on-chain fee: 4210 sat\n",
"Service fee: 3200 sat\n",
"\n",
"CONTINUE SWAP? (y/n): Starting instant swap out\n"
]
}
},
{
"time_ms": 1711,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "response",
"message_type": "looprpc.InstantOutResponse",
"payload": {
"instant_out_hash": "RfjHjKyop9NqqQzGXnyF1mnv9mPeWJXyz+5seKnKZ4k=",
"sweep_tx_id": "6f6f29961d2b4ab9f893850a0250f1f55fb93b144966833a5546986b171255a4",
"state": "PushPreimage"
}
}
},
{
"time_ms": 1711,
"kind": "stdout",
"data": {
"lines": [
"Instant out swap initiated with ID: 45f8c78caca8a7d36aa90cc65e7c85d669eff663de5895f2cfee6c78a9ca6789, State: PushPreimage \n"
]
}
},
{
"time_ms": 1711,
"kind": "stdout",
"data": {
"lines": [
"Sweepless sweep tx id: 6f6f29961d2b4ab9f893850a0250f1f55fb93b144966833a5546986b171255a4 \n"
]
}
},
{
"time_ms": 1716,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,200 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"instantout",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1377442432,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "stdin",
"data": {
"text": "1\ny\n"
}
},
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "request",
"message_type": "looprpc.ListReservationsRequest",
"payload": {}
}
},
{
"time_ms": 178,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListReservations",
"event": "response",
"message_type": "looprpc.ListReservationsResponse",
"payload": {
"reservations": [
{
"reservation_id": "s/RD6Lj/eXdmtFCtd/6AvrJQrP14h1YrblyZZMWITYc=",
"state": "Spent",
"amount": "800000",
"tx_id": "8b3b5e235ad24be42bc3802c6694566964c394df247b4ae1f2f4fd785e5acd17",
"vout": 0,
"expiry": 1619
},
{
"reservation_id": "Wl+CTsY+V2zUvkK5bs9WIttkvKLrKM8A1kpZ5VZ/MHs=",
"state": "Spent",
"amount": "800000",
"tx_id": "beb447976c6c1a93fdadee41280b912e62b305c7c30815dc172d58732b6c3836",
"vout": 0,
"expiry": 1623
},
{
"reservation_id": "Mu65fbhayEtRzougKLBnoeRN8f+tEM1+O9QuNvUIfbI=",
"state": "Spent",
"amount": "800000",
"tx_id": "5f898ca1b3852c62889f1113e07c73d0a3d721fd98d0a5433c6b7122cc706952",
"vout": 0,
"expiry": 1626
},
{
"reservation_id": "/AslVrUe/D6Eum+CZsg1x+EalEVACi6ICfc8rVURH3U=",
"state": "Spent",
"amount": "800000",
"tx_id": "322d031e9ab55e0bcdac421c57d2d5d7705b80d01d3217a672aa1bf73edfc963",
"vout": 0,
"expiry": 1632
},
{
"reservation_id": "0JK1m4RmC5iKV9dR/SlixC0IEGmobPMTlVgmk13thx8=",
"state": "Spent",
"amount": "800000",
"tx_id": "081eed1a78e87498ca29af380aaecc64e949c47aeb400375e662fdf630554ace",
"vout": 0,
"expiry": 1653
},
{
"reservation_id": "cSfKVONNmsK9+p4Uc5nc3ZtE+37uOODHeq1vprhh/x4=",
"state": "Confirmed",
"amount": "800000",
"tx_id": "46808a670542c1f3bf20ade2af77c254e3535378aab6ccd32c58de9976b0cf26",
"vout": 0,
"expiry": 1671
}
]
}
}
},
{
"time_ms": 179,
"kind": "stdout",
"data": {
"lines": [
"Available reservations: \n",
"\n",
"Reservation 1: shortid 7127ca, amt 800000, expiry height 1671 \n",
"\n",
"Max amount to instant out: 800000\n",
"\n",
"Select reservations for instantout (e.g. '1,2,3')\n",
"Type 'ALL' to use all available reservations.\n"
]
}
},
{
"time_ms": 179,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "request",
"message_type": "looprpc.InstantOutQuoteRequest",
"payload": {
"amt": "800000",
"num_reservations": 0,
"reservation_ids": [
"cSfKVONNmsK9+p4Uc5nc3ZtE+37uOODHeq1vprhh/x4="
]
}
}
},
{
"time_ms": 186,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOutQuote",
"event": "response",
"message_type": "looprpc.InstantOutQuoteResponse",
"payload": {
"service_fee_sat": "1600",
"sweep_fee_sat": "2772"
}
}
},
{
"time_ms": 186,
"kind": "stdout",
"data": {
"lines": [
"\n",
"Estimated on-chain fee: 2772 sat\n",
"Service fee: 1600 sat\n",
"\n",
"CONTINUE SWAP? (y/n): Starting instant swap out\n"
]
}
},
{
"time_ms": 186,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "request",
"message_type": "looprpc.InstantOutRequest",
"payload": {
"reservation_ids": [
"cSfKVONNmsK9+p4Uc5nc3ZtE+37uOODHeq1vprhh/x4="
],
"outgoing_chan_set": [],
"dest_addr": "",
"max_swap_fee_sat": "1600"
}
}
},
{
"time_ms": 1376,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/InstantOut",
"event": "response",
"message_type": "looprpc.InstantOutResponse",
"payload": {
"instant_out_hash": "0LvpOmXv+cqCnXgQ/lNOlbCvlI0AZkkkS1pFJk2n3us=",
"sweep_tx_id": "650e166a7d346f2b268c26c7fe075d8e2dc25655a747f546f5a039f01973d979",
"state": "WaitForSweeplessSweepConfirmed"
}
}
},
{
"time_ms": 1377,
"kind": "stdout",
"data": {
"lines": [
"Instant out swap initiated with ID: d0bbe93a65eff9ca829d7810fe534e95b0af948d006649244b5a45264da7deeb, State: WaitForSweeplessSweepConfirmed \n",
"Sweepless sweep tx id: 650e166a7d346f2b268c26c7fe075d8e2dc25655a747f546f5a039f01973d979 \n"
]
}
},
{
"time_ms": 1377,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,79 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"listauth",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 157631491,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetL402Tokens",
"event": "request",
"message_type": "looprpc.TokensRequest",
"payload": {}
}
},
{
"time_ms": 157,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetL402Tokens",
"event": "response",
"message_type": "looprpc.TokensResponse",
"payload": {
"tokens": [
{
"base_macaroon": "AgEEbHNhdAJCAAAkJPkWepsd1GgURnFywD7MZi7yoPZC/BjJHNrIs6en83L1T2GVJqpTIlrcw09RnFx5xHfjsNDjLRIekGAPVN/oAAIPc2VydmljZXM9bG9vcDowAAISbG9vcF9jYXBhYmlsaXRpZXM9AAAGIGIkKNbF9/LxQzqEKurnzP7lkGJbA2CsVbkwrKXyUTx/",
"payment_hash": "JCT5FnqbHdRoFEZxcsA+zGYu8qD2QvwYyRzayLOnp/M=",
"payment_preimage": "pSZZqEORw/4tPCPPS3sTDeh/2mq3EAzwqqWor/xwTmM=",
"amount_paid_msat": "1000000",
"routing_fee_paid_msat": "0",
"time_created": "1770076917",
"expired": false,
"storage_name": "/home/user/.loop/regtest/l402.token",
"id": "72f54f619526aa53225adcc34f519c5c79c477e3b0d0e32d121e90600f54dfe8"
}
]
}
}
},
{
"time_ms": 157,
"kind": "stdout",
"data": {
"lines": [
"[\n",
" {\n",
" \"amount_paid_msat\": 1000000,\n",
" \"base_macaroon\": \"0201046c736174024200002424f9167a9b1dd46814467172c03ecc662ef2a0f642fc18c91cdac8b3a7a7f372f54f619526aa53225adcc34f519c5c79c477e3b0d0e32d121e90600f54dfe800020f73657276696365733d6c6f6f703a300002126c6f6f705f6361706162696c69746965733d00000620622428d6c5f7f2f1433a842aeae7ccfee590625b0360ac55b930aca5f2513c7f\",\n",
" \"expired\": false,\n",
" \"file_name\": \"/home/user/.loop/regtest/l402.token\",\n",
" \"id\": \"72f54f619526aa53225adcc34f519c5c79c477e3b0d0e32d121e90600f54dfe8\",\n",
" \"payment_hash\": \"2424f9167a9b1dd46814467172c03ecc662ef2a0f642fc18c91cdac8b3a7a7f3\",\n",
" \"payment_preimage\": \"a52659a84391c3fe2d3c23cf4b7b130de87fda6ab7100cf0aaa5a8affc704e63\",\n",
" \"routing_fee_paid_msat\": 0,\n",
" \"time_created\": \"2026-02-02T19:01:57-05:00\",\n",
" \"valid_until\": \"\"\n",
" }\n",
"]\n"
]
}
},
{
"time_ms": 157,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,52 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"fetchl402",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 177710196,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/FetchL402Token",
"event": "request",
"message_type": "looprpc.FetchL402TokenRequest",
"payload": {}
}
},
{
"time_ms": 177,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/FetchL402Token",
"event": "response",
"message_type": "looprpc.FetchL402TokenResponse",
"payload": {}
}
},
{
"time_ms": 177,
"kind": "stdout",
"data": {
"lines": [
"{}\n"
]
}
},
{
"time_ms": 177,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,110 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"getparams",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 213058408,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 212,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 212,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"account\": \"\",\n",
" \"account_addr_type\": \"ADDRESS_TYPE_UNKNOWN\",\n",
" \"auto_max_in_flight\": \"1\",\n",
" \"autoloop\": false,\n",
" \"autoloop_budget_last_refresh\": \"1770076824\",\n",
" \"autoloop_budget_refresh_period_sec\": \"604800\",\n",
" \"autoloop_budget_sat\": \"335544\",\n",
" \"autoloop_budget_start_sec\": \"0\",\n",
" \"autoloop_dest_address\": \"\",\n",
" \"easy_asset_params\": {},\n",
" \"easy_autoloop\": false,\n",
" \"easy_autoloop_excluded_peers\": [],\n",
" \"easy_autoloop_local_target_sat\": \"0\",\n",
" \"failure_backoff_sec\": \"86400\",\n",
" \"fast_swap_publication\": true,\n",
" \"fee_ppm\": \"20000\",\n",
" \"htlc_conf_target\": 6,\n",
" \"loop_in_source\": \"LOOP_IN_SOURCE_WALLET\",\n",
" \"max_miner_fee_sat\": \"0\",\n",
" \"max_prepay_routing_fee_ppm\": \"0\",\n",
" \"max_prepay_sat\": \"0\",\n",
" \"max_routing_fee_ppm\": \"0\",\n",
" \"max_swap_amount\": \"0\",\n",
" \"max_swap_fee_ppm\": \"0\",\n",
" \"min_swap_amount\": \"0\",\n",
" \"rules\": [],\n",
" \"sweep_conf_target\": 100,\n",
" \"sweep_fee_rate_sat_per_vbyte\": \"0\"\n",
"}\n"
]
}
},
{
"time_ms": 213,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,93 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "at least one flag required to set params",
"duration": 150177446,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 5,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 149,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 150,
"kind": "stderr",
"data": {
"lines": [
"[loop] at least one flag required to set params\n"
]
}
},
{
"time_ms": 150,
"kind": "exit",
"data": {
"run_error": "at least one flag required to set params"
}
}
]
}

View file

@ -1,125 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--feepercent",
"2"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 249608692,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 6,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 188,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 189,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 248,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 249,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,97 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--feepercent",
"2",
"--sweeplimit",
"1"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "feepercent cannot be set with specific fee category flags",
"duration": 161206474,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 4,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 159,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 161,
"kind": "stderr",
"data": {
"lines": [
"[loop] feepercent cannot be set with specific fee category flags\n"
]
}
},
{
"time_ms": 161,
"kind": "exit",
"data": {
"run_error": "feepercent cannot be set with specific fee category flags"
}
}
]
}

View file

@ -1,94 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"034fa8802fdc9f8c137e8f2cce2e4d30e590a1314e16bd060ac7d772dc698ff4bc"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "provide at least one flag to set rules or use the --clear flag to remove rules",
"duration": 143258905,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 142,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 143,
"kind": "stderr",
"data": {
"lines": [
"[loop] provide at least one flag to set rules or use the --clear flag to remove rules\n"
]
}
},
{
"time_ms": 143,
"kind": "exit",
"data": {
"run_error": "provide at least one flag to set rules or use the --clear flag to remove rules"
}
}
]
}

View file

@ -1,35 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "please set a channel id or peer pubkey for the rule update",
"duration": 1924009,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stderr",
"data": {
"lines": [
"[loop] please set a channel id or peer pubkey for the rule update\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {
"run_error": "please set a channel id or peer pubkey for the rule update"
}
}
]
}

View file

@ -1,58 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"suggestswaps",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "no rules set for autolooper, please set rules using the setrule command",
"duration": 178932401,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "request",
"message_type": "looprpc.SuggestSwapsRequest",
"payload": {}
}
},
{
"time_ms": 178,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "error",
"error": "rpc error: code = FailedPrecondition desc = no rules set for autoloop",
"status": {
"code": 9,
"message": "no rules set for autoloop"
}
}
},
{
"time_ms": 178,
"kind": "stderr",
"data": {
"lines": [
"[loop] no rules set for autolooper, please set rules using the setrule command\n"
]
}
},
{
"time_ms": 178,
"kind": "exit",
"data": {
"run_error": "no rules set for autolooper, please set rules using the setrule command"
}
}
]
}

View file

@ -1,144 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"--incoming_threshold",
"10",
"034fa8802fdc9f8c137e8f2cce2e4d30e590a1314e16bd060ac7d772dc698ff4bc"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 130661787,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 124,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 124,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 130,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 130,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,61 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"suggestswaps",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 137916583,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "request",
"message_type": "looprpc.SuggestSwapsRequest",
"payload": {}
}
},
{
"time_ms": 137,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "response",
"message_type": "looprpc.SuggestSwapsResponse",
"payload": {
"loop_out": [],
"loop_in": [],
"disqualified": []
}
}
},
{
"time_ms": 137,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"disqualified\": [],\n",
" \"loop_in\": [],\n",
" \"loop_out\": [],\n",
" \"static_loop_in\": []\n",
"}\n"
]
}
},
{
"time_ms": 137,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,152 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"--incoming_threshold",
"40",
"125344325763072"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 204743867,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 135,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 135,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
},
{
"channel_id": "125344325763072",
"swap_type": "LOOP_OUT",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 40,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 204,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 204,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,185 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"--type",
"in",
"--outgoing_threshold",
"30",
"124244814135296"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "rpc error: code = Unknown desc = channel level rules not supported for loop in swaps, only peer-level rules allowed",
"duration": 212373344,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 168,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "125344325763072",
"swap_type": "LOOP_OUT",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 40,
"outgoing_threshold": 0
},
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 168,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "125344325763072",
"swap_type": "LOOP_OUT",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 40,
"outgoing_threshold": 0
},
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
},
{
"channel_id": "124244814135296",
"swap_type": "LOOP_IN",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 0,
"outgoing_threshold": 30
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 212,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "error",
"error": "rpc error: code = Unknown desc = channel level rules not supported for loop in swaps, only peer-level rules allowed",
"status": {
"code": 2,
"message": "channel level rules not supported for loop in swaps, only peer-level rules allowed"
}
}
},
{
"time_ms": 212,
"kind": "stderr",
"data": {
"lines": [
"[loop] rpc error: code = Unknown desc = channel level rules not supported for loop in swaps, only peer-level rules allowed\n"
]
}
},
{
"time_ms": 212,
"kind": "exit",
"data": {
"run_error": "rpc error: code = Unknown desc = channel level rules not supported for loop in swaps, only peer-level rules allowed"
}
}
]
}

View file

@ -1,151 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"--clear",
"125344325763072"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 232507711,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 150,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "125344325763072",
"swap_type": "LOOP_OUT",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 40,
"outgoing_threshold": 0
},
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 150,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 231,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 232,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,191 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--maxswapfee",
"0.1",
"--maxroutingfee",
"0.1",
"--maxprepayfee",
"0.1",
"--maxprepay",
"1000",
"--maxminer",
"1000",
"--sweepconf",
"10",
"--failurebackoff",
"60",
"--autoloop",
"--autobudget",
"1000",
"--autobudgetrefreshperiod",
"1h",
"--autoinflight",
"2",
"--minamt",
"1000",
"--maxamt",
"200000",
"--htlc_conf",
"2",
"--easyautoloop",
"--localbalancesat",
"1000000",
"--easyautoloop_excludepeer",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7",
"--fast"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "rpc error: code = Unknown desc = sweep fee rate limit must be > 1 sat/vByte",
"duration": 325173938,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 276,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 276,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "1000",
"max_swap_amount": "200000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 324,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "error",
"error": "rpc error: code = Unknown desc = sweep fee rate limit must be > 1 sat/vByte",
"status": {
"code": 2,
"message": "sweep fee rate limit must be > 1 sat/vByte"
}
}
},
{
"time_ms": 325,
"kind": "stderr",
"data": {
"lines": [
"[loop] rpc error: code = Unknown desc = sweep fee rate limit must be > 1 sat/vByte\n"
]
}
},
{
"time_ms": 325,
"kind": "exit",
"data": {
"run_error": "rpc error: code = Unknown desc = sweep fee rate limit must be > 1 sat/vByte"
}
}
]
}

View file

@ -1,193 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--sweeplimit",
"2",
"--maxswapfee",
"0.1",
"--maxroutingfee",
"0.1",
"--maxprepayfee",
"0.1",
"--maxprepay",
"1000",
"--maxminer",
"1000",
"--sweepconf",
"10",
"--failurebackoff",
"60",
"--autoloop",
"--autobudget",
"1000",
"--autobudgetrefreshperiod",
"1h",
"--autoinflight",
"2",
"--minamt",
"1000",
"--maxamt",
"200000",
"--htlc_conf",
"2",
"--easyautoloop",
"--localbalancesat",
"1000000",
"--easyautoloop_excludepeer",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7",
"--fast"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "rpc error: code = Unknown desc = minimum swap amount is less than server minimum",
"duration": 273083406,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 191,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 191,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "1000",
"max_swap_amount": "200000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 272,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "error",
"error": "rpc error: code = Unknown desc = minimum swap amount is less than server minimum",
"status": {
"code": 2,
"message": "minimum swap amount is less than server minimum"
}
}
},
{
"time_ms": 272,
"kind": "stderr",
"data": {
"lines": [
"[loop] rpc error: code = Unknown desc = minimum swap amount is less than server minimum\n"
]
}
},
{
"time_ms": 273,
"kind": "exit",
"data": {
"run_error": "rpc error: code = Unknown desc = minimum swap amount is less than server minimum"
}
}
]
}

View file

@ -1,178 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--sweeplimit",
"2",
"--maxswapfee",
"0.1",
"--maxroutingfee",
"0.1",
"--maxprepayfee",
"0.1",
"--maxprepay",
"1000",
"--maxminer",
"1000",
"--sweepconf",
"10",
"--failurebackoff",
"60",
"--autoloop",
"--autobudget",
"1000",
"--autobudgetrefreshperiod",
"1h",
"--autoinflight",
"2",
"--minamt",
"500000",
"--maxamt",
"1000000",
"--htlc_conf",
"2",
"--easyautoloop",
"--localbalancesat",
"1000000",
"--easyautoloop_excludepeer",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7",
"--fast"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 184771728,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 160,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 161,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 182,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 184,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,147 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--destaddr",
"bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 235471909,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 223,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 224,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 234,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 235,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,149 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--account",
"default",
"--account_addr_type",
"p2tr"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 264493138,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 249,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 250,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 264,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 264,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,144 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--easyatutoloop_includeallpeers"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 304215261,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 263,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [
"AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3"
],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 271,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770076824",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 303,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 304,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,154 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setrule",
"--network",
"regtest",
"--type",
"out",
"--incoming_threshold",
"20",
"125344325763072"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 195037901,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 137,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770092424",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 137,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_OUT",
"pubkey": "A0+ogC/cn4wTfo8szi5NMOWQoTFOFr0GCsfXctxpj/S8",
"type": "THRESHOLD",
"incoming_threshold": 10,
"outgoing_threshold": 0
},
{
"channel_id": "125344325763072",
"swap_type": "LOOP_OUT",
"pubkey": "",
"type": "THRESHOLD",
"incoming_threshold": 20,
"outgoing_threshold": 0
}
],
"fee_ppm": "0",
"sweep_fee_rate_sat_per_vbyte": "2",
"max_swap_fee_ppm": "1000",
"max_routing_fee_ppm": "1000",
"max_prepay_routing_fee_ppm": "1000",
"max_prepay_sat": "1000",
"max_miner_fee_sat": "1000",
"sweep_conf_target": 10,
"failure_backoff_sec": "60",
"autoloop": true,
"autoloop_budget_sat": "1000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "2",
"min_swap_amount": "500000",
"max_swap_amount": "1000000",
"htlc_conf_target": 2,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "3600",
"autoloop_budget_last_refresh": "1770092424",
"easy_autoloop": true,
"easy_autoloop_local_target_sat": "1000000",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
}
},
{
"time_ms": 194,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 195,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,36 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--network",
"regtest",
"notanumber"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "invalid amt value \"notanumber\"",
"duration": 3964079,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "stderr",
"data": {
"lines": [
"[loop] invalid amt value \"notanumber\"\n"
]
}
},
{
"time_ms": 3,
"kind": "exit",
"data": {
"run_error": "invalid amt value \"notanumber\""
}
}
]
}

View file

@ -1,39 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--external",
"--conf_target",
"6",
"--network",
"regtest",
"10000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "external and conf_target both set",
"duration": 13741983,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 13,
"kind": "stderr",
"data": {
"lines": [
"[loop] external and conf_target both set\n"
]
}
},
{
"time_ms": 13,
"kind": "exit",
"data": {
"run_error": "external and conf_target both set"
}
}
]
}

View file

@ -1,39 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--route_hints",
"{}",
"--private",
"--network",
"regtest",
"10000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "private and route_hints both set",
"duration": 2834558,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "stderr",
"data": {
"lines": [
"[loop] private and route_hints both set\n"
]
}
},
{
"time_ms": 2,
"kind": "exit",
"data": {
"run_error": "private and route_hints both set"
}
}
]
}

View file

@ -1,101 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--network",
"regtest",
"--external",
"--amt",
"50000",
"--last_hop",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "swap canceled",
"duration": 539895736,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stdin",
"data": {
"text": "n\n"
}
},
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "50000",
"conf_target": 0,
"external_htlc": true,
"swap_publication_deadline": "0",
"loop_in_last_hop": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 539,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1801",
"htlc_publish_fee_sat": "0",
"cltv_delta": 0,
"conf_target": 0,
"quoted_amt": "50000"
}
}
},
{
"time_ms": 539,
"kind": "stderr",
"data": {
"lines": [
"[loop] swap canceled\n"
]
}
},
{
"time_ms": 539,
"kind": "stdout",
"data": {
"lines": [
"On-chain fee for external loop in is not included.\n",
"Sufficient fees will need to be paid when constructing the transaction in the external wallet.\n",
"\n",
"Send on-chain: 50000 sat\n",
"Receive off-chain: 48199 sat\n",
"Loop service fee: 1801 sat\n",
"\n",
"CONTINUE SWAP? (y/n): "
]
}
},
{
"time_ms": 539,
"kind": "exit",
"data": {
"run_error": "swap canceled"
}
}
]
}

View file

@ -1,104 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--network",
"regtest",
"--external",
"--verbose",
"--amt",
"50000",
"--last_hop",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "swap canceled",
"duration": 164102130,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 0,
"kind": "stdin",
"data": {
"text": "n\n"
}
},
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "50000",
"conf_target": 0,
"external_htlc": true,
"swap_publication_deadline": "0",
"loop_in_last_hop": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 163,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1801",
"htlc_publish_fee_sat": "0",
"cltv_delta": 0,
"conf_target": 0,
"quoted_amt": "50000"
}
}
},
{
"time_ms": 163,
"kind": "stdout",
"data": {
"lines": [
"On-chain fee for external loop in is not included.\n",
"Sufficient fees will need to be paid when constructing the transaction in the external wallet.\n",
"\n",
"Send on-chain: 50000 sat\n",
"Receive off-chain: 48199 sat\n",
"Loop service fee: 1801 sat\n",
"\n",
"CLTV expiry delta: 0 block\n",
"\n",
"CONTINUE SWAP? (y/n): "
]
}
},
{
"time_ms": 164,
"kind": "stderr",
"data": {
"lines": [
"[loop] swap canceled\n"
]
}
},
{
"time_ms": 164,
"kind": "exit",
"data": {
"run_error": "swap canceled"
}
}
]
}

View file

@ -1,116 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"in",
"--network",
"regtest",
"--external",
"--amt",
"500000",
"--force"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1067066878,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 0,
"external_htlc": true,
"swap_publication_deadline": "0",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 561,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1824",
"htlc_publish_fee_sat": "0",
"cltv_delta": 0,
"conf_target": 0,
"quoted_amt": "500000"
}
}
},
{
"time_ms": 561,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopIn",
"event": "request",
"message_type": "looprpc.LoopInRequest",
"payload": {
"amt": "500000",
"max_swap_fee": "1824",
"max_miner_fee": "0",
"last_hop": "",
"external_htlc": true,
"htlc_conf_target": 0,
"label": "",
"initiator": "loop-cli",
"route_hints": [],
"private": false
}
}
},
{
"time_ms": 1065,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopIn",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "2f46a232e0f584fadf2abc7e7aaeefab0176f21e0fba5344a10e21838ec390fe",
"id_bytes": "L0aiMuD1hPrfKrx+eq7vqwF28h4PulNEoQ4hg47DkP4=",
"htlc_address": "bcrt1pcred99xc9l2cxphneej4ph6d6mczc0442d6ulyh3a9ad389z2aaq4n775y",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pcred99xc9l2cxphneej4ph6d6mczc0442d6ulyh3a9ad389z2aaq4n775y",
"server_message": ""
}
}
},
{
"time_ms": 1066,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: 2f46a232e0f584fadf2abc7e7aaeefab0176f21e0fba5344a10e21838ec390fe\n",
"HTLC address (P2TR): bcrt1pcred99xc9l2cxphneej4ph6d6mczc0442d6ulyh3a9ad389z2aaq4n775y\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 1067,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,127 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"--force",
"--fast",
"500000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 228730686,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769407086",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 139,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3875",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3793",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 139,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "",
"max_swap_routing_fee": "10010",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3875",
"max_prepay_amt": "1337",
"max_miner_fee": "948250",
"loop_out_channel": "0",
"outgoing_chan_set": [],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769407086",
"label": "",
"initiator": "loop-cli",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"is_external_addr": false,
"reservation_ids": [],
"payment_timeout": 0,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 228,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "46b8db64110a6008bd36b423439cbba1e8e76c0264e8a68f442fc22a3c2b4f6b",
"id_bytes": "RrjbZBEKYAi9NrQjQ5y7oejnbAJk6KaPRC/CKjwrT2s=",
"htlc_address": "bcrt1pj90ffd4j9hc2a58xpl3q77c6pf8ckhqhv9w9m2529kqsk77f6lksrca9pv",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pj90ffd4j9hc2a58xpl3q77c6pf8ckhqhv9w9m2529kqsk77f6lksrca9pv",
"server_message": ""
}
}
},
{
"time_ms": 228,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: 46b8db64110a6008bd36b423439cbba1e8e76c0264e8a68f442fc22a3c2b4f6b\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 228,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,36 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"notanumber"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "invalid amt value \"notanumber\"",
"duration": 1215843,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stderr",
"data": {
"lines": [
"[loop] invalid amt value \"notanumber\"\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {
"run_error": "invalid amt value \"notanumber\""
}
}
]
}

View file

@ -1,40 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--addr",
"bcrt1qtest",
"--account",
"testacct",
"--network",
"regtest",
"50000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "cannot set --addr and --account at the same time. Please specify only one source for a new address to sweep the loop amount to",
"duration": 1276860,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stderr",
"data": {
"lines": [
"[loop] cannot set --addr and --account at the same time. Please specify only one source for a new address to sweep the loop amount to\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {
"run_error": "cannot set --addr and --account at the same time. Please specify only one source for a new address to sweep the loop amount to"
}
}
]
}

View file

@ -1,40 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--account",
"testacct",
"--account_addr_type",
"foo",
"--network",
"regtest",
"50000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"run_error": "unknown account address type",
"duration": 1107998,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stderr",
"data": {
"lines": [
"[loop] unknown account address type\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {
"run_error": "unknown account address type"
}
}
]
}

View file

@ -1,135 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"--amt",
"500000",
"--channel",
"125344325763072",
"--max_swap_routing_fee",
"1000",
"--payment_timeout",
"30s",
"--force"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 288562226,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 161,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3873",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3791",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 161,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "",
"max_swap_routing_fee": "1000",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3873",
"max_prepay_amt": "1337",
"max_miner_fee": "947750",
"loop_out_channel": "0",
"outgoing_chan_set": [
"125344325763072"
],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769408886",
"label": "",
"initiator": "loop-cli",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"is_external_addr": false,
"reservation_ids": [],
"payment_timeout": 30,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 288,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "a564cdea5bac2fa1e350da6e7096ddd3bc0256c157f9f4b9b71a1262742f4f0d",
"id_bytes": "pWTN6lusL6HjUNpucJbd07wCVsFX+fS5txoSYnQvTw0=",
"htlc_address": "bcrt1pg84j6m04s6ecvlgput74dh4kfppn0lxn7f9h5cyl7vylyu3l55ss2f9xyh",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1pg84j6m04s6ecvlgput74dh4kfppn0lxn7f9h5cyl7vylyu3l55ss2f9xyh",
"server_message": ""
}
}
},
{
"time_ms": 288,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: a564cdea5bac2fa1e350da6e7096ddd3bc0256c157f9f4b9b71a1262742f4f0d\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 288,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,129 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"--amt",
"500000",
"--addr",
"bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw",
"--force"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 299201197,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 230,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3873",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3791",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 230,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw",
"max_swap_routing_fee": "10010",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3873",
"max_prepay_amt": "1337",
"max_miner_fee": "947750",
"loop_out_channel": "0",
"outgoing_chan_set": [],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769408886",
"label": "",
"initiator": "loop-cli",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"is_external_addr": true,
"reservation_ids": [],
"payment_timeout": 0,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 298,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "67712e10d785636ec4b19c0c3d0f7b7612c1de5ccb2cb81df94e32ff13914fab",
"id_bytes": "Z3EuENeFY27EsZwMPQ97dhLB3lzLLLgd+U4y/xORT6s=",
"htlc_address": "bcrt1p0fn96tdw5ngu9cpww0w8q96tcps6w7jezaf0xnw0wa7e5d5gxv3st47amw",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1p0fn96tdw5ngu9cpww0w8q96tcps6w7jezaf0xnw0wa7e5d5gxv3st47amw",
"server_message": ""
}
}
},
{
"time_ms": 298,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: 67712e10d785636ec4b19c0c3d0f7b7612c1de5ccb2cb81df94e32ff13914fab\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 299,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,128 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"--amt",
"500000",
"--force",
"bcrt1pfu9g59aqtxd39653f76y4c8z7r3t9tmcvrvhl57a3dgj3epdwxdqcd9fpw"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 290631176,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 205,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3873",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3791",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 205,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "",
"max_swap_routing_fee": "10010",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3873",
"max_prepay_amt": "1337",
"max_miner_fee": "947750",
"loop_out_channel": "0",
"outgoing_chan_set": [],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769408886",
"label": "",
"initiator": "loop-cli",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"is_external_addr": false,
"reservation_ids": [],
"payment_timeout": 0,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 290,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "aa7d0c558627aaa8af70503cd81d4679400cc9771063cb474c32c625d5f2a8f3",
"id_bytes": "qn0MVYYnqqivcFA82B1GeUAMyXcQY8tHTDLGJdXyqPM=",
"htlc_address": "bcrt1p9jhv4amddjazuyzs8xr2u2fzzqdt2s5tx95ffhd43avxvq7d0xxsykf5r5",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1p9jhv4amddjazuyzs8xr2u2fzzqdt2s5tx95ffhd43avxvq7d0xxsykf5r5",
"server_message": ""
}
}
},
{
"time_ms": 290,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: aa7d0c558627aaa8af70503cd81d4679400cc9771063cb474c32c625d5f2a8f3\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 290,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,131 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"out",
"--network",
"regtest",
"--amt",
"500000",
"--account",
"default",
"--account_addr_type",
"p2tr",
"--force"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 466934696,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 360,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3873",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3791",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 360,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "request",
"message_type": "looprpc.LoopOutRequest",
"payload": {
"amt": "500000",
"dest": "",
"max_swap_routing_fee": "10010",
"max_prepay_routing_fee": "36",
"max_swap_fee": "3873",
"max_prepay_amt": "1337",
"max_miner_fee": "947750",
"loop_out_channel": "0",
"outgoing_chan_set": [],
"sweep_conf_target": 9,
"htlc_confirmations": 1,
"swap_publication_deadline": "1769408886",
"label": "",
"initiator": "loop-cli",
"account": "default",
"account_addr_type": "TAPROOT_PUBKEY",
"is_external_addr": false,
"reservation_ids": [],
"payment_timeout": 0,
"asset_info": null,
"asset_rfq_info": null
}
}
},
{
"time_ms": 462,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOut",
"event": "response",
"message_type": "looprpc.SwapResponse",
"payload": {
"id": "04c1ef677f170f50ad3f221f49a779b99a464907bde2e0003a7b1dd6402d1419",
"id_bytes": "BMHvZ38XD1CtPyIfSad5uZpGSQe94uAAOnsd1kAtFBk=",
"htlc_address": "bcrt1p8lvg7l4zkdu0l4pe74h96240ndmp3demreml3zeqy28dvgq7j95q23zqcy",
"htlc_address_p2wsh": "",
"htlc_address_p2tr": "bcrt1p8lvg7l4zkdu0l4pe74h96240ndmp3demreml3zeqy28dvgq7j95q23zqcy",
"server_message": ""
}
}
},
{
"time_ms": 462,
"kind": "stdout",
"data": {
"lines": [
"Swap initiated\n",
"ID: 04c1ef677f170f50ad3f221f49a779b99a464907bde2e0003a7b1dd6402d1419\n",
"\n",
"Run `loop monitor` to monitor progress.\n"
]
}
},
{
"time_ms": 466,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,103 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"terms",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 199188467,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 7,
"kind": "stdout",
"data": {
"lines": [
"Loop Out\n",
"--------\n"
]
}
},
{
"time_ms": 7,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutTerms",
"event": "request",
"message_type": "looprpc.TermsRequest",
"payload": {}
}
},
{
"time_ms": 156,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutTerms",
"event": "response",
"message_type": "looprpc.OutTermsResponse",
"payload": {
"min_swap_amount": "50000",
"max_swap_amount": "1000000",
"min_cltv_delta": 50,
"max_cltv_delta": 250
}
}
},
{
"time_ms": 156,
"kind": "stdout",
"data": {
"lines": [
"Amount: 50000 - 1000000\n",
"Cltv delta: 50 - 250\n",
"\n",
"Loop In\n",
"------\n"
]
}
},
{
"time_ms": 156,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInTerms",
"event": "request",
"message_type": "looprpc.TermsRequest",
"payload": {}
}
},
{
"time_ms": 198,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInTerms",
"event": "response",
"message_type": "looprpc.InTermsResponse",
"payload": {
"min_swap_amount": "50000",
"max_swap_amount": "1000000"
}
}
},
{
"time_ms": 198,
"kind": "stdout",
"data": {
"lines": [
"Amount: 50000 - 1000000\n"
]
}
},
{
"time_ms": 199,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,76 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"out",
"--network",
"regtest",
"500000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 159166421,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "500000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 158,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3875",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3793",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 158,
"kind": "stdout",
"data": {
"lines": [
"Send off-chain: 500000 sat\n",
"Receive on-chain: 492332 sat\n",
"Estimated total fee: 7668 sat\n"
]
}
},
{
"time_ms": 159,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,92 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"in",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1248128,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
"NAME:\n",
" loop quote in - get a quote for the cost of a loop in swap\n",
"\n",
"USAGE:\n",
" loop quote in amt\n",
"\n",
"DESCRIPTION:\n",
" Allows to determine the cost of a swap up front.Either specify an amount or deposit outpoints.\n",
"\n",
"OPTIONS:\n"
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
" --last_hop string the pubkey of the last hop to use for the quote\n",
" --conf_target uint the target number of blocks the on-chain htlc broadcast by the swap client should confirm within (default: 0)\n",
" --verbose, -v show expanded details (default: false)\n",
" --private generates and passes routehints. Should be used if the connected node is only reachable via private channels (default: false)\n",
" --route_hints string [ --route_hints string ] "
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
" route hints that can each be individually used to assist in reaching the invoice's destination\n",
" --deposit_outpoint string [ --deposit_outpoint string ] one or more static address deposit outpoints to quote for. Deposit outpoints are not to be used in combination with an amount. Eachadditional outpoint can be added by specifying --deposit_outpoint tx_id:idx\n"
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
" --help, -h "
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
" "
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
"show help\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,55 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"out",
"--network",
"regtest"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 1687027,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
"NAME:"
]
}
},
{
"time_ms": 1,
"kind": "stdout",
"data": {
"lines": [
"\n",
" loop quote out - get a quote for the cost of a loop out swap\n",
"\n",
"USAGE:\n",
" loop quote out amt\n",
"\n",
"DESCRIPTION:\n",
" Allows to determine the cost of a swap up front\n",
"\n",
"OPTIONS:\n",
" --conf_target uint the number of blocks from the swap initiation height that the on-chain HTLC should be swept within in a Loop Out (default: 9)\n",
" --fast Indicate you want to swap immediately, paying potentially a higher fee. If not set the swap server might choose to wait up to 30 minutes before publishing the swap HTLC on-chain, to save on chain fees. Not setting this flag might result in a lower swap fee. (default: false)\n",
" --verbose, -v show expanded details (default: false)\n",
" --help, -h show help\n"
]
}
},
{
"time_ms": 1,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,114 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"in",
"--network",
"regtest",
"--deposit_outpoint",
"edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 622954327,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "UNKNOWN_STATE",
"outpoints": [
"edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0"
]
}
}
},
{
"time_ms": 165,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": [
{
"id": "aCYqEEyewyXea+w3uOMb2HW70vXwuc4togzwvWNvxEg=",
"state": "DEPOSITED",
"outpoint": "edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0",
"value": "500000",
"confirmation_height": "125",
"blocks_until_expiry": "14394",
"swap_hash": ""
}
]
}
}
},
{
"time_ms": 165,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "0",
"conf_target": 0,
"external_htlc": false,
"swap_publication_deadline": "0",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [
"edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0"
],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 622,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1825",
"htlc_publish_fee_sat": "0",
"cltv_delta": 0,
"conf_target": 0,
"quoted_amt": "500000"
}
}
},
{
"time_ms": 622,
"kind": "stdout",
"data": {
"lines": [
"Previously deposited on-chain: 500000 sat\n",
"Receive off-chain: 498175 sat\n",
"Estimated total fee: 1825 sat\n"
]
}
},
{
"time_ms": 622,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,76 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"in",
"--network",
"regtest",
"--last_hop",
"0271d6e29301159d9e1cc5d3983479a51f3b3c0c682eda7f16aa1f47dfe09b22f7",
"50000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 226964878,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "50000",
"conf_target": 0,
"external_htlc": false,
"swap_publication_deadline": "0",
"loop_in_last_hop": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 226,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1801",
"htlc_publish_fee_sat": "3873",
"cltv_delta": 0,
"conf_target": 6,
"quoted_amt": "50000"
}
}
},
{
"time_ms": 226,
"kind": "stdout",
"data": {
"lines": [
"Send on-chain: 50000 sat\n",
"Receive off-chain: 44326 sat\n",
"Estimated total fee: 5674 sat\n"
]
}
},
{
"time_ms": 226,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,93 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"out",
"--network",
"regtest",
"--verbose",
"50000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 273326957,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "50000",
"conf_target": 9,
"external_htlc": false,
"swap_publication_deadline": "1769408886",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 272,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/LoopOutQuote",
"event": "response",
"message_type": "looprpc.OutQuoteResponse",
"payload": {
"swap_fee_sat": "3828",
"prepay_amt_sat": "1337",
"htlc_sweep_fee_sat": "3791",
"swap_payment_dest": "AnHW4pMBFZ2eHMXTmDR5pR87PAxoLtp/FqofR9/gmyL3",
"cltv_delta": 0,
"conf_target": 9,
"asset_rfq_info": null
}
}
},
{
"time_ms": 273,
"kind": "stdout",
"data": {
"lines": [
"Send off-chain: 50000 sat\n",
"Receive on-chain: 42381 sat\n",
"\n",
"Estimated on-chain fee: 3791 sat\n",
"Loop service fee: 3828 sat\n",
"Estimated total fee: 7619 sat\n",
"\n"
]
}
},
{
"time_ms": 273,
"kind": "stdout",
"data": {
"lines": [
"No show penalty (prepay): 1337 sat\n",
"Conf target: 9 block\n",
"CLTV expiry delta: 0 block\n",
"Publication deadline: 2026-01-26 01:28:06 -0500 EST\n"
]
}
},
{
"time_ms": 273,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,81 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"quote",
"in",
"--network",
"regtest",
"--verbose",
"50000"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 380680881,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "request",
"message_type": "looprpc.QuoteRequest",
"payload": {
"amt": "50000",
"conf_target": 0,
"external_htlc": false,
"swap_publication_deadline": "0",
"loop_in_last_hop": "",
"loop_in_route_hints": [],
"private": false,
"deposit_outpoints": [],
"asset_info": null,
"auto_select_deposits": false,
"fast": false
}
}
},
{
"time_ms": 379,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLoopInQuote",
"event": "response",
"message_type": "looprpc.InQuoteResponse",
"payload": {
"swap_fee_sat": "1800",
"htlc_publish_fee_sat": "3871",
"cltv_delta": 0,
"conf_target": 6,
"quoted_amt": "50000"
}
}
},
{
"time_ms": 380,
"kind": "stdout",
"data": {
"lines": [
"Send on-chain: 50000 sat\n",
"Receive off-chain: 44329 sat\n",
"\n",
"Estimated on-chain fee: 3871 sat\n",
"Loop service fee: 1800 sat\n",
"Estimated total fee: 5671 sat\n",
"\n",
"Conf target: 6 block\n",
"CLTV expiry delta: 0 block\n"
]
}
},
{
"time_ms": 380,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,151 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--loopinsource",
"static-address",
"--minamt",
"250000",
"--maxamt",
"1000000",
"--feepercent",
"5",
"--autoloop",
"--autobudget",
"50000",
"--autoinflight",
"1"
],
"version": "0.33.0-beta commit=v0.33.0-beta-74-g6a3c3e1df5f807b450d3ba663f4824f6588e1c13 commit_hash=6a3c3e1df5f807b450d3ba663f4824f6588e1c13",
"duration": 32231921,
"clock_start_unix": 1779406641
},
"events": [
{
"time_ms": 10,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 16,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_IN",
"pubkey": "AgN4nvpxLgk471Ol2/D2BGUJ9l/FOlv8dRHcZ/6+oaFl",
"type": "THRESHOLD",
"incoming_threshold": 0,
"outgoing_threshold": 60
}
],
"fee_ppm": "50000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": true,
"autoloop_budget_sat": "50000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "250000",
"max_swap_amount": "1000000",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1779405910",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_STATIC_ADDRESS"
}
}
},
{
"time_ms": 18,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_IN",
"pubkey": "AgN4nvpxLgk471Ol2/D2BGUJ9l/FOlv8dRHcZ/6+oaFl",
"type": "THRESHOLD",
"incoming_threshold": 0,
"outgoing_threshold": 60
}
],
"fee_ppm": "50000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": true,
"autoloop_budget_sat": "50000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "250000",
"max_swap_amount": "1000000",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1779405910",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_STATIC_ADDRESS"
}
}
}
},
{
"time_ms": 31,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "response",
"message_type": "looprpc.SetLiquidityParamsResponse",
"payload": {}
}
},
{
"time_ms": 32,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,125 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"getparams",
"--network",
"regtest"
],
"version": "0.33.0-beta commit=v0.33.0-beta-74-g6a3c3e1df5f807b450d3ba663f4824f6588e1c13 commit_hash=6a3c3e1df5f807b450d3ba663f4824f6588e1c13",
"duration": 12324887,
"clock_start_unix": 1779406647
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 5,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [
{
"channel_id": "0",
"swap_type": "LOOP_IN",
"pubkey": "AgN4nvpxLgk471Ol2/D2BGUJ9l/FOlv8dRHcZ/6+oaFl",
"type": "THRESHOLD",
"incoming_threshold": 0,
"outgoing_threshold": 60
}
],
"fee_ppm": "50000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": true,
"autoloop_budget_sat": "50000",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "250000",
"max_swap_amount": "1000000",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1779405910",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_STATIC_ADDRESS"
}
}
},
{
"time_ms": 12,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"account\": \"\",\n",
" \"account_addr_type\": \"ADDRESS_TYPE_UNKNOWN\",\n",
" \"auto_max_in_flight\": \"1\",\n",
" \"autoloop\": true,\n",
" \"autoloop_budget_last_refresh\": \"1779405910\",\n",
" \"autoloop_budget_refresh_period_sec\": \"604800\",\n",
" \"autoloop_budget_sat\": \"50000\",\n",
" \"autoloop_budget_start_sec\": \"0\",\n",
" \"autoloop_dest_address\": \"\",\n",
" \"easy_asset_params\": {},\n",
" \"easy_autoloop\": false,\n",
" \"easy_autoloop_excluded_peers\": [],\n",
" \"easy_autoloop_local_target_sat\": \"0\",\n",
" \"failure_backoff_sec\": \"86400\",\n",
" \"fast_swap_publication\": true,\n",
" \"fee_ppm\": \"50000\",\n",
" \"htlc_conf_target\": 6,\n",
" \"loop_in_source\": \"LOOP_IN_SOURCE_STATIC_ADDRESS\",\n",
" \"max_miner_fee_sat\": \"0\",\n",
" \"max_prepay_routing_fee_ppm\": \"0\",\n",
" \"max_prepay_sat\": \"0\",\n",
" \"max_routing_fee_ppm\": \"0\",\n",
" \"max_swap_amount\": \"1000000\",\n",
" \"max_swap_fee_ppm\": \"0\",\n",
" \"min_swap_amount\": \"250000\",\n",
" \"rules\": [\n",
" {\n",
" \"channel_id\": \"0\",\n",
" \"incoming_threshold\": 0,\n",
" \"outgoing_threshold\": 60,\n",
" \"pubkey\": \"0203789efa712e0938ef53a5dbf0f6046509f65fc53a5bfc7511dc67febea1a165\",\n",
" \"swap_type\": \"LOOP_IN\",\n",
" \"type\": \"THRESHOLD\"\n",
" }\n",
" ],\n",
" \"sweep_conf_target\": 100,\n",
" \"sweep_fee_rate_sat_per_vbyte\": \"0\"\n",
"}\n"
]
}
},
{
"time_ms": 12,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,101 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"suggestswaps",
"--network",
"regtest"
],
"version": "0.33.0-beta commit=v0.33.0-beta-74-g6a3c3e1df5f807b450d3ba663f4824f6588e1c13 commit_hash=6a3c3e1df5f807b450d3ba663f4824f6588e1c13",
"duration": 44264819,
"clock_start_unix": 1779406653
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "request",
"message_type": "looprpc.SuggestSwapsRequest",
"payload": {}
}
},
{
"time_ms": 43,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SuggestSwaps",
"event": "response",
"message_type": "looprpc.SuggestSwapsResponse",
"payload": {
"loop_out": [],
"loop_in": [],
"static_loop_in": [
{
"outpoints": [
"9cfb0ec5ab2c851b4db5fe372cc3573f4074c436e50b13d2b7d214e45571def9:1"
],
"max_swap_fee_satoshis": "1828",
"last_hop": "AgN4nvpxLgk471Ol2/D2BGUJ9l/FOlv8dRHcZ/6+oaFl",
"label": "[reserved]: autoloop-in",
"initiator": "autoloop",
"route_hints": [],
"private": false,
"payment_timeout_seconds": 0,
"amount": "500000",
"fast": false
}
],
"disqualified": [
{
"channel_id": "127543348887552",
"pubkey": "",
"reason": "AUTO_REASON_CUSTOM_CHANNEL_DATA"
}
]
}
}
},
{
"time_ms": 43,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"disqualified\": [\n",
" {\n",
" \"channel_id\": \"127543348887552\",\n",
" \"pubkey\": \"\",\n",
" \"reason\": \"AUTO_REASON_CUSTOM_CHANNEL_DATA\"\n",
" }\n",
" ],\n",
" \"loop_in\": [],\n",
" \"loop_out\": [],\n",
" \"static_loop_in\": [\n",
" {\n",
" \"amount\": \"500000\",\n",
" \"fast\": false,\n",
" \"initiator\": \"autoloop\",\n",
" \"label\": \"[reserved]: autoloop-in\",\n",
" \"last_hop\": \"0203789efa712e0938ef53a5dbf0f6046509f65fc53a5bfc7511dc67febea1a165\",\n",
" \"max_swap_fee_satoshis\": \"1828\",\n",
" \"outpoints\": [\n",
" \"9cfb0ec5ab2c851b4db5fe372cc3573f4074c436e50b13d2b7d214e45571def9:1\"\n",
" ],\n",
" \"payment_timeout_seconds\": 0,\n",
" \"private\": false,\n",
" \"route_hints\": []\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 44,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,137 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"setparams",
"--network",
"regtest",
"--loopinsource",
"static-address"
],
"version": "0.33.0-beta commit=v0.33.0-beta-74-g6a3c3e1df5f807b450d3ba663f4824f6588e1c13 commit_hash=6a3c3e1df5f807b450d3ba663f4824f6588e1c13",
"run_error": "rpc error: code = FailedPrecondition desc = static address autoloop requires --experimental",
"duration": 12501068,
"clock_start_unix": 1779406912
},
"events": [
{
"time_ms": 4,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "request",
"message_type": "looprpc.GetLiquidityParamsRequest",
"payload": {}
}
},
{
"time_ms": 11,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/GetLiquidityParams",
"event": "response",
"message_type": "looprpc.LiquidityParameters",
"payload": {
"rules": [],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1779406903",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_WALLET"
}
}
},
{
"time_ms": 11,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "request",
"message_type": "looprpc.SetLiquidityParamsRequest",
"payload": {
"parameters": {
"rules": [],
"fee_ppm": "20000",
"sweep_fee_rate_sat_per_vbyte": "0",
"max_swap_fee_ppm": "0",
"max_routing_fee_ppm": "0",
"max_prepay_routing_fee_ppm": "0",
"max_prepay_sat": "0",
"max_miner_fee_sat": "0",
"sweep_conf_target": 100,
"failure_backoff_sec": "86400",
"autoloop": false,
"autoloop_budget_sat": "335544",
"autoloop_budget_start_sec": "0",
"auto_max_in_flight": "1",
"min_swap_amount": "0",
"max_swap_amount": "0",
"htlc_conf_target": 6,
"autoloop_dest_address": "",
"autoloop_budget_refresh_period_sec": "604800",
"autoloop_budget_last_refresh": "1779406903",
"easy_autoloop": false,
"easy_autoloop_local_target_sat": "0",
"account": "",
"account_addr_type": "ADDRESS_TYPE_UNKNOWN",
"easy_asset_params": {},
"fast_swap_publication": true,
"easy_autoloop_excluded_peers": [],
"loop_in_source": "LOOP_IN_SOURCE_STATIC_ADDRESS"
}
}
}
},
{
"time_ms": 12,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/SetLiquidityParams",
"event": "error",
"error": "rpc error: code = FailedPrecondition desc = static address autoloop requires --experimental",
"status": {
"code": 9,
"message": "static address autoloop requires --experimental"
}
}
},
{
"time_ms": 12,
"kind": "stderr",
"data": {
"lines": [
"[loop] rpc error: code = FailedPrecondition desc = static address autoloop requires --experimental\n"
]
}
},
{
"time_ms": 12,
"kind": "exit",
"data": {
"run_error": "rpc error: code = FailedPrecondition desc = static address autoloop requires --experimental"
}
}
]
}

View file

@ -1,62 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"deposited"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 304503708,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "DEPOSITED",
"outpoints": []
}
}
},
{
"time_ms": 303,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": []
}
}
},
{
"time_ms": 304,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": []\n",
"}\n"
]
}
},
{
"time_ms": 304,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,82 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"withdrawing"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 210052149,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "WITHDRAWING",
"outpoints": []
}
}
},
{
"time_ms": 209,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": [
{
"id": "u38FDfC3w+H+YQEOEK1F4w3fes0wH6bgWi3bgltcLvs=",
"state": "WITHDRAWING",
"outpoint": "56cd081a3a6eadf25b7d3fe0b61207389352ed69a622d2ec28c5d669bf6a5313:0",
"value": "500000",
"confirmation_height": "160",
"blocks_until_expiry": "14395",
"swap_hash": ""
}
]
}
}
},
{
"time_ms": 209,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": [\n",
" {\n",
" \"blocks_until_expiry\": \"14395\",\n",
" \"confirmation_height\": \"160\",\n",
" \"id\": \"bb7f050df0b7c3e1fe61010e10ad45e30ddf7acd301fa6e05a2ddb825b5c2efb\",\n",
" \"outpoint\": \"56cd081a3a6eadf25b7d3fe0b61207389352ed69a622d2ec28c5d669bf6a5313:0\",\n",
" \"state\": \"WITHDRAWING\",\n",
" \"swap_hash\": \"\",\n",
" \"value\": \"500000\"\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 210,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,136 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"withdrawn"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 271219039,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "WITHDRAWN",
"outpoints": []
}
}
},
{
"time_ms": 269,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": [
{
"id": "aCYqEEyewyXea+w3uOMb2HW70vXwuc4togzwvWNvxEg=",
"state": "WITHDRAWN",
"outpoint": "edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0",
"value": "500000",
"confirmation_height": "125",
"blocks_until_expiry": "14360",
"swap_hash": ""
},
{
"id": "hrXizflpTI5zmOQq/eEJdm180hQiA5Bbpj+9DrE3DvM=",
"state": "WITHDRAWN",
"outpoint": "bb358e4f73ae97c4e2d99c6d64e852bba7cf56e13105b05d1200b8ae1796665e:0",
"value": "500000",
"confirmation_height": "127",
"blocks_until_expiry": "14362",
"swap_hash": ""
},
{
"id": "bCkPdTbqUJeUav/6xqaZBqJtd1gj67rO3+by1pwHReQ=",
"state": "WITHDRAWN",
"outpoint": "5eaa7dd7a291665393eddf5dece91feef901f22665933cce7a0732a9b81c3001:0",
"value": "500000",
"confirmation_height": "128",
"blocks_until_expiry": "14363",
"swap_hash": ""
},
{
"id": "AYK02JWxxGcpCue1xsQv92sqQiWAepQhHJcxcNWog+s=",
"state": "WITHDRAWN",
"outpoint": "7e6360d6e6a394cfd096adf0bfe1275c5a83541eb573e90e463a78dc715f8894:0",
"value": "500000",
"confirmation_height": "142",
"blocks_until_expiry": "14377",
"swap_hash": ""
}
]
}
}
},
{
"time_ms": 269,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": [\n",
" {\n",
" \"blocks_until_expiry\": \"14360\",\n",
" \"confirmation_height\": \"125\",\n",
" \"id\": \"68262a104c9ec325de6bec37b8e31bd875bbd2f5f0b9ce2da20cf0bd636fc448\",\n",
" \"outpoint\": \"edcdab8f0b1138d853a453b8b7a5ac3c694bd53ad38b7ccf062e45f99440e6e6:0\",\n",
" \"state\": \"WITHDRAWN\",\n",
" \"swap_hash\": \"\",\n",
" \"value\": \"500000\"\n",
" },\n",
" {\n",
" \"blocks_until_expiry\": \"14362\",\n",
" \"confirmation_height\": \"127\",\n",
" \"id\": \"86b5e2cdf9694c8e7398e42afde109766d7cd2142203905ba63fbd0eb1370ef3\",\n",
" \"outpoint\": \"bb358e4f73ae97c4e2d99c6d64e852bba7cf56e13105b05d1200b8ae1796665e:0\",\n",
" \"state\": \"WITHDRAWN\",\n",
" \"swap_hash\": \"\",\n",
" \"value\": \"500000\"\n",
" },\n",
" {\n",
" \"blocks_until_expiry\": \"14363\",\n",
" \"confirmation_height\": \"128\",\n",
" \"id\": \"6c290f7536ea5097946afffac6a69906a26d775823ebbacedfe6f2d69c0745e4\",\n",
" \"outpoint\": \"5eaa7dd7a291665393eddf5dece91feef901f22665933cce7a0732a9b81c3001:0\",\n",
" \"state\": \"WITHDRAWN\",\n",
" \"swap_hash\": \"\",\n",
" \"value\": \"500000\"\n",
" },\n",
" {\n",
" \"blocks_until_expiry\": \"14377\",\n",
" \"confirmation_height\": \"142\",\n",
" \"id\": \"0182b4d895b1c467290ae7b5c6c42ff76b2a4225807a94211c973170d5a883eb\",\n",
" \"outpoint\": \"7e6360d6e6a394cfd096adf0bfe1275c5a83541eb573e90e463a78dc715f8894:0\",\n",
" \"state\": \"WITHDRAWN\",\n",
" \"swap_hash\": \"\",\n",
" \"value\": \"500000\"\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 271,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,62 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"looping_in"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 268850770,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "LOOPING_IN",
"outpoints": []
}
}
},
{
"time_ms": 268,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": []
}
}
},
{
"time_ms": 268,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": []\n",
"}\n"
]
}
},
{
"time_ms": 268,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,82 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"looped_in"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 169458759,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 3,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "LOOPED_IN",
"outpoints": []
}
}
},
{
"time_ms": 168,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": [
{
"id": "j71tovlF3ikFqn+pOGB0TZOH00ZEhDYOluRnpR3jvJ0=",
"state": "LOOPED_IN",
"outpoint": "9fa0d5dd5348794aa0541dd2729497f0907890606d044e1c4757bdc848f38df8:0",
"value": "500000",
"confirmation_height": "148",
"blocks_until_expiry": "14383",
"swap_hash": "hDAjN0JANkGTlqt5ZN14uFsaSBqfHbc9tc3e5XwkQ+c="
}
]
}
}
},
{
"time_ms": 168,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": [\n",
" {\n",
" \"blocks_until_expiry\": \"14383\",\n",
" \"confirmation_height\": \"148\",\n",
" \"id\": \"8fbd6da2f945de2905aa7fa93860744d9387d3464484360e96e467a51de3bc9d\",\n",
" \"outpoint\": \"9fa0d5dd5348794aa0541dd2729497f0907890606d044e1c4757bdc848f38df8:0\",\n",
" \"state\": \"LOOPED_IN\",\n",
" \"swap_hash\": \"84302337424036419396ab7964dd78b85b1a481a9f1db73db5cddee57c2443e7\",\n",
" \"value\": \"500000\"\n",
" }\n",
" ]\n",
"}\n"
]
}
},
{
"time_ms": 169,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,62 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"publish_expired_deposit"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 230057326,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 2,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "PUBLISH_EXPIRED",
"outpoints": []
}
}
},
{
"time_ms": 229,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": []
}
}
},
{
"time_ms": 230,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": []\n",
"}\n"
]
}
},
{
"time_ms": 230,
"kind": "exit",
"data": {}
}
]
}

View file

@ -1,62 +0,0 @@
{
"metadata": {
"args": [
"/home/user/bin/loop",
"static",
"listdeposits",
"--network",
"regtest",
"--filter",
"sweep_htlc_timeout"
],
"env": {
"HOME": "/home/user"
},
"version": "0.31.7-beta commit=v0.31.7-beta-28-g6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845 commit_hash=6d8ddfc59ddc2dcfd1a9b4e4b3c53a9cf15dd845",
"duration": 224559305,
"clock_start_unix": 1769407086
},
"events": [
{
"time_ms": 1,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "request",
"message_type": "looprpc.ListStaticAddressDepositsRequest",
"payload": {
"state_filter": "SWEEP_HTLC_TIMEOUT",
"outpoints": []
}
}
},
{
"time_ms": 223,
"kind": "grpc",
"data": {
"method": "/looprpc.SwapClient/ListStaticAddressDeposits",
"event": "response",
"message_type": "looprpc.ListStaticAddressDepositsResponse",
"payload": {
"filtered_deposits": []
}
}
},
{
"time_ms": 224,
"kind": "stdout",
"data": {
"lines": [
"{\n",
" \"filtered_deposits\": []\n",
"}\n"
]
}
},
{
"time_ms": 224,
"kind": "exit",
"data": {}
}
]
}

Some files were not shown because too many files have changed in this diff Show more