From d3745de943572eeda82c2a63c18c5cd891a75578 Mon Sep 17 00:00:00 2001 From: adrianbrad Date: Sat, 27 Jan 2024 19:06:03 +0200 Subject: [PATCH] feat: add new mempool transaction types and improve the mempool fetching logic. bump deps --- btcsuite/btcsuite_rpc_client.go | 22 ++++++ chain_reorg.go | 6 +- chain_reorg_test.go | 62 ++++++++--------- go.mod | 39 ++++++----- go.sum | 112 ++++++++++++++++--------------- mock/mock_rpc_client.go | 50 ++++++++++++++ node.go | 114 +++++++++++++++++++++++++++----- rpc_client.go | 4 +- transaction.go | 12 ++++ tview/tview_data.go | 74 ++++++--------------- tview/tview_models.go | 28 ++------ tview/tview_tui.go | 11 ++- tview/tview_views.go | 53 +++++++++------ 13 files changed, 363 insertions(+), 224 deletions(-) diff --git a/btcsuite/btcsuite_rpc_client.go b/btcsuite/btcsuite_rpc_client.go index e65fd50..adf6cda 100644 --- a/btcsuite/btcsuite_rpc_client.go +++ b/btcsuite/btcsuite_rpc_client.go @@ -364,6 +364,28 @@ func (c RPCClient) GetCoinbaseValue(context.Context) (int64, error) { return v, nil } +// GetTransactionOutputs returns the outputs of a transaction. +func (c RPCClient) GetTransactionOutputs( + ctx context.Context, + txHash string, +) ([]privatebtc.MempoolTransactionOutput, error) { + tx, err := c.GetTransaction(ctx, txHash) + if err != nil { + return nil, fmt.Errorf("get transaction: %w", err) + } + + outputs := make([]privatebtc.MempoolTransactionOutput, len(tx.Vout)) + + for i, v := range tx.Vout { + outputs[i] = privatebtc.MempoolTransactionOutput{ + Address: v.ScriptPubKey.Address, + Value: v.Value, + } + } + + return outputs, nil +} + var _ privatebtc.RPCClientFactory = (*RPCClientFactory)(nil) // RPCClientFactory is a factory for RPC clients. diff --git a/chain_reorg.go b/chain_reorg.go index 2c332f7..ad28272 100644 --- a/chain_reorg.go +++ b/chain_reorg.go @@ -439,7 +439,7 @@ func (c *ChainReorgWithAssertion) MineBlocksOnNetwork( discBestBlockHash, err := c.ChainReorg.disconnectedNode.RPCClient().GetBestBlockHash(ctx) if err != nil { - return nil, fmt.Errorf("get disconnected node best block hash: %w", err) + return nil, fmt.Errorf("get disconnected node best block Hash: %w", err) } if bestBlockHash == discBestBlockHash { @@ -516,7 +516,7 @@ func (c *ChainReorgWithAssertion) ReconnectNode(ctx context.Context) error { if syncNetwork := dcBc > nBc; syncNetwork { blockHash, err := c.disconnectedNode.RPCClient().GetBestBlockHash(ctx) if err != nil { - return fmt.Errorf("get disconnected node best block hash: %w", err) + return fmt.Errorf("get disconnected node best block Hash: %w", err) } const timeout = 5 * time.Second @@ -532,7 +532,7 @@ func (c *ChainReorgWithAssertion) ReconnectNode(ctx context.Context) error { if syncDisconnected := nBc > dcBc; syncDisconnected { blockHash, err := c.networkNodes[0].RPCClient().GetBestBlockHash(ctx) if err != nil { - return fmt.Errorf("get network best block hash: %w", err) + return fmt.Errorf("get network best block Hash: %w", err) } const timeout = 5 * time.Second diff --git a/chain_reorg_test.go b/chain_reorg_test.go index 2428973..ef46409 100644 --- a/chain_reorg_test.go +++ b/chain_reorg_test.go @@ -264,11 +264,11 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } c.GetRawMempoolFunc = func(ctx context.Context) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } disconnectedNodeRPCClient := newChainReorgSuccessRPCClient(peerCount) @@ -295,7 +295,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: require.NoError, - expectedHash: "hash", + expectedHash: "Hash", }, "MustDisconnectFirstError": { chainReorgArgs: chainReorgArgs{ @@ -369,7 +369,7 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } c.GetRawMempoolFunc = func(ctx context.Context) ([]string, error) { @@ -409,11 +409,11 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } c.GetRawMempoolFunc = func(ctx context.Context) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } disconnectedNodeRPCClient := newChainReorgSuccessRPCClient(peerCount) @@ -464,11 +464,11 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } c.GetRawMempoolFunc = func(ctx context.Context) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } return newPrivateNetworkStartSuccessRPCClientFactory( @@ -552,13 +552,13 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } disconenctedNodeRPCClient.GetRawMempoolFunc = func( ctx context.Context, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } networkRPCClient := newChainReorgSuccessRPCClient(peerCount) @@ -585,7 +585,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: require.NoError, - expectedHash: "hash", + expectedHash: "Hash", }, "MustDisconnectFirstError": { chainReorgArgs: chainReorgArgs{ @@ -659,7 +659,7 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } c.GetRawMempoolFunc = func(ctx context.Context) ([]string, error) { @@ -698,13 +698,13 @@ func TestChainReorg(t *testing.T) { string, float64, ) (string, error) { - return "hash", nil + return "Hash", nil } disconnectedNodeRPCClient.GetRawMempoolFunc = func( ctx context.Context, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } networkRPCClient := newChainReorgSuccessRPCClient(peerCount) @@ -805,13 +805,13 @@ func TestChainReorg(t *testing.T) { int64, string, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } networkClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } return newRPCClientFactoryWithDetachedNode( @@ -828,7 +828,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: require.NoError, - expectedHashes: []string{"hash"}, + expectedHashes: []string{"Hash"}, }, "MustDisconnectFirstError": { chainReorgArgs: chainReorgArgs{ @@ -898,7 +898,7 @@ func TestChainReorg(t *testing.T) { int64, string, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } c.GetBestBlockHashFunc = func(ctx context.Context) (string, error) { @@ -945,13 +945,13 @@ func TestChainReorg(t *testing.T) { int64, string, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } networkClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } return newRPCClientFactoryWithDetachedNode( @@ -968,7 +968,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: func(t require.TestingT, err error, i ...any) { - require.ErrorContains(t, err, "get disconnected node best block hash") + require.ErrorContains(t, err, "get disconnected node best block Hash") require.ErrorIs(t, err, assert.AnError) }, expectedHashes: nil, @@ -987,11 +987,11 @@ func TestChainReorg(t *testing.T) { int64, string, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } c.GetBestBlockHashFunc = func(ctx context.Context) (string, error) { - return "hash", nil + return "Hash", nil } return newPrivateNetworkStartSuccessRPCClientFactory(c) @@ -1063,7 +1063,7 @@ func TestChainReorg(t *testing.T) { networkNodeClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } disconnectedNodeClient := newChainReorgSuccessRPCClient(peerCount) @@ -1073,13 +1073,13 @@ func TestChainReorg(t *testing.T) { int64, string, ) ([]string, error) { - return []string{"hash"}, nil + return []string{"Hash"}, nil } disconnectedNodeClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } return newRPCClientFactoryWithDetachedNode( @@ -1096,7 +1096,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: require.NoError, - expectedHashes: []string{"hash"}, + expectedHashes: []string{"Hash"}, }, "MustDisconnectFirstError": { chainReorgArgs: chainReorgArgs{ @@ -1359,7 +1359,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: func(t require.TestingT, err error, i ...any) { - require.ErrorContains(t, err, "get disconnected node best block hash") + require.ErrorContains(t, err, "get disconnected node best block Hash") require.ErrorIs(t, err, assert.AnError) }, }, @@ -1400,7 +1400,7 @@ func TestChainReorg(t *testing.T) { disconnectedNodeClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } return newRPCClientFactoryWithDetachedNode( @@ -1464,7 +1464,7 @@ func TestChainReorg(t *testing.T) { }, newChainReorgFunc: newChainReorgWithDisconnect, assertErr: func(t require.TestingT, err error, i ...any) { - require.ErrorContains(t, err, "get network best block hash") + require.ErrorContains(t, err, "get network best block Hash") require.ErrorIs(t, err, assert.AnError) }, }, @@ -1491,7 +1491,7 @@ func TestChainReorg(t *testing.T) { networkNodeClient.GetBestBlockHashFunc = func( ctx context.Context, ) (string, error) { - return "hash", nil + return "Hash", nil } disconnectedNodeClient := newChainReorgSuccessRPCClient(peerCount) diff --git a/go.mod b/go.mod index d1af668..4e6c131 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,17 @@ require ( github.com/avast/retry-go v3.0.0+incompatible github.com/btcsuite/btcd v0.24.0 github.com/btcsuite/btcd/btcutil v1.1.5 - github.com/docker/docker v24.0.7+incompatible + github.com/docker/docker v25.0.1+incompatible github.com/docker/go-connections v0.5.0 github.com/gdamore/tcell/v2 v2.7.0 + github.com/gorilla/websocket v1.5.1 github.com/matryer/is v1.4.1 github.com/ory/dockertest/v3 v3.10.0 - github.com/rivo/tview v0.0.0-20240101144852-b3bd1aa5e9f2 + github.com/rivo/tview v0.0.0-20240122063236-8526c9fe1b54 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.27.0 - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a golang.org/x/sync v0.6.0 ) @@ -31,7 +32,7 @@ require ( github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/containerd/containerd v1.7.11 // indirect + github.com/containerd/containerd v1.7.12 // indirect github.com/containerd/continuity v0.4.3 // indirect github.com/containerd/log v0.1.0 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect @@ -39,18 +40,19 @@ require ( github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/docker/cli v24.0.7+incompatible // indirect - github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/cli v25.0.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gdamore/encoding v1.0.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/compress v1.17.5 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect @@ -59,15 +61,16 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.1.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/opencontainers/image-spec v1.1.0-rc6 // indirect github.com/opencontainers/runc v1.1.11 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.5 // indirect github.com/shirou/gopsutil/v3 v3.23.12 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -78,15 +81,19 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - golang.org/x/crypto v0.17.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect + golang.org/x/net v0.20.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/grpc v1.60.1 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/grpc v1.61.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index a7084d4..ce52c14 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHS github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= @@ -25,8 +24,6 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.4 h1:mWvWRLRIPuoeZsVRpc0xNCkfeNxWy1E4jIZ06ZpGI1A= -github.com/btcsuite/btcd/btcutil v1.1.4/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= @@ -47,8 +44,8 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= -github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= +github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -72,24 +69,27 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3 github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= -github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU= +github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA= +github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.7.0 h1:I5LiGTQuwrysAt1KS9wg1yFfOI3arI3ucFrxtd/xqaA= github.com/gdamore/tcell/v2 v2.7.0/go.mod h1:hl/KtAANGBecfIPxk+FzKvThTqI84oplgbPEmVX60b8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -118,13 +118,14 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -133,8 +134,8 @@ github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlT github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E= +github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -158,6 +159,8 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -173,10 +176,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= -github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40= -github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M= +github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= +github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/runc v1.1.11 h1:9LjxyVlE0BPMRP2wuQDRlHV4941Jp9rc3F0+YKimopA= github.com/opencontainers/runc v1.1.11/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= @@ -188,19 +189,15 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/rivo/tview v0.0.0-20231206124440-5f078138442e h1:mPy47VW9tkqImnSPgcjnEHJuG3XHDBtXj2hDb1qBrRs= -github.com/rivo/tview v0.0.0-20231206124440-5f078138442e/go.mod h1:c0SPlNPXkM+/Zgjn/0vD3W0Ds1yxstN7lpquqLDpWCg= -github.com/rivo/tview v0.0.0-20240101144852-b3bd1aa5e9f2 h1:Q41smlaCKxGtMlRwvZchzy7iDXAk89Wj5wMhlZXkpMI= -github.com/rivo/tview v0.0.0-20240101144852-b3bd1aa5e9f2/go.mod h1:c0SPlNPXkM+/Zgjn/0vD3W0Ds1yxstN7lpquqLDpWCg= +github.com/rivo/tview v0.0.0-20240122063236-8526c9fe1b54 h1:O2sPgzemzBPoeLuVrIyyNPwFxWqgh/AuAOfd65OIqMc= +github.com/rivo/tview v0.0.0-20240122063236-8526c9fe1b54/go.mod h1:c0SPlNPXkM+/Zgjn/0vD3W0Ds1yxstN7lpquqLDpWCg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.5 h1:I1LlsYBcfG4ggKsNaL6bi8pL7Z8SizVPVBrh3Y8F/BA= +github.com/rivo/uniseg v0.4.5/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -243,17 +240,31 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= -golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -271,16 +282,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -305,14 +314,12 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= @@ -331,18 +338,19 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= +google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/mock/mock_rpc_client.go b/mock/mock_rpc_client.go index 3514e94..63a8721 100644 --- a/mock/mock_rpc_client.go +++ b/mock/mock_rpc_client.go @@ -52,6 +52,9 @@ var _ privatebtc.RPCClient = &RPCClient{} // GetTransactionFunc: func(ctx context.Context, txHash string) (*privatebtc.Transaction, error) { // panic("mock out the GetTransaction method") // }, +// GetTransactionOutputsFunc: func(ctx context.Context, txHash string) ([]privatebtc.MempoolTransactionOutput, error) { +// panic("mock out the GetTransactionOutputs method") +// }, // ListAddressesFunc: func(ctx context.Context) ([]string, error) { // panic("mock out the ListAddresses method") // }, @@ -104,6 +107,9 @@ type RPCClient struct { // GetTransactionFunc mocks the GetTransaction method. GetTransactionFunc func(ctx context.Context, txHash string) (*privatebtc.Transaction, error) + // GetTransactionOutputsFunc mocks the GetTransactionOutputs method. + GetTransactionOutputsFunc func(ctx context.Context, txHash string) ([]privatebtc.MempoolTransactionOutput, error) + // ListAddressesFunc mocks the ListAddresses method. ListAddressesFunc func(ctx context.Context) ([]string, error) @@ -185,6 +191,13 @@ type RPCClient struct { // TxHash is the txHash argument value. TxHash string } + // GetTransactionOutputs holds details about calls to the GetTransactionOutputs method. + GetTransactionOutputs []struct { + // Ctx is the ctx argument value. + Ctx context.Context + // TxHash is the txHash argument value. + TxHash string + } // ListAddresses holds details about calls to the ListAddresses method. ListAddresses []struct { // Ctx is the ctx argument value. @@ -227,6 +240,7 @@ type RPCClient struct { lockGetNewAddress sync.RWMutex lockGetRawMempool sync.RWMutex lockGetTransaction sync.RWMutex + lockGetTransactionOutputs sync.RWMutex lockListAddresses sync.RWMutex lockRemovePeer sync.RWMutex lockSendCustomTransaction sync.RWMutex @@ -609,6 +623,42 @@ func (mock *RPCClient) GetTransactionCalls() []struct { return calls } +// GetTransactionOutputs calls GetTransactionOutputsFunc. +func (mock *RPCClient) GetTransactionOutputs(ctx context.Context, txHash string) ([]privatebtc.MempoolTransactionOutput, error) { + if mock.GetTransactionOutputsFunc == nil { + panic("RPCClient.GetTransactionOutputsFunc: method is nil but RPCClient.GetTransactionOutputs was just called") + } + callInfo := struct { + Ctx context.Context + TxHash string + }{ + Ctx: ctx, + TxHash: txHash, + } + mock.lockGetTransactionOutputs.Lock() + mock.calls.GetTransactionOutputs = append(mock.calls.GetTransactionOutputs, callInfo) + mock.lockGetTransactionOutputs.Unlock() + return mock.GetTransactionOutputsFunc(ctx, txHash) +} + +// GetTransactionOutputsCalls gets all the calls that were made to GetTransactionOutputs. +// Check the length with: +// +// len(mockedRPCClient.GetTransactionOutputsCalls()) +func (mock *RPCClient) GetTransactionOutputsCalls() []struct { + Ctx context.Context + TxHash string +} { + var calls []struct { + Ctx context.Context + TxHash string + } + mock.lockGetTransactionOutputs.RLock() + calls = mock.calls.GetTransactionOutputs + mock.lockGetTransactionOutputs.RUnlock() + return calls +} + // ListAddresses calls ListAddressesFunc. func (mock *RPCClient) ListAddresses(ctx context.Context) ([]string, error) { if mock.ListAddressesFunc == nil { diff --git a/node.go b/node.go index 3349aae..463b69a 100644 --- a/node.go +++ b/node.go @@ -6,10 +6,13 @@ import ( "fmt" "io" "strings" + "sync" "sync/atomic" "time" "github.com/avast/retry-go" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" "golang.org/x/sync/errgroup" ) @@ -46,7 +49,7 @@ func (n Node) RPCClient() RPCClient { // Fund is a helper function for funding a node. // It generates a new address to the block and mines 101 blocks to it, -// returning the hash of the last block that was mined +// returning the Hash of the last block that was mined // This method will fund the node wallet with 50 BTC. func (n Node) Fund(ctx context.Context) (string, error) { addr, err := n.RPCClient().GetNewAddress(ctx, "fund") @@ -68,18 +71,16 @@ func (n Node) Fund(ctx context.Context) (string, error) { func (n Node) DisconnectFromNetwork(ctx context.Context) error { eg, egCtx := errgroup.WithContext(ctx) - for i, node := range n.pn.nodes { - if node == n { + for i := range n.pn.nodes { + node := n.pn.nodes[i] + + if node.id == n.id { continue } - node := node - i := i - eg.Go(func() error { - err := node.RPCClient().RemovePeer(egCtx, n) - if err != nil { - return fmt.Errorf("add node %d: %w", i, err) + if err := node.RPCClient().RemovePeer(egCtx, n); err != nil { + return fmt.Errorf("remove peer %d from node %d: %w", n.id, node.id, err) } return nil @@ -94,7 +95,7 @@ func (n Node) ConnectToNetwork(ctx context.Context) error { eg, egCtx := errgroup.WithContext(ctx) for i, node := range n.pn.nodes { - if node == n { + if node.id == n.id { continue } @@ -165,7 +166,7 @@ func (nodes Nodes) Sync(ctx context.Context, toBlockHash string) error { eg.Go(func() error { blockHash, err := nodes[i].RPCClient().GetBestBlockHash(egCtx) if err != nil { - return fmt.Errorf("get best block hash for node %d: %w", i, err) + return fmt.Errorf("get best block Hash for node %d: %w", i, err) } if blockHash != toBlockHash { @@ -276,6 +277,85 @@ func (nodes Nodes) EnsureTransactionNotInAnyMempool(ctx context.Context, txHash return eg.Wait() } +// NetworkMempoolTransaction represents a transaction in the network mempool. +type NetworkMempoolTransaction struct { + MempoolTransaction + Nodes []int +} + +// NetworkMempool represents the network mempool. +type NetworkMempool map[string]*NetworkMempoolTransaction + +// Hashes returns the hashes of the network mempool transactions. +func (m NetworkMempool) Hashes() []string { + hashes := maps.Keys(m) + + slices.Sort(hashes) + + return hashes +} + +// NetworkMempool returns all the transactions aggregated from the nodes mempools. +func (nodes Nodes) NetworkMempool(ctx context.Context) (NetworkMempool, error) { + mempoolTransactions := NetworkMempool{} + + var mutex sync.Mutex + + eg, egCtx := errgroup.WithContext(ctx) + + for i := range nodes { + i := i + + eg.Go(func() error { + nodeMempoolTxs, err := nodes[i].RPCClient().GetRawMempool(egCtx) + if err != nil { + return fmt.Errorf("get mempool for node %d: %w", i, err) + } + + for _, txHash := range nodeMempoolTxs { + mutex.Lock() + + if mpTx, exists := mempoolTransactions[txHash]; exists { + mpTx.Nodes = append(mpTx.Nodes, i) + + mutex.Unlock() + + continue + } + + txOutputs, err := nodes[i].RPCClient().GetTransactionOutputs(egCtx, txHash) + if err != nil { + mutex.Unlock() + + return fmt.Errorf("get transaction outputs: %w", err) + } + + mempoolTransactions[txHash] = &NetworkMempoolTransaction{ + MempoolTransaction: MempoolTransaction{ + Hash: txHash, + Outputs: txOutputs, + }, + Nodes: []int{i}, + } + + mutex.Unlock() + } + + return nil + }) + } + + if err := eg.Wait(); err != nil { + return NetworkMempool{}, fmt.Errorf("fetch nodes mempools: %w", err) + } + + for i := range mempoolTransactions { + slices.Sort(mempoolTransactions[i].Nodes) + } + + return mempoolTransactions, nil +} + // Balance is a balance of a node. type Balance struct { Trusted float64 @@ -295,14 +375,14 @@ func connectNodes(ctx context.Context, nodes []Node) error { eg, egCtx := errgroup.WithContext(ctx) - for i, node := range nodes { - for j := i + 1; j < len(nodes); j++ { - j := j - node := node + for nodeIdx := range nodes { + for nextNodeIdx := nodeIdx + 1; nextNodeIdx < len(nodes); nextNodeIdx++ { + nextNode := nodes[nextNodeIdx] + node := nodes[nodeIdx] eg.Go(func() error { - if err := node.RPCClient().AddPeer(egCtx, nodes[j]); err != nil { - return fmt.Errorf("add node %d: %w", j, err) + if err := node.RPCClient().AddPeer(egCtx, nextNode); err != nil { + return fmt.Errorf("add node %d: %w", nextNode.id, err) } return nil diff --git a/rpc_client.go b/rpc_client.go index 6b3831d..784f9b2 100644 --- a/rpc_client.go +++ b/rpc_client.go @@ -44,7 +44,7 @@ type RPCClient interface { // thus affected by options which limit spendability such as -spendzeroconfchange. GetBalance(ctx context.Context) (Balance, error) - // GetTransaction returns the transaction with the given hash. + // GetTransaction returns the transaction with the given Hash. GetTransaction(ctx context.Context, txHash string) (*Transaction, error) ListAddresses(ctx context.Context) ([]string, error) @@ -52,6 +52,8 @@ type RPCClient interface { GetBestBlockHash(ctx context.Context) (string, error) GetCoinbaseValue(ctx context.Context) (int64, error) + + GetTransactionOutputs(ctx context.Context, txHash string) ([]MempoolTransactionOutput, error) } // RPCClientFactory is an interface for RPC client factories. diff --git a/transaction.go b/transaction.go index 8d7d9ec..918d50a 100644 --- a/transaction.go +++ b/transaction.go @@ -81,3 +81,15 @@ type TransactionVout struct { Address string } } + +// MempoolTransaction represents a BTC mempool transaction. +type MempoolTransaction struct { + Hash string + Outputs []MempoolTransactionOutput +} + +// MempoolTransactionOutput represents a BTC mempool transaction output. +type MempoolTransactionOutput struct { + Address string + Value float64 +} diff --git a/tview/tview_data.go b/tview/tview_data.go index 0053a1c..65731e3 100644 --- a/tview/tview_data.go +++ b/tview/tview_data.go @@ -1,17 +1,17 @@ package tview import ( + "context" "fmt" "github.com/adrianbrad/privatebtc" - "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" ) type data struct { - btcpn *privatebtc.PrivateNetwork - nodesDetails []*nodeDetails - mempool map[string]*mempoolTxDetails + btcpn *privatebtc.PrivateNetwork + nodesDetails []*nodeDetails + networkMempool privatebtc.NetworkMempool } const burnAddress = "bcrt1qzlfc3dw3ecjncvkwmwpvs84ejqzp4fr4agghm8" @@ -28,16 +28,14 @@ func (d *data) toFormAddresses() []string { return addrs } -func (d *data) update() error { - eg := new(errgroup.Group) - - maps.Clear(d.mempool) +func (d *data) update(ctx context.Context) error { + eg, egCtx := errgroup.WithContext(ctx) for nodeID := range d.btcpn.Nodes() { nodeID := nodeID eg.Go(func() error { - if err := d.updateNodeData(nodeID); err != nil { + if err := d.updateNodeData(egCtx, nodeID); err != nil { return fmt.Errorf("update node %d data: %w", nodeID, err) } @@ -45,41 +43,25 @@ func (d *data) update() error { }) } - if err := eg.Wait(); err != nil { - return err - } + eg.Go(func() error { + mp, err := d.btcpn.Nodes().NetworkMempool(ctx) + if err != nil { + return fmt.Errorf("network mempool: %w", err) + } - for _, details := range d.nodesDetails { - for _, txHash := range details.mempoolTxs { - tx, ok := d.mempool[txHash] - if !ok { - outputs, err := getTransactionOutputs( - d.btcpn.Nodes()[details.id].RPCClient(), - txHash, - ) - if err != nil { - return fmt.Errorf("get transaction outputs: %w", err) - } - - d.mempool[txHash] = &mempoolTxDetails{ - hash: txHash, - nodes: map[int]struct{}{ - details.id: {}, - }, - outputs: outputs, - } - - continue - } + d.networkMempool = mp - tx.nodes[details.id] = struct{}{} - } + return nil + }) + + if err := eg.Wait(); err != nil { + return fmt.Errorf("eg wait: %w", err) } return nil } -func (d *data) updateNodeData(nodeID int) error { +func (d *data) updateNodeData(ctx context.Context, nodeID int) error { eg, egCtx := errgroup.WithContext(ctx) node := d.btcpn.Nodes()[nodeID] @@ -134,21 +116,3 @@ func (d *data) updateNodeData(nodeID int) error { return nil } - -func getTransactionOutputs( - client privatebtc.RPCClient, - txHash string, -) (mempoolTxDetailsOutputs, error) { - tx, err := client.GetTransaction(ctx, txHash) - if err != nil { - return nil, fmt.Errorf("get transaction: %w", err) - } - - outputs := make(mempoolTxDetailsOutputs, len(tx.Vout)) - - for _, v := range tx.Vout { - outputs[v.ScriptPubKey.Address] = v.Value - } - - return outputs, nil -} diff --git a/tview/tview_models.go b/tview/tview_models.go index 103c5c6..a5a6116 100644 --- a/tview/tview_models.go +++ b/tview/tview_models.go @@ -2,47 +2,33 @@ package tview import ( "fmt" - "sort" "strings" "github.com/adrianbrad/privatebtc" - "golang.org/x/exp/maps" ) -type mempoolTxDetailsOutputs map[string]float64 +type mempoolTxDetailsOutputs []privatebtc.MempoolTransactionOutput func (s mempoolTxDetailsOutputs) String() string { - ks := maps.Keys(s) - - sort.Strings(ks) - var b strings.Builder b.WriteString("\n") - for _, k := range ks { - b.WriteString(fmt.Sprintf("%s: %f\n", k, s[k])) + for _, output := range s { + b.WriteString(fmt.Sprintf("%s: %f\n", output.Address, output.Value)) } return b.String() } -type mempoolTxDetails struct { - hash string - nodes map[int]struct{} - outputs mempoolTxDetailsOutputs -} +type mempoolTxDetails privatebtc.NetworkMempoolTransaction func (m mempoolTxDetails) String() string { - nodes := maps.Keys(m.nodes) - - sort.Ints(nodes) - return fmt.Sprintf( "Hash: %s\nNodes: %v\nOutputs: %v", - m.hash, - nodes, - m.outputs, + m.Hash, + m.Nodes, + mempoolTxDetailsOutputs(m.Outputs), ) } diff --git a/tview/tview_tui.go b/tview/tview_tui.go index b0142a1..b39b409 100644 --- a/tview/tview_tui.go +++ b/tview/tview_tui.go @@ -1,6 +1,7 @@ package tview import ( + "context" "fmt" "time" @@ -20,8 +21,6 @@ const bitcoinMaxAddressLength = 44 // NewTUI creates a new Terminal User Interface for the given bitcoin private network. func NewTUI(btcPrivateNetwork *privatebtc.PrivateNetwork, version string) *TUI { - mempoolTxs := map[string]*mempoolTxDetails{} - nodesDetails := make([]*nodeDetails, len(btcPrivateNetwork.Nodes())) for i := range btcPrivateNetwork.Nodes() { @@ -32,9 +31,9 @@ func NewTUI(btcPrivateNetwork *privatebtc.PrivateNetwork, version string) *TUI { } data := &data{ - btcpn: btcPrivateNetwork, - nodesDetails: nodesDetails, - mempool: mempoolTxs, + btcpn: btcPrivateNetwork, + nodesDetails: nodesDetails, + networkMempool: privatebtc.NetworkMempool{}, } // Mempool @@ -88,7 +87,7 @@ func NewTUI(btcPrivateNetwork *privatebtc.PrivateNetwork, version string) *TUI { <-time.After(delay) app.QueueUpdateDraw(func() { - if err := data.update(); err != nil { + if err := data.update(context.Background()); err != nil { outputView.AddError(fmt.Sprintf("update error: %s", err)) return } diff --git a/tview/tview_views.go b/tview/tview_views.go index a0ad2dc..3a6d60f 100644 --- a/tview/tview_views.go +++ b/tview/tview_views.go @@ -105,13 +105,15 @@ func newMempoolTransactionsList( func (l *mempoolTransactionsList) refresh() { l.Clear() - if len(l.data.mempool) == 0 { + if len(l.data.networkMempool) == 0 { l.mempoolTransactionDetails.Clear() return } - for k := range l.data.mempool { - l.AddItem(k, "", 0, nil) + hashes := l.data.networkMempool.Hashes() + + for i := range hashes { + l.AddItem(hashes[i], "", 0, nil) } } @@ -123,7 +125,7 @@ type mempoolTransactionDetails struct { func (d *mempoolTransactionDetails) updateDisplayedTx(txHash string) { d.currentTxHash = txHash - d.SetText(d.data.mempool[d.currentTxHash].String()) + d.SetText(mempoolTxDetails(*d.data.networkMempool[d.currentTxHash]).String()) } func newMempoolTransactionDetails(data *data) *mempoolTransactionDetails { @@ -171,7 +173,7 @@ func newNodeActionsList( nodesList, ) - rbfDrainToAdress := newReplaceByFeeDrainToAddressForm( + rbfDrainToAddress := newReplaceByFeeDrainToAddressForm( appPages, actionsHandler, outputView, @@ -219,6 +221,8 @@ func newNodeActionsList( case 1: // Send to address appPages.ShowPage("sendBitcoinForm") + sendBitcoinForm.SetFocus(1) + sendBitcoinForm.GetFormItem(0).(*tview.TextView).SetText( fmt.Sprintf( "Node %d Balance: %.2f", @@ -249,6 +253,8 @@ func newNodeActionsList( case 2: // Mine to address appPages.ShowPage("mineBlocksForm") + mineBlocksForm.SetFocus(1) + mineBlocksForm.GetFormItem(0).(*tview.TextView).SetText( fmt.Sprintf("Node %d", currentNodeIndex), ) @@ -319,15 +325,20 @@ func newNodeActionsList( actionsHandler.data.nodesDetails[currentNodeIndex].connected = true case 5: // Replace by fee drain to address + if mempoolTxList.List.GetItemCount() == 0 { + outputView.AddError("no transactions in mempool") + return + } + appPages.ShowPage("replaceByFeeDrainToAddressForm") - rbfDrainToAdress.GetFormItem(0).(*tview.TextView).SetText( + rbfDrainToAddress.GetFormItem(0).(*tview.TextView).SetText( fmt.Sprintf("Node %d", currentNodeIndex), ) txID, _ := mempoolTxList.GetItemText(mempoolTxList.GetCurrentItem()) - rbfDrainToAdress.GetFormItem(1).(*tview.TextView).SetText(txID) + rbfDrainToAddress.GetFormItem(1).(*tview.TextView).SetText(txID) nodeRPCClient := actionsHandler.btcpn.Nodes()[currentNodeIndex].RPCClient() @@ -353,10 +364,10 @@ func newNodeActionsList( return } - rbfDrainToAdress.GetFormItem(2).(*tview.TextView). + rbfDrainToAddress.GetFormItem(2).(*tview.TextView). SetText(fmt.Sprintf("%.2f", totalInputs)) - rbfDrainToAdress.GetFormItem(3).(*tview.DropDown). + rbfDrainToAddress.GetFormItem(3).(*tview.DropDown). SetOptions( data.toFormAddresses(), func(option string, i int) { @@ -369,7 +380,7 @@ func newNodeActionsList( addr = a[1] } - rbfDrainToAdress. + rbfDrainToAddress. GetFormItem(4).(*tview.InputField). SetText(addr) }). @@ -386,7 +397,7 @@ func newNodeActionsList( List: list, sendBitcoinForm: sendBitcoinForm, mineBlocksForm: mineBlocksForm, - rbfDrainToAddress: rbfDrainToAdress, + rbfDrainToAddress: rbfDrainToAddress, } } @@ -402,7 +413,7 @@ func newSendBitcoinForm( ) *sendBitcoinForm { form := tview.NewForm() - hide := hideForm(appPages, "sendBitcoinForm", form) + hide := hideForm(appPages, form) const ( labelSenderNode = "Sender Node" @@ -495,7 +506,7 @@ func newMineBlocksForm( ) *mineBlocksForm { form := tview.NewForm() - hide := hideForm(appPages, "mineBlocksForm", form) + hide := hideForm(appPages, form) const ( labelMinerNode = "Miner Node" @@ -595,7 +606,7 @@ func newReplaceByFeeDrainToAddressForm( ) *replaceByFeeDrainToAddressForm { form := tview.NewForm() - hide := hideForm(appPages, "replaceByFeeDrainToAddressForm", form) + hide := hideForm(appPages, form) const ( labelNode = "Node" @@ -683,9 +694,9 @@ func newReplaceByFeeDrainToAddressForm( } } -func hideForm(appPages *tview.Pages, pageName string, form *tview.Form) func() { +func hideForm(appPages *tview.Pages, form *tview.Form) func() { return func() { - appPages.HidePage(pageName) + appPages.SwitchToPage("background") for i := 0; i < form.GetFormItemCount(); i++ { switch formItem := form.GetFormItem(i).(type) { @@ -695,8 +706,6 @@ func hideForm(appPages *tview.Pages, pageName string, form *tview.Form) func() { formItem.SetText("") } } - - form.SetFocus(1) } } @@ -743,13 +752,13 @@ func newAppPages( pages. AddPage("background", appFlex, true, true). - AddPage("sendBitcoinForm", centeredForm( - nodeActionsList.sendBitcoinForm, + AddPage("mineBlocksForm", centeredForm( + nodeActionsList.mineBlocksForm, width, height, ), true, false). - AddPage("mineBlocksForm", centeredForm( - nodeActionsList.mineBlocksForm, + AddPage("sendBitcoinForm", centeredForm( + nodeActionsList.sendBitcoinForm, width, height, ), true, false).