Page MenuHomePhabricator

[chronik] add electrum method blockchain.transaction.get
ClosedPublic

Authored by PiRK on Mon, Dec 9, 15:15.

Details

Reviewers
Fabien
Group Reviewers
Restricted Project
Maniphest Tasks
Restricted Maniphest Task
Commits
rABC5ccdd58eaef1: [chronik] add electrum method blockchain.transaction.get
Summary

Return a raw tx (hex string) for a txid, or a tx object (similar to the one returned by the node's gettransaction RPC in verbose mode)

Depends on D17314
Ref T3598

Test Plan
./src/bitcoind -regtest -chronik -chronikelectrumbind="[::1]:50001" -chronikelectrumbind="127.0.0.1:50001"

In a different terminal, query the regtest genesis coinbase transaction:

$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"], "id": "test"}' | nc 127.0.0.1 50001
$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", false], "id": "test"}' | nc 127.0.0.1 50001
$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": {"txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"}, "id": "test"}' | nc 127.0.0.1 50001
$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": {"txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", "verbose": false}, "id": "test"}' | nc 127.0.0.1 50001

{"id":"test","jsonrpc":"2.0","result":"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000"}

$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": ["4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", true], "id": "test"}' | nc 127.0.0.1 50001
$ echo '{"jsonrpc": "2.0", "method": "blockchain.transaction.get", "params": {"txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", "verbose": true}, "id": "test"}' | nc 127.0.0.1 50001
{"id":"test","jsonrpc":"2.0","result":{"blockhash":"06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f","blocktime":1296688602,"confirmations":2,"hash":"4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b","hex":"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000","time":0}}

ninja all check-all

Event Timeline

implement params parsing and raw tx fetching. TODO: Vec<u8> -> hex -> JSON string

Tail of the build log:

   Doc-tests chronik-bridge

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-db

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-http

running 1 test
test chronik/chronik-http/src/protobuf.rs - protobuf::Protobuf (line 29) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.34s

   Doc-tests chronik-indexer

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-common

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-impl

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-proto

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-util

running 2 tests
test chronik/chronik-util/src/log.rs - log::log (line 65) ... ignored
test chronik/chronik-util/src/log.rs - log::log_chronik (line 87) ... ignored

test result: ok. 0 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 0.00s

ninja: build stopped: cannot make progress due to previous errors.
Build build-chronik failed with exit code 1

Tail of the build log:

    Checking mime v0.3.17
    Checking tower v0.5.1
    Checking axum-core v0.4.5
    Checking hyper-util v0.1.10
    Checking memoffset v0.9.1
    Checking serde_urlencoded v0.7.1
    Checking tokio-tungstenite v0.24.0
   Compiling karyon_jsonrpc_macro v0.1.8
    Checking futures-executor v0.3.31
    Checking serde_path_to_error v0.1.16
    Checking unindent v0.2.3
    Checking base64 v0.22.1
    Checking unicode-segmentation v1.12.0
    Checking matchit v0.7.3
    Checking futures v0.3.31
    Checking tower-http v0.5.2
   Compiling chronik-bridge v0.1.0 (/work/chronik/chronik-bridge)
    Checking convert_case v0.6.0
   Compiling librocksdb-sys v0.11.0+8.1.1
    Checking toml_edit v0.22.22
    Checking axum v0.7.9
   Compiling pyo3-macros v0.23.3
    Checking toml v0.8.19
    Checking chronik-plugin-impl v0.1.0 (/work/chronik/chronik-plugin-impl)
   Compiling aws-lc-rs v1.11.1
   Compiling rustls v0.23.19
    Checking rustls-webpki v0.102.8
    Checking futures-rustls v0.26.0
    Checking karyon_async_rustls v0.1.8
    Checking karyon_net v0.1.8
    Checking karyon_jsonrpc v0.1.8
    Checking rocksdb v0.21.0
    Checking chronik-db v0.1.0 (/work/chronik/chronik-db)
    Checking chronik-indexer v0.1.0 (/work/chronik/chronik-indexer)
    Checking chronik-http v0.1.0 (/work/chronik/chronik-http)
error: accessing first element with `arr.get(0)`
   --> chronik/chronik-http/src/electrum.rs:163:34
    |
163 |               Value::Array(arr) => arr
    |  __________________________________^
164 | |                 .get(0)
    | |_______________________^ help: try: `arr.first()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#get_first
    = note: `-D clippy::get-first` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::get_first)]`

error: deref which would be done by auto-deref
   --> chronik/chronik-http/src/electrum.rs:183:35
    |
183 |         let txid = TxId::from_str(&*txid)
    |                                   ^^^^^^ help: try: `&txid`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
    = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::explicit_auto_deref)]`

error: could not compile `chronik-http` (lib) due to 2 previous errors
ninja: build stopped: cannot make progress due to previous errors.
Build build-chronik-plugins failed with exit code 1
PiRK retitled this revision from [chronki] add blockchain.transaction.get electrum method (WIP) to [chronik] add electrum method blockchain.transaction.get electrum method.
PiRK edited the summary of this revision. (Show Details)
PiRK edited the test plan for this revision. (Show Details)

return the raw hex tx (TODO: verbose)

Tail of the build log:

   Doc-tests chronik-bridge

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-db

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-http

running 1 test
test chronik/chronik-http/src/protobuf.rs - protobuf::Protobuf (line 29) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.36s

   Doc-tests chronik-indexer

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-common

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-impl

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-proto

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-util

running 2 tests
test chronik/chronik-util/src/log.rs - log::log (line 65) ... ignored
test chronik/chronik-util/src/log.rs - log::log_chronik (line 87) ... ignored

test result: ok. 0 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 0.00s

ninja: build stopped: cannot make progress due to previous errors.
Build build-chronik failed with exit code 1

Tail of the build log:

   Doc-tests chronik-bridge

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-db

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-http

running 1 test
test chronik/chronik-http/src/protobuf.rs - protobuf::Protobuf (line 29) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.34s

   Doc-tests chronik-indexer

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-common

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-plugin-impl

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-proto

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests chronik-util

running 2 tests
test chronik/chronik-util/src/log.rs - log::log (line 65) ... ignored
test chronik/chronik-util/src/log.rs - log::log_chronik (line 87) ... ignored

test result: ok. 0 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 0.00s

ninja: build stopped: cannot make progress due to previous errors.
Build build-chronik-plugins failed with exit code 1
PiRK edited the test plan for this revision. (Show Details)
Fabien added inline comments.
chronik/chronik-http/src/electrum.rs
166 ↗(On Diff #51523)

TIL, I thought you had to assign a default value and make them mutable

172 ↗(On Diff #51523)

"Missing mandatory txid parameter" is better imo

173–174 ↗(On Diff #51523)

Let's unwrap at the right place. ok_or() returns a Result, but no need to clone the Result only the contained Value

194 ↗(On Diff #51523)
204 ↗(On Diff #51523)

It would be a good idea to implement TryFrom<serde_json::Value> for TxId and avoid this boilerplate. See bitcoinsuite_core/src/tx/txid.rs

PiRK retitled this revision from [chronik] add electrum method blockchain.transaction.get electrum method to [chronik] add electrum method blockchain.transaction.get.Thu, Dec 12, 11:43
PiRK edited the summary of this revision. (Show Details)
PiRK edited the test plan for this revision. (Show Details)
PiRK added a task: Restricted Maniphest Task.

rebase, address most feedback, add a functional test

PiRK published this revision for review.Fri, Dec 13, 13:35
PiRK added inline comments.
chronik/chronik-http/src/electrum.rs
204 ↗(On Diff #51523)

I agree that it will be useful when we encounter a use case in which we don't reuse the intermediate txid_hex value later.

Fabien added inline comments.
chronik/chronik-http/src/electrum.rs
204 ↗(On Diff #51523)

it can take the Value by reference to avoid consuming it. But I agree, let's do that in another diff

This revision is now accepted and ready to land.Mon, Dec 16, 11:01

use consistent error message "Unknown transaction id"

Failed tests logs:

====== Bitcoin ABC functional tests: chronik_cors.py ======

------- Stdout: -------
2024-12-16T13:05:01.431000Z TestFramework (INFO): Initializing test directory /work/abc-ci-builds/build-chronik-plugins/test/tmp/test_runner_₿₵_🏃_20241216_130122/chronik_cors_206
2024-12-16T13:05:04.199000Z TestFramework (ERROR): Assertion failed
Traceback (most recent call last):
  File "/work/test/functional/test_framework/test_framework.py", line 635, in start_nodes
    node.wait_for_rpc_connection()
  File "/work/test/functional/test_framework/test_node.py", line 353, in wait_for_rpc_connection
    raise FailedToStartError(
test_framework.test_node.FailedToStartError: [node 0] bitcoind exited with status 1 during initialization

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/work/test/functional/test_framework/test_framework.py", line 148, in main
    self.setup()
  File "/work/test/functional/test_framework/test_framework.py", line 383, in setup
    self.setup_network()
  File "/work/test/functional/test_framework/test_framework.py", line 490, in setup_network
    self.setup_nodes()
  File "/work/test/functional/test_framework/test_framework.py", line 515, in setup_nodes
    self.start_nodes()
  File "/work/test/functional/test_framework/test_framework.py", line 638, in start_nodes
    self.stop_nodes()
  File "/work/test/functional/test_framework/test_framework.py", line 653, in stop_nodes
    node.stop_node(wait=wait, wait_until_stopped=False)
  File "/work/test/functional/test_framework/test_node.py", line 515, in stop_node
    self.stop(wait=wait)
    ^^^^^^^^^
  File "/work/test/functional/test_framework/test_node.py", line 283, in __getattr__
    assert self.rpc is not None, self._node_msg("Error: RPC not initialized")
           ^^^^^^^^^^^^^^^^^^^^
AssertionError: [node 0] Error: RPC not initialized
2024-12-16T13:05:04.255000Z TestFramework (INFO): Stopping nodes
[node 0] Cleaning up leftover process
------- Stderr: -------
Traceback (most recent call last):
  File "/work/test/functional/chronik_cors.py", line 41, in <module>
    ChronikCorsTest().main()
  File "/work/test/functional/test_framework/test_framework.py", line 172, in main
    exit_code = self.shutdown()
                ^^^^^^^^^^^^^^^
  File "/work/test/functional/test_framework/test_framework.py", line 398, in shutdown
    self.stop_nodes()
  File "/work/test/functional/test_framework/test_framework.py", line 653, in stop_nodes
    node.stop_node(wait=wait, wait_until_stopped=False)
  File "/work/test/functional/test_framework/test_node.py", line 515, in stop_node
    self.stop(wait=wait)
    ^^^^^^^^^
  File "/work/test/functional/test_framework/test_node.py", line 283, in __getattr__
    assert self.rpc is not None, self._node_msg("Error: RPC not initialized")
           ^^^^^^^^^^^^^^^^^^^^
AssertionError: [node 0] Error: RPC not initialized

Each failure log is accessible here:
Bitcoin ABC functional tests: chronik_cors.py

rebase after reducing the port range in D17314

Tail of the build log:

  File "/work/abc-ci-builds/ecash-lib-integration-tests/test/functional/test_runner.py", line 361, in main
    os.makedirs(tmpdir)
  File "<frozen os>", line 225, in makedirs
FileExistsError: [Errno 17] File exists: '/work/abc-ci-builds/ecash-lib-integration-tests/test/tmp/test_runner_₿₵_🏃_20241216_145110'
Test runner completed with code 1
----------------------------|---------|----------|---------|---------|------------------------------
File                        | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s            
----------------------------|---------|----------|---------|---------|------------------------------
All files                   |   58.43 |    46.55 |   56.95 |   58.46 |                              
 ecash-lib                  |       0 |        0 |       0 |       0 |                              
  eslint.config.js          |       0 |        0 |       0 |       0 |                              
 ecash-lib/src              |   70.01 |     50.2 |   71.26 |    70.1 |                              
  consts.ts                 |       0 |      100 |     100 |       0 | 6-8                          
  ecc.ts                    |   57.14 |    83.33 |      40 |   57.14 | 23-31                        
  hash.ts                   |   88.88 |    83.33 |      80 |   88.88 | 14                           
  index.ts                  |       0 |        0 |       0 |       0 |                              
  indexBrowser.ts           |       0 |        0 |       0 |       0 |                              
  indexNodeJs.ts            |       0 |        0 |       0 |       0 |                              
  initBrowser.ts            |       0 |      100 |       0 |       0 | 11-13                        
  initNodeJs.ts             |     100 |      100 |     100 |     100 |                              
  op.ts                     |      40 |    44.44 |   66.66 |      40 | ...4,107,109,117-122,133-161 
  opcode.ts                 |     100 |    83.33 |     100 |     100 | 1                            
  script.ts                 |   52.63 |    38.09 |      60 |    50.9 | ...4-135,146,156,166,188-199 
  sigHashType.ts            |   78.94 |       44 |   85.71 |   78.94 | 26-38                        
  tx.ts                     |   93.47 |    79.16 |    90.9 |   93.18 | 123-125                      
  txBuilder.ts              |   81.72 |    58.62 |   85.71 |   81.11 | ...1,154,173-178,183,253-257 
  unsignedTx.ts             |   49.27 |    30.18 |      60 |   50.38 | ...1,279,287,312,320,326-329 
 ecash-lib/src/ffi          |   28.26 |    15.94 |   16.98 |   28.98 |                              
  ecash_lib_wasm_browser.js |       0 |        0 |       0 |       0 | 3-336                        
  ecash_lib_wasm_nodejs.js  |    61.9 |       55 |   39.13 |   62.75 | ...1,197-215,237,250-251,255 
 ecash-lib/src/io           |   59.55 |    60.29 |   70.58 |   58.77 |                              
  bytes.ts                  |     7.4 |    71.42 |    12.5 |     7.4 | 13-64                        
  hex.ts                    |   82.05 |     62.5 |      80 |   82.35 | 41-45,50,58                  
  int.ts                    |       0 |        0 |       0 |       0 |                              
  str.ts                    |   85.71 |    83.33 |   66.66 |   85.71 | 15                           
  varsize.ts                |      32 |    36.36 |   66.66 |      32 | 14-24,40-47                  
  writer.ts                 |       0 |        0 |       0 |       0 |                              
  writerbytes.ts            |   83.33 |    68.42 |     100 |   83.33 | 33,43,53,63,79               
  writerlength.ts           |     100 |    83.33 |     100 |     100 | 1                            
 ecash-lib/src/test         |   86.58 |    52.08 |   84.21 |   86.84 |                              
  testRunner.ts             |   86.58 |    52.08 |   84.21 |   86.84 | 73-75,87-89,112,123,166,203  
 ecash-lib/src/token        |   60.33 |    47.14 |   53.33 |   60.11 |                              
  alp.ts                    |   82.92 |    89.47 |   83.33 |   82.92 | 110-123,142                  
  common.ts                 |     100 |    83.33 |     100 |     100 | 1                            
  empp.ts                   |    92.3 |       75 |     100 |   91.66 | 12                           
  slp.ts                    |    28.2 |    13.51 |    7.69 |    28.2 | ...7,174-178,185-197,201-211 
----------------------------|---------|----------|---------|---------|------------------------------

##teamcity[blockOpened name='Code Coverage Summary']
##teamcity[buildStatisticValue key='CodeCoverageAbsBCovered' value='769']
##teamcity[buildStatisticValue key='CodeCoverageAbsBTotal' value='1316']
##teamcity[buildStatisticValue key='CodeCoverageAbsRCovered' value='230']
##teamcity[buildStatisticValue key='CodeCoverageAbsRTotal' value='494']
##teamcity[buildStatisticValue key='CodeCoverageAbsMCovered' value='127']
##teamcity[buildStatisticValue key='CodeCoverageAbsMTotal' value='223']
##teamcity[buildStatisticValue key='CodeCoverageAbsLCovered' value='749']
##teamcity[buildStatisticValue key='CodeCoverageAbsLTotal' value='1281']
##teamcity[blockClosed name='Code Coverage Summary']
mv: cannot stat 'test_results/ecash-lib-integration-tests-junit.xml': No such file or directory
Build ecash-lib-integration-tests failed with exit code 1
This revision was landed with ongoing or failed builds.Mon, Dec 16, 15:32
This revision was automatically updated to reflect the committed changes.