From 9cc1981eab10e5cd3274f45330b3293c1109ffdb Mon Sep 17 00:00:00 2001 From: Marcel Hernandez Date: Sat, 9 Nov 2024 17:04:39 -0300 Subject: [PATCH 1/5] simple integration test --- .github/workflows/test.yml | 14 ++++++++ README.md | 2 ++ go.mod | 2 +- go.sum | 5 ++- main_test.go | 69 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 main_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b7fdc7f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,14 @@ +name: continuous integration + +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.22 + - run: make trustedcoin + - run: go test -v diff --git a/README.md b/README.md index 4e5ce9e..c6bbba5 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ ## The `trustedcoin` plugin +[![continuous integration](https://github.com/nbd-wtf/trustedcoin/actions/workflows/test.yml/badge.svg)](https://github.com/nbd-wtf/trustedcoin/actions/workflows/test.yml) + A plugin that uses block explorers (`blockstream.info`, `mempool.space`, `mempool.emzy.de`, `blockchair.com`, `blockchain.info` -- [suggest others](https://github.com/fiatjaf/trustedcoin/issues)) as backends instead of your own Bitcoin node. This isn't what you should be doing, but sometimes you may need it. diff --git a/go.mod b/go.mod index e305c46..7ae1230 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/btcsuite/btcd v0.24.2 github.com/btcsuite/btcd/btcutil v1.1.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 - github.com/fiatjaf/lightningd-gjson-rpc v1.6.2 + github.com/fiatjaf/lightningd-gjson-rpc v1.6.3 ) require ( diff --git a/go.sum b/go.sum index a5a94e0..73ac81d 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/1ma/lightningd-gjson-rpc v0.0.0-20241109130140-ddc6991a26cf h1:4QFQcuL5Fks+WRI+BSrgTxGoJxNUM/Ht8YCgQRoKcDY= +github.com/1ma/lightningd-gjson-rpc v0.0.0-20241109130140-ddc6991a26cf/go.mod h1:DqVHlrgk0q0J08nbPBCwDVuB7vzPohRnrzuGZ0ct0fg= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -207,8 +209,9 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fergusstrange/embedded-postgres v1.10.0/go.mod h1:a008U8/Rws5FtIOTGYDYa7beVWsT3qVKyqExqYYjL+c= github.com/fergusstrange/embedded-postgres v1.25.0 h1:sa+k2Ycrtz40eCRPOzI7Ry7TtkWXXJ+YRsxpKMDhxK0= github.com/fergusstrange/embedded-postgres v1.25.0/go.mod h1:t/MLs0h9ukYM6FSt99R7InCHs1nW0ordoVCcnzmpTYw= -github.com/fiatjaf/lightningd-gjson-rpc v1.6.2 h1:QlnPE3piGCAd8qElWIPuVbDYq4E1WTspgwP01q3olrI= github.com/fiatjaf/lightningd-gjson-rpc v1.6.2/go.mod h1:DqVHlrgk0q0J08nbPBCwDVuB7vzPohRnrzuGZ0ct0fg= +github.com/fiatjaf/lightningd-gjson-rpc v1.6.3 h1:HcIF8YRIz06HmYak/NCx93SLveZ3gybUpwg5hHSgG10= +github.com/fiatjaf/lightningd-gjson-rpc v1.6.3/go.mod h1:DqVHlrgk0q0J08nbPBCwDVuB7vzPohRnrzuGZ0ct0fg= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..c10a10a --- /dev/null +++ b/main_test.go @@ -0,0 +1,69 @@ +package main_test + +import ( + "bufio" + "io" + "os/exec" + "strings" + "testing" +) + +const executable = "./trustedcoin" + +const manifestRequest = `{"jsonrpc":"2.0","id":"manifest","method":"init","params":{"options":{},"configuration":{"network":"bitcoin","lightning-dir":"/tmp","rpc-file":"foo"}}}` +const manifestExpectedResponse = `{"jsonrpc":"2.0","id":"manifest"}` + +const initRequest = `{"jsonrpc":"2.0","id":"init","method":"getmanifest","params":{}}` +const initExpectedResponse = `{"jsonrpc":"2.0","id":"init","result":{"options":[{"name":"bitcoin-rpcconnect","type":"string","default":"","description":"Hostname (IP) to bitcoind RPC (optional)."},{"name":"bitcoin-rpcport","type":"string","default":"","description":"Port to bitcoind RPC (optional)."},{"name":"bitcoin-rpcuser","type":"string","default":"","description":"Username to bitcoind RPC (optional)."},{"name":"bitcoin-rpcpassword","type":"string","default":"","description":"Password to bitcoind RPC (optional)."}],"rpcmethods":[{"name":"getrawblockbyheight","usage":"height","description":"Get the bitcoin block at a given height","long_description":""},{"name":"getchaininfo","usage":"","description":"Get the chain id, the header count, the block count and whether this is IBD.","long_description":""},{"name":"estimatefees","usage":"","description":"Get the Bitcoin feerate in sat/kilo-vbyte.","long_description":""},{"name":"sendrawtransaction","usage":"tx","description":"Send a raw transaction to the Bitcoin network.","long_description":""},{"name":"getutxout","usage":"txid vout","description":"Get informations about an output, identified by a {txid} an a {vout}","long_description":""}],"subscriptions":[],"hooks":[],"featurebits":{"features":"","channel":"","init":"","invoice":""},"dynamic":false,"notifications":[]}}` + +const shutdownNotification = `{"jsonrpc":"2.0","method":"shutdown","params":{}}` + +func TestInitAndShutdown(t *testing.T) { + cmd, stdin, stdout, stderr := start(t) + stop(t, cmd, stdin, stdout, stderr) +} + +func start(t *testing.T) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadCloser) { + cmd := exec.Command(executable) + stdin, _ := cmd.StdinPipe() + stdout, _ := cmd.StdoutPipe() + stderr, _ := cmd.StderrPipe() + + err := cmd.Start() + if err != nil { + t.Fatalf("expected trustedcoin to start, got %v", err) + } + + _, _ = io.WriteString(stdin, manifestRequest) + if response := readline(stdout); response != manifestExpectedResponse { + t.Fatalf("unexpected RPC response: %s", response) + } + + _, _ = io.WriteString(stdin, initRequest) + if response := readline(stdout); response != initExpectedResponse { + t.Fatalf("unexpected RPC response: %s", response) + } + + if response := readline(stderr); !strings.Contains(response, "initialized plugin") { + t.Fatalf("unexpected output in stderr: %s", response) + } + + return cmd, stdin, stdout, stderr +} + +func stop(t *testing.T, cmd *exec.Cmd, stdin io.WriteCloser, stdout, stderr io.ReadCloser) { + _, _ = io.WriteString(stdin, shutdownNotification) + _ = stdin.Close() + _ = stdout.Close() + _ = stderr.Close() + + if err := cmd.Wait(); err != nil { + t.Fatalf("expected process to exit cleanly, got %v", err) + } +} + +func readline(r io.Reader) string { + line, _ := bufio.NewReader(r).ReadString('\n') + + return strings.TrimSuffix(line, "\n") +} From 64a09e71385e4fe10d50635b0ada98d5406599f9 Mon Sep 17 00:00:00 2001 From: Marcel Hernandez Date: Sun, 10 Nov 2024 23:42:15 -0300 Subject: [PATCH 2/5] pyln tests scaffolding --- .github/scripts/install-bitcoind.sh | 12 ++++ .github/scripts/install-cln.sh | 13 ++++ .github/workflows/test.yml | 35 +++++++++- .gitignore | 4 +- integration_test.py | 10 +++ requirements.in | 12 ++++ requirements.txt | 102 ++++++++++++++++++++++++++++ 7 files changed, 184 insertions(+), 4 deletions(-) create mode 100755 .github/scripts/install-bitcoind.sh create mode 100755 .github/scripts/install-cln.sh create mode 100644 integration_test.py create mode 100644 requirements.in create mode 100644 requirements.txt diff --git a/.github/scripts/install-bitcoind.sh b/.github/scripts/install-bitcoind.sh new file mode 100755 index 0000000..123cc2f --- /dev/null +++ b/.github/scripts/install-bitcoind.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -xeu + +DIRNAME="bitcoin-${BITCOIN_CORE_VERSION}" +FILENAME="${DIRNAME}-x86_64-linux-gnu.tar.gz" + +cd "${HOME}" +wget -q "https://bitcoincore.org/bin/bitcoin-core-${BITCOIN_CORE_VERSION}/${FILENAME}" +tar -xf "${FILENAME}" +sudo mv "${DIRNAME}"/bin/* "/usr/local/bin" +rm -rf "${FILENAME}" "${DIRNAME}" diff --git a/.github/scripts/install-cln.sh b/.github/scripts/install-cln.sh new file mode 100755 index 0000000..b72645a --- /dev/null +++ b/.github/scripts/install-cln.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -xeu + +DIRNAME="usr" +FILENAME="clightning-v${CORE_LIGHTNING_VERSION}-Ubuntu-24.04.tar.xz" + +cd "${HOME}" +wget -q "https://github.com/ElementsProject/lightning/releases/download/v${CORE_LIGHTNING_VERSION}/${FILENAME}" +tar -xf "${FILENAME}" +sudo cp -r "${DIRNAME}"/bin /usr/local +sudo cp -r "${DIRNAME}"/libexec /usr/local/libexec +rm -rf "${FILENAME}" "${DIRNAME}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b7fdc7f..b8cf01e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,13 +2,42 @@ name: continuous integration on: push +env: + BITCOIN_CORE_VERSION: '27.2' + CORE_LIGHTNING_VERSION: '24.08.2' + GO_VERSION: '1.22' + PYTHON_VERSION: '3.13' + jobs: - test: - runs-on: ubuntu-latest + isolation-tests: + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: ${{ env.GO_VERSION }} - run: make trustedcoin - run: go test -v + + integration-tests: + runs-on: ubuntu-24.04 + env: + VENV_PATH: venv + VALGRIND: 0 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + - run: make trustedcoin + - run: .github/scripts/install-bitcoind.sh + - run: .github/scripts/install-cln.sh + - run: | + python -m venv ${{ env.VENV_PATH }} + echo "${{ env.VENV_PATH }}/bin" >> $GITHUB_PATH + source ${{ env.VENV_PATH }}/bin/activate + pip install -r requirements.txt + - run: pytest diff --git a/.gitignore b/.gitignore index 26ebcf1..f9d0e7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -dist/* +__pycache__/ +dist/ trustedcoin +venv/ \ No newline at end of file diff --git a/integration_test.py b/integration_test.py new file mode 100644 index 0000000..97fc997 --- /dev/null +++ b/integration_test.py @@ -0,0 +1,10 @@ +from pyln.testing.fixtures import * + +def test_bcli(node_factory, bitcoind, chainparams): + """ + This tests the bcli plugin, used to gather Bitcoin data from a local + bitcoind. + Mostly sanity checks of the interface... + """ + l1 = node_factory.get_node() + assert 1 == 1 \ No newline at end of file diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..23fef71 --- /dev/null +++ b/requirements.in @@ -0,0 +1,12 @@ +# build updated requirements.txt with: +# $ python -m venv venv +# $ source venv/bin/activate +# $ pip install pip-tools +# $ pip-compile --strip-extras requirements.in +# load current requirements.txt in a virtualenv with: +# $ python -m venv venv +# $ source venv/bin/activate +# $ pip install -r requirements.txt + +pyln.client +pyln.testing diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bc91f28 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,102 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --strip-extras requirements.in +# +asn1crypto==1.5.1 + # via coincurve +attrs==24.2.0 + # via + # jsonschema + # referencing +base58==2.1.1 + # via pyln-proto +bitarray==2.9.3 + # via bitstring +bitstring==4.2.3 + # via pyln-proto +blinker==1.9.0 + # via flask +certifi==2024.8.30 + # via requests +cffi==1.17.1 + # via + # coincurve + # cryptography +charset-normalizer==3.4.0 + # via requests +cheroot==10.0.0 + # via pyln-testing +click==8.1.7 + # via flask +coincurve==20.0.0 + # via pyln-proto +cryptography==42.0.8 + # via pyln-proto +ephemeral-port-reserve==1.1.4 + # via pyln-testing +flask==2.3.3 + # via pyln-testing +idna==3.10 + # via requests +iniconfig==2.0.0 + # via pytest +itsdangerous==2.2.0 + # via flask +jaraco-functools==4.1.0 + # via cheroot +jinja2==3.1.4 + # via flask +jsonschema==4.23.0 + # via pyln-testing +jsonschema-specifications==2024.10.1 + # via jsonschema +markupsafe==3.0.2 + # via + # jinja2 + # werkzeug +more-itertools==10.5.0 + # via + # cheroot + # jaraco-functools +packaging==24.2 + # via pytest +pluggy==1.5.0 + # via pytest +psutil==5.9.8 + # via pyln-testing +psycopg2-binary==2.9.10 + # via pyln-testing +pycparser==2.22 + # via cffi +pyln-bolt7==1.0.246 + # via pyln-client +pyln-client==24.8.2 + # via + # -r requirements.in + # pyln-testing +pyln-proto==24.8.2 + # via pyln-client +pyln-testing==24.8.2 + # via -r requirements.in +pysocks==1.7.1 + # via pyln-proto +pytest==7.4.4 + # via pyln-testing +python-bitcoinlib==0.11.2 + # via pyln-testing +referencing==0.35.1 + # via + # jsonschema + # jsonschema-specifications +requests==2.32.3 + # via pyln-testing +rpds-py==0.21.0 + # via + # jsonschema + # referencing +urllib3==2.2.3 + # via requests +werkzeug==3.1.3 + # via flask From df70d78810b11d95425ca336fdde55667d059c63 Mon Sep 17 00:00:00 2001 From: Marcel Hernandez Date: Mon, 11 Nov 2024 16:17:57 -0300 Subject: [PATCH 3/5] fix in isolation tests --- main_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/main_test.go b/main_test.go index c10a10a..a5cc200 100644 --- a/main_test.go +++ b/main_test.go @@ -10,11 +10,11 @@ import ( const executable = "./trustedcoin" -const manifestRequest = `{"jsonrpc":"2.0","id":"manifest","method":"init","params":{"options":{},"configuration":{"network":"bitcoin","lightning-dir":"/tmp","rpc-file":"foo"}}}` -const manifestExpectedResponse = `{"jsonrpc":"2.0","id":"manifest"}` +const getManifestRequest = `{"jsonrpc":"2.0","id":"getmanifest","method":"getmanifest","params":{}}` +const getManifestExpectedResponse = `{"jsonrpc":"2.0","id":"getmanifest","result":{"options":[{"name":"bitcoin-rpcconnect","type":"string","default":"","description":"Hostname (IP) to bitcoind RPC (optional)."},{"name":"bitcoin-rpcport","type":"string","default":"","description":"Port to bitcoind RPC (optional)."},{"name":"bitcoin-rpcuser","type":"string","default":"","description":"Username to bitcoind RPC (optional)."},{"name":"bitcoin-rpcpassword","type":"string","default":"","description":"Password to bitcoind RPC (optional)."}],"rpcmethods":[{"name":"getrawblockbyheight","usage":"height","description":"Get the bitcoin block at a given height","long_description":""},{"name":"getchaininfo","usage":"","description":"Get the chain id, the header count, the block count and whether this is IBD.","long_description":""},{"name":"estimatefees","usage":"","description":"Get the Bitcoin feerate in sat/kilo-vbyte.","long_description":""},{"name":"sendrawtransaction","usage":"tx","description":"Send a raw transaction to the Bitcoin network.","long_description":""},{"name":"getutxout","usage":"txid vout","description":"Get informations about an output, identified by a {txid} an a {vout}","long_description":""}],"subscriptions":[],"hooks":[],"featurebits":{"features":"","channel":"","init":"","invoice":""},"dynamic":false,"notifications":[]}}` -const initRequest = `{"jsonrpc":"2.0","id":"init","method":"getmanifest","params":{}}` -const initExpectedResponse = `{"jsonrpc":"2.0","id":"init","result":{"options":[{"name":"bitcoin-rpcconnect","type":"string","default":"","description":"Hostname (IP) to bitcoind RPC (optional)."},{"name":"bitcoin-rpcport","type":"string","default":"","description":"Port to bitcoind RPC (optional)."},{"name":"bitcoin-rpcuser","type":"string","default":"","description":"Username to bitcoind RPC (optional)."},{"name":"bitcoin-rpcpassword","type":"string","default":"","description":"Password to bitcoind RPC (optional)."}],"rpcmethods":[{"name":"getrawblockbyheight","usage":"height","description":"Get the bitcoin block at a given height","long_description":""},{"name":"getchaininfo","usage":"","description":"Get the chain id, the header count, the block count and whether this is IBD.","long_description":""},{"name":"estimatefees","usage":"","description":"Get the Bitcoin feerate in sat/kilo-vbyte.","long_description":""},{"name":"sendrawtransaction","usage":"tx","description":"Send a raw transaction to the Bitcoin network.","long_description":""},{"name":"getutxout","usage":"txid vout","description":"Get informations about an output, identified by a {txid} an a {vout}","long_description":""}],"subscriptions":[],"hooks":[],"featurebits":{"features":"","channel":"","init":"","invoice":""},"dynamic":false,"notifications":[]}}` +const initRequest = `{"jsonrpc":"2.0","id":"init","method":"init","params":{"options":{},"configuration":{"network":"bitcoin","lightning-dir":"/tmp","rpc-file":"foo"}}}` +const initExpectedResponse = `{"jsonrpc":"2.0","id":"init"}` const shutdownNotification = `{"jsonrpc":"2.0","method":"shutdown","params":{}}` @@ -34,8 +34,8 @@ func start(t *testing.T) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadClose t.Fatalf("expected trustedcoin to start, got %v", err) } - _, _ = io.WriteString(stdin, manifestRequest) - if response := readline(stdout); response != manifestExpectedResponse { + _, _ = io.WriteString(stdin, getManifestRequest) + if response := readline(stdout); response != getManifestExpectedResponse { t.Fatalf("unexpected RPC response: %s", response) } From f5985b9c13ffddb6142ae21d2030b2146bc3fe57 Mon Sep 17 00:00:00 2001 From: Marcel Hernandez Date: Mon, 11 Nov 2024 17:47:17 -0300 Subject: [PATCH 4/5] full integration test with bcli for now --- integration_test.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/integration_test.py b/integration_test.py index 97fc997..0b7b150 100644 --- a/integration_test.py +++ b/integration_test.py @@ -1,4 +1,6 @@ +from pyln.client import RpcError from pyln.testing.fixtures import * +from pyln.testing.utils import wait_for def test_bcli(node_factory, bitcoind, chainparams): """ @@ -6,5 +8,44 @@ def test_bcli(node_factory, bitcoind, chainparams): bitcoind. Mostly sanity checks of the interface... """ + l1 = node_factory.get_node() - assert 1 == 1 \ No newline at end of file + l2 = node_factory.get_node() + + # We cant stop it dynamically + with pytest.raises(RpcError): + l1.rpc.plugin_stop("bcli") + + # Failure case of feerate is tested in test_misc.py + estimates = l1.rpc.call("estimatefees") + assert 'feerate_floor' in estimates + assert [f['blocks'] for f in estimates['feerates']] == [2, 6, 12, 100] + + resp = l1.rpc.call("getchaininfo", {"last_height": 0}) + assert resp["chain"] == chainparams['name'] + for field in ["headercount", "blockcount", "ibd"]: + assert field in resp + + # We shouldn't get upset if we ask for an unknown-yet block + resp = l1.rpc.call("getrawblockbyheight", {"height": 500}) + assert resp["blockhash"] is resp["block"] is None + resp = l1.rpc.call("getrawblockbyheight", {"height": 50}) + assert resp["blockhash"] is not None and resp["blockhash"] is not None + # Some other bitcoind-failure cases for this call are covered in + # tests/test_misc.py + + l1.fundwallet(10**5) + l1.connect(l2) + fc = l1.rpc.fundchannel(l2.info["id"], 10**4 * 3) + txo = l1.rpc.call("getutxout", {"txid": fc['txid'], "vout": fc['outnum']}) + assert (Millisatoshi(txo["amount"]) == Millisatoshi(10**4 * 3 * 10**3) + and txo["script"].startswith("0020")) + l1.rpc.close(l2.info["id"]) + # When output is spent, it should give us null ! + wait_for(lambda: l1.rpc.call("getutxout", { + "txid": fc['txid'], + "vout": fc['outnum'] + })['amount'] is None) + + resp = l1.rpc.call("sendrawtransaction", {"tx": "dummy", "allowhighfees": False}) + assert not resp["success"] and "decode failed" in resp["errmsg"] From 3c3acfee03226936d5dce0a750fd2ecc54a34cf7 Mon Sep 17 00:00:00 2001 From: Marcel Hernandez Date: Tue, 12 Nov 2024 14:07:34 -0300 Subject: [PATCH 5/5] use trustedcoin in integration test, and fix estimatefees and bitcoin-datadir errors --- .gitignore | 2 +- estimatefees.go | 14 +++++++++++++- go.sum | 2 -- integration_test.py | 40 ++++++++++------------------------------ main.go | 3 ++- main_test.go | 6 +++--- 6 files changed, 29 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index f9d0e7a..74fcde5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ __pycache__/ dist/ trustedcoin -venv/ \ No newline at end of file +venv/ diff --git a/estimatefees.go b/estimatefees.go index 0c3c7de..33a4587 100644 --- a/estimatefees.go +++ b/estimatefees.go @@ -18,7 +18,19 @@ type FeeRate struct { FeeRate int `json:"feerate"` } -func getFeeRates() (*EstimatedFees, error) { +func getFeeRates(network string) (*EstimatedFees, error) { + if network == "regtest" { + return &EstimatedFees{ + FeeRateFloor: 1000, + FeeRates: []FeeRate{ + {Blocks: 2, FeeRate: 1000}, + {Blocks: 6, FeeRate: 1000}, + {Blocks: 12, FeeRate: 1000}, + {Blocks: 100, FeeRate: 1000}, + }, + }, nil + } + // try bitcoind first if bitcoind != nil { in2, err2 := bitcoind.EstimateSmartFee(2, &btcjson.EstimateModeConservative) diff --git a/go.sum b/go.sum index 73ac81d..5e6369c 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/1ma/lightningd-gjson-rpc v0.0.0-20241109130140-ddc6991a26cf h1:4QFQcuL5Fks+WRI+BSrgTxGoJxNUM/Ht8YCgQRoKcDY= -github.com/1ma/lightningd-gjson-rpc v0.0.0-20241109130140-ddc6991a26cf/go.mod h1:DqVHlrgk0q0J08nbPBCwDVuB7vzPohRnrzuGZ0ct0fg= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/integration_test.py b/integration_test.py index 0b7b150..c783469 100644 --- a/integration_test.py +++ b/integration_test.py @@ -1,51 +1,31 @@ from pyln.client import RpcError from pyln.testing.fixtures import * -from pyln.testing.utils import wait_for def test_bcli(node_factory, bitcoind, chainparams): """ - This tests the bcli plugin, used to gather Bitcoin data from a local - bitcoind. - Mostly sanity checks of the interface... + Based on the test_bcli from Core Lightning """ - - l1 = node_factory.get_node() - l2 = node_factory.get_node() + node = node_factory.get_node(opts={ + "disable-plugin": "bcli", + "plugin": os.path.join(os.getcwd(), 'trustedcoin'), + }) # We cant stop it dynamically with pytest.raises(RpcError): - l1.rpc.plugin_stop("bcli") + node.rpc.plugin_stop("bcli") # Failure case of feerate is tested in test_misc.py - estimates = l1.rpc.call("estimatefees") + estimates = node.rpc.call("estimatefees") assert 'feerate_floor' in estimates assert [f['blocks'] for f in estimates['feerates']] == [2, 6, 12, 100] - resp = l1.rpc.call("getchaininfo", {"last_height": 0}) + resp = node.rpc.call("getchaininfo", {"last_height": 0}) assert resp["chain"] == chainparams['name'] for field in ["headercount", "blockcount", "ibd"]: assert field in resp # We shouldn't get upset if we ask for an unknown-yet block - resp = l1.rpc.call("getrawblockbyheight", {"height": 500}) + resp = node.rpc.call("getrawblockbyheight", {"height": 500}) assert resp["blockhash"] is resp["block"] is None - resp = l1.rpc.call("getrawblockbyheight", {"height": 50}) + resp = node.rpc.call("getrawblockbyheight", {"height": 50}) assert resp["blockhash"] is not None and resp["blockhash"] is not None - # Some other bitcoind-failure cases for this call are covered in - # tests/test_misc.py - - l1.fundwallet(10**5) - l1.connect(l2) - fc = l1.rpc.fundchannel(l2.info["id"], 10**4 * 3) - txo = l1.rpc.call("getutxout", {"txid": fc['txid'], "vout": fc['outnum']}) - assert (Millisatoshi(txo["amount"]) == Millisatoshi(10**4 * 3 * 10**3) - and txo["script"].startswith("0020")) - l1.rpc.close(l2.info["id"]) - # When output is spent, it should give us null ! - wait_for(lambda: l1.rpc.call("getutxout", { - "txid": fc['txid'], - "vout": fc['outnum'] - })['amount'] is None) - - resp = l1.rpc.call("sendrawtransaction", {"tx": "dummy", "allowhighfees": False}) - assert not resp["success"] and "decode failed" in resp["errmsg"] diff --git a/main.go b/main.go index 2e35c4a..a5c8ff2 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,7 @@ func main() { {Name: "bitcoin-rpcport", Type: "string", Description: "Port to bitcoind RPC (optional).", Default: ""}, {Name: "bitcoin-rpcuser", Type: "string", Description: "Username to bitcoind RPC (optional).", Default: ""}, {Name: "bitcoin-rpcpassword", Type: "string", Description: "Password to bitcoind RPC (optional).", Default: ""}, + {Name: "bitcoin-datadir", Type: "string", Description: "-datadir arg for bitcoin-cli. For compatibility with bcli, not actually used.", Default: ""}, }, RPCMethods: []plugin.RPCMethod{ { @@ -131,7 +132,7 @@ func main() { Description: "Get the Bitcoin feerate in sat/kilo-vbyte.", LongDescription: "", Handler: func(p *plugin.Plugin, params plugin.Params) (resp any, errCode int, err error) { - estfees, err := getFeeRates() + estfees, err := getFeeRates(p.Network) if err != nil { p.Logf("estimatefees error: %s", err.Error()) estfees = &EstimatedFees{} diff --git a/main_test.go b/main_test.go index a5cc200..5140161 100644 --- a/main_test.go +++ b/main_test.go @@ -11,7 +11,7 @@ import ( const executable = "./trustedcoin" const getManifestRequest = `{"jsonrpc":"2.0","id":"getmanifest","method":"getmanifest","params":{}}` -const getManifestExpectedResponse = `{"jsonrpc":"2.0","id":"getmanifest","result":{"options":[{"name":"bitcoin-rpcconnect","type":"string","default":"","description":"Hostname (IP) to bitcoind RPC (optional)."},{"name":"bitcoin-rpcport","type":"string","default":"","description":"Port to bitcoind RPC (optional)."},{"name":"bitcoin-rpcuser","type":"string","default":"","description":"Username to bitcoind RPC (optional)."},{"name":"bitcoin-rpcpassword","type":"string","default":"","description":"Password to bitcoind RPC (optional)."}],"rpcmethods":[{"name":"getrawblockbyheight","usage":"height","description":"Get the bitcoin block at a given height","long_description":""},{"name":"getchaininfo","usage":"","description":"Get the chain id, the header count, the block count and whether this is IBD.","long_description":""},{"name":"estimatefees","usage":"","description":"Get the Bitcoin feerate in sat/kilo-vbyte.","long_description":""},{"name":"sendrawtransaction","usage":"tx","description":"Send a raw transaction to the Bitcoin network.","long_description":""},{"name":"getutxout","usage":"txid vout","description":"Get informations about an output, identified by a {txid} an a {vout}","long_description":""}],"subscriptions":[],"hooks":[],"featurebits":{"features":"","channel":"","init":"","invoice":""},"dynamic":false,"notifications":[]}}` +const getManifestExpectedResponse = `{"jsonrpc":"2.0","id":"getmanifest","result":{"options":[{"name":"bitcoin-rpcconnect","type":"string","default":"","description":"Hostname (IP) to bitcoind RPC (optional)."},{"name":"bitcoin-rpcport","type":"string","default":"","description":"Port to bitcoind RPC (optional)."},{"name":"bitcoin-rpcuser","type":"string","default":"","description":"Username to bitcoind RPC (optional)."},{"name":"bitcoin-rpcpassword","type":"string","default":"","description":"Password to bitcoind RPC (optional)."},{"name":"bitcoin-datadir","type":"string","default":"","description":"-datadir arg for bitcoin-cli. For compatibility with bcli, not actually used."}],"rpcmethods":[{"name":"getrawblockbyheight","usage":"height","description":"Get the bitcoin block at a given height","long_description":""},{"name":"getchaininfo","usage":"","description":"Get the chain id, the header count, the block count and whether this is IBD.","long_description":""},{"name":"estimatefees","usage":"","description":"Get the Bitcoin feerate in sat/kilo-vbyte.","long_description":""},{"name":"sendrawtransaction","usage":"tx","description":"Send a raw transaction to the Bitcoin network.","long_description":""},{"name":"getutxout","usage":"txid vout","description":"Get informations about an output, identified by a {txid} an a {vout}","long_description":""}],"subscriptions":[],"hooks":[],"featurebits":{"features":"","channel":"","init":"","invoice":""},"dynamic":false,"notifications":[]}}` const initRequest = `{"jsonrpc":"2.0","id":"init","method":"init","params":{"options":{},"configuration":{"network":"bitcoin","lightning-dir":"/tmp","rpc-file":"foo"}}}` const initExpectedResponse = `{"jsonrpc":"2.0","id":"init"}` @@ -36,12 +36,12 @@ func start(t *testing.T) (*exec.Cmd, io.WriteCloser, io.ReadCloser, io.ReadClose _, _ = io.WriteString(stdin, getManifestRequest) if response := readline(stdout); response != getManifestExpectedResponse { - t.Fatalf("unexpected RPC response: %s", response) + t.Fatalf("unexpected manifest response: %s", response) } _, _ = io.WriteString(stdin, initRequest) if response := readline(stdout); response != initExpectedResponse { - t.Fatalf("unexpected RPC response: %s", response) + t.Fatalf("unexpected init response: %s", response) } if response := readline(stderr); !strings.Contains(response, "initialized plugin") {