From a6909402ad7dcb11e1512d19498cfb28e01ca79d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 12:59:46 +0200 Subject: [PATCH 001/128] introduce 'restapi.py' --- counterparty-core/counterpartycore/server.py | 60 ++-------- counterparty-lib/counterpartylib/lib/api.py | 33 +----- .../counterpartylib/lib/config.py | 6 +- .../counterpartylib/lib/restapi.py | 110 ++++++++++++++++++ counterparty-lib/counterpartylib/lib/util.py | 2 - counterparty-lib/counterpartylib/server.py | 85 ++++++++++++-- .../counterpartywallet/wallet/__init__.py | 2 +- 7 files changed, 198 insertions(+), 100 deletions(-) create mode 100644 counterparty-lib/counterpartylib/lib/restapi.py diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 09ec0a0891..1f529504b0 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -5,7 +5,7 @@ from urllib.parse import quote_plus as urlencode from counterpartylib import server -from counterpartylib.lib import config, log, setup +from counterpartylib.lib import config, setup from termcolor import cprint logger = logging.getLogger(config.LOGGER_NAME) @@ -227,6 +227,10 @@ "help": "how long to keep a lock on a UTXO being tracked", }, ], + [ + ("--legacy-api",), + {"action": "store_true", "default": False, "help": "Use legacy API (Deprecated)"}, + ], ] @@ -360,57 +364,7 @@ def main(): exit(0) # Configuration - init_args = dict( - database_file=args.database_file, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - customnet=args.customnet, - api_limit_rows=args.api_limit_rows, - backend_connect=args.backend_connect, - backend_port=args.backend_port, - backend_user=args.backend_user, - backend_password=args.backend_password, - backend_ssl=args.backend_ssl, - backend_ssl_no_verify=args.backend_ssl_no_verify, - backend_poll_interval=args.backend_poll_interval, - indexd_connect=args.indexd_connect, - indexd_port=args.indexd_port, - rpc_host=args.rpc_host, - rpc_port=args.rpc_port, - rpc_user=args.rpc_user, - rpc_password=args.rpc_password, - rpc_no_allow_cors=args.rpc_no_allow_cors, - requests_timeout=args.requests_timeout, - rpc_batch_size=args.rpc_batch_size, - check_asset_conservation=not args.no_check_asset_conservation, - force=args.force, - p2sh_dust_return_pubkey=args.p2sh_dust_return_pubkey, - utxo_locks_max_addresses=args.utxo_locks_max_addresses, - utxo_locks_max_age=args.utxo_locks_max_age, - ) - - server.initialise_log_config( - verbose=args.verbose, - quiet=args.quiet, - log_file=args.log_file, - api_log_file=args.api_log_file, - no_log_files=args.no_log_files, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - json_log=args.json_log, - ) - - # set up logging - log.set_up( - verbose=config.VERBOSE, - quiet=config.QUIET, - log_file=config.LOG, - log_in_console=args.action == "start", - ) - - server.initialise_config(**init_args) + server.initialise_log_and_config(args) logger.info(f"Running v{APP_VERSION} of {APP_NAME}.") @@ -436,7 +390,7 @@ def main(): ) elif args.action == "start": - server.start_all(catch_up=args.catch_up) + server.start_all(args) elif args.action == "show-params": server.show_params() diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index ec80a02b0a..64237a81af 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -184,10 +184,7 @@ "p2sh_pretx_txid", ] -API_MAX_LOG_SIZE = ( - 10 * 1024 * 1024 -) # max log size of 20 MB before rotation (make configurable later) -API_MAX_LOG_COUNT = 10 + JSON_RPC_ERROR_API_COMPOSE = -32001 # code to use for error composing transaction result CURRENT_API_STATUS_CODE = None # is updated by the APIStatusPoller @@ -652,7 +649,7 @@ def init_api_access_log(app): # Log to file, if configured... if config.API_LOG: handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", API_MAX_LOG_SIZE, API_MAX_LOG_COUNT + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT ) for l in loggers: # noqa: E741 handler.setLevel(logging.DEBUG) @@ -1214,32 +1211,6 @@ def _set_cors_headers(response): ##### REST ROUTES ##### - @app.route("/addresses/
/balances", methods=["GET"]) - def handle_address_balances(address): - return remove_rowids(ledger.get_address_balances(self.db, address)) - - @app.route("/assets//balances", methods=["GET"]) - def handle_asset_balances(asset): - return remove_rowids(ledger.get_asset_balances(self.db, asset)) - - @app.route("/assets//", methods=["GET"]) - def handle_asset_info(asset): - return remove_rowids(get_asset_info(asset=asset)) - - @app.route("/assets//orders", methods=["GET"]) - def handle_asset_orders(asset): - status = request.args.get("status", "open") - return remove_rowids(ledger.get_orders_by_asset(self.db, asset, status)) - - @app.route("/orders/", methods=["GET"]) - def handle_order_info(tx_hash): - return remove_rowids(ledger.get_order(self.db, tx_hash)) - - @app.route("/orders//matches", methods=["GET"]) - def handle_order_matches(tx_hash): - status = request.args.get("status", "pending") - return remove_rowids(ledger.get_order_matches_by_order(self.db, tx_hash, status)) - @app.route("/healthz", methods=["GET"]) def handle_healthz(): msg, code = "Healthy", 200 diff --git a/counterparty-lib/counterpartylib/lib/config.py b/counterparty-lib/counterpartylib/lib/config.py index 9107dbc530..001967825d 100644 --- a/counterparty-lib/counterpartylib/lib/config.py +++ b/counterparty-lib/counterpartylib/lib/config.py @@ -154,5 +154,7 @@ BOOTSTRAP_URL_MAINNET = "https://bootstrap.counterparty.io/counterparty.latest.tar.gz" BOOTSTRAP_URL_TESTNET = "https://bootstrap.counterparty.io/counterparty-testnet.latest.tar.gz" - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +API_MAX_LOG_SIZE = ( + 10 * 1024 * 1024 +) # max log size of 20 MB before rotation (make configurable later) +API_MAX_LOG_COUNT = 10 diff --git a/counterparty-lib/counterpartylib/lib/restapi.py b/counterparty-lib/counterpartylib/lib/restapi.py new file mode 100644 index 0000000000..1597e7124f --- /dev/null +++ b/counterparty-lib/counterpartylib/lib/restapi.py @@ -0,0 +1,110 @@ +import argparse +import logging +import multiprocessing +from logging import handlers as logging_handlers +from multiprocessing import Process + +import flask +from flask import Flask, request + +from counterpartylib import server +from counterpartylib.lib import ( + config, + database, + ledger, +) + +multiprocessing.set_start_method("spawn", force=True) + +logger = logging.getLogger(config.LOGGER_NAME) +app = Flask(__name__) +db = None +api_process = None + + +def init_api_access_log(app): + """Initialize API logger.""" + werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) + + # Disable console logging... + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + werkzeug_logger.setLevel(logging.CRITICAL) + werkzeug_logger.propagate = False + + # Log to file, if configured... + if config.API_LOG: + handler = logging_handlers.RotatingFileHandler( + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT + ) + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + handler.setLevel(logging.DEBUG) + werkzeug_logger.addHandler(handler) + + flask.cli.show_server_banner = lambda *args: None + + +def remove_rowids(query_result): + """Remove the rowid field from the query result.""" + filtered_results = [] + for row in list(query_result): + if "rowid" in row: + del row["rowid"] + if "MAX(rowid)" in row: + del row["MAX(rowid)"] + filtered_results.append(row) + return filtered_results + + +@app.route("/addresses/
/balances", methods=["GET"]) +def handle_address_balances(address): + return remove_rowids(ledger.get_address_balances(db, address)) + + +@app.route("/assets//balances", methods=["GET"]) +def handle_asset_balances(asset): + return remove_rowids(ledger.get_asset_balances(db, asset)) + + +# @app.route("/assets//", methods=["GET"]) +# def handle_asset_info(asset): +# return remove_rowids(get_asset_info(asset=asset)) + + +@app.route("/assets//orders", methods=["GET"]) +def handle_asset_orders(asset): + status = request.args.get("status", "open") + return remove_rowids(ledger.get_orders_by_asset(db, asset, status)) + + +@app.route("/orders/", methods=["GET"]) +def handle_order_info(tx_hash): + return remove_rowids(ledger.get_order(db, tx_hash)) + + +@app.route("/orders//matches", methods=["GET"]) +def handle_order_matches(tx_hash): + status = request.args.get("status", "pending") + return remove_rowids(ledger.get_order_matches_by_order(db, tx_hash, status)) + + +def run_api_server(args): + global db # noqa: PLW0603 + # Initialise log and config + server.initialise_log_and_config(argparse.Namespace(**args)) + init_api_access_log(app) + # Connect to the database + db = database.get_connection(read_only=True) + # Start the API server + app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) + print(f"REST API started at {config.RPC_HOST}:{config.RPC_PORT}") + + +def start(args): + api_process = Process(target=run_api_server, args=(vars(args),)) + api_process.start() + + +def stop(): + if api_process and api_process.is_alive(): + api_process.terminate() + print(f"REST API stopped at {config.RPC_HOST}:{config.RPC_PORT}") diff --git a/counterparty-lib/counterpartylib/lib/util.py b/counterparty-lib/counterpartylib/lib/util.py index 1700f03693..db3ba41511 100644 --- a/counterparty-lib/counterpartylib/lib/util.py +++ b/counterparty-lib/counterpartylib/lib/util.py @@ -1,12 +1,10 @@ import binascii import collections import decimal -import fractions # noqa: F401 import hashlib import itertools import json import logging -import os # noqa: F401 import random import re import sys diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 39bb1f6edd..6e5891f865 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -28,7 +28,8 @@ config, database, ledger, - log, # noqa: F401 + log, + restapi, transaction, util, ) @@ -57,10 +58,13 @@ def sigterm_handler(_signo, _stack_frame): assert False # noqa: B011 logger.info(f"Received {signal_name}.") + restapi.stop() + if "api_server" in globals(): logger.info("Stopping API server.") api_server.stop() # noqa: F821 api_status_poller.stop() # noqa: F821 + logger.info("Stopping backend.") backend.stop() logger.info("Shutting down.") @@ -544,6 +548,60 @@ def initialise_config( logger.info(f"Running v{config.VERSION_STRING} of counterparty-lib.") +def initialise_log_and_config(args): + config_args = dict( + database_file=args.database_file, + testnet=args.testnet, + testcoin=args.testcoin, + regtest=args.regtest, + customnet=args.customnet, + api_limit_rows=args.api_limit_rows, + backend_connect=args.backend_connect, + backend_port=args.backend_port, + backend_user=args.backend_user, + backend_password=args.backend_password, + backend_ssl=args.backend_ssl, + backend_ssl_no_verify=args.backend_ssl_no_verify, + backend_poll_interval=args.backend_poll_interval, + indexd_connect=args.indexd_connect, + indexd_port=args.indexd_port, + rpc_host=args.rpc_host, + rpc_port=args.rpc_port, + rpc_user=args.rpc_user, + rpc_password=args.rpc_password, + rpc_no_allow_cors=args.rpc_no_allow_cors, + requests_timeout=args.requests_timeout, + rpc_batch_size=args.rpc_batch_size, + check_asset_conservation=not args.no_check_asset_conservation, + force=args.force, + p2sh_dust_return_pubkey=args.p2sh_dust_return_pubkey, + utxo_locks_max_addresses=args.utxo_locks_max_addresses, + utxo_locks_max_age=args.utxo_locks_max_age, + ) + log_args = dict( + verbose=args.verbose, + quiet=args.quiet, + log_file=args.log_file, + api_log_file=args.api_log_file, + no_log_files=args.no_log_files, + testnet=args.testnet, + testcoin=args.testcoin, + regtest=args.regtest, + json_log=args.json_log, + ) + # initialise log config + initialise_log_config(**log_args) + # set up logging + log.set_up( + verbose=config.VERBOSE, + quiet=config.QUIET, + log_file=config.LOG, + log_in_console=args.action == "start", + ) + # initialise other config + initialise_config(**config_args) + + def initialise_db(): if config.FORCE: cprint("THE OPTION `--force` IS NOT FOR USE ON PRODUCTION SYSTEMS.", "yellow") @@ -595,11 +653,11 @@ def connect_to_addrindexrs(): print(f"{OK_GREEN} {step}") -def start_all(catch_up="normal"): +def start_all(args): # Backend. connect_to_backend() - if not os.path.exists(config.DATABASE) and catch_up == "bootstrap": + if not os.path.exists(config.DATABASE) and args.catch_up == "bootstrap": bootstrap(no_confirm=True) db = initialise_db() @@ -608,17 +666,22 @@ def start_all(catch_up="normal"): # initilise_config transaction.initialise() - # API Status Poller. - api_status_poller = api.APIStatusPoller() - api_status_poller.daemon = True - api_status_poller.start() + if args.legacy_api: + # API Status Poller. + api_status_poller = api.APIStatusPoller() + api_status_poller.daemon = True + api_status_poller.start() - # API Server. - api_server = api.APIServer() - api_server.daemon = True - api_server.start() + # API Server. + api_server = api.APIServer() + api_server.daemon = True + api_server.start() + else: + # REST API Server. + restapi.start(args) # Server + blocks.follow(db) diff --git a/counterparty-wallet/counterpartywallet/wallet/__init__.py b/counterparty-wallet/counterpartywallet/wallet/__init__.py index b1d0438e77..108a1db3e3 100644 --- a/counterparty-wallet/counterpartywallet/wallet/__init__.py +++ b/counterparty-wallet/counterpartywallet/wallet/__init__.py @@ -20,7 +20,7 @@ class LockedWalletError(WalletError): def wallet(): - return sys.modules[f"counterpartycli.wallet.{config.WALLET_NAME}"] + return sys.modules[f"counterpartywallet.wallet.{config.WALLET_NAME}"] def get_wallet_addresses(): From 00181f9d92b259f7c7a1a71b7ae9e165c3007efe Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 13:49:48 +0200 Subject: [PATCH 002/128] DRY routes --- .../counterpartylib/lib/restapi.py | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/restapi.py b/counterparty-lib/counterpartylib/lib/restapi.py index 1597e7124f..e460a40718 100644 --- a/counterparty-lib/counterpartylib/lib/restapi.py +++ b/counterparty-lib/counterpartylib/lib/restapi.py @@ -21,6 +21,26 @@ db = None api_process = None +ROUTES = { + "/addresses/
/balances": { + "function": ledger.get_address_balances, + }, + "/assets//balances": { + "function": ledger.get_asset_balances, + }, + "/assets//orders": { + "function": ledger.get_orders_by_asset, + "args": [("status", "open")], + }, + "/orders/": { + "function": ledger.get_order, + }, + "/orders//matches": { + "function": ledger.get_order_matches_by_order, + "args": [("status", "pending")], + }, +} + def init_api_access_log(app): """Initialize API logger.""" @@ -55,36 +75,19 @@ def remove_rowids(query_result): return filtered_results -@app.route("/addresses/
/balances", methods=["GET"]) -def handle_address_balances(address): - return remove_rowids(ledger.get_address_balances(db, address)) - - -@app.route("/assets//balances", methods=["GET"]) -def handle_asset_balances(asset): - return remove_rowids(ledger.get_asset_balances(db, asset)) - - # @app.route("/assets//", methods=["GET"]) # def handle_asset_info(asset): # return remove_rowids(get_asset_info(asset=asset)) -@app.route("/assets//orders", methods=["GET"]) -def handle_asset_orders(asset): - status = request.args.get("status", "open") - return remove_rowids(ledger.get_orders_by_asset(db, asset, status)) - - -@app.route("/orders/", methods=["GET"]) -def handle_order_info(tx_hash): - return remove_rowids(ledger.get_order(db, tx_hash)) - - -@app.route("/orders//matches", methods=["GET"]) -def handle_order_matches(tx_hash): - status = request.args.get("status", "pending") - return remove_rowids(ledger.get_order_matches_by_order(db, tx_hash, status)) +def handle_route(**kwargs): + route = ROUTES.get(str(request.url_rule.rule)) + function_args = dict(kwargs) + if "args" in route: + for arg in route["args"]: + function_args[arg[0]] = request.args.get(arg[0], arg[1]) + result = route["function"](db, **function_args) + return remove_rowids(result) def run_api_server(args): @@ -94,9 +97,11 @@ def run_api_server(args): init_api_access_log(app) # Connect to the database db = database.get_connection(read_only=True) + # Add routes + for path in ROUTES.keys(): + app.add_url_rule(path, view_func=handle_route) # Start the API server app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) - print(f"REST API started at {config.RPC_HOST}:{config.RPC_PORT}") def start(args): @@ -107,4 +112,3 @@ def start(args): def stop(): if api_process and api_process.is_alive(): api_process.terminate() - print(f"REST API stopped at {config.RPC_HOST}:{config.RPC_PORT}") From 110e5c8f433f432c32478b8284af6e7c2e92615f Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 14:13:10 +0200 Subject: [PATCH 003/128] refactor 'get_asset_info()' --- .../counterpartylib/lib/ledger.py | 44 ++++++++++++++++--- .../counterpartylib/lib/restapi.py | 12 +++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 43bcaf22f6..82c0ea3979 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -6,7 +6,7 @@ import time from decimal import Decimal as D -from counterpartylib.lib import config, exceptions, log, util +from counterpartylib.lib import backend, config, exceptions, log, util logger = logging.getLogger(config.LOGGER_NAME) @@ -609,18 +609,50 @@ def get_asset_issuances_quantity(db, asset): def get_asset_info(db, asset): - if asset == config.BTC or asset == config.XCP: - return {"divisible": True} + asset_name = resolve_subasset_longname(db, asset) + + # Defaults. + asset_info = { + "asset": asset_name, + "asset_longname": None, + "owner": None, + "divisible": True, + "locked": False, + "supply": 0, + "description": "", + "issuer": None, + } + + if asset_name == config.BTC: + asset_info["supply"] = backend.get_btc_supply(normalize=False) + return asset_info + elif asset_name == config.XCP: + asset_info["supply"] = xcp_supply(db) + return asset_info + else: + asset_info["supply"] = asset_supply(db, asset_name) + cursor = db.cursor() query = """ SELECT * FROM issuances WHERE (status = ? AND asset = ?) - ORDER BY tx_index DESC + ORDER BY rowid DESC + LIMIT 1 """ bindings = ("valid", asset) cursor.execute(query, bindings) - issuances = cursor.fetchall() - return issuances[0] + issuance = cursor.fetchone() + + asset_info = asset_info | { + "asset_longname": issuance["asset_longname"], + "owner": issuance["issuer"], + "divisible": bool(issuance["divisible"]), + "locked": bool(issuance["locked"]), + "description": issuance["description"], + "issuer": issuance["issuer"], + } + + return asset_info def get_issuances(db, asset=None, status=None, locked=None, first=False, last=False): diff --git a/counterparty-lib/counterpartylib/lib/restapi.py b/counterparty-lib/counterpartylib/lib/restapi.py index e460a40718..84307b904b 100644 --- a/counterparty-lib/counterpartylib/lib/restapi.py +++ b/counterparty-lib/counterpartylib/lib/restapi.py @@ -25,6 +25,9 @@ "/addresses/
/balances": { "function": ledger.get_address_balances, }, + "/assets//": { + "function": ledger.get_asset_info, + }, "/assets//balances": { "function": ledger.get_asset_balances, }, @@ -42,7 +45,7 @@ } -def init_api_access_log(app): +def init_api_access_log(): """Initialize API logger.""" werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) @@ -75,11 +78,6 @@ def remove_rowids(query_result): return filtered_results -# @app.route("/assets//", methods=["GET"]) -# def handle_asset_info(asset): -# return remove_rowids(get_asset_info(asset=asset)) - - def handle_route(**kwargs): route = ROUTES.get(str(request.url_rule.rule)) function_args = dict(kwargs) @@ -94,7 +92,7 @@ def run_api_server(args): global db # noqa: PLW0603 # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) - init_api_access_log(app) + init_api_access_log() # Connect to the database db = database.get_connection(read_only=True) # Add routes From ba618772ca12ea94b0a95376062e4251e3f1510f Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 15:02:46 +0200 Subject: [PATCH 004/128] Add API authentification --- counterparty-lib/counterpartylib/lib/restapi.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/counterparty-lib/counterpartylib/lib/restapi.py b/counterparty-lib/counterpartylib/lib/restapi.py index 84307b904b..1bde190b92 100644 --- a/counterparty-lib/counterpartylib/lib/restapi.py +++ b/counterparty-lib/counterpartylib/lib/restapi.py @@ -6,6 +6,7 @@ import flask from flask import Flask, request +from flask_httpauth import HTTPBasicAuth from counterpartylib import server from counterpartylib.lib import ( @@ -18,9 +19,11 @@ logger = logging.getLogger(config.LOGGER_NAME) app = Flask(__name__) +auth = HTTPBasicAuth() db = None api_process = None + ROUTES = { "/addresses/
/balances": { "function": ledger.get_address_balances, @@ -45,6 +48,11 @@ } +@auth.verify_password +def verify_password(username, password): + return username == config.RPC_USER and password == config.RPC_PASSWORD + + def init_api_access_log(): """Initialize API logger.""" werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) @@ -78,6 +86,7 @@ def remove_rowids(query_result): return filtered_results +@auth.login_required def handle_route(**kwargs): route = ROUTES.get(str(request.url_rule.rule)) function_args = dict(kwargs) From 893c86ee5caeef6d4812d176ad9b7db9376daec4 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 17:22:24 +0200 Subject: [PATCH 005/128] move old api in v1/; rename restapi.py to api.py --- counterparty-lib/counterpartylib/lib/api.py | 1490 +---------------- .../counterpartylib/lib/restapi.py | 121 -- .../counterpartylib/lib/v1/api.py | 1449 ++++++++++++++++ counterparty-lib/counterpartylib/server.py | 10 +- .../test/bytespersigop_test.py | 11 +- .../counterpartylib/test/complex_unit_test.py | 7 +- .../counterpartylib/test/conftest.py | 3 +- .../test/estimate_fee_per_kb_test.py | 3 +- .../test/p2sh_encoding_test.py | 24 +- .../counterpartylib/test/util_test.py | 5 +- 10 files changed, 1563 insertions(+), 1560 deletions(-) delete mode 100644 counterparty-lib/counterpartylib/lib/restapi.py create mode 100644 counterparty-lib/counterpartylib/lib/v1/api.py diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 64237a81af..1bde190b92 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -1,439 +1,77 @@ -#! /usr/bin/python3 - -""" -The database connections are read‐only, so SQL injection attacks can’t be a -problem. -""" - -import collections -import decimal -import json +import argparse import logging -import os # noqa: F401 -import re -import sys -import threading -import time -import traceback +import multiprocessing from logging import handlers as logging_handlers +from multiprocessing import Process -import requests # noqa: F401 - -D = decimal.Decimal -import binascii # noqa: E402 -import inspect # noqa: E402 -import math # noqa: E402 -import struct # noqa: E402, F401 - -import apsw # noqa: E402, F401 -import flask # noqa: E402 -import jsonrpc # noqa: E402 -from flask import request # noqa: E402 -from flask_httpauth import HTTPBasicAuth # noqa: E402 -from jsonrpc import dispatcher # noqa: E402 -from jsonrpc.exceptions import JSONRPCDispatchException # noqa: E402 -from xmltodict import unparse as serialize_to_xml # noqa: E402 +import flask +from flask import Flask, request +from flask_httpauth import HTTPBasicAuth -from counterpartylib.lib import ( # noqa: E402 - backend, - blocks, # noqa: F401 +from counterpartylib import server +from counterpartylib.lib import ( config, database, - exceptions, - gettxinfo, ledger, - message_type, - script, - transaction, - util, ) -from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser # noqa: E402 -from counterpartylib.lib.messages import ( # noqa: E402 - bet, # noqa: F401 - broadcast, # noqa: F401 - btcpay, # noqa: F401 - burn, # noqa: F401 - cancel, # noqa: F401 - destroy, # noqa: F401 - dispenser, # noqa: F401 - dividend, # noqa: F401 - issuance, # noqa: F401 - order, # noqa: F401 - rps, # noqa: F401 - rpsresolve, # noqa: F401 - send, - sweep, # noqa: F401 -) -from counterpartylib.lib.messages.versions import enhanced_send # noqa: E402 - -logger = logging.getLogger(config.LOGGER_NAME) -API_TABLES = [ - "assets", - "balances", - "credits", - "debits", - "bets", - "bet_matches", - "broadcasts", - "btcpays", - "burns", - "cancels", - "destructions", - "dividends", - "issuances", - "orders", - "order_matches", - "sends", - "bet_expirations", - "order_expirations", - "bet_match_expirations", - "order_match_expirations", - "bet_match_resolutions", - "rps", - "rpsresolves", - "rps_matches", - "rps_expirations", - "rps_match_expirations", - "mempool", - "sweeps", - "dispensers", - "dispenses", - "transactions", -] +multiprocessing.set_start_method("spawn", force=True) -VIEW_QUERIES = { - "balances": """ - SELECT *, MAX(rowid) AS rowid - FROM balances - GROUP BY address, asset - """, - "orders": """ - SELECT *, MAX(rowid) AS rowid - FROM orders - GROUP BY tx_hash - """, - "order_matches": """ - SELECT *, MAX(rowid) AS rowid - FROM order_matches - GROUP BY id - """, - "bets": """ - SELECT *, MAX(rowid) AS rowid - FROM bets - GROUP BY tx_hash - """, - "bets_matches": """ - SELECT *, MAX(rowid) AS rowid - FROM bet_matches - GROUP BY id - """, - "rps": """ - SELECT *, MAX(rowid) AS rowid - FROM rps - GROUP BY tx_hash - """, - "rps_matches": """ - SELECT *, MAX(rowid) AS rowid - FROM rps_matches - GROUP BY id - """, - "dispensers": """ - SELECT *, MAX(rowid) AS rowid - FROM dispensers - GROUP BY tx_hash - """, +logger = logging.getLogger(config.LOGGER_NAME) +app = Flask(__name__) +auth = HTTPBasicAuth() +db = None +api_process = None + + +ROUTES = { + "/addresses/
/balances": { + "function": ledger.get_address_balances, + }, + "/assets//": { + "function": ledger.get_asset_info, + }, + "/assets//balances": { + "function": ledger.get_asset_balances, + }, + "/assets//orders": { + "function": ledger.get_orders_by_asset, + "args": [("status", "open")], + }, + "/orders/": { + "function": ledger.get_order, + }, + "/orders//matches": { + "function": ledger.get_order_matches_by_order, + "args": [("status", "pending")], + }, } -API_TRANSACTIONS = [ - "bet", - "broadcast", - "btcpay", - "burn", - "cancel", - "destroy", - "dividend", - "issuance", - "order", - "send", - "rps", - "rpsresolve", - "sweep", - "dispenser", -] - -COMMONS_ARGS = [ - "encoding", - "fee_per_kb", - "regular_dust_size", - "multisig_dust_size", - "op_return_value", - "pubkey", - "allow_unconfirmed_inputs", - "fee", - "fee_provided", - "estimate_fee_per_kb", - "estimate_fee_per_kb_nblocks", - "unspent_tx_hash", - "custom_inputs", - "dust_return_pubkey", - "disable_utxo_locks", - "extended_tx_info", - "p2sh_source_multisig_pubkeys", - "p2sh_source_multisig_pubkeys_required", - "p2sh_pretx_txid", -] - - -JSON_RPC_ERROR_API_COMPOSE = -32001 # code to use for error composing transaction result - -CURRENT_API_STATUS_CODE = None # is updated by the APIStatusPoller -CURRENT_API_STATUS_RESPONSE_JSON = None # is updated by the APIStatusPoller - - -class APIError(Exception): - pass - - -class BackendError(Exception): - pass - - -def check_backend_state(): - f"""Checks blocktime of last block to see if {config.BTC_NAME} Core is running behind.""" # noqa: B021 - block_count = backend.getblockcount() - block_hash = backend.getblockhash(block_count) - cblock = backend.getblock(block_hash) - time_behind = time.time() - cblock.nTime # TODO: Block times are not very reliable. - if time_behind > 60 * 60 * 2: # Two hours. - raise BackendError(f"Bitcoind is running about {round(time_behind / 3600)} hours behind.") - - # check backend index - blocks_behind = backend.getindexblocksbehind() - if blocks_behind > 5: - raise BackendError(f"Indexd is running {blocks_behind} blocks behind.") - - logger.debug("Backend state check passed.") - - -class DatabaseError(Exception): - pass - - -def check_database_state(db, blockcount): - f"""Checks {config.XCP_NAME} database to see if is caught up with backend.""" # noqa: B021 - if ledger.CURRENT_BLOCK_INDEX + 1 < blockcount: - raise DatabaseError(f"{config.XCP_NAME} database is behind backend.") - logger.debug("Database state check passed.") - return - -# TODO: ALL queries EVERYWHERE should be done with these methods -def db_query(db, statement, bindings=(), callback=None, **callback_args): - """Allow direct access to the database in a parametrized manner.""" - cursor = db.cursor() +@auth.verify_password +def verify_password(username, password): + return username == config.RPC_USER and password == config.RPC_PASSWORD - # Sanitize. - forbidden_words = ["pragma", "attach", "database", "begin", "transaction"] - for word in forbidden_words: - # This will find if the forbidden word is in the statement as a whole word. For example, "transactions" will be allowed because the "s" at the end - if re.search(r"\b" + word + "\b", statement.lower()): - raise APIError(f"Forbidden word in query: '{word}'.") - if callable(callback): - cursor.execute(statement, bindings) - for row in cursor: - callback(row, **callback_args) - results = None - else: - results = list(cursor.execute(statement, bindings)) - cursor.close() - return results - - -def get_rows( - db, - table, - filters=None, - filterop="AND", - order_by=None, - order_dir=None, - start_block=None, - end_block=None, - status=None, - limit=1000, - offset=0, - show_expired=True, -): - """SELECT * FROM wrapper. Filters results based on a filter data structure (as used by the API).""" - - if filters == None: # noqa: E711 - filters = [] - - def value_to_marker(value): - # if value is an array place holder is (?,?,?,..) - if isinstance(value, list): - return f"""({','.join(['?' for e in range(0, len(value))])})""" - else: - return """?""" - - # TODO: Document that op can be anything that SQLite3 accepts. - if not table or table.lower() not in API_TABLES: - raise APIError("Unknown table") - if filterop and filterop.upper() not in ["OR", "AND"]: - raise APIError("Invalid filter operator (OR, AND)") - if order_dir and order_dir.upper() not in ["ASC", "DESC"]: - raise APIError("Invalid order direction (ASC, DESC)") - if not isinstance(limit, int): - raise APIError("Invalid limit") - elif config.API_LIMIT_ROWS != 0 and limit > config.API_LIMIT_ROWS: - raise APIError(f"Limit should be lower or equal to {config.API_LIMIT_ROWS}") - elif config.API_LIMIT_ROWS != 0 and limit == 0: - raise APIError("Limit should be greater than 0") - if not isinstance(offset, int): - raise APIError("Invalid offset") - # TODO: accept an object: {'field1':'ASC', 'field2': 'DESC'} - if order_by and not re.compile("^[a-z0-9_]+$").match(order_by): - raise APIError("Invalid order_by, must be a field name") - - if isinstance(filters, dict): # single filter entry, convert to a one entry list - filters = [ - filters, - ] - elif not isinstance(filters, list): - filters = [] - - # TODO: Document this! (Each filter can be an ordered list.) - new_filters = [] - for filter_ in filters: - if type(filter_) in (list, tuple) and len(filter_) in [3, 4]: - new_filter = {"field": filter_[0], "op": filter_[1], "value": filter_[2]} - if len(filter_) == 4: - new_filter["case_sensitive"] = filter_[3] - new_filters.append(new_filter) - elif type(filter_) == dict: # noqa: E721 - new_filters.append(filter_) - else: - raise APIError("Unknown filter type") - filters = new_filters - - # validate filter(s) - for filter_ in filters: - for field in ["field", "op", "value"]: # should have all fields - if field not in filter_: - raise APIError(f"A specified filter is missing the '{field}' field") - if not isinstance(filter_["value"], (str, int, float, list)): - raise APIError(f"Invalid value for the field '{filter_['field']}'") - if isinstance(filter_["value"], list) and filter_["op"].upper() not in ["IN", "NOT IN"]: - raise APIError(f"Invalid value for the field '{filter_['field']}'") - if filter_["op"].upper() not in [ - "=", - "==", - "!=", - ">", - "<", - ">=", - "<=", - "IN", - "LIKE", - "NOT IN", - "NOT LIKE", - ]: - raise APIError(f"Invalid operator for the field '{filter_['field']}'") - if "case_sensitive" in filter_ and not isinstance(filter_["case_sensitive"], bool): - raise APIError("case_sensitive must be a boolean") - - # special case for memo and memo_hex field searches - if table == "sends": - adjust_get_sends_memo_filters(filters) - - # SELECT - source = VIEW_QUERIES[table] if table in VIEW_QUERIES else table - # no sql injection here - statement = f"""SELECT * FROM ({source})""" # nosec B608 # noqa: S608 - # WHERE - bindings = [] - conditions = [] - for filter_ in filters: - case_sensitive = False if "case_sensitive" not in filter_ else filter_["case_sensitive"] - if filter_["op"] == "LIKE" and case_sensitive == False: # noqa: E712 - filter_["field"] = f"""UPPER({filter_['field']})""" - filter_["value"] = filter_["value"].upper() - marker = value_to_marker(filter_["value"]) - conditions.append(f"""{filter_['field']} {filter_['op']} {marker}""") - if isinstance(filter_["value"], list): - bindings += filter_["value"] - else: - bindings.append(filter_["value"]) - # AND filters - more_conditions = [] - if table not in ["balances", "order_matches", "bet_matches"]: - if start_block != None: # noqa: E711 - more_conditions.append("""block_index >= ?""") - bindings.append(start_block) - if end_block != None: # noqa: E711 - more_conditions.append("""block_index <= ?""") - bindings.append(end_block) - elif table in ["order_matches", "bet_matches"]: - if start_block != None: # noqa: E711 - more_conditions.append("""tx0_block_index >= ?""") - bindings.append(start_block) - if end_block != None: # noqa: E711 - more_conditions.append("""tx1_block_index <= ?""") - bindings.append(end_block) - - # status - if isinstance(status, list) and len(status) > 0: - more_conditions.append(f"""status IN {value_to_marker(status)}""") - bindings += status - elif isinstance(status, str) and status != "": - more_conditions.append("""status == ?""") - bindings.append(status) - - # legacy filters - if not show_expired and table == "orders": - # Ignore BTC orders one block early. - expire_index = ledger.CURRENT_BLOCK_INDEX + 1 - more_conditions.append("""((give_asset == ? AND expire_index > ?) OR give_asset != ?)""") - bindings += [config.BTC, expire_index, config.BTC] - - if (len(conditions) + len(more_conditions)) > 0: - statement += """ WHERE""" - all_conditions = [] - if len(conditions) > 0: - all_conditions.append(f"""({f' {filterop.upper()} '.join(conditions)})""") - if len(more_conditions) > 0: - all_conditions.append(f"""({' AND '.join(more_conditions)})""") - statement += f""" {' AND '.join(all_conditions)}""" - - # ORDER BY - if order_by != None: # noqa: E711 - statement += f""" ORDER BY {order_by}""" - if order_dir != None: # noqa: E711 - statement += f""" {order_dir.upper()}""" - # LIMIT - if limit and limit > 0: - statement += f""" LIMIT {limit}""" - if offset: - statement += f""" OFFSET {offset}""" - - query_result = db_query(db, statement, tuple(bindings)) - - if table == "balances": - return adjust_get_balances_results(query_result, db) - - if table == "destructions": - return adjust_get_destructions_results(query_result) +def init_api_access_log(): + """Initialize API logger.""" + werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) - if table == "sends": - # for sends, handle the memo field properly - return adjust_get_sends_results(query_result) + # Disable console logging... + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + werkzeug_logger.setLevel(logging.CRITICAL) + werkzeug_logger.propagate = False - if table == "transactions": - # for transactions, handle the data field properly - return adjust_get_transactions_results(query_result) + # Log to file, if configured... + if config.API_LOG: + handler = logging_handlers.RotatingFileHandler( + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT + ) + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + handler.setLevel(logging.DEBUG) + werkzeug_logger.addHandler(handler) - return remove_rowids(query_result) + flask.cli.show_server_banner = lambda *args: None def remove_rowids(query_result): @@ -445,1001 +83,39 @@ def remove_rowids(query_result): if "MAX(rowid)" in row: del row["MAX(rowid)"] filtered_results.append(row) - - return filtered_results - - -def adjust_get_balances_results(query_result, db): - filtered_results = [] - assets = {} - for balances_row in list(query_result): - asset = balances_row["asset"] - if asset not in assets: - assets[asset] = ledger.is_divisible(db, asset) - - balances_row["divisible"] = assets[asset] - filtered_results.append(balances_row) - - return filtered_results - - -def adjust_get_destructions_results(query_result): - filtered_results = [] - for destruction_row in list(query_result): - if type(destruction_row["tag"]) == bytes: # noqa: E721 - destruction_row["tag"] = destruction_row["tag"].decode("utf-8", "ignore") - - filtered_results.append(destruction_row) - - return filtered_results - - -def adjust_get_sends_memo_filters(filters): - """Convert memo to a byte string. If memo_hex is supplied, attempt to decode it and use that instead.""" - for filter_ in filters: - if filter_["field"] == "memo": - filter_["value"] = bytes(filter_["value"], "utf-8") - if filter_["field"] == "memo_hex": - # search the indexed memo field with a byte string - filter_["field"] = "memo" - try: - filter_["value"] = bytes.fromhex(filter_["value"]) - except ValueError as e: # noqa: F841 - raise APIError("Invalid memo_hex value") # noqa: B904 - - -def adjust_get_sends_results(query_result): - """Format the memo_hex field. Try and decode the memo from a utf-8 uncoded string. Invalid utf-8 strings return an empty memo.""" - filtered_results = [] - for send_row in list(query_result): - try: - if send_row["memo"] is None: - send_row["memo_hex"] = None - send_row["memo"] = None - else: - if type(send_row["memo"]) == str: # noqa: E721 - send_row["memo"] = bytes(send_row["memo"], "utf-8") - - send_row["memo_hex"] = binascii.hexlify(send_row["memo"]).decode("utf8") - send_row["memo"] = send_row["memo"].decode("utf-8") - except UnicodeDecodeError: - send_row["memo"] = "" - filtered_results.append(send_row) - return filtered_results - - -def adjust_get_transactions_results(query_result): - """Format the data field. Try and decode the data from a utf-8 uncoded string. Invalid utf-8 strings return an empty data.""" - filtered_results = [] - for transaction_row in list(query_result): - transaction_row["data"] = transaction_row["data"].hex() - filtered_results.append(transaction_row) return filtered_results -def compose_transaction( - db, - name, - params, - encoding="auto", - fee_per_kb=None, - estimate_fee_per_kb=None, - regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, - multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, - op_return_value=config.DEFAULT_OP_RETURN_VALUE, - pubkey=None, - allow_unconfirmed_inputs=False, - fee=None, - fee_provided=0, - unspent_tx_hash=None, - custom_inputs=None, - dust_return_pubkey=None, - disable_utxo_locks=False, - extended_tx_info=False, - p2sh_source_multisig_pubkeys=None, - p2sh_source_multisig_pubkeys_required=None, - p2sh_pretx_txid=None, - old_style_api=True, - segwit=False, -): - """Create and return a transaction.""" - - # Get provided pubkeys. - if type(pubkey) == str: # noqa: E721 - provided_pubkeys = [pubkey] - elif type(pubkey) == list: # noqa: E721 - provided_pubkeys = pubkey - elif pubkey == None: # noqa: E711 - provided_pubkeys = [] - else: - assert False # noqa: B011 - - # Get additional pubkeys from `source` and `destination` params. - # Convert `source` and `destination` to pubkeyhash form. - for address_name in ["source", "destination"]: - if address_name in params: - address = params[address_name] - if isinstance(address, list): - # pkhshs = [] - # for addr in address: - # provided_pubkeys += script.extract_pubkeys(addr) - # pkhshs.append(script.make_pubkeyhash(addr)) - # params[address_name] = pkhshs - pass - else: - provided_pubkeys += script.extract_pubkeys(address) - params[address_name] = script.make_pubkeyhash(address) - - # Check validity of collected pubkeys. - for pubkey in provided_pubkeys: - if not script.is_fully_valid(binascii.unhexlify(pubkey)): - raise script.AddressError(f"invalid public key: {pubkey}") - - compose_method = sys.modules[f"counterpartylib.lib.messages.{name}"].compose - compose_params = inspect.getfullargspec(compose_method)[0] - missing_params = [p for p in compose_params if p not in params and p != "db"] - for param in missing_params: - params[param] = None - - # dont override fee_per_kb if specified - if fee_per_kb is not None: - estimate_fee_per_kb = False - else: - fee_per_kb = config.DEFAULT_FEE_PER_KB - - if "extended_tx_info" in params: - extended_tx_info = params["extended_tx_info"] - del params["extended_tx_info"] - - if "old_style_api" in params: - old_style_api = params["old_style_api"] - del params["old_style_api"] - - if "segwit" in params: - segwit = params["segwit"] - del params["segwit"] - - tx_info = compose_method(db, **params) - return transaction.construct( - db, - tx_info, - encoding=encoding, - fee_per_kb=fee_per_kb, - estimate_fee_per_kb=estimate_fee_per_kb, - regular_dust_size=regular_dust_size, - multisig_dust_size=multisig_dust_size, - op_return_value=op_return_value, - provided_pubkeys=provided_pubkeys, - allow_unconfirmed_inputs=allow_unconfirmed_inputs, - exact_fee=fee, - fee_provided=fee_provided, - unspent_tx_hash=unspent_tx_hash, - custom_inputs=custom_inputs, - dust_return_pubkey=dust_return_pubkey, - disable_utxo_locks=disable_utxo_locks, - extended_tx_info=extended_tx_info, - p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, - p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required, - p2sh_pretx_txid=p2sh_pretx_txid, - old_style_api=old_style_api, - segwit=segwit, - ) - - -def conditional_decorator(decorator, condition): - """Checks the condition and if True applies specified decorator.""" - - def gen_decorator(f): - if not condition: - return f - return decorator(f) - - return gen_decorator - - -def init_api_access_log(app): - """Initialize API logger.""" - loggers = (logging.getLogger("werkzeug"), app.logger) - - # Disable console logging... - for l in loggers: # noqa: E741 - l.setLevel(logging.CRITICAL) - l.propagate = False - - # Log to file, if configured... - if config.API_LOG: - handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT - ) - for l in loggers: # noqa: E741 - handler.setLevel(logging.DEBUG) - l.addHandler(handler) - - flask.cli.show_server_banner = lambda *args: None - - -class APIStatusPoller(threading.Thread): - """Perform regular checks on the state of the backend and the database.""" - - def __init__(self): - self.last_database_check = 0 - threading.Thread.__init__(self) - self.stop_event = threading.Event() - - def stop(self): - self.stop_event.set() - - def run(self): - logger.debug("Starting API Status Poller.") - global CURRENT_API_STATUS_CODE, CURRENT_API_STATUS_RESPONSE_JSON # noqa: PLW0603 - db = database.get_connection(read_only=True) - - while self.stop_event.is_set() != True: # noqa: E712 - try: - # Check that backend is running, communicable, and caught up with the blockchain. - # Check that the database has caught up with bitcoind. - if ( - time.time() - self.last_database_check > 10 * 60 - ): # Ten minutes since last check. - if not config.FORCE: - code = 11 - logger.debug("Checking backend state.") - check_backend_state() - code = 12 - logger.debug("Checking database state.") - check_database_state(db, backend.getblockcount()) - self.last_database_check = time.time() - except (BackendError, DatabaseError) as e: - exception_name = e.__class__.__name__ - exception_text = str(e) - logger.debug("API Status Poller: %s", exception_text) - jsonrpc_response = jsonrpc.exceptions.JSONRPCServerError( - message=exception_name, data=exception_text - ) - CURRENT_API_STATUS_CODE = code - CURRENT_API_STATUS_RESPONSE_JSON = jsonrpc_response.json.encode() - else: - CURRENT_API_STATUS_CODE = None - CURRENT_API_STATUS_RESPONSE_JSON = None - time.sleep(config.BACKEND_POLL_INTERVAL) - - -class APIServer(threading.Thread): - """Handle JSON-RPC API calls.""" - - def __init__(self, db=None): - self.db = db - self.is_ready = False - threading.Thread.__init__(self) - self.stop_event = threading.Event() - - def stop(self): - self.join() - self.stop_event.set() - - def run(self): - logger.info("Starting API Server.") - self.db = self.db or database.get_connection(read_only=True) - app = flask.Flask(__name__) - auth = HTTPBasicAuth() - - @auth.get_password - def get_pw(username): - if username == config.RPC_USER: - return config.RPC_PASSWORD - return None - - ###################### - # READ API - - # Generate dynamically get_{table} methods - def generate_get_method(table): - def get_method(**kwargs): - try: - return get_rows(self.db, table=table, **kwargs) - except TypeError as e: # TODO: generalise for all API methods - raise APIError(str(e)) # noqa: B904 - - return get_method - - for table in API_TABLES: - new_method = generate_get_method(table) - new_method.__name__ = f"get_{table}" - dispatcher.add_method(new_method) - - @dispatcher.add_method - def sql(query, bindings=None): - if bindings == None: # noqa: E711 - bindings = [] - return db_query(self.db, query, tuple(bindings)) - - ###################### - # WRITE/ACTION API - - # Generate dynamically create_{transaction} methods - def generate_create_method(tx): - def split_params(**kwargs): - transaction_args = {} - common_args = {} - private_key_wif = None - for key in kwargs: - if key in COMMONS_ARGS: - common_args[key] = kwargs[key] - elif key == "privkey": - private_key_wif = kwargs[key] - else: - transaction_args[key] = kwargs[key] - return transaction_args, common_args, private_key_wif - - def create_method(**kwargs): - try: - transaction_args, common_args, private_key_wif = split_params(**kwargs) - return compose_transaction( - self.db, name=tx, params=transaction_args, **common_args - ) - except ( - TypeError, - script.AddressError, - exceptions.ComposeError, - exceptions.TransactionError, - exceptions.BalanceError, - ) as error: - # TypeError happens when unexpected keyword arguments are passed in - error_msg = f"Error composing {tx} transaction via API: {str(error)}" - logging.warning(error_msg) - logging.warning(traceback.format_exc()) - raise JSONRPCDispatchException( # noqa: B904 - code=JSON_RPC_ERROR_API_COMPOSE, message=error_msg - ) - - return create_method - - for tx in API_TRANSACTIONS: - create_method = generate_create_method(tx) - create_method.__name__ = f"create_{tx}" - dispatcher.add_method(create_method) - - @dispatcher.add_method - def get_messages(block_index): - if not isinstance(block_index, int): - raise APIError("block_index must be an integer.") - - messages = ledger.get_messages(self.db, block_index=block_index) - return messages - - @dispatcher.add_method - def get_messages_by_index(message_indexes): - """Get specific messages from the feed, based on the message_index. - - @param message_index: A single index, or a list of one or more message indexes to retrieve. - """ - if not isinstance(message_indexes, list): - message_indexes = [ - message_indexes, - ] - for idx in message_indexes: # make sure the data is clean - if not isinstance(idx, int): - raise APIError("All items in message_indexes are not integers") - - messages = ledger.get_messages(self.db, message_index_in=message_indexes) - return messages - - @dispatcher.add_method - def get_supply(asset): - if asset == "BTC": - return backend.get_btc_supply(normalize=False) - elif asset == "XCP": - return ledger.xcp_supply(self.db) - else: - asset = ledger.resolve_subasset_longname(self.db, asset) - return ledger.asset_supply(self.db, asset) - - @dispatcher.add_method - def get_xcp_supply(): - logger.warning("Deprecated method: `get_xcp_supply`") - return ledger.xcp_supply(self.db) - - @dispatcher.add_method - def get_asset_info(assets=None, asset=None): - if asset is not None: - assets = [asset] - - if not isinstance(assets, list): - raise APIError( - "assets must be a list of asset names, even if it just contains one entry" - ) - assets_info = [] - for asset in assets: - asset = ledger.resolve_subasset_longname(self.db, asset) # noqa: PLW2901 - - # BTC and XCP. - if asset in [config.BTC, config.XCP]: - if asset == config.BTC: - supply = backend.get_btc_supply(normalize=False) - else: - supply = ledger.xcp_supply(self.db) - - assets_info.append( - { - "asset": asset, - "asset_longname": None, - "owner": None, - "divisible": True, - "locked": False, - "supply": supply, - "description": "", - "issuer": None, - } - ) - continue - - # User‐created asset. - cursor = self.db.cursor() - issuances = ledger.get_issuances(self.db, asset=asset, status="valid", first=True) - cursor.close() - if not issuances: - continue # asset not found, most likely - else: - last_issuance = issuances[-1] - locked = False - for e in issuances: - if e["locked"]: - locked = True - assets_info.append( - { - "asset": asset, - "asset_longname": last_issuance["asset_longname"], - "owner": last_issuance["issuer"], - "divisible": bool(last_issuance["divisible"]), - "locked": locked, - "supply": ledger.asset_supply(self.db, asset), - "description": last_issuance["description"], - "issuer": last_issuance["issuer"], - } - ) - return assets_info - - @dispatcher.add_method - def get_block_info(block_index): - assert isinstance(block_index, int) - cursor = self.db.cursor() - cursor.execute("""SELECT * FROM blocks WHERE block_index = ?""", (block_index,)) - blocks = list(cursor) # noqa: F811 - if len(blocks) == 1: - block = blocks[0] - elif len(blocks) == 0: - raise exceptions.DatabaseError("No blocks found.") - else: - assert False # noqa: B011 - cursor.close() - return block - - @dispatcher.add_method - def fee_per_kb(conf_target=config.ESTIMATE_FEE_CONF_TARGET, mode=config.ESTIMATE_FEE_MODE): - return backend.fee_per_kb(conf_target, mode) - - @dispatcher.add_method - def get_blocks(block_indexes, min_message_index=None): - """fetches block info and messages for the specified block indexes - @param min_message_index: Retrieve blocks from the message feed on or after this specific message index - (useful since blocks may appear in the message feed more than once, if a reorg occurred). Note that - if this parameter is not specified, the messages for the first block will be returned. - """ - if not isinstance(block_indexes, (list, tuple)): - raise APIError("block_indexes must be a list of integers.") - if len(block_indexes) >= 250: - raise APIError("can only specify up to 250 indexes at a time.") - for block_index in block_indexes: - if not isinstance(block_index, int): - raise APIError("block_indexes must be a list of integers.") - - cursor = self.db.cursor() - - block_indexes_placeholder = f"({','.join(['?'] * len(block_indexes))})" - # no sql injection here - cursor.execute( - f"SELECT * FROM blocks WHERE block_index IN ({block_indexes_placeholder}) ORDER BY block_index ASC", # nosec B608 # noqa: S608 - block_indexes, - ) - blocks = cursor.fetchall() # noqa: F811 - - messages = collections.deque(ledger.get_messages(self.db, block_index_in=block_indexes)) - - # Discard any messages less than min_message_index - if min_message_index: - while len(messages) and messages[0]["message_index"] < min_message_index: - messages.popleft() - - # Packages messages into their appropriate block in the data structure to be returned - for block in blocks: - block["_messages"] = [] - while len(messages) and messages[0]["block_index"] == block["block_index"]: - block["_messages"].append(messages.popleft()) - # NOTE: if len(messages), then we're only returning the messages for the first set of blocks before the reorg - - cursor.close() - return blocks - - @dispatcher.add_method - def get_running_info(): - latest_block_index = backend.getblockcount() - - try: - check_database_state(self.db, latest_block_index) - except DatabaseError: - caught_up = False - else: - caught_up = True - - try: - cursor = self.db.cursor() - blocks = list( - cursor.execute( - """SELECT * FROM blocks WHERE block_index = ?""", - (ledger.CURRENT_BLOCK_INDEX,), - ) - ) - assert len(blocks) == 1 - last_block = blocks[0] - cursor.close() - except: # noqa: E722 - last_block = None - - try: - last_message = ledger.last_message(self.db) - except: # noqa: E722 - last_message = None - - try: - indexd_blocks_behind = backend.getindexblocksbehind() - except: # noqa: E722 - indexd_blocks_behind = latest_block_index if latest_block_index > 0 else 999999 - indexd_caught_up = indexd_blocks_behind <= 1 - - server_ready = caught_up and indexd_caught_up - - return { - "server_ready": server_ready, - "db_caught_up": caught_up, - "bitcoin_block_count": latest_block_index, - "last_block": last_block, - "indexd_caught_up": indexd_caught_up, - "indexd_blocks_behind": indexd_blocks_behind, - "last_message_index": last_message["message_index"] if last_message else -1, - "api_limit_rows": config.API_LIMIT_ROWS, - "running_testnet": config.TESTNET, - "running_regtest": config.REGTEST, - "running_testcoin": config.TESTCOIN, - "version_major": config.VERSION_MAJOR, - "version_minor": config.VERSION_MINOR, - "version_revision": config.VERSION_REVISION, - } - - @dispatcher.add_method - def get_element_counts(): - counts = {} - cursor = self.db.cursor() - for element in [ - "transactions", - "blocks", - "debits", - "credits", - "balances", - "sends", - "orders", - "order_matches", - "btcpays", - "issuances", - "broadcasts", - "bets", - "bet_matches", - "dividends", - "burns", - "cancels", - "order_expirations", - "bet_expirations", - "order_match_expirations", - "bet_match_expirations", - "messages", - "destructions", - ]: - # no sql injection here, element is hardcoded - cursor.execute(f"SELECT COUNT(*) AS count FROM {element}") # nosec B608 # noqa: S608 - count_list = cursor.fetchall() - assert len(count_list) == 1 - counts[element] = count_list[0]["count"] - cursor.close() - return counts - - @dispatcher.add_method - def get_asset_names(longnames=False): - all_assets = ledger.get_valid_assets(self.db) - if longnames: - names = [ - {"asset": row["asset"], "asset_longname": row["asset_longname"]} - for row in all_assets - ] - else: - names = [row["asset"] for row in all_assets] - return names - - @dispatcher.add_method - def get_asset_longnames(): - return get_asset_names(longnames=True) - - @dispatcher.add_method - def get_holder_count(asset): - asset = ledger.resolve_subasset_longname(self.db, asset) - holders = ledger.holders(self.db, asset, True) - addresses = [] - for holder in holders: - addresses.append(holder["address"]) - return {asset: len(set(addresses))} - - @dispatcher.add_method - def get_holders(asset): - asset = ledger.resolve_subasset_longname(self.db, asset) - holders = ledger.holders(self.db, asset, True) - return holders - - @dispatcher.add_method - def search_raw_transactions(address, unconfirmed=True, only_tx_hashes=False): - return backend.search_raw_transactions( - address, unconfirmed=unconfirmed, only_tx_hashes=only_tx_hashes - ) - - @dispatcher.add_method - def get_oldest_tx(address): - return backend.get_oldest_tx(address) - - @dispatcher.add_method - def get_unspent_txouts(address, unconfirmed=False, unspent_tx_hash=None, order_by=None): - results = backend.get_unspent_txouts( - address, unconfirmed=unconfirmed, unspent_tx_hash=unspent_tx_hash - ) - if order_by is None: - return results - else: - order_key = order_by - reverse = False - if order_key.startswith("-"): - order_key = order_key[1:] - reverse = True - return sorted(results, key=lambda x: x[order_key], reverse=reverse) - - @dispatcher.add_method - def getrawtransaction(tx_hash, verbose=False, skip_missing=False): - return backend.getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing) - - @dispatcher.add_method - def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False): - return backend.getrawtransaction_batch( - txhash_list, verbose=verbose, skip_missing=skip_missing - ) - - @dispatcher.add_method - def get_tx_info(tx_hex, block_index=None): - # block_index mandatory for transactions before block 335000 - source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( - self.db, BlockchainParser().deserialize_tx(tx_hex), block_index=block_index - ) - return source, destination, btc_amount, fee, util.hexlify(data) if data else "" - - @dispatcher.add_method - def unpack(data_hex): - data = binascii.unhexlify(data_hex) - message_type_id, message = message_type.unpack(data) - - # TODO: Enabled only for `send`. - if message_type_id == send.ID: - unpack_method = send.unpack - elif message_type_id == enhanced_send.ID: - unpack_method = enhanced_send.unpack - else: - raise APIError("unsupported message type") - unpacked = unpack_method(self.db, message, ledger.CURRENT_BLOCK_INDEX) - return message_type_id, unpacked - - @dispatcher.add_method - # TODO: Rename this method. - def search_pubkey(pubkeyhash, provided_pubkeys=None): - return backend.pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=provided_pubkeys) - - @dispatcher.add_method - def get_dispenser_info(tx_hash=None, tx_index=None): - cursor = self.db.cursor() # noqa: F841 - - if tx_hash is None and tx_index is None: - raise APIError("You must provided a tx hash or a tx index") - - dispensers = [] - if tx_hash is not None: - dispensers = get_dispenser_info(self.db, tx_hash=tx_hash) - else: - dispensers = get_dispenser_info(self.db, tx_index=tx_index) - - if len(dispensers) == 1: - dispenser = dispensers[0] - oracle_price = "" - satoshi_price = "" - fiat_price = "" - oracle_price_last_updated = "" - oracle_fiat_label = "" - - if dispenser["oracle_address"] != None: # noqa: E711 - fiat_price = util.satoshirate_to_fiat(dispenser["satoshirate"]) - oracle_price, oracle_fee, oracle_fiat_label, oracle_price_last_updated = ( - ledger.get_oracle_last_price( - self.db, dispenser["oracle_address"], ledger.CURRENT_BLOCK_INDEX - ) - ) - - if oracle_price > 0: - satoshi_price = math.ceil((fiat_price / oracle_price) * config.UNIT) - else: - raise APIError("Last oracle price is zero") - - return { - "tx_index": dispenser["tx_index"], - "tx_hash": dispenser["tx_hash"], - "block_index": dispenser["block_index"], - "source": dispenser["source"], - "asset": dispenser["asset"], - "give_quantity": dispenser["give_quantity"], - "escrow_quantity": dispenser["escrow_quantity"], - "mainchainrate": dispenser["satoshirate"], - "fiat_price": fiat_price, - "fiat_unit": oracle_fiat_label, - "oracle_price": oracle_price, - "satoshi_price": satoshi_price, - "status": dispenser["status"], - "give_remaining": dispenser["give_remaining"], - "oracle_address": dispenser["oracle_address"], - "oracle_price_last_updated": oracle_price_last_updated, - "asset_longname": dispenser["asset_longname"], - } - - return {} - - def _set_cors_headers(response): - if not config.RPC_NO_ALLOW_CORS: - response.headers["Access-Control-Allow-Origin"] = "*" - response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" - response.headers["Access-Control-Allow-Headers"] = ( - "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" - ) - - ##### REST ROUTES ##### - - @app.route("/healthz", methods=["GET"]) - def handle_healthz(): - msg, code = "Healthy", 200 - - type_ = request.args.get("type", "heavy") - - try: - if type_ == "light": - logger.debug("Performing light healthz check.") - latest_block_index = backend.getblockcount() - check_database_state(self.db, latest_block_index) - else: - logger.debug("Performing heavy healthz check.") - compose_transaction( - self.db, - name="send", - params={ - "source": config.UNSPENDABLE, - "destination": config.UNSPENDABLE, - "asset": config.XCP, - "quantity": 100000000, - }, - allow_unconfirmed_inputs=True, - fee=1000, - ) - except Exception: - msg, code = "Unhealthy", 503 - - return flask.Response(msg, code, mimetype="application/json") - - @app.route("/", defaults={"args_path": ""}, methods=["GET", "POST", "OPTIONS"]) - @app.route("/", methods=["GET", "POST", "OPTIONS"]) - # Only require authentication if RPC_PASSWORD is set. - @conditional_decorator(auth.login_required, hasattr(config, "RPC_PASSWORD")) - def handle_root(args_path): - """Handle all paths, decide where to forward the query.""" - if ( - args_path == "" - or args_path.startswith("api/") - or args_path.startswith("API/") - or args_path.startswith("rpc/") - or args_path.startswith("RPC/") - ): - if flask.request.method == "POST": - # Need to get those here because it might not be available in this aux function. - request_json = flask.request.get_data().decode("utf-8") - response = handle_rpc_post(request_json) - return response - elif flask.request.method == "OPTIONS": - response = handle_rpc_options() - return response - else: - error = "Invalid method." - return flask.Response(error, 405, mimetype="application/json") - elif args_path.startswith("rest/") or args_path.startswith("REST/"): - if flask.request.method == "GET" or flask.request.method == "POST": - # Pass the URL path without /REST/ part and Flask request object. - rest_path = args_path.split("/", 1)[1] - response = handle_rest(rest_path, flask.request) - return response - else: - error = "Invalid method." - return flask.Response(error, 405, mimetype="application/json") - else: - # Not found - return flask.Response(None, 404, mimetype="application/json") - - ###################### - # JSON-RPC API - ###################### - def handle_rpc_options(): - response = flask.Response("", 204) - _set_cors_headers(response) - return response - - def handle_rpc_post(request_json): - """Handle /API/ POST route. Call relevant get_rows/create_transaction wrapper.""" - # Check for valid request format. - try: - request_data = json.loads(request_json) - assert ( - "id" in request_data - and request_data["jsonrpc"] == "2.0" - and request_data["method"] - ) - # params may be omitted - except: # noqa: E722 - obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( - data="Invalid JSON-RPC 2.0 request format" - ) - return flask.Response(obj_error.json.encode(), 400, mimetype="application/json") - - # Only arguments passed as a `dict` are supported. - if request_data.get("params", None) and not isinstance(request_data["params"], dict): - obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( - data="Arguments must be passed as a JSON object (list of unnamed arguments not supported)" - ) - return flask.Response(obj_error.json.encode(), 400, mimetype="application/json") - - # Return an error if the API Status Poller checks fail. - if not config.FORCE and CURRENT_API_STATUS_CODE: - return flask.Response( - CURRENT_API_STATUS_RESPONSE_JSON, 503, mimetype="application/json" - ) - - # Answer request normally. - # NOTE: `UnboundLocalError: local variable 'output' referenced before assignment` means the method doesn’t return anything. - jsonrpc_response = jsonrpc.JSONRPCResponseManager.handle(request_json, dispatcher) - response = flask.Response( - jsonrpc_response.json.encode(), 200, mimetype="application/json" - ) - _set_cors_headers(response) - return response - - ###################### - # HTTP REST API - ###################### - def handle_rest(path_args, flask_request): - """Handle /REST/ route. Query the database using get_rows or create transaction using compose_transaction.""" - url_action = flask_request.path.split("/")[-1] - if url_action == "compose": - compose = True - elif url_action == "get": - compose = False - else: - error = f'Invalid action "{url_action}".' - return flask.Response(error, 400, mimetype="application/json") - - # Get all arguments passed via URL. - url_args = path_args.split("/") - try: - query_type = url_args.pop(0).lower() - except IndexError: - error = "No query_type provided." - return flask.Response(error, 400, mimetype="application/json") - # Check if message type or table name are valid. - if (compose and query_type not in API_TRANSACTIONS) or ( - not compose and query_type not in API_TABLES - ): - error = f'No such query type in supported queries: "{query_type}".' - return flask.Response(error, 400, mimetype="application/json") - - # Parse the additional arguments. - extra_args = flask_request.args.items() - query_data = {} - - if compose: - common_args = {} - transaction_args = {} - for key, value in extra_args: - # Determine value type. - try: - value = int(value) # noqa: PLW2901 - except ValueError: - try: - value = float(value) # noqa: PLW2901 - except ValueError: - pass - # Split keys into common and transaction-specific arguments. Discard the privkey. - if key in COMMONS_ARGS: - common_args[key] = value - elif key == "privkey": - pass - else: - transaction_args[key] = value - - # Must have some additional transaction arguments. - if not len(transaction_args): - error = "No transaction arguments provided." - return flask.Response(error, 400, mimetype="application/json") - - # Compose the transaction. - try: - query_data = compose_transaction( - self.db, name=query_type, params=transaction_args, **common_args - ) - except ( - script.AddressError, - exceptions.ComposeError, - exceptions.TransactionError, - exceptions.BalanceError, - ) as error: - error_msg = logging.warning( - f"{error.__class__.__name__} -- error composing {query_type} transaction via API: {error}" - ) - return flask.Response(error_msg, 400, mimetype="application/json") - else: - # Need to de-generate extra_args to pass it through. - query_args = dict([item for item in extra_args]) - operator = query_args.pop("op", "AND") - # Put the data into specific dictionary format. - data_filter = [ - {"field": key, "op": "==", "value": value} - for (key, value) in query_args.items() - ] - - # Run the query. - try: - query_data = get_rows( - self.db, table=query_type, filters=data_filter, filterop=operator - ) - except APIError as error: # noqa: F841 - return flask.Response("API Error", 400, mimetype="application/json") - - # See which encoding to choose from. - file_format = flask_request.headers["Accept"] - # JSON as default. - if file_format == "application/json" or file_format == "*/*": - response_data = json.dumps(query_data) - elif file_format == "application/xml": - # Add document root for XML. Note when xmltodict encounters a list, it produces separate tags for every item. - # Hence we end up with multiple query_type roots. To combat this we put it in a separate item dict. - response_data = serialize_to_xml({query_type: {"item": query_data}}) - else: - error = f'Invalid file format: "{file_format}".' - return flask.Response(error, 400, mimetype="application/json") +@auth.login_required +def handle_route(**kwargs): + route = ROUTES.get(str(request.url_rule.rule)) + function_args = dict(kwargs) + if "args" in route: + for arg in route["args"]: + function_args[arg[0]] = request.args.get(arg[0], arg[1]) + result = route["function"](db, **function_args) + return remove_rowids(result) - response = flask.Response(response_data, 200, mimetype=file_format) - return response - # Init the HTTP Server. - init_api_access_log(app) +def run_api_server(args): + global db # noqa: PLW0603 + # Initialise log and config + server.initialise_log_and_config(argparse.Namespace(**args)) + init_api_access_log() + # Connect to the database + db = database.get_connection(read_only=True) + # Add routes + for path in ROUTES.keys(): + app.add_url_rule(path, view_func=handle_route) + # Start the API server + app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) - # Run app server (blocking) - self.is_ready = True - app.run(host=config.RPC_HOST, port=config.RPC_PORT, threaded=True) - self.db.close() - return +def start(args): + api_process = Process(target=run_api_server, args=(vars(args),)) + api_process.start() -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 +def stop(): + if api_process and api_process.is_alive(): + api_process.terminate() diff --git a/counterparty-lib/counterpartylib/lib/restapi.py b/counterparty-lib/counterpartylib/lib/restapi.py deleted file mode 100644 index 1bde190b92..0000000000 --- a/counterparty-lib/counterpartylib/lib/restapi.py +++ /dev/null @@ -1,121 +0,0 @@ -import argparse -import logging -import multiprocessing -from logging import handlers as logging_handlers -from multiprocessing import Process - -import flask -from flask import Flask, request -from flask_httpauth import HTTPBasicAuth - -from counterpartylib import server -from counterpartylib.lib import ( - config, - database, - ledger, -) - -multiprocessing.set_start_method("spawn", force=True) - -logger = logging.getLogger(config.LOGGER_NAME) -app = Flask(__name__) -auth = HTTPBasicAuth() -db = None -api_process = None - - -ROUTES = { - "/addresses/
/balances": { - "function": ledger.get_address_balances, - }, - "/assets//": { - "function": ledger.get_asset_info, - }, - "/assets//balances": { - "function": ledger.get_asset_balances, - }, - "/assets//orders": { - "function": ledger.get_orders_by_asset, - "args": [("status", "open")], - }, - "/orders/": { - "function": ledger.get_order, - }, - "/orders//matches": { - "function": ledger.get_order_matches_by_order, - "args": [("status", "pending")], - }, -} - - -@auth.verify_password -def verify_password(username, password): - return username == config.RPC_USER and password == config.RPC_PASSWORD - - -def init_api_access_log(): - """Initialize API logger.""" - werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) - - # Disable console logging... - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - werkzeug_logger.setLevel(logging.CRITICAL) - werkzeug_logger.propagate = False - - # Log to file, if configured... - if config.API_LOG: - handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT - ) - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - handler.setLevel(logging.DEBUG) - werkzeug_logger.addHandler(handler) - - flask.cli.show_server_banner = lambda *args: None - - -def remove_rowids(query_result): - """Remove the rowid field from the query result.""" - filtered_results = [] - for row in list(query_result): - if "rowid" in row: - del row["rowid"] - if "MAX(rowid)" in row: - del row["MAX(rowid)"] - filtered_results.append(row) - return filtered_results - - -@auth.login_required -def handle_route(**kwargs): - route = ROUTES.get(str(request.url_rule.rule)) - function_args = dict(kwargs) - if "args" in route: - for arg in route["args"]: - function_args[arg[0]] = request.args.get(arg[0], arg[1]) - result = route["function"](db, **function_args) - return remove_rowids(result) - - -def run_api_server(args): - global db # noqa: PLW0603 - # Initialise log and config - server.initialise_log_and_config(argparse.Namespace(**args)) - init_api_access_log() - # Connect to the database - db = database.get_connection(read_only=True) - # Add routes - for path in ROUTES.keys(): - app.add_url_rule(path, view_func=handle_route) - # Start the API server - app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) - - -def start(args): - api_process = Process(target=run_api_server, args=(vars(args),)) - api_process.start() - - -def stop(): - if api_process and api_process.is_alive(): - api_process.terminate() diff --git a/counterparty-lib/counterpartylib/lib/v1/api.py b/counterparty-lib/counterpartylib/lib/v1/api.py new file mode 100644 index 0000000000..a90dbc8e3c --- /dev/null +++ b/counterparty-lib/counterpartylib/lib/v1/api.py @@ -0,0 +1,1449 @@ +#! /usr/bin/python3 + +""" +The database connections are read‐only, so SQL injection attacks can’t be a +problem. +""" + +import collections +import decimal +import json +import logging +import os # noqa: F401 +import re +import sys +import threading +import time +import traceback +from logging import handlers as logging_handlers + +import requests # noqa: F401 + +D = decimal.Decimal +import binascii # noqa: E402 +import inspect # noqa: E402 +import math # noqa: E402 +import struct # noqa: E402, F401 + +import apsw # noqa: E402, F401 +import flask # noqa: E402 +import jsonrpc # noqa: E402 +from counterpartylib.lib import ( # noqa: E402 + backend, + blocks, # noqa: F401 + config, + database, + exceptions, + gettxinfo, + ledger, + message_type, + script, + transaction, + util, +) +from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser # noqa: E402 +from counterpartylib.lib.messages import ( # noqa: E402 + bet, # noqa: F401 + broadcast, # noqa: F401 + btcpay, # noqa: F401 + burn, # noqa: F401 + cancel, # noqa: F401 + destroy, # noqa: F401 + dispenser, # noqa: F401 + dividend, # noqa: F401 + issuance, # noqa: F401 + order, # noqa: F401 + rps, # noqa: F401 + rpsresolve, # noqa: F401 + send, + sweep, # noqa: F401 +) +from counterpartylib.lib.messages.versions import enhanced_send # noqa: E402 +from flask import request # noqa: E402 +from flask_httpauth import HTTPBasicAuth # noqa: E402 +from jsonrpc import dispatcher # noqa: E402 +from jsonrpc.exceptions import JSONRPCDispatchException # noqa: E402 +from xmltodict import unparse as serialize_to_xml # noqa: E402 + +logger = logging.getLogger(config.LOGGER_NAME) + +API_TABLES = [ + "assets", + "balances", + "credits", + "debits", + "bets", + "bet_matches", + "broadcasts", + "btcpays", + "burns", + "cancels", + "destructions", + "dividends", + "issuances", + "orders", + "order_matches", + "sends", + "bet_expirations", + "order_expirations", + "bet_match_expirations", + "order_match_expirations", + "bet_match_resolutions", + "rps", + "rpsresolves", + "rps_matches", + "rps_expirations", + "rps_match_expirations", + "mempool", + "sweeps", + "dispensers", + "dispenses", + "transactions", +] + +VIEW_QUERIES = { + "balances": """ + SELECT *, MAX(rowid) AS rowid + FROM balances + GROUP BY address, asset + """, + "orders": """ + SELECT *, MAX(rowid) AS rowid + FROM orders + GROUP BY tx_hash + """, + "order_matches": """ + SELECT *, MAX(rowid) AS rowid + FROM order_matches + GROUP BY id + """, + "bets": """ + SELECT *, MAX(rowid) AS rowid + FROM bets + GROUP BY tx_hash + """, + "bets_matches": """ + SELECT *, MAX(rowid) AS rowid + FROM bet_matches + GROUP BY id + """, + "rps": """ + SELECT *, MAX(rowid) AS rowid + FROM rps + GROUP BY tx_hash + """, + "rps_matches": """ + SELECT *, MAX(rowid) AS rowid + FROM rps_matches + GROUP BY id + """, + "dispensers": """ + SELECT *, MAX(rowid) AS rowid + FROM dispensers + GROUP BY tx_hash + """, +} + +API_TRANSACTIONS = [ + "bet", + "broadcast", + "btcpay", + "burn", + "cancel", + "destroy", + "dividend", + "issuance", + "order", + "send", + "rps", + "rpsresolve", + "sweep", + "dispenser", +] + +COMMONS_ARGS = [ + "encoding", + "fee_per_kb", + "regular_dust_size", + "multisig_dust_size", + "op_return_value", + "pubkey", + "allow_unconfirmed_inputs", + "fee", + "fee_provided", + "estimate_fee_per_kb", + "estimate_fee_per_kb_nblocks", + "estimate_fee_per_kb_conf_target", + "estimate_fee_per_kb_mode", + "unspent_tx_hash", + "custom_inputs", + "dust_return_pubkey", + "disable_utxo_locks", + "extended_tx_info", + "p2sh_source_multisig_pubkeys", + "p2sh_source_multisig_pubkeys_required", + "p2sh_pretx_txid", +] + + +JSON_RPC_ERROR_API_COMPOSE = -32001 # code to use for error composing transaction result + +CURRENT_API_STATUS_CODE = None # is updated by the APIStatusPoller +CURRENT_API_STATUS_RESPONSE_JSON = None # is updated by the APIStatusPoller + + +class APIError(Exception): + pass + + +class BackendError(Exception): + pass + + +def check_backend_state(): + f"""Checks blocktime of last block to see if {config.BTC_NAME} Core is running behind.""" # noqa: B021 + block_count = backend.getblockcount() + block_hash = backend.getblockhash(block_count) + cblock = backend.getblock(block_hash) + time_behind = time.time() - cblock.nTime # TODO: Block times are not very reliable. + if time_behind > 60 * 60 * 2: # Two hours. + raise BackendError(f"Bitcoind is running about {round(time_behind / 3600)} hours behind.") + + # check backend index + blocks_behind = backend.getindexblocksbehind() + if blocks_behind > 5: + raise BackendError(f"Indexd is running {blocks_behind} blocks behind.") + + logger.debug("Backend state check passed.") + + +class DatabaseError(Exception): + pass + + +def check_database_state(db, blockcount): + f"""Checks {config.XCP_NAME} database to see if is caught up with backend.""" # noqa: B021 + if ledger.CURRENT_BLOCK_INDEX + 1 < blockcount: + raise DatabaseError(f"{config.XCP_NAME} database is behind backend.") + logger.debug("Database state check passed.") + return + + +# TODO: ALL queries EVERYWHERE should be done with these methods +def db_query(db, statement, bindings=(), callback=None, **callback_args): + """Allow direct access to the database in a parametrized manner.""" + cursor = db.cursor() + + # Sanitize. + forbidden_words = ["pragma", "attach", "database", "begin", "transaction"] + for word in forbidden_words: + # This will find if the forbidden word is in the statement as a whole word. For example, "transactions" will be allowed because the "s" at the end + if re.search(r"\b" + word + "\b", statement.lower()): + raise APIError(f"Forbidden word in query: '{word}'.") + + if callable(callback): + cursor.execute(statement, bindings) + for row in cursor: + callback(row, **callback_args) + results = None + else: + results = list(cursor.execute(statement, bindings)) + cursor.close() + return results + + +def get_rows( + db, + table, + filters=None, + filterop="AND", + order_by=None, + order_dir=None, + start_block=None, + end_block=None, + status=None, + limit=1000, + offset=0, + show_expired=True, +): + """SELECT * FROM wrapper. Filters results based on a filter data structure (as used by the API).""" + + if filters == None: # noqa: E711 + filters = [] + + def value_to_marker(value): + # if value is an array place holder is (?,?,?,..) + if isinstance(value, list): + return f"""({','.join(['?' for e in range(0, len(value))])})""" + else: + return """?""" + + # TODO: Document that op can be anything that SQLite3 accepts. + if not table or table.lower() not in API_TABLES: + raise APIError("Unknown table") + if filterop and filterop.upper() not in ["OR", "AND"]: + raise APIError("Invalid filter operator (OR, AND)") + if order_dir and order_dir.upper() not in ["ASC", "DESC"]: + raise APIError("Invalid order direction (ASC, DESC)") + if not isinstance(limit, int): + raise APIError("Invalid limit") + elif config.API_LIMIT_ROWS != 0 and limit > config.API_LIMIT_ROWS: + raise APIError(f"Limit should be lower or equal to {config.API_LIMIT_ROWS}") + elif config.API_LIMIT_ROWS != 0 and limit == 0: + raise APIError("Limit should be greater than 0") + if not isinstance(offset, int): + raise APIError("Invalid offset") + # TODO: accept an object: {'field1':'ASC', 'field2': 'DESC'} + if order_by and not re.compile("^[a-z0-9_]+$").match(order_by): + raise APIError("Invalid order_by, must be a field name") + + if isinstance(filters, dict): # single filter entry, convert to a one entry list + filters = [ + filters, + ] + elif not isinstance(filters, list): + filters = [] + + # TODO: Document this! (Each filter can be an ordered list.) + new_filters = [] + for filter_ in filters: + if type(filter_) in (list, tuple) and len(filter_) in [3, 4]: + new_filter = {"field": filter_[0], "op": filter_[1], "value": filter_[2]} + if len(filter_) == 4: + new_filter["case_sensitive"] = filter_[3] + new_filters.append(new_filter) + elif type(filter_) == dict: # noqa: E721 + new_filters.append(filter_) + else: + raise APIError("Unknown filter type") + filters = new_filters + + # validate filter(s) + for filter_ in filters: + for field in ["field", "op", "value"]: # should have all fields + if field not in filter_: + raise APIError(f"A specified filter is missing the '{field}' field") + if not isinstance(filter_["value"], (str, int, float, list)): + raise APIError(f"Invalid value for the field '{filter_['field']}'") + if isinstance(filter_["value"], list) and filter_["op"].upper() not in ["IN", "NOT IN"]: + raise APIError(f"Invalid value for the field '{filter_['field']}'") + if filter_["op"].upper() not in [ + "=", + "==", + "!=", + ">", + "<", + ">=", + "<=", + "IN", + "LIKE", + "NOT IN", + "NOT LIKE", + ]: + raise APIError(f"Invalid operator for the field '{filter_['field']}'") + if "case_sensitive" in filter_ and not isinstance(filter_["case_sensitive"], bool): + raise APIError("case_sensitive must be a boolean") + + # special case for memo and memo_hex field searches + if table == "sends": + adjust_get_sends_memo_filters(filters) + + # SELECT + source = VIEW_QUERIES[table] if table in VIEW_QUERIES else table + # no sql injection here + statement = f"""SELECT * FROM ({source})""" # nosec B608 # noqa: S608 + # WHERE + bindings = [] + conditions = [] + for filter_ in filters: + case_sensitive = False if "case_sensitive" not in filter_ else filter_["case_sensitive"] + if filter_["op"] == "LIKE" and case_sensitive == False: # noqa: E712 + filter_["field"] = f"""UPPER({filter_['field']})""" + filter_["value"] = filter_["value"].upper() + marker = value_to_marker(filter_["value"]) + conditions.append(f"""{filter_['field']} {filter_['op']} {marker}""") + if isinstance(filter_["value"], list): + bindings += filter_["value"] + else: + bindings.append(filter_["value"]) + # AND filters + more_conditions = [] + if table not in ["balances", "order_matches", "bet_matches"]: + if start_block != None: # noqa: E711 + more_conditions.append("""block_index >= ?""") + bindings.append(start_block) + if end_block != None: # noqa: E711 + more_conditions.append("""block_index <= ?""") + bindings.append(end_block) + elif table in ["order_matches", "bet_matches"]: + if start_block != None: # noqa: E711 + more_conditions.append("""tx0_block_index >= ?""") + bindings.append(start_block) + if end_block != None: # noqa: E711 + more_conditions.append("""tx1_block_index <= ?""") + bindings.append(end_block) + + # status + if isinstance(status, list) and len(status) > 0: + more_conditions.append(f"""status IN {value_to_marker(status)}""") + bindings += status + elif isinstance(status, str) and status != "": + more_conditions.append("""status == ?""") + bindings.append(status) + + # legacy filters + if not show_expired and table == "orders": + # Ignore BTC orders one block early. + expire_index = ledger.CURRENT_BLOCK_INDEX + 1 + more_conditions.append("""((give_asset == ? AND expire_index > ?) OR give_asset != ?)""") + bindings += [config.BTC, expire_index, config.BTC] + + if (len(conditions) + len(more_conditions)) > 0: + statement += """ WHERE""" + all_conditions = [] + if len(conditions) > 0: + all_conditions.append(f"""({f' {filterop.upper()} '.join(conditions)})""") + if len(more_conditions) > 0: + all_conditions.append(f"""({' AND '.join(more_conditions)})""") + statement += f""" {' AND '.join(all_conditions)}""" + + # ORDER BY + if order_by != None: # noqa: E711 + statement += f""" ORDER BY {order_by}""" + if order_dir != None: # noqa: E711 + statement += f""" {order_dir.upper()}""" + # LIMIT + if limit and limit > 0: + statement += f""" LIMIT {limit}""" + if offset: + statement += f""" OFFSET {offset}""" + + query_result = db_query(db, statement, tuple(bindings)) + + if table == "balances": + return adjust_get_balances_results(query_result, db) + + if table == "destructions": + return adjust_get_destructions_results(query_result) + + if table == "sends": + # for sends, handle the memo field properly + return adjust_get_sends_results(query_result) + + if table == "transactions": + # for transactions, handle the data field properly + return adjust_get_transactions_results(query_result) + + return remove_rowids(query_result) + + +def remove_rowids(query_result): + """Remove the rowid field from the query result.""" + filtered_results = [] + for row in list(query_result): + if "rowid" in row: + del row["rowid"] + if "MAX(rowid)" in row: + del row["MAX(rowid)"] + filtered_results.append(row) + + return filtered_results + + +def adjust_get_balances_results(query_result, db): + filtered_results = [] + assets = {} + for balances_row in list(query_result): + asset = balances_row["asset"] + if asset not in assets: + assets[asset] = ledger.is_divisible(db, asset) + + balances_row["divisible"] = assets[asset] + filtered_results.append(balances_row) + + return filtered_results + + +def adjust_get_destructions_results(query_result): + filtered_results = [] + for destruction_row in list(query_result): + if type(destruction_row["tag"]) == bytes: # noqa: E721 + destruction_row["tag"] = destruction_row["tag"].decode("utf-8", "ignore") + + filtered_results.append(destruction_row) + + return filtered_results + + +def adjust_get_sends_memo_filters(filters): + """Convert memo to a byte string. If memo_hex is supplied, attempt to decode it and use that instead.""" + for filter_ in filters: + if filter_["field"] == "memo": + filter_["value"] = bytes(filter_["value"], "utf-8") + if filter_["field"] == "memo_hex": + # search the indexed memo field with a byte string + filter_["field"] = "memo" + try: + filter_["value"] = bytes.fromhex(filter_["value"]) + except ValueError as e: # noqa: F841 + raise APIError("Invalid memo_hex value") # noqa: B904 + + +def adjust_get_sends_results(query_result): + """Format the memo_hex field. Try and decode the memo from a utf-8 uncoded string. Invalid utf-8 strings return an empty memo.""" + filtered_results = [] + for send_row in list(query_result): + try: + if send_row["memo"] is None: + send_row["memo_hex"] = None + send_row["memo"] = None + else: + if type(send_row["memo"]) == str: # noqa: E721 + send_row["memo"] = bytes(send_row["memo"], "utf-8") + + send_row["memo_hex"] = binascii.hexlify(send_row["memo"]).decode("utf8") + send_row["memo"] = send_row["memo"].decode("utf-8") + except UnicodeDecodeError: + send_row["memo"] = "" + filtered_results.append(send_row) + return filtered_results + + +def adjust_get_transactions_results(query_result): + """Format the data field. Try and decode the data from a utf-8 uncoded string. Invalid utf-8 strings return an empty data.""" + filtered_results = [] + for transaction_row in list(query_result): + transaction_row["data"] = transaction_row["data"].hex() + filtered_results.append(transaction_row) + return filtered_results + + +def compose_transaction( + db, + name, + params, + encoding="auto", + fee_per_kb=None, + estimate_fee_per_kb=None, + estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET, + estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE, + regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, + multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, + op_return_value=config.DEFAULT_OP_RETURN_VALUE, + pubkey=None, + allow_unconfirmed_inputs=False, + fee=None, + fee_provided=0, + unspent_tx_hash=None, + custom_inputs=None, + dust_return_pubkey=None, + disable_utxo_locks=False, + extended_tx_info=False, + p2sh_source_multisig_pubkeys=None, + p2sh_source_multisig_pubkeys_required=None, + p2sh_pretx_txid=None, + old_style_api=True, + segwit=False, +): + """Create and return a transaction.""" + + # Get provided pubkeys. + if type(pubkey) == str: # noqa: E721 + provided_pubkeys = [pubkey] + elif type(pubkey) == list: # noqa: E721 + provided_pubkeys = pubkey + elif pubkey == None: # noqa: E711 + provided_pubkeys = [] + else: + assert False # noqa: B011 + + # Get additional pubkeys from `source` and `destination` params. + # Convert `source` and `destination` to pubkeyhash form. + for address_name in ["source", "destination"]: + if address_name in params: + address = params[address_name] + if isinstance(address, list): + # pkhshs = [] + # for addr in address: + # provided_pubkeys += script.extract_pubkeys(addr) + # pkhshs.append(script.make_pubkeyhash(addr)) + # params[address_name] = pkhshs + pass + else: + provided_pubkeys += script.extract_pubkeys(address) + params[address_name] = script.make_pubkeyhash(address) + + # Check validity of collected pubkeys. + for pubkey in provided_pubkeys: + if not script.is_fully_valid(binascii.unhexlify(pubkey)): + raise script.AddressError(f"invalid public key: {pubkey}") + + compose_method = sys.modules[f"counterpartylib.lib.messages.{name}"].compose + compose_params = inspect.getfullargspec(compose_method)[0] + missing_params = [p for p in compose_params if p not in params and p != "db"] + for param in missing_params: + params[param] = None + + # dont override fee_per_kb if specified + if fee_per_kb is not None: + estimate_fee_per_kb = False + else: + fee_per_kb = config.DEFAULT_FEE_PER_KB + + if "extended_tx_info" in params: + extended_tx_info = params["extended_tx_info"] + del params["extended_tx_info"] + + if "old_style_api" in params: + old_style_api = params["old_style_api"] + del params["old_style_api"] + + if "segwit" in params: + segwit = params["segwit"] + del params["segwit"] + + tx_info = compose_method(db, **params) + return transaction.construct( + db, + tx_info, + encoding=encoding, + fee_per_kb=fee_per_kb, + estimate_fee_per_kb=estimate_fee_per_kb, + estimate_fee_per_kb_conf_target=estimate_fee_per_kb_conf_target, + regular_dust_size=regular_dust_size, + multisig_dust_size=multisig_dust_size, + op_return_value=op_return_value, + provided_pubkeys=provided_pubkeys, + allow_unconfirmed_inputs=allow_unconfirmed_inputs, + exact_fee=fee, + fee_provided=fee_provided, + unspent_tx_hash=unspent_tx_hash, + custom_inputs=custom_inputs, + dust_return_pubkey=dust_return_pubkey, + disable_utxo_locks=disable_utxo_locks, + extended_tx_info=extended_tx_info, + p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, + p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required, + p2sh_pretx_txid=p2sh_pretx_txid, + old_style_api=old_style_api, + segwit=segwit, + ) + + +def conditional_decorator(decorator, condition): + """Checks the condition and if True applies specified decorator.""" + + def gen_decorator(f): + if not condition: + return f + return decorator(f) + + return gen_decorator + + +def init_api_access_log(app): + """Initialize API logger.""" + loggers = (logging.getLogger("werkzeug"), app.logger) + + # Disable console logging... + for l in loggers: # noqa: E741 + l.setLevel(logging.CRITICAL) + l.propagate = False + + # Log to file, if configured... + if config.API_LOG: + handler = logging_handlers.RotatingFileHandler( + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT + ) + for l in loggers: # noqa: E741 + handler.setLevel(logging.DEBUG) + l.addHandler(handler) + + flask.cli.show_server_banner = lambda *args: None + + +class APIStatusPoller(threading.Thread): + """Perform regular checks on the state of the backend and the database.""" + + def __init__(self): + self.last_database_check = 0 + threading.Thread.__init__(self) + self.stop_event = threading.Event() + + def stop(self): + self.stop_event.set() + + def run(self): + logger.debug("Starting API Status Poller.") + global CURRENT_API_STATUS_CODE, CURRENT_API_STATUS_RESPONSE_JSON # noqa: PLW0603 + db = database.get_connection(read_only=True) + + while self.stop_event.is_set() != True: # noqa: E712 + try: + # Check that backend is running, communicable, and caught up with the blockchain. + # Check that the database has caught up with bitcoind. + if ( + time.time() - self.last_database_check > 10 * 60 + ): # Ten minutes since last check. + if not config.FORCE: + code = 11 + logger.debug("Checking backend state.") + check_backend_state() + code = 12 + logger.debug("Checking database state.") + check_database_state(db, backend.getblockcount()) + self.last_database_check = time.time() + except (BackendError, DatabaseError) as e: + exception_name = e.__class__.__name__ + exception_text = str(e) + logger.debug("API Status Poller: %s", exception_text) + jsonrpc_response = jsonrpc.exceptions.JSONRPCServerError( + message=exception_name, data=exception_text + ) + CURRENT_API_STATUS_CODE = code + CURRENT_API_STATUS_RESPONSE_JSON = jsonrpc_response.json.encode() + else: + CURRENT_API_STATUS_CODE = None + CURRENT_API_STATUS_RESPONSE_JSON = None + time.sleep(config.BACKEND_POLL_INTERVAL) + + +class APIServer(threading.Thread): + """Handle JSON-RPC API calls.""" + + def __init__(self, db=None): + self.db = db + self.is_ready = False + threading.Thread.__init__(self) + self.stop_event = threading.Event() + + def stop(self): + self.join() + self.stop_event.set() + + def run(self): + logger.info("Starting API Server.") + self.db = self.db or database.get_connection(read_only=True) + app = flask.Flask(__name__) + auth = HTTPBasicAuth() + + @auth.get_password + def get_pw(username): + if username == config.RPC_USER: + return config.RPC_PASSWORD + return None + + ###################### + # READ API + + # Generate dynamically get_{table} methods + def generate_get_method(table): + def get_method(**kwargs): + try: + return get_rows(self.db, table=table, **kwargs) + except TypeError as e: # TODO: generalise for all API methods + raise APIError(str(e)) # noqa: B904 + + return get_method + + for table in API_TABLES: + new_method = generate_get_method(table) + new_method.__name__ = f"get_{table}" + dispatcher.add_method(new_method) + + @dispatcher.add_method + def sql(query, bindings=None): + if bindings == None: # noqa: E711 + bindings = [] + return db_query(self.db, query, tuple(bindings)) + + ###################### + # WRITE/ACTION API + + # Generate dynamically create_{transaction} methods + def generate_create_method(tx): + def split_params(**kwargs): + transaction_args = {} + common_args = {} + private_key_wif = None + for key in kwargs: + if key in COMMONS_ARGS: + common_args[key] = kwargs[key] + elif key == "privkey": + private_key_wif = kwargs[key] + else: + transaction_args[key] = kwargs[key] + return transaction_args, common_args, private_key_wif + + def create_method(**kwargs): + try: + transaction_args, common_args, private_key_wif = split_params(**kwargs) + return compose_transaction( + self.db, name=tx, params=transaction_args, **common_args + ) + except ( + TypeError, + script.AddressError, + exceptions.ComposeError, + exceptions.TransactionError, + exceptions.BalanceError, + ) as error: + # TypeError happens when unexpected keyword arguments are passed in + error_msg = f"Error composing {tx} transaction via API: {str(error)}" + logging.warning(error_msg) + logging.warning(traceback.format_exc()) + raise JSONRPCDispatchException( # noqa: B904 + code=JSON_RPC_ERROR_API_COMPOSE, message=error_msg + ) + + return create_method + + for tx in API_TRANSACTIONS: + create_method = generate_create_method(tx) + create_method.__name__ = f"create_{tx}" + dispatcher.add_method(create_method) + + @dispatcher.add_method + def get_messages(block_index): + if not isinstance(block_index, int): + raise APIError("block_index must be an integer.") + + messages = ledger.get_messages(self.db, block_index=block_index) + return messages + + @dispatcher.add_method + def get_messages_by_index(message_indexes): + """Get specific messages from the feed, based on the message_index. + + @param message_index: A single index, or a list of one or more message indexes to retrieve. + """ + if not isinstance(message_indexes, list): + message_indexes = [ + message_indexes, + ] + for idx in message_indexes: # make sure the data is clean + if not isinstance(idx, int): + raise APIError("All items in message_indexes are not integers") + + messages = ledger.get_messages(self.db, message_index_in=message_indexes) + return messages + + @dispatcher.add_method + def get_supply(asset): + if asset == "BTC": + return backend.get_btc_supply(normalize=False) + elif asset == "XCP": + return ledger.xcp_supply(self.db) + else: + asset = ledger.resolve_subasset_longname(self.db, asset) + return ledger.asset_supply(self.db, asset) + + @dispatcher.add_method + def get_xcp_supply(): + logger.warning("Deprecated method: `get_xcp_supply`") + return ledger.xcp_supply(self.db) + + @dispatcher.add_method + def get_asset_info(assets=None, asset=None): + if asset is not None: + assets = [asset] + + if not isinstance(assets, list): + raise APIError( + "assets must be a list of asset names, even if it just contains one entry" + ) + assets_info = [] + for asset in assets: + asset = ledger.resolve_subasset_longname(self.db, asset) # noqa: PLW2901 + + # BTC and XCP. + if asset in [config.BTC, config.XCP]: + if asset == config.BTC: + supply = backend.get_btc_supply(normalize=False) + else: + supply = ledger.xcp_supply(self.db) + + assets_info.append( + { + "asset": asset, + "asset_longname": None, + "owner": None, + "divisible": True, + "locked": False, + "supply": supply, + "description": "", + "issuer": None, + } + ) + continue + + # User‐created asset. + cursor = self.db.cursor() + issuances = ledger.get_issuances(self.db, asset=asset, status="valid", first=True) + cursor.close() + if not issuances: + continue # asset not found, most likely + else: + last_issuance = issuances[-1] + locked = False + for e in issuances: + if e["locked"]: + locked = True + assets_info.append( + { + "asset": asset, + "asset_longname": last_issuance["asset_longname"], + "owner": last_issuance["issuer"], + "divisible": bool(last_issuance["divisible"]), + "locked": locked, + "supply": ledger.asset_supply(self.db, asset), + "description": last_issuance["description"], + "issuer": last_issuance["issuer"], + } + ) + return assets_info + + @dispatcher.add_method + def get_block_info(block_index): + assert isinstance(block_index, int) + cursor = self.db.cursor() + cursor.execute("""SELECT * FROM blocks WHERE block_index = ?""", (block_index,)) + blocks = list(cursor) # noqa: F811 + if len(blocks) == 1: + block = blocks[0] + elif len(blocks) == 0: + raise exceptions.DatabaseError("No blocks found.") + else: + assert False # noqa: B011 + cursor.close() + return block + + @dispatcher.add_method + def fee_per_kb(conf_target=config.ESTIMATE_FEE_CONF_TARGET, mode=config.ESTIMATE_FEE_MODE): + return backend.fee_per_kb(conf_target, mode) + + @dispatcher.add_method + def get_blocks(block_indexes, min_message_index=None): + """fetches block info and messages for the specified block indexes + @param min_message_index: Retrieve blocks from the message feed on or after this specific message index + (useful since blocks may appear in the message feed more than once, if a reorg occurred). Note that + if this parameter is not specified, the messages for the first block will be returned. + """ + if not isinstance(block_indexes, (list, tuple)): + raise APIError("block_indexes must be a list of integers.") + if len(block_indexes) >= 250: + raise APIError("can only specify up to 250 indexes at a time.") + for block_index in block_indexes: + if not isinstance(block_index, int): + raise APIError("block_indexes must be a list of integers.") + + cursor = self.db.cursor() + + block_indexes_placeholder = f"({','.join(['?'] * len(block_indexes))})" + # no sql injection here + cursor.execute( + f"SELECT * FROM blocks WHERE block_index IN ({block_indexes_placeholder}) ORDER BY block_index ASC", # nosec B608 # noqa: S608 + block_indexes, + ) + blocks = cursor.fetchall() # noqa: F811 + + messages = collections.deque(ledger.get_messages(self.db, block_index_in=block_indexes)) + + # Discard any messages less than min_message_index + if min_message_index: + while len(messages) and messages[0]["message_index"] < min_message_index: + messages.popleft() + + # Packages messages into their appropriate block in the data structure to be returned + for block in blocks: + block["_messages"] = [] + while len(messages) and messages[0]["block_index"] == block["block_index"]: + block["_messages"].append(messages.popleft()) + # NOTE: if len(messages), then we're only returning the messages for the first set of blocks before the reorg + + cursor.close() + return blocks + + @dispatcher.add_method + def get_running_info(): + latest_block_index = backend.getblockcount() + + try: + check_database_state(self.db, latest_block_index) + except DatabaseError: + caught_up = False + else: + caught_up = True + + try: + cursor = self.db.cursor() + blocks = list( + cursor.execute( + """SELECT * FROM blocks WHERE block_index = ?""", + (ledger.CURRENT_BLOCK_INDEX,), + ) + ) + assert len(blocks) == 1 + last_block = blocks[0] + cursor.close() + except: # noqa: E722 + last_block = None + + try: + last_message = ledger.last_message(self.db) + except: # noqa: E722 + last_message = None + + try: + indexd_blocks_behind = backend.getindexblocksbehind() + except: # noqa: E722 + indexd_blocks_behind = latest_block_index if latest_block_index > 0 else 999999 + indexd_caught_up = indexd_blocks_behind <= 1 + + server_ready = caught_up and indexd_caught_up + + return { + "server_ready": server_ready, + "db_caught_up": caught_up, + "bitcoin_block_count": latest_block_index, + "last_block": last_block, + "indexd_caught_up": indexd_caught_up, + "indexd_blocks_behind": indexd_blocks_behind, + "last_message_index": last_message["message_index"] if last_message else -1, + "api_limit_rows": config.API_LIMIT_ROWS, + "running_testnet": config.TESTNET, + "running_regtest": config.REGTEST, + "running_testcoin": config.TESTCOIN, + "version_major": config.VERSION_MAJOR, + "version_minor": config.VERSION_MINOR, + "version_revision": config.VERSION_REVISION, + } + + @dispatcher.add_method + def get_element_counts(): + counts = {} + cursor = self.db.cursor() + for element in [ + "transactions", + "blocks", + "debits", + "credits", + "balances", + "sends", + "orders", + "order_matches", + "btcpays", + "issuances", + "broadcasts", + "bets", + "bet_matches", + "dividends", + "burns", + "cancels", + "order_expirations", + "bet_expirations", + "order_match_expirations", + "bet_match_expirations", + "messages", + "destructions", + ]: + # no sql injection here, element is hardcoded + cursor.execute(f"SELECT COUNT(*) AS count FROM {element}") # nosec B608 # noqa: S608 + count_list = cursor.fetchall() + assert len(count_list) == 1 + counts[element] = count_list[0]["count"] + cursor.close() + return counts + + @dispatcher.add_method + def get_asset_names(longnames=False): + all_assets = ledger.get_valid_assets(self.db) + if longnames: + names = [ + {"asset": row["asset"], "asset_longname": row["asset_longname"]} + for row in all_assets + ] + else: + names = [row["asset"] for row in all_assets] + return names + + @dispatcher.add_method + def get_asset_longnames(): + return get_asset_names(longnames=True) + + @dispatcher.add_method + def get_holder_count(asset): + asset = ledger.resolve_subasset_longname(self.db, asset) + holders = ledger.holders(self.db, asset, True) + addresses = [] + for holder in holders: + addresses.append(holder["address"]) + return {asset: len(set(addresses))} + + @dispatcher.add_method + def get_holders(asset): + asset = ledger.resolve_subasset_longname(self.db, asset) + holders = ledger.holders(self.db, asset, True) + return holders + + @dispatcher.add_method + def search_raw_transactions(address, unconfirmed=True, only_tx_hashes=False): + return backend.search_raw_transactions( + address, unconfirmed=unconfirmed, only_tx_hashes=only_tx_hashes + ) + + @dispatcher.add_method + def get_oldest_tx(address): + return backend.get_oldest_tx(address) + + @dispatcher.add_method + def get_unspent_txouts(address, unconfirmed=False, unspent_tx_hash=None, order_by=None): + results = backend.get_unspent_txouts( + address, unconfirmed=unconfirmed, unspent_tx_hash=unspent_tx_hash + ) + if order_by is None: + return results + else: + order_key = order_by + reverse = False + if order_key.startswith("-"): + order_key = order_key[1:] + reverse = True + return sorted(results, key=lambda x: x[order_key], reverse=reverse) + + @dispatcher.add_method + def getrawtransaction(tx_hash, verbose=False, skip_missing=False): + return backend.getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing) + + @dispatcher.add_method + def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False): + return backend.getrawtransaction_batch( + txhash_list, verbose=verbose, skip_missing=skip_missing + ) + + @dispatcher.add_method + def get_tx_info(tx_hex, block_index=None): + # block_index mandatory for transactions before block 335000 + source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( + self.db, BlockchainParser().deserialize_tx(tx_hex), block_index=block_index + ) + return source, destination, btc_amount, fee, util.hexlify(data) if data else "" + + @dispatcher.add_method + def unpack(data_hex): + data = binascii.unhexlify(data_hex) + message_type_id, message = message_type.unpack(data) + + # TODO: Enabled only for `send`. + if message_type_id == send.ID: + unpack_method = send.unpack + elif message_type_id == enhanced_send.ID: + unpack_method = enhanced_send.unpack + else: + raise APIError("unsupported message type") + unpacked = unpack_method(self.db, message, ledger.CURRENT_BLOCK_INDEX) + return message_type_id, unpacked + + @dispatcher.add_method + # TODO: Rename this method. + def search_pubkey(pubkeyhash, provided_pubkeys=None): + return backend.pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=provided_pubkeys) + + @dispatcher.add_method + def get_dispenser_info(tx_hash=None, tx_index=None): + cursor = self.db.cursor() # noqa: F841 + + if tx_hash is None and tx_index is None: + raise APIError("You must provided a tx hash or a tx index") + + dispensers = [] + if tx_hash is not None: + dispensers = get_dispenser_info(self.db, tx_hash=tx_hash) + else: + dispensers = get_dispenser_info(self.db, tx_index=tx_index) + + if len(dispensers) == 1: + dispenser = dispensers[0] + oracle_price = "" + satoshi_price = "" + fiat_price = "" + oracle_price_last_updated = "" + oracle_fiat_label = "" + + if dispenser["oracle_address"] != None: # noqa: E711 + fiat_price = util.satoshirate_to_fiat(dispenser["satoshirate"]) + oracle_price, oracle_fee, oracle_fiat_label, oracle_price_last_updated = ( + ledger.get_oracle_last_price( + self.db, dispenser["oracle_address"], ledger.CURRENT_BLOCK_INDEX + ) + ) + + if oracle_price > 0: + satoshi_price = math.ceil((fiat_price / oracle_price) * config.UNIT) + else: + raise APIError("Last oracle price is zero") + + return { + "tx_index": dispenser["tx_index"], + "tx_hash": dispenser["tx_hash"], + "block_index": dispenser["block_index"], + "source": dispenser["source"], + "asset": dispenser["asset"], + "give_quantity": dispenser["give_quantity"], + "escrow_quantity": dispenser["escrow_quantity"], + "mainchainrate": dispenser["satoshirate"], + "fiat_price": fiat_price, + "fiat_unit": oracle_fiat_label, + "oracle_price": oracle_price, + "satoshi_price": satoshi_price, + "status": dispenser["status"], + "give_remaining": dispenser["give_remaining"], + "oracle_address": dispenser["oracle_address"], + "oracle_price_last_updated": oracle_price_last_updated, + "asset_longname": dispenser["asset_longname"], + } + + return {} + + def _set_cors_headers(response): + if not config.RPC_NO_ALLOW_CORS: + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" + response.headers["Access-Control-Allow-Headers"] = ( + "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" + ) + + ##### REST ROUTES ##### + + @app.route("/healthz", methods=["GET"]) + def handle_healthz(): + msg, code = "Healthy", 200 + + type_ = request.args.get("type", "heavy") + + try: + if type_ == "light": + logger.debug("Performing light healthz check.") + latest_block_index = backend.getblockcount() + check_database_state(self.db, latest_block_index) + else: + logger.debug("Performing heavy healthz check.") + compose_transaction( + self.db, + name="send", + params={ + "source": config.UNSPENDABLE, + "destination": config.UNSPENDABLE, + "asset": config.XCP, + "quantity": 100000000, + }, + allow_unconfirmed_inputs=True, + fee=1000, + ) + except Exception: + msg, code = "Unhealthy", 503 + + return flask.Response(msg, code, mimetype="application/json") + + @app.route("/", defaults={"args_path": ""}, methods=["GET", "POST", "OPTIONS"]) + @app.route("/", methods=["GET", "POST", "OPTIONS"]) + # Only require authentication if RPC_PASSWORD is set. + @conditional_decorator(auth.login_required, hasattr(config, "RPC_PASSWORD")) + def handle_root(args_path): + """Handle all paths, decide where to forward the query.""" + if ( + args_path == "" + or args_path.startswith("api/") + or args_path.startswith("API/") + or args_path.startswith("rpc/") + or args_path.startswith("RPC/") + ): + if flask.request.method == "POST": + # Need to get those here because it might not be available in this aux function. + request_json = flask.request.get_data().decode("utf-8") + response = handle_rpc_post(request_json) + return response + elif flask.request.method == "OPTIONS": + response = handle_rpc_options() + return response + else: + error = "Invalid method." + return flask.Response(error, 405, mimetype="application/json") + elif args_path.startswith("rest/") or args_path.startswith("REST/"): + if flask.request.method == "GET" or flask.request.method == "POST": + # Pass the URL path without /REST/ part and Flask request object. + rest_path = args_path.split("/", 1)[1] + response = handle_rest(rest_path, flask.request) + return response + else: + error = "Invalid method." + return flask.Response(error, 405, mimetype="application/json") + else: + # Not found + return flask.Response(None, 404, mimetype="application/json") + + ###################### + # JSON-RPC API + ###################### + def handle_rpc_options(): + response = flask.Response("", 204) + _set_cors_headers(response) + return response + + def handle_rpc_post(request_json): + """Handle /API/ POST route. Call relevant get_rows/create_transaction wrapper.""" + # Check for valid request format. + try: + request_data = json.loads(request_json) + assert ( + "id" in request_data + and request_data["jsonrpc"] == "2.0" + and request_data["method"] + ) + # params may be omitted + except: # noqa: E722 + obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( + data="Invalid JSON-RPC 2.0 request format" + ) + return flask.Response(obj_error.json.encode(), 400, mimetype="application/json") + + # Only arguments passed as a `dict` are supported. + if request_data.get("params", None) and not isinstance(request_data["params"], dict): + obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( + data="Arguments must be passed as a JSON object (list of unnamed arguments not supported)" + ) + return flask.Response(obj_error.json.encode(), 400, mimetype="application/json") + + # Return an error if the API Status Poller checks fail. + if not config.FORCE and CURRENT_API_STATUS_CODE: + return flask.Response( + CURRENT_API_STATUS_RESPONSE_JSON, 503, mimetype="application/json" + ) + + # Answer request normally. + # NOTE: `UnboundLocalError: local variable 'output' referenced before assignment` means the method doesn’t return anything. + jsonrpc_response = jsonrpc.JSONRPCResponseManager.handle(request_json, dispatcher) + response = flask.Response( + jsonrpc_response.json.encode(), 200, mimetype="application/json" + ) + _set_cors_headers(response) + return response + + ###################### + # HTTP REST API + ###################### + def handle_rest(path_args, flask_request): + """Handle /REST/ route. Query the database using get_rows or create transaction using compose_transaction.""" + url_action = flask_request.path.split("/")[-1] + if url_action == "compose": + compose = True + elif url_action == "get": + compose = False + else: + error = f'Invalid action "{url_action}".' + return flask.Response(error, 400, mimetype="application/json") + + # Get all arguments passed via URL. + url_args = path_args.split("/") + try: + query_type = url_args.pop(0).lower() + except IndexError: + error = "No query_type provided." + return flask.Response(error, 400, mimetype="application/json") + # Check if message type or table name are valid. + if (compose and query_type not in API_TRANSACTIONS) or ( + not compose and query_type not in API_TABLES + ): + error = f'No such query type in supported queries: "{query_type}".' + return flask.Response(error, 400, mimetype="application/json") + + # Parse the additional arguments. + extra_args = flask_request.args.items() + query_data = {} + + if compose: + common_args = {} + transaction_args = {} + for key, value in extra_args: + # Determine value type. + try: + value = int(value) # noqa: PLW2901 + except ValueError: + try: + value = float(value) # noqa: PLW2901 + except ValueError: + pass + # Split keys into common and transaction-specific arguments. Discard the privkey. + if key in COMMONS_ARGS: + common_args[key] = value + elif key == "privkey": + pass + else: + transaction_args[key] = value + + # Must have some additional transaction arguments. + if not len(transaction_args): + error = "No transaction arguments provided." + return flask.Response(error, 400, mimetype="application/json") + + # Compose the transaction. + try: + query_data = compose_transaction( + self.db, name=query_type, params=transaction_args, **common_args + ) + except ( + script.AddressError, + exceptions.ComposeError, + exceptions.TransactionError, + exceptions.BalanceError, + ) as error: + error_msg = logging.warning( + f"{error.__class__.__name__} -- error composing {query_type} transaction via API: {error}" + ) + return flask.Response(error_msg, 400, mimetype="application/json") + else: + # Need to de-generate extra_args to pass it through. + query_args = dict([item for item in extra_args]) + operator = query_args.pop("op", "AND") + # Put the data into specific dictionary format. + data_filter = [ + {"field": key, "op": "==", "value": value} + for (key, value) in query_args.items() + ] + + # Run the query. + try: + query_data = get_rows( + self.db, table=query_type, filters=data_filter, filterop=operator + ) + except APIError as error: # noqa: F841 + return flask.Response("API Error", 400, mimetype="application/json") + + # See which encoding to choose from. + file_format = flask_request.headers["Accept"] + # JSON as default. + if file_format == "application/json" or file_format == "*/*": + response_data = json.dumps(query_data) + elif file_format == "application/xml": + # Add document root for XML. Note when xmltodict encounters a list, it produces separate tags for every item. + # Hence we end up with multiple query_type roots. To combat this we put it in a separate item dict. + response_data = serialize_to_xml({query_type: {"item": query_data}}) + else: + error = f'Invalid file format: "{file_format}".' + return flask.Response(error, 400, mimetype="application/json") + + response = flask.Response(response_data, 200, mimetype=file_format) + return response + + # Init the HTTP Server. + init_api_access_log(app) + + # Run app server (blocking) + self.is_ready = True + app.run(host=config.RPC_HOST, port=config.RPC_PORT, threaded=True) + + self.db.close() + return + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 6e5891f865..8c08fb3f3d 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -29,11 +29,11 @@ database, ledger, log, - restapi, transaction, util, ) from counterpartylib.lib import kickstart as kickstarter +from counterpartylib.lib.v1 import api as api_v1 logger = logging.getLogger(config.LOGGER_NAME) D = decimal.Decimal @@ -58,7 +58,7 @@ def sigterm_handler(_signo, _stack_frame): assert False # noqa: B011 logger.info(f"Received {signal_name}.") - restapi.stop() + api.stop() if "api_server" in globals(): logger.info("Stopping API server.") @@ -668,17 +668,17 @@ def start_all(args): if args.legacy_api: # API Status Poller. - api_status_poller = api.APIStatusPoller() + api_status_poller = api_v1.APIStatusPoller() api_status_poller.daemon = True api_status_poller.start() # API Server. - api_server = api.APIServer() + api_server = api_v1.APIServer() api_server.daemon = True api_server.start() else: # REST API Server. - restapi.start(args) + api.start(args) # Server diff --git a/counterparty-lib/counterpartylib/test/bytespersigop_test.py b/counterparty-lib/counterpartylib/test/bytespersigop_test.py index 19fd451747..8bc6ffd727 100644 --- a/counterparty-lib/counterpartylib/test/bytespersigop_test.py +++ b/counterparty-lib/counterpartylib/test/bytespersigop_test.py @@ -1,19 +1,14 @@ import binascii -import pprint # noqa: F401 import tempfile import bitcoin as bitcoinlib -import pytest # noqa: F401 -from counterpartylib.lib import api, blocks, exceptions, ledger, transaction, util # noqa: F401 -from counterpartylib.test import ( - conftest, # noqa: F401 - util_test, -) +from counterpartylib.lib import ledger +from counterpartylib.lib.v1 import api +from counterpartylib.test import util_test from counterpartylib.test.fixtures.params import ADDR # this is require near the top to do setup of the test suite -from counterpartylib.test.fixtures.params import DEFAULT_PARAMS as DP # noqa: F401 from counterpartylib.test.util_test import CURR_DIR FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" diff --git a/counterparty-lib/counterpartylib/test/complex_unit_test.py b/counterparty-lib/counterpartylib/test/complex_unit_test.py index 3f8603dfe6..8ae48a90fa 100644 --- a/counterparty-lib/counterpartylib/test/complex_unit_test.py +++ b/counterparty-lib/counterpartylib/test/complex_unit_test.py @@ -1,12 +1,11 @@ -import json import pprint # noqa: F401 import tempfile import pytest -import requests from apsw import ConstraintError -from counterpartylib.lib import api, blocks, config, ledger, util +from counterpartylib.lib import blocks, ledger, util +from counterpartylib.lib.v1 import api # this is require near the top to do setup of the test suite from counterpartylib.test import ( @@ -331,6 +330,7 @@ def test_updated_tables_endpoints(): } +""" @pytest.mark.usefixtures("api_server") def test_new_get_balances_by_address(): alice = ADDR[0] @@ -570,3 +570,4 @@ def test_messages_table(server_db): for row in result: bindings = json.loads(row["bindings"]) assert isinstance(bindings, dict) + """ diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index a0ac913b92..a17fb404e2 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -18,7 +18,8 @@ from pycoin.coins.bitcoin import Tx # noqa: F401 from counterpartylib import server -from counterpartylib.lib import api, arc4, config, database, ledger, log, script, util +from counterpartylib.lib import arc4, config, database, ledger, log, script, util +from counterpartylib.lib.v1 import api from counterpartylib.test import util_test from counterpartylib.test.fixtures.params import DEFAULT_PARAMS from counterpartylib.test.fixtures.scenarios import INTEGRATION_SCENARIOS diff --git a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py index 12d8a1036b..3ec95ca4fb 100644 --- a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py +++ b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py @@ -4,7 +4,8 @@ import bitcoin as bitcoinlib -from counterpartylib.lib import api, backend, transaction +from counterpartylib.lib import (backend, transaction) +from counterpartylib.lib.v1 import api from counterpartylib.test import ( util_test, ) diff --git a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py index 35ded5283b..6aa9e31eef 100644 --- a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py +++ b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py @@ -1,26 +1,13 @@ import binascii import hashlib import logging -import math # noqa: F401 -import pprint # noqa: F401 import tempfile import time import bitcoin as bitcoinlib import pytest -# this is require near the top to do setup of the test suite -from counterpartylib.test import ( - conftest, # noqa: F401 - util_test, -) -from counterpartylib.test.fixtures.params import ADDR, DP, P2SH_ADDR -from counterpartylib.test.util_test import CURR_DIR - -logger = logging.getLogger(__name__) - from counterpartylib.lib import ( # noqa: E402 - api, backend, config, exceptions, @@ -31,6 +18,17 @@ ) from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser # noqa: E402 from counterpartylib.lib.transaction_helper import p2sh_encoding, serializer # noqa: E402, F401 +from counterpartylib.lib.v1 import api + +# this is require near the top to do setup of the test suite +from counterpartylib.test import ( + conftest, # noqa: F401 + util_test, +) +from counterpartylib.test.fixtures.params import ADDR, DP, P2SH_ADDR +from counterpartylib.test.util_test import CURR_DIR + +logger = logging.getLogger(__name__) FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" FIXTURE_DB = tempfile.gettempdir() + "/fixtures.unittest_fixture.db" diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index d0ad1b3fcc..25825a34cd 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -803,7 +803,10 @@ def check_outputs( try: tested_module = sys.modules[f"counterpartylib.lib.{tx_name}"] except KeyError: # TODO: hack - tested_module = sys.modules[f"counterpartylib.lib.messages.{tx_name}"] + if tx_name == "api": + tested_module = sys.modules["counterpartylib.lib.v1.api"] + else: + tested_module = sys.modules[f"counterpartylib.lib.messages.{tx_name}"] tested_method = getattr(tested_module, method) with MockProtocolChangesContext(**(mock_protocol_changes or {})): From c1bf32bebb0b3b565322a3c6d55349b5f3625ea0 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 17:27:25 +0200 Subject: [PATCH 006/128] --legacy-api -> --enable-v1-api --- counterparty-core/counterpartycore/server.py | 2 +- counterparty-lib/counterpartylib/server.py | 2 +- .../counterpartylib/test/p2sh_encoding_test.py | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 1f529504b0..5602c997cb 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -228,7 +228,7 @@ }, ], [ - ("--legacy-api",), + ("--enable-v1-api",), {"action": "store_true", "default": False, "help": "Use legacy API (Deprecated)"}, ], ] diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 8c08fb3f3d..84e531d7e7 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -666,7 +666,7 @@ def start_all(args): # initilise_config transaction.initialise() - if args.legacy_api: + if args.enable_v1_api: # API Status Poller. api_status_poller = api_v1.APIStatusPoller() api_status_poller.daemon = True diff --git a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py index 6aa9e31eef..78267f404a 100644 --- a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py +++ b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py @@ -7,17 +7,16 @@ import bitcoin as bitcoinlib import pytest -from counterpartylib.lib import ( # noqa: E402 +from counterpartylib.lib import ( backend, config, exceptions, gettxinfo, ledger, script, - util, # noqa: F401 ) -from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser # noqa: E402 -from counterpartylib.lib.transaction_helper import p2sh_encoding, serializer # noqa: E402, F401 +from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser +from counterpartylib.lib.transaction_helper import p2sh_encoding from counterpartylib.lib.v1 import api # this is require near the top to do setup of the test suite From ad47e52ac8ba84a1dedd457f6857edbcd2c6aa8a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 17:32:46 +0200 Subject: [PATCH 007/128] lint --- .../counterpartylib/lib/ledger.py | 7 +- counterparty-lib/counterpartylib/server.py | 80 +++++++++---------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 82c0ea3979..e419b3f48e 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -626,11 +626,12 @@ def get_asset_info(db, asset): if asset_name == config.BTC: asset_info["supply"] = backend.get_btc_supply(normalize=False) return asset_info - elif asset_name == config.XCP: + + if asset_name == config.XCP: asset_info["supply"] = xcp_supply(db) return asset_info - else: - asset_info["supply"] = asset_supply(db, asset_name) + + asset_info["supply"] = asset_supply(db, asset_name) cursor = db.cursor() query = """ diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 84e531d7e7..14ff81e7a3 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -549,46 +549,46 @@ def initialise_config( def initialise_log_and_config(args): - config_args = dict( - database_file=args.database_file, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - customnet=args.customnet, - api_limit_rows=args.api_limit_rows, - backend_connect=args.backend_connect, - backend_port=args.backend_port, - backend_user=args.backend_user, - backend_password=args.backend_password, - backend_ssl=args.backend_ssl, - backend_ssl_no_verify=args.backend_ssl_no_verify, - backend_poll_interval=args.backend_poll_interval, - indexd_connect=args.indexd_connect, - indexd_port=args.indexd_port, - rpc_host=args.rpc_host, - rpc_port=args.rpc_port, - rpc_user=args.rpc_user, - rpc_password=args.rpc_password, - rpc_no_allow_cors=args.rpc_no_allow_cors, - requests_timeout=args.requests_timeout, - rpc_batch_size=args.rpc_batch_size, - check_asset_conservation=not args.no_check_asset_conservation, - force=args.force, - p2sh_dust_return_pubkey=args.p2sh_dust_return_pubkey, - utxo_locks_max_addresses=args.utxo_locks_max_addresses, - utxo_locks_max_age=args.utxo_locks_max_age, - ) - log_args = dict( - verbose=args.verbose, - quiet=args.quiet, - log_file=args.log_file, - api_log_file=args.api_log_file, - no_log_files=args.no_log_files, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - json_log=args.json_log, - ) + config_args = { + "database_file": args.database_file, + "testnet": args.testnet, + "testcoin": args.testcoin, + "regtest": args.regtest, + "customnet": args.customnet, + "api_limit_rows": args.api_limit_rows, + "backend_connect": args.backend_connect, + "backend_port": args.backend_port, + "backend_user": args.backend_user, + "backend_password": args.backend_password, + "backend_ssl": args.backend_ssl, + "backend_ssl_no_verify": args.backend_ssl_no_verify, + "backend_poll_interval": args.backend_poll_interval, + "indexd_connect": args.indexd_connect, + "indexd_port": args.indexd_port, + "rpc_host": args.rpc_host, + "rpc_port": args.rpc_port, + "rpc_user": args.rpc_user, + "rpc_password": args.rpc_password, + "rpc_no_allow_cors": args.rpc_no_allow_cors, + "requests_timeout": args.requests_timeout, + "rpc_batch_size": args.rpc_batch_size, + "check_asset_conservation": not args.no_check_asset_conservation, + "force": args.force, + "p2sh_dust_return_pubkey": args.p2sh_dust_return_pubkey, + "utxo_locks_max_addresses": args.utxo_locks_max_addresses, + "utxo_locks_max_age": args.utxo_locks_max_age, + } + log_args = { + "verbose": args.verbose, + "quiet": args.quiet, + "log_file": args.log_file, + "api_log_file": args.api_log_file, + "no_log_files": args.no_log_files, + "testnet": args.testnet, + "testcoin": args.testcoin, + "regtest": args.regtest, + "json_log": args.json_log, + } # initialise log config initialise_log_config(**log_args) # set up logging From 3d434cf3a29af30727daf3ea6630bc36df598151 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 18:02:37 +0200 Subject: [PATCH 008/128] Add routes for blocks --- counterparty-lib/counterpartylib/lib/api.py | 13 +++++ .../counterpartylib/lib/ledger.py | 48 +++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 1bde190b92..6b6364d29a 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -25,6 +25,19 @@ ROUTES = { + "/blocks": { + "function": ledger.get_blocks, + "args": [("last", None), ("limit", 10)], + }, + "/blocks/": { + "function": ledger.get_block, + }, + "/blocks//transactions": { + "function": ledger.get_transactions_by_block, + }, + "/blocks//events": { + "function": ledger.get_messages, + }, "/addresses/
/balances": { "function": ledger.get_address_balances, }, diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index e419b3f48e..1983c59b31 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -772,9 +772,44 @@ def get_burns(db, status=None, source=None): return cursor.fetchall() -########################### -# TRANSACTIONS # -########################### +###################################### +# BLOCKS AND TRANSACTIONS # +###################################### + + +def get_blocks(db, last=None, limit=10): + cursor = db.cursor() + bindings = [] + query = """ + SELECT * FROM blocks WHERE + ORDER BY block_index DESC + """ + if last is not None: + query += "WHERE BLOCK_INDEX <= ?" + bindings.append(last) + query += "LIMIT ?" + bindings.append(limit) + cursor.execute(query, tuple(bindings)) + return cursor.fetchall() + + +def get_block(db, block_index): + blocks = get_blocks(db, last=block_index, limit=1) + if blocks: + return blocks[0] + return None + + +def get_transactions_by_block(db, block_index): + cursor = db.cursor() + query = """ + SELECT * FROM transactions + WHERE block_index = ? + ORDER BY tx_index ASC + """ + bindings = (block_index,) + cursor.execute(query, bindings) + return cursor.fetchall() def get_vouts(db, tx_hash): @@ -804,6 +839,13 @@ def get_transactions(db, tx_hash=None): return cursor.fetchall() +def get_transaction(db, tx_hash): + transactions = get_transactions(db, tx_hash) + if transactions: + return transactions[0] + return None + + def get_transaction_source(db, tx_hash): cursor = db.cursor() query = """SELECT source FROM transactions WHERE tx_hash = ?""" From a222bb8a285698b70a94387394c78238fe2aeab6 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 18:10:49 +0200 Subject: [PATCH 009/128] fix tests --- counterparty-lib/counterpartylib/test/fixtures/vectors.py | 7 +++---- counterparty-lib/counterpartylib/test/util_test.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/counterparty-lib/counterpartylib/test/fixtures/vectors.py b/counterparty-lib/counterpartylib/test/fixtures/vectors.py index 3fbca51dc9..e69720ee46 100644 --- a/counterparty-lib/counterpartylib/test/fixtures/vectors.py +++ b/counterparty-lib/counterpartylib/test/fixtures/vectors.py @@ -9,17 +9,16 @@ """ import binascii -import json # noqa: F401 from fractions import Fraction import bitcoin as bitcoinlib -from counterpartylib.lib import address, config, exceptions, script # noqa: F401 -from counterpartylib.lib.api import APIError +from counterpartylib.lib import config, exceptions, script from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser from counterpartylib.lib.ledger import CreditError, DebitError from counterpartylib.lib.messages import issuance from counterpartylib.lib.util import QuantityError, RPCError +from counterpartylib.lib.v1.api import APIError from .params import ( ADDR, @@ -7696,7 +7695,7 @@ }, ], }, - "api": { + "api_v1": { "get_rows": [ { "in": ("balances", None, "AND", None, None, None, None, None, 1000, 0, True), diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index 25825a34cd..2e29881499 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -803,7 +803,7 @@ def check_outputs( try: tested_module = sys.modules[f"counterpartylib.lib.{tx_name}"] except KeyError: # TODO: hack - if tx_name == "api": + if tx_name == "api_v1": tested_module = sys.modules["counterpartylib.lib.v1.api"] else: tested_module = sys.modules[f"counterpartylib.lib.messages.{tx_name}"] From a0d07f4f753893208c31090823b4521f733318c0 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 19:47:22 +0200 Subject: [PATCH 010/128] Add tests for new API --- counterparty-lib/counterpartylib/lib/api.py | 27 +- .../lib/backend/addrindexrs.py | 2 +- .../counterpartylib/lib/ledger.py | 4 + .../counterpartylib/test/api_v2_test.py | 249 ++++++++++++++++++ .../counterpartylib/test/complex_unit_test.py | 237 +---------------- .../counterpartylib/test/conftest.py | 62 ++++- 6 files changed, 335 insertions(+), 246 deletions(-) create mode 100644 counterparty-lib/counterpartylib/test/api_v2_test.py diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 6b6364d29a..8b48cfc05d 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -10,6 +10,7 @@ from counterpartylib import server from counterpartylib.lib import ( + blocks, config, database, ledger, @@ -41,7 +42,7 @@ "/addresses/
/balances": { "function": ledger.get_address_balances, }, - "/assets//": { + "/assets/": { "function": ledger.get_asset_info, }, "/assets//balances": { @@ -89,13 +90,20 @@ def init_api_access_log(): def remove_rowids(query_result): """Remove the rowid field from the query result.""" - filtered_results = [] - for row in list(query_result): - if "rowid" in row: - del row["rowid"] - if "MAX(rowid)" in row: - del row["MAX(rowid)"] - filtered_results.append(row) + if isinstance(query_result, list): + filtered_results = [] + for row in list(query_result): + if "rowid" in row: + del row["rowid"] + if "MAX(rowid)" in row: + del row["MAX(rowid)"] + filtered_results.append(row) + return filtered_results + filtered_results = query_result + if "rowid" in filtered_results: + del filtered_results["rowid"] + if "MAX(rowid)" in filtered_results: + del filtered_results["MAX(rowid)"] return filtered_results @@ -117,6 +125,8 @@ def run_api_server(args): init_api_access_log() # Connect to the database db = database.get_connection(read_only=True) + # Get the last block index + ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(db) # Add routes for path in ROUTES.keys(): app.add_url_rule(path, view_func=handle_route) @@ -127,6 +137,7 @@ def run_api_server(args): def start(args): api_process = Process(target=run_api_server, args=(vars(args),)) api_process.start() + return api_process def stop(): diff --git a/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py b/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py index 70bd27a300..98701c4362 100644 --- a/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py +++ b/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py @@ -729,7 +729,7 @@ def init(): def stop(): - if "INDEXER_THREAD" in globals(): + if "INDEXER_THREAD" in globals() and INDEXER_THREAD is not None: INDEXER_THREAD.stop() diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 1983c59b31..fc9e528038 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -631,6 +631,8 @@ def get_asset_info(db, asset): asset_info["supply"] = xcp_supply(db) return asset_info + print("ASSET NAME", asset_name) + asset_info["supply"] = asset_supply(db, asset_name) cursor = db.cursor() @@ -644,6 +646,8 @@ def get_asset_info(db, asset): cursor.execute(query, bindings) issuance = cursor.fetchone() + print("ASSET issuance", issuance) + asset_info = asset_info | { "asset_longname": issuance["asset_longname"], "owner": issuance["issuer"], diff --git a/counterparty-lib/counterpartylib/test/api_v2_test.py b/counterparty-lib/counterpartylib/test/api_v2_test.py new file mode 100644 index 0000000000..14e0fdd32f --- /dev/null +++ b/counterparty-lib/counterpartylib/test/api_v2_test.py @@ -0,0 +1,249 @@ +import tempfile + +import pytest +import requests + +from counterpartylib.lib import util + +# this is require near the top to do setup of the test suite +from counterpartylib.test import ( + conftest, # noqa: F401 +) +from counterpartylib.test.fixtures.params import ADDR +from counterpartylib.test.util_test import CURR_DIR + +FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" +FIXTURE_DB = tempfile.gettempdir() + "/fixtures.unittest_fixture.db" +API_ROOT = "http://rpc:pass@localhost:10009" + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_balances_by_address(): + alice = ADDR[0] + url = f"{API_ROOT}/addresses/{alice}/balances" + result = requests.get(url) # noqa: S113 + assert result.json() == [ + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "quantity": 100000000, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "CALLABLE", + "quantity": 1000, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 98800000000, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "LOCKED", + "quantity": 1000, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "MAXI", + "quantity": 9223372036854775807, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "quantity": 100000000, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 91875000000, + }, + ] + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_balances_by_asset(): + asset = "XCP" + url = f"{API_ROOT}/assets/{asset}/balances" + result = requests.get(url) # noqa: S113 + assert result.json() == [ + { + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "XCP", + "quantity": 300000000, + }, + { + "address": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", + "asset": "XCP", + "quantity": 46449548498, + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 91875000000, + }, + { + "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", + "asset": "XCP", + "quantity": 92945878046, + }, + { + "address": "mrPk7hTeZWjjSCrMTC2ET4SAUThQt7C4uK", + "asset": "XCP", + "quantity": 14999857, + }, + { + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "XCP", + "quantity": 99999990, + }, + { + "address": "munimLLHjPhGeSU5rYB2HN79LJa8bRZr5b", + "asset": "XCP", + "quantity": 92999130360, + }, + { + "address": "mwtPsLQxW9xpm7gdLmwWvJK5ABdPUVJm42", + "asset": "XCP", + "quantity": 92949122099, + }, + { + "address": "myAtcJEHAsDLbTkai6ipWDZeeL7VkxXsiM", + "asset": "XCP", + "quantity": 92999138812, + }, + { + "address": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + "asset": "XCP", + "quantity": 92999030129, + }, + ] + + +@pytest.mark.usefixtures("api_server") +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_balances_vs_old(): + asset = "XCP" + url = f"{API_ROOT}/assets/{asset}/balances" + new_balances = requests.get(url).json() # noqa: S113 + old_balance = util.api( + "get_balances", + { + "filters": [ + {"field": "asset", "op": "==", "value": asset}, + {"field": "quantity", "op": "!=", "value": 0}, + ], + }, + ) + new_balances = sorted(new_balances, key=lambda x: (x["address"], x["asset"], x["quantity"])) + old_balance = sorted(old_balance, key=lambda x: (x["address"], x["asset"], x["quantity"])) + assert len(new_balances) == len(old_balance) + for new_balance, old_balance in zip(new_balances, old_balance): # noqa: B020 + assert new_balance["address"] == old_balance["address"] + assert new_balance["asset"] == old_balance["asset"] + assert new_balance["quantity"] == old_balance["quantity"] + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_asset_info(): + asset = "NODIVISIBLE" + url = f"{API_ROOT}/assets/{asset}" + result = requests.get(url) # noqa: S113 + + print("RESULT", result) + assert result.json() == { + "asset": "NODIVISIBLE", + "asset_longname": None, + "description": "No divisible asset", + "divisible": False, + "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "locked": False, + "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "supply": 1000, + } + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_asset_orders(): + asset = "XCP" + url = f"{API_ROOT}/assets/{asset}/orders" + result = requests.get(url).json() # noqa: S113 + assert len(result) == 6 + assert result[0] == { + "tx_index": 11, + "tx_hash": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", + "block_index": 310010, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 100000000, + "get_asset": "BTC", + "get_quantity": 1000000, + "get_remaining": 1000000, + "expiration": 2000, + "expire_index": 312010, + "fee_required": 900000, + "fee_required_remaining": 900000, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "status": "open", + } + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_order_info(): + tx_hash = "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a" + url = f"{API_ROOT}/orders/{tx_hash}" + result = requests.get(url).json() # noqa: S113 + assert result[0] == { + "tx_index": 11, + "tx_hash": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", + "block_index": 310010, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 100000000, + "get_asset": "BTC", + "get_quantity": 1000000, + "get_remaining": 1000000, + "expiration": 2000, + "expire_index": 312010, + "fee_required": 900000, + "fee_required_remaining": 900000, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "status": "open", + } + + +@pytest.mark.usefixtures("api_server_v2") +def test_new_get_order_matches(): + tx_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" + url = f"{API_ROOT}/orders/{tx_hash}/matches" + result = requests.get(url).json() # noqa: S113 + assert result[0] == { + "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx0_index": 492, + "tx0_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx0_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "tx1_index": 493, + "tx1_hash": "1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx1_address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "forward_asset": "XCP", + "forward_quantity": 100000000, + "backward_asset": "BTC", + "backward_quantity": 800000, + "tx0_block_index": 310491, + "tx1_block_index": 310492, + "block_index": 310492, + "tx0_expiration": 2000, + "tx1_expiration": 2000, + "match_expire_index": 310512, + "fee_paid": 7200, + "status": "pending", + } diff --git a/counterparty-lib/counterpartylib/test/complex_unit_test.py b/counterparty-lib/counterpartylib/test/complex_unit_test.py index 8ae48a90fa..2fed39a6ac 100644 --- a/counterparty-lib/counterpartylib/test/complex_unit_test.py +++ b/counterparty-lib/counterpartylib/test/complex_unit_test.py @@ -1,4 +1,4 @@ -import pprint # noqa: F401 +import json import tempfile import pytest @@ -12,7 +12,7 @@ conftest, # noqa: F401 util_test, ) -from counterpartylib.test.fixtures.params import ADDR, DP # noqa: F401 +from counterpartylib.test.fixtures.params import ADDR from counterpartylib.test.util_test import CURR_DIR FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" @@ -330,238 +330,6 @@ def test_updated_tables_endpoints(): } -""" -@pytest.mark.usefixtures("api_server") -def test_new_get_balances_by_address(): - alice = ADDR[0] - url = f"{config.API_ROOT}/addresses/{alice}/balances" - result = requests.get(url) # noqa: S113 - assert result.json() == [ - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "A95428956661682277", - "quantity": 100000000, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "CALLABLE", - "quantity": 1000, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 98800000000, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "LOCKED", - "quantity": 1000, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "MAXI", - "quantity": 9223372036854775807, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 985, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "PARENT", - "quantity": 100000000, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 91875000000, - }, - ] - - -@pytest.mark.usefixtures("api_server") -def test_new_get_balances_by_asset(): - asset = "XCP" - url = f"{config.API_ROOT}/assets/{asset}/balances" - result = requests.get(url) # noqa: S113 - assert result.json() == [ - { - "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "XCP", - "quantity": 300000000, - }, - { - "address": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", - "asset": "XCP", - "quantity": 46449548498, - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 91875000000, - }, - { - "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", - "asset": "XCP", - "quantity": 92945878046, - }, - { - "address": "mrPk7hTeZWjjSCrMTC2ET4SAUThQt7C4uK", - "asset": "XCP", - "quantity": 14999857, - }, - { - "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "XCP", - "quantity": 99999990, - }, - { - "address": "munimLLHjPhGeSU5rYB2HN79LJa8bRZr5b", - "asset": "XCP", - "quantity": 92999130360, - }, - { - "address": "mwtPsLQxW9xpm7gdLmwWvJK5ABdPUVJm42", - "asset": "XCP", - "quantity": 92949122099, - }, - { - "address": "myAtcJEHAsDLbTkai6ipWDZeeL7VkxXsiM", - "asset": "XCP", - "quantity": 92999138812, - }, - { - "address": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", - "asset": "XCP", - "quantity": 92999030129, - }, - ] - - -@pytest.mark.usefixtures("api_server") -def test_new_get_balances_vs_old(): - asset = "XCP" - url = f"{config.API_ROOT}/assets/{asset}/balances" - new_balances = requests.get(url).json() # noqa: S113 - old_balance = util.api( - "get_balances", - { - "filters": [ - {"field": "asset", "op": "==", "value": asset}, - {"field": "quantity", "op": "!=", "value": 0}, - ], - }, - ) - new_balances = sorted(new_balances, key=lambda x: (x["address"], x["asset"], x["quantity"])) - old_balance = sorted(old_balance, key=lambda x: (x["address"], x["asset"], x["quantity"])) - assert len(new_balances) == len(old_balance) - for new_balance, old_balance in zip(new_balances, old_balance): # noqa: B020 - assert new_balance["address"] == old_balance["address"] - assert new_balance["asset"] == old_balance["asset"] - assert new_balance["quantity"] == old_balance["quantity"] - - -@pytest.mark.usefixtures("api_server") -def test_new_get_asset_info(): - asset = "NODIVISIBLE" - url = f"{config.API_ROOT}/assets/{asset}" - result = requests.get(url) # noqa: S113 - assert result.json() == [ - { - "asset": "NODIVISIBLE", - "asset_longname": None, - "description": "No divisible asset", - "divisible": False, - "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "locked": False, - "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "supply": 1000, - } - ] - - -@pytest.mark.usefixtures("api_server") -def test_new_get_asset_orders(): - asset = "XCP" - url = f"{config.API_ROOT}/assets/{asset}/orders" - result = requests.get(url).json() # noqa: S113 - assert len(result) == 6 - assert result[0] == { - "tx_index": 11, - "tx_hash": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", - "block_index": 310010, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "give_asset": "XCP", - "give_quantity": 100000000, - "give_remaining": 100000000, - "get_asset": "BTC", - "get_quantity": 1000000, - "get_remaining": 1000000, - "expiration": 2000, - "expire_index": 312010, - "fee_required": 900000, - "fee_required_remaining": 900000, - "fee_provided": 6800, - "fee_provided_remaining": 6800, - "status": "open", - } - - -@pytest.mark.usefixtures("api_server") -def test_new_get_order_info(): - tx_hash = "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a" - url = f"{config.API_ROOT}/orders/{tx_hash}" - result = requests.get(url).json() # noqa: S113 - assert result[0] == { - "tx_index": 11, - "tx_hash": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", - "block_index": 310010, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "give_asset": "XCP", - "give_quantity": 100000000, - "give_remaining": 100000000, - "get_asset": "BTC", - "get_quantity": 1000000, - "get_remaining": 1000000, - "expiration": 2000, - "expire_index": 312010, - "fee_required": 900000, - "fee_required_remaining": 900000, - "fee_provided": 6800, - "fee_provided_remaining": 6800, - "status": "open", - } - - -@pytest.mark.usefixtures("api_server") -def test_new_get_order_matches(): - tx_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" - url = f"{config.API_ROOT}/orders/{tx_hash}/matches" - result = requests.get(url).json() # noqa: S113 - assert result[0] == { - "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", - "tx0_index": 492, - "tx0_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx0_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "tx1_index": 493, - "tx1_hash": "1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", - "tx1_address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "forward_asset": "XCP", - "forward_quantity": 100000000, - "backward_asset": "BTC", - "backward_quantity": 800000, - "tx0_block_index": 310491, - "tx1_block_index": 310492, - "block_index": 310492, - "tx0_expiration": 2000, - "tx1_expiration": 2000, - "match_expire_index": 310512, - "fee_paid": 7200, - "status": "pending", - } - - @pytest.mark.usefixtures("server_db") def test_messages_table(server_db): cursor = server_db.cursor() @@ -570,4 +338,3 @@ def test_messages_table(server_db): for row in result: bindings = json.loads(row["bindings"]) assert isinstance(bindings, dict) - """ diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index a17fb404e2..e312868606 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -2,11 +2,10 @@ Test suite configuration """ +import argparse import binascii import json import logging -import os # noqa: F401 -import pprint # noqa: F401 import time from datetime import datetime @@ -18,6 +17,7 @@ from pycoin.coins.bitcoin import Tx # noqa: F401 from counterpartylib import server +from counterpartylib.lib import api as api_v2 from counterpartylib.lib import arc4, config, database, ledger, log, script, util from counterpartylib.lib.v1 import api from counterpartylib.test import util_test @@ -235,12 +235,70 @@ def api_server(request, cp_server): return api_server +@pytest.fixture(scope="module") +def api_server_v2(request, cp_server): + default_config = { + "testnet": False, + "testcoin": False, + "regtest": False, + "api_limit_rows": 1000, + "backend_connect": None, + "backend_port": None, + "backend_user": None, + "backend_password": None, + "indexd_connect": None, + "indexd_port": None, + "backend_ssl": False, + "backend_ssl_no_verify": False, + "backend_poll_interval": None, + "rpc_host": None, + "rpc_user": None, + "rpc_password": None, + "rpc_no_allow_cors": False, + "force": False, + "requests_timeout": config.DEFAULT_REQUESTS_TIMEOUT, + "rpc_batch_size": config.DEFAULT_RPC_BATCH_SIZE, + "check_asset_conservation": config.DEFAULT_CHECK_ASSET_CONSERVATION, + "backend_ssl_verify": None, + "rpc_allow_cors": None, + "p2sh_dust_return_pubkey": None, + "utxo_locks_max_addresses": config.DEFAULT_UTXO_LOCKS_MAX_ADDRESSES, + "utxo_locks_max_age": config.DEFAULT_UTXO_LOCKS_MAX_AGE, + "estimate_fee_per_kb": None, + "customnet": None, + "verbose": False, + "quiet": False, + "log_file": None, + "api_log_file": None, + "no_log_files": False, + "json_log": False, + "no_check_asset_conservation": True, + "action": "", + } + server_config = ( + default_config + | util_test.COUNTERPARTYD_OPTIONS + | { + "database_file": request.module.FIXTURE_DB, + "rpc_port": TEST_RPC_PORT + 10, + } + ) + args = argparse.Namespace(**server_config) + api_process = api_v2.start(args) + time.sleep(1) + + request.addfinalizer(lambda: api_process.terminate()) + + return api_process + + @pytest.fixture(scope="module") def cp_server(request): dbfile = request.module.FIXTURE_DB sqlfile = request.module.FIXTURE_SQL_FILE options = getattr(request.module, "FIXTURE_OPTIONS", {}) + print(f"cp_server: {dbfile} {sqlfile} {options}") db = util_test.init_database(sqlfile, dbfile, options) # noqa: F841 # monkeypatch this here because init_mock_functions can run before cp_server From 4e6cc2d903e1cb69b4fb28753eccee737bfffbfe Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 20:15:18 +0200 Subject: [PATCH 011/128] use app context instead global variables --- counterparty-lib/counterpartylib/lib/api.py | 32 +++++++++++-------- .../counterpartylib/lib/ledger.py | 4 --- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 8b48cfc05d..58ead7eddd 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -6,6 +6,7 @@ import flask from flask import Flask, request +from flask import g as flask_globals from flask_httpauth import HTTPBasicAuth from counterpartylib import server @@ -19,12 +20,9 @@ multiprocessing.set_start_method("spawn", force=True) logger = logging.getLogger(config.LOGGER_NAME) -app = Flask(__name__) auth = HTTPBasicAuth() -db = None api_process = None - ROUTES = { "/blocks": { "function": ledger.get_blocks, @@ -69,7 +67,7 @@ def verify_password(username, password): def init_api_access_log(): """Initialize API logger.""" - werkzeug_loggers = (logging.getLogger("werkzeug"), app.logger) + werkzeug_loggers = (logging.getLogger("werkzeug"), flask.current_app.logger) # Disable console logging... for werkzeug_logger in werkzeug_loggers: # noqa: E741 @@ -114,22 +112,28 @@ def handle_route(**kwargs): if "args" in route: for arg in route["args"]: function_args[arg[0]] = request.args.get(arg[0], arg[1]) - result = route["function"](db, **function_args) + result = route["function"](get_db(), **function_args) return remove_rowids(result) +def get_db(): + """Get the database connection.""" + if not hasattr(flask_globals, "db"): + flask_globals.db = database.get_connection(read_only=True) + return flask_globals.db + + def run_api_server(args): - global db # noqa: PLW0603 + app = Flask(config.APP_NAME) # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) - init_api_access_log() - # Connect to the database - db = database.get_connection(read_only=True) - # Get the last block index - ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(db) - # Add routes - for path in ROUTES.keys(): - app.add_url_rule(path, view_func=handle_route) + with app.app_context(): + init_api_access_log() + # Get the last block index + ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) + # Add routes + for path in ROUTES.keys(): + app.add_url_rule(path, view_func=handle_route) # Start the API server app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index fc9e528038..1983c59b31 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -631,8 +631,6 @@ def get_asset_info(db, asset): asset_info["supply"] = xcp_supply(db) return asset_info - print("ASSET NAME", asset_name) - asset_info["supply"] = asset_supply(db, asset_name) cursor = db.cursor() @@ -646,8 +644,6 @@ def get_asset_info(db, asset): cursor.execute(query, bindings) issuance = cursor.fetchone() - print("ASSET issuance", issuance) - asset_info = asset_info | { "asset_longname": issuance["asset_longname"], "owner": issuance["issuer"], From 840633e1c87ecaf3833dfc88615385d29da3a02d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 5 Apr 2024 21:19:24 +0200 Subject: [PATCH 012/128] tweaks --- counterparty-lib/counterpartylib/lib/api.py | 45 ++++++++++--------- counterparty-lib/counterpartylib/server.py | 7 ++- .../counterpartylib/test/conftest.py | 7 +-- .../counterpartylib/test/utxolocks_test.py | 1 - 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 58ead7eddd..7c68570865 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -21,7 +21,6 @@ logger = logging.getLogger(config.LOGGER_NAME) auth = HTTPBasicAuth() -api_process = None ROUTES = { "/blocks": { @@ -60,11 +59,6 @@ } -@auth.verify_password -def verify_password(username, password): - return username == config.RPC_USER and password == config.RPC_PASSWORD - - def init_api_access_log(): """Initialize API logger.""" werkzeug_loggers = (logging.getLogger("werkzeug"), flask.current_app.logger) @@ -105,6 +99,18 @@ def remove_rowids(query_result): return filtered_results +def get_db(): + """Get the database connection.""" + if not hasattr(flask_globals, "db"): + flask_globals.db = database.get_connection(read_only=True) + return flask_globals.db + + +@auth.verify_password +def verify_password(username, password): + return username == config.RPC_USER and password == config.RPC_PASSWORD + + @auth.login_required def handle_route(**kwargs): route = ROUTES.get(str(request.url_rule.rule)) @@ -116,13 +122,6 @@ def handle_route(**kwargs): return remove_rowids(result) -def get_db(): - """Get the database connection.""" - if not hasattr(flask_globals, "db"): - flask_globals.db = database.get_connection(read_only=True) - return flask_globals.db - - def run_api_server(args): app = Flask(config.APP_NAME) # Initialise log and config @@ -138,12 +137,18 @@ def run_api_server(args): app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) -def start(args): - api_process = Process(target=run_api_server, args=(vars(args),)) - api_process.start() - return api_process +class APIServer(object): + def __init__(self): + self.process = None + def start(self, args): + if self.process is not None: + raise Exception("API server is already running") + self.process = Process(target=run_api_server, args=(vars(args),)) + self.process.start() + return self.process -def stop(): - if api_process and api_process.is_alive(): - api_process.terminate() + def stop(self): + if self.process and self.process.is_alive(): + self.process.terminate() + self.process = None diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 14ff81e7a3..b34ffa136f 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -58,11 +58,10 @@ def sigterm_handler(_signo, _stack_frame): assert False # noqa: B011 logger.info(f"Received {signal_name}.") - api.stop() - if "api_server" in globals(): logger.info("Stopping API server.") api_server.stop() # noqa: F821 + if "api_status_poller" in globals(): api_status_poller.stop() # noqa: F821 logger.info("Stopping backend.") @@ -678,10 +677,10 @@ def start_all(args): api_server.start() else: # REST API Server. - api.start(args) + api_server = api.APIServer() + api_server.start(args) # Server - blocks.follow(db) diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index e312868606..091554825f 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -284,12 +284,13 @@ def api_server_v2(request, cp_server): } ) args = argparse.Namespace(**server_config) - api_process = api_v2.start(args) + api_server = api_v2.APIServer() + api_server.start(args) time.sleep(1) - request.addfinalizer(lambda: api_process.terminate()) + request.addfinalizer(lambda: api_server.stop()) - return api_process + return api_server @pytest.fixture(scope="module") diff --git a/counterparty-lib/counterpartylib/test/utxolocks_test.py b/counterparty-lib/counterpartylib/test/utxolocks_test.py index 91e544b693..e1d5458964 100644 --- a/counterparty-lib/counterpartylib/test/utxolocks_test.py +++ b/counterparty-lib/counterpartylib/test/utxolocks_test.py @@ -4,7 +4,6 @@ from io import BytesIO import bitcoin -import pytest # noqa: F401 from counterpartylib.lib import transaction from counterpartylib.lib.messages import send From 6e752054055f37ba82cbf8183c38ed714bd232bf Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sat, 6 Apr 2024 16:33:25 +0200 Subject: [PATCH 013/128] More routes for blocks, credits and debits --- counterparty-lib/counterpartylib/lib/api.py | 24 +++++++ .../counterpartylib/lib/ledger.py | 64 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 7c68570865..08c7683afe 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -36,9 +36,27 @@ "/blocks//events": { "function": ledger.get_messages, }, + "/blocks//credits": { + "function": ledger.get_credits, + }, + "/blocks//debits": { + "function": ledger.get_debits, + }, + "/blocks//expirations": { + "function": ledger.get_expirations, + }, + "/transactions/": { + "function": ledger.get_transaction, + }, "/addresses/
/balances": { "function": ledger.get_address_balances, }, + "/addresses/
/credits": { + "function": ledger.get_credits, + }, + "/addresses/
/debits": { + "function": ledger.get_debits, + }, "/assets/": { "function": ledger.get_asset_info, }, @@ -49,6 +67,12 @@ "function": ledger.get_orders_by_asset, "args": [("status", "open")], }, + "/assets//credits": { + "function": ledger.get_credits, + }, + "/assets//debits": { + "function": ledger.get_debits, + }, "/orders/": { "function": ledger.get_order, }, diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 1983c59b31..b696c180ce 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -326,6 +326,36 @@ def get_balances_count(db, address): return cursor.fetchall() +def get_credits_or_debits(db, table, address=None, asset=None, block_index=None, tx_index=None): + cursor = db.cursor() + where = [] + bindings = [] + if address is not None: + where.append("address = ?") + bindings.append(address) + if asset is not None: + where.append("asset = ?") + bindings.append(asset) + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) + if tx_index is not None: + where.append("tx_index = ?") + bindings.append(tx_index) + # no sql injection here + query = f"""SELECT * FROM {table} WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + cursor.execute(query, tuple(bindings)) + return cursor.fetchall() + + +def get_credits(db, address=None, asset=None, block_index=None, tx_index=None): + return get_credits_or_debits(db, "credits", address, asset, block_index, tx_index) + + +def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): + return get_credits_or_debits(db, "debits", address, asset, block_index, tx_index) + + ##################### # ISSUANCES # ##################### @@ -867,6 +897,40 @@ def get_addresses(db, address=None): return cursor.fetchall() +def get_expirations(db, block_index): + cursor = db.cursor() + queries = [ + """ + SELECT 'order' AS type, order_hash AS object_id FROM order_expirations + WHERE block_index = ? + """, + """ + SELECT 'order_match' AS type, order_match_id AS object_id FROM order_match_expirations + WHERE block_index = ? + """, + """ + SELECT 'bet' AS type, bet_hash AS object_id FROM bet_expirations + WHERE block_index = ? + """, + """ + SELECT 'bet_match' AS type, bet_match_id AS object_id FROM bet_match_expirations + WHERE block_index = ? + """, + """ + SELECT 'rps' AS type, rps_hash AS object_id FROM rps_expirations + WHERE block_index = ? + """, + """ + SELECT 'rps_match' AS type, rps_match_id AS object_id FROM rps_match_expirations + WHERE block_index = ? + """, + ] + query = " UNION ALL ".join(queries) + bindings = (block_index,) + cursor.execute(query, bindings) + return cursor.fetchall() + + ############################### # UTIL FUNCTIONS # ############################### From b275944945a7e20240498ade41f5e7fb979ea4dc Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sat, 6 Apr 2024 16:42:46 +0200 Subject: [PATCH 014/128] clean imports --- counterparty-lib/counterpartylib/lib/script.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/script.py b/counterparty-lib/counterpartylib/lib/script.py index bbd2f6306d..74005a885f 100644 --- a/counterparty-lib/counterpartylib/lib/script.py +++ b/counterparty-lib/counterpartylib/lib/script.py @@ -12,8 +12,12 @@ from bitcoin.core.key import CPubKey from counterparty_rs import b58, utils -# We are using PyCryptodome not PyCrypto -# from Crypto.Hash import RIPEMD160 +# TODO: Use `python-bitcointools` instead. (Get rid of `pycoin` dependency.) +from pycoin.ecdsa.secp256k1 import secp256k1_generator as generator_secp256k1 +from pycoin.encoding.b58 import a2b_hashed_base58 +from pycoin.encoding.bytes32 import from_bytes_32 +from pycoin.encoding.exceptions import EncodingError +from pycoin.encoding.sec import public_pair_to_sec from ripemd import ripemd160 as RIPEMD160 # nosec B413 from counterpartylib.lib import config, exceptions, ledger, opcodes, util @@ -404,14 +408,6 @@ def scriptpubkey_to_address(scriptpubkey): return None -# TODO: Use `python-bitcointools` instead. (Get rid of `pycoin` dependency.) -from pycoin.ecdsa.secp256k1 import secp256k1_generator as generator_secp256k1 # noqa: E402 -from pycoin.encoding.b58 import a2b_hashed_base58 # noqa: E402 -from pycoin.encoding.bytes32 import from_bytes_32 # noqa: E402 -from pycoin.encoding.exceptions import EncodingError # noqa: E402 -from pycoin.encoding.sec import public_pair_to_sec # noqa: E402 - - def wif_to_tuple_of_prefix_secret_exponent_compressed(wif): """ Return a tuple of (prefix, secret_exponent, is_compressed). From 6f4ffeba256334115d847354bfa0018e7393e3e4 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sat, 6 Apr 2024 20:36:47 +0200 Subject: [PATCH 015/128] More routes --- counterparty-lib/counterpartylib/lib/api.py | 43 +++++++++++ .../counterpartylib/lib/ledger.py | 77 ++++++++++++++++--- .../counterpartylib/lib/messages/bet.py | 6 +- .../counterpartylib/lib/messages/broadcast.py | 4 +- .../counterpartylib/lib/messages/burn.py | 4 +- 5 files changed, 117 insertions(+), 17 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 08c7683afe..6e40d808f3 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -23,6 +23,7 @@ auth = HTTPBasicAuth() ROUTES = { + ### /blocks ### "/blocks": { "function": ledger.get_blocks, "args": [("last", None), ("limit", 10)], @@ -45,24 +46,49 @@ "/blocks//expirations": { "function": ledger.get_expirations, }, + "/blocks//cancels": { + "function": ledger.get_cancels, + }, + "/blocks//destructions": { + "function": ledger.get_destructions, + }, + ### /transactions ### "/transactions/": { "function": ledger.get_transaction, }, + ### /addresses ### "/addresses/
/balances": { "function": ledger.get_address_balances, }, + "/addresses/
/balances/": { + "function": ledger.get_balance_object, + }, "/addresses/
/credits": { "function": ledger.get_credits, }, "/addresses/
/debits": { "function": ledger.get_debits, }, + "/addresses/
/bets": { + "function": ledger.get_bet_by_feed, + "args": [("status", "open")], + }, + "/addresses/
/broadcasts": { + "function": ledger.get_broadcasts_by_source, + }, + "/addresses/
/burns": { + "function": ledger.get_burns, + }, + ### /assets ### "/assets/": { "function": ledger.get_asset_info, }, "/assets//balances": { "function": ledger.get_asset_balances, }, + "/assets//balances/
": { + "function": ledger.get_balance_object, + }, "/assets//orders": { "function": ledger.get_orders_by_asset, "args": [("status", "open")], @@ -73,6 +99,7 @@ "/assets//debits": { "function": ledger.get_debits, }, + ### /orders ### "/orders/": { "function": ledger.get_order, }, @@ -80,6 +107,22 @@ "function": ledger.get_order_matches_by_order, "args": [("status", "pending")], }, + "/orders//btcpays": { + "function": ledger.get_btcpays_by_order, + "args": [("status", "pending")], + }, + ### /bets ### + "/bets/": { + "function": ledger.get_bet, + }, + "/bets//matches": { + "function": ledger.get_bet_matches_by_bet, + "args": [("status", "pending")], + }, + ### /burns ### + "/burns": { + "function": ledger.get_burns, + }, } diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index b696c180ce..acc6620772 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -285,6 +285,14 @@ def get_balance(db, address, asset, raise_error_if_no_balance=False, return_list return balances[0]["quantity"] +def get_balance_object(db, address, asset): + return { + "address": address, + "asset": asset, + "quantity": get_balance(db, address, asset), + } + + def get_address_balances(db, address): cursor = db.cursor() query = """ @@ -769,14 +777,14 @@ def get_oracle_last_price(db, oracle_address, block_index): ) -def get_broadcasts_by_source(db, source, status): +def get_broadcasts_by_source(db, address, status="valid", order_by="DESC"): cursor = db.cursor() - query = """ + query = f""" SELECT * FROM broadcasts WHERE (status = ? AND source = ?) - ORDER BY tx_index ASC - """ - bindings = (status, source) + ORDER BY tx_index {order_by} + """ # nosec B608 # noqa: S608 + bindings = (status, address) cursor.execute(query, bindings) return cursor.fetchall() @@ -786,16 +794,16 @@ def get_broadcasts_by_source(db, source, status): ##################### -def get_burns(db, status=None, source=None): +def get_burns(db, address=None, status="valid"): cursor = db.cursor() where = [] bindings = [] if status is not None: where.append("status = ?") bindings.append(status) - if source is not None: + if address is not None: where.append("source = ?") - bindings.append(source) + bindings.append(address) # no sql injection here query = f"""SELECT * FROM burns WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 cursor.execute(query, tuple(bindings)) @@ -931,6 +939,28 @@ def get_expirations(db, block_index): return cursor.fetchall() +def get_cancels(db, block_index): + cursor = db.cursor() + query = """ + SELECT * FROM cancels + WHERE block_index = ? + """ + bindings = (block_index,) + cursor.execute(query, bindings) + return cursor.fetchall() + + +def get_destructions(db, block_index): + cursor = db.cursor() + query = """ + SELECT * FROM destructions + WHERE block_index = ? + """ + bindings = (block_index,) + cursor.execute(query, bindings) + return cursor.fetchall() + + ############################### # UTIL FUNCTIONS # ############################### @@ -1369,7 +1399,7 @@ def get_matching_bets(db, feed_address, bet_type): return cursor.fetchall() -def get_open_bet_by_feed(db, feed_address): +def get_bet_by_feed(db, address, status="open"): cursor = db.cursor() query = """ SELECT * FROM ( @@ -1380,7 +1410,22 @@ def get_open_bet_by_feed(db, feed_address): ) WHERE status = ? ORDER BY tx_index, tx_hash """ - bindings = (feed_address, "open") + bindings = (address, status) + cursor.execute(query, bindings) + return cursor.fetchall() + + +def get_bet_matches_by_bet(db, tx_hash, status="pending"): + cursor = db.cursor() + query = """ + SELECT * FROM ( + SELECT *, MAX(rowid) + FROM bet_matches + WHERE (tx0_hash = ? OR tx1_hash = ?) + GROUP BY id + ) WHERE status = ? + """ + bindings = (tx_hash, tx_hash, status) cursor.execute(query, bindings) return cursor.fetchall() @@ -1565,6 +1610,18 @@ def get_order_matches_by_order(db, tx_hash, status="pending"): return cursor.fetchall() +def get_btcpays_by_order(db, tx_hash): + cursor = db.cursor() + query = """ + SELECT * + FROM btc_pays + WHERE order_match_id LIKE '%?%' + """ + bindings = (tx_hash,) + cursor.execute(query, bindings) + return cursor.fetchall() + + ### UPDATES ### diff --git a/counterparty-lib/counterpartylib/lib/messages/bet.py b/counterparty-lib/counterpartylib/lib/messages/bet.py index 352afa63e0..c9fd2f0bd9 100644 --- a/counterparty-lib/counterpartylib/lib/messages/bet.py +++ b/counterparty-lib/counterpartylib/lib/messages/bet.py @@ -254,7 +254,7 @@ def cancel_bet_match(db, bet_match, status, block_index, tx_index): def get_fee_fraction(db, feed_address): """Get fee fraction from last broadcast from the feed_address address.""" - broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid") + broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid", order_by="ASC") if broadcasts: last_broadcast = broadcasts[-1] @@ -297,7 +297,7 @@ def validate( problems.append("integer overflow") # Look at feed to be bet on. - broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid") + broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid", order_by="ASC") if not broadcasts: problems.append("feed doesn’t exist") elif not broadcasts[-1]["text"]: @@ -661,7 +661,7 @@ def match(db, tx): ledger.update_bet(db, tx1["tx_hash"], set_data) # Get last value of feed. - broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid") + broadcasts = ledger.get_broadcasts_by_source(db, feed_address, "valid", order_by="ASC") initial_value = broadcasts[-1]["value"] # Record bet fulfillment. diff --git a/counterparty-lib/counterpartylib/lib/messages/broadcast.py b/counterparty-lib/counterpartylib/lib/messages/broadcast.py index c6d5df638f..ee43c67c3e 100644 --- a/counterparty-lib/counterpartylib/lib/messages/broadcast.py +++ b/counterparty-lib/counterpartylib/lib/messages/broadcast.py @@ -111,7 +111,7 @@ def validate(db, source, timestamp, value, fee_fraction_int, text, block_index): if not source: problems.append("null source address") # Check previous broadcast in this feed. - broadcasts = ledger.get_broadcasts_by_source(db, source, "valid") + broadcasts = ledger.get_broadcasts_by_source(db, source, "valid", order_by="ASC") if broadcasts: last_broadcast = broadcasts[-1] if last_broadcast["locked"]: @@ -256,7 +256,7 @@ def parse(db, tx, message): if value is None or value < 0: # Cancel Open Bets? if value == -2: - for i in ledger.get_open_bet_by_feed(db, tx["source"]): + for i in ledger.get_bet_by_feed(db, tx["source"], status="open"): bet.cancel_bet(db, i, "dropped", tx["block_index"], tx["tx_index"]) # Cancel Pending Bet Matches? if value == -3: diff --git a/counterparty-lib/counterpartylib/lib/messages/burn.py b/counterparty-lib/counterpartylib/lib/messages/burn.py index 235a706264..64a2de73e2 100644 --- a/counterparty-lib/counterpartylib/lib/messages/burn.py +++ b/counterparty-lib/counterpartylib/lib/messages/burn.py @@ -82,7 +82,7 @@ def compose(db, source, quantity, overburn=False): raise exceptions.ComposeError(problems) # Check that a maximum of 1 BTC total is burned per address. - burns = ledger.get_burns(db, status="valid", source=source) + burns = ledger.get_burns(db, source) already_burned = sum([burn["burned"] for burn in burns]) if quantity > (1 * config.UNIT - already_burned) and not overburn: @@ -118,7 +118,7 @@ def parse(db, tx, mainnet_burns, message=None): if status == "valid": # Calculate quantity of XCP earned. (Maximum 1 BTC in total, ever.) - burns = ledger.get_burns(db, status="valid", source=tx["source"]) + burns = ledger.get_burns(db, tx["source"]) already_burned = sum([burn["burned"] for burn in burns]) one = 1 * config.UNIT max_burn = one - already_burned From 3ed4ba7ac9319119c0ea7e31f23a5adc330ddd36 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 13:06:33 +0200 Subject: [PATCH 016/128] More routes: sends, resolutions, .. --- counterparty-lib/counterpartylib/lib/api.py | 31 +++++++++ .../counterpartylib/lib/ledger.py | 69 ++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 6e40d808f3..e8649f6dbc 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -52,6 +52,12 @@ "/blocks//destructions": { "function": ledger.get_destructions, }, + "/blocks//issuances": { + "function": ledger.get_issuances, + }, + "/blocks//sends": { + "function": ledger.get_sends_or_receives, + }, ### /transactions ### "/transactions/": { "function": ledger.get_transaction, @@ -79,6 +85,18 @@ "/addresses/
/burns": { "function": ledger.get_burns, }, + "/addresses/
/sends": { + "function": ledger.get_sends, + }, + "/addresses/
/receives": { + "function": ledger.get_receives, + }, + "/addresses/
/sends/": { + "function": ledger.get_sends, + }, + "/addresses/
/receives/": { + "function": ledger.get_receives, + }, ### /assets ### "/assets/": { "function": ledger.get_asset_info, @@ -99,6 +117,15 @@ "/assets//debits": { "function": ledger.get_debits, }, + "/assets//dividends": { + "function": ledger.get_dividends, + }, + "/assets//issuances": { + "function": ledger.get_issuances, + }, + "/assets//sends": { + "function": ledger.get_sends_or_receives, + }, ### /orders ### "/orders/": { "function": ledger.get_order, @@ -119,6 +146,10 @@ "function": ledger.get_bet_matches_by_bet, "args": [("status", "pending")], }, + "/bets//resolutions": { + "function": ledger.get_resolutions_by_bet, + "args": [("status", "pending")], + }, ### /burns ### "/burns": { "function": ledger.get_burns, diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index acc6620772..8624ee94f1 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -364,6 +364,45 @@ def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): return get_credits_or_debits(db, "debits", address, asset, block_index, tx_index) +def get_sends_or_receives( + db, source=None, destination=None, asset=None, block_index=None, status="valid" +): + cursor = db.cursor() + where = [] + bindings = [] + if source is not None: + where.append("source = ?") + bindings.append(source) + if destination is not None: + where.append("destination = ?") + bindings.append(destination) + if asset is not None: + where.append("asset = ?") + bindings.append(asset) + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) + if status is not None: + where.append("status = ?") + bindings.append(status) + # no sql injection here + query = f"""SELECT * FROM sends WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + cursor.execute(query, tuple(bindings)) + return cursor.fetchall() + + +def get_sends(db, address=None, asset=None, block_index=None, status="valid"): + return get_sends_or_receives( + db, source=address, asset=asset, block_index=block_index, status=status + ) + + +def get_receives(db, address=None, asset=None, block_index=None, status="valid"): + return get_sends_or_receives( + db, destination=address, asset=asset, block_index=block_index, status=status + ) + + ##################### # ISSUANCES # ##################### @@ -694,7 +733,9 @@ def get_asset_info(db, asset): return asset_info -def get_issuances(db, asset=None, status=None, locked=None, first=False, last=False): +def get_issuances( + db, asset=None, status=None, locked=None, block_index=None, first=False, last=False +): cursor = db.cursor() cursor = db.cursor() where = [] @@ -708,6 +749,9 @@ def get_issuances(db, asset=None, status=None, locked=None, first=False, last=Fa if locked is not None: where.append("locked = ?") bindings.append(locked) + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) # no sql injection here query = f"""SELECT * FROM issuances WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 if first: @@ -742,6 +786,17 @@ def get_valid_assets(db): return cursor.fetchall() +def get_dividends(db, asset): + cursor = db.cursor() + query = """ + SELECT * FROM dividends + WHERE asset = ? AND status = ? + """ + bindings = (asset, "valid") + cursor.execute(query, bindings) + return cursor.fetchall() + + ##################### # BROADCASTS # ##################### @@ -1430,6 +1485,18 @@ def get_bet_matches_by_bet(db, tx_hash, status="pending"): return cursor.fetchall() +def get_resolutions_by_bet(db, tx_hash): + cursor = db.cursor() + query = """ + SELECT * + FROM bet_match_resolutions + WHERE bet_match_id LIKE '%?%' + """ + bindings = (tx_hash,) + cursor.execute(query, bindings) + return cursor.fetchall() + + ### UPDATES ### From 83bd420d75bc27d54769db4e287fce37b37f100c Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 13:22:53 +0200 Subject: [PATCH 017/128] Add 'event' field in mempool table --- counterparty-lib/counterpartylib/lib/blocks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/counterparty-lib/counterpartylib/lib/blocks.py b/counterparty-lib/counterpartylib/lib/blocks.py index 9e48c437be..13a068467c 100644 --- a/counterparty-lib/counterpartylib/lib/blocks.py +++ b/counterparty-lib/counterpartylib/lib/blocks.py @@ -605,6 +605,9 @@ def initialise(db): bindings TEXT, timestamp INTEGER) """) + columns = [column["name"] for column in cursor.execute("""PRAGMA table_info(mempool)""")] + if "event" not in columns: + cursor.execute("""ALTER TABLE mempool ADD COLUMN event TEXT""") # Lock UPDATE on all tables for table in TABLES: @@ -1204,7 +1207,7 @@ def follow(db): tx_hash, new_message = message new_message["tx_hash"] = tx_hash cursor.execute( - """INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp)""", + """INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp, :event)""", new_message, ) From 38fb1617d456482c4365bd80e3d673e3dd806d16 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 17:00:17 +0200 Subject: [PATCH 018/128] More routes: dispensers, sweeps, holders, .. --- counterparty-lib/counterpartylib/lib/api.py | 38 +++++++++++++ .../counterpartylib/lib/ledger.py | 54 +++++++++++++++++-- .../counterpartylib/lib/messages/dispenser.py | 16 +++--- 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index e8649f6dbc..23452abeb7 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -58,6 +58,12 @@ "/blocks//sends": { "function": ledger.get_sends_or_receives, }, + "/blocks//dispenses": { + "function": ledger.get_dispenses, + }, + "/blocks//sweeps": { + "function": ledger.get_sweeps, + }, ### /transactions ### "/transactions/": { "function": ledger.get_transaction, @@ -97,7 +103,21 @@ "/addresses/
/receives/": { "function": ledger.get_receives, }, + "/addresses/
/dispensers": { + "function": ledger.get_dispensers, + "args": [("status", 0)], + }, + "/addresses/
/dispensers/": { + "function": ledger.get_dispensers, + "args": [("status", 0)], + }, + "/addresses/
/sweeps": { + "function": ledger.get_sweeps, + }, ### /assets ### + "/assets": { + "function": ledger.get_valid_assets, + }, "/assets/": { "function": ledger.get_asset_info, }, @@ -126,6 +146,17 @@ "/assets//sends": { "function": ledger.get_sends_or_receives, }, + "/assets//dispensers": { + "function": ledger.get_dispensers, + "args": [("status", 0)], + }, + "/asset//dispensers/
": { + "function": ledger.get_dispensers, + "args": [("status", 0)], + }, + "/assets//holders": { + "function": ledger.get_asset_holders, + }, ### /orders ### "/orders/": { "function": ledger.get_order, @@ -154,6 +185,13 @@ "/burns": { "function": ledger.get_burns, }, + ### /dispensers ### + "/dispensers/": { + "function": ledger.get_dispenser_info, + }, + "/dispensers//dispenses": { + "function": ledger.get_dispenses, + }, } diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 8624ee94f1..7ac6830e11 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -403,6 +403,25 @@ def get_receives(db, address=None, asset=None, block_index=None, status="valid") ) +def get_sweeps(db, address=None, block_index=None, status="valid"): + cursor = db.cursor() + where = [] + bindings = [] + if address is not None: + where.append("source = ?") + bindings.append(address) + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) + if status is not None: + where.append("status = ?") + bindings.append(status) + # no sql injection here + query = f"""SELECT * FROM sweeps WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + cursor.execute(query, tuple(bindings)) + return cursor.fetchall() + + ##################### # ISSUANCES # ##################### @@ -706,9 +725,11 @@ def get_asset_info(db, asset): if asset_name == config.XCP: asset_info["supply"] = xcp_supply(db) + asset_info["holder_count"] = get_asset_holder_count(db, asset) return asset_info asset_info["supply"] = asset_supply(db, asset_name) + asset_info["holder_count"] = get_asset_holder_count(db, asset) cursor = db.cursor() query = """ @@ -1301,7 +1322,7 @@ def get_dispensers( db, status_in=None, source_in=None, - source=None, + address=None, asset=None, origin=None, status=None, @@ -1313,9 +1334,9 @@ def get_dispensers( bindings = [] # where for immutable fields first_where = [] - if source is not None: + if address is not None: first_where.append("source = ?") - bindings.append(source) + bindings.append(address) if source_in is not None: first_where.append(f"source IN ({','.join(['?' for e in range(0, len(source_in))])})") bindings += source_in @@ -1356,6 +1377,22 @@ def get_dispensers( return cursor.fetchall() +def get_dispenses(db, dispenser_tx_hash=None, block_index=None): + cursor = db.cursor() + where = [] + bindings = [] + if dispenser_tx_hash is not None: + where.append("dispenser_tx_hash = ?") + bindings.append(dispenser_tx_hash) + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) + # no sql injection here + query = f"""SELECT * FROM dispenses WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + cursor.execute(query, tuple(bindings)) + return cursor.fetchall() + + ### UPDATES ### @@ -2117,6 +2154,17 @@ def holders(db, asset, exclude_empty_holders=False): return holders +def get_asset_holders(db, asset): + asset_name = resolve_subasset_longname(db, asset) + return holders(db, asset_name, True) + + +def get_asset_holder_count(db, asset): + holders = get_asset_holders(db, asset) + addresses = [holder["address"] for holder in holders] + return len(set(addresses)) + + def xcp_created(db): """Return number of XCP created thus far.""" cursor = db.cursor() diff --git a/counterparty-lib/counterpartylib/lib/messages/dispenser.py b/counterparty-lib/counterpartylib/lib/messages/dispenser.py index 4f2ea6780c..158fc1516c 100644 --- a/counterparty-lib/counterpartylib/lib/messages/dispenser.py +++ b/counterparty-lib/counterpartylib/lib/messages/dispenser.py @@ -228,12 +228,12 @@ def validate( and open_address != source ): open_dispensers = ledger.get_dispensers( - db, status_in=[0, 11], source=open_address, asset=asset, origin=source + db, status_in=[0, 11], address=open_address, asset=asset, origin=source ) else: query_address = open_address if status == STATUS_OPEN_EMPTY_ADDRESS else source open_dispensers = ledger.get_dispensers( - db, status_in=[0, 11], source=query_address, asset=asset + db, status_in=[0, 11], address=query_address, asset=asset ) if len(open_dispensers) == 0 or open_dispensers[0]["status"] != STATUS_CLOSING: @@ -459,7 +459,7 @@ def parse(db, tx, message): else: if dispenser_status == STATUS_OPEN or dispenser_status == STATUS_OPEN_EMPTY_ADDRESS: existing = ledger.get_dispensers( - db, source=action_address, asset=asset, status=STATUS_OPEN + db, address=action_address, asset=asset, status=STATUS_OPEN ) if len(existing) == 0: @@ -612,7 +612,7 @@ def parse(db, tx, message): ) dispenser_tx_hash = ledger.get_dispensers( - db, source=action_address, asset=asset, status=STATUS_OPEN + db, address=action_address, asset=asset, status=STATUS_OPEN )[0]["tx_hash"] bindings_refill = { "tx_index": tx["tx_index"], @@ -647,14 +647,14 @@ def parse(db, tx, message): if close_from_another_address: existing = ledger.get_dispensers( db, - source=action_address, + address=action_address, asset=asset, status=STATUS_OPEN, origin=tx["source"], ) else: existing = ledger.get_dispensers( - db, source=tx["source"], asset=asset, status=STATUS_OPEN + db, address=tx["source"], asset=asset, status=STATUS_OPEN ) if len(existing) == 1: if close_delay == 0: @@ -692,7 +692,7 @@ def is_dispensable(db, address, amount): if address is None: return False - dispensers = ledger.get_dispensers(db, source=address, status_in=[0, 11]) + dispensers = ledger.get_dispensers(db, address=address, status_in=[0, 11]) for next_dispenser in dispensers: if next_dispenser["oracle_address"] != None: # noqa: E711 @@ -731,7 +731,7 @@ def dispense(db, tx): dispensers = [] if next_out["destination"] is not None: dispensers = ledger.get_dispensers( - db, source=next_out["destination"], status_in=[0, 11], order_by="asset" + db, address=next_out["destination"], status_in=[0, 11], order_by="asset" ) for dispenser in dispensers: From 092b312362eff01e8fbf6f7cb76a4ed62a37bce9 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 19:47:42 +0200 Subject: [PATCH 019/128] Add routes for events --- counterparty-lib/counterpartylib/lib/api.py | 13 ++++++- .../counterpartylib/lib/ledger.py | 37 +++++++++++++++++++ .../counterpartylib/test/api_v2_test.py | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 23452abeb7..95544d965f 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -35,7 +35,10 @@ "function": ledger.get_transactions_by_block, }, "/blocks//events": { - "function": ledger.get_messages, + "function": ledger.get_events, + }, + "/blocks//events/": { + "function": ledger.get_events, }, "/blocks//credits": { "function": ledger.get_credits, @@ -192,6 +195,14 @@ "/dispensers//dispenses": { "function": ledger.get_dispenses, }, + ### /events ### + "/events/": { + "function": ledger.get_events, + }, + "/events/": { + "function": ledger.get_events, + "args": [("last", None), ("limit", 100)], + }, } diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index 7ac6830e11..a91bf96a84 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -64,6 +64,43 @@ def get_messages(db, block_index=None, block_index_in=None, message_index_in=Non return cursor.fetchall() +def get_events(db, block_index=None, event=None, event_index=None, last=None, limit=None): + cursor = db.cursor() + where = [] + bindings = [] + if block_index is not None: + where.append("block_index = ?") + bindings.append(block_index) + if event is not None: + where.append("event = ?") + bindings.append(event) + if event_index is not None: + where.append("message_index = ?") + bindings.append(event_index) + if last is not None: + where.append("message_index <= ?") + bindings.append(last) + if block_index is None and limit is None: + limit = 100 + if limit is not None: + limit = "LIMIT ?" + bindings.append(limit) + else: + limit = "" + # no sql injection here + query = f""" + SELECT message_index AS event_index, event, bindings, block_index, timestamp + FROM messages + WHERE ({" AND ".join(where)}) + ORDER BY message_index DESC {limit} + """ # nosec B608 # noqa: S608 + cursor.execute(query, tuple(bindings)) + events = cursor.fetchall() + for i, _ in enumerate(events): + events[i]["bindings"] = json.loads(events[i]["bindings"]) + return events + + # we are using a function here for testing purposes def curr_time(): return int(time.time()) diff --git a/counterparty-lib/counterpartylib/test/api_v2_test.py b/counterparty-lib/counterpartylib/test/api_v2_test.py index 14e0fdd32f..c808246d32 100644 --- a/counterparty-lib/counterpartylib/test/api_v2_test.py +++ b/counterparty-lib/counterpartylib/test/api_v2_test.py @@ -155,7 +155,6 @@ def test_new_get_asset_info(): url = f"{API_ROOT}/assets/{asset}" result = requests.get(url) # noqa: S113 - print("RESULT", result) assert result.json() == { "asset": "NODIVISIBLE", "asset_longname": None, @@ -165,6 +164,7 @@ def test_new_get_asset_info(): "locked": False, "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "supply": 1000, + "holder_count": 3, } From 46ebe887bcb8f7c21f64bd24851c00ef169ff638 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 20:22:42 +0200 Subject: [PATCH 020/128] Add routes for mempool and events count --- counterparty-lib/counterpartylib/lib/api.py | 14 ++++++- .../counterpartylib/lib/ledger.py | 37 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api.py index 95544d965f..15a1346b25 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api.py @@ -37,9 +37,18 @@ "/blocks//events": { "function": ledger.get_events, }, + "/blocks//events/counts": { + "function": ledger.get_events_counts, + }, "/blocks//events/": { "function": ledger.get_events, }, + "/blocks/mempool/events": { + "function": ledger.get_mempool_events, + }, + "/blocks/mempool/events/": { + "function": ledger.get_mempool_events, + }, "/blocks//credits": { "function": ledger.get_credits, }, @@ -153,7 +162,7 @@ "function": ledger.get_dispensers, "args": [("status", 0)], }, - "/asset//dispensers/
": { + "/assets//dispensers/
": { "function": ledger.get_dispensers, "args": [("status", 0)], }, @@ -199,6 +208,9 @@ "/events/": { "function": ledger.get_events, }, + "/events/counts": { + "function": ledger.get_events_counts, + }, "/events/": { "function": ledger.get_events, "args": [("last", None), ("limit", 100)], diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index a91bf96a84..d3a8cffc4f 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -101,6 +101,43 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li return events +def get_mempool_events(db, event_name=None): + cursor = db.cursor() + where = [] + bindings = [] + if event_name is not None: + where.append("event = ?") + bindings.append(event_name) + # no sql injection here + query = """ + SELECT tx_hash, event, bindings, timestamp + FROM mempool + """ + if event_name is not None: + query += f"""WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + query += """ORDER BY timestamp DESC""" + cursor.execute(query, tuple(bindings)) + events = cursor.fetchall() + for i, _ in enumerate(events): + events[i]["bindings"] = json.loads(events[i]["bindings"]) + return events + + +def get_events_counts(db, block_index=None): + cursor = db.cursor() + bindings = [] + query = """ + SELECT event, COUNT(*) AS event_count + FROM messages + """ + if block_index is not None: + query += "WHERE block_index = ?" + bindings.append(block_index) + query += "GROUP BY event" + cursor.execute(query) + return cursor.fetchall() + + # we are using a function here for testing purposes def curr_time(): return int(time.time()) From f8cc62c278d4038c74177ddb0bef97d0d90668d8 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 20:54:24 +0200 Subject: [PATCH 021/128] module in subfolder --- .../counterpartylib/lib/api/api_server.py | 118 +++++++++++++++++ .../lib/{v1/api.py => api/api_v1.py} | 0 .../lib/{api.py => api/routes.py} | 123 +----------------- counterparty-lib/counterpartylib/server.py | 6 +- .../test/bytespersigop_test.py | 2 +- .../counterpartylib/test/complex_unit_test.py | 2 +- .../counterpartylib/test/conftest.py | 8 +- .../test/estimate_fee_per_kb_test.py | 2 +- .../counterpartylib/test/fixtures/vectors.py | 2 +- .../test/p2sh_encoding_test.py | 2 +- .../counterpartylib/test/util_test.py | 2 +- 11 files changed, 137 insertions(+), 130 deletions(-) create mode 100644 counterparty-lib/counterpartylib/lib/api/api_server.py rename counterparty-lib/counterpartylib/lib/{v1/api.py => api/api_v1.py} (100%) rename counterparty-lib/counterpartylib/lib/{api.py => api/routes.py} (61%) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py new file mode 100644 index 0000000000..b1c6fca25d --- /dev/null +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -0,0 +1,118 @@ +import argparse +import logging +import multiprocessing +from logging import handlers as logging_handlers +from multiprocessing import Process + +import flask +from counterpartylib import server +from counterpartylib.lib import ( + blocks, + config, + database, + ledger, +) +from counterpartylib.lib.api.routes import ROUTES +from flask import Flask, request +from flask import g as flask_globals +from flask_httpauth import HTTPBasicAuth + +multiprocessing.set_start_method("spawn", force=True) + +logger = logging.getLogger(config.LOGGER_NAME) +auth = HTTPBasicAuth() + + +def init_api_access_log(): + """Initialize API logger.""" + werkzeug_loggers = (logging.getLogger("werkzeug"), flask.current_app.logger) + + # Disable console logging... + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + werkzeug_logger.setLevel(logging.CRITICAL) + werkzeug_logger.propagate = False + + # Log to file, if configured... + if config.API_LOG: + handler = logging_handlers.RotatingFileHandler( + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT + ) + for werkzeug_logger in werkzeug_loggers: # noqa: E741 + handler.setLevel(logging.DEBUG) + werkzeug_logger.addHandler(handler) + + flask.cli.show_server_banner = lambda *args: None + + +def remove_rowids(query_result): + """Remove the rowid field from the query result.""" + if isinstance(query_result, list): + filtered_results = [] + for row in list(query_result): + if "rowid" in row: + del row["rowid"] + if "MAX(rowid)" in row: + del row["MAX(rowid)"] + filtered_results.append(row) + return filtered_results + filtered_results = query_result + if "rowid" in filtered_results: + del filtered_results["rowid"] + if "MAX(rowid)" in filtered_results: + del filtered_results["MAX(rowid)"] + return filtered_results + + +def get_db(): + """Get the database connection.""" + if not hasattr(flask_globals, "db"): + flask_globals.db = database.get_connection(read_only=True) + return flask_globals.db + + +@auth.verify_password +def verify_password(username, password): + return username == config.RPC_USER and password == config.RPC_PASSWORD + + +@auth.login_required +def handle_route(**kwargs): + route = ROUTES.get(str(request.url_rule.rule)) + function_args = dict(kwargs) + if "args" in route: + for arg in route["args"]: + function_args[arg[0]] = request.args.get(arg[0], arg[1]) + result = route["function"](get_db(), **function_args) + return remove_rowids(result) + + +def run_api_server(args): + app = Flask(config.APP_NAME) + # Initialise log and config + server.initialise_log_and_config(argparse.Namespace(**args)) + with app.app_context(): + init_api_access_log() + # Get the last block index + ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) + # Add routes + for path in ROUTES.keys(): + app.add_url_rule(path, view_func=handle_route) + # Start the API server + app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) + + +class APIServer(object): + def __init__(self): + self.process = None + + def start(self, args): + if self.process is not None: + raise Exception("API server is already running") + self.process = Process(target=run_api_server, args=(vars(args),)) + self.process.start() + return self.process + + def stop(self): + if self.process and self.process.is_alive(): + self.process.terminate() + self.process = None diff --git a/counterparty-lib/counterpartylib/lib/v1/api.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py similarity index 100% rename from counterparty-lib/counterpartylib/lib/v1/api.py rename to counterparty-lib/counterpartylib/lib/api/api_v1.py diff --git a/counterparty-lib/counterpartylib/lib/api.py b/counterparty-lib/counterpartylib/lib/api/routes.py similarity index 61% rename from counterparty-lib/counterpartylib/lib/api.py rename to counterparty-lib/counterpartylib/lib/api/routes.py index 15a1346b25..cc404e4487 100644 --- a/counterparty-lib/counterpartylib/lib/api.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -1,26 +1,4 @@ -import argparse -import logging -import multiprocessing -from logging import handlers as logging_handlers -from multiprocessing import Process - -import flask -from flask import Flask, request -from flask import g as flask_globals -from flask_httpauth import HTTPBasicAuth - -from counterpartylib import server -from counterpartylib.lib import ( - blocks, - config, - database, - ledger, -) - -multiprocessing.set_start_method("spawn", force=True) - -logger = logging.getLogger(config.LOGGER_NAME) -auth = HTTPBasicAuth() +from counterpartylib.lib import ledger ROUTES = { ### /blocks ### @@ -205,6 +183,10 @@ "function": ledger.get_dispenses, }, ### /events ### + "/events": { + "function": ledger.get_events, + "args": [("last", None), ("limit", 100)], + }, "/events/": { "function": ledger.get_events, }, @@ -216,98 +198,3 @@ "args": [("last", None), ("limit", 100)], }, } - - -def init_api_access_log(): - """Initialize API logger.""" - werkzeug_loggers = (logging.getLogger("werkzeug"), flask.current_app.logger) - - # Disable console logging... - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - werkzeug_logger.setLevel(logging.CRITICAL) - werkzeug_logger.propagate = False - - # Log to file, if configured... - if config.API_LOG: - handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT - ) - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - handler.setLevel(logging.DEBUG) - werkzeug_logger.addHandler(handler) - - flask.cli.show_server_banner = lambda *args: None - - -def remove_rowids(query_result): - """Remove the rowid field from the query result.""" - if isinstance(query_result, list): - filtered_results = [] - for row in list(query_result): - if "rowid" in row: - del row["rowid"] - if "MAX(rowid)" in row: - del row["MAX(rowid)"] - filtered_results.append(row) - return filtered_results - filtered_results = query_result - if "rowid" in filtered_results: - del filtered_results["rowid"] - if "MAX(rowid)" in filtered_results: - del filtered_results["MAX(rowid)"] - return filtered_results - - -def get_db(): - """Get the database connection.""" - if not hasattr(flask_globals, "db"): - flask_globals.db = database.get_connection(read_only=True) - return flask_globals.db - - -@auth.verify_password -def verify_password(username, password): - return username == config.RPC_USER and password == config.RPC_PASSWORD - - -@auth.login_required -def handle_route(**kwargs): - route = ROUTES.get(str(request.url_rule.rule)) - function_args = dict(kwargs) - if "args" in route: - for arg in route["args"]: - function_args[arg[0]] = request.args.get(arg[0], arg[1]) - result = route["function"](get_db(), **function_args) - return remove_rowids(result) - - -def run_api_server(args): - app = Flask(config.APP_NAME) - # Initialise log and config - server.initialise_log_and_config(argparse.Namespace(**args)) - with app.app_context(): - init_api_access_log() - # Get the last block index - ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) - # Add routes - for path in ROUTES.keys(): - app.add_url_rule(path, view_func=handle_route) - # Start the API server - app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) - - -class APIServer(object): - def __init__(self): - self.process = None - - def start(self, args): - if self.process is not None: - raise Exception("API server is already running") - self.process = Process(target=run_api_server, args=(vars(args),)) - self.process.start() - return self.process - - def stop(self): - if self.process and self.process.is_alive(): - self.process.terminate() - self.process = None diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index b34ffa136f..35b61504f3 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -21,7 +21,6 @@ from termcolor import colored, cprint from counterpartylib.lib import ( - api, backend, blocks, check, @@ -33,7 +32,8 @@ util, ) from counterpartylib.lib import kickstart as kickstarter -from counterpartylib.lib.v1 import api as api_v1 +from counterpartylib.lib.api import api_server as api_v2 +from counterpartylib.lib.api import api_v1 logger = logging.getLogger(config.LOGGER_NAME) D = decimal.Decimal @@ -677,7 +677,7 @@ def start_all(args): api_server.start() else: # REST API Server. - api_server = api.APIServer() + api_server = api_v2.APIServer() api_server.start(args) # Server diff --git a/counterparty-lib/counterpartylib/test/bytespersigop_test.py b/counterparty-lib/counterpartylib/test/bytespersigop_test.py index 8bc6ffd727..b4b3c7ff36 100644 --- a/counterparty-lib/counterpartylib/test/bytespersigop_test.py +++ b/counterparty-lib/counterpartylib/test/bytespersigop_test.py @@ -4,7 +4,7 @@ import bitcoin as bitcoinlib from counterpartylib.lib import ledger -from counterpartylib.lib.v1 import api +from counterpartylib.lib.api import api_v1 as api from counterpartylib.test import util_test from counterpartylib.test.fixtures.params import ADDR diff --git a/counterparty-lib/counterpartylib/test/complex_unit_test.py b/counterparty-lib/counterpartylib/test/complex_unit_test.py index 2fed39a6ac..19c5e6c332 100644 --- a/counterparty-lib/counterpartylib/test/complex_unit_test.py +++ b/counterparty-lib/counterpartylib/test/complex_unit_test.py @@ -5,7 +5,7 @@ from apsw import ConstraintError from counterpartylib.lib import blocks, ledger, util -from counterpartylib.lib.v1 import api +from counterpartylib.lib.api import api_v1 as api # this is require near the top to do setup of the test suite from counterpartylib.test import ( diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index 091554825f..ffca6c8aa3 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -17,9 +17,9 @@ from pycoin.coins.bitcoin import Tx # noqa: F401 from counterpartylib import server -from counterpartylib.lib import api as api_v2 from counterpartylib.lib import arc4, config, database, ledger, log, script, util -from counterpartylib.lib.v1 import api +from counterpartylib.lib.api import api_server as api_v2 +from counterpartylib.lib.api import api_v1 as api from counterpartylib.test import util_test from counterpartylib.test.fixtures.params import DEFAULT_PARAMS from counterpartylib.test.fixtures.scenarios import INTEGRATION_SCENARIOS @@ -514,7 +514,9 @@ def init_arc4(seed): monkeypatch.setattr("counterpartylib.lib.log.isodt", isodt) monkeypatch.setattr("counterpartylib.lib.ledger.curr_time", curr_time) monkeypatch.setattr("counterpartylib.lib.util.date_passed", date_passed) - monkeypatch.setattr("counterpartylib.lib.api.init_api_access_log", init_api_access_log) + monkeypatch.setattr( + "counterpartylib.lib.api.api_server.init_api_access_log", init_api_access_log + ) if hasattr(config, "PREFIX"): monkeypatch.setattr("counterpartylib.lib.config.PREFIX", b"TESTXXXX") monkeypatch.setattr("counterpartylib.lib.backend.getrawtransaction", mocked_getrawtransaction) diff --git a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py index 3ec95ca4fb..6f49d4a930 100644 --- a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py +++ b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py @@ -5,7 +5,7 @@ import bitcoin as bitcoinlib from counterpartylib.lib import (backend, transaction) -from counterpartylib.lib.v1 import api +from counterpartylib.lib.api import api_v1 as api from counterpartylib.test import ( util_test, ) diff --git a/counterparty-lib/counterpartylib/test/fixtures/vectors.py b/counterparty-lib/counterpartylib/test/fixtures/vectors.py index e69720ee46..a548d8befd 100644 --- a/counterparty-lib/counterpartylib/test/fixtures/vectors.py +++ b/counterparty-lib/counterpartylib/test/fixtures/vectors.py @@ -14,11 +14,11 @@ import bitcoin as bitcoinlib from counterpartylib.lib import config, exceptions, script +from counterpartylib.lib.api.api_v1 import APIError from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser from counterpartylib.lib.ledger import CreditError, DebitError from counterpartylib.lib.messages import issuance from counterpartylib.lib.util import QuantityError, RPCError -from counterpartylib.lib.v1.api import APIError from .params import ( ADDR, diff --git a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py index 78267f404a..5cf51448f7 100644 --- a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py +++ b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py @@ -15,9 +15,9 @@ ledger, script, ) +from counterpartylib.lib.api import api_v1 as api from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser from counterpartylib.lib.transaction_helper import p2sh_encoding -from counterpartylib.lib.v1 import api # this is require near the top to do setup of the test suite from counterpartylib.test import ( diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index 2e29881499..bedfbf81a6 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -804,7 +804,7 @@ def check_outputs( tested_module = sys.modules[f"counterpartylib.lib.{tx_name}"] except KeyError: # TODO: hack if tx_name == "api_v1": - tested_module = sys.modules["counterpartylib.lib.v1.api"] + tested_module = sys.modules["counterpartylib.lib.api.api_v1"] else: tested_module = sys.modules[f"counterpartylib.lib.messages.{tx_name}"] tested_method = getattr(tested_module, method) From 313b7471106000b2a0adfd4258de7653d2ca422b Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Sun, 7 Apr 2024 21:41:56 +0200 Subject: [PATCH 022/128] Add route to compose transactions --- .../counterpartylib/lib/api/api_server.py | 4 +- .../counterpartylib/lib/api/api_v1.py | 232 ++---------------- .../counterpartylib/lib/api/routes.py | 9 +- .../counterpartylib/lib/transaction.py | 75 +++++- .../test/bytespersigop_test.py | 13 +- .../test/estimate_fee_per_kb_test.py | 3 +- .../test/p2sh_encoding_test.py | 18 +- 7 files changed, 120 insertions(+), 234 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index b1c6fca25d..1f05736a70 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -79,7 +79,9 @@ def verify_password(username, password): def handle_route(**kwargs): route = ROUTES.get(str(request.url_rule.rule)) function_args = dict(kwargs) - if "args" in route: + if "pass_all_args" in route and route["pass_all_args"]: + function_args = request.args | function_args + elif "args" in route: for arg in route["args"]: function_args[arg[0]] = request.args.get(arg[0], arg[1]) result = route["function"](get_db(), **function_args) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index a90dbc8e3c..72cd2d6d3a 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -5,32 +5,22 @@ problem. """ +import binascii import collections import decimal import json import logging -import os # noqa: F401 +import math import re -import sys import threading import time import traceback from logging import handlers as logging_handlers -import requests # noqa: F401 - -D = decimal.Decimal -import binascii # noqa: E402 -import inspect # noqa: E402 -import math # noqa: E402 -import struct # noqa: E402, F401 - -import apsw # noqa: E402, F401 -import flask # noqa: E402 -import jsonrpc # noqa: E402 +import flask +import jsonrpc from counterpartylib.lib import ( # noqa: E402 backend, - blocks, # noqa: F401 config, database, exceptions, @@ -59,11 +49,13 @@ sweep, # noqa: F401 ) from counterpartylib.lib.messages.versions import enhanced_send # noqa: E402 -from flask import request # noqa: E402 -from flask_httpauth import HTTPBasicAuth # noqa: E402 -from jsonrpc import dispatcher # noqa: E402 -from jsonrpc.exceptions import JSONRPCDispatchException # noqa: E402 -from xmltodict import unparse as serialize_to_xml # noqa: E402 +from flask import request +from flask_httpauth import HTTPBasicAuth +from jsonrpc import dispatcher +from jsonrpc.exceptions import JSONRPCDispatchException +from xmltodict import unparse as serialize_to_xml + +D = decimal.Decimal logger = logging.getLogger(config.LOGGER_NAME) @@ -144,48 +136,6 @@ """, } -API_TRANSACTIONS = [ - "bet", - "broadcast", - "btcpay", - "burn", - "cancel", - "destroy", - "dividend", - "issuance", - "order", - "send", - "rps", - "rpsresolve", - "sweep", - "dispenser", -] - -COMMONS_ARGS = [ - "encoding", - "fee_per_kb", - "regular_dust_size", - "multisig_dust_size", - "op_return_value", - "pubkey", - "allow_unconfirmed_inputs", - "fee", - "fee_provided", - "estimate_fee_per_kb", - "estimate_fee_per_kb_nblocks", - "estimate_fee_per_kb_conf_target", - "estimate_fee_per_kb_mode", - "unspent_tx_hash", - "custom_inputs", - "dust_return_pubkey", - "disable_utxo_locks", - "extended_tx_info", - "p2sh_source_multisig_pubkeys", - "p2sh_source_multisig_pubkeys_required", - "p2sh_pretx_txid", -] - - JSON_RPC_ERROR_API_COMPOSE = -32001 # code to use for error composing transaction result CURRENT_API_STATUS_CODE = None # is updated by the APIStatusPoller @@ -518,118 +468,6 @@ def adjust_get_transactions_results(query_result): return filtered_results -def compose_transaction( - db, - name, - params, - encoding="auto", - fee_per_kb=None, - estimate_fee_per_kb=None, - estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET, - estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE, - regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, - multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, - op_return_value=config.DEFAULT_OP_RETURN_VALUE, - pubkey=None, - allow_unconfirmed_inputs=False, - fee=None, - fee_provided=0, - unspent_tx_hash=None, - custom_inputs=None, - dust_return_pubkey=None, - disable_utxo_locks=False, - extended_tx_info=False, - p2sh_source_multisig_pubkeys=None, - p2sh_source_multisig_pubkeys_required=None, - p2sh_pretx_txid=None, - old_style_api=True, - segwit=False, -): - """Create and return a transaction.""" - - # Get provided pubkeys. - if type(pubkey) == str: # noqa: E721 - provided_pubkeys = [pubkey] - elif type(pubkey) == list: # noqa: E721 - provided_pubkeys = pubkey - elif pubkey == None: # noqa: E711 - provided_pubkeys = [] - else: - assert False # noqa: B011 - - # Get additional pubkeys from `source` and `destination` params. - # Convert `source` and `destination` to pubkeyhash form. - for address_name in ["source", "destination"]: - if address_name in params: - address = params[address_name] - if isinstance(address, list): - # pkhshs = [] - # for addr in address: - # provided_pubkeys += script.extract_pubkeys(addr) - # pkhshs.append(script.make_pubkeyhash(addr)) - # params[address_name] = pkhshs - pass - else: - provided_pubkeys += script.extract_pubkeys(address) - params[address_name] = script.make_pubkeyhash(address) - - # Check validity of collected pubkeys. - for pubkey in provided_pubkeys: - if not script.is_fully_valid(binascii.unhexlify(pubkey)): - raise script.AddressError(f"invalid public key: {pubkey}") - - compose_method = sys.modules[f"counterpartylib.lib.messages.{name}"].compose - compose_params = inspect.getfullargspec(compose_method)[0] - missing_params = [p for p in compose_params if p not in params and p != "db"] - for param in missing_params: - params[param] = None - - # dont override fee_per_kb if specified - if fee_per_kb is not None: - estimate_fee_per_kb = False - else: - fee_per_kb = config.DEFAULT_FEE_PER_KB - - if "extended_tx_info" in params: - extended_tx_info = params["extended_tx_info"] - del params["extended_tx_info"] - - if "old_style_api" in params: - old_style_api = params["old_style_api"] - del params["old_style_api"] - - if "segwit" in params: - segwit = params["segwit"] - del params["segwit"] - - tx_info = compose_method(db, **params) - return transaction.construct( - db, - tx_info, - encoding=encoding, - fee_per_kb=fee_per_kb, - estimate_fee_per_kb=estimate_fee_per_kb, - estimate_fee_per_kb_conf_target=estimate_fee_per_kb_conf_target, - regular_dust_size=regular_dust_size, - multisig_dust_size=multisig_dust_size, - op_return_value=op_return_value, - provided_pubkeys=provided_pubkeys, - allow_unconfirmed_inputs=allow_unconfirmed_inputs, - exact_fee=fee, - fee_provided=fee_provided, - unspent_tx_hash=unspent_tx_hash, - custom_inputs=custom_inputs, - dust_return_pubkey=dust_return_pubkey, - disable_utxo_locks=disable_utxo_locks, - extended_tx_info=extended_tx_info, - p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, - p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required, - p2sh_pretx_txid=p2sh_pretx_txid, - old_style_api=old_style_api, - segwit=segwit, - ) - - def conditional_decorator(decorator, condition): """Checks the condition and if True applies specified decorator.""" @@ -762,23 +600,12 @@ def sql(query, bindings=None): # Generate dynamically create_{transaction} methods def generate_create_method(tx): - def split_params(**kwargs): - transaction_args = {} - common_args = {} - private_key_wif = None - for key in kwargs: - if key in COMMONS_ARGS: - common_args[key] = kwargs[key] - elif key == "privkey": - private_key_wif = kwargs[key] - else: - transaction_args[key] = kwargs[key] - return transaction_args, common_args, private_key_wif - def create_method(**kwargs): try: - transaction_args, common_args, private_key_wif = split_params(**kwargs) - return compose_transaction( + transaction_args, common_args, private_key_wif = ( + transaction.split_compose_arams(**kwargs) + ) + return transaction.compose_transaction( self.db, name=tx, params=transaction_args, **common_args ) except ( @@ -798,7 +625,7 @@ def create_method(**kwargs): return create_method - for tx in API_TRANSACTIONS: + for tx in transaction.COMPOSABLE_TRANSACTIONS: create_method = generate_create_method(tx) create_method.__name__ = f"create_{tx}" dispatcher.add_method(create_method) @@ -1228,7 +1055,7 @@ def handle_healthz(): check_database_state(self.db, latest_block_index) else: logger.debug("Performing heavy healthz check.") - compose_transaction( + transaction.compose_transaction( self.db, name="send", params={ @@ -1351,7 +1178,7 @@ def handle_rest(path_args, flask_request): error = "No query_type provided." return flask.Response(error, 400, mimetype="application/json") # Check if message type or table name are valid. - if (compose and query_type not in API_TRANSACTIONS) or ( + if (compose and query_type not in transaction.COMPOSABLE_TRANSACTIONS) or ( not compose and query_type not in API_TABLES ): error = f'No such query type in supported queries: "{query_type}".' @@ -1362,24 +1189,9 @@ def handle_rest(path_args, flask_request): query_data = {} if compose: - common_args = {} - transaction_args = {} - for key, value in extra_args: - # Determine value type. - try: - value = int(value) # noqa: PLW2901 - except ValueError: - try: - value = float(value) # noqa: PLW2901 - except ValueError: - pass - # Split keys into common and transaction-specific arguments. Discard the privkey. - if key in COMMONS_ARGS: - common_args[key] = value - elif key == "privkey": - pass - else: - transaction_args[key] = value + transaction_args, common_args, private_key_wif = transaction.split_compose_arams( + **extra_args + ) # Must have some additional transaction arguments. if not len(transaction_args): @@ -1388,7 +1200,7 @@ def handle_rest(path_args, flask_request): # Compose the transaction. try: - query_data = compose_transaction( + query_data = transaction.compose_transaction( self.db, name=query_type, params=transaction_args, **common_args ) except ( diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index cc404e4487..0af4fd8dcd 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -1,4 +1,7 @@ -from counterpartylib.lib import ledger +from counterpartylib.lib import ( + ledger, + transaction, +) ROUTES = { ### /blocks ### @@ -55,6 +58,10 @@ "function": ledger.get_sweeps, }, ### /transactions ### + "/transactions/compose/": { + "function": transaction.compose, + "pass_all_args": True, + }, "/transactions/": { "function": ledger.get_transaction, }, diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index 644f56fc41..7ae778cf08 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -7,26 +7,27 @@ import binascii import decimal import hashlib +import inspect import io -import json # noqa: F401 import logging +<<<<<<< HEAD import math # noqa: F401 import os # noqa: F401 import re # noqa: F401 import sys # noqa: F401 import threading import time # noqa: F401 +======= +import sys +>>>>>>> 0595b1a1 (Add route to compose transactions) import bitcoin as bitcoinlib import cachetools -import requests # noqa: F401 -from bitcoin.core import CTransaction, b2lx, x # noqa: F401 -from bitcoin.core.script import CScript # noqa: F401 +from bitcoin.core import CTransaction from counterpartylib.lib import ( arc4, # noqa: F401 backend, - blocks, # noqa: F401 config, exceptions, gettxinfo, @@ -957,3 +958,67 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): raise script.InputError("Invalid private key.") # noqa: B904 return dust_return_pubkey + + +COMPOSE_COMMONS_ARGS = [ + "encoding", + "fee_per_kb", + "regular_dust_size", + "multisig_dust_size", + "op_return_value", + "pubkey", + "allow_unconfirmed_inputs", + "fee", + "fee_provided", + "estimate_fee_per_kb", + "estimate_fee_per_kb_nblocks", + "estimate_fee_per_kb_conf_target", + "estimate_fee_per_kb_mode", + "unspent_tx_hash", + "custom_inputs", + "dust_return_pubkey", + "disable_utxo_locks", + "extended_tx_info", + "p2sh_source_multisig_pubkeys", + "p2sh_source_multisig_pubkeys_required", + "p2sh_pretx_txid", +] + + +def split_compose_arams(**kwargs): + transaction_args = {} + common_args = {} + private_key_wif = None + for key in kwargs: + if key in COMPOSE_COMMONS_ARGS: + common_args[key] = kwargs[key] + elif key == "privkey": + private_key_wif = kwargs[key] + else: + transaction_args[key] = kwargs[key] + return transaction_args, common_args, private_key_wif + + +COMPOSABLE_TRANSACTIONS = [ + "bet", + "broadcast", + "btcpay", + "burn", + "cancel", + "destroy", + "dividend", + "issuance", + "order", + "send", + "rps", + "rpsresolve", + "sweep", + "dispenser", +] + + +def compose(db, transaction_name, **kwargs): + if transaction_name not in COMPOSABLE_TRANSACTIONS: + raise exceptions.TransactionError("Transaction type not composable.") + transaction_args, common_args, _ = split_compose_arams(**kwargs) + return compose_transaction(db, name=transaction_name, params=transaction_args, **common_args) diff --git a/counterparty-lib/counterpartylib/test/bytespersigop_test.py b/counterparty-lib/counterpartylib/test/bytespersigop_test.py index b4b3c7ff36..79778e2b37 100644 --- a/counterparty-lib/counterpartylib/test/bytespersigop_test.py +++ b/counterparty-lib/counterpartylib/test/bytespersigop_test.py @@ -3,8 +3,7 @@ import bitcoin as bitcoinlib -from counterpartylib.lib import ledger -from counterpartylib.lib.api import api_v1 as api +from counterpartylib.lib import ledger, transaction from counterpartylib.test import util_test from counterpartylib.test.fixtures.params import ADDR @@ -21,7 +20,7 @@ def test_bytespersigop(server_db): transaction.initialise() # ADDR[0], bytespersigop=False, desc 41 bytes, opreturn - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "issuance", { @@ -41,7 +40,7 @@ def test_bytespersigop(server_db): assert "OP_RETURN" in repr(tx.vout[0].scriptPubKey) # ADDR[0], bytespersigop=False, desc 42 bytes, multisig - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "issuance", { @@ -66,7 +65,7 @@ def test_bytespersigop(server_db): assert ledger.enabled("bytespersigop") == True # noqa: E712 # ADDR[0], bytespersigop=True, desc 41 bytes, opreturn - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "issuance", { @@ -86,7 +85,7 @@ def test_bytespersigop(server_db): assert "OP_RETURN" in repr(tx.vout[0].scriptPubKey) # ADDR[1], bytespersigop=True, desc 41 bytes, opreturn encoding - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "issuance", { @@ -107,7 +106,7 @@ def test_bytespersigop(server_db): # ADDR[1], bytespersigop=True, desc 20 bytes, FORCED encoding=multisig # will use 2 UTXOs to make the bytes:sigop ratio in our favor - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "issuance", { diff --git a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py index 6f49d4a930..ef74ba3e64 100644 --- a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py +++ b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py @@ -40,9 +40,10 @@ def _fee_per_kb(conf_target, mode): ) with util_test.ConfigContext(ESTIMATE_FEE_PER_KB=True): + transaction.initialise() - txhex = api.compose_transaction( + txhex = transaction.compose_transaction( server_db, "send", {"source": ADDR[0], "destination": ADDR[1], "asset": "XCP", "quantity": 100}, diff --git a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py index 5cf51448f7..1f001cd522 100644 --- a/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py +++ b/counterparty-lib/counterpartylib/test/p2sh_encoding_test.py @@ -14,8 +14,8 @@ gettxinfo, ledger, script, + transaction, ) -from counterpartylib.lib.api import api_v1 as api from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser from counterpartylib.lib.transaction_helper import p2sh_encoding @@ -75,7 +75,7 @@ def test_p2sh_encoding(server_db): fee = 20000 fee_per_kb = 50000 - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "send", {"source": source, "destination": destination, "asset": "XCP", "quantity": 100}, @@ -147,7 +147,7 @@ def test_p2sh_encoding(server_db): logger.debug(f"pretxid {pretxid}") # check that when we do another, unrelated, send that it won't use our UTXO - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "send", {"source": source, "destination": destination, "asset": "XCP", "quantity": 100}, @@ -161,7 +161,7 @@ def test_p2sh_encoding(server_db): ) # now compose the data transaction - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "send", {"source": source, "destination": destination, "asset": "XCP", "quantity": 100}, @@ -253,7 +253,7 @@ def test_p2sh_encoding_long_data(server_db): # pprint.pprint(utxos) fee_per_kb = 50000 - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "broadcast", { @@ -330,7 +330,7 @@ def test_p2sh_encoding_long_data(server_db): logger.debug(f"pretxid {pretxid}") # now compose the data transaction - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "broadcast", { @@ -435,7 +435,7 @@ def test_p2sh_encoding_p2sh_source_not_supported(server_db): fee_per_kb = 50000 with pytest.raises(exceptions.TransactionError): - result = api.compose_transaction( # noqa: F841 + result = transaction.compose_transaction( # noqa: F841 server_db, "send", {"source": source, "destination": destination, "asset": "XCP", "quantity": 100}, @@ -477,7 +477,7 @@ def test_p2sh_encoding_manual_multisig_transaction(server_db): # setup transaction fee = 20000 fee_per_kb = 50000 - pretxhex = api.compose_transaction( + pretxhex = transaction.compose_transaction( server_db, "send", { @@ -503,7 +503,7 @@ def test_p2sh_encoding_manual_multisig_transaction(server_db): logger.debug(f"pretxid {pretxid}") # now compose the data transaction - result = api.compose_transaction( + result = transaction.compose_transaction( server_db, "send", {"source": source, "destination": destination, "asset": "XCP", "quantity": 100}, From d699408b11e0af007b87782ee9e1a615abe3c180 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 11:40:09 +0200 Subject: [PATCH 023/128] Add route for healthz and get_tx_info --- .../counterpartylib/lib/api/api_v1.py | 45 ++++-------------- .../counterpartylib/lib/api/routes.py | 10 ++++ .../counterpartylib/lib/api/util.py | 46 +++++++++++++++++++ .../counterpartylib/lib/transaction.py | 14 ++++++ 4 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 counterparty-lib/counterpartylib/lib/api/util.py diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index 72cd2d6d3a..63bdd7f170 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -19,7 +19,7 @@ import flask import jsonrpc -from counterpartylib.lib import ( # noqa: E402 +from counterpartylib.lib import ( backend, config, database, @@ -31,8 +31,9 @@ transaction, util, ) -from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser # noqa: E402 -from counterpartylib.lib.messages import ( # noqa: E402 +from counterpartylib.lib.api import util as api_util +from counterpartylib.lib.kickstart.blocks_parser import BlockchainParser +from counterpartylib.lib.messages import ( bet, # noqa: F401 broadcast, # noqa: F401 btcpay, # noqa: F401 @@ -171,14 +172,6 @@ class DatabaseError(Exception): pass -def check_database_state(db, blockcount): - f"""Checks {config.XCP_NAME} database to see if is caught up with backend.""" # noqa: B021 - if ledger.CURRENT_BLOCK_INDEX + 1 < blockcount: - raise DatabaseError(f"{config.XCP_NAME} database is behind backend.") - logger.debug("Database state check passed.") - return - - # TODO: ALL queries EVERYWHERE should be done with these methods def db_query(db, statement, bindings=(), callback=None, **callback_args): """Allow direct access to the database in a parametrized manner.""" @@ -529,7 +522,7 @@ def run(self): check_backend_state() code = 12 logger.debug("Checking database state.") - check_database_state(db, backend.getblockcount()) + api_util.check_database_state(db, backend.getblockcount()) self.last_database_check = time.time() except (BackendError, DatabaseError) as e: exception_name = e.__class__.__name__ @@ -796,7 +789,7 @@ def get_running_info(): latest_block_index = backend.getblockcount() try: - check_database_state(self.db, latest_block_index) + api_util.check_database_state(self.db, latest_block_index) except DatabaseError: caught_up = False else: @@ -1045,31 +1038,9 @@ def _set_cors_headers(response): @app.route("/healthz", methods=["GET"]) def handle_healthz(): msg, code = "Healthy", 200 - - type_ = request.args.get("type", "heavy") - - try: - if type_ == "light": - logger.debug("Performing light healthz check.") - latest_block_index = backend.getblockcount() - check_database_state(self.db, latest_block_index) - else: - logger.debug("Performing heavy healthz check.") - transaction.compose_transaction( - self.db, - name="send", - params={ - "source": config.UNSPENDABLE, - "destination": config.UNSPENDABLE, - "asset": config.XCP, - "quantity": 100000000, - }, - allow_unconfirmed_inputs=True, - fee=1000, - ) - except Exception: + check_type = request.args.get("type", "heavy") + if not api_util.healthz(self.db, check_type): msg, code = "Unhealthy", 503 - return flask.Response(msg, code, mimetype="application/json") @app.route("/", defaults={"args_path": ""}, methods=["GET", "POST", "OPTIONS"]) diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index 0af4fd8dcd..d22f4b6576 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -2,6 +2,7 @@ ledger, transaction, ) +from counterpartylib.lib.api.util import handle_healthz_route ROUTES = { ### /blocks ### @@ -62,6 +63,10 @@ "function": transaction.compose, "pass_all_args": True, }, + "/transactions/info": { + "function": transaction.info, + "args": [("rawtransaction", None), ("block_index", None)], + }, "/transactions/": { "function": ledger.get_transaction, }, @@ -204,4 +209,9 @@ "function": ledger.get_events, "args": [("last", None), ("limit", 100)], }, + ### /healthz ### + "/healthz": { + "function": handle_healthz_route, + "args": [("check", "heavy")], + }, } diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py new file mode 100644 index 0000000000..c7f92c6fe8 --- /dev/null +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -0,0 +1,46 @@ +import logging + +import flask +from counterparylib.lib import backend, config, exceptions, ledger, transaction + +logger = logging.getLogger(config.LOGGER_NAME) + + +def check_last_parsed_block(blockcount): + f"""Checks {config.XCP_NAME} database to see if is caught up with backend.""" # noqa: B021 + if ledger.CURRENT_BLOCK_INDEX + 1 < blockcount: + raise exceptions.DatabaseError(f"{config.XCP_NAME} database is behind backend.") + logger.debug("Database state check passed.") + return + + +def healthz(db, check_type="heavy"): + try: + if check_type == "light": + logger.debug("Performing light healthz check.") + latest_block_index = backend.getblockcount() + check_last_parsed_block(latest_block_index) + else: + logger.debug("Performing heavy healthz check.") + transaction.compose_transaction( + db, + name="send", + params={ + "source": config.UNSPENDABLE, + "destination": config.UNSPENDABLE, + "asset": config.XCP, + "quantity": 100000000, + }, + allow_unconfirmed_inputs=True, + fee=1000, + ) + except Exception: + return False + return True + + +def handle_healthz_route(db, check="heavy"): + msg, code = "Healthy", 200 + if not healthz(db, check): + msg, code = "Unhealthy", 503 + return flask.Response(msg, code, mimetype="application/json") diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index 7ae778cf08..184bf039d4 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -1022,3 +1022,17 @@ def compose(db, transaction_name, **kwargs): raise exceptions.TransactionError("Transaction type not composable.") transaction_args, common_args, _ = split_compose_arams(**kwargs) return compose_transaction(db, name=transaction_name, params=transaction_args, **common_args) + + +def info(db, rawtransaction, block_index=None): + # block_index mandatory for transactions before block 335000 + source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( + db, BlockchainParser().deserialize_tx(rawtransaction), block_index=block_index + ) + return { + "source": source, + "destination": destination, + "btc_amount": btc_amount, + "fee": fee, + "data": util.hexlify(data) if data else "", + } From 7d956f6e46e238e8766a510bd1556d1b9612cac3 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 12:53:33 +0200 Subject: [PATCH 024/128] Add backend proxy routes --- .../counterpartylib/lib/api/api_server.py | 20 +--------- .../counterpartylib/lib/api/routes.py | 37 ++++++++++++++++++- .../counterpartylib/lib/api/util.py | 34 ++++++++++++++++- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index 1f05736a70..f980b49a24 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -13,6 +13,7 @@ ledger, ) from counterpartylib.lib.api.routes import ROUTES +from counterpartylib.lib.api.util import remove_rowids from flask import Flask, request from flask import g as flask_globals from flask_httpauth import HTTPBasicAuth @@ -44,25 +45,6 @@ def init_api_access_log(): flask.cli.show_server_banner = lambda *args: None -def remove_rowids(query_result): - """Remove the rowid field from the query result.""" - if isinstance(query_result, list): - filtered_results = [] - for row in list(query_result): - if "rowid" in row: - del row["rowid"] - if "MAX(rowid)" in row: - del row["MAX(rowid)"] - filtered_results.append(row) - return filtered_results - filtered_results = query_result - if "rowid" in filtered_results: - del filtered_results["rowid"] - if "MAX(rowid)" in filtered_results: - del filtered_results["MAX(rowid)"] - return filtered_results - - def get_db(): """Get the database connection.""" if not hasattr(flask_globals, "db"): diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index d22f4b6576..7853b89b53 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -1,8 +1,10 @@ from counterpartylib.lib import ( + backend, + config, ledger, transaction, ) -from counterpartylib.lib.api.util import handle_healthz_route +from counterpartylib.lib.api import util ROUTES = { ### /blocks ### @@ -211,7 +213,38 @@ }, ### /healthz ### "/healthz": { - "function": handle_healthz_route, + "function": util.handle_healthz_route, "args": [("check", "heavy")], }, + ### /backend ### + "/backend/addresses/
/transactions": { + "function": backend.search_raw_transactions, + "args": [("unconfirmed", True), ("only_tx_hashes", False)], + }, + "/backend/addresses/
/transactions/oldest": { + "function": backend.get_oldest_tx, + }, + "/backend/addresses/
/utxos": { + "function": backend.get_unspent_txouts, + "args": [("unconfirmed", True), ("unspent_tx_hash", None)], + }, + "/backend/addresses/
/pubkey": { + "function": util.pubkeyhash_to_pubkey, + "args": [("provided_pubkeys", "")], + }, + "/backend/transactions": { + "function": util.getrawtransactions, + "args": [("tx_hashes", ""), ("verbose", False), ("skip_missing", False)], + }, + "/backend/transactions/": { + "function": backend.getrawtransaction, + "args": [("verbose", False), ("skip_missing", False)], + }, + "/backend/estimatesmartfee": { + "function": backend.fee_per_kb, + "args": [ + ("conf_target", config.ESTIMATE_FEE_CONF_TARGET), + ("mode", config.ESTIMATE_FEE_MODE), + ], + }, } diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index c7f92c6fe8..edbe404a89 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -1,7 +1,7 @@ import logging import flask -from counterparylib.lib import backend, config, exceptions, ledger, transaction +from counterpartylib.lib import backend, config, exceptions, ledger, transaction logger = logging.getLogger(config.LOGGER_NAME) @@ -44,3 +44,35 @@ def handle_healthz_route(db, check="heavy"): if not healthz(db, check): msg, code = "Unhealthy", 503 return flask.Response(msg, code, mimetype="application/json") + + +def remove_rowids(query_result): + """Remove the rowid field from the query result.""" + if isinstance(query_result, list): + filtered_results = [] + for row in list(query_result): + if "rowid" in row: + del row["rowid"] + if "MAX(rowid)" in row: + del row["MAX(rowid)"] + filtered_results.append(row) + return filtered_results + filtered_results = query_result + if "rowid" in filtered_results: + del filtered_results["rowid"] + if "MAX(rowid)" in filtered_results: + del filtered_results["MAX(rowid)"] + return filtered_results + + +def getrawtransactions(tx_hashes, verbose=False, skip_missing=False, _retry=0): + txhash_list = tx_hashes.split(",") + return backend.getrawtransaction_batch(txhash_list, verbose, skip_missing, _retry) + + +def pubkeyhash_to_pubkey(address, provided_pubkeys=None): + if provided_pubkeys: + provided_pubkeys_list = provided_pubkeys.split(",") + else: + provided_pubkeys_list = None + return backend.pubkeyhash_to_pubkey(address, provided_pubkeys=provided_pubkeys_list) From d890979d22159375d7239c85158f9ca85f620922 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 16:12:26 +0200 Subject: [PATCH 025/128] Add route for root --- .../counterpartylib/lib/api/api_server.py | 48 ++++++++++++++++++- .../counterpartylib/lib/api/routes.py | 1 + .../counterpartylib/lib/api/util.py | 20 +++++--- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index f980b49a24..ab1cc9f305 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -3,6 +3,7 @@ import multiprocessing from logging import handlers as logging_handlers from multiprocessing import Process +from threading import Timer import flask from counterpartylib import server @@ -13,7 +14,7 @@ ledger, ) from counterpartylib.lib.api.routes import ROUTES -from counterpartylib.lib.api.util import remove_rowids +from counterpartylib.lib.api.util import get_backend_height, remove_rowids from flask import Flask, request from flask import g as flask_globals from flask_httpauth import HTTPBasicAuth @@ -23,6 +24,10 @@ logger = logging.getLogger(config.LOGGER_NAME) auth = HTTPBasicAuth() +BACKEND_HEIGHT = 0 +REFRESH_BACKEND_HEIGHT_INTERVAL = 10 +BACKEND_HEIGHT_TIMER = None + def init_api_access_log(): """Initialize API logger.""" @@ -57,6 +62,27 @@ def verify_password(username, password): return username == config.RPC_USER and password == config.RPC_PASSWORD +@auth.login_required +def api_root(): + counterparty_height = blocks.last_db_index(get_db()) + routes = list(ROUTES.keys()) + network = "mainnet" + if config.TESTNET: + network = "testnet" + elif config.REGTEST: + network = "regtest" + elif config.TESTCOIN: + network = "testcoin" + return { + "server_ready": counterparty_height >= BACKEND_HEIGHT, + "network": network, + "version": config.VERSION_STRING, + "backend_height": BACKEND_HEIGHT, + "counterparty_height": counterparty_height, + "routes": routes, + } + + @auth.login_required def handle_route(**kwargs): route = ROUTES.get(str(request.url_rule.rule)) @@ -75,14 +101,32 @@ def run_api_server(args): # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) with app.app_context(): + # Initialise the API access log init_api_access_log() # Get the last block index ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) # Add routes + app.route("/")(api_root) for path in ROUTES.keys(): app.add_url_rule(path, view_func=handle_route) + # run the scheduler to refresh the backend height + refresh_backend_height() # Start the API server - app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) + try: + app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) + finally: + # ensure timer is cancelled + if BACKEND_HEIGHT_TIMER: + BACKEND_HEIGHT_TIMER.cancel() + + +def refresh_backend_height(): + global BACKEND_HEIGHT, BACKEND_HEIGHT_TIMER # noqa F811 + BACKEND_HEIGHT = get_backend_height() + if BACKEND_HEIGHT_TIMER: + BACKEND_HEIGHT_TIMER.cancel() + BACKEND_HEIGHT_TIMER = Timer(REFRESH_BACKEND_HEIGHT_INTERVAL, refresh_backend_height) + BACKEND_HEIGHT_TIMER.start() class APIServer(object): diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index 7853b89b53..e99e381278 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -6,6 +6,7 @@ ) from counterpartylib.lib.api import util +# Define the API routes except root (`/`) defined in `api_server.py` ROUTES = { ### /blocks ### "/blocks": { diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index edbe404a89..a717979349 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -57,12 +57,14 @@ def remove_rowids(query_result): del row["MAX(rowid)"] filtered_results.append(row) return filtered_results - filtered_results = query_result - if "rowid" in filtered_results: - del filtered_results["rowid"] - if "MAX(rowid)" in filtered_results: - del filtered_results["MAX(rowid)"] - return filtered_results + if isinstance(query_result, dict): + filtered_results = query_result + if "rowid" in filtered_results: + del filtered_results["rowid"] + if "MAX(rowid)" in filtered_results: + del filtered_results["MAX(rowid)"] + return filtered_results + return query_result def getrawtransactions(tx_hashes, verbose=False, skip_missing=False, _retry=0): @@ -76,3 +78,9 @@ def pubkeyhash_to_pubkey(address, provided_pubkeys=None): else: provided_pubkeys_list = None return backend.pubkeyhash_to_pubkey(address, provided_pubkeys=provided_pubkeys_list) + + +def get_backend_height(): + block_count = backend.getblockcount() + blocks_behind = backend.getindexblocksbehind() + return block_count + blocks_behind From 1e3a226a7e5a492212ecacd5c0fc7e1b8f509b5d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 18:38:58 +0200 Subject: [PATCH 026/128] fix tests --- counterparty-lib/counterpartylib/lib/api/api_server.py | 7 +++++-- counterparty-lib/counterpartylib/test/conftest.py | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index ab1cc9f305..d178ea466b 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -110,11 +110,14 @@ def run_api_server(args): for path in ROUTES.keys(): app.add_url_rule(path, view_func=handle_route) # run the scheduler to refresh the backend height - refresh_backend_height() - # Start the API server + # `no_refresh_backend_height` used only for testing. TODO: find a way to mock it + if "no_refresh_backend_height" not in args or not args["no_refresh_backend_height"]: + refresh_backend_height() try: + # Start the API server app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) finally: + pass # ensure timer is cancelled if BACKEND_HEIGHT_TIMER: BACKEND_HEIGHT_TIMER.cancel() diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index ffca6c8aa3..d1086d44b9 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -274,6 +274,7 @@ def api_server_v2(request, cp_server): "json_log": False, "no_check_asset_conservation": True, "action": "", + "no_refresh_backend_height": True, } server_config = ( default_config @@ -283,6 +284,7 @@ def api_server_v2(request, cp_server): "rpc_port": TEST_RPC_PORT + 10, } ) + args = argparse.Namespace(**server_config) api_server = api_v2.APIServer() api_server.start(args) From c506c0ad9d8436d34c6725eede454844c54e3515 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 19:03:36 +0200 Subject: [PATCH 027/128] inject headers --- .../counterpartylib/lib/api/api_server.py | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index d178ea466b..ff81eb9df8 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -62,7 +62,6 @@ def verify_password(username, password): return username == config.RPC_USER and password == config.RPC_PASSWORD -@auth.login_required def api_root(): counterparty_height = blocks.last_db_index(get_db()) routes = list(ROUTES.keys()) @@ -83,17 +82,38 @@ def api_root(): } +def inject_headers(db, result): + response = flask.make_response(flask.jsonify(result)) + if not config.RPC_NO_ALLOW_CORS: + response.headers["Access-Control-Allow-Origin"] = "*" + response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" + response.headers["Access-Control-Allow-Headers"] = ( + "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" + ) + counterparty_height = blocks.last_db_index(db) + response.headers["X-COUNTERPARTY-HEIGHT"] = counterparty_height + response.headers["X-COUNTERPARTY-READY"] = counterparty_height >= BACKEND_HEIGHT + response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT + return response + + @auth.login_required def handle_route(**kwargs): - route = ROUTES.get(str(request.url_rule.rule)) - function_args = dict(kwargs) - if "pass_all_args" in route and route["pass_all_args"]: - function_args = request.args | function_args - elif "args" in route: - for arg in route["args"]: - function_args[arg[0]] = request.args.get(arg[0], arg[1]) - result = route["function"](get_db(), **function_args) - return remove_rowids(result) + db = get_db() + rule = str(request.url_rule.rule) + if rule == "/": + result = api_root() + else: + route = ROUTES.get(rule) + function_args = dict(kwargs) + if "pass_all_args" in route and route["pass_all_args"]: + function_args = request.args | function_args + elif "args" in route: + for arg in route["args"]: + function_args[arg[0]] = request.args.get(arg[0], arg[1]) + result = route["function"](db, **function_args) + result = remove_rowids(result) + return inject_headers(db, result) def run_api_server(args): @@ -106,7 +126,7 @@ def run_api_server(args): # Get the last block index ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) # Add routes - app.route("/")(api_root) + app.add_url_rule("/", view_func=handle_route) for path in ROUTES.keys(): app.add_url_rule(path, view_func=handle_route) # run the scheduler to refresh the backend height From 7cb5a40b6d0c51489adc0bc5a35e97ed10319163 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 8 Apr 2024 21:31:11 +0200 Subject: [PATCH 028/128] Progress in unpack support --- .../counterpartylib/lib/api/api_v1.py | 5 +- .../counterpartylib/lib/messages/bet.py | 44 ++++++++++++-- .../counterpartylib/lib/messages/broadcast.py | 25 ++++++-- .../counterpartylib/lib/messages/btcpay.py | 21 +++++-- .../counterpartylib/lib/messages/cancel.py | 18 ++++-- .../counterpartylib/lib/messages/destroy.py | 4 +- .../counterpartylib/lib/messages/dispenser.py | 57 +++++++++++++++++-- .../counterpartylib/lib/messages/dividend.py | 35 ++++++++---- .../lib/messages/versions/enhanced_send.py | 4 +- .../counterpartylib/lib/transaction.py | 56 +++++++++++++++++- .../counterpartylib/test/util_test.py | 1 + 11 files changed, 227 insertions(+), 43 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index 63bdd7f170..d42fe71bbe 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -956,12 +956,11 @@ def unpack(data_hex): # TODO: Enabled only for `send`. if message_type_id == send.ID: - unpack_method = send.unpack + unpacked = send.unpack(self.db, message, ledger.CURRENT_BLOCK_INDEX) elif message_type_id == enhanced_send.ID: - unpack_method = enhanced_send.unpack + unpacked = enhanced_send.unpack(message, ledger.CURRENT_BLOCK_INDEX) else: raise APIError("unsupported message type") - unpacked = unpack_method(self.db, message, ledger.CURRENT_BLOCK_INDEX) return message_type_id, unpacked @dispatcher.add_method diff --git a/counterparty-lib/counterpartylib/lib/messages/bet.py b/counterparty-lib/counterpartylib/lib/messages/bet.py index c9fd2f0bd9..0a8208f9dc 100644 --- a/counterparty-lib/counterpartylib/lib/messages/bet.py +++ b/counterparty-lib/counterpartylib/lib/messages/bet.py @@ -403,9 +403,7 @@ def compose( return (source, [(feed_address, None)], data) -def parse(db, tx, message): - bet_parse_cursor = db.cursor() - +def unpack(message, return_dict=False): # Unpack message. try: if len(message) != LENGTH: @@ -429,9 +427,45 @@ def parse(db, tx, message): target_value, leverage, expiration, - fee_fraction_int, # noqa: F841 - ) = 0, 0, 0, 0, 0, 0, 0, 0 + ) = 0, 0, 0, 0, 0, 0, 0 status = "invalid: could not unpack" + if return_dict: + return { + "bet_type": bet_type, + "deadline": deadline, + "wager_quantity": wager_quantity, + "counterwager_quantity": counterwager_quantity, + "target_value": target_value, + "leverage": leverage, + "expiration": expiration, + "status": status, + } + return ( + bet_type, + deadline, + wager_quantity, + counterwager_quantity, + target_value, + leverage, + expiration, + status, + ) + + +def parse(db, tx, message): + bet_parse_cursor = db.cursor() + + # Unpack message. + ( + bet_type, + deadline, + wager_quantity, + counterwager_quantity, + target_value, + leverage, + expiration, + status, + ) = unpack(message) odds, fee_fraction = 0, 0 feed_address = tx["destination"] diff --git a/counterparty-lib/counterpartylib/lib/messages/broadcast.py b/counterparty-lib/counterpartylib/lib/messages/broadcast.py index ee43c67c3e..37168f9d61 100644 --- a/counterparty-lib/counterpartylib/lib/messages/broadcast.py +++ b/counterparty-lib/counterpartylib/lib/messages/broadcast.py @@ -162,12 +162,9 @@ def compose(db, source, timestamp, value, fee_fraction, text): return (source, [], data) -def parse(db, tx, message): - cursor = db.cursor() - - # Unpack message. +def unpack(message, block_index, return_dict=False): try: - if ledger.enabled("broadcast_pack_text", tx["block_index"]): + if ledger.enabled("broadcast_pack_text", block_index): timestamp, value, fee_fraction_int, rawtext = struct.unpack( FORMAT + f"{len(message) - LENGTH}s", message ) @@ -197,6 +194,24 @@ def parse(db, tx, message): except AssertionError: timestamp, value, fee_fraction_int, text = 0, None, 0, None status = "invalid: could not unpack text" + + if return_dict: + return { + "timestamp": timestamp, + "value": value, + "fee_fraction_int": fee_fraction_int, + "text": text, + "status": status, + } + return timestamp, value, fee_fraction_int, text, status + + +def parse(db, tx, message): + cursor = db.cursor() + + # Unpack message. + timestamp, value, fee_fraction_int, text, status = unpack(message, tx["block_index"]) + if status == "valid": # For SQLite3 timestamp = min(timestamp, config.MAX_INT) diff --git a/counterparty-lib/counterpartylib/lib/messages/btcpay.py b/counterparty-lib/counterpartylib/lib/messages/btcpay.py index ef02ee5b0b..76361656c8 100644 --- a/counterparty-lib/counterpartylib/lib/messages/btcpay.py +++ b/counterparty-lib/counterpartylib/lib/messages/btcpay.py @@ -133,10 +133,7 @@ def compose(db, source, order_match_id): return (source, [(destination, btc_quantity)], data) -def parse(db, tx, message): - cursor = db.cursor() - - # Unpack message. +def unpack(message, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError @@ -151,6 +148,22 @@ def parse(db, tx, message): tx0_hash, tx1_hash, order_match_id = None, None, None status = "invalid: could not unpack" + if return_dict: + return { + "tx0_hash": tx0_hash, + "tx1_hash": tx1_hash, + "order_match_id": order_match_id, + "status": status, + } + return tx0_hash, tx1_hash, order_match_id, status + + +def parse(db, tx, message): + cursor = db.cursor() + + # Unpack message. + tx0_hash, tx1_hash, order_match_id, status = unpack(message) + if status == "valid": destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems = ( validate(db, tx["source"], order_match_id, tx["block_index"]) diff --git a/counterparty-lib/counterpartylib/lib/messages/cancel.py b/counterparty-lib/counterpartylib/lib/messages/cancel.py index 5b3bbde4ea..e476526ec0 100644 --- a/counterparty-lib/counterpartylib/lib/messages/cancel.py +++ b/counterparty-lib/counterpartylib/lib/messages/cancel.py @@ -94,10 +94,7 @@ def compose(db, source, offer_hash): return (source, [], data) -def parse(db, tx, message): - cursor = db.cursor() - - # Unpack message. +def unpack(message, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError @@ -107,6 +104,19 @@ def parse(db, tx, message): except (exceptions.UnpackError, struct.error) as e: # noqa: F841 offer_hash = None status = "invalid: could not unpack" + if return_dict: + return { + "offer_hash": offer_hash, + "status": status, + } + return offer_hash, status + + +def parse(db, tx, message): + cursor = db.cursor() + + # Unpack message. + offer_hash, status = unpack(message) if status == "valid": offer, offer_type, problems = validate(db, tx["source"], offer_hash) diff --git a/counterparty-lib/counterpartylib/lib/messages/destroy.py b/counterparty-lib/counterpartylib/lib/messages/destroy.py index bec442735c..cafc97bd83 100644 --- a/counterparty-lib/counterpartylib/lib/messages/destroy.py +++ b/counterparty-lib/counterpartylib/lib/messages/destroy.py @@ -68,7 +68,7 @@ def pack(db, asset, quantity, tag): return data -def unpack(db, message): +def unpack(db, message, return_dict=False): try: asset_id, quantity = struct.unpack(FORMAT, message[0:16]) tag = message[16:] @@ -80,6 +80,8 @@ def unpack(db, message): except AssetIDError: # noqa: F405 raise UnpackError("asset id invalid") # noqa: B904, F405 + if return_dict: + return {"asset": asset, "quantity": quantity, "tag": tag} return asset, quantity, tag diff --git a/counterparty-lib/counterpartylib/lib/messages/dispenser.py b/counterparty-lib/counterpartylib/lib/messages/dispenser.py index 158fc1516c..b12d262f36 100644 --- a/counterparty-lib/counterpartylib/lib/messages/dispenser.py +++ b/counterparty-lib/counterpartylib/lib/messages/dispenser.py @@ -409,12 +409,9 @@ def calculate_oracle_fee( return oracle_fee_btc -def parse(db, tx, message): - cursor = db.cursor() - - # Unpack message. +def unpack(message, return_dict=False): try: - action_address = tx["source"] + action_address = None oracle_address = None assetid, give_quantity, escrow_quantity, mainchainrate, dispenser_status = struct.unpack( FORMAT, message[0:LENGTH] @@ -432,9 +429,57 @@ def parse(db, tx, message): asset = ledger.generate_asset_name(assetid, ledger.CURRENT_BLOCK_INDEX) status = "valid" except (exceptions.UnpackError, struct.error) as e: # noqa: F841 - assetid, give_quantity, mainchainrate, asset = None, None, None, None + ( + give_quantity, + escrow_quantity, + mainchainrate, + dispenser_status, + action_address, + oracle_address, + asset, + ) = None, None, None, None, None, None, None status = "invalid: could not unpack" + if return_dict: + return { + "asset": asset, + "give_quantity": give_quantity, + "escrow_quantity": escrow_quantity, + "mainchainrate": mainchainrate, + "dispenser_status": dispenser_status, + "action_address": action_address, + "oracle_address": oracle_address, + "status": status, + } + return ( + asset, + give_quantity, + escrow_quantity, + mainchainrate, + dispenser_status, + action_address, + oracle_address, + status, + ) + + +def parse(db, tx, message): + cursor = db.cursor() + + # Unpack message. + ( + asset, + give_quantity, + escrow_quantity, + mainchainrate, + dispenser_status, + action_address, + oracle_address, + status, + ) = unpack(message) + if action_address is None: + action_address = tx["source"] + if status == "valid": if ledger.enabled("dispenser_parsing_validation", ledger.CURRENT_BLOCK_INDEX): asset_id, problems = validate( diff --git a/counterparty-lib/counterpartylib/lib/messages/dividend.py b/counterparty-lib/counterpartylib/lib/messages/dividend.py index ec6a868aa2..494b88b29e 100644 --- a/counterparty-lib/counterpartylib/lib/messages/dividend.py +++ b/counterparty-lib/counterpartylib/lib/messages/dividend.py @@ -207,23 +207,16 @@ def compose(db, source, quantity_per_unit, asset, dividend_asset): return (source, [], data) -def parse(db, tx, message): - dividend_parse_cursor = db.cursor() - - fee = 0 - - # Unpack message. +def unpack(db, message, block_index, return_dict=False): try: - if (tx["block_index"] > 288150 or config.TESTNET or config.REGTEST) and len( - message - ) == LENGTH_2: + if (block_index > 288150 or config.TESTNET or config.REGTEST) and len(message) == LENGTH_2: quantity_per_unit, asset_id, dividend_asset_id = struct.unpack(FORMAT_2, message) - asset = ledger.get_asset_name(db, asset_id, tx["block_index"]) - dividend_asset = ledger.get_asset_name(db, dividend_asset_id, tx["block_index"]) + asset = ledger.get_asset_name(db, asset_id, block_index) + dividend_asset = ledger.get_asset_name(db, dividend_asset_id, block_index) status = "valid" elif len(message) == LENGTH_1: quantity_per_unit, asset_id = struct.unpack(FORMAT_1, message) - asset = ledger.get_asset_name(db, asset_id, tx["block_index"]) + asset = ledger.get_asset_name(db, asset_id, block_index) dividend_asset = config.XCP status = "valid" else: @@ -232,6 +225,24 @@ def parse(db, tx, message): dividend_asset, quantity_per_unit, asset = None, None, None status = "invalid: could not unpack" + if return_dict: + return { + "asset": asset, + "quantity_per_unit": quantity_per_unit, + "dividend_asset": dividend_asset, + "status": status, + } + return asset, quantity_per_unit, dividend_asset, status + + +def parse(db, tx, message): + dividend_parse_cursor = db.cursor() + + fee = 0 + + # Unpack message. + asset, quantity_per_unit, dividend_asset, status = unpack(db, message, tx["block_index"]) + if dividend_asset == config.BTC: status = f"invalid: cannot pay {config.BTC} dividends within protocol" diff --git a/counterparty-lib/counterpartylib/lib/messages/versions/enhanced_send.py b/counterparty-lib/counterpartylib/lib/messages/versions/enhanced_send.py index d46e49d835..022d62f46e 100644 --- a/counterparty-lib/counterpartylib/lib/messages/versions/enhanced_send.py +++ b/counterparty-lib/counterpartylib/lib/messages/versions/enhanced_send.py @@ -14,7 +14,7 @@ ID = 2 # 0x02 -def unpack(db, message, block_index): +def unpack(message, block_index): try: # account for memo bytes memo_bytes_length = len(message) - LENGTH @@ -150,7 +150,7 @@ def parse(db, tx, message): # Unpack message. try: - unpacked = unpack(db, message, tx["block_index"]) + unpacked = unpack(message, tx["block_index"]) asset, quantity, destination, memo_bytes = ( unpacked["asset"], unpacked["quantity"], diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index 184bf039d4..c00e79a415 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -32,6 +32,8 @@ exceptions, gettxinfo, ledger, + message_type, + messages, script, util, ) @@ -1006,6 +1008,7 @@ def split_compose_arams(**kwargs): "burn", "cancel", "destroy", + "dispenser", "dividend", "issuance", "order", @@ -1013,7 +1016,6 @@ def split_compose_arams(**kwargs): "rps", "rpsresolve", "sweep", - "dispenser", ] @@ -1036,3 +1038,55 @@ def info(db, rawtransaction, block_index=None): "fee": fee, "data": util.hexlify(data) if data else "", } + + +def unpack(db, data_hex): + data = binascii.unhexlify(data_hex) + message_type_id, message = message_type.unpack(data) + + # Unknown message type + message_data = {"error": "Unknown message type"} + # Bet + if message_type_id == messages.bet.ID: + message_type = "bet" + message_data = messages.bet.unpack(message, return_dict=True) + # Broadcast + elif message_type_id == messages.broadcast.ID: + message_type = "broadcast" + message_data = messages.broadcast.unpack( + message, ledger.CURRENT_BLOCK_INDEX, return_dict=True + ) + # BTCPay + elif message_type_id == messages.btcpay.ID: + message_type = "btcpay" + message_data = messages.btcpay.unpack(message, return_dict=True) + # Cancel + elif message_type_id == messages.cancel.ID: + message_type = "cancel" + message_data = messages.cancel.unpack(message, return_dict=True) + # Destroy + elif message_type_id == messages.destroy.ID: + message_type = "destroy" + message_data = messages.destroy.unpack(db, message, return_dict=True) + # Dispenser + elif message_type_id == messages.dispenser.ID: + message_type = "dispenser" + message_data = messages.dispenser.unpack(message, return_dict=True) + # Dividend + elif message_type_id == messages.dividend.ID: + message_type = "dividend" + message_data = messages.dividend.unpack(db, message, return_dict=True) + # Send + elif message_type_id == messages.send.ID: + message_type = "send" + message_data = messages.send.unpack(db, message, ledger.CURRENT_BLOCK_INDEX) + # Enhanced send + elif message_type_id == messages.versions.enhanced_send.ID: + message_type = "enhanced_send" + message_data = messages.versions.enhanced_send.unpack(message, ledger.CURRENT_BLOCK_INDEX) + + return { + "message_type": message_type, + "message_type_id": message_type_id, + "message_data": message_data, + } diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index bedfbf81a6..986ba3c16b 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -777,6 +777,7 @@ def exec_tested_method(tx_name, method, tested_method, inputs, server_db): or tx_name == "backend" or tx_name == "message_type" or tx_name == "address" + or (tx_name == "versions.enhanced_send" and method == "unpack") ): return tested_method(*inputs) else: From 71cfb0c48ffe56e25c9bbd70c3064f63ea7e0f90 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 12:33:13 +0200 Subject: [PATCH 029/128] Add route to unpack; supports all type of message --- .../counterpartylib/lib/api/routes.py | 4 + .../counterpartylib/lib/messages/issuance.py | 89 +++++++++++++++---- .../counterpartylib/lib/messages/order.py | 30 +++++-- .../counterpartylib/lib/messages/rps.py | 20 ++++- .../lib/messages/rpsresolve.py | 21 ++++- .../counterpartylib/lib/messages/sweep.py | 4 +- .../lib/messages/versions/mpma.py | 4 +- .../counterpartylib/lib/transaction.py | 46 ++++++++-- .../counterpartylib/test/fixtures/vectors.py | 4 - .../counterpartylib/test/util_test.py | 2 + 10 files changed, 179 insertions(+), 45 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index e99e381278..fb73e6e4fd 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -70,6 +70,10 @@ "function": transaction.info, "args": [("rawtransaction", None), ("block_index", None)], }, + "/transactions/unpack": { + "function": transaction.unpack, + "args": [("datahex", None), ("block_index", None)], + }, "/transactions/": { "function": ledger.get_transaction, }, diff --git a/counterparty-lib/counterpartylib/lib/messages/issuance.py b/counterparty-lib/counterpartylib/lib/messages/issuance.py index c0469aa0d2..8c2b7e0280 100644 --- a/counterparty-lib/counterpartylib/lib/messages/issuance.py +++ b/counterparty-lib/counterpartylib/lib/messages/issuance.py @@ -555,27 +555,26 @@ def compose(db, source, transfer_destination, asset, quantity, divisible, lock, return (source, destination_outputs, data) -def parse(db, tx, message, message_type_id): - issuance_parse_cursor = db.cursor() +def unpack(db, message, message_type_id, block_index, return_dict=False): asset_format = ledger.get_value_by_block_index( - "issuance_asset_serialization_format", tx["block_index"] + "issuance_asset_serialization_format", block_index ) asset_format_length = ledger.get_value_by_block_index( - "issuance_asset_serialization_length", tx["block_index"] + "issuance_asset_serialization_length", block_index ) subasset_format = ledger.get_value_by_block_index( - "issuance_subasset_serialization_format", tx["block_index"] + "issuance_subasset_serialization_format", block_index ) subasset_format_length = ledger.get_value_by_block_index( - "issuance_subasset_serialization_length", tx["block_index"] + "issuance_subasset_serialization_length", block_index ) # Unpack message. try: subasset_longname = None if message_type_id == LR_SUBASSET_ID or message_type_id == SUBASSET_ID: - if not ledger.enabled("subassets", block_index=tx["block_index"]): - logger.warning(f"subassets are not enabled at block {tx['block_index']}") + if not ledger.enabled("subassets", block_index=block_index): + logger.warning(f"subassets are not enabled at block {block_index}") raise exceptions.UnpackError # parse a subasset original issuance message @@ -597,9 +596,7 @@ def parse(db, tx, message, message_type_id): description_length = len(message) - subasset_format_length - compacted_subasset_length if description_length < 0: - logger.warning( - f"invalid subasset length: [issuance] tx [{tx['tx_hash']}]: {compacted_subasset_length}" - ) + logger.warning(f"invalid subasset length: {compacted_subasset_length}") raise exceptions.UnpackError messages_format = f">{compacted_subasset_length}s{description_length}s" compacted_subasset_longname, description = struct.unpack( @@ -618,7 +615,7 @@ def parse(db, tx, message, message_type_id): description = None except UnicodeDecodeError: description = "" - elif (tx["block_index"] > 283271 or config.TESTNET or config.REGTEST) and len( + elif (block_index > 283271 or config.TESTNET or config.REGTEST) and len( message ) >= asset_format_length: # Protocol change. if (len(message) - asset_format_length <= 42) and not ledger.enabled( @@ -688,11 +685,11 @@ def parse(db, tx, message, message_type_id): "", ) try: - asset = ledger.generate_asset_name(asset_id, tx["block_index"]) + asset = ledger.generate_asset_name(asset_id, block_index) ##This is for backwards compatibility with assets names longer than 12 characters if asset.startswith("A"): - named_asset = ledger.get_asset_name(db, asset_id, tx["block_index"]) + named_asset = ledger.get_asset_name(db, asset_id, block_index) if named_asset != 0: asset = named_asset @@ -708,7 +705,21 @@ def parse(db, tx, message, message_type_id): asset = None status = "invalid: bad asset name" except exceptions.UnpackError as e: # noqa: F841 - asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description = ( + ( + asset_id, + asset, + subasset_longname, + quantity, + divisible, + lock, + reset, + callable_, + call_date, + call_price, + description, + ) = ( + None, + None, None, None, None, @@ -721,6 +732,52 @@ def parse(db, tx, message, message_type_id): ) status = "invalid: could not unpack" + if return_dict: + return { + "asset_id": asset_id, + "asset": asset, + "subasset_longname": subasset_longname, + "quantity": quantity, + "divisible": divisible, + "lock": lock, + "reset": reset, + "callable": callable_, + "call_date": call_date, + "call_price": call_price, + "description": description, + "status": status, + } + return ( + asset_id, + asset, + subasset_longname, + quantity, + divisible, + lock, + reset, + callable_, + call_date, + call_price, + description, + status, + ) + + +def parse(db, tx, message, message_type_id): + ( + asset_id, + asset, + subasset_longname, + quantity, + divisible, + lock, + reset, + callable_, + call_date, + call_price, + description, + status, + ) = unpack(db, message, message_type_id, tx["block_index"]) # parse and validate the subasset from the message subasset_parent = None if status == "valid" and subasset_longname is not None: # Protocol change. @@ -932,5 +989,3 @@ def parse(db, tx, message, message_type_id): action="issuance", event=tx["tx_hash"], ) - - issuance_parse_cursor.close() diff --git a/counterparty-lib/counterpartylib/lib/messages/order.py b/counterparty-lib/counterpartylib/lib/messages/order.py index adfdfcdf39..d8ead13c94 100644 --- a/counterparty-lib/counterpartylib/lib/messages/order.py +++ b/counterparty-lib/counterpartylib/lib/messages/order.py @@ -470,18 +470,15 @@ def compose( return (source, [], data) -def parse(db, tx, message): - order_parse_cursor = db.cursor() - - # Unpack message. +def unpack(db, message, block_index, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError give_id, give_quantity, get_id, get_quantity, expiration, fee_required = struct.unpack( FORMAT, message ) - give_asset = ledger.get_asset_name(db, give_id, tx["block_index"]) - get_asset = ledger.get_asset_name(db, get_id, tx["block_index"]) + give_asset = ledger.get_asset_name(db, give_id, block_index) + get_asset = ledger.get_asset_name(db, get_id, block_index) status = "open" except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e: # noqa: F841 give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required = ( @@ -494,6 +491,27 @@ def parse(db, tx, message): ) status = "invalid: could not unpack" + if return_dict: + return { + "give_asset": give_asset, + "give_quantity": give_quantity, + "get_asset": get_asset, + "get_quantity": get_quantity, + "expiration": expiration, + "fee_required": fee_required, + "status": status, + } + return give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, status + + +def parse(db, tx, message): + order_parse_cursor = db.cursor() + + # Unpack message. + (give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, status) = unpack( + db, message, tx["block_index"] + ) + price = 0 if status == "open": try: diff --git a/counterparty-lib/counterpartylib/lib/messages/rps.py b/counterparty-lib/counterpartylib/lib/messages/rps.py index 0aba236df1..8734186ccc 100644 --- a/counterparty-lib/counterpartylib/lib/messages/rps.py +++ b/counterparty-lib/counterpartylib/lib/messages/rps.py @@ -298,9 +298,7 @@ def compose(db, source, possible_moves, wager, move_random_hash, expiration): return (source, [], data) -def parse(db, tx, message): - rps_parse_cursor = db.cursor() - # Unpack message. +def upack(message, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError @@ -310,6 +308,22 @@ def parse(db, tx, message): (possible_moves, wager, move_random_hash, expiration) = 0, 0, "", 0 status = "invalid: could not unpack" + if return_dict: + return { + "possible_moves": possible_moves, + "wager": wager, + "move_random_hash": binascii.hexlify(move_random_hash).decode("utf8"), + "expiration": expiration, + "status": status, + } + return possible_moves, wager, move_random_hash, expiration, status + + +def parse(db, tx, message): + rps_parse_cursor = db.cursor() + # Unpack message. + possible_moves, wager, move_random_hash, expiration, status = upack(message) + if status == "open": move_random_hash = binascii.hexlify(move_random_hash).decode("utf8") # Overbet diff --git a/counterparty-lib/counterpartylib/lib/messages/rpsresolve.py b/counterparty-lib/counterpartylib/lib/messages/rpsresolve.py index a10fcc061f..4351f225cf 100644 --- a/counterparty-lib/counterpartylib/lib/messages/rpsresolve.py +++ b/counterparty-lib/counterpartylib/lib/messages/rpsresolve.py @@ -128,10 +128,7 @@ def compose(db, source, move, random, rps_match_id): return (source, [], data) -def parse(db, tx, message): - cursor = db.cursor() - - # Unpack message. +def unpack(message, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError @@ -147,6 +144,22 @@ def parse(db, tx, message): move, random, tx0_hash, tx1_hash, rps_match_id = None, None, None, None, None status = "invalid: could not unpack" + if return_dict: + return { + "move": move, + "random": random, + "rps_match_id": rps_match_id, + "status": status, + } + return move, random, rps_match_id, status + + +def parse(db, tx, message): + cursor = db.cursor() + + # Unpack message. + move, random, rps_match_id, status = unpack(message) + if status == "valid": txn, rps_match, problems = validate(db, tx["source"], move, random, rps_match_id) if problems: diff --git a/counterparty-lib/counterpartylib/lib/messages/sweep.py b/counterparty-lib/counterpartylib/lib/messages/sweep.py index 51fad57cdf..30da525bd9 100644 --- a/counterparty-lib/counterpartylib/lib/messages/sweep.py +++ b/counterparty-lib/counterpartylib/lib/messages/sweep.py @@ -126,7 +126,7 @@ def compose(db, source, destination, flags, memo): return (source, [], data) -def unpack(db, message, block_index): +def unpack(message): try: memo_bytes_length = len(message) - LENGTH if memo_bytes_length < 0: @@ -162,7 +162,7 @@ def parse(db, tx, message): # Unpack message. try: - unpacked = unpack(db, message, tx["block_index"]) + unpacked = unpack(message) destination, flags, memo_bytes = ( unpacked["destination"], unpacked["flags"], diff --git a/counterparty-lib/counterpartylib/lib/messages/versions/mpma.py b/counterparty-lib/counterpartylib/lib/messages/versions/mpma.py index c6a145ccf4..b4a36a651d 100644 --- a/counterparty-lib/counterpartylib/lib/messages/versions/mpma.py +++ b/counterparty-lib/counterpartylib/lib/messages/versions/mpma.py @@ -21,7 +21,7 @@ ## expected functions for message version -def unpack(db, message, block_index): +def unpack(message, block_index): try: unpacked = _decode_mpma_send_decode(message, block_index) except struct.error as e: # noqa: F841 @@ -132,7 +132,7 @@ def compose(db, source, asset_dest_quant_list, memo, memo_is_hex): def parse(db, tx, message): try: - unpacked = unpack(db, message, tx["block_index"]) + unpacked = unpack(message, tx["block_index"]) status = "valid" except struct.error as e: # noqa: F841 status = "invalid: truncated message" diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index c00e79a415..ef18e49dec 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -1040,9 +1040,17 @@ def info(db, rawtransaction, block_index=None): } -def unpack(db, data_hex): - data = binascii.unhexlify(data_hex) +def unpack(db, datahex, block_index=None): + data = binascii.unhexlify(datahex) message_type_id, message = message_type.unpack(data) + block_index = block_index or ledger.CURRENT_BLOCK_INDEX + + issuance_ids = [ + messages.issuance.ID, + messages.issuance.LR_ISSUANCE_ID, + messages.issuance.SUBASSET_ID, + messages.issuance.LR_SUBASSET_ID, + ] # Unknown message type message_data = {"error": "Unknown message type"} @@ -1053,9 +1061,7 @@ def unpack(db, data_hex): # Broadcast elif message_type_id == messages.broadcast.ID: message_type = "broadcast" - message_data = messages.broadcast.unpack( - message, ledger.CURRENT_BLOCK_INDEX, return_dict=True - ) + message_data = messages.broadcast.unpack(message, block_index, return_dict=True) # BTCPay elif message_type_id == messages.btcpay.ID: message_type = "btcpay" @@ -1076,14 +1082,40 @@ def unpack(db, data_hex): elif message_type_id == messages.dividend.ID: message_type = "dividend" message_data = messages.dividend.unpack(db, message, return_dict=True) + # Issuance + elif message_type_id in issuance_ids: + message_type = "issuance" + message_data = messages.issuance.unpack( + db, message, message_type_id, block_index, return_dict=True + ) + # Order + elif message_type_id == messages.order.ID: + message_type = "order" + message_data = messages.order.unpack(db, message, block_index, return_dict=True) # Send elif message_type_id == messages.send.ID: message_type = "send" - message_data = messages.send.unpack(db, message, ledger.CURRENT_BLOCK_INDEX) + message_data = messages.send.unpack(db, message, block_index) # Enhanced send elif message_type_id == messages.versions.enhanced_send.ID: message_type = "enhanced_send" - message_data = messages.versions.enhanced_send.unpack(message, ledger.CURRENT_BLOCK_INDEX) + message_data = messages.versions.enhanced_send.unpack(message, block_index) + # MPMA send + elif message_type_id == messages.versions.mpma.ID: + message_type = "mpma_send" + message_data = messages.versions.mpma.unpack(message, block_index) + # RPS + elif message_type_id == messages.rps.ID: + message_type = "rps" + message_data = messages.rps.unpack(message, return_dict=True) + # RPS Resolve + elif message_type_id == messages.rpsresolve.ID: + message_type = "rpsresolve" + message_data = messages.rpsresolve.unpack(message, return_dict=True) + # Sweep + elif message_type_id == messages.sweep.ID: + message_type = "sweep" + message_data = messages.sweep.unpack(message) return { "message_type": message_type, diff --git a/counterparty-lib/counterpartylib/test/fixtures/vectors.py b/counterparty-lib/counterpartylib/test/fixtures/vectors.py index a548d8befd..544aacfbe2 100644 --- a/counterparty-lib/counterpartylib/test/fixtures/vectors.py +++ b/counterparty-lib/counterpartylib/test/fixtures/vectors.py @@ -6478,28 +6478,24 @@ { "in": ( b"o\x9c\x8d\x1fT\x05E\x1d\xe6\x07\x0b\xf1\xdb\x86\xabj\xcc\xb4\x95\xb6%\x01", - DP["default_block_index"], ), "out": {"destination": ADDR[5], "flags": 1, "memo": None}, }, { "in": ( b"o\x9c\x8d\x1fT\x05E\x1d\xe6\x07\x0b\xf1\xdb\x86\xabj\xcc\xb4\x95\xb6%\x02", - DP["default_block_index"], ), "out": {"destination": ADDR[5], "flags": 2, "memo": None}, }, { "in": ( b"o\x9c\x8d\x1fT\x05E\x1d\xe6\x07\x0b\xf1\xdb\x86\xabj\xcc\xb4\x95\xb6%\x03test", - DP["default_block_index"], ), "out": {"destination": ADDR[5], "flags": 3, "memo": "test"}, }, { "in": ( b"o\x9c\x8d\x1fT\x05E\x1d\xe6\x07\x0b\xf1\xdb\x86\xabj\xcc\xb4\x95\xb6%\x07\xca\xfe\xba\xbe", - DP["default_block_index"], ), "out": {"destination": ADDR[5], "flags": 7, "memo": b"\xca\xfe\xba\xbe"}, }, diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index 986ba3c16b..ddded47c28 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -778,6 +778,8 @@ def exec_tested_method(tx_name, method, tested_method, inputs, server_db): or tx_name == "message_type" or tx_name == "address" or (tx_name == "versions.enhanced_send" and method == "unpack") + or (tx_name == "versions.mpma" and method == "unpack") + or (tx_name == "sweep" and method == "unpack") ): return tested_method(*inputs) else: From a2878c60f9bbde98d01ec87e002df895e313131e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 12:40:04 +0200 Subject: [PATCH 030/128] update ledger.CURRENT_BLOCK_INDEX on each request --- .../counterpartylib/lib/api/api_server.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index ff81eb9df8..dadb014c5b 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -82,7 +82,7 @@ def api_root(): } -def inject_headers(db, result): +def inject_headers(result): response = flask.make_response(flask.jsonify(result)) if not config.RPC_NO_ALLOW_CORS: response.headers["Access-Control-Allow-Origin"] = "*" @@ -90,9 +90,8 @@ def inject_headers(db, result): response.headers["Access-Control-Allow-Headers"] = ( "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" ) - counterparty_height = blocks.last_db_index(db) - response.headers["X-COUNTERPARTY-HEIGHT"] = counterparty_height - response.headers["X-COUNTERPARTY-READY"] = counterparty_height >= BACKEND_HEIGHT + response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX + response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT return response @@ -100,6 +99,8 @@ def inject_headers(db, result): @auth.login_required def handle_route(**kwargs): db = get_db() + # update the current block index + ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(db) rule = str(request.url_rule.rule) if rule == "/": result = api_root() @@ -113,7 +114,7 @@ def handle_route(**kwargs): function_args[arg[0]] = request.args.get(arg[0], arg[1]) result = route["function"](db, **function_args) result = remove_rowids(result) - return inject_headers(db, result) + return inject_headers(result) def run_api_server(args): From 981d3a5ba7e4a1a0f039c1d79251d622aa84c9e5 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 13:53:34 +0200 Subject: [PATCH 031/128] new flags and config values for the new API --- counterparty-core/counterpartycore/server.py | 33 +++++++ .../counterpartylib/lib/api/api_server.py | 10 ++- .../counterpartylib/lib/config.py | 10 ++- counterparty-lib/counterpartylib/server.py | 90 ++++++++++++++++--- 4 files changed, 125 insertions(+), 18 deletions(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 5602c997cb..c622461f0a 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -132,6 +132,39 @@ {"default": "localhost", "help": "the hostname or IP of the indexd server"}, ], [("--indexd-port",), {"type": int, "help": "the indexd server port to connect to"}], + [ + ("--api-host",), + { + "default": "localhost", + "help": "the IP of the interface to bind to for providing API access (0.0.0.0 for all interfaces)", + }, + ], + [ + ("--api-port",), + {"type": int, "help": f"port on which to provide the {config.APP_NAME} API"}, + ], + [ + ("--api-user",), + { + "default": "rpc", + "help": f"required username to use the {config.APP_NAME} API (via HTTP basic auth)", + }, + ], + [ + ("--api-password",), + { + "default": "rpc", + "help": f"required password (for --api-user) to use the {config.APP_NAME} API (via HTTP basic auth)", + }, + ], + [ + ("--api-no-allow-cors",), + {"action": "store_true", "default": False, "help": "allow ajax cross domain request"}, + ], + [ + ("--api-not-ready-http-code",), + {"type": int, "default": 202, "help": "http code returned when server is not ready"}, + ], [ ("--rpc-host",), { diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index dadb014c5b..0caa62fc16 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -59,7 +59,7 @@ def get_db(): @auth.verify_password def verify_password(username, password): - return username == config.RPC_USER and password == config.RPC_PASSWORD + return username == config.API_USER and password == config.API_PASSWORD def api_root(): @@ -83,8 +83,10 @@ def api_root(): def inject_headers(result): - response = flask.make_response(flask.jsonify(result)) - if not config.RPC_NO_ALLOW_CORS: + server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT + http_code = 200 if server_ready else config.API_NOT_READY_HTTP_CODE + response = flask.make_response(flask.jsonify(result), http_code) + if not config.API_NO_ALLOW_CORS: response.headers["Access-Control-Allow-Origin"] = "*" response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" response.headers["Access-Control-Allow-Headers"] = ( @@ -136,7 +138,7 @@ def run_api_server(args): refresh_backend_height() try: # Start the API server - app.run(host=config.RPC_HOST, port=config.RPC_PORT, debug=False) + app.run(host=config.API_HOST, port=config.API_PORT, debug=False) finally: pass # ensure timer is cancelled diff --git a/counterparty-lib/counterpartylib/lib/config.py b/counterparty-lib/counterpartylib/lib/config.py index 001967825d..8101486e5d 100644 --- a/counterparty-lib/counterpartylib/lib/config.py +++ b/counterparty-lib/counterpartylib/lib/config.py @@ -53,9 +53,13 @@ FULL_APP_NAME = "Counterparty Core" LOGGER_NAME = APP_NAME -DEFAULT_RPC_PORT_REGTEST = 24000 -DEFAULT_RPC_PORT_TESTNET = 14000 -DEFAULT_RPC_PORT = 4000 +DEFAULT_API_PORT_REGTEST = 24000 +DEFAULT_API_PORT_TESTNET = 14000 +DEFAULT_API_PORT = 4000 + +DEFAULT_RPC_PORT_REGTEST = 24100 +DEFAULT_RPC_PORT_TESTNET = 14100 +DEFAULT_RPC_PORT = 4100 DEFAULT_BACKEND_PORT_REGTEST = 28332 DEFAULT_BACKEND_PORT_TESTNET = 18332 diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 35b61504f3..e32b7fca8a 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -58,11 +58,15 @@ def sigterm_handler(_signo, _stack_frame): assert False # noqa: B011 logger.info(f"Received {signal_name}.") - if "api_server" in globals(): + global_vars = globals() + if "api_server_v1" in global_vars: + logger.info("Stopping API server v1.") + global_vars["api_server_v1"].stop() + if "api_server" in global_vars: logger.info("Stopping API server.") - api_server.stop() # noqa: F821 - if "api_status_poller" in globals(): - api_status_poller.stop() # noqa: F821 + global_vars["api_server"].stop() + if "api_status_poller" in global_vars: + global_vars["api_status_poller"].stop() logger.info("Stopping backend.") backend.stop() @@ -182,6 +186,12 @@ def initialise_config( backend_ssl=False, backend_ssl_no_verify=False, backend_poll_interval=None, + api_host=None, + api_port=None, + api_user=None, + api_password=None, + api_no_allow_cors=False, + api_not_ready_http_code=202, rpc_host=None, rpc_port=None, rpc_user=None, @@ -450,6 +460,57 @@ def initialise_config( config.RPC_BATCH_SIZE = rpc_batch_size + # API V2 Settings + + if api_host: + config.API_HOST = api_host + else: + config.API_HOST = "localhost" + + if api_port: + config.API_PORT = rpc_port + else: + if config.TESTNET: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT_TESTNET + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT_TESTNET + elif config.REGTEST: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT_REGTEST + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT_REGTEST + else: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT + try: + config.API_PORT = int(config.API_PORT) + if not (int(config.API_PORT) > 1 and int(config.API_PORT) < 65535): + raise ConfigurationError("invalid server API port number") + except: # noqa: E722 + raise ConfigurationError( # noqa: B904 + "Please specific a valid port number api-port configuration parameter" + ) + + if api_user: + config.API_USER = api_user + else: + config.API_USER = "api" + + if api_password: + config.API_PASSWORD = api_password + else: + config.API_PASSWORD = "api" # noqa: S105 + + if api_no_allow_cors: + config.API_NO_ALLOW_CORS = api_no_allow_cors + else: + config.API_NO_ALLOW_CORS = False + + config.API_NOT_READY_HTTP_CODE = api_not_ready_http_code + ############## # OTHER SETTINGS @@ -564,6 +625,12 @@ def initialise_log_and_config(args): "backend_poll_interval": args.backend_poll_interval, "indexd_connect": args.indexd_connect, "indexd_port": args.indexd_port, + "api_host": args.api_host, + "api_port": args.api_port, + "api_user": args.api_user, + "api_password": args.api_password, + "api_no_allow_cors": args.api_no_allow_cors, + "api_not_ready_http_code": args.api_not_ready_http_code, "rpc_host": args.rpc_host, "rpc_port": args.rpc_port, "rpc_user": args.rpc_user, @@ -653,6 +720,7 @@ def connect_to_addrindexrs(): def start_all(args): + global api_server_v1, api_server, api_status_poller # noqa: PLW0603 # Backend. connect_to_backend() @@ -672,13 +740,13 @@ def start_all(args): api_status_poller.start() # API Server. - api_server = api_v1.APIServer() - api_server.daemon = True - api_server.start() - else: - # REST API Server. - api_server = api_v2.APIServer() - api_server.start(args) + api_server_v1 = api_v1.APIServer() + api_server_v1.daemon = True + api_server_v1.start() + + # REST API Server. + api_server = api_v2.APIServer() + api_server.start(args) # Server blocks.follow(db) From 91e69d14ae0f52929da49f7cc2ec580b7a42fa47 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 14:32:36 +0200 Subject: [PATCH 032/128] fix tests --- counterparty-core/counterpartycore/server.py | 4 ++-- counterparty-lib/counterpartylib/lib/api/api_server.py | 1 + counterparty-lib/counterpartylib/server.py | 2 +- counterparty-lib/counterpartylib/test/api_v2_test.py | 2 +- counterparty-lib/counterpartylib/test/conftest.py | 7 ++++++- counterparty-lib/counterpartylib/test/util_test.py | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index c622461f0a..f0a2129ea5 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -146,14 +146,14 @@ [ ("--api-user",), { - "default": "rpc", + "default": "api", "help": f"required username to use the {config.APP_NAME} API (via HTTP basic auth)", }, ], [ ("--api-password",), { - "default": "rpc", + "default": "api", "help": f"required password (for --api-user) to use the {config.APP_NAME} API (via HTTP basic auth)", }, ], diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index 0caa62fc16..d7bcf01bf3 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -138,6 +138,7 @@ def run_api_server(args): refresh_backend_height() try: # Start the API server + print("API server started", config.API_HOST, config.API_PORT) app.run(host=config.API_HOST, port=config.API_PORT, debug=False) finally: pass diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index e32b7fca8a..2549647d1f 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -468,7 +468,7 @@ def initialise_config( config.API_HOST = "localhost" if api_port: - config.API_PORT = rpc_port + config.API_PORT = api_port else: if config.TESTNET: if config.TESTCOIN: diff --git a/counterparty-lib/counterpartylib/test/api_v2_test.py b/counterparty-lib/counterpartylib/test/api_v2_test.py index c808246d32..938c3464a8 100644 --- a/counterparty-lib/counterpartylib/test/api_v2_test.py +++ b/counterparty-lib/counterpartylib/test/api_v2_test.py @@ -14,7 +14,7 @@ FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" FIXTURE_DB = tempfile.gettempdir() + "/fixtures.unittest_fixture.db" -API_ROOT = "http://rpc:pass@localhost:10009" +API_ROOT = "http://api:api@localhost:10009" @pytest.mark.usefixtures("api_server_v2") diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index d1086d44b9..5bc910b40e 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -255,6 +255,11 @@ def api_server_v2(request, cp_server): "rpc_user": None, "rpc_password": None, "rpc_no_allow_cors": False, + "api_host": "localhost", + "api_user": "api", + "api_password": "api", + "api_no_allow_cors": False, + "api_not_ready_http_code": 202, "force": False, "requests_timeout": config.DEFAULT_REQUESTS_TIMEOUT, "rpc_batch_size": config.DEFAULT_RPC_BATCH_SIZE, @@ -281,7 +286,7 @@ def api_server_v2(request, cp_server): | util_test.COUNTERPARTYD_OPTIONS | { "database_file": request.module.FIXTURE_DB, - "rpc_port": TEST_RPC_PORT + 10, + "api_port": TEST_RPC_PORT + 10, } ) diff --git a/counterparty-lib/counterpartylib/test/util_test.py b/counterparty-lib/counterpartylib/test/util_test.py index ddded47c28..6d2093b965 100644 --- a/counterparty-lib/counterpartylib/test/util_test.py +++ b/counterparty-lib/counterpartylib/test/util_test.py @@ -67,6 +67,7 @@ "testcoin": False, "rpc_port": 9999, "rpc_password": "pass", + "api_password": "api", "backend_port": 18332, "backend_password": "pass", "backend_ssl_no_verify": True, From 9fb4df006d9d35cb0ebfb2cfd63bb74b39714caa Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 14:39:57 +0200 Subject: [PATCH 033/128] lint --- counterparty-lib/counterpartylib/lib/api/api_server.py | 3 +-- counterparty-lib/counterpartylib/lib/api/api_v1.py | 4 ++-- counterparty-lib/counterpartylib/lib/api/util.py | 3 +-- counterparty-lib/counterpartylib/lib/transaction.py | 8 ++++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index d7bcf01bf3..50b19af606 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -130,7 +130,7 @@ def run_api_server(args): ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) # Add routes app.add_url_rule("/", view_func=handle_route) - for path in ROUTES.keys(): + for path in ROUTES: app.add_url_rule(path, view_func=handle_route) # run the scheduler to refresh the backend height # `no_refresh_backend_height` used only for testing. TODO: find a way to mock it @@ -141,7 +141,6 @@ def run_api_server(args): print("API server started", config.API_HOST, config.API_PORT) app.run(host=config.API_HOST, port=config.API_PORT, debug=False) finally: - pass # ensure timer is cancelled if BACKEND_HEIGHT_TIMER: BACKEND_HEIGHT_TIMER.cancel() diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index d42fe71bbe..f1fcb0416a 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -522,7 +522,7 @@ def run(self): check_backend_state() code = 12 logger.debug("Checking database state.") - api_util.check_database_state(db, backend.getblockcount()) + api_util.check_last_parsed_block(db, backend.getblockcount()) self.last_database_check = time.time() except (BackendError, DatabaseError) as e: exception_name = e.__class__.__name__ @@ -789,7 +789,7 @@ def get_running_info(): latest_block_index = backend.getblockcount() try: - api_util.check_database_state(self.db, latest_block_index) + api_util.check_last_parsed_block(self.db, latest_block_index) except DatabaseError: caught_up = False else: diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index a717979349..b7d03fc23c 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -7,11 +7,10 @@ def check_last_parsed_block(blockcount): - f"""Checks {config.XCP_NAME} database to see if is caught up with backend.""" # noqa: B021 + """Checks database to see if is caught up with backend.""" if ledger.CURRENT_BLOCK_INDEX + 1 < blockcount: raise exceptions.DatabaseError(f"{config.XCP_NAME} database is behind backend.") logger.debug("Database state check passed.") - return def healthz(db, check_type="heavy"): diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index ef18e49dec..401086caff 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -991,13 +991,13 @@ def split_compose_arams(**kwargs): transaction_args = {} common_args = {} private_key_wif = None - for key in kwargs: + for key, value in kwargs.items(): if key in COMPOSE_COMMONS_ARGS: - common_args[key] = kwargs[key] + common_args[key] = value elif key == "privkey": - private_key_wif = kwargs[key] + private_key_wif = value else: - transaction_args[key] = kwargs[key] + transaction_args[key] = value return transaction_args, common_args, private_key_wif From b1b86ac7f8a359e4345e6b8814e60b7d1b2381ea Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 15:21:13 +0200 Subject: [PATCH 034/128] fix sql query --- counterparty-lib/counterpartylib/lib/ledger.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/ledger.py b/counterparty-lib/counterpartylib/lib/ledger.py index d3a8cffc4f..cc291915da 100644 --- a/counterparty-lib/counterpartylib/lib/ledger.py +++ b/counterparty-lib/counterpartylib/lib/ledger.py @@ -83,8 +83,7 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li if block_index is None and limit is None: limit = 100 if limit is not None: - limit = "LIMIT ?" - bindings.append(limit) + limit = f"LIMIT {int(limit)}" else: limit = "" # no sql injection here From 590b965e94ced5a7c2e6aa5bc93b232f600a5750 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 15:45:39 +0200 Subject: [PATCH 035/128] Fix api v1 shutdown; Fix typos --- .../counterpartylib/lib/api/api_server.py | 1 - counterparty-lib/counterpartylib/lib/api/api_v1.py | 14 ++++++++------ counterparty-lib/counterpartylib/server.py | 7 +++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index 50b19af606..405f94cea6 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -138,7 +138,6 @@ def run_api_server(args): refresh_backend_height() try: # Start the API server - print("API server started", config.API_HOST, config.API_PORT) app.run(host=config.API_HOST, port=config.API_PORT, debug=False) finally: # ensure timer is cancelled diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index f1fcb0416a..6449d11721 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -54,6 +54,7 @@ from flask_httpauth import HTTPBasicAuth from jsonrpc import dispatcher from jsonrpc.exceptions import JSONRPCDispatchException +from werkzeug.serving import make_server from xmltodict import unparse as serialize_to_xml D = decimal.Decimal @@ -507,7 +508,6 @@ def stop(self): def run(self): logger.debug("Starting API Status Poller.") global CURRENT_API_STATUS_CODE, CURRENT_API_STATUS_RESPONSE_JSON # noqa: PLW0603 - db = database.get_connection(read_only=True) while self.stop_event.is_set() != True: # noqa: E712 try: @@ -522,7 +522,7 @@ def run(self): check_backend_state() code = 12 logger.debug("Checking database state.") - api_util.check_last_parsed_block(db, backend.getblockcount()) + api_util.check_last_parsed_block(backend.getblockcount()) self.last_database_check = time.time() except (BackendError, DatabaseError) as e: exception_name = e.__class__.__name__ @@ -546,11 +546,10 @@ def __init__(self, db=None): self.db = db self.is_ready = False threading.Thread.__init__(self) - self.stop_event = threading.Event() def stop(self): + self.server.shutdown() self.join() - self.stop_event.set() def run(self): logger.info("Starting API Server.") @@ -789,7 +788,7 @@ def get_running_info(): latest_block_index = backend.getblockcount() try: - api_util.check_last_parsed_block(self.db, latest_block_index) + api_util.check_last_parsed_block(latest_block_index) except DatabaseError: caught_up = False else: @@ -1222,7 +1221,10 @@ def handle_rest(path_args, flask_request): # Run app server (blocking) self.is_ready = True - app.run(host=config.RPC_HOST, port=config.RPC_PORT, threaded=True) + self.server = make_server(config.RPC_HOST, config.RPC_PORT, app) + self.ctx = app.app_context() + self.ctx.push() + self.server.serve_forever() self.db.close() return diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 2549647d1f..4e4733c667 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -59,14 +59,17 @@ def sigterm_handler(_signo, _stack_frame): logger.info(f"Received {signal_name}.") global_vars = globals() + # API v1 if "api_server_v1" in global_vars: logger.info("Stopping API server v1.") global_vars["api_server_v1"].stop() + if "api_status_poller" in global_vars: + logger.info("Stopping API Status Pooler.") + global_vars["api_status_poller"].stop() + # API v2 if "api_server" in global_vars: logger.info("Stopping API server.") global_vars["api_server"].stop() - if "api_status_poller" in global_vars: - global_vars["api_status_poller"].stop() logger.info("Stopping backend.") backend.stop() From bc30b79be3f2eba2001976ad2219dc64a697e23b Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 15:55:31 +0200 Subject: [PATCH 036/128] Put API v1 behind '/old' --- counterparty-lib/counterpartylib/lib/api/api_v1.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index 6449d11721..75c93f9b32 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -1047,12 +1047,11 @@ def handle_healthz(): @conditional_decorator(auth.login_required, hasattr(config, "RPC_PASSWORD")) def handle_root(args_path): """Handle all paths, decide where to forward the query.""" + request_path = args_path.lower() if ( - args_path == "" - or args_path.startswith("api/") - or args_path.startswith("API/") - or args_path.startswith("rpc/") - or args_path.startswith("RPC/") + request_path == "old" + or request_path.startswith("old/api/") + or request_path.startswith("old/rpc/") ): if flask.request.method == "POST": # Need to get those here because it might not be available in this aux function. @@ -1065,7 +1064,7 @@ def handle_root(args_path): else: error = "Invalid method." return flask.Response(error, 405, mimetype="application/json") - elif args_path.startswith("rest/") or args_path.startswith("REST/"): + elif request_path.startswith("old/rest/"): if flask.request.method == "GET" or flask.request.method == "POST": # Pass the URL path without /REST/ part and Flask request object. rest_path = args_path.split("/", 1)[1] From 7a403e45fc27986468f09b8dc15a720197fae6e6 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 16:01:09 +0200 Subject: [PATCH 037/128] Add X-API-Warn header --- counterparty-lib/counterpartylib/lib/api/api_v1.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index 75c93f9b32..17898917a4 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -1083,6 +1083,7 @@ def handle_root(args_path): def handle_rpc_options(): response = flask.Response("", 204) _set_cors_headers(response) + response.headers["X-API-WARN"] = "Deprecated API" return response def handle_rpc_post(request_json): @@ -1122,6 +1123,7 @@ def handle_rpc_post(request_json): jsonrpc_response.json.encode(), 200, mimetype="application/json" ) _set_cors_headers(response) + response.headers["X-API-WARN"] = "Deprecated API" return response ###################### From d8b4b63b3bf18a5d923396431428a14a56507005 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 16:09:07 +0200 Subject: [PATCH 038/128] use 'flask_cors' for cors --- counterparty-lib/counterpartylib/lib/api/api_server.py | 9 +++------ counterparty-lib/requirements.txt | 1 + 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index 405f94cea6..c0e029f53e 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -17,6 +17,7 @@ from counterpartylib.lib.api.util import get_backend_height, remove_rowids from flask import Flask, request from flask import g as flask_globals +from flask_cors import CORS from flask_httpauth import HTTPBasicAuth multiprocessing.set_start_method("spawn", force=True) @@ -86,12 +87,6 @@ def inject_headers(result): server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT http_code = 200 if server_ready else config.API_NOT_READY_HTTP_CODE response = flask.make_response(flask.jsonify(result), http_code) - if not config.API_NO_ALLOW_CORS: - response.headers["Access-Control-Allow-Origin"] = "*" - response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" - response.headers["Access-Control-Allow-Headers"] = ( - "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" - ) response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT @@ -124,6 +119,8 @@ def run_api_server(args): # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) with app.app_context(): + if not config.API_NO_ALLOW_CORS: + CORS(app) # Initialise the API access log init_api_access_log() # Get the last block index diff --git a/counterparty-lib/requirements.txt b/counterparty-lib/requirements.txt index 9bf40d969d..d14b3c654a 100644 --- a/counterparty-lib/requirements.txt +++ b/counterparty-lib/requirements.txt @@ -4,6 +4,7 @@ setuptools-markdown==0.4.1 python-dateutil==2.8.2 Flask-HTTPAuth==4.8.0 Flask==3.0.0 +flask-cors==4.0.0 colorlog==6.8.0 json-rpc==1.15.0 pycoin==0.92.20230326 From 861411cf3e2390c27e7a3bb946b333571e565a75 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 16:14:56 +0200 Subject: [PATCH 039/128] fix config.RPC_WEBROOT --- counterparty-lib/counterpartylib/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 4e4733c667..0b1273aa8c 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -409,8 +409,8 @@ def initialise_config( else: config.RPC_HOST = "localhost" - # The web root directory for API calls, eg. localhost:14000/rpc/ - config.RPC_WEBROOT = "/rpc/" + # The web root directory for API calls, eg. localhost:14000/old/rpc/ + config.RPC_WEBROOT = "/old/rpc/" # Server API RPC port if rpc_port: From 366a0609baab85a680399d1073b712c3a2d22312 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 19:25:12 +0200 Subject: [PATCH 040/128] new routes for mempool --- counterparty-lib/counterpartylib/lib/api/routes.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index fb73e6e4fd..3c68e92903 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -28,12 +28,6 @@ "/blocks//events/": { "function": ledger.get_events, }, - "/blocks/mempool/events": { - "function": ledger.get_mempool_events, - }, - "/blocks/mempool/events/": { - "function": ledger.get_mempool_events, - }, "/blocks//credits": { "function": ledger.get_credits, }, @@ -252,4 +246,11 @@ ("mode", config.ESTIMATE_FEE_MODE), ], }, + ### /mempool ### + "/mempool/events": { + "function": ledger.get_mempool_events, + }, + "/mempool/events/": { + "function": ledger.get_mempool_events, + }, } From 7916b6287c2e3b90c01690f5c4ae1382ce182528 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 19:27:13 +0200 Subject: [PATCH 041/128] API_NOT_READY_HTTP_CODE=503 by default --- counterparty-core/counterpartycore/server.py | 2 +- counterparty-lib/counterpartylib/server.py | 2 +- counterparty-lib/counterpartylib/test/conftest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index f0a2129ea5..c408d342b3 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -163,7 +163,7 @@ ], [ ("--api-not-ready-http-code",), - {"type": int, "default": 202, "help": "http code returned when server is not ready"}, + {"type": int, "default": 503, "help": "http code returned when server is not ready"}, ], [ ("--rpc-host",), diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index 0b1273aa8c..b0975b7eb2 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -194,7 +194,7 @@ def initialise_config( api_user=None, api_password=None, api_no_allow_cors=False, - api_not_ready_http_code=202, + api_not_ready_http_code=503, rpc_host=None, rpc_port=None, rpc_user=None, diff --git a/counterparty-lib/counterpartylib/test/conftest.py b/counterparty-lib/counterpartylib/test/conftest.py index 5bc910b40e..f797b8096f 100644 --- a/counterparty-lib/counterpartylib/test/conftest.py +++ b/counterparty-lib/counterpartylib/test/conftest.py @@ -259,7 +259,7 @@ def api_server_v2(request, cp_server): "api_user": "api", "api_password": "api", "api_no_allow_cors": False, - "api_not_ready_http_code": 202, + "api_not_ready_http_code": 503, "force": False, "requests_timeout": config.DEFAULT_REQUESTS_TIMEOUT, "rpc_batch_size": config.DEFAULT_RPC_BATCH_SIZE, From 9ca45d62f8f79320fc1879fa07eb80a3374962a1 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 19:36:35 +0200 Subject: [PATCH 042/128] check -> check_type --- counterparty-lib/counterpartylib/lib/api/routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counterparty-lib/counterpartylib/lib/api/routes.py b/counterparty-lib/counterpartylib/lib/api/routes.py index 3c68e92903..c025935328 100644 --- a/counterparty-lib/counterpartylib/lib/api/routes.py +++ b/counterparty-lib/counterpartylib/lib/api/routes.py @@ -213,7 +213,7 @@ ### /healthz ### "/healthz": { "function": util.handle_healthz_route, - "args": [("check", "heavy")], + "args": [("check_type", "heavy")], }, ### /backend ### "/backend/addresses/
/transactions": { From f27531c48179de225269abea0184babb1d3bcb9e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 9 Apr 2024 19:47:35 +0200 Subject: [PATCH 043/128] lint --- .../counterpartylib/lib/api/api_v1.py | 2 ++ .../counterpartylib/lib/messages/rps.py | 4 +-- .../counterpartylib/lib/transaction.py | 34 +++++++++---------- counterparty-lib/counterpartylib/server.py | 6 ++-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index 17898917a4..bf1ec4a8ec 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -545,6 +545,8 @@ class APIServer(threading.Thread): def __init__(self, db=None): self.db = db self.is_ready = False + self.server = None + self.ctx = None threading.Thread.__init__(self) def stop(self): diff --git a/counterparty-lib/counterpartylib/lib/messages/rps.py b/counterparty-lib/counterpartylib/lib/messages/rps.py index 8734186ccc..571938c29d 100644 --- a/counterparty-lib/counterpartylib/lib/messages/rps.py +++ b/counterparty-lib/counterpartylib/lib/messages/rps.py @@ -298,7 +298,7 @@ def compose(db, source, possible_moves, wager, move_random_hash, expiration): return (source, [], data) -def upack(message, return_dict=False): +def unpack(message, return_dict=False): try: if len(message) != LENGTH: raise exceptions.UnpackError @@ -322,7 +322,7 @@ def upack(message, return_dict=False): def parse(db, tx, message): rps_parse_cursor = db.cursor() # Unpack message. - possible_moves, wager, move_random_hash, expiration, status = upack(message) + possible_moves, wager, move_random_hash, expiration, status = unpack(message) if status == "open": move_random_hash = binascii.hexlify(move_random_hash).decode("utf8") diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index 401086caff..e2b177ab77 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -1056,69 +1056,69 @@ def unpack(db, datahex, block_index=None): message_data = {"error": "Unknown message type"} # Bet if message_type_id == messages.bet.ID: - message_type = "bet" + message_type_name = "bet" message_data = messages.bet.unpack(message, return_dict=True) # Broadcast elif message_type_id == messages.broadcast.ID: - message_type = "broadcast" + message_type_name = "broadcast" message_data = messages.broadcast.unpack(message, block_index, return_dict=True) # BTCPay elif message_type_id == messages.btcpay.ID: - message_type = "btcpay" + message_type_name = "btcpay" message_data = messages.btcpay.unpack(message, return_dict=True) # Cancel elif message_type_id == messages.cancel.ID: - message_type = "cancel" + message_type_name = "cancel" message_data = messages.cancel.unpack(message, return_dict=True) # Destroy elif message_type_id == messages.destroy.ID: - message_type = "destroy" + message_type_name = "destroy" message_data = messages.destroy.unpack(db, message, return_dict=True) # Dispenser elif message_type_id == messages.dispenser.ID: - message_type = "dispenser" + message_type_name = "dispenser" message_data = messages.dispenser.unpack(message, return_dict=True) # Dividend elif message_type_id == messages.dividend.ID: - message_type = "dividend" - message_data = messages.dividend.unpack(db, message, return_dict=True) + message_type_name = "dividend" + message_data = messages.dividend.unpack(db, message, block_index, return_dict=True) # Issuance elif message_type_id in issuance_ids: - message_type = "issuance" + message_type_name = "issuance" message_data = messages.issuance.unpack( db, message, message_type_id, block_index, return_dict=True ) # Order elif message_type_id == messages.order.ID: - message_type = "order" + message_type_name = "order" message_data = messages.order.unpack(db, message, block_index, return_dict=True) # Send elif message_type_id == messages.send.ID: - message_type = "send" + message_type_name = "send" message_data = messages.send.unpack(db, message, block_index) # Enhanced send elif message_type_id == messages.versions.enhanced_send.ID: - message_type = "enhanced_send" + message_type_name = "enhanced_send" message_data = messages.versions.enhanced_send.unpack(message, block_index) # MPMA send elif message_type_id == messages.versions.mpma.ID: - message_type = "mpma_send" + message_type_name = "mpma_send" message_data = messages.versions.mpma.unpack(message, block_index) # RPS elif message_type_id == messages.rps.ID: - message_type = "rps" + message_type_name = "rps" message_data = messages.rps.unpack(message, return_dict=True) # RPS Resolve elif message_type_id == messages.rpsresolve.ID: - message_type = "rpsresolve" + message_type_name = "rpsresolve" message_data = messages.rpsresolve.unpack(message, return_dict=True) # Sweep elif message_type_id == messages.sweep.ID: - message_type = "sweep" + message_type_name = "sweep" message_data = messages.sweep.unpack(message) return { - "message_type": message_type, + "message_type": message_type_name, "message_type_id": message_type_id, "message_data": message_data, } diff --git a/counterparty-lib/counterpartylib/server.py b/counterparty-lib/counterpartylib/server.py index b0975b7eb2..08eec4e8bb 100755 --- a/counterparty-lib/counterpartylib/server.py +++ b/counterparty-lib/counterpartylib/server.py @@ -48,6 +48,8 @@ class ConfigurationError(Exception): SIGTERM_CALLBACKS = [] +global api_server_v1, api_server, api_status_poller # noqa: PLW0603 + def sigterm_handler(_signo, _stack_frame): if _signo == 15: @@ -492,10 +494,10 @@ def initialise_config( config.API_PORT = int(config.API_PORT) if not (int(config.API_PORT) > 1 and int(config.API_PORT) < 65535): raise ConfigurationError("invalid server API port number") - except: # noqa: E722 + except ConfigurationError as e: # noqa: E722 raise ConfigurationError( # noqa: B904 "Please specific a valid port number api-port configuration parameter" - ) + ) from e if api_user: config.API_USER = api_user From 4b0a7a6a23449639790f7072e36a593f416c2220 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 10 Apr 2024 11:02:14 +0200 Subject: [PATCH 044/128] fix rebase --- .../counterpartylib/lib/transaction.py | 120 ++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/transaction.py b/counterparty-lib/counterpartylib/lib/transaction.py index e2b177ab77..2e4d63b835 100644 --- a/counterparty-lib/counterpartylib/lib/transaction.py +++ b/counterparty-lib/counterpartylib/lib/transaction.py @@ -10,16 +10,8 @@ import inspect import io import logging -<<<<<<< HEAD -import math # noqa: F401 -import os # noqa: F401 -import re # noqa: F401 -import sys # noqa: F401 -import threading -import time # noqa: F401 -======= import sys ->>>>>>> 0595b1a1 (Add route to compose transactions) +import threading import bitcoin as bitcoinlib import cachetools @@ -1019,6 +1011,116 @@ def split_compose_arams(**kwargs): ] +def compose_transaction( + db, + name, + params, + encoding="auto", + fee_per_kb=None, + estimate_fee_per_kb=None, + regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, + multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, + op_return_value=config.DEFAULT_OP_RETURN_VALUE, + pubkey=None, + allow_unconfirmed_inputs=False, + fee=None, + fee_provided=0, + unspent_tx_hash=None, + custom_inputs=None, + dust_return_pubkey=None, + disable_utxo_locks=False, + extended_tx_info=False, + p2sh_source_multisig_pubkeys=None, + p2sh_source_multisig_pubkeys_required=None, + p2sh_pretx_txid=None, + old_style_api=True, + segwit=False, +): + """Create and return a transaction.""" + + # Get provided pubkeys. + if isinstance(pubkey, str): + provided_pubkeys = [pubkey] + elif isinstance(pubkey, list): + provided_pubkeys = pubkey + elif pubkey is None: + provided_pubkeys = [] + else: + raise exceptions.TransactionError("Invalid pubkey.") + + # Get additional pubkeys from `source` and `destination` params. + # Convert `source` and `destination` to pubkeyhash form. + for address_name in ["source", "destination"]: + if address_name in params: + address = params[address_name] + if isinstance(address, list): + # pkhshs = [] + # for addr in address: + # provided_pubkeys += script.extract_pubkeys(addr) + # pkhshs.append(script.make_pubkeyhash(addr)) + # params[address_name] = pkhshs + pass + else: + provided_pubkeys += script.extract_pubkeys(address) + params[address_name] = script.make_pubkeyhash(address) + + # Check validity of collected pubkeys. + for pubkey in provided_pubkeys: + if not script.is_fully_valid(binascii.unhexlify(pubkey)): + raise script.AddressError(f"invalid public key: {pubkey}") + + compose_method = sys.modules[f"counterpartylib.lib.messages.{name}"].compose + compose_params = inspect.getfullargspec(compose_method)[0] + missing_params = [p for p in compose_params if p not in params and p != "db"] + for param in missing_params: + params[param] = None + + # dont override fee_per_kb if specified + if fee_per_kb is not None: + estimate_fee_per_kb = False + else: + fee_per_kb = config.DEFAULT_FEE_PER_KB + + if "extended_tx_info" in params: + extended_tx_info = params["extended_tx_info"] + del params["extended_tx_info"] + + if "old_style_api" in params: + old_style_api = params["old_style_api"] + del params["old_style_api"] + + if "segwit" in params: + segwit = params["segwit"] + del params["segwit"] + + tx_info = compose_method(db, **params) + initialise(db) + return construct( + db, + tx_info, + encoding=encoding, + fee_per_kb=fee_per_kb, + estimate_fee_per_kb=estimate_fee_per_kb, + regular_dust_size=regular_dust_size, + multisig_dust_size=multisig_dust_size, + op_return_value=op_return_value, + provided_pubkeys=provided_pubkeys, + allow_unconfirmed_inputs=allow_unconfirmed_inputs, + exact_fee=fee, + fee_provided=fee_provided, + unspent_tx_hash=unspent_tx_hash, + custom_inputs=custom_inputs, + dust_return_pubkey=dust_return_pubkey, + disable_utxo_locks=disable_utxo_locks, + extended_tx_info=extended_tx_info, + p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, + p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required, + p2sh_pretx_txid=p2sh_pretx_txid, + old_style_api=old_style_api, + segwit=segwit, + ) + + def compose(db, transaction_name, **kwargs): if transaction_name not in COMPOSABLE_TRANSACTIONS: raise exceptions.TransactionError("Transaction type not composable.") From a4b4fd33461619b467f80b9092a994f29466b8d9 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 10 Apr 2024 11:10:15 +0200 Subject: [PATCH 045/128] fix ruff alert --- counterparty-lib/counterpartylib/lib/address.py | 11 ++++++++--- .../counterpartylib/test/estimate_fee_per_kb_test.py | 4 +--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/address.py b/counterparty-lib/counterpartylib/lib/address.py index 2f9b15abd9..3f8cd8a686 100644 --- a/counterparty-lib/counterpartylib/lib/address.py +++ b/counterparty-lib/counterpartylib/lib/address.py @@ -2,7 +2,8 @@ import struct # noqa: F401 import bitcoin -from counterpartylib.lib import config, ledger, script, exceptions # noqa: F401 + +from counterpartylib.lib import config, exceptions, ledger, script # noqa: F401 logger = logging.getLogger(config.LOGGER_NAME) @@ -58,10 +59,14 @@ def unpack(short_address_bytes): """ from .ledger import enabled # Here to account for test mock changes - if short_address_bytes == b'': + if short_address_bytes == b"": raise exceptions.UnpackError - if enabled('segwit_support') and short_address_bytes[0] >= 0x80 and short_address_bytes[0] <= 0x8F: + if ( + enabled("segwit_support") + and short_address_bytes[0] >= 0x80 + and short_address_bytes[0] <= 0x8F + ): # we have a segwit address here witver = short_address_bytes[0] - 0x80 witprog = short_address_bytes[1:] diff --git a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py index ef74ba3e64..9e19996bc2 100644 --- a/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py +++ b/counterparty-lib/counterpartylib/test/estimate_fee_per_kb_test.py @@ -4,8 +4,7 @@ import bitcoin as bitcoinlib -from counterpartylib.lib import (backend, transaction) -from counterpartylib.lib.api import api_v1 as api +from counterpartylib.lib import backend, transaction from counterpartylib.test import ( util_test, ) @@ -40,7 +39,6 @@ def _fee_per_kb(conf_target, mode): ) with util_test.ConfigContext(ESTIMATE_FEE_PER_KB=True): - transaction.initialise() txhex = transaction.compose_transaction( From 45998bdbfe8892f8c49a1fa4027aebb4e0cca3b6 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 10 Apr 2024 20:16:09 +0200 Subject: [PATCH 046/128] Fix log in file --- .../counterpartylib/lib/api/api_server.py | 26 ++----------------- .../counterpartylib/lib/api/api_v1.py | 24 +---------------- .../counterpartylib/lib/api/util.py | 19 ++++++++++++++ .../lib/backend/addrindexrs.py | 2 +- 4 files changed, 23 insertions(+), 48 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index c0e029f53e..b5a5d01cdd 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -1,7 +1,6 @@ import argparse import logging import multiprocessing -from logging import handlers as logging_handlers from multiprocessing import Process from threading import Timer @@ -14,7 +13,7 @@ ledger, ) from counterpartylib.lib.api.routes import ROUTES -from counterpartylib.lib.api.util import get_backend_height, remove_rowids +from counterpartylib.lib.api.util import get_backend_height, init_api_access_log, remove_rowids from flask import Flask, request from flask import g as flask_globals from flask_cors import CORS @@ -30,27 +29,6 @@ BACKEND_HEIGHT_TIMER = None -def init_api_access_log(): - """Initialize API logger.""" - werkzeug_loggers = (logging.getLogger("werkzeug"), flask.current_app.logger) - - # Disable console logging... - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - werkzeug_logger.setLevel(logging.CRITICAL) - werkzeug_logger.propagate = False - - # Log to file, if configured... - if config.API_LOG: - handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT - ) - for werkzeug_logger in werkzeug_loggers: # noqa: E741 - handler.setLevel(logging.DEBUG) - werkzeug_logger.addHandler(handler) - - flask.cli.show_server_banner = lambda *args: None - - def get_db(): """Get the database connection.""" if not hasattr(flask_globals, "db"): @@ -122,7 +100,7 @@ def run_api_server(args): if not config.API_NO_ALLOW_CORS: CORS(app) # Initialise the API access log - init_api_access_log() + init_api_access_log(app) # Get the last block index ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(get_db()) # Add routes diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index bf1ec4a8ec..b93d4155d0 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -15,7 +15,6 @@ import threading import time import traceback -from logging import handlers as logging_handlers import flask import jsonrpc @@ -473,27 +472,6 @@ def gen_decorator(f): return gen_decorator -def init_api_access_log(app): - """Initialize API logger.""" - loggers = (logging.getLogger("werkzeug"), app.logger) - - # Disable console logging... - for l in loggers: # noqa: E741 - l.setLevel(logging.CRITICAL) - l.propagate = False - - # Log to file, if configured... - if config.API_LOG: - handler = logging_handlers.RotatingFileHandler( - config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT - ) - for l in loggers: # noqa: E741 - handler.setLevel(logging.DEBUG) - l.addHandler(handler) - - flask.cli.show_server_banner = lambda *args: None - - class APIStatusPoller(threading.Thread): """Perform regular checks on the state of the backend and the database.""" @@ -1220,7 +1198,7 @@ def handle_rest(path_args, flask_request): return response # Init the HTTP Server. - init_api_access_log(app) + api_util.init_api_access_log(app) # Run app server (blocking) self.is_ready = True diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index b7d03fc23c..ea8d5c6a6e 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -1,4 +1,5 @@ import logging +from logging import handlers as logging_handlers import flask from counterpartylib.lib import backend, config, exceptions, ledger, transaction @@ -83,3 +84,21 @@ def get_backend_height(): block_count = backend.getblockcount() blocks_behind = backend.getindexblocksbehind() return block_count + blocks_behind + + +def init_api_access_log(flask_app): + """Initialize API logger.""" + flask_app.logger.removeHandler(flask.logging.default_handler) + flask_app.logger.setLevel(logging.DEBUG) + werkzeug_logger = logging.getLogger("werkzeug") + + # Log to file, if configured... + if config.API_LOG: + handler = logging_handlers.RotatingFileHandler( + config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT + ) + handler.setLevel(logging.DEBUG) + flask_app.logger.addHandler(handler) + werkzeug_logger.addHandler(handler) + + flask.cli.show_server_banner = lambda *args: None diff --git a/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py b/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py index 98701c4362..033e064a81 100644 --- a/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py +++ b/counterparty-lib/counterpartylib/lib/backend/addrindexrs.py @@ -528,7 +528,7 @@ def send(self, msg): def _run(self): while self.is_running: try: - logger.debug("AddrIndexRsClient.thread -- waiting for message") + # logger.debug("AddrIndexRsClient.thread -- waiting for message") # if there is no messager after 1 sec, it will raise queue.Empty msg = self.req_queue.get(timeout=1) self.socket_manager.send(msg) From d60760234e0f183ff8ed0b34d6fd34edc640baab Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 10 Apr 2024 20:37:25 +0200 Subject: [PATCH 047/128] Add warning when using API v1 --- counterparty-lib/counterpartylib/lib/api/api_v1.py | 14 ++++++-------- counterparty-lib/counterpartylib/lib/api/util.py | 2 ++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_v1.py b/counterparty-lib/counterpartylib/lib/api/api_v1.py index b93d4155d0..02f11647d1 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_v1.py +++ b/counterparty-lib/counterpartylib/lib/api/api_v1.py @@ -532,7 +532,7 @@ def stop(self): self.join() def run(self): - logger.info("Starting API Server.") + logger.info("Starting API Server v1.") self.db = self.db or database.get_connection(read_only=True) app = flask.Flask(__name__) auth = HTTPBasicAuth() @@ -1104,6 +1104,9 @@ def handle_rpc_post(request_json): ) _set_cors_headers(response) response.headers["X-API-WARN"] = "Deprecated API" + logger.warning( + "API v1 is deprecated and should be removed soon. Please migrate to REST API." + ) return response ###################### @@ -1198,17 +1201,12 @@ def handle_rest(path_args, flask_request): return response # Init the HTTP Server. - api_util.init_api_access_log(app) - - # Run app server (blocking) self.is_ready = True self.server = make_server(config.RPC_HOST, config.RPC_PORT, app) + api_util.init_api_access_log(app) self.ctx = app.app_context() self.ctx.push() + # Run app server (blocking) self.server.serve_forever() self.db.close() - return - - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index ea8d5c6a6e..be0e1cfa56 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -91,6 +91,8 @@ def init_api_access_log(flask_app): flask_app.logger.removeHandler(flask.logging.default_handler) flask_app.logger.setLevel(logging.DEBUG) werkzeug_logger = logging.getLogger("werkzeug") + while werkzeug_logger.hasHandlers(): + werkzeug_logger.removeHandler(werkzeug_logger.handlers[0]) # Log to file, if configured... if config.API_LOG: From 2945b0754bae4b7ad2c4d1390093f408ba559c46 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 10 Apr 2024 21:03:41 +0200 Subject: [PATCH 048/128] fix duplicate sigterm catch --- counterparty-lib/counterpartylib/lib/api/api_server.py | 5 +++++ counterparty-lib/counterpartylib/lib/api/util.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/counterparty-lib/counterpartylib/lib/api/api_server.py b/counterparty-lib/counterpartylib/lib/api/api_server.py index b5a5d01cdd..f97b215fec 100644 --- a/counterparty-lib/counterpartylib/lib/api/api_server.py +++ b/counterparty-lib/counterpartylib/lib/api/api_server.py @@ -1,6 +1,7 @@ import argparse import logging import multiprocessing +import signal from multiprocessing import Process from threading import Timer @@ -93,6 +94,10 @@ def handle_route(**kwargs): def run_api_server(args): + # default signal handlers + signal.signal(signal.SIGTERM, signal.SIG_DFL) + signal.signal(signal.SIGINT, signal.default_int_handler) + app = Flask(config.APP_NAME) # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) diff --git a/counterparty-lib/counterpartylib/lib/api/util.py b/counterparty-lib/counterpartylib/lib/api/util.py index be0e1cfa56..3585234367 100644 --- a/counterparty-lib/counterpartylib/lib/api/util.py +++ b/counterparty-lib/counterpartylib/lib/api/util.py @@ -91,7 +91,7 @@ def init_api_access_log(flask_app): flask_app.logger.removeHandler(flask.logging.default_handler) flask_app.logger.setLevel(logging.DEBUG) werkzeug_logger = logging.getLogger("werkzeug") - while werkzeug_logger.hasHandlers(): + while len(werkzeug_logger.handlers) > 0: werkzeug_logger.removeHandler(werkzeug_logger.handlers[0]) # Log to file, if configured... @@ -99,6 +99,7 @@ def init_api_access_log(flask_app): handler = logging_handlers.RotatingFileHandler( config.API_LOG, "a", config.API_MAX_LOG_SIZE, config.API_MAX_LOG_COUNT ) + handler.propagate = False handler.setLevel(logging.DEBUG) flask_app.logger.addHandler(handler) werkzeug_logger.addHandler(handler) From 69c522d42837e29085e57d65c75002c4c893ec39 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 12:56:26 +0200 Subject: [PATCH 049/128] fix merge --- counterparty-core/counterpartycore/lib/api/routes.py | 4 ++-- counterparty-core/counterpartycore/lib/api/util.py | 2 +- counterparty-core/counterpartycore/lib/transaction.py | 2 +- counterparty-core/requirements.txt | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index c025935328..b66613af7e 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -1,10 +1,10 @@ -from counterpartylib.lib import ( +from counterpartycore.lib import ( backend, config, ledger, transaction, ) -from counterpartylib.lib.api import util +from counterpartycore.lib.api import util # Define the API routes except root (`/`) defined in `api_server.py` ROUTES = { diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 3585234367..7d6e58b22a 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -2,7 +2,7 @@ from logging import handlers as logging_handlers import flask -from counterpartylib.lib import backend, config, exceptions, ledger, transaction +from counterpartycore.lib import backend, config, exceptions, ledger, transaction logger = logging.getLogger(config.LOGGER_NAME) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 22514d6928..df8ef8d255 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1071,7 +1071,7 @@ def compose_transaction( if not script.is_fully_valid(binascii.unhexlify(pubkey)): raise script.AddressError(f"invalid public key: {pubkey}") - compose_method = sys.modules[f"counterpartylib.lib.messages.{name}"].compose + compose_method = sys.modules[f"counterpartycore.lib.messages.{name}"].compose compose_params = inspect.getfullargspec(compose_method)[0] missing_params = [p for p in compose_params if p not in params and p != "db"] for param in missing_params: diff --git a/counterparty-core/requirements.txt b/counterparty-core/requirements.txt index 8bc89d009a..01267b95de 100644 --- a/counterparty-core/requirements.txt +++ b/counterparty-core/requirements.txt @@ -26,4 +26,5 @@ arc4==0.4.0 halo==0.0.31 termcolor==2.4.0 sentry-sdk==1.45.0 +Flask-Cors==4.0.0 counterparty-rs==10.1.0 From d3d62ac6a37a41d9aca4e64cce37ab23ca37398d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 15:40:14 +0200 Subject: [PATCH 050/128] fix merge --- counterparty-core/counterpartycore/cli.py | 93 ++++------ .../counterpartycore/lib/api/api_server.py | 1 + .../counterpartycore/lib/api/api_v1.py | 10 -- counterparty-core/counterpartycore/lib/log.py | 3 +- .../counterpartycore/lib/transaction.py | 18 +- counterparty-core/counterpartycore/server.py | 169 ++++++++++++++++-- .../test/complex_unit_test.py | 5 +- .../counterpartycore/test/conftest.py | 7 +- .../counterpartycore/test/fixtures/vectors.py | 2 +- 9 files changed, 216 insertions(+), 92 deletions(-) diff --git a/counterparty-core/counterpartycore/cli.py b/counterparty-core/counterpartycore/cli.py index a3fd2e3816..a5ebd04b49 100755 --- a/counterparty-core/counterpartycore/cli.py +++ b/counterparty-core/counterpartycore/cli.py @@ -7,7 +7,7 @@ from termcolor import cprint from counterpartycore import server -from counterpartycore.lib import config, log, setup +from counterpartycore.lib import config, setup logger = logging.getLogger(config.LOGGER_NAME) @@ -170,6 +170,35 @@ "help": f"number of RPC queries by batch (default: {config.DEFAULT_RPC_BATCH_SIZE})", }, ], + [ + ("--api-host",), + { + "default": "localhost", + "help": "the IP of the interface to bind to for providing API access (0.0.0.0 for all interfaces)", + }, + ], + [ + ("--api-port",), + {"type": int, "help": f"port on which to provide the {config.APP_NAME} API"}, + ], + [ + ("--api-user",), + { + "default": "api", + "help": f"required username to use the {config.APP_NAME} API (via HTTP basic auth)", + }, + ], + [ + ("--api-password",), + { + "default": "api", + "help": f"required password (for rpc-user) to use the {config.APP_NAME} API (via HTTP basic auth)", + }, + ], + [ + ("--api-no-allow-cors",), + {"action": "store_true", "default": False, "help": "allow ajax cross domain request"}, + ], [ ("--requests-timeout",), { @@ -204,6 +233,10 @@ "help": "log API requests to the specified file", }, ], + [ + ("--enable-api-v1",), + {"action": "store_true", "default": False, "help": "Enable the API v1"}, + ], [ ("--no-log-files",), {"action": "store_true", "default": False, "help": "Don't write log files"}, @@ -368,60 +401,8 @@ def main(): parser.print_help() exit(0) - # Configuration - init_args = dict( - database_file=args.database_file, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - customnet=args.customnet, - api_limit_rows=args.api_limit_rows, - backend_connect=args.backend_connect, - backend_port=args.backend_port, - backend_user=args.backend_user, - backend_password=args.backend_password, - backend_ssl=args.backend_ssl, - backend_ssl_no_verify=args.backend_ssl_no_verify, - backend_poll_interval=args.backend_poll_interval, - indexd_connect=args.indexd_connect, - indexd_port=args.indexd_port, - rpc_host=args.rpc_host, - rpc_port=args.rpc_port, - rpc_user=args.rpc_user, - rpc_password=args.rpc_password, - rpc_no_allow_cors=args.rpc_no_allow_cors, - requests_timeout=args.requests_timeout, - rpc_batch_size=args.rpc_batch_size, - check_asset_conservation=args.check_asset_conservation, - force=args.force, - p2sh_dust_return_pubkey=args.p2sh_dust_return_pubkey, - utxo_locks_max_addresses=args.utxo_locks_max_addresses, - utxo_locks_max_age=args.utxo_locks_max_age, - no_mempool=args.no_mempool, - skip_db_check=args.skip_db_check, - ) - - server.initialise_log_config( - verbose=args.verbose, - quiet=args.quiet, - log_file=args.log_file, - api_log_file=args.api_log_file, - no_log_files=args.no_log_files, - testnet=args.testnet, - testcoin=args.testcoin, - regtest=args.regtest, - json_log=args.json_log, - ) - - # set up logging - log.set_up( - verbose=config.VERBOSE, - quiet=config.QUIET, - log_file=config.LOG, - log_in_console=args.action == "start", - ) - - server.initialise_config(**init_args) + # Configuration and logging + server.initialise_log_and_config(args) logger.info(f"Running v{APP_VERSION} of {APP_NAME}.") @@ -447,7 +428,7 @@ def main(): ) elif args.action == "start": - server.start_all(catch_up=args.catch_up) + server.start_all(args) elif args.action == "show-params": server.show_params() diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 3cccf3cdc5..063f6b94fd 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -146,6 +146,7 @@ def start(self, args): return self.process def stop(self): + logger.info("Stopping API server v2...") if self.process and self.process.is_alive(): self.process.terminate() self.process = None diff --git a/counterparty-core/counterpartycore/lib/api/api_v1.py b/counterparty-core/counterpartycore/lib/api/api_v1.py index 18f65d558d..ddccf65fd5 100644 --- a/counterparty-core/counterpartycore/lib/api/api_v1.py +++ b/counterparty-core/counterpartycore/lib/api/api_v1.py @@ -8,7 +8,6 @@ import binascii import collections import decimal -import inspect import json import logging import math @@ -483,15 +482,6 @@ def adjust_get_transactions_results(query_result): return filtered_results -def get_default_args(func): - signature = inspect.signature(func) - return { - k: v.default - for k, v in signature.parameters.items() - if v.default is not inspect.Parameter.empty - } - - def conditional_decorator(decorator, condition): """Checks the condition and if True applies specified decorator.""" diff --git a/counterparty-core/counterpartycore/lib/log.py b/counterparty-core/counterpartycore/lib/log.py index 1df6153e37..4ff3cea510 100644 --- a/counterparty-core/counterpartycore/lib/log.py +++ b/counterparty-core/counterpartycore/lib/log.py @@ -3,6 +3,7 @@ import sys import traceback from datetime import datetime +from logging.handlers import RotatingFileHandler from colorlog import ColoredFormatter from dateutil.tz import tzlocal @@ -34,7 +35,7 @@ def set_up(verbose=False, quiet=True, log_file=None, log_in_console=False): # File Logging if log_file: max_log_size = 20 * 1024 * 1024 # 20 MB - fileh = logging.handlers.RotatingFileHandler(log_file, maxBytes=max_log_size, backupCount=5) + fileh = RotatingFileHandler(log_file, maxBytes=max_log_size, backupCount=5) fileh.setLevel(log_level) log_format = "%(asctime)s [%(levelname)s] %(message)s" formatter = logging.Formatter(log_format, "%Y-%m-%d-T%H:%M:%S%z") diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index df8ef8d255..93cfc04200 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1013,6 +1013,15 @@ def split_compose_arams(**kwargs): ] +def get_default_args(func): + signature = inspect.signature(func) + return { + k: v.default + for k, v in signature.parameters.items() + if v.default is not inspect.Parameter.empty + } + + def compose_transaction( db, name, @@ -1074,8 +1083,13 @@ def compose_transaction( compose_method = sys.modules[f"counterpartycore.lib.messages.{name}"].compose compose_params = inspect.getfullargspec(compose_method)[0] missing_params = [p for p in compose_params if p not in params and p != "db"] - for param in missing_params: - params[param] = None + if len(missing_params) > 0: + default_values = get_default_args(compose_method) + for param in missing_params: + if param in default_values: + params[param] = default_values[param] + else: + raise exceptions.ComposeError(f"missing parameters: {', '.join(missing_params)}") # dont override fee_per_kb if specified if fee_per_kb is not None: diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 6fb375d715..ce0cd9e020 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -19,17 +19,19 @@ from termcolor import colored, cprint from counterpartycore.lib import ( - api, backend, blocks, check, config, database, ledger, + log, transaction, util, ) from counterpartycore.lib import kickstart as kickstarter +from counterpartycore.lib.api import api_server as api_v2 +from counterpartycore.lib.api import api_v1 logger = logging.getLogger(config.LOGGER_NAME) D = decimal.Decimal @@ -152,6 +154,12 @@ def initialise_config( rpc_user=None, rpc_password=None, rpc_no_allow_cors=False, + api_host=None, + api_port=None, + api_user=None, + api_password=None, + api_no_allow_cors=False, + api_not_ready_http_code=503, force=False, requests_timeout=config.DEFAULT_REQUESTS_TIMEOUT, rpc_batch_size=config.DEFAULT_RPC_BATCH_SIZE, @@ -364,7 +372,7 @@ def initialise_config( config.RPC_HOST = "localhost" # The web root directory for API calls, eg. localhost:14000/rpc/ - config.RPC_WEBROOT = "/rpc/" + config.RPC_WEBROOT = "/old/rpc/" # Server API RPC port if rpc_port: @@ -417,6 +425,58 @@ def initialise_config( config.RPC_BATCH_SIZE = rpc_batch_size + # Server API RPC host + if api_host: + config.API_HOST = api_host + else: + config.API_HOST = "localhost" + + # Server API port + if api_port: + config.API_PORT = api_port + else: + if config.TESTNET: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT_TESTNET + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT_TESTNET + elif config.REGTEST: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT_REGTEST + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT_REGTEST + else: + if config.TESTCOIN: + config.API_PORT = config.DEFAULT_API_PORT + 1 + else: + config.API_PORT = config.DEFAULT_API_PORT + try: + config.API_PORT = int(config.API_PORT) + if not (int(config.API_PORT) > 1 and int(config.API_PORT) < 65535): + raise ConfigurationError("invalid server API port number") + except: # noqa: E722 + raise ConfigurationError( # noqa: B904 + "Please specific a valid port number rpc-port configuration parameter" + ) + + # Server API user + if api_user: + config.API_USER = api_user + else: + config.API_USER = "api" + + if api_password: + config.API_PASSWORD = api_password + else: + config.API_PASSWORD = "api" # noqa: S105 + + config.API_NOT_READY_HTTP_CODE = api_not_ready_http_code + + if api_no_allow_cors: + config.API_NO_ALLOW_CORS = api_no_allow_cors + else: + config.API_NO_ALLOW_CORS = False + ############## # OTHER SETTINGS @@ -517,6 +577,67 @@ def initialise_config( logger.info(f"Running v{config.VERSION_STRING} of counterparty-core.") +def initialise_log_and_config(args): + # Configuration + init_args = { + "database_file": args.database_file, + "testnet": args.testnet, + "testcoin": args.testcoin, + "regtest": args.regtest, + "customnet": args.customnet, + "api_limit_rows": args.api_limit_rows, + "backend_connect": args.backend_connect, + "backend_port": args.backend_port, + "backend_user": args.backend_user, + "backend_password": args.backend_password, + "backend_ssl": args.backend_ssl, + "backend_ssl_no_verify": args.backend_ssl_no_verify, + "backend_poll_interval": args.backend_poll_interval, + "indexd_connect": args.indexd_connect, + "indexd_port": args.indexd_port, + "rpc_host": args.rpc_host, + "rpc_port": args.rpc_port, + "rpc_user": args.rpc_user, + "rpc_password": args.rpc_password, + "rpc_no_allow_cors": args.rpc_no_allow_cors, + "api_host": args.api_host, + "api_port": args.api_port, + "api_user": args.api_user, + "api_password": args.api_password, + "api_no_allow_cors": args.api_no_allow_cors, + "requests_timeout": args.requests_timeout, + "rpc_batch_size": args.rpc_batch_size, + "check_asset_conservation": args.check_asset_conservation, + "force": args.force, + "p2sh_dust_return_pubkey": args.p2sh_dust_return_pubkey, + "utxo_locks_max_addresses": args.utxo_locks_max_addresses, + "utxo_locks_max_age": args.utxo_locks_max_age, + "no_mempool": args.no_mempool, + "skip_db_check": args.skip_db_check, + } + + initialise_log_config( + verbose=args.verbose, + quiet=args.quiet, + log_file=args.log_file, + api_log_file=args.api_log_file, + no_log_files=args.no_log_files, + testnet=args.testnet, + testcoin=args.testcoin, + regtest=args.regtest, + json_log=args.json_log, + ) + + # set up logging + log.set_up( + verbose=config.VERBOSE, + quiet=config.QUIET, + log_file=config.LOG, + log_in_console=args.action == "start", + ) + initialise_config(**init_args) + + def initialise_db(): if config.FORCE: cprint("THE OPTION `--force` IS NOT FOR USE ON PRODUCTION SYSTEMS.", "yellow") @@ -566,12 +687,16 @@ def connect_to_addrindexrs(): print(f"{OK_GREEN} {step}") -def start_all(catch_up="normal"): - try: - # Backend. - connect_to_backend() +def start_all(args): + api_status_poller = None + api_server_v1 = None + api_server_v2 = None + + # Backend. + connect_to_backend() - if not os.path.exists(config.DATABASE) and catch_up == "bootstrap": + try: + if not os.path.exists(config.DATABASE) and args.catch_up == "bootstrap": bootstrap(no_confirm=True) db = initialise_db() @@ -580,23 +705,33 @@ def start_all(catch_up="normal"): # initilise_config transaction.initialise() - # API Status Poller. - api_status_poller = api.APIStatusPoller() - api_status_poller.daemon = True - api_status_poller.start() + if args.enable_api_v1: + # API Status Poller. + api_status_poller = api_v1.APIStatusPoller() + api_status_poller.daemon = True + api_status_poller.start() + + # API Server v1. + api_server_v1 = api_v1.APIServer() + api_server_v1.daemon = True + api_server_v1.start() - # API Server. - api_server = api.APIServer() - api_server.daemon = True - api_server.start() + # API Server v2. + api_server_v2 = api_v2.APIServer() + api_server_v2.start(args) # Server blocks.follow(db) except KeyboardInterrupt: pass finally: - api_status_poller.stop() - api_server.stop() + if args.enable_api_v1: + if api_status_poller: + api_status_poller.stop() + if api_server_v1: + api_server_v1.stop() + if api_server_v2: + api_server_v2.stop() backend.stop() database.optimize(db) logger.info("Closing database...") diff --git a/counterparty-core/counterpartycore/test/complex_unit_test.py b/counterparty-core/counterpartycore/test/complex_unit_test.py index f2b23073e2..7373d7ce3f 100644 --- a/counterparty-core/counterpartycore/test/complex_unit_test.py +++ b/counterparty-core/counterpartycore/test/complex_unit_test.py @@ -4,7 +4,8 @@ import pytest from apsw import ConstraintError -from counterpartycore.lib import api, blocks, ledger, util +from counterpartycore.lib import blocks, ledger, util +from counterpartycore.lib.api import api_v1 # this is require near the top to do setup of the test suite from counterpartycore.test import ( @@ -216,7 +217,7 @@ def test_update_lock(server_db): @pytest.mark.usefixtures("api_server") def test_updated_tables_endpoints(): - for table in api.API_TABLES: + for table in api_v1.API_TABLES: if table in ["mempool"]: continue result = util.api("get_" + table, {}) diff --git a/counterparty-core/counterpartycore/test/conftest.py b/counterparty-core/counterpartycore/test/conftest.py index ec542525c7..613db5ca3f 100644 --- a/counterparty-core/counterpartycore/test/conftest.py +++ b/counterparty-core/counterpartycore/test/conftest.py @@ -263,7 +263,7 @@ def api_server_v2(request, cp_server): "force": False, "requests_timeout": config.DEFAULT_REQUESTS_TIMEOUT, "rpc_batch_size": config.DEFAULT_RPC_BATCH_SIZE, - "check_asset_conservation": config.DEFAULT_CHECK_ASSET_CONSERVATION, + "check_asset_conservation": False, "backend_ssl_verify": None, "rpc_allow_cors": None, "p2sh_dust_return_pubkey": None, @@ -280,6 +280,8 @@ def api_server_v2(request, cp_server): "no_check_asset_conservation": True, "action": "", "no_refresh_backend_height": True, + "no_mempool": False, + "skip_db_check": False, } server_config = ( default_config @@ -289,7 +291,6 @@ def api_server_v2(request, cp_server): "api_port": TEST_RPC_PORT + 10, } ) - args = argparse.Namespace(**server_config) api_server = api_v2.APIServer() api_server.start(args) @@ -521,7 +522,7 @@ def init_arc4(seed): monkeypatch.setattr("counterpartycore.lib.log.isodt", isodt) monkeypatch.setattr("counterpartycore.lib.ledger.curr_time", curr_time) monkeypatch.setattr("counterpartycore.lib.util.date_passed", date_passed) - monkeypatch.setattr("counterpartycore.lib.api.init_api_access_log", init_api_access_log) + monkeypatch.setattr("counterpartycore.lib.api.util.init_api_access_log", init_api_access_log) if hasattr(config, "PREFIX"): monkeypatch.setattr("counterpartycore.lib.config.PREFIX", b"TESTXXXX") monkeypatch.setattr("counterpartycore.lib.backend.getrawtransaction", mocked_getrawtransaction) diff --git a/counterparty-core/counterpartycore/test/fixtures/vectors.py b/counterparty-core/counterpartycore/test/fixtures/vectors.py index 28b90eb97b..43717b875b 100644 --- a/counterparty-core/counterpartycore/test/fixtures/vectors.py +++ b/counterparty-core/counterpartycore/test/fixtures/vectors.py @@ -14,7 +14,7 @@ import bitcoin as bitcoinlib from counterpartycore.lib import address, config, exceptions, script # noqa: F401 -from counterpartycore.lib.api import APIError +from counterpartycore.lib.api.api_v1 import APIError from counterpartycore.lib.kickstart.blocks_parser import BlockchainParser from counterpartycore.lib.ledger import CreditError, DebitError from counterpartycore.lib.messages import issuance From 9cc97c1ef7a890928c33e5fb2ac095465f5a7fdf Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 16:31:47 +0200 Subject: [PATCH 051/128] Fix /healthz endpoint after merging --- .../counterpartycore/lib/api/api_server.py | 5 +- .../counterpartycore/lib/api/api_v1.py | 41 +--------------- .../counterpartycore/lib/api/util.py | 48 +++++++++++-------- 3 files changed, 35 insertions(+), 59 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 063f6b94fd..a2cd5c3cc3 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -65,7 +65,10 @@ def api_root(): def inject_headers(result): server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT http_code = 200 if server_ready else config.API_NOT_READY_HTTP_CODE - response = flask.make_response(flask.jsonify(result), http_code) + if isinstance(result, flask.Response): + response = result + else: + response = flask.make_response(flask.jsonify(result), http_code) response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT diff --git a/counterparty-core/counterpartycore/lib/api/api_v1.py b/counterparty-core/counterpartycore/lib/api/api_v1.py index ddccf65fd5..fcf03d6bde 100644 --- a/counterparty-core/counterpartycore/lib/api/api_v1.py +++ b/counterparty-core/counterpartycore/lib/api/api_v1.py @@ -1063,45 +1063,8 @@ def _set_cors_headers(response): @app.route("/healthz", methods=["GET"]) def handle_healthz(): - msg, code = "Healthy", 200 - - type_ = request.args.get("type", "light") - - def light_check(): - latest_block_index = backend.getblockcount() - api_util.check_last_parsed_block(self.db, latest_block_index) - - def heavy_check(): - transaction.compose_transaction( - self.db, - name="send", - params={ - "source": config.UNSPENDABLE, - "destination": config.UNSPENDABLE, - "asset": config.XCP, - "quantity": 100000000, - }, - allow_unconfirmed_inputs=True, - fee=1000, - ) - - try: - if type_ == "heavy": - # Perform a heavy healthz check. - # Do everything in light but also compose a - # send tx - - logger.debug("Performing heavy healthz check.") - - light_check() - heavy_check() - else: - logger.debug("Performing light healthz check.") - light_check() - - except Exception: - msg, code = "Unhealthy", 503 - return flask.Response(msg, code, mimetype="application/json") + check_type = request.args.get("type", "light") + return api_util.handle_healthz_route(self.db, check_type) @app.route("/", defaults={"args_path": ""}, methods=["GET", "POST", "OPTIONS"]) @app.route("/", methods=["GET", "POST", "OPTIONS"]) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 7d6e58b22a..71e5059105 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -14,34 +14,44 @@ def check_last_parsed_block(blockcount): logger.debug("Database state check passed.") +def healthz_light(db): + logger.debug("Performing light healthz check.") + latest_block_index = backend.getblockcount() + check_last_parsed_block(latest_block_index) + + +def healthz_heavy(db): + logger.debug("Performing heavy healthz check.") + transaction.compose_transaction( + db, + name="send", + params={ + "source": config.UNSPENDABLE, + "destination": config.UNSPENDABLE, + "asset": config.XCP, + "quantity": 100000000, + }, + allow_unconfirmed_inputs=True, + fee=1000, + ) + + def healthz(db, check_type="heavy"): try: if check_type == "light": - logger.debug("Performing light healthz check.") - latest_block_index = backend.getblockcount() - check_last_parsed_block(latest_block_index) + healthz_light(db) else: - logger.debug("Performing heavy healthz check.") - transaction.compose_transaction( - db, - name="send", - params={ - "source": config.UNSPENDABLE, - "destination": config.UNSPENDABLE, - "asset": config.XCP, - "quantity": 100000000, - }, - allow_unconfirmed_inputs=True, - fee=1000, - ) - except Exception: + healthz_light(db) + healthz_heavy(db) + except Exception as e: + logger.error(f"Health check failed: {e}") return False return True -def handle_healthz_route(db, check="heavy"): +def handle_healthz_route(db, check_type="heavy"): msg, code = "Healthy", 200 - if not healthz(db, check): + if not healthz(db, check_type): msg, code = "Unhealthy", 503 return flask.Response(msg, code, mimetype="application/json") From 68e7f1e9db8423459ab86a81e36988fa77bc2bef Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 20:58:00 +0200 Subject: [PATCH 052/128] new route for compose: /address//compose/ --- .../counterpartycore/lib/api/routes.py | 10 +-- .../counterpartycore/lib/transaction.py | 71 ++++++++++++++----- .../counterpartywallet/messages.py | 3 - 3 files changed, 58 insertions(+), 26 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index b66613af7e..817fe56676 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -56,10 +56,6 @@ "function": ledger.get_sweeps, }, ### /transactions ### - "/transactions/compose/": { - "function": transaction.compose, - "pass_all_args": True, - }, "/transactions/info": { "function": transaction.info, "args": [("rawtransaction", None), ("block_index", None)], @@ -254,3 +250,9 @@ "function": ledger.get_mempool_events, }, } +### /address//compose/ ### +for transaction_name, compose_function in transaction.COMPOSE_FUNCTIONS.items(): + ROUTES[f"/address//compose/{transaction_name}"] = { + "function": compose_function, + "pass_all_args": True, + } diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 93cfc04200..0165fdbee7 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -6,6 +6,7 @@ import binascii import decimal +import functools import hashlib import inspect import io @@ -995,24 +996,6 @@ def split_compose_arams(**kwargs): return transaction_args, common_args, private_key_wif -COMPOSABLE_TRANSACTIONS = [ - "bet", - "broadcast", - "btcpay", - "burn", - "cancel", - "destroy", - "dispenser", - "dividend", - "issuance", - "order", - "send", - "rps", - "rpsresolve", - "sweep", -] - - def get_default_args(func): signature = inspect.signature(func) return { @@ -1022,6 +1005,30 @@ def get_default_args(func): } +COMPOSE_COMMONS_ARGS = [ + "encoding", + "fee_per_kb", + "estimate_fee_per_kb", + "regular_dust_size", + "multisig_dust_size", + "op_return_value", + "pubkey", + "allow_unconfirmed_inputs", + "fee", + "fee_provided", + "unspent_tx_hash", + "custom_inputs", + "dust_return_pubkey", + "disable_utxo_locks", + "extended_tx_info", + "p2sh_source_multisig_pubkeys", + "p2sh_source_multisig_pubkeys_required", + "p2sh_pretx_txid", + "old_style_api", + "segwit", +] + + def compose_transaction( db, name, @@ -1137,13 +1144,39 @@ def compose_transaction( ) -def compose(db, transaction_name, **kwargs): +COMPOSABLE_TRANSACTIONS = [ + "bet", + "broadcast", + "btcpay", + "burn", + "cancel", + "destroy", + "dispenser", + "dividend", + "issuance", + "order", + "send", + "rps", + "rpsresolve", + "sweep", +] + + +def compose(db, source, transaction_name, **kwargs): if transaction_name not in COMPOSABLE_TRANSACTIONS: raise exceptions.TransactionError("Transaction type not composable.") transaction_args, common_args, _ = split_compose_arams(**kwargs) + transaction_args["source"] = source return compose_transaction(db, name=transaction_name, params=transaction_args, **common_args) +COMPOSE_FUNCTIONS = {} +for transaction_name in COMPOSABLE_TRANSACTIONS: + COMPOSE_FUNCTIONS[transaction_name] = functools.partial( + compose, transaction_name=transaction_name + ) + + def info(db, rawtransaction, block_index=None): # block_index mandatory for transactions before block 335000 source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( diff --git a/counterparty-wallet/counterpartywallet/messages.py b/counterparty-wallet/counterpartywallet/messages.py index f044a41771..84c2cd0291 100755 --- a/counterparty-wallet/counterpartywallet/messages.py +++ b/counterparty-wallet/counterpartywallet/messages.py @@ -350,6 +350,3 @@ def compose(message, args): return compose_transaction(args, message, param_names) else: raise ArgumentError("Invalid message name") - - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 From 07f933cff617f2c21a5c3124de8c4476fa4b294f Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 21:27:14 +0200 Subject: [PATCH 053/128] Add type to routes args --- .../counterpartycore/lib/api/api_server.py | 36 +++++++++++---- .../counterpartycore/lib/api/routes.py | 46 +++++++++---------- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index a2cd5c3cc3..f26969e54f 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -62,9 +62,13 @@ def api_root(): } -def inject_headers(result): +def inject_headers(result, return_code=None): server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT - http_code = 200 if server_ready else config.API_NOT_READY_HTTP_CODE + http_code = 200 + if return_code: + http_code = return_code + elif not server_ready: + http_code = config.API_NOT_READY_HTTP_CODE if isinstance(result, flask.Response): response = result else: @@ -75,6 +79,24 @@ def inject_headers(result): return response +def prepare_args(route, **kwargs): + function_args = dict(kwargs) + if "pass_all_args" in route and route["pass_all_args"]: + function_args = request.args | function_args + elif "args" in route: + for arg in route["args"]: + str_arg = request.args.get(arg[0]) + if str_arg is None: + function_args[arg[0]] = arg[2] + elif arg[1] == bool: + function_args[arg[0]] = str_arg.lower() in ["true", "1"] + elif arg[1] == int: + function_args[arg[0]] = int(str_arg) + else: + function_args[arg[0]] = str_arg + return function_args + + @auth.login_required def handle_route(**kwargs): db = get_db() @@ -85,12 +107,10 @@ def handle_route(**kwargs): result = api_root() else: route = ROUTES.get(rule) - function_args = dict(kwargs) - if "pass_all_args" in route and route["pass_all_args"]: - function_args = request.args | function_args - elif "args" in route: - for arg in route["args"]: - function_args[arg[0]] = request.args.get(arg[0], arg[1]) + try: + function_args = prepare_args(route, **kwargs) + except ValueError as e: + return inject_headers({"error": f"Invalid parameter: {e}"}, return_code=400) result = route["function"](db, **function_args) result = remove_rowids(result) return inject_headers(result) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 817fe56676..b52b2376c7 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -11,7 +11,7 @@ ### /blocks ### "/blocks": { "function": ledger.get_blocks, - "args": [("last", None), ("limit", 10)], + "args": [("last", int, None), ("limit", int, 10)], }, "/blocks/": { "function": ledger.get_block, @@ -58,11 +58,11 @@ ### /transactions ### "/transactions/info": { "function": transaction.info, - "args": [("rawtransaction", None), ("block_index", None)], + "args": [("rawtransaction", str, None), ("block_index", int, None)], }, "/transactions/unpack": { "function": transaction.unpack, - "args": [("datahex", None), ("block_index", None)], + "args": [("datahex", str, None), ("block_index", int, None)], }, "/transactions/": { "function": ledger.get_transaction, @@ -82,7 +82,7 @@ }, "/addresses/
/bets": { "function": ledger.get_bet_by_feed, - "args": [("status", "open")], + "args": [("status", str, "open")], }, "/addresses/
/broadcasts": { "function": ledger.get_broadcasts_by_source, @@ -104,11 +104,11 @@ }, "/addresses/
/dispensers": { "function": ledger.get_dispensers, - "args": [("status", 0)], + "args": [("status", int, 0)], }, "/addresses/
/dispensers/": { "function": ledger.get_dispensers, - "args": [("status", 0)], + "args": [("status", int, 0)], }, "/addresses/
/sweeps": { "function": ledger.get_sweeps, @@ -128,7 +128,7 @@ }, "/assets//orders": { "function": ledger.get_orders_by_asset, - "args": [("status", "open")], + "args": [("status", str, "open")], }, "/assets//credits": { "function": ledger.get_credits, @@ -147,11 +147,11 @@ }, "/assets//dispensers": { "function": ledger.get_dispensers, - "args": [("status", 0)], + "args": [("status", int, 0)], }, "/assets//dispensers/
": { "function": ledger.get_dispensers, - "args": [("status", 0)], + "args": [("status", int, 0)], }, "/assets//holders": { "function": ledger.get_asset_holders, @@ -162,11 +162,11 @@ }, "/orders//matches": { "function": ledger.get_order_matches_by_order, - "args": [("status", "pending")], + "args": [("status", str, "pending")], }, "/orders//btcpays": { "function": ledger.get_btcpays_by_order, - "args": [("status", "pending")], + "args": [("status", str, "pending")], }, ### /bets ### "/bets/": { @@ -174,11 +174,11 @@ }, "/bets//matches": { "function": ledger.get_bet_matches_by_bet, - "args": [("status", "pending")], + "args": [("status", str, "pending")], }, "/bets//resolutions": { "function": ledger.get_resolutions_by_bet, - "args": [("status", "pending")], + "args": [("status", str, "pending")], }, ### /burns ### "/burns": { @@ -194,7 +194,7 @@ ### /events ### "/events": { "function": ledger.get_events, - "args": [("last", None), ("limit", 100)], + "args": [("last", int, None), ("limit", int, 100)], }, "/events/": { "function": ledger.get_events, @@ -204,42 +204,42 @@ }, "/events/": { "function": ledger.get_events, - "args": [("last", None), ("limit", 100)], + "args": [("last", int, None), ("limit", int, 100)], }, ### /healthz ### "/healthz": { "function": util.handle_healthz_route, - "args": [("check_type", "heavy")], + "args": [("check_type", str, "heavy")], }, ### /backend ### "/backend/addresses/
/transactions": { "function": backend.search_raw_transactions, - "args": [("unconfirmed", True), ("only_tx_hashes", False)], + "args": [("unconfirmed", bool, True), ("only_tx_hashes", bool, False)], }, "/backend/addresses/
/transactions/oldest": { "function": backend.get_oldest_tx, }, "/backend/addresses/
/utxos": { "function": backend.get_unspent_txouts, - "args": [("unconfirmed", True), ("unspent_tx_hash", None)], + "args": [("unconfirmed", bool, True), ("unspent_tx_hash", str, None)], }, "/backend/addresses/
/pubkey": { "function": util.pubkeyhash_to_pubkey, - "args": [("provided_pubkeys", "")], + "args": [("provided_pubkeys", str, "")], }, "/backend/transactions": { "function": util.getrawtransactions, - "args": [("tx_hashes", ""), ("verbose", False), ("skip_missing", False)], + "args": [("tx_hashes", str, ""), ("verbose", bool, False), ("skip_missing", bool, False)], }, "/backend/transactions/": { "function": backend.getrawtransaction, - "args": [("verbose", False), ("skip_missing", False)], + "args": [("verbose", bool, False), ("skip_missing", bool, False)], }, "/backend/estimatesmartfee": { "function": backend.fee_per_kb, "args": [ - ("conf_target", config.ESTIMATE_FEE_CONF_TARGET), - ("mode", config.ESTIMATE_FEE_MODE), + ("conf_target", int, config.ESTIMATE_FEE_CONF_TARGET), + ("mode", str, config.ESTIMATE_FEE_MODE), ], }, ### /mempool ### From 3cc540631f01201c2f039bd178bca0e4f5b71e19 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 17 Apr 2024 22:12:36 +0200 Subject: [PATCH 054/128] Add type hints to all compose functions --- .../counterpartycore/lib/messages/bet.py | 18 ++--- .../lib/messages/broadcast.py | 2 +- .../counterpartycore/lib/messages/btcpay.py | 2 +- .../counterpartycore/lib/messages/burn.py | 2 +- .../counterpartycore/lib/messages/cancel.py | 2 +- .../counterpartycore/lib/messages/destroy.py | 2 +- .../lib/messages/dispenser.py | 16 ++--- .../counterpartycore/lib/messages/dividend.py | 2 +- .../counterpartycore/lib/messages/issuance.py | 16 ++--- .../counterpartycore/lib/messages/order.py | 9 ++- .../counterpartycore/lib/messages/rps.py | 4 +- .../lib/messages/rpsresolve.py | 2 +- .../counterpartycore/lib/messages/send.py | 9 ++- .../counterpartycore/lib/messages/sweep.py | 2 +- .../lib/messages/versions/enhanced_send.py | 4 +- .../lib/messages/versions/mpma.py | 2 +- .../lib/messages/versions/send1.py | 2 +- .../counterpartycore/lib/transaction.py | 70 ++++++------------- 18 files changed, 80 insertions(+), 86 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/messages/bet.py b/counterparty-core/counterpartycore/lib/messages/bet.py index 3ad54029ba..2af526b8b4 100644 --- a/counterparty-core/counterpartycore/lib/messages/bet.py +++ b/counterparty-core/counterpartycore/lib/messages/bet.py @@ -358,15 +358,15 @@ def validate( def compose( db, - source, - feed_address, - bet_type, - deadline, - wager_quantity, - counterwager_quantity, - target_value, - leverage, - expiration, + source: str, + feed_address: str, + bet_type: int, + deadline: int, + wager_quantity: int, + counterwager_quantity: int, + target_value: int, + leverage: int, + expiration: int, ): if ledger.get_balance(db, source, config.XCP) < wager_quantity: raise exceptions.ComposeError("insufficient funds") diff --git a/counterparty-core/counterpartycore/lib/messages/broadcast.py b/counterparty-core/counterpartycore/lib/messages/broadcast.py index bc8cf9ca59..482cc0a6c2 100644 --- a/counterparty-core/counterpartycore/lib/messages/broadcast.py +++ b/counterparty-core/counterpartycore/lib/messages/broadcast.py @@ -135,7 +135,7 @@ def validate(db, source, timestamp, value, fee_fraction_int, text, block_index): return problems -def compose(db, source, timestamp, value, fee_fraction, text): +def compose(db, source: str, timestamp: int, value: int, fee_fraction: float, text: str): # Store the fee fraction as an integer. fee_fraction_int = int(fee_fraction * 1e8) diff --git a/counterparty-core/counterpartycore/lib/messages/btcpay.py b/counterparty-core/counterpartycore/lib/messages/btcpay.py index feb79ec832..49308b5e14 100644 --- a/counterparty-core/counterpartycore/lib/messages/btcpay.py +++ b/counterparty-core/counterpartycore/lib/messages/btcpay.py @@ -106,7 +106,7 @@ def validate(db, source, order_match_id, block_index): return destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems -def compose(db, source, order_match_id): +def compose(db, source: str, order_match_id: str): tx0_hash, tx1_hash = util.parse_id(order_match_id) destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems = validate( diff --git a/counterparty-core/counterpartycore/lib/messages/burn.py b/counterparty-core/counterpartycore/lib/messages/burn.py index d571480ccd..f7cc8f2a8d 100644 --- a/counterparty-core/counterpartycore/lib/messages/burn.py +++ b/counterparty-core/counterpartycore/lib/messages/burn.py @@ -72,7 +72,7 @@ def validate(db, source, destination, quantity, block_index, overburn=False): return problems -def compose(db, source, quantity, overburn=False): +def compose(db, source: str, quantity: int, overburn: bool = False): cursor = db.cursor() destination = config.UNSPENDABLE problems = validate( diff --git a/counterparty-core/counterpartycore/lib/messages/cancel.py b/counterparty-core/counterpartycore/lib/messages/cancel.py index 869d572c49..bc51437e9f 100644 --- a/counterparty-core/counterpartycore/lib/messages/cancel.py +++ b/counterparty-core/counterpartycore/lib/messages/cancel.py @@ -82,7 +82,7 @@ def validate(db, source, offer_hash): return offer, offer_type, problems -def compose(db, source, offer_hash): +def compose(db, source: str, offer_hash: str): # Check that offer exists. offer, offer_type, problems = validate(db, source, offer_hash) if problems: diff --git a/counterparty-core/counterpartycore/lib/messages/destroy.py b/counterparty-core/counterpartycore/lib/messages/destroy.py index b41b3db1b8..1e556b58d6 100644 --- a/counterparty-core/counterpartycore/lib/messages/destroy.py +++ b/counterparty-core/counterpartycore/lib/messages/destroy.py @@ -115,7 +115,7 @@ def validate(db, source, destination, asset, quantity): raise BalanceError("balance insufficient") # noqa: F405 -def compose(db, source, asset, quantity, tag): +def compose(db, source: str, asset: str, quantity: int, tag: str): # resolve subassets asset = ledger.resolve_subasset_longname(db, asset) diff --git a/counterparty-core/counterpartycore/lib/messages/dispenser.py b/counterparty-core/counterpartycore/lib/messages/dispenser.py index 961683f52e..9dbf07af54 100644 --- a/counterparty-core/counterpartycore/lib/messages/dispenser.py +++ b/counterparty-core/counterpartycore/lib/messages/dispenser.py @@ -338,14 +338,14 @@ def validate( def compose( db, - source, - asset, - give_quantity, - escrow_quantity, - mainchainrate, - status, - open_address=None, - oracle_address=None, + source: str, + asset: str, + give_quantity: int, + escrow_quantity: int, + mainchainrate: int, + status: int, + open_address: str = None, + oracle_address: str = None, ): assetid, problems = validate( db, diff --git a/counterparty-core/counterpartycore/lib/messages/dividend.py b/counterparty-core/counterpartycore/lib/messages/dividend.py index 6b3d0f1c48..d7f5afe433 100644 --- a/counterparty-core/counterpartycore/lib/messages/dividend.py +++ b/counterparty-core/counterpartycore/lib/messages/dividend.py @@ -179,7 +179,7 @@ def validate(db, source, quantity_per_unit, asset, dividend_asset, block_index): return dividend_total, outputs, problems, fee -def compose(db, source, quantity_per_unit, asset, dividend_asset): +def compose(db, source: str, quantity_per_unit: int, asset: str, dividend_asset: str): # resolve subassets asset = ledger.resolve_subasset_longname(db, asset) dividend_asset = ledger.resolve_subasset_longname(db, dividend_asset) diff --git a/counterparty-core/counterpartycore/lib/messages/issuance.py b/counterparty-core/counterpartycore/lib/messages/issuance.py index c09812c151..0f07948950 100644 --- a/counterparty-core/counterpartycore/lib/messages/issuance.py +++ b/counterparty-core/counterpartycore/lib/messages/issuance.py @@ -347,14 +347,14 @@ def validate( def compose( db, - source, - asset, - quantity, - transfer_destination=None, - divisible=None, - lock=None, - reset=None, - description=None, + source: str, + asset: str, + quantity: int, + transfer_destination: str | None = None, + divisible: bool | None = None, + lock: bool | None = None, + reset: bool | None = None, + description: str | None = None, ): # Callability is deprecated, so for re‐issuances set relevant parameters # to old values; for first issuances, make uncallable. diff --git a/counterparty-core/counterpartycore/lib/messages/order.py b/counterparty-core/counterpartycore/lib/messages/order.py index 687a40a910..3038483614 100644 --- a/counterparty-core/counterpartycore/lib/messages/order.py +++ b/counterparty-core/counterpartycore/lib/messages/order.py @@ -432,7 +432,14 @@ def validate( def compose( - db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required + db, + source: str, + give_asset: str, + give_quantity: int, + get_asset: str, + get_quantity: int, + expiration: int, + fee_required: int, ): cursor = db.cursor() diff --git a/counterparty-core/counterpartycore/lib/messages/rps.py b/counterparty-core/counterpartycore/lib/messages/rps.py index 92bb1e5b39..134dd89f38 100644 --- a/counterparty-core/counterpartycore/lib/messages/rps.py +++ b/counterparty-core/counterpartycore/lib/messages/rps.py @@ -282,7 +282,9 @@ def validate(db, source, possible_moves, wager, move_random_hash, expiration, bl return problems -def compose(db, source, possible_moves, wager, move_random_hash, expiration): +def compose( + db, source: str, possible_moves: int, wager: int, move_random_hash: str, expiration: int +): problems = validate( db, source, possible_moves, wager, move_random_hash, expiration, ledger.CURRENT_BLOCK_INDEX ) diff --git a/counterparty-core/counterpartycore/lib/messages/rpsresolve.py b/counterparty-core/counterpartycore/lib/messages/rpsresolve.py index 231e815570..77c8bf4ca0 100644 --- a/counterparty-core/counterpartycore/lib/messages/rpsresolve.py +++ b/counterparty-core/counterpartycore/lib/messages/rpsresolve.py @@ -106,7 +106,7 @@ def validate(db, source, move, random, rps_match_id): return txn, rps_match, problems -def compose(db, source, move, random, rps_match_id): +def compose(db, source: str, move: int, random: str, rps_match_id: str): tx0_hash, tx1_hash = util.parse_id(rps_match_id) txn, rps_match, problems = validate(db, source, move, random, rps_match_id) diff --git a/counterparty-core/counterpartycore/lib/messages/send.py b/counterparty-core/counterpartycore/lib/messages/send.py index fbe0a3a96e..69aacd2883 100644 --- a/counterparty-core/counterpartycore/lib/messages/send.py +++ b/counterparty-core/counterpartycore/lib/messages/send.py @@ -112,7 +112,14 @@ def validate(db, source, destination, asset, quantity, block_index): def compose( - db, source, destination, asset, quantity, memo=None, memo_is_hex=False, use_enhanced_send=None + db, + source: str, + destination: str, + asset: str, + quantity: int, + memo: str | None = None, + memo_is_hex: bool = False, + use_enhanced_send: bool | None = None, ): # special case - enhanced_send replaces send by default when it is enabled # but it can be explicitly disabled with an API parameter diff --git a/counterparty-core/counterpartycore/lib/messages/sweep.py b/counterparty-core/counterpartycore/lib/messages/sweep.py index 3a95fd8c06..9ed916cb8f 100644 --- a/counterparty-core/counterpartycore/lib/messages/sweep.py +++ b/counterparty-core/counterpartycore/lib/messages/sweep.py @@ -103,7 +103,7 @@ def validate(db, source, destination, flags, memo, block_index): return problems, total_fee -def compose(db, source, destination, flags, memo): +def compose(db, source: str, destination: str, flags: int, memo: str): if memo is None: memo = b"" elif flags & FLAG_BINARY_MEMO: diff --git a/counterparty-core/counterpartycore/lib/messages/versions/enhanced_send.py b/counterparty-core/counterpartycore/lib/messages/versions/enhanced_send.py index 612bd35fae..b423af2f9f 100644 --- a/counterparty-core/counterpartycore/lib/messages/versions/enhanced_send.py +++ b/counterparty-core/counterpartycore/lib/messages/versions/enhanced_send.py @@ -98,7 +98,9 @@ def validate(db, source, destination, asset, quantity, memo_bytes, block_index): return problems -def compose(db, source, destination, asset, quantity, memo, memo_is_hex): +def compose( + db, source: str, destination: str, asset: str, quantity: int, memo: str, memo_is_hex: bool +): cursor = db.cursor() # Just send BTC? diff --git a/counterparty-core/counterpartycore/lib/messages/versions/mpma.py b/counterparty-core/counterpartycore/lib/messages/versions/mpma.py index a6cea2639e..2d6a2c5c48 100644 --- a/counterparty-core/counterpartycore/lib/messages/versions/mpma.py +++ b/counterparty-core/counterpartycore/lib/messages/versions/mpma.py @@ -98,7 +98,7 @@ def validate(db, source, asset_dest_quant_list, block_index): return problems -def compose(db, source, asset_dest_quant_list, memo, memo_is_hex): +def compose(db, source: str, asset_dest_quant_list: list, memo: str, memo_is_hex: bool): cursor = db.cursor() out_balances = util.accumulate([(t[0], t[2]) for t in asset_dest_quant_list]) diff --git a/counterparty-core/counterpartycore/lib/messages/versions/send1.py b/counterparty-core/counterpartycore/lib/messages/versions/send1.py index 95e6642376..459c166d48 100644 --- a/counterparty-core/counterpartycore/lib/messages/versions/send1.py +++ b/counterparty-core/counterpartycore/lib/messages/versions/send1.py @@ -68,7 +68,7 @@ def validate(db, source, destination, asset, quantity, block_index): return problems -def compose(db, source, destination, asset, quantity): +def compose(db, source: str, destination: str, asset: str, quantity: int): cursor = db.cursor() # Just send BTC? diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 0165fdbee7..0254b83200 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -957,31 +957,6 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): return dust_return_pubkey -COMPOSE_COMMONS_ARGS = [ - "encoding", - "fee_per_kb", - "regular_dust_size", - "multisig_dust_size", - "op_return_value", - "pubkey", - "allow_unconfirmed_inputs", - "fee", - "fee_provided", - "estimate_fee_per_kb", - "estimate_fee_per_kb_nblocks", - "estimate_fee_per_kb_conf_target", - "estimate_fee_per_kb_mode", - "unspent_tx_hash", - "custom_inputs", - "dust_return_pubkey", - "disable_utxo_locks", - "extended_tx_info", - "p2sh_source_multisig_pubkeys", - "p2sh_source_multisig_pubkeys_required", - "p2sh_pretx_txid", -] - - def split_compose_arams(**kwargs): transaction_args = {} common_args = {} @@ -1005,28 +980,25 @@ def get_default_args(func): } -COMPOSE_COMMONS_ARGS = [ - "encoding", - "fee_per_kb", - "estimate_fee_per_kb", - "regular_dust_size", - "multisig_dust_size", - "op_return_value", - "pubkey", - "allow_unconfirmed_inputs", - "fee", - "fee_provided", - "unspent_tx_hash", - "custom_inputs", - "dust_return_pubkey", - "disable_utxo_locks", - "extended_tx_info", - "p2sh_source_multisig_pubkeys", - "p2sh_source_multisig_pubkeys_required", - "p2sh_pretx_txid", - "old_style_api", - "segwit", -] +COMPOSE_COMMONS_ARGS = { + "encoding": (str, "auto"), + "fee_per_kb": (int, None), + "estimate_fee_per_kb": (int, None), + "regular_dust_size": (int, config.DEFAULT_REGULAR_DUST_SIZE), + "multisig_dust_size": (int, config.DEFAULT_MULTISIG_DUST_SIZE), + "op_return_value": (int, config.DEFAULT_OP_RETURN_VALUE), + "pubkey": (str, None), + "allow_unconfirmed_inputs": (bool, False), + "fee": (int, None), + "fee_provided": (int, 0), + "unspent_tx_hash": (str, None), + "dust_return_pubkey": (str, None), + "disable_utxo_locks": (bool, False), + "extended_tx_info": (bool, False), + "p2sh_pretx_txid": (str, None), + "old_style_api": (bool, True), + "segwit": (bool, False), +} def compose_transaction( @@ -1177,6 +1149,10 @@ def compose(db, source, transaction_name, **kwargs): ) +def get_compose_common_args(): + return [(name, value[0], value[1]) for name, value in COMPOSE_COMMONS_ARGS.items()] + + def info(db, rawtransaction, block_index=None): # block_index mandatory for transactions before block 335000 source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( From 7474f5babffa36ca9ff3b213a85d6639cfd9e175 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 18 Apr 2024 15:01:37 +0200 Subject: [PATCH 055/128] Routes args from function signature; Routes doc from function docstring --- .../counterpartycore/lib/api/api_server.py | 35 +++++++++++++----- .../counterpartycore/lib/api/routes.py | 10 ++++- .../counterpartycore/lib/api/util.py | 37 +++++++++++++++++++ .../counterpartycore/lib/ledger.py | 9 ++++- counterparty-core/requirements.txt | 1 + 5 files changed, 80 insertions(+), 12 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index f26969e54f..9a258fd665 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -44,7 +44,16 @@ def verify_password(username, password): def api_root(): counterparty_height = blocks.last_db_index(get_db()) - routes = list(ROUTES.keys()) + routes = [] + for path in ROUTES: + route = ROUTES[path] + routes.append( + { + "path": path, + "args": route.get("args", []), + "description": route.get("description", ""), + } + ) network = "mainnet" if config.TESTNET: network = "testnet" @@ -85,15 +94,23 @@ def prepare_args(route, **kwargs): function_args = request.args | function_args elif "args" in route: for arg in route["args"]: - str_arg = request.args.get(arg[0]) + arg_name = arg["name"] + if arg_name in function_args: + continue + str_arg = request.args.get(arg_name) + if str_arg is None and arg["required"]: + raise ValueError(f"Missing required parameter: {arg_name}") if str_arg is None: - function_args[arg[0]] = arg[2] - elif arg[1] == bool: - function_args[arg[0]] = str_arg.lower() in ["true", "1"] - elif arg[1] == int: - function_args[arg[0]] = int(str_arg) + function_args[arg_name] = arg["default"] + elif arg["type"] == "bool": + function_args[arg_name] = str_arg.lower() in ["true", "1"] + elif arg["type"] == "int": + try: + function_args[arg_name] = int(str_arg) + except ValueError as e: + raise ValueError(f"Invalid integer: {arg_name}") from e else: - function_args[arg[0]] = str_arg + function_args[arg_name] = str_arg return function_args @@ -110,7 +127,7 @@ def handle_route(**kwargs): try: function_args = prepare_args(route, **kwargs) except ValueError as e: - return inject_headers({"error": f"Invalid parameter: {e}"}, return_code=400) + return inject_headers({"error": str(e)}, return_code=400) result = route["function"](db, **function_args) result = remove_rowids(result) return inject_headers(result) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index b52b2376c7..a997307d18 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -11,7 +11,6 @@ ### /blocks ### "/blocks": { "function": ledger.get_blocks, - "args": [("last", int, None), ("limit", int, 10)], }, "/blocks/": { "function": ledger.get_block, @@ -250,9 +249,18 @@ "function": ledger.get_mempool_events, }, } + +# Add compose routes for each transaction type ### /address//compose/ ### for transaction_name, compose_function in transaction.COMPOSE_FUNCTIONS.items(): ROUTES[f"/address//compose/{transaction_name}"] = { "function": compose_function, "pass_all_args": True, } + +# Add description and args to each route +for path, route in ROUTES.items(): + if "/compose/" in path: + continue + ROUTES[path]["description"] = util.get_function_description(route["function"]) + ROUTES[path]["args"] = util.prepare_route_args(route["function"]) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 71e5059105..32a183e928 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -1,8 +1,10 @@ +import inspect import logging from logging import handlers as logging_handlers import flask from counterpartycore.lib import backend, config, exceptions, ledger, transaction +from docstring_parser import parse as parse_docstring logger = logging.getLogger(config.LOGGER_NAME) @@ -115,3 +117,38 @@ def init_api_access_log(flask_app): werkzeug_logger.addHandler(handler) flask.cli.show_server_banner = lambda *args: None + + +def get_args_description(function): + docstring = parse_docstring(function.__doc__) + args = {} + for param in docstring.params: + args[param.arg_name] = param.description + return args + + +def prepare_route_args(function): + args = [] + function_args = inspect.signature(function).parameters + args_description = get_args_description(function) + for arg_name, arg in function_args.items(): + annotation = arg.annotation + if annotation is inspect.Parameter.empty: + continue + route_arg = {"name": arg_name} + default = arg.default + if default is not inspect.Parameter.empty: + route_arg["default"] = default + route_arg["required"] = False + else: + route_arg["required"] = True + route_arg["type"] = arg.annotation.__name__ + if arg_name in args_description: + route_arg["description"] = args_description[arg_name] + args.append(route_arg) + return args + + +def get_function_description(function): + docstring = parse_docstring(function.__doc__) + return docstring.description diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index b9256be7d1..592c477076 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -964,7 +964,12 @@ def get_burns(db, address=None, status="valid"): ###################################### -def get_blocks(db, last=None, limit=10): +def get_blocks(db, last: int = None, limit: int = 10): + """ + Returns the list of the last ten blocks + :param int last: The index of the most recent block to return + :param int limit: The number of blocks to return + """ cursor = db.cursor() bindings = [] query = """ @@ -980,7 +985,7 @@ def get_blocks(db, last=None, limit=10): return cursor.fetchall() -def get_block(db, block_index): +def get_block(db, block_index: int): blocks = get_blocks(db, last=block_index, limit=1) if blocks: return blocks[0] diff --git a/counterparty-core/requirements.txt b/counterparty-core/requirements.txt index 01267b95de..65e8050011 100644 --- a/counterparty-core/requirements.txt +++ b/counterparty-core/requirements.txt @@ -27,4 +27,5 @@ halo==0.0.31 termcolor==2.4.0 sentry-sdk==1.45.0 Flask-Cors==4.0.0 +docstring_parser==0.16 counterparty-rs==10.1.0 From c2dea422d717b0e65a2f766bab2f947a4bb4218c Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 18 Apr 2024 20:32:23 +0200 Subject: [PATCH 056/128] Progress in routes documentation --- .../counterpartycore/lib/api/routes.py | 144 +++-------- .../counterpartycore/lib/blocks.py | 3 - .../counterpartycore/lib/exceptions.py | 4 + .../counterpartycore/lib/ledger.py | 227 +++++++++++++++++- .../counterpartycore/lib/transaction.py | 15 +- counterparty-core/counterpartycore/server.py | 2 + 6 files changed, 274 insertions(+), 121 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index a997307d18..b528b8eb10 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -9,109 +9,40 @@ # Define the API routes except root (`/`) defined in `api_server.py` ROUTES = { ### /blocks ### - "/blocks": { - "function": ledger.get_blocks, - }, - "/blocks/": { - "function": ledger.get_block, - }, - "/blocks//transactions": { - "function": ledger.get_transactions_by_block, - }, - "/blocks//events": { - "function": ledger.get_events, - }, - "/blocks//events/counts": { - "function": ledger.get_events_counts, - }, - "/blocks//events/": { - "function": ledger.get_events, - }, - "/blocks//credits": { - "function": ledger.get_credits, - }, - "/blocks//debits": { - "function": ledger.get_debits, - }, - "/blocks//expirations": { - "function": ledger.get_expirations, - }, - "/blocks//cancels": { - "function": ledger.get_cancels, - }, - "/blocks//destructions": { - "function": ledger.get_destructions, - }, - "/blocks//issuances": { - "function": ledger.get_issuances, - }, - "/blocks//sends": { - "function": ledger.get_sends_or_receives, - }, - "/blocks//dispenses": { - "function": ledger.get_dispenses, - }, - "/blocks//sweeps": { - "function": ledger.get_sweeps, - }, + "/blocks": ledger.get_blocks, + "/blocks/": ledger.get_block, + "/blocks//transactions": ledger.get_transactions_by_block, + "/blocks//events": ledger.get_events_by_block, + "/blocks//events/counts": ledger.get_events_counts_by_block, + "/blocks//events/": ledger.get_events_by_block_and_event, + "/blocks//credits": ledger.get_credits_by_block, + "/blocks//debits": ledger.get_debits_by_block, + "/blocks//expirations": ledger.get_expirations, + "/blocks//cancels": ledger.get_cancels, + "/blocks//destructions": ledger.get_destructions, + "/blocks//issuances": ledger.get_issuances_by_block, + "/blocks//sends": ledger.get_sends_or_receives_by_block, + "/blocks//dispenses": ledger.get_dispenses_by_block, + "/blocks//sweeps": ledger.get_sweeps_by_block, ### /transactions ### - "/transactions/info": { - "function": transaction.info, - "args": [("rawtransaction", str, None), ("block_index", int, None)], - }, - "/transactions/unpack": { - "function": transaction.unpack, - "args": [("datahex", str, None), ("block_index", int, None)], - }, - "/transactions/": { - "function": ledger.get_transaction, - }, + "/transactions/info": transaction.info, + "/transactions/unpack": transaction.unpack, + "/transactions/": ledger.get_transaction, ### /addresses ### - "/addresses/
/balances": { - "function": ledger.get_address_balances, - }, - "/addresses/
/balances/": { - "function": ledger.get_balance_object, - }, - "/addresses/
/credits": { - "function": ledger.get_credits, - }, - "/addresses/
/debits": { - "function": ledger.get_debits, - }, - "/addresses/
/bets": { - "function": ledger.get_bet_by_feed, - "args": [("status", str, "open")], - }, - "/addresses/
/broadcasts": { - "function": ledger.get_broadcasts_by_source, - }, - "/addresses/
/burns": { - "function": ledger.get_burns, - }, - "/addresses/
/sends": { - "function": ledger.get_sends, - }, - "/addresses/
/receives": { - "function": ledger.get_receives, - }, - "/addresses/
/sends/": { - "function": ledger.get_sends, - }, - "/addresses/
/receives/": { - "function": ledger.get_receives, - }, - "/addresses/
/dispensers": { - "function": ledger.get_dispensers, - "args": [("status", int, 0)], - }, - "/addresses/
/dispensers/": { - "function": ledger.get_dispensers, - "args": [("status", int, 0)], - }, - "/addresses/
/sweeps": { - "function": ledger.get_sweeps, - }, + "/addresses/
/balances": ledger.get_address_balances, + "/addresses/
/balances/": ledger.get_balance_object, + "/addresses/
/credits": ledger.get_credits_by_address, + "/addresses/
/debits": ledger.get_debits_by_address, + "/addresses/
/bets": ledger.get_bet_by_feed, + "/addresses/
/broadcasts": ledger.get_broadcasts_by_source, + "/addresses/
/burns": ledger.get_burns, + "/addresses/
/sends": ledger.get_send_by_address, + "/addresses/
/receives": ledger.get_receive_by_address, + "/addresses/
/sends/": ledger.get_send_by_address_and_asset, + "/addresses/
/receives/": ledger.get_receive_by_address_and_asset, + "/addresses/
/dispensers": ledger.get_dispensers_by_address, + "/addresses/
/dispensers/": ledger.get_dispensers_by_address_and_asset, + "/addresses/
/sweeps": ledger.get_sweeps_by_address, ### /assets ### "/assets": { "function": ledger.get_valid_assets, @@ -144,10 +75,7 @@ "/assets//sends": { "function": ledger.get_sends_or_receives, }, - "/assets//dispensers": { - "function": ledger.get_dispensers, - "args": [("status", int, 0)], - }, + "/assets//dispensers": ledger.get_dispensers_by_asset, "/assets//dispensers/
": { "function": ledger.get_dispensers, "args": [("status", int, 0)], @@ -250,6 +178,12 @@ }, } +for path, route in ROUTES.items(): + if not isinstance(route, dict): + ROUTES[path] = { + "function": route, + } + # Add compose routes for each transaction type ### /address//compose/ ### for transaction_name, compose_function in transaction.COMPOSE_FUNCTIONS.items(): diff --git a/counterparty-core/counterpartycore/lib/blocks.py b/counterparty-core/counterpartycore/lib/blocks.py index 6f46452532..8bfbda920f 100644 --- a/counterparty-core/counterpartycore/lib/blocks.py +++ b/counterparty-core/counterpartycore/lib/blocks.py @@ -901,9 +901,6 @@ def follow(db): check.software_version() last_software_check = time.time() - # Initialise. - initialise(db) - # Get index of last block. if ledger.CURRENT_BLOCK_INDEX == 0: logger.warning("New database.") diff --git a/counterparty-core/counterpartycore/lib/exceptions.py b/counterparty-core/counterpartycore/lib/exceptions.py index 07f846c577..cbe5260481 100644 --- a/counterparty-core/counterpartycore/lib/exceptions.py +++ b/counterparty-core/counterpartycore/lib/exceptions.py @@ -83,4 +83,8 @@ class ComposeTransactionError(Exception): pass +class InvalidArgument(Exception): + pass + + # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 592c477076..750c158d70 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -100,6 +100,23 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li return events +def get_events_by_block(db, block_index: int): + """ + Returns the events of a block + :param int block_index: The index of the block to return + """ + return get_events(db, block_index=block_index) + + +def get_events_by_block_and_event(db, block_index: int, event: str): + """ + Returns the events of a block filtered by event + :param int block_index: The index of the block to return + :param str event: The event to filter by + """ + return get_events(db, block_index=block_index, event=event) + + def get_mempool_events(db, event_name=None): cursor = db.cursor() where = [] @@ -137,6 +154,14 @@ def get_events_counts(db, block_index=None): return cursor.fetchall() +def get_events_counts_by_block(db, block_index: int): + """ + Returns the event counts of a block + :param int block_index: The index of the block to return + """ + return get_events_counts(db, block_index=block_index) + + # we are using a function here for testing purposes def curr_time(): return int(time.time()) @@ -358,7 +383,12 @@ def get_balance(db, address, asset, raise_error_if_no_balance=False, return_list return balances[0]["quantity"] -def get_balance_object(db, address, asset): +def get_balance_object(db, address: str, asset: str): + """ + Returns the balance of an address and asset + :param str address: The address to return + :param str asset: The asset to return + """ return { "address": address, "asset": asset, @@ -366,7 +396,11 @@ def get_balance_object(db, address, asset): } -def get_address_balances(db, address): +def get_address_balances(db, address: str): + """ + Returns the balances of an address + :param str address: The address to return + """ cursor = db.cursor() query = """ SELECT address, asset, quantity, MAX(rowid) @@ -433,10 +467,42 @@ def get_credits(db, address=None, asset=None, block_index=None, tx_index=None): return get_credits_or_debits(db, "credits", address, asset, block_index, tx_index) +def get_credits_by_block(db, block_index: int): + """ + Returns the credits of a block + :param int block_index: The index of the block to return + """ + return get_credits(db, block_index=block_index) + + +def get_credits_by_address(db, address: str): + """ + Returns the credits of an address + :param str address: The address to return + """ + return get_credits(db, address=address) + + def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): return get_credits_or_debits(db, "debits", address, asset, block_index, tx_index) +def get_debits_by_block(db, block_index: int): + """ + Returns the debits of a block + :param int block_index: The index of the block to return + """ + return get_debits(db, block_index=block_index) + + +def get_debits_by_address(db, address: str): + """ + Returns the debits of an address + :param str address: The address to return + """ + return get_debits(db, address=address) + + def get_sends_or_receives( db, source=None, destination=None, asset=None, block_index=None, status="valid" ): @@ -464,18 +530,60 @@ def get_sends_or_receives( return cursor.fetchall() +def get_sends_or_receives_by_block(db, block_index: int): + """ + Returns the sends of a block + :param int block_index: The index of the block to return + """ + return get_sends_or_receives(db, block_index=block_index) + + def get_sends(db, address=None, asset=None, block_index=None, status="valid"): return get_sends_or_receives( db, source=address, asset=asset, block_index=block_index, status=status ) +def get_send_by_address(db, address: str): + """ + Returns the sends of an address + :param str address: The address to return + """ + return get_sends(db, address=address) + + +def get_send_by_address_and_asset(db, address: str, asset: str): + """ + Returns the sends of an address and asset + :param str address: The address to return + :param str asset: The asset to return + """ + return get_sends(db, address=address, asset=asset) + + def get_receives(db, address=None, asset=None, block_index=None, status="valid"): return get_sends_or_receives( db, destination=address, asset=asset, block_index=block_index, status=status ) +def get_receive_by_address(db, address: str): + """ + Returns the receives of an address + :param str address: The address to return + """ + return get_receives(db, address=address) + + +def get_receive_by_address_and_asset(db, address: str, asset: str): + """ + Returns the receives of an address and asset + :param str address: The address to return + :param str asset: The asset to return + """ + return get_receives(db, address=address, asset=asset) + + def get_sweeps(db, address=None, block_index=None, status="valid"): cursor = db.cursor() where = [] @@ -495,6 +603,22 @@ def get_sweeps(db, address=None, block_index=None, status="valid"): return cursor.fetchall() +def get_sweeps_by_block(db, block_index: int): + """ + Returns the sweeps of a block + :param int block_index: The index of the block to return + """ + return get_sweeps(db, block_index=block_index) + + +def get_sweeps_by_address(db, address: str): + """ + Returns the sweeps of an address + :param str address: The address to return + """ + return get_sweeps(db, address=address) + + ##################### # ISSUANCES # ##################### @@ -856,6 +980,14 @@ def get_issuances( return cursor.fetchall() +def get_issuances_by_block(db, block_index: int): + """ + Returns the issuances of a block + :param int block_index: The index of the block to return + """ + return get_issuances(db, block_index=block_index) + + def get_assets_by_longname(db, asset_longname): cursor = db.cursor() query = """ @@ -926,7 +1058,15 @@ def get_oracle_last_price(db, oracle_address, block_index): ) -def get_broadcasts_by_source(db, address, status="valid", order_by="DESC"): +def get_broadcasts_by_source(db, address: str, status: str = "valid", order_by: str = "DESC"): + """ + Returns the broadcasts of a source + :param str address: The address to return + :param str status: The status of the broadcasts to return + :param str order_by: The order of the broadcasts to return + """ + if order_by not in ["ASC", "DESC"]: + raise exceptions.InvalidArgument("Invalid order_by parameter") cursor = db.cursor() query = f""" SELECT * FROM broadcasts @@ -943,7 +1083,12 @@ def get_broadcasts_by_source(db, address, status="valid", order_by="DESC"): ##################### -def get_burns(db, address=None, status="valid"): +def get_burns(db, address: str = None, status: str = "valid"): + """ + Returns the burns of an address + :param str address: The address to return + :param str status: The status of the burns to return + """ cursor = db.cursor() where = [] bindings = [] @@ -986,13 +1131,21 @@ def get_blocks(db, last: int = None, limit: int = 10): def get_block(db, block_index: int): + """ + Return the information of a block + :param int block_index: The index of the block to return + """ blocks = get_blocks(db, last=block_index, limit=1) if blocks: return blocks[0] return None -def get_transactions_by_block(db, block_index): +def get_transactions_by_block(db, block_index: int): + """ + Returns the transactions of a block + :param int block_index: The index of the block to return + """ cursor = db.cursor() query = """ SELECT * FROM transactions @@ -1031,7 +1184,11 @@ def get_transactions(db, tx_hash=None): return cursor.fetchall() -def get_transaction(db, tx_hash): +def get_transaction(db, tx_hash: str): + """ + Returns the information of a transaction + :param str tx_hash: The hash of the transaction to return + """ transactions = get_transactions(db, tx_hash) if transactions: return transactions[0] @@ -1059,7 +1216,11 @@ def get_addresses(db, address=None): return cursor.fetchall() -def get_expirations(db, block_index): +def get_expirations(db, block_index: int): + """ + Returns the expirations of a block + :param int block_index: The index of the block to return + """ cursor = db.cursor() queries = [ """ @@ -1093,7 +1254,11 @@ def get_expirations(db, block_index): return cursor.fetchall() -def get_cancels(db, block_index): +def get_cancels(db, block_index: int): + """ + Returns the cancels of a block + :param int block_index: The index of the block to return + """ cursor = db.cursor() query = """ SELECT * FROM cancels @@ -1104,7 +1269,11 @@ def get_cancels(db, block_index): return cursor.fetchall() -def get_destructions(db, block_index): +def get_destructions(db, block_index: int): + """ + Returns the destructions of a block + :param int block_index: The index of the block to return + """ cursor = db.cursor() query = """ SELECT * FROM destructions @@ -1455,6 +1624,31 @@ def get_dispensers( return cursor.fetchall() +def get_dispensers_by_address(db, address: str, status: int = 0): + """ + Returns the dispensers of an address + :param str address: The address to return + """ + return get_dispensers(db, address=address, status=status) + + +def get_dispensers_by_asset(db, asset: str, status: int = 0): + """ + Returns the dispensers of an asset + :param str asset: The asset to return + """ + return get_dispensers(db, asset=asset, status=status) + + +def get_dispensers_by_address_and_asset(db, address: str, asset: str, status: int = 0): + """ + Returns the dispensers of an address and an asset + :param str address: The address to return + :param str asset: The asset to return + """ + return get_dispensers(db, address=address, asset=asset, status=status) + + def get_dispenses(db, dispenser_tx_hash=None, block_index=None): cursor = db.cursor() where = [] @@ -1471,6 +1665,14 @@ def get_dispenses(db, dispenser_tx_hash=None, block_index=None): return cursor.fetchall() +def get_dispenses_by_block(db, block_index: int): + """ + Returns the dispenses of a block + :param int block_index: The index of the block to return + """ + return get_dispenses(db, block_index=block_index) + + ### UPDATES ### @@ -1569,7 +1771,12 @@ def get_matching_bets(db, feed_address, bet_type): return cursor.fetchall() -def get_bet_by_feed(db, address, status="open"): +def get_bet_by_feed(db, address: str, status: str = "open"): + """ + Returns the bets of a feed + :param str address: The address of the feed + :param str status: The status of the bet + """ cursor = db.cursor() query = """ SELECT * FROM ( diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 0254b83200..62a07fa449 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1153,8 +1153,12 @@ def get_compose_common_args(): return [(name, value[0], value[1]) for name, value in COMPOSE_COMMONS_ARGS.items()] -def info(db, rawtransaction, block_index=None): - # block_index mandatory for transactions before block 335000 +def info(db, rawtransaction: str, block_index: int = None): + """ + Returns Counterparty information from a raw transaction in hex format. + :param rawtransaction: Raw transaction in hex format + :param block_index: Block index mandatory for transactions before block 335000 + """ source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( db, BlockchainParser().deserialize_tx(rawtransaction), block_index=block_index ) @@ -1167,7 +1171,12 @@ def info(db, rawtransaction, block_index=None): } -def unpack(db, datahex, block_index=None): +def unpack(db, datahex: str, block_index: int = None): + """ + Unpacks Counterparty data in hex format and returns the message type and data. + :param datahex: Data in hex format + :param block_index: Block index of the transaction containing this data + """ data = binascii.unhexlify(datahex) message_type_id, message = message_type.unpack(data) block_index = block_index or ledger.CURRENT_BLOCK_INDEX diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index ce0cd9e020..e0e4ff2986 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -700,6 +700,8 @@ def start_all(args): bootstrap(no_confirm=True) db = initialise_db() + # Initialise. + blocks.initialise(db) # Reset UTXO_LOCKS. This previously was done in # initilise_config From c22ee5f2b53a9df8e8145bf7dac4ec6ba0f86416 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 18 Apr 2024 21:55:53 +0200 Subject: [PATCH 057/128] All routes documented except compose --- .../counterpartycore/lib/api/routes.py | 160 +++---------- .../counterpartycore/lib/api/util.py | 22 +- .../counterpartycore/lib/backend/__init__.py | 41 +++- .../lib/backend/addrindexrs.py | 4 +- .../counterpartycore/lib/ledger.py | 211 +++++++++++++++++- 5 files changed, 285 insertions(+), 153 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index b528b8eb10..2b0b2d3dff 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -1,6 +1,5 @@ from counterpartycore.lib import ( backend, - config, ledger, transaction, ) @@ -35,7 +34,7 @@ "/addresses/
/debits": ledger.get_debits_by_address, "/addresses/
/bets": ledger.get_bet_by_feed, "/addresses/
/broadcasts": ledger.get_broadcasts_by_source, - "/addresses/
/burns": ledger.get_burns, + "/addresses/
/burns": ledger.get_burns_by_address, "/addresses/
/sends": ledger.get_send_by_address, "/addresses/
/receives": ledger.get_receive_by_address, "/addresses/
/sends/": ledger.get_send_by_address_and_asset, @@ -44,138 +43,49 @@ "/addresses/
/dispensers/": ledger.get_dispensers_by_address_and_asset, "/addresses/
/sweeps": ledger.get_sweeps_by_address, ### /assets ### - "/assets": { - "function": ledger.get_valid_assets, - }, - "/assets/": { - "function": ledger.get_asset_info, - }, - "/assets//balances": { - "function": ledger.get_asset_balances, - }, - "/assets//balances/
": { - "function": ledger.get_balance_object, - }, - "/assets//orders": { - "function": ledger.get_orders_by_asset, - "args": [("status", str, "open")], - }, - "/assets//credits": { - "function": ledger.get_credits, - }, - "/assets//debits": { - "function": ledger.get_debits, - }, - "/assets//dividends": { - "function": ledger.get_dividends, - }, - "/assets//issuances": { - "function": ledger.get_issuances, - }, - "/assets//sends": { - "function": ledger.get_sends_or_receives, - }, + "/assets": ledger.get_valid_assets, + "/assets/": ledger.get_asset_info, + "/assets//balances": ledger.get_asset_balances, + "/assets//balances/
": ledger.get_balance_object, + "/assets//orders": ledger.get_orders_by_asset, + "/assets//credits": ledger.get_credits_by_asset, + "/assets//debits": ledger.get_debits_by_asset, + "/assets//dividends": ledger.get_dividends, + "/assets//issuances": ledger.get_issuances_by_asset, + "/assets//sends": ledger.get_sends_or_receives_by_asset, "/assets//dispensers": ledger.get_dispensers_by_asset, - "/assets//dispensers/
": { - "function": ledger.get_dispensers, - "args": [("status", int, 0)], - }, - "/assets//holders": { - "function": ledger.get_asset_holders, - }, + "/assets//dispensers/
": ledger.get_dispensers_by_address_and_asset, + "/assets//holders": ledger.get_asset_holders, ### /orders ### - "/orders/": { - "function": ledger.get_order, - }, - "/orders//matches": { - "function": ledger.get_order_matches_by_order, - "args": [("status", str, "pending")], - }, - "/orders//btcpays": { - "function": ledger.get_btcpays_by_order, - "args": [("status", str, "pending")], - }, + "/orders/": ledger.get_order, + "/orders//matches": ledger.get_order_matches_by_order, + "/orders//btcpays": ledger.get_btcpays_by_order, ### /bets ### - "/bets/": { - "function": ledger.get_bet, - }, - "/bets//matches": { - "function": ledger.get_bet_matches_by_bet, - "args": [("status", str, "pending")], - }, - "/bets//resolutions": { - "function": ledger.get_resolutions_by_bet, - "args": [("status", str, "pending")], - }, + "/bets/": ledger.get_bet, + "/bets//matches": ledger.get_bet_matches_by_bet, + "/bets//resolutions": ledger.get_resolutions_by_bet, ### /burns ### - "/burns": { - "function": ledger.get_burns, - }, + "/burns": ledger.get_all_burns, ### /dispensers ### - "/dispensers/": { - "function": ledger.get_dispenser_info, - }, - "/dispensers//dispenses": { - "function": ledger.get_dispenses, - }, + "/dispensers/": ledger.get_dispenser_info_by_tx_hash, + "/dispensers//dispenses": ledger.get_dispenses_by_dispenser, ### /events ### - "/events": { - "function": ledger.get_events, - "args": [("last", int, None), ("limit", int, 100)], - }, - "/events/": { - "function": ledger.get_events, - }, - "/events/counts": { - "function": ledger.get_events_counts, - }, - "/events/": { - "function": ledger.get_events, - "args": [("last", int, None), ("limit", int, 100)], - }, + "/events": ledger.get_all_events, + "/events/": ledger.get_event_by_index, + "/events/counts": ledger.get_all_events_counts, + "/events/": ledger.get_events_by_event, ### /healthz ### - "/healthz": { - "function": util.handle_healthz_route, - "args": [("check_type", str, "heavy")], - }, + "/healthz": util.handle_healthz_route, ### /backend ### - "/backend/addresses/
/transactions": { - "function": backend.search_raw_transactions, - "args": [("unconfirmed", bool, True), ("only_tx_hashes", bool, False)], - }, - "/backend/addresses/
/transactions/oldest": { - "function": backend.get_oldest_tx, - }, - "/backend/addresses/
/utxos": { - "function": backend.get_unspent_txouts, - "args": [("unconfirmed", bool, True), ("unspent_tx_hash", str, None)], - }, - "/backend/addresses/
/pubkey": { - "function": util.pubkeyhash_to_pubkey, - "args": [("provided_pubkeys", str, "")], - }, - "/backend/transactions": { - "function": util.getrawtransactions, - "args": [("tx_hashes", str, ""), ("verbose", bool, False), ("skip_missing", bool, False)], - }, - "/backend/transactions/": { - "function": backend.getrawtransaction, - "args": [("verbose", bool, False), ("skip_missing", bool, False)], - }, - "/backend/estimatesmartfee": { - "function": backend.fee_per_kb, - "args": [ - ("conf_target", int, config.ESTIMATE_FEE_CONF_TARGET), - ("mode", str, config.ESTIMATE_FEE_MODE), - ], - }, + "/backend/addresses/
/transactions": backend.search_raw_transactions, + "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, + "/backend/addresses/
/utxos": backend.get_unspent_txouts, + "/backend/addresses/
/pubkey": util.pubkeyhash_to_pubkey, + "/backend/transactions/": util.get_raw_transaction, + "/backend/estimatesmartfee": backend.fee_per_kb, ### /mempool ### - "/mempool/events": { - "function": ledger.get_mempool_events, - }, - "/mempool/events/": { - "function": ledger.get_mempool_events, - }, + "/mempool/events": ledger.get_all_mempool_events, + "/mempool/events/": ledger.get_mempool_events_by_event, } for path, route in ROUTES.items(): diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 32a183e928..8e82357c07 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -51,7 +51,11 @@ def healthz(db, check_type="heavy"): return True -def handle_healthz_route(db, check_type="heavy"): +def handle_healthz_route(db, check_type: str = "heavy"): + """ + Health check route. + :param check_type: Type of health check to perform. Options are 'light' and 'heavy'. + """ msg, code = "Healthy", 200 if not healthz(db, check_type): msg, code = "Unhealthy", 503 @@ -84,7 +88,12 @@ def getrawtransactions(tx_hashes, verbose=False, skip_missing=False, _retry=0): return backend.getrawtransaction_batch(txhash_list, verbose, skip_missing, _retry) -def pubkeyhash_to_pubkey(address, provided_pubkeys=None): +def pubkeyhash_to_pubkey(address: str, provided_pubkeys: str = None): + """ + Get pubkey for an address. + :param address: Address to get pubkey for. + :param provided_pubkeys: Comma separated list of provided pubkeys. + """ if provided_pubkeys: provided_pubkeys_list = provided_pubkeys.split(",") else: @@ -92,6 +101,15 @@ def pubkeyhash_to_pubkey(address, provided_pubkeys=None): return backend.pubkeyhash_to_pubkey(address, provided_pubkeys=provided_pubkeys_list) +def get_raw_transaction(tx_hash: str, verbose: bool = False): + """ + Get a raw transaction from the blockchain + :param tx_hash: The transaction hash + :param verbose: Whether to return JSON output or raw hex + """ + return backend.getrawtransaction(tx_hash, verbose=verbose) + + def get_backend_height(): block_count = backend.getblockcount() blocks_behind = backend.getindexblocksbehind() diff --git a/counterparty-core/counterpartycore/lib/backend/__init__.py b/counterparty-core/counterpartycore/lib/backend/__init__.py index bf476b658e..4ed53f602a 100644 --- a/counterparty-core/counterpartycore/lib/backend/__init__.py +++ b/counterparty-core/counterpartycore/lib/backend/__init__.py @@ -72,7 +72,9 @@ def clear_pretx(txid): del PRETX_CACHE[binascii.hexlify(txid).decode("utf8")] -def getrawtransaction(tx_hash, verbose=False, skip_missing=False, block_index=None): +def getrawtransaction( + tx_hash: str, verbose: bool = False, skip_missing: bool = False, block_index: int = None +): if block_index and block_index in prefetcher.BLOCKCHAIN_CACHE: return prefetcher.BLOCKCHAIN_CACHE[block_index]["raw_transactions"][tx_hash] @@ -124,14 +126,15 @@ def ensure_script_pub_key_for_inputs(coins): return coins -def fee_per_kb(conf_target, mode, nblocks=None): +def fee_per_kb( + conf_target: int = config.ESTIMATE_FEE_CONF_TARGET, mode: str = config.ESTIMATE_FEE_MODE +): """ - :param conf_target: - :param mode: - :return: fee_per_kb in satoshis, or None when unable to determine + Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. + :param conf_target: Confirmation target in blocks (1 - 1008) + :param mode: The fee estimate mode. """ - - return backend().fee_per_kb(conf_target, mode, nblocks=nblocks) + return backend().fee_per_kb(conf_target, mode, nblocks=None) def deserialize(tx_hex): @@ -207,9 +210,12 @@ class MempoolError(Exception): pass -def get_unspent_txouts(source, unconfirmed=False, unspent_tx_hash=None): - """returns a list of unspent outputs for a specific address - @return: A list of dicts, with each entry in the dict having the following keys: +def get_unspent_txouts(source: str, unconfirmed: bool = False, unspent_tx_hash: str = None): + """ + Returns a list of unspent outputs for a specific address + :param source: The address to search for + :param unconfirmed: Include unconfirmed transactions + :param unspent_tx_hash: Filter by unspent_tx_hash """ unspent = backend().get_unspent_txouts(source) @@ -232,11 +238,22 @@ def get_unspent_txouts(source, unconfirmed=False, unspent_tx_hash=None): return unspent -def search_raw_transactions(address, unconfirmed=True, only_tx_hashes=False): +def search_raw_transactions(address, unconfirmed: bool = True, only_tx_hashes: bool = False): + """ + Returns all transactions involving a given address + :param address: The address to search for + :param unconfirmed: Include unconfirmed transactions + :param only_tx_hashes: Return only the tx hashes + """ return backend().search_raw_transactions(address, unconfirmed, only_tx_hashes) -def get_oldest_tx(address, block_index=None): +def get_oldest_tx(address: str, block_index: int = None): + """ + Get the oldest transaction for an address. + :param address: The address to search for. + :param block_index: The block index to search from. + """ return backend().get_oldest_tx(address, block_index=block_index) diff --git a/counterparty-core/counterpartycore/lib/backend/addrindexrs.py b/counterparty-core/counterpartycore/lib/backend/addrindexrs.py index 2ea4fd4d9c..4bd492b0d3 100644 --- a/counterparty-core/counterpartycore/lib/backend/addrindexrs.py +++ b/counterparty-core/counterpartycore/lib/backend/addrindexrs.py @@ -667,7 +667,7 @@ def get_unspent_txouts(source): # ] # # } -def search_raw_transactions(address, unconfirmed=True, only_tx_hashes=False): +def search_raw_transactions(address, unconfirmed: bool = True, only_tx_hashes: bool = False): hsh = _address_to_hash(address) txs = INDEXER_THREAD.send({"method": "blockchain.scripthash.get_history", "params": [hsh]})[ "result" @@ -801,7 +801,7 @@ def get_oldest_tx(self, address, timeout=ADDRINDEXRS_CLIENT_TIMEOUT, block_index ADDRINDEXRS_CLIENT = None -def get_oldest_tx(address, block_index=None): +def get_oldest_tx(address: str, block_index: int = None): current_block_index = block_index or ledger.CURRENT_BLOCK_INDEX hardcoded_key = f"{current_block_index}-{address}" if hardcoded_key in GET_OLDEST_TX_HARDCODED: diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 750c158d70..6700c1a560 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -100,6 +100,15 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li return events +def get_all_events(db, last: int = None, limit: int = 100): + """ + Returns all events + :param int last: The last event index to return + :param int limit: The maximum number of events to return + """ + return get_events(db, last=last, limit=limit) + + def get_events_by_block(db, block_index: int): """ Returns the events of a block @@ -117,6 +126,24 @@ def get_events_by_block_and_event(db, block_index: int, event: str): return get_events(db, block_index=block_index, event=event) +def get_event_by_index(db, event_index: int): + """ + Returns the event of an index + :param int event_index: The index of the event to return + """ + return get_events(db, event_index=event_index) + + +def get_events_by_event(db, event: str, last: int = None, limit: int = 100): + """ + Returns the events filtered by event name + :param str event: The event to return + :param int last: The last event index to return + :param int limit: The maximum number of events to return + """ + return get_events(db, event=event, last=last, limit=limit) + + def get_mempool_events(db, event_name=None): cursor = db.cursor() where = [] @@ -139,6 +166,21 @@ def get_mempool_events(db, event_name=None): return events +def get_all_mempool_events(db): + """ + Returns all mempool events + """ + return get_mempool_events(db) + + +def get_mempool_events_by_event(db, event_name: str): + """ + Returns the mempool events filtered by event name + :param str event_name: The event to return + """ + return get_mempool_events(db, event_name=event_name) + + def get_events_counts(db, block_index=None): cursor = db.cursor() bindings = [] @@ -162,6 +204,13 @@ def get_events_counts_by_block(db, block_index: int): return get_events_counts(db, block_index=block_index) +def get_all_events_counts(db): + """ + Returns the event counts of all blocks + """ + return get_events_counts(db) + + # we are using a function here for testing purposes def curr_time(): return int(time.time()) @@ -483,6 +532,14 @@ def get_credits_by_address(db, address: str): return get_credits(db, address=address) +def get_credits_by_asset(db, asset: str): + """ + Returns the credits of an asset + :param str asset: The asset to return + """ + return get_credits(db, asset=asset) + + def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): return get_credits_or_debits(db, "debits", address, asset, block_index, tx_index) @@ -503,6 +560,14 @@ def get_debits_by_address(db, address: str): return get_debits(db, address=address) +def get_debits_by_asset(db, asset: str): + """ + Returns the debits of an asset + :param str asset: The asset to return + """ + return get_debits(db, asset=asset) + + def get_sends_or_receives( db, source=None, destination=None, asset=None, block_index=None, status="valid" ): @@ -538,6 +603,14 @@ def get_sends_or_receives_by_block(db, block_index: int): return get_sends_or_receives(db, block_index=block_index) +def get_sends_or_receives_by_asset(db, asset: str): + """ + Returns the sends of an asset + :param str asset: The asset to return + """ + return get_sends_or_receives(db, asset=asset) + + def get_sends(db, address=None, asset=None, block_index=None, status="valid"): return get_sends_or_receives( db, source=address, asset=asset, block_index=block_index, status=status @@ -867,7 +940,12 @@ def get_asset_issued(db, address): return cursor.fetchall() -def get_asset_balances(db, asset, exclude_zero_balances=True): +def get_asset_balances(db, asset: str, exclude_zero_balances: bool = True): + """ + Returns the asset balances + :param str asset: The asset to return + :param bool exclude_zero_balances: Whether to exclude zero balances + """ cursor = db.cursor() query = """ SELECT address, asset, quantity, MAX(rowid) @@ -901,7 +979,11 @@ def get_asset_issuances_quantity(db, asset): return issuances[0]["issuances_count"] -def get_asset_info(db, asset): +def get_asset_info(db, asset: str): + """ + Returns the asset information + :param str asset: The asset to return + """ asset_name = resolve_subasset_longname(db, asset) # Defaults. @@ -988,6 +1070,14 @@ def get_issuances_by_block(db, block_index: int): return get_issuances(db, block_index=block_index) +def get_issuances_by_asset(db, asset: str): + """ + Returns the issuances of an asset + :param str asset: The asset to return + """ + return get_issuances(db, asset=asset) + + def get_assets_by_longname(db, asset_longname): cursor = db.cursor() query = """ @@ -999,7 +1089,17 @@ def get_assets_by_longname(db, asset_longname): return cursor.fetchall() -def get_valid_assets(db): +def get_valid_assets(db, offset: int = 0, limit: int = 100): + """ + Returns the valid assets + :param int offset: The offset of the assets to return + :param int limit: The limit of the assets to return + """ + try: + int(offset) + int(limit) + except ValueError as e: + raise exceptions.InvalidArgument("Invalid offset or limit parameter") from e cursor = db.cursor() query = """ SELECT asset, asset_longname @@ -1012,7 +1112,11 @@ def get_valid_assets(db): return cursor.fetchall() -def get_dividends(db, asset): +def get_dividends(db, asset: str): + """ + Returns the dividends of an asset + :param str asset: The asset to return + """ cursor = db.cursor() query = """ SELECT * FROM dividends @@ -1104,6 +1208,38 @@ def get_burns(db, address: str = None, status: str = "valid"): return cursor.fetchall() +def get_burns_by_address(db, address: str): + """ + Returns the burns of an address + :param str address: The address to return + """ + return get_burns(db, address=address) + + +def get_all_burns(db, status: str = "valid", offset: int = 0, limit: int = 100): + """ + Returns the burns + :param str status: The status of the burns to return + :param int offset: The offset of the burns to return + :param int limit: The limit of the burns to return + """ + try: + int(offset) + int(limit) + except ValueError as e: + raise exceptions.InvalidArgument("Invalid offset or limit parameter") from e + cursor = db.cursor() + query = """ + SELECT * FROM burns + WHERE status = ? + ORDER BY tx_index ASC + LIMIT ? OFFSET ? + """ + bindings = (status, limit, offset) + cursor.execute(query, bindings) + return cursor.fetchall() + + ###################################### # BLOCKS AND TRANSACTIONS # ###################################### @@ -1455,6 +1591,14 @@ def get_dispenser_info(db, tx_hash=None, tx_index=None): return cursor.fetchall() +def get_dispenser_info_by_tx_hash(db, tx_hash: str): + """ + Returns the dispenser information by tx_hash + :param str tx_hash: The hash of the dispenser to return + """ + return get_dispenser_info(db, tx_hash=tx_hash) + + def get_refilling_count(db, dispenser_tx_hash): cursor = db.cursor() query = """ @@ -1673,6 +1817,14 @@ def get_dispenses_by_block(db, block_index: int): return get_dispenses(db, block_index=block_index) +def get_dispenses_by_dispenser(db, tx_hash: str): + """ + Returns the dispenses of a dispenser + :param str tx_hash: The hash of the dispenser to return + """ + return get_dispenses(db, dispenser_tx_hash=tx_hash) + + ### UPDATES ### @@ -1727,7 +1879,11 @@ def get_bet_matches_to_expire(db, block_time): return cursor.fetchall() -def get_bet(db, tx_hash): +def get_bet(db, tx_hash: str): + """ + Returns the information of a bet + :param str tx_hash: The hash of the bet to return + """ cursor = db.cursor() query = """ SELECT * FROM bets @@ -1792,7 +1948,12 @@ def get_bet_by_feed(db, address: str, status: str = "open"): return cursor.fetchall() -def get_bet_matches_by_bet(db, tx_hash, status="pending"): +def get_bet_matches_by_bet(db, tx_hash: str, status: str = "pending"): + """ + Returns the bet matches of a bet + :param str tx_hash: The hash of the bet + :param str status: The status of the bet matches + """ cursor = db.cursor() query = """ SELECT * FROM ( @@ -1807,7 +1968,11 @@ def get_bet_matches_by_bet(db, tx_hash, status="pending"): return cursor.fetchall() -def get_resolutions_by_bet(db, tx_hash): +def get_resolutions_by_bet(db, tx_hash: str): + """ + Returns the resolutions of a bet + :param str tx_hash: The hash of the bet + """ cursor = db.cursor() query = """ SELECT * @@ -1898,7 +2063,11 @@ def get_order_matches_to_expire(db, block_index): return cursor.fetchall() -def get_order(db, tx_hash): +def get_order(db, tx_hash: str): + """ + Returns the information of an order + :param str tx_hash: The hash of the order to return + """ cursor = db.cursor() query = """ SELECT * FROM orders @@ -1969,7 +2138,12 @@ def get_matching_orders(db, tx_hash, give_asset, get_asset): return cursor.fetchall() -def get_orders_by_asset(db, asset, status="open"): +def get_orders_by_asset(db, asset: str, status: str = "open"): + """ + Returns the orders of an asset + :param str asset: The asset to return + :param str status: The status of the orders to return + """ cursor = db.cursor() query = """ SELECT * FROM ( @@ -1984,7 +2158,12 @@ def get_orders_by_asset(db, asset, status="open"): return cursor.fetchall() -def get_order_matches_by_order(db, tx_hash, status="pending"): +def get_order_matches_by_order(db, tx_hash: str, status: str = "pending"): + """ + Returns the order matches of an order + :param str tx_hash: The hash of the order + :param str status: The status of the order matches to return + """ cursor = db.cursor() query = """ SELECT * FROM ( @@ -1999,7 +2178,11 @@ def get_order_matches_by_order(db, tx_hash, status="pending"): return cursor.fetchall() -def get_btcpays_by_order(db, tx_hash): +def get_btcpays_by_order(db, tx_hash: str): + """ + Returns the BTC pays of an order + :param str tx_hash: The hash of the order + """ cursor = db.cursor() query = """ SELECT * @@ -2439,7 +2622,11 @@ def holders(db, asset, exclude_empty_holders=False): return holders -def get_asset_holders(db, asset): +def get_asset_holders(db, asset: str): + """ + Returns the holders of an asset + :param str asset: The asset to return + """ asset_name = resolve_subasset_longname(db, asset) return holders(db, asset_name, True) From 1c3b049a06dbece814ce8dc61f04f72d050eb869 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 14:38:31 +0200 Subject: [PATCH 058/128] One route, one function. Even for compose functions --- .../counterpartycore/lib/api/routes.py | 198 +++++++++--------- .../counterpartycore/lib/api/util.py | 23 ++ .../counterpartycore/lib/transaction.py | 142 ++++++++++--- 3 files changed, 232 insertions(+), 131 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 2b0b2d3dff..821a2e24fc 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -6,105 +6,101 @@ from counterpartycore.lib.api import util # Define the API routes except root (`/`) defined in `api_server.py` -ROUTES = { - ### /blocks ### - "/blocks": ledger.get_blocks, - "/blocks/": ledger.get_block, - "/blocks//transactions": ledger.get_transactions_by_block, - "/blocks//events": ledger.get_events_by_block, - "/blocks//events/counts": ledger.get_events_counts_by_block, - "/blocks//events/": ledger.get_events_by_block_and_event, - "/blocks//credits": ledger.get_credits_by_block, - "/blocks//debits": ledger.get_debits_by_block, - "/blocks//expirations": ledger.get_expirations, - "/blocks//cancels": ledger.get_cancels, - "/blocks//destructions": ledger.get_destructions, - "/blocks//issuances": ledger.get_issuances_by_block, - "/blocks//sends": ledger.get_sends_or_receives_by_block, - "/blocks//dispenses": ledger.get_dispenses_by_block, - "/blocks//sweeps": ledger.get_sweeps_by_block, - ### /transactions ### - "/transactions/info": transaction.info, - "/transactions/unpack": transaction.unpack, - "/transactions/": ledger.get_transaction, - ### /addresses ### - "/addresses/
/balances": ledger.get_address_balances, - "/addresses/
/balances/": ledger.get_balance_object, - "/addresses/
/credits": ledger.get_credits_by_address, - "/addresses/
/debits": ledger.get_debits_by_address, - "/addresses/
/bets": ledger.get_bet_by_feed, - "/addresses/
/broadcasts": ledger.get_broadcasts_by_source, - "/addresses/
/burns": ledger.get_burns_by_address, - "/addresses/
/sends": ledger.get_send_by_address, - "/addresses/
/receives": ledger.get_receive_by_address, - "/addresses/
/sends/": ledger.get_send_by_address_and_asset, - "/addresses/
/receives/": ledger.get_receive_by_address_and_asset, - "/addresses/
/dispensers": ledger.get_dispensers_by_address, - "/addresses/
/dispensers/": ledger.get_dispensers_by_address_and_asset, - "/addresses/
/sweeps": ledger.get_sweeps_by_address, - ### /assets ### - "/assets": ledger.get_valid_assets, - "/assets/": ledger.get_asset_info, - "/assets//balances": ledger.get_asset_balances, - "/assets//balances/
": ledger.get_balance_object, - "/assets//orders": ledger.get_orders_by_asset, - "/assets//credits": ledger.get_credits_by_asset, - "/assets//debits": ledger.get_debits_by_asset, - "/assets//dividends": ledger.get_dividends, - "/assets//issuances": ledger.get_issuances_by_asset, - "/assets//sends": ledger.get_sends_or_receives_by_asset, - "/assets//dispensers": ledger.get_dispensers_by_asset, - "/assets//dispensers/
": ledger.get_dispensers_by_address_and_asset, - "/assets//holders": ledger.get_asset_holders, - ### /orders ### - "/orders/": ledger.get_order, - "/orders//matches": ledger.get_order_matches_by_order, - "/orders//btcpays": ledger.get_btcpays_by_order, - ### /bets ### - "/bets/": ledger.get_bet, - "/bets//matches": ledger.get_bet_matches_by_bet, - "/bets//resolutions": ledger.get_resolutions_by_bet, - ### /burns ### - "/burns": ledger.get_all_burns, - ### /dispensers ### - "/dispensers/": ledger.get_dispenser_info_by_tx_hash, - "/dispensers//dispenses": ledger.get_dispenses_by_dispenser, - ### /events ### - "/events": ledger.get_all_events, - "/events/": ledger.get_event_by_index, - "/events/counts": ledger.get_all_events_counts, - "/events/": ledger.get_events_by_event, - ### /healthz ### - "/healthz": util.handle_healthz_route, - ### /backend ### - "/backend/addresses/
/transactions": backend.search_raw_transactions, - "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, - "/backend/addresses/
/utxos": backend.get_unspent_txouts, - "/backend/addresses/
/pubkey": util.pubkeyhash_to_pubkey, - "/backend/transactions/": util.get_raw_transaction, - "/backend/estimatesmartfee": backend.fee_per_kb, - ### /mempool ### - "/mempool/events": ledger.get_all_mempool_events, - "/mempool/events/": ledger.get_mempool_events_by_event, -} - -for path, route in ROUTES.items(): - if not isinstance(route, dict): - ROUTES[path] = { - "function": route, - } - -# Add compose routes for each transaction type -### /address//compose/ ### -for transaction_name, compose_function in transaction.COMPOSE_FUNCTIONS.items(): - ROUTES[f"/address//compose/{transaction_name}"] = { - "function": compose_function, - "pass_all_args": True, +ROUTES = util.prepare_routes( + { + ### /blocks ### + "/blocks": ledger.get_blocks, + "/blocks/": ledger.get_block, + "/blocks//transactions": ledger.get_transactions_by_block, + "/blocks//events": ledger.get_events_by_block, + "/blocks//events/counts": ledger.get_events_counts_by_block, + "/blocks//events/": ledger.get_events_by_block_and_event, + "/blocks//credits": ledger.get_credits_by_block, + "/blocks//debits": ledger.get_debits_by_block, + "/blocks//expirations": ledger.get_expirations, + "/blocks//cancels": ledger.get_cancels, + "/blocks//destructions": ledger.get_destructions, + "/blocks//issuances": ledger.get_issuances_by_block, + "/blocks//sends": ledger.get_sends_or_receives_by_block, + "/blocks//dispenses": ledger.get_dispenses_by_block, + "/blocks//sweeps": ledger.get_sweeps_by_block, + ### /transactions ### + "/transactions/info": transaction.info, + "/transactions/unpack": transaction.unpack, + "/transactions/": ledger.get_transaction, + ### /addresses ### + "/addresses/
/balances": ledger.get_address_balances, + "/addresses/
/balances/": ledger.get_balance_object, + "/addresses/
/credits": ledger.get_credits_by_address, + "/addresses/
/debits": ledger.get_debits_by_address, + "/addresses/
/bets": ledger.get_bet_by_feed, + "/addresses/
/broadcasts": ledger.get_broadcasts_by_source, + "/addresses/
/burns": ledger.get_burns_by_address, + "/addresses/
/sends": ledger.get_send_by_address, + "/addresses/
/receives": ledger.get_receive_by_address, + "/addresses/
/sends/": ledger.get_send_by_address_and_asset, + "/addresses/
/receives/": ledger.get_receive_by_address_and_asset, + "/addresses/
/dispensers": ledger.get_dispensers_by_address, + "/addresses/
/dispensers/": ledger.get_dispensers_by_address_and_asset, + "/addresses/
/sweeps": ledger.get_sweeps_by_address, + ### /address/
/compose/ ### + "/address/
/compose/bet": transaction.compose_bet, + # "/address/
/compose/broadcast": transaction.compose_broadcast, + # "/address/
/compose/btcpay": transaction.compose_btcpay, + # "/address/
/compose/burn": transaction.compose_burn, + # "/address/
/compose/cancel": transaction.compose_cancel, + # "/address/
/compose/destroy": transaction.compose_destroy, + # "/address/
/compose/dispenser": transaction.compose_dispenser, + # "/address/
/compose/dividend": transaction.compose_dividend, + # "/address/
/compose/issuance": transaction.compose_issuance, + # "/address/
/compose/order": transaction.compose_order, + # "/address/
/compose/send": transaction.compose_send, + # "/address/
/compose/rps": transaction.compose_rps, + # "/address/
/compose/rpsresolve": transaction.compose_rpsresolve, + # "/address/
/compose/sweep": transaction.compose_sweep, + ### /assets ### + "/assets": ledger.get_valid_assets, + "/assets/": ledger.get_asset_info, + "/assets//balances": ledger.get_asset_balances, + "/assets//balances/
": ledger.get_balance_object, + "/assets//orders": ledger.get_orders_by_asset, + "/assets//credits": ledger.get_credits_by_asset, + "/assets//debits": ledger.get_debits_by_asset, + "/assets//dividends": ledger.get_dividends, + "/assets//issuances": ledger.get_issuances_by_asset, + "/assets//sends": ledger.get_sends_or_receives_by_asset, + "/assets//dispensers": ledger.get_dispensers_by_asset, + "/assets//dispensers/
": ledger.get_dispensers_by_address_and_asset, + "/assets//holders": ledger.get_asset_holders, + ### /orders ### + "/orders/": ledger.get_order, + "/orders//matches": ledger.get_order_matches_by_order, + "/orders//btcpays": ledger.get_btcpays_by_order, + ### /bets ### + "/bets/": ledger.get_bet, + "/bets//matches": ledger.get_bet_matches_by_bet, + "/bets//resolutions": ledger.get_resolutions_by_bet, + ### /burns ### + "/burns": ledger.get_all_burns, + ### /dispensers ### + "/dispensers/": ledger.get_dispenser_info_by_tx_hash, + "/dispensers//dispenses": ledger.get_dispenses_by_dispenser, + ### /events ### + "/events": ledger.get_all_events, + "/events/": ledger.get_event_by_index, + "/events/counts": ledger.get_all_events_counts, + "/events/": ledger.get_events_by_event, + ### /healthz ### + "/healthz": util.handle_healthz_route, + ### /backend ### + "/backend/addresses/
/transactions": backend.search_raw_transactions, + "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, + "/backend/addresses/
/utxos": backend.get_unspent_txouts, + "/backend/addresses/
/pubkey": util.pubkeyhash_to_pubkey, + "/backend/transactions/": util.get_raw_transaction, + "/backend/estimatesmartfee": backend.fee_per_kb, + ### /mempool ### + "/mempool/events": ledger.get_all_mempool_events, + "/mempool/events/": ledger.get_mempool_events_by_event, } - -# Add description and args to each route -for path, route in ROUTES.items(): - if "/compose/" in path: - continue - ROUTES[path]["description"] = util.get_function_description(route["function"]) - ROUTES[path]["args"] = util.prepare_route_args(route["function"]) +) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 8e82357c07..06d0fa0713 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -150,6 +150,18 @@ def prepare_route_args(function): function_args = inspect.signature(function).parameters args_description = get_args_description(function) for arg_name, arg in function_args.items(): + if arg_name == "construct_args": + for carg_name, carg_info in transaction.COMPOSE_COMMONS_ARGS.items(): + args.append( + { + "name": carg_name, + "type": carg_info[0].__name__, + "default": carg_info[1], + "description": carg_info[2], + "required": False, + } + ) + continue annotation = arg.annotation if annotation is inspect.Parameter.empty: continue @@ -170,3 +182,14 @@ def prepare_route_args(function): def get_function_description(function): docstring = parse_docstring(function.__doc__) return docstring.description + + +def prepare_routes(routes): + prepared_routes = {} + for route, function in routes.items(): + prepared_routes[route] = { + "function": function, + "description": get_function_description(function), + "args": prepare_route_args(function), + } + return prepared_routes diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 62a07fa449..c9c52463eb 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -6,7 +6,6 @@ import binascii import decimal -import functools import hashlib import inspect import io @@ -957,6 +956,78 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): return dust_return_pubkey +COMPOSE_COMMONS_ARGS = { + "encoding": (str, "auto", "The encoding method to use"), + "fee_per_kb": ( + int, + None, + "The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi)", + ), + "regular_dust_size": ( + int, + config.DEFAULT_REGULAR_DUST_SIZE, + "Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output.", + ), + "multisig_dust_size": ( + int, + config.DEFAULT_MULTISIG_DUST_SIZE, + "Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output", + ), + "op_return_value": ( + int, + config.DEFAULT_OP_RETURN_VALUE, + "The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away", + ), + "pubkey": ( + str, + None, + "The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash.", + ), + "allow_unconfirmed_inputs": ( + bool, + False, + "Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs", + ), + "fee": ( + int, + None, + "If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose", + ), + "fee_provided": ( + int, + 0, + "If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value", + ), + "unspent_tx_hash": ( + str, + None, + "When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs", + ), + "dust_return_pubkey": ( + str, + None, + "The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception", + ), + "disable_utxo_locks": ( + bool, + False, + "By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs", + ), + "extended_tx_info": ( + bool, + False, + "When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee", + ), + "p2sh_pretx_txid": ( + str, + None, + "The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction", + ), + "old_style_api": (bool, True, "Use the old style API"), + "segwit": (bool, False, "Use segwit"), +} + + def split_compose_arams(**kwargs): transaction_args = {} common_args = {} @@ -980,27 +1051,6 @@ def get_default_args(func): } -COMPOSE_COMMONS_ARGS = { - "encoding": (str, "auto"), - "fee_per_kb": (int, None), - "estimate_fee_per_kb": (int, None), - "regular_dust_size": (int, config.DEFAULT_REGULAR_DUST_SIZE), - "multisig_dust_size": (int, config.DEFAULT_MULTISIG_DUST_SIZE), - "op_return_value": (int, config.DEFAULT_OP_RETURN_VALUE), - "pubkey": (str, None), - "allow_unconfirmed_inputs": (bool, False), - "fee": (int, None), - "fee_provided": (int, 0), - "unspent_tx_hash": (str, None), - "dust_return_pubkey": (str, None), - "disable_utxo_locks": (bool, False), - "extended_tx_info": (bool, False), - "p2sh_pretx_txid": (str, None), - "old_style_api": (bool, True), - "segwit": (bool, False), -} - - def compose_transaction( db, name, @@ -1142,17 +1192,49 @@ def compose(db, source, transaction_name, **kwargs): return compose_transaction(db, name=transaction_name, params=transaction_args, **common_args) -COMPOSE_FUNCTIONS = {} -for transaction_name in COMPOSABLE_TRANSACTIONS: - COMPOSE_FUNCTIONS[transaction_name] = functools.partial( - compose, transaction_name=transaction_name +def compose_bet( + db, + address: str, + feed_address: str, + bet_type: int, + deadline: int, + wager_quantity: int, + counterwager_quantity: int, + expiration: int, + leverage: int = 5040, + target_value: float = None, + **construct_args, +): + """ + Composes a transaction to issue a bet against a feed. + :param address: The address that will make the bet + :param feed_address: The address that hosts the feed to be bet on + :param bet_type: Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual + :param deadline: The time at which the bet should be decided/settled, in Unix time (seconds since epoch) + :param wager_quantity: The quantities of XCP to wager (in satoshis, hence integer). + :param counterwager_quantity: The minimum quantities of XCP to be wagered against, for the bets to match + :param target_value: Target value for Equal/NotEqual bet + :param leverage: Leverage, as a fraction of 5040 + :param expiration: The number of blocks after which the bet expires if it remains unmatched + """ + return compose_transaction( + db, + name="bet", + params={ + "source": address, + "feed_address": feed_address, + "bet_type": bet_type, + "deadline": deadline, + "wager_quantity": wager_quantity, + "counterwager_quantity": counterwager_quantity, + "target_value": target_value, + "leverage": leverage, + "expiration": expiration, + }, + **construct_args, ) -def get_compose_common_args(): - return [(name, value[0], value[1]) for name, value in COMPOSE_COMMONS_ARGS.items()] - - def info(db, rawtransaction: str, block_index: int = None): """ Returns Counterparty information from a raw transaction in hex format. From d89d5f3d3c9262cb4e8a6f4cff36e46722b2a65e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 15:26:58 +0200 Subject: [PATCH 059/128] progress in compose routes --- .../counterpartycore/lib/api/routes.py | 8 +-- .../lib/messages/broadcast.py | 2 +- .../counterpartycore/lib/transaction.py | 68 +++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 821a2e24fc..8c02b2384b 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -45,10 +45,10 @@ "/addresses/
/sweeps": ledger.get_sweeps_by_address, ### /address/
/compose/ ### "/address/
/compose/bet": transaction.compose_bet, - # "/address/
/compose/broadcast": transaction.compose_broadcast, - # "/address/
/compose/btcpay": transaction.compose_btcpay, - # "/address/
/compose/burn": transaction.compose_burn, - # "/address/
/compose/cancel": transaction.compose_cancel, + "/address/
/compose/broadcast": transaction.compose_broadcast, + "/address/
/compose/btcpay": transaction.compose_btcpay, + "/address/
/compose/burn": transaction.compose_burn, + "/address/
/compose/cancel": transaction.compose_cancel, # "/address/
/compose/destroy": transaction.compose_destroy, # "/address/
/compose/dispenser": transaction.compose_dispenser, # "/address/
/compose/dividend": transaction.compose_dividend, diff --git a/counterparty-core/counterpartycore/lib/messages/broadcast.py b/counterparty-core/counterpartycore/lib/messages/broadcast.py index 482cc0a6c2..0b55e0eee1 100644 --- a/counterparty-core/counterpartycore/lib/messages/broadcast.py +++ b/counterparty-core/counterpartycore/lib/messages/broadcast.py @@ -135,7 +135,7 @@ def validate(db, source, timestamp, value, fee_fraction_int, text, block_index): return problems -def compose(db, source: str, timestamp: int, value: int, fee_fraction: float, text: str): +def compose(db, source: str, timestamp: int, value: float, fee_fraction: float, text: str): # Store the fee fraction as an integer. fee_fraction_int = int(fee_fraction * 1e8) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index c9c52463eb..6b245d9029 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1235,6 +1235,74 @@ def compose_bet( ) +def compose_broadcast( + db, address: str, timestamp: int, value: float, fee_fraction: float, text: str, **construct_args +): + """ + Composes a transaction to broadcast textual and numerical information to the network. + :param address: The address that will be sending (must have the necessary quantity of the specified asset) + :param timestamp: The timestamp of the broadcast, in Unix time + :param value: Numerical value of the broadcast + :param fee_fraction: How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) + :param text: The textual part of the broadcast + """ + return compose_transaction( + db, + name="broadcast", + params={ + "source": address, + "timestamp": timestamp, + "value": value, + "fee_fraction": fee_fraction, + "text": text, + }, + **construct_args, + ) + + +def compose_btcpay(db, address: str, order_match_id: str, **construct_args): + """ + Composes a transaction to pay for a BTC order match. + :param address: The address that will be sending the payment + :param order_match_id: The ID of the order match to pay for + """ + return compose_transaction( + db, + name="btcpay", + params={"source": address, "order_match_id": order_match_id}, + **construct_args, + ) + + +def compose_burn(db, address: str, quantity: int, overburn: bool = False, **construct_args): + """ + Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). + :param address: The address with the BTC to burn + :param quantity: The quantities of BTC to burn (1 BTC maximum burn per address) + :param overburn: Whether to allow the burn to exceed 1 BTC for the address + """ + return compose_transaction( + db, + name="burn", + params={"source": address, "quantity": quantity, "overburn": overburn}, + **construct_args, + ) + + +def compose_cancel(db, address: str, offer_hash: str, **construct_args): + """ + Composes a transaction to cancel an open order or bet. + :param address: The address that placed the order/bet to be cancelled + :param offer_hash: The hash of the order/bet to be cancelled + """ + return compose_transaction( + db, + name="cancel", + params={"source": address, "offer_hash": offer_hash}, + **construct_args, + ) + + def info(db, rawtransaction: str, block_index: int = None): """ Returns Counterparty information from a raw transaction in hex format. From d989d7fff162c315cf391d88f9ea5d5a334fe88b Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 17:34:43 +0200 Subject: [PATCH 060/128] finish compose functions and routes --- .../counterpartycore/lib/api/routes.py | 17 +- .../counterpartycore/lib/messages/issuance.py | 10 +- .../counterpartycore/lib/messages/send.py | 4 +- .../counterpartycore/lib/transaction.py | 264 +++++++++++++++++- 4 files changed, 277 insertions(+), 18 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 8c02b2384b..8b32a86a88 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -49,15 +49,14 @@ "/address/
/compose/btcpay": transaction.compose_btcpay, "/address/
/compose/burn": transaction.compose_burn, "/address/
/compose/cancel": transaction.compose_cancel, - # "/address/
/compose/destroy": transaction.compose_destroy, - # "/address/
/compose/dispenser": transaction.compose_dispenser, - # "/address/
/compose/dividend": transaction.compose_dividend, - # "/address/
/compose/issuance": transaction.compose_issuance, - # "/address/
/compose/order": transaction.compose_order, - # "/address/
/compose/send": transaction.compose_send, - # "/address/
/compose/rps": transaction.compose_rps, - # "/address/
/compose/rpsresolve": transaction.compose_rpsresolve, - # "/address/
/compose/sweep": transaction.compose_sweep, + "/address/
/compose/destroy": transaction.compose_destroy, + "/address/
/compose/dispenser": transaction.compose_dispenser, + "/address/
/compose/dividend": transaction.compose_dividend, + "/address/
/compose/issuance": transaction.compose_issuance, + "/address/
/compose/mpma": transaction.compose_mpma, + "/address/
/compose/order": transaction.compose_order, + "/address/
/compose/send": transaction.compose_send, + "/address/
/compose/sweep": transaction.compose_sweep, ### /assets ### "/assets": ledger.get_valid_assets, "/assets/": ledger.get_asset_info, diff --git a/counterparty-core/counterpartycore/lib/messages/issuance.py b/counterparty-core/counterpartycore/lib/messages/issuance.py index 0f07948950..81d1a6ff81 100644 --- a/counterparty-core/counterpartycore/lib/messages/issuance.py +++ b/counterparty-core/counterpartycore/lib/messages/issuance.py @@ -350,11 +350,11 @@ def compose( source: str, asset: str, quantity: int, - transfer_destination: str | None = None, - divisible: bool | None = None, - lock: bool | None = None, - reset: bool | None = None, - description: str | None = None, + transfer_destination: str = None, + divisible: bool = None, + lock: bool = None, + reset: bool = None, + description: str = None, ): # Callability is deprecated, so for re‐issuances set relevant parameters # to old values; for first issuances, make uncallable. diff --git a/counterparty-core/counterpartycore/lib/messages/send.py b/counterparty-core/counterpartycore/lib/messages/send.py index 69aacd2883..decce16caa 100644 --- a/counterparty-core/counterpartycore/lib/messages/send.py +++ b/counterparty-core/counterpartycore/lib/messages/send.py @@ -117,9 +117,9 @@ def compose( destination: str, asset: str, quantity: int, - memo: str | None = None, + memo: str = None, memo_is_hex: bool = False, - use_enhanced_send: bool | None = None, + use_enhanced_send: bool = None, ): # special case - enhanced_send replaces send by default when it is enabled # but it can be explicitly disabled with an API parameter diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 6b245d9029..0192826379 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1176,10 +1176,9 @@ def compose_transaction( "dispenser", "dividend", "issuance", + "mpma", "order", "send", - "rps", - "rpsresolve", "sweep", ] @@ -1303,6 +1302,267 @@ def compose_cancel(db, address: str, offer_hash: str, **construct_args): ) +def compose_destroy(db, address: str, asset: str, quantity: int, tag: str, **construct_args): + """ + Composes a transaction to destroy a quantity of an asset. + :param address: The address that will be sending the asset to be destroyed + :param asset: The asset to be destroyed + :param quantity: The quantity of the asset to be destroyed + :param tag: A tag for the destruction + """ + return compose_transaction( + db, + name="destroy", + params={"source": address, "asset": asset, "quantity": quantity, "tag": tag}, + **construct_args, + ) + + +def compose_dispenser( + db, + address: str, + asset: str, + give_quantity: int, + escrow_quantity: int, + mainchainrate: int, + status: int, + open_address: str = None, + oracle_address: str = None, + **construct_args, +): + """ + Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. + :param address: The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) + :param asset: The asset or subasset to dispense + :param give_quantity: The quantity of the asset to dispense + :param escrow_quantity: The quantity of the asset to reserve for this dispenser + :param mainchainrate: The quantity of the main chain asset (BTC) per dispensed portion + :param status: The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed + :param open_address: The address that you would like to open the dispenser on + :param oracle_address: The address that you would like to use as a price oracle for this dispenser + """ + return compose_transaction( + db, + name="dispenser", + params={ + "source": address, + "asset": asset, + "give_quantity": give_quantity, + "escrow_quantity": escrow_quantity, + "mainchainrate": mainchainrate, + "status": status, + "open_address": open_address, + "oracle_address": oracle_address, + }, + **construct_args, + ) + + +def compose_dividend( + db, address: str, quantity_per_unit: int, asset: str, dividend_asset: str, **construct_args +): + """ + Composes a transaction to issue a dividend to holders of a given asset. + :param address: The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) + :param quantity_per_unit: The amount of dividend_asset rewarded + :param asset: The asset or subasset that the dividends are being rewarded on + :param dividend_asset: The asset or subasset that the dividends are paid in + """ + return compose_transaction( + db, + name="dividend", + params={ + "source": address, + "quantity_per_unit": quantity_per_unit, + "asset": asset, + "dividend_asset": dividend_asset, + }, + **construct_args, + ) + + +def compose_issuance( + db, + address: str, + asset: str, + quantity: int, + transfer_destination: str = None, + divisible: bool = True, + lock: bool = False, + reset: bool = False, + description: str = None, + **construct_args, +): + """ + Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. + :param address: The address that will be issuing or transfering the asset + :param asset: The assets to issue or transfer. This can also be a subasset longname for new subasset issuances + :param quantity: The quantity of the asset to issue (set to 0 if transferring an asset) + :param transfer_destination: The address to receive the asset + :param divisible: Whether this asset is divisible or not (if a transfer, this value must match the value specified when the asset was originally issued) + :param lock: Whether this issuance should lock supply of this asset forever + :param reset: Wether this issuance should reset any existing supply + :param description: A textual description for the asset + """ + return compose_transaction( + db, + name="issuance", + params={ + "source": address, + "asset": asset, + "quantity": quantity, + "transfer_destination": transfer_destination, + "divisible": divisible, + "lock": lock, + "reset": reset, + "description": description, + }, + **construct_args, + ) + + +def compose_mpma( + db, + source: str, + assets: str, + destinations: str, + quantities: str, + memo: str, + memo_is_hex: bool, + **construct_args, +): + """ + Composes a transaction to send multiple payments to multiple addresses. + :param source: The address that will be sending (must have the necessary quantity of the specified asset) + :param assets: comma-separated list of assets to send + :param destinations: comma-separated list of addresses to send to + :param quantities: comma-separated list of quantities to send + :param memo: The Memo associated with this transaction + :param memo_is_hex: Whether the memo field is a hexadecimal string + """ + asset_list = assets.split(",") + destination_list = destinations.split(",") + quantity_list = quantities.split(",") + if len(asset_list) != len(destination_list) or len(asset_list) != len(quantity_list): + raise exceptions.ComposeError( + "The number of assets, destinations, and quantities must be equal" + ) + for quantity in quantity_list: + if not quantity.isdigit(): + raise exceptions.ComposeError("Quantity must be an integer") + asset_dest_quant_list = list(zip(asset_list, destination_list, quantity_list)) + + return compose_transaction( + db, + name="version.mpma", + params={ + "source": source, + "asset_dest_quant_list": asset_dest_quant_list, + "memo": memo, + "memo_is_hex": memo_is_hex, + }, + **construct_args, + ) + + +def compose_order( + db, + address: str, + give_asset: str, + give_quantity: int, + get_asset: str, + get_quantity: int, + expiration: int, + fee_required: int, + **construct_args, +): + """ + Composes a transaction to place an order on the distributed exchange. + :param address: The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) + :param give_asset: The asset that will be given in the trade + :param give_quantity: The quantity of the asset that will be given + :param get_asset: The asset that will be received in the trade + :param get_quantity: The quantity of the asset that will be received + :param expiration: The number of blocks for which the order should be valid + :param fee_required: The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + """ + return compose_transaction( + db, + name="order", + params={ + "source": address, + "give_asset": give_asset, + "give_quantity": give_quantity, + "get_asset": get_asset, + "get_quantity": get_quantity, + "expiration": expiration, + "fee_required": fee_required, + }, + **construct_args, + ) + + +def compose_send( + db, + address: str, + destination: str, + asset: str, + quantity: int, + memo: str = None, + memo_is_hex: bool = False, + use_enhanced_send: bool = True, + **construct_args, +): + """ + Composes a transaction to send a quantity of an asset to another address. + :param address: The address that will be sending (must have the necessary quantity of the specified asset) + :param destination: The address that will be receiving the asset + :param asset: The asset or subasset to send + :param quantity: The quantity of the asset to send + :param memo: The Memo associated with this transaction + :param memo_is_hex: Whether the memo field is a hexadecimal string + :param use_enhanced_send: If this is false, the construct a legacy transaction sending bitcoin dust + """ + return compose_transaction( + db, + name="send", + params={ + "source": address, + "destination": destination, + "asset": asset, + "quantity": quantity, + "memo": memo, + "memo_is_hex": memo_is_hex, + "use_enhanced_send": use_enhanced_send, + }, + **construct_args, + ) + + +def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **construct_args): + """ + Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. + :param address: The address that will be sending + :param destination: The address to receive the assets and/or ownerships + :param flags: An OR mask of flags indicating how the sweep should be processed. Possible flags are: + - FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. + - FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. + - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. + :param memo: The Memo associated with this transaction + """ + return compose_transaction( + db, + name="sweep", + params={ + "source": address, + "destination": destination, + "flags": flags, + "memo": memo, + }, + **construct_args, + ) + + def info(db, rawtransaction: str, block_index: int = None): """ Returns Counterparty information from a raw transaction in hex format. From d5f6d437e84cd7b3f6a17430a8261889c7c8c610 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 19:06:11 +0200 Subject: [PATCH 061/128] fix merge --- counterparty-core/counterpartycore/server.py | 1 - 1 file changed, 1 deletion(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index c1f837913f..eb34ab5e84 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -614,7 +614,6 @@ def initialise_log_and_config(args): "utxo_locks_max_addresses": args.utxo_locks_max_addresses, "utxo_locks_max_age": args.utxo_locks_max_age, "no_mempool": args.no_mempool, - "skip_db_check": args.skip_db_check, } initialise_log_config( From 881505e8a2aa1b3d97926544b429db18e6f5413e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 19:13:05 +0200 Subject: [PATCH 062/128] disable missing params check for API v1 --- .../counterpartycore/lib/api/api_v1.py | 2 +- .../counterpartycore/lib/transaction.py | 25 +++++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_v1.py b/counterparty-core/counterpartycore/lib/api/api_v1.py index 2f26926ed6..5c6a105741 100644 --- a/counterparty-core/counterpartycore/lib/api/api_v1.py +++ b/counterparty-core/counterpartycore/lib/api/api_v1.py @@ -599,7 +599,7 @@ def create_method(**kwargs): transaction.split_compose_arams(**kwargs) ) return transaction.compose_transaction( - self.db, name=tx, params=transaction_args, **common_args + self.db, name=tx, params=transaction_args, api_v1=True, **common_args ) except ( TypeError, diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 0192826379..7828404c9f 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1075,6 +1075,7 @@ def compose_transaction( p2sh_pretx_txid=None, old_style_api=True, segwit=False, + api_v1=False, ): """Create and return a transaction.""" @@ -1112,13 +1113,19 @@ def compose_transaction( compose_method = sys.modules[f"counterpartycore.lib.messages.{name}"].compose compose_params = inspect.getfullargspec(compose_method)[0] missing_params = [p for p in compose_params if p not in params and p != "db"] - if len(missing_params) > 0: - default_values = get_default_args(compose_method) + if api_v1: for param in missing_params: - if param in default_values: - params[param] = default_values[param] - else: - raise exceptions.ComposeError(f"missing parameters: {', '.join(missing_params)}") + params[param] = None + else: + if len(missing_params) > 0: + default_values = get_default_args(compose_method) + for param in missing_params: + if param in default_values: + params[param] = default_values[param] + else: + raise exceptions.ComposeError( + f"missing parameters: {', '.join(missing_params)}" + ) # dont override fee_per_kb if specified if fee_per_kb is not None: @@ -1183,12 +1190,14 @@ def compose_transaction( ] -def compose(db, source, transaction_name, **kwargs): +def compose(db, source, transaction_name, api_v1=False, **kwargs): if transaction_name not in COMPOSABLE_TRANSACTIONS: raise exceptions.TransactionError("Transaction type not composable.") transaction_args, common_args, _ = split_compose_arams(**kwargs) transaction_args["source"] = source - return compose_transaction(db, name=transaction_name, params=transaction_args, **common_args) + return compose_transaction( + db, name=transaction_name, params=transaction_args, api_v1=api_v1, **common_args + ) def compose_bet( From e91815c4624b8d0e1bbc7edcfcbc9738538dd313 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 19 Apr 2024 20:10:29 +0200 Subject: [PATCH 063/128] fixes --- .../counterpartycore/lib/api/api_server.py | 9 ++++-- .../counterpartycore/lib/api/routes.py | 28 +++++++++---------- .../counterpartycore/lib/api/util.py | 15 ++++++++++ .../counterpartycore/lib/kickstart/utils.py | 9 ------ .../counterpartycore/lib/ledger.py | 15 +++++----- .../counterpartycore/test/api_v2_test.py | 13 +++++++++ 6 files changed, 57 insertions(+), 32 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 9a258fd665..5e9e496c35 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -14,7 +14,12 @@ ledger, ) from counterpartycore.lib.api.routes import ROUTES -from counterpartycore.lib.api.util import get_backend_height, init_api_access_log, remove_rowids +from counterpartycore.lib.api.util import ( + get_backend_height, + init_api_access_log, + remove_rowids, + to_json, +) from flask import Flask, request from flask import g as flask_globals from flask_cors import CORS @@ -81,7 +86,7 @@ def inject_headers(result, return_code=None): if isinstance(result, flask.Response): response = result else: - response = flask.make_response(flask.jsonify(result), http_code) + response = flask.make_response(to_json(result), http_code) response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 8b32a86a88..74c53a7584 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -43,20 +43,20 @@ "/addresses/
/dispensers": ledger.get_dispensers_by_address, "/addresses/
/dispensers/": ledger.get_dispensers_by_address_and_asset, "/addresses/
/sweeps": ledger.get_sweeps_by_address, - ### /address/
/compose/ ### - "/address/
/compose/bet": transaction.compose_bet, - "/address/
/compose/broadcast": transaction.compose_broadcast, - "/address/
/compose/btcpay": transaction.compose_btcpay, - "/address/
/compose/burn": transaction.compose_burn, - "/address/
/compose/cancel": transaction.compose_cancel, - "/address/
/compose/destroy": transaction.compose_destroy, - "/address/
/compose/dispenser": transaction.compose_dispenser, - "/address/
/compose/dividend": transaction.compose_dividend, - "/address/
/compose/issuance": transaction.compose_issuance, - "/address/
/compose/mpma": transaction.compose_mpma, - "/address/
/compose/order": transaction.compose_order, - "/address/
/compose/send": transaction.compose_send, - "/address/
/compose/sweep": transaction.compose_sweep, + ### /addresses/
/compose/ ### + "/addresses/
/compose/bet": transaction.compose_bet, + "/addresses/
/compose/broadcast": transaction.compose_broadcast, + "/addresses/
/compose/btcpay": transaction.compose_btcpay, + "/addresses/
/compose/burn": transaction.compose_burn, + "/addresses/
/compose/cancel": transaction.compose_cancel, + "/addresses/
/compose/destroy": transaction.compose_destroy, + "/addresses/
/compose/dispenser": transaction.compose_dispenser, + "/addresses/
/compose/dividend": transaction.compose_dividend, + "/addresses/
/compose/issuance": transaction.compose_issuance, + "/addresses/
/compose/mpma": transaction.compose_mpma, + "/addresses/
/compose/order": transaction.compose_order, + "/addresses/
/compose/send": transaction.compose_send, + "/addresses/
/compose/sweep": transaction.compose_sweep, ### /assets ### "/assets": ledger.get_valid_assets, "/assets/": ledger.get_asset_info, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 06d0fa0713..c74db6c13a 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -1,4 +1,6 @@ +import decimal import inspect +import json import logging from logging import handlers as logging_handlers @@ -193,3 +195,16 @@ def prepare_routes(routes): "args": prepare_route_args(function), } return prepared_routes + + +class ApiJsonEncoder(json.JSONEncoder): + def default(self, o): + if isinstance(o, decimal.Decimal): + return str(o) + if isinstance(o, bytes): + return o.hex() + return super().default(o) + + +def to_json(obj): + return json.dumps(obj, cls=ApiJsonEncoder, indent=4) diff --git a/counterparty-core/counterpartycore/lib/kickstart/utils.py b/counterparty-core/counterpartycore/lib/kickstart/utils.py index 71dc0da42a..b5f8d05224 100644 --- a/counterparty-core/counterpartycore/lib/kickstart/utils.py +++ b/counterparty-core/counterpartycore/lib/kickstart/utils.py @@ -1,7 +1,5 @@ import binascii -import decimal import hashlib -import json import math import os from multiprocessing import resource_tracker @@ -31,13 +29,6 @@ def ib2h(b): return inverse_hash(b2h(b)) -class JsonDecimalEncoder(json.JSONEncoder): - def default(self, o): - if isinstance(o, decimal.Decimal): - return str(o) - return super(DecimalEncoder, self).default(o) # noqa: F821 - - def decode_value(key, value): # Repeated key to make both same length adjusted_key = key * int(math.ceil(float(len(value)) / len(key))) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 6700c1a560..42bc31e6e2 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -123,6 +123,8 @@ def get_events_by_block_and_event(db, block_index: int, event: str): :param int block_index: The index of the block to return :param str event: The event to filter by """ + if event == "count": + return get_events_counts_by_block(db, block_index=block_index) return get_events(db, block_index=block_index, event=event) @@ -191,8 +193,8 @@ def get_events_counts(db, block_index=None): if block_index is not None: query += "WHERE block_index = ?" bindings.append(block_index) - query += "GROUP BY event" - cursor.execute(query) + query += " GROUP BY event" + cursor.execute(query, bindings) return cursor.fetchall() @@ -1254,13 +1256,12 @@ def get_blocks(db, last: int = None, limit: int = 10): cursor = db.cursor() bindings = [] query = """ - SELECT * FROM blocks WHERE - ORDER BY block_index DESC + SELECT * FROM blocks """ if last is not None: - query += "WHERE BLOCK_INDEX <= ?" + query += "WHERE block_index <= ?" bindings.append(last) - query += "LIMIT ?" + query += " ORDER BY block_index DESC LIMIT ?" bindings.append(limit) cursor.execute(query, tuple(bindings)) return cursor.fetchall() @@ -1385,7 +1386,7 @@ def get_expirations(db, block_index: int): """, ] query = " UNION ALL ".join(queries) - bindings = (block_index,) + bindings = (block_index,) * 6 cursor.execute(query, bindings) return cursor.fetchall() diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 9e53b6a4b4..4bde22dcea 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -4,6 +4,7 @@ import requests from counterpartycore.lib import util +from counterpartycore.lib.api import routes # this is require near the top to do setup of the test suite from counterpartycore.test import ( @@ -17,6 +18,18 @@ API_ROOT = "http://api:api@localhost:10009" +@pytest.mark.usefixtures("api_server_v2") +def test_api_v2(): + block_index = 310491 + for route in routes.ROUTES: + url = f"{API_ROOT}{route}" + if route.startswith("/blocks"): + url = url.replace("", str(block_index)) + # print(url) + result = requests.get(url) # noqa: S113 + assert result.status_code == 200 + + @pytest.mark.usefixtures("api_server_v2") def test_new_get_balances_by_address(): alice = ADDR[0] From 234f9898763f55d90f19b3deb1cbe1d04820cd62 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 11:30:24 +0200 Subject: [PATCH 064/128] more tests; fixes --- .../counterpartycore/lib/ledger.py | 21 +++++++----- .../counterpartycore/test/api_v2_test.py | 34 ++++++++++++++++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 42bc31e6e2..7ab0ba2de2 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -87,12 +87,17 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li else: limit = "" # no sql injection here - query = f""" + query = """ SELECT message_index AS event_index, event, bindings, block_index, timestamp FROM messages - WHERE ({" AND ".join(where)}) + """ + if len(where) > 0: + query += f""" + WHERE ({" AND ".join(where)}) + """ # nosec B608 # noqa: S608 + query += f""" ORDER BY message_index DESC {limit} - """ # nosec B608 # noqa: S608 + """ cursor.execute(query, tuple(bindings)) events = cursor.fetchall() for i, _ in enumerate(events): @@ -1978,9 +1983,9 @@ def get_resolutions_by_bet(db, tx_hash: str): query = """ SELECT * FROM bet_match_resolutions - WHERE bet_match_id LIKE '%?%' + WHERE bet_match_id LIKE '%:tx_hash%' """ - bindings = (tx_hash,) + bindings = {"tx_hash": tx_hash} cursor.execute(query, bindings) return cursor.fetchall() @@ -2187,10 +2192,10 @@ def get_btcpays_by_order(db, tx_hash: str): cursor = db.cursor() query = """ SELECT * - FROM btc_pays - WHERE order_match_id LIKE '%?%' + FROM btcpays + WHERE order_match_id LIKE '%:tx_hash%' """ - bindings = (tx_hash,) + bindings = {"tx_hash": tx_hash} cursor.execute(query, bindings) return cursor.fetchall() diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 4bde22dcea..8ae1de76cc 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -21,13 +21,37 @@ @pytest.mark.usefixtures("api_server_v2") def test_api_v2(): block_index = 310491 + address = ADDR[0] + asset = "NODIVISIBLE" + tx_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" + order_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" + bet_hash = "e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42" + dispsenser_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" + event = "CREDIT" + event_index = 10 + exclude_routes = ["compose", "unpack", "info", "mempool", "healthz", "backend"] + for route in routes.ROUTES: + if any([exclude in route for exclude in exclude_routes]): + continue + url = f"{API_ROOT}{route}" - if route.startswith("/blocks"): - url = url.replace("", str(block_index)) - # print(url) - result = requests.get(url) # noqa: S113 - assert result.status_code == 200 + url = url.replace("", str(block_index)) + url = url.replace("
", address) + url = url.replace("", asset) + url = url.replace("", event) + url = url.replace("", str(event_index)) + if route.startswith("/orders"): + url = url.replace("", order_hash) + elif route.startswith("/bets"): + url = url.replace("", bet_hash) + elif route.startswith("/dispensers"): + url = url.replace("", dispsenser_hash) + else: + url = url.replace("", tx_hash) + # print(url) + result = requests.get(url) # noqa: S113 + assert result.status_code == 200 @pytest.mark.usefixtures("api_server_v2") From 1ec5960252a13b23815a5be671fe75cc2a4b1da7 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 11:55:56 +0200 Subject: [PATCH 065/128] compare result with fixtures --- .../counterpartycore/test/api_v2_test.py | 16 +- .../counterpartycore/test/conftest.py | 6 + .../test/fixtures/api_v2_fixtures.json | 1398 +++++++++++++++++ 3 files changed, 1419 insertions(+), 1 deletion(-) create mode 100644 counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 8ae1de76cc..ccb3ad3d8f 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -1,3 +1,4 @@ +import json import tempfile import pytest @@ -15,11 +16,12 @@ FIXTURE_SQL_FILE = CURR_DIR + "/fixtures/scenarios/unittest_fixture.sql" FIXTURE_DB = tempfile.gettempdir() + "/fixtures.unittest_fixture.db" +API_V2_FIXTURES = CURR_DIR + "/fixtures/api_v2_fixtures.json" API_ROOT = "http://api:api@localhost:10009" @pytest.mark.usefixtures("api_server_v2") -def test_api_v2(): +def test_api_v2(request): block_index = 310491 address = ADDR[0] asset = "NODIVISIBLE" @@ -30,6 +32,10 @@ def test_api_v2(): event = "CREDIT" event_index = 10 exclude_routes = ["compose", "unpack", "info", "mempool", "healthz", "backend"] + results = {} + fixtures = {} + with open(API_V2_FIXTURES, "r") as f: + fixtures = json.load(f) for route in routes.ROUTES: if any([exclude in route for exclude in exclude_routes]): @@ -49,9 +55,17 @@ def test_api_v2(): url = url.replace("", dispsenser_hash) else: url = url.replace("", tx_hash) + if route.startswith("/events"): + url += "?limit=5" # print(url) result = requests.get(url) # noqa: S113 + results[url] = result.json() assert result.status_code == 200 + assert results[url] == fixtures[url] + + if request.config.getoption("saveapifixtures"): + with open(API_V2_FIXTURES, "w") as f: + f.write(json.dumps(results, indent=4)) @pytest.mark.usefixtures("api_server_v2") diff --git a/counterparty-core/counterpartycore/test/conftest.py b/counterparty-core/counterpartycore/test/conftest.py index 613db5ca3f..f196be1d1a 100644 --- a/counterparty-core/counterpartycore/test/conftest.py +++ b/counterparty-core/counterpartycore/test/conftest.py @@ -179,6 +179,12 @@ def pytest_addoption(parser): parser.addoption( "--testbook", action="store_true", default=False, help="Include testnet test book" ) + parser.addoption( + "--saveapifixtures", + action="store_true", + default=False, + help="Generate api v2 fixtures for tests", + ) @pytest.fixture(scope="function") diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json new file mode 100644 index 0000000000..463f90c33d --- /dev/null +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -0,0 +1,1398 @@ +{ + "http://api:api@localhost:10009/blocks": [ + { + "block_index": 310500, + "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", + "block_time": 310500000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", + "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1", + "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f" + }, + { + "block_index": 310499, + "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", + "block_time": 310499000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", + "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5", + "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c" + }, + { + "block_index": 310498, + "block_hash": "b7058b6d1ddc325a10bf33144937e06ce6025215b416518ae120da9440ae279e", + "block_time": 310498000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", + "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7", + "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98" + }, + { + "block_index": 310497, + "block_hash": "f1118591fe79b8bf52ccf0c5de9826bfd266b1fdc24b44676cf22bbcc76d464e", + "block_time": 310497000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "28c6e92b2299b9cbbb5953f8b7ff3de0fe962d15642ba27e43faa64e1935e819", + "txlist_hash": "ff8136601b9e0138a999d1f0467af6e8535a2bcdd2b622af7be0178a083b9519", + "messages_hash": "ac8d8759fbddc8f92ad8b5d8b4637a63b8d21a04ba1a4126baf2b42a87edfce3" + }, + { + "block_index": 310496, + "block_hash": "65884816927e8c566655e85c07bc2bc2c7ee26e625742f219939d43238fb31f8", + "block_time": 310496000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "7ac6121c624b634f44695172761830926afe76bb18c4cc9195773f3a26966941", + "txlist_hash": "9eda85cce745579122ba9c6e24b63cd83f2e5161031a34e6ee9bf08b80823cb4", + "messages_hash": "1b1ed76f99d39b36f4d0737299ce15b21fed9e077d0476658c023b09819853a7" + }, + { + "block_index": 310495, + "block_hash": "4769aa7030f28a05a137a85ef4ee0c1765c37013773212b93ec90f1227168b67", + "block_time": 310495000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5a7e5a36882466373d576bb5f4ccd1bc72ecaf548b9589baa803a7275a7a24cd", + "txlist_hash": "09e9db121649cacd979fd18bbaa35e519361e727e7e072e2f2f86291160cdb29", + "messages_hash": "56518bc9abc11bf108188834058bb9c51c9b8dc3b6276116c5b786b07c797fab" + }, + { + "block_index": 310494, + "block_hash": "7dda1d3e12785313d5651ee5314d0aecf17588196f9150b10c55695dbaebee5d", + "block_time": 310494000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "72d71bd72263699ea9f2b097ad141be5bc394f49d8b0b0a6b2ff6a87b0ee3919", + "txlist_hash": "9350c3ba33d0546d1194c5fa767ced28834b26246aedc56d89b1d48ec4f26014", + "messages_hash": "0d3c87692d49dc033eed975eb8b8ee17ff2f0f877c8ed5a5866f73ce63bf8715" + }, + { + "block_index": 310493, + "block_hash": "c19e2915b750279b2be4b52e57e5ce29f63dffb4e14d9aad30c9e820affc0cbf", + "block_time": 310493000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "29119cd30a4733916fbfd0551506eaa16f7bb1bdfbdf8d17ac4e5bb20d1cb09c", + "txlist_hash": "7ec4cfa94544900c8e8732ad51be7cee6452aa1884ea940cd5c98862fb4aaba6", + "messages_hash": "53a339feb73df3a98bc4acc84af77e111d4780533c8f5a26cf250594d9613cf2" + }, + { + "block_index": 310492, + "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", + "block_time": 310492000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "98af18583618fdeed545347c013763d068e8294405d265911cc5e1bc420bc740", + "txlist_hash": "daf4d2c1a1ad5206abcf7744bdd06fae99c442fb2607a843dcabb5727d02916e", + "messages_hash": "0d34fbc26126add5a57b7e8f6f71bad150d7232abf046f3fa9b1fc72b10d61b2" + }, + { + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", + "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8", + "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994" + } + ], + "http://api:api@localhost:10009/blocks/310491": { + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", + "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8", + "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994" + }, + "http://api:api@localhost:10009/blocks/310491/transactions": [ + { + "tx_index": 492, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "", + "btc_amount": 0, + "fee": 6800, + "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "supported": 1 + } + ], + "http://api:api@localhost:10009/blocks/310491/events": [ + { + "event_index": 1182, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310491, + "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", + "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994", + "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8" + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1181, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1180, + "event": "OPEN_ORDER", + "bindings": { + "block_index": 310491, + "expiration": 2000, + "expire_index": 312491, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "fee_required": 900000, + "fee_required_remaining": 900000, + "get_asset": "BTC", + "get_quantity": 800000, + "get_remaining": 800000, + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 100000000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "status": "open", + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1179, + "event": "DEBIT", + "bindings": { + "action": "open order", + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "block_index": 310491, + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "quantity": 100000000, + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1178, + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_index": 310491, + "block_time": 310491000, + "btc_amount": 0, + "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "destination": "", + "fee": 6800, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "supported": true, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1177, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_index": 310491, + "block_time": 310491000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, + "block_index": 310491, + "timestamp": 0 + } + ], + "http://api:api@localhost:10009/blocks/310491/events/counts": [ + { + "event": "BLOCK_PARSED", + "event_count": 1 + }, + { + "event": "DEBIT", + "event_count": 1 + }, + { + "event": "NEW_BLOCK", + "event_count": 1 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 1 + }, + { + "event": "OPEN_ORDER", + "event_count": 1 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 1 + } + ], + "http://api:api@localhost:10009/blocks/310491/events/CREDIT": [], + "http://api:api@localhost:10009/blocks/310491/credits": [], + "http://api:api@localhost:10009/blocks/310491/debits": [ + { + "block_index": 310491, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + } + ], + "http://api:api@localhost:10009/blocks/310491/expirations": [], + "http://api:api@localhost:10009/blocks/310491/cancels": [], + "http://api:api@localhost:10009/blocks/310491/destructions": [], + "http://api:api@localhost:10009/blocks/310491/issuances": [], + "http://api:api@localhost:10009/blocks/310491/sends": [], + "http://api:api@localhost:10009/blocks/310491/dispenses": [], + "http://api:api@localhost:10009/blocks/310491/sweeps": [], + "http://api:api@localhost:10009/transactions/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { + "tx_index": 492, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "", + "btc_amount": 0, + "fee": 6800, + "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "supported": 1 + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances": [ + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "quantity": 100000000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "CALLABLE", + "quantity": 1000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 98800000000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "LOCKED", + "quantity": 1000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "MAXI", + "quantity": 9223372036854775807 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "quantity": 100000000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 91875000000 + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances/NODIVISIBLE": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/credits": [ + { + "block_index": 310000, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 93000000000, + "calling_function": "burn", + "event": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "tx_index": 1 + }, + { + "block_index": 310001, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000000, + "calling_function": "issuance", + "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", + "tx_index": 2 + }, + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310003, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "CALLABLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", + "tx_index": 4 + }, + { + "block_index": 310004, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "LOCKED", + "quantity": 1000, + "calling_function": "issuance", + "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", + "tx_index": 5 + }, + { + "block_index": 310016, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "MAXI", + "quantity": 9223372036854775807, + "calling_function": "issuance", + "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", + "tx_index": 17 + }, + { + "block_index": 310020, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "calling_function": "filled", + "event": "5c6562ddad0bc8a1faaded18813a65522cd273709acd190cf9d3271817eefc93", + "tx_index": 21 + }, + { + "block_index": 310102, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 9, + "calling_function": "bet settled", + "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "tx_index": 103 + }, + { + "block_index": 310102, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "calling_function": "feed fee", + "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "tx_index": 103 + }, + { + "block_index": 310482, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "calling_function": "send", + "event": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", + "tx_index": 483 + }, + { + "block_index": 310497, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "quantity": 100000000, + "calling_function": "issuance", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "tx_index": 498 + }, + { + "block_index": 310498, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "quantity": 100000000, + "calling_function": "issuance", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "tx_index": 499 + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/debits": [ + { + "block_index": 310001, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", + "tx_index": 2 + }, + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310003, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", + "tx_index": 4 + }, + { + "block_index": 310004, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", + "tx_index": 5 + }, + { + "block_index": 310005, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "action": "issuance fee", + "event": "344dcc8909ca3a137630726d0071dfd2df4f7c855bac150c7d3a8367835c90bc", + "tx_index": 6 + }, + { + "block_index": 310006, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "4f0433ba841038e2e16328445930dd7bca35309b14b0da4451c8f94c631368b8", + "tx_index": 7 + }, + { + "block_index": 310007, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000, + "action": "send", + "event": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", + "tx_index": 8 + }, + { + "block_index": 310008, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", + "tx_index": 9 + }, + { + "block_index": 310009, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "21460d5c07284f9be9baf824927d0d4e4eb790e297f3162305841607b672349b", + "tx_index": 10 + }, + { + "block_index": 310010, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", + "tx_index": 11 + }, + { + "block_index": 310012, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 300000000, + "action": "send", + "event": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", + "tx_index": 13 + }, + { + "block_index": 310013, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 1000000000, + "action": "send", + "event": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", + "tx_index": 14 + }, + { + "block_index": 310014, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 5, + "action": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 10, + "action": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + }, + { + "block_index": 310016, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", + "tx_index": 17 + }, + { + "block_index": 310019, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 9, + "action": "bet", + "event": "2a2169991597036b6dad687ea1feffd55465a204466f40c35cbba811cb3109b1", + "tx_index": 20 + }, + { + "block_index": 310110, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000, + "action": "send", + "event": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", + "tx_index": 111 + }, + { + "block_index": 310481, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", + "tx_index": 482 + }, + { + "block_index": 310491, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + { + "block_index": 310497, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "tx_index": 498 + }, + { + "block_index": 310498, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 25000000, + "action": "issuance fee", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "tx_index": 499 + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/bets": [ + { + "tx_index": 102, + "tx_hash": "db4ea092bea6036e3d1e5f6ec863db9b900252b4f4d6d9faa6165323f433c51e", + "block_index": 310101, + "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "feed_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "bet_type": 3, + "deadline": 1388000200, + "wager_quantity": 10, + "wager_remaining": 10, + "counterwager_quantity": 10, + "counterwager_remaining": 10, + "target_value": 0.0, + "leverage": 5040, + "expiration": 1000, + "expire_index": 311101, + "fee_fraction_int": 5000000, + "status": "open" + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/broadcasts": [ + { + "tx_index": 103, + "tx_hash": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "block_index": 310102, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "timestamp": 1388000002, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "locked": 0, + "status": "valid" + }, + { + "tx_index": 18, + "tx_hash": "d14388b74b63d93e4477b1fe8426028bb8ab20656703e3ce8deeb23c2fe0b8af", + "block_index": 310017, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "timestamp": 1388000000, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "locked": 0, + "status": "valid" + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/burns": [ + { + "tx_index": 1, + "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "block_index": 310000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "burned": 62000000, + "earned": 93000000000, + "status": "valid" + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends": [ + { + "tx_index": 8, + "tx_hash": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", + "block_index": 310007, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "DIVISIBLE", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 9, + "tx_hash": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", + "block_index": 310008, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 13, + "tx_hash": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", + "block_index": 310012, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "XCP", + "quantity": 300000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 14, + "tx_hash": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", + "block_index": 310013, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "DIVISIBLE", + "quantity": 1000000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 111, + "tx_hash": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", + "block_index": 310110, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", + "asset": "DIVISIBLE", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 482, + "tx_hash": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", + "block_index": 310481, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": "68656c6c6f" + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives": [ + { + "tx_index": 483, + "tx_hash": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", + "block_index": 310482, + "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "destination": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": "fade0001" + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends/NODIVISIBLE": [ + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives/NODIVISIBLE": [], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers": [], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers/NODIVISIBLE": [], + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sweeps": [], + "http://api:api@localhost:10009/assets": [ + { + "asset": "A95428956661682277", + "asset_longname": "PARENT.already.issued" + }, + { + "asset": "CALLABLE", + "asset_longname": null + }, + { + "asset": "DIVIDEND", + "asset_longname": null + }, + { + "asset": "DIVISIBLE", + "asset_longname": null + }, + { + "asset": "LOCKED", + "asset_longname": null + }, + { + "asset": "LOCKEDPREV", + "asset_longname": null + }, + { + "asset": "MAXI", + "asset_longname": null + }, + { + "asset": "NODIVISIBLE", + "asset_longname": null + }, + { + "asset": "PARENT", + "asset_longname": null + }, + { + "asset": "PAYTOSCRIPT", + "asset_longname": null + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE": { + "asset": "NODIVISIBLE", + "asset_longname": null, + "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "divisible": false, + "locked": false, + "supply": 1000, + "description": "No divisible asset", + "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "holder_count": 3 + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/balances": [ + { + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 + }, + { + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5 + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE/balances/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/orders": [], + "http://api:api@localhost:10009/assets/NODIVISIBLE/credits": [ + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310014, + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "calling_function": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "calling_function": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE/debits": [ + { + "block_index": 310014, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 5, + "action": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 10, + "action": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE/dividends": [], + "http://api:api@localhost:10009/assets/NODIVISIBLE/issuances": [ + { + "tx_index": 3, + "tx_hash": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "msg_index": 0, + "block_index": 310002, + "asset": "NODIVISIBLE", + "quantity": 1000, + "divisible": 0, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "No divisible asset", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE/sends": [ + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers": [], + "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": [], + "http://api:api@localhost:10009/assets/NODIVISIBLE/holders": [ + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "address_quantity": 985, + "escrow": null + }, + { + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "address_quantity": 5, + "escrow": null + }, + { + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "address_quantity": 10, + "escrow": null + } + ], + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": [ + { + "tx_index": 492, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "block_index": 310492, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 0, + "get_asset": "BTC", + "get_quantity": 800000, + "get_remaining": 0, + "expiration": 2000, + "expire_index": 312491, + "fee_required": 900000, + "fee_required_remaining": 892800, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "status": "open" + } + ], + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/matches": [ + { + "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx0_index": 492, + "tx0_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx0_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "tx1_index": 493, + "tx1_hash": "1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx1_address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "forward_asset": "XCP", + "forward_quantity": 100000000, + "backward_asset": "BTC", + "backward_quantity": 800000, + "tx0_block_index": 310491, + "tx1_block_index": 310492, + "block_index": 310492, + "tx0_expiration": 2000, + "tx1_expiration": 2000, + "match_expire_index": 310512, + "fee_paid": 7200, + "status": "pending" + } + ], + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/btcpays": [], + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42": [], + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/matches": [], + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/resolutions": [], + "http://api:api@localhost:10009/burns": [ + { + "tx_index": 1, + "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "block_index": 310000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "burned": 62000000, + "earned": 93000000000, + "status": "valid" + }, + { + "tx_index": 104, + "tx_hash": "65d4048700fb8ae03f321be93c6669b8497f506a1f43920f96d994f43358c35b", + "block_index": 310103, + "source": "myAtcJEHAsDLbTkai6ipWDZeeL7VkxXsiM", + "burned": 62000000, + "earned": 92999138821, + "status": "valid" + }, + { + "tx_index": 105, + "tx_hash": "95332a7e3e2b04f2c10e3027327bfc31b686947fb05381e28903e3ff569bd4ff", + "block_index": 310104, + "source": "munimLLHjPhGeSU5rYB2HN79LJa8bRZr5b", + "burned": 62000000, + "earned": 92999130460, + "status": "valid" + }, + { + "tx_index": 106, + "tx_hash": "e062d1ebf4cb71bd22d80c949b956f5286080838a7607ccf87945b2b3abfcafa", + "block_index": 310105, + "source": "mwtPsLQxW9xpm7gdLmwWvJK5ABdPUVJm42", + "burned": 62000000, + "earned": 92999122099, + "status": "valid" + }, + { + "tx_index": 107, + "tx_hash": "bbf0b9f6992755a3e371fb0c0b72f6828831e81c6f7ada6f95ba1104fb901ac3", + "block_index": 310106, + "source": "mrPk7hTeZWjjSCrMTC2ET4SAUThQt7C4uK", + "burned": 10000, + "earned": 14999857, + "status": "valid" + }, + { + "tx_index": 109, + "tx_hash": "93c6d2499a0536c31c77a3db3fc9fc8456fbd0726c45b8f716af16f938727a73", + "block_index": 310108, + "source": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", + "burned": 31000000, + "earned": 46499548508, + "status": "valid" + }, + { + "tx_index": 117, + "tx_hash": "27929c4fcad307a76ea7da34dd2691084f678a22ee43ce7f3842b78730ee08f9", + "block_index": 310116, + "source": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + "burned": 62000000, + "earned": 92999030129, + "status": "valid" + }, + { + "tx_index": 494, + "tx_hash": "c0733e1287afb1bb3d2fdacd1db7c74ea84f14362f3a8d1c038e662e1d0b1b1a", + "block_index": 310493, + "source": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", + "burned": 62000000, + "earned": 92995878046, + "status": "valid" + } + ], + "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": [], + "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/dispenses": [], + "http://api:api@localhost:10009/events?limit=5": [ + { + "event_index": 1237, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310500, + "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", + "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f", + "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1" + }, + "block_index": 310500, + "timestamp": 0 + }, + { + "event_index": 1236, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", + "block_index": 310500, + "block_time": 310500000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, + "block_index": 310500, + "timestamp": 0 + }, + { + "event_index": 1235, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310499, + "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", + "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c", + "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5" + }, + "block_index": 310499, + "timestamp": 0 + }, + { + "event_index": 1234, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", + "block_index": 310499, + "block_time": 310499000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, + "block_index": 310499, + "timestamp": 0 + }, + { + "event_index": 1233, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310498, + "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", + "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98", + "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7" + }, + "block_index": 310498, + "timestamp": 0 + } + ], + "http://api:api@localhost:10009/events/10?limit=5": [ + { + "event_index": 10, + "event": "ASSET_CREATION", + "bindings": { + "asset_id": "697326324582", + "asset_longname": null, + "asset_name": "DIVISIBLE", + "block_index": 310001 + }, + "block_index": 310001, + "timestamp": 0 + } + ], + "http://api:api@localhost:10009/events/counts?limit=5": [ + { + "event": "ASSET_CREATION", + "event_count": 10 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 13 + }, + { + "event": "BET_MATCH", + "event_count": 1 + }, + { + "event": "BET_MATCH_RESOLUTON", + "event_count": 1 + }, + { + "event": "BET_MATCH_UPDATE", + "event_count": 1 + }, + { + "event": "BET_UPDATE", + "event_count": 2 + }, + { + "event": "BLOCK_PARSED", + "event_count": 502 + }, + { + "event": "BROADCAST", + "event_count": 8 + }, + { + "event": "BURN", + "event_count": 8 + }, + { + "event": "CREDIT", + "event_count": 34 + }, + { + "event": "DEBIT", + "event_count": 34 + }, + { + "event": "ENHANCED_SEND", + "event_count": 2 + }, + { + "event": "NEW_BLOCK", + "event_count": 502 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 52 + }, + { + "event": "OPEN_BET", + "event_count": 5 + }, + { + "event": "OPEN_DISPENSER", + "event_count": 1 + }, + { + "event": "OPEN_ORDER", + "event_count": 6 + }, + { + "event": "ORDER_MATCH", + "event_count": 1 + }, + { + "event": "ORDER_UPDATE", + "event_count": 2 + }, + { + "event": "SEND", + "event_count": 9 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 44 + } + ], + "http://api:api@localhost:10009/events/CREDIT?limit=5": [ + { + "event_index": 1231, + "event": "CREDIT", + "bindings": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "block_index": 310498, + "calling_function": "issuance", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "quantity": 100000000, + "tx_index": 499 + }, + "block_index": 310498, + "timestamp": 0 + }, + { + "event_index": 1223, + "event": "CREDIT", + "bindings": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "block_index": 310497, + "calling_function": "issuance", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "quantity": 100000000, + "tx_index": 498 + }, + "block_index": 310497, + "timestamp": 0 + }, + { + "event_index": 1214, + "event": "CREDIT", + "bindings": { + "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", + "asset": "XCP", + "block_index": 310496, + "calling_function": "send", + "event": "a35ab1736565aceddbd1d71f92fc7f39d1361006aa9099f731e54e762964d5ba", + "quantity": 92945878046, + "tx_index": 497 + }, + "block_index": 310496, + "timestamp": 0 + }, + { + "event_index": 1207, + "event": "CREDIT", + "bindings": { + "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", + "asset": "DIVIDEND", + "block_index": 310495, + "calling_function": "send", + "event": "02156b9a1f643fb48330396274a37620c8abbbe5eddb2f8b53dadd135f5d2e2e", + "quantity": 10, + "tx_index": 496 + }, + "block_index": 310495, + "timestamp": 0 + }, + { + "event_index": 1201, + "event": "CREDIT", + "bindings": { + "address": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", + "asset": "DIVIDEND", + "block_index": 310494, + "calling_function": "issuance", + "event": "321bed395482e034f2ce0a4dbf28d1f800592a658e26ea91ae9c5b0928204503", + "quantity": 100, + "tx_index": 495 + }, + "block_index": 310494, + "timestamp": 0 + } + ] +} \ No newline at end of file From 01cf5460be9fc23008c9fe5a973580a991f5fc3e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 13:46:49 +0200 Subject: [PATCH 066/128] Test and fix unpack route --- .../counterpartycore/lib/transaction.py | 128 ++-- .../counterpartycore/test/api_v2_test.py | 12 + .../test/fixtures/api_v2_unpack_fixtures.json | 710 ++++++++++++++++++ 3 files changed, 788 insertions(+), 62 deletions(-) create mode 100644 counterparty-core/counterpartycore/test/fixtures/api_v2_unpack_fixtures.json diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 7828404c9f..60613b9bd4 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1609,68 +1609,72 @@ def unpack(db, datahex: str, block_index: int = None): # Unknown message type message_data = {"error": "Unknown message type"} - # Bet - if message_type_id == messages.bet.ID: - message_type_name = "bet" - message_data = messages.bet.unpack(message, return_dict=True) - # Broadcast - elif message_type_id == messages.broadcast.ID: - message_type_name = "broadcast" - message_data = messages.broadcast.unpack(message, block_index, return_dict=True) - # BTCPay - elif message_type_id == messages.btcpay.ID: - message_type_name = "btcpay" - message_data = messages.btcpay.unpack(message, return_dict=True) - # Cancel - elif message_type_id == messages.cancel.ID: - message_type_name = "cancel" - message_data = messages.cancel.unpack(message, return_dict=True) - # Destroy - elif message_type_id == messages.destroy.ID: - message_type_name = "destroy" - message_data = messages.destroy.unpack(db, message, return_dict=True) - # Dispenser - elif message_type_id == messages.dispenser.ID: - message_type_name = "dispenser" - message_data = messages.dispenser.unpack(message, return_dict=True) - # Dividend - elif message_type_id == messages.dividend.ID: - message_type_name = "dividend" - message_data = messages.dividend.unpack(db, message, block_index, return_dict=True) - # Issuance - elif message_type_id in issuance_ids: - message_type_name = "issuance" - message_data = messages.issuance.unpack( - db, message, message_type_id, block_index, return_dict=True - ) - # Order - elif message_type_id == messages.order.ID: - message_type_name = "order" - message_data = messages.order.unpack(db, message, block_index, return_dict=True) - # Send - elif message_type_id == messages.send.ID: - message_type_name = "send" - message_data = messages.send.unpack(db, message, block_index) - # Enhanced send - elif message_type_id == messages.versions.enhanced_send.ID: - message_type_name = "enhanced_send" - message_data = messages.versions.enhanced_send.unpack(message, block_index) - # MPMA send - elif message_type_id == messages.versions.mpma.ID: - message_type_name = "mpma_send" - message_data = messages.versions.mpma.unpack(message, block_index) - # RPS - elif message_type_id == messages.rps.ID: - message_type_name = "rps" - message_data = messages.rps.unpack(message, return_dict=True) - # RPS Resolve - elif message_type_id == messages.rpsresolve.ID: - message_type_name = "rpsresolve" - message_data = messages.rpsresolve.unpack(message, return_dict=True) - # Sweep - elif message_type_id == messages.sweep.ID: - message_type_name = "sweep" - message_data = messages.sweep.unpack(message) + message_type_name = "unknown" + try: + # Bet + if message_type_id == messages.bet.ID: + message_type_name = "bet" + message_data = messages.bet.unpack(message, return_dict=True) + # Broadcast + elif message_type_id == messages.broadcast.ID: + message_type_name = "broadcast" + message_data = messages.broadcast.unpack(message, block_index, return_dict=True) + # BTCPay + elif message_type_id == messages.btcpay.ID: + message_type_name = "btcpay" + message_data = messages.btcpay.unpack(message, return_dict=True) + # Cancel + elif message_type_id == messages.cancel.ID: + message_type_name = "cancel" + message_data = messages.cancel.unpack(message, return_dict=True) + # Destroy + elif message_type_id == messages.destroy.ID: + message_type_name = "destroy" + message_data = messages.destroy.unpack(db, message, return_dict=True) + # Dispenser + elif message_type_id == messages.dispenser.ID: + message_type_name = "dispenser" + message_data = messages.dispenser.unpack(message, return_dict=True) + # Dividend + elif message_type_id == messages.dividend.ID: + message_type_name = "dividend" + message_data = messages.dividend.unpack(db, message, block_index, return_dict=True) + # Issuance + elif message_type_id in issuance_ids: + message_type_name = "issuance" + message_data = messages.issuance.unpack( + db, message, message_type_id, block_index, return_dict=True + ) + # Order + elif message_type_id == messages.order.ID: + message_type_name = "order" + message_data = messages.order.unpack(db, message, block_index, return_dict=True) + # Send + elif message_type_id == messages.send.ID: + message_type_name = "send" + message_data = messages.send.unpack(db, message, block_index) + # Enhanced send + elif message_type_id == messages.versions.enhanced_send.ID: + message_type_name = "enhanced_send" + message_data = messages.versions.enhanced_send.unpack(message, block_index) + # MPMA send + elif message_type_id == messages.versions.mpma.ID: + message_type_name = "mpma_send" + message_data = messages.versions.mpma.unpack(message, block_index) + # RPS + elif message_type_id == messages.rps.ID: + message_type_name = "rps" + message_data = messages.rps.unpack(message, return_dict=True) + # RPS Resolve + elif message_type_id == messages.rpsresolve.ID: + message_type_name = "rpsresolve" + message_data = messages.rpsresolve.unpack(message, return_dict=True) + # Sweep + elif message_type_id == messages.sweep.ID: + message_type_name = "sweep" + message_data = messages.sweep.unpack(message) + except exceptions.UnpackError as e: + message_data = {"error": str(e)} return { "message_type": message_type_name, diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index ccb3ad3d8f..edb79c6a85 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -68,6 +68,18 @@ def test_api_v2(request): f.write(json.dumps(results, indent=4)) +@pytest.mark.usefixtures("api_server_v2") +def test_api_v2_unpack(request, server_db): + with open(CURR_DIR + "/fixtures/api_v2_unpack_fixtures.json", "r") as f: + datas = json.load(f) + url = f"{API_ROOT}/transactions/unpack" + + for data in datas: + result = requests.get(url, params={"datahex": data["datahex"]}) # noqa: S113 + assert result.status_code == 200 + assert result.json() == data["result"] + + @pytest.mark.usefixtures("api_server_v2") def test_new_get_balances_by_address(): alice = ADDR[0] diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_unpack_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_unpack_fixtures.json new file mode 100644 index 0000000000..dbe15ab232 --- /dev/null +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_unpack_fixtures.json @@ -0,0 +1,710 @@ +[ + { + "datahex": "00000014000000a25be34b66000000174876e800010000000000000000000f446976697369626c65206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 697326324582, + "asset": "DIVISIBLE", + "subasset_longname": null, + "quantity": 100000000000, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Divisible asset", + "status": "valid" + } + } + }, + { + "datahex": "000000140006cad8dc7f0b6600000000000003e800000000000000000000124e6f20646976697369626c65206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 1911882621324134, + "asset": "NODIVISIBLE", + "subasset_longname": null, + "quantity": 1000, + "divisible": false, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "No divisible asset", + "status": "valid" + } + } + }, + { + "datahex": "0000001400000003c58e5c5600000000000003e8010000000000000000000e43616c6c61626c65206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 16199343190, + "asset": "CALLABLE", + "subasset_longname": null, + "quantity": 1000, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Callable asset", + "status": "valid" + } + } + }, + { + "datahex": "0000001400000000082c82e300000000000003e8010000000000000000000c4c6f636b6564206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 137134819, + "asset": "LOCKED", + "subasset_longname": null, + "quantity": 1000, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Locked asset", + "status": "valid" + } + } + }, + { + "datahex": "0000001400000000082c82e3000000000000000001000000000000000000044c4f434b", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 137134819, + "asset": "LOCKED", + "subasset_longname": null, + "quantity": 0, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "LOCK", + "status": "valid" + } + } + }, + { + "datahex": "0000000a00000000000000010000000005f5e100000000a25be34b660000000005f5e10007d00000000000000000", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "XCP", + "give_quantity": 100000000, + "get_asset": "DIVISIBLE", + "get_quantity": 100000000, + "expiration": 2000, + "fee_required": 0, + "status": "open" + } + } + }, + { + "datahex": "00000000000000a25be34b660000000005f5e100", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "DIVISIBLE", + "quantity": 100000000 + } + } + }, + { + "datahex": "0000000000000000000000010000000005f5e100", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "XCP", + "quantity": 100000000 + } + } + }, + { + "datahex": "0000000a00000000000000010000000005f5e100000000a25be34b660000000005f5e10007d00000000000000000", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "XCP", + "give_quantity": 100000000, + "get_asset": "DIVISIBLE", + "get_quantity": 100000000, + "expiration": 2000, + "fee_required": 0, + "status": "open" + } + } + }, + { + "datahex": "0000000a00000000000000010000000005f5e100000000000000000000000000000f424007d000000000000dbba0", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "XCP", + "give_quantity": 100000000, + "get_asset": "BTC", + "get_quantity": 1000000, + "expiration": 2000, + "fee_required": 900000, + "status": "open" + } + } + }, + { + "datahex": "0000000a000000000000000000000000000a2c2b00000000000000010000000005f5e10007d00000000000000000", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "BTC", + "give_quantity": 666667, + "get_asset": "XCP", + "get_quantity": 100000000, + "expiration": 2000, + "fee_required": 0, + "status": "open" + } + } + }, + { + "datahex": "0000000000000000000000010000000011e1a300", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "XCP", + "quantity": 300000000 + } + } + }, + { + "datahex": "00000000000000a25be34b66000000003b9aca00", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "DIVISIBLE", + "quantity": 1000000000 + } + } + }, + { + "datahex": "000000000006cad8dc7f0b660000000000000005", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "NODIVISIBLE", + "quantity": 5 + } + } + }, + { + "datahex": "000000000006cad8dc7f0b66000000000000000a", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "NODIVISIBLE", + "quantity": 10 + } + } + }, + { + "datahex": "000000140000000000033a3e7fffffffffffffff01000000000000000000104d6178696d756d207175616e74697479", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 211518, + "asset": "MAXI", + "subasset_longname": null, + "quantity": 9223372036854775807, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Maximum quantity", + "status": "valid" + } + } + }, + { + "datahex": "0000001e52bb33003ff0000000000000004c4b4009556e69742054657374", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000000, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "status": "valid" + } + } + }, + { + "datahex": "0000001e4cc552003ff000000000000000000000046c6f636b", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1288000000, + "value": 1.0, + "fee_fraction_int": 0, + "text": "lock", + "status": "valid" + } + } + }, + { + "datahex": "00000028000152bb3301000000000000000900000000000000090000000000000000000013b000000064", + "result": { + "message_type": "bet", + "message_type_id": 40, + "message_data": { + "bet_type": 1, + "deadline": 1388000001, + "wager_quantity": 9, + "counterwager_quantity": 9, + "target_value": 0.0, + "leverage": 5040, + "expiration": 100, + "status": "open" + } + } + }, + { + "datahex": "00000028000052bb3301000000000000000900000000000000090000000000000000000013b000000064", + "result": { + "message_type": "bet", + "message_type_id": 40, + "message_data": { + "bet_type": 0, + "deadline": 1388000001, + "wager_quantity": 9, + "counterwager_quantity": 9, + "target_value": 0.0, + "leverage": 5040, + "expiration": 100, + "status": "open" + } + } + }, + { + "datahex": "00000028000352bb33c8000000000000000a000000000000000a0000000000000000000013b0000003e8", + "result": { + "message_type": "bet", + "message_type_id": 40, + "message_data": { + "bet_type": 3, + "deadline": 1388000200, + "wager_quantity": 10, + "counterwager_quantity": 10, + "target_value": 0.0, + "leverage": 5040, + "expiration": 1000, + "status": "open" + } + } + }, + { + "datahex": "0000001e52bb33023ff0000000000000004c4b4009556e69742054657374", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000002, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "status": "valid" + } + } + }, + { + "datahex": "0000000c000000000000000100000000000000640000000000000064000000000000006400", + "result": { + "message_type": "dispenser", + "message_type_id": 12, + "message_data": { + "asset": "XCP", + "give_quantity": 100, + "escrow_quantity": 100, + "mainchainrate": 100, + "dispenser_status": 0, + "action_address": null, + "oracle_address": null, + "status": "valid" + } + } + }, + { + "datahex": "0000001400078a8fe2e5e44100000000000003e8000000000000000000001050534820697373756564206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 2122675428648001, + "asset": "PAYTOSCRIPT", + "subasset_longname": null, + "quantity": 1000, + "divisible": false, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "PSH issued asset", + "status": "valid" + } + } + }, + { + "datahex": "00000000000000a25be34b660000000005f5e100", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "DIVISIBLE", + "quantity": 100000000 + } + } + }, + { + "datahex": "0000001e52bb33023ff0000000000000004c4b4009556e69742054657374", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000002, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "status": "valid" + } + } + }, + { + "datahex": "00000028000352bb33c8000000000000000a000000000000000a0000000000000000000013b0000003e8", + "result": { + "message_type": "bet", + "message_type_id": 40, + "message_data": { + "bet_type": 3, + "deadline": 1388000200, + "wager_quantity": 10, + "counterwager_quantity": 10, + "target_value": 0.0, + "leverage": 5040, + "expiration": 1000, + "status": "open" + } + } + }, + { + "datahex": "00000014000038fedf6d2c6900000000000003e8010000000000000000000c4c6f636b6564206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 62667321322601, + "asset": "LOCKEDPREV", + "subasset_longname": null, + "quantity": 1000, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Locked asset", + "status": "valid" + } + } + }, + { + "datahex": "00000014000038fedf6d2c69000000000000000001000000000000000000044c4f434b", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 62667321322601, + "asset": "LOCKEDPREV", + "subasset_longname": null, + "quantity": 0, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "LOCK", + "status": "valid" + } + } + }, + { + "datahex": "00000014000038fedf6d2c69000000000000000001000000000000000000076368616e676564", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 62667321322601, + "asset": "LOCKEDPREV", + "subasset_longname": null, + "quantity": 0, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "changed", + "status": "valid" + } + } + }, + { + "datahex": "0000000200000000000000010000000005f5e1006f8d6ae8a3b381663118b4e1eff4cfc7d0954dd6ec68656c6c6f", + "result": { + "message_type": "enhanced_send", + "message_type_id": 2, + "message_data": { + "asset": "XCP", + "quantity": 100000000, + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "memo": "68656c6c6f" + } + } + }, + { + "datahex": "0000000200000000000000010000000005f5e1006f4838d8b3588c4c7ba7c1d06f866e9b3739c63037fade0001", + "result": { + "message_type": "enhanced_send", + "message_type_id": 2, + "message_data": { + "asset": "XCP", + "quantity": 100000000, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "memo": "fade0001" + } + } + }, + { + "datahex": "0000001e52bb33003ff0000000000000004c4b4009556e69742054657374", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000000, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "status": "valid" + } + } + }, + { + "datahex": "00000028000152bb3301000000000000000900000000000000090000000000000000000013b000000064", + "result": { + "message_type": "bet", + "message_type_id": 40, + "message_data": { + "bet_type": 1, + "deadline": 1388000001, + "wager_quantity": 9, + "counterwager_quantity": 9, + "target_value": 0.0, + "leverage": 5040, + "expiration": 100, + "status": "open" + } + } + }, + { + "datahex": "0000001e52bb33023ff000000000000000000000096f7074696f6e732030", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000002, + "value": 1.0, + "fee_fraction_int": 0, + "text": "options 0", + "status": "valid" + } + } + }, + { + "datahex": "0000001e52bb33033ff000000000000000000000046c6f636b", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000003, + "value": 1.0, + "fee_fraction_int": 0, + "text": "lock", + "status": "valid" + } + } + }, + { + "datahex": "0000001e52bb33043ff000000000000000000000096f7074696f6e732031", + "result": { + "message_type": "broadcast", + "message_type_id": 30, + "message_data": { + "timestamp": 1388000004, + "value": 1.0, + "fee_fraction_int": 0, + "text": "options 1", + "status": "valid" + } + } + }, + { + "datahex": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "XCP", + "give_quantity": 100000000, + "get_asset": "BTC", + "get_quantity": 800000, + "expiration": 2000, + "fee_required": 900000, + "status": "open" + } + } + }, + { + "datahex": "0000000a000000000000000000000000000c350000000000000000010000000005f5e10007d00000000000000000", + "result": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "BTC", + "give_quantity": 800000, + "get_asset": "XCP", + "get_quantity": 100000000, + "expiration": 2000, + "fee_required": 0, + "status": "open" + } + } + }, + { + "datahex": "00000014000000063e985ffd00000000000000640100000000000000000000", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 26819977213, + "asset": "DIVIDEND", + "subasset_longname": null, + "quantity": 100, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "", + "status": "valid" + } + } + }, + { + "datahex": "00000000000000063e985ffd000000000000000a", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "DIVIDEND", + "quantity": 10 + } + } + }, + { + "datahex": "00000000000000000000000100000015a4018c1e", + "result": { + "message_type": "send", + "message_type_id": 0, + "message_data": { + "asset": "XCP", + "quantity": 92945878046 + } + } + }, + { + "datahex": "00000014000000000aa4097d0000000005f5e100010000000000000000000c506172656e74206173736574", + "result": { + "message_type": "issuance", + "message_type_id": 20, + "message_data": { + "asset_id": 178522493, + "asset": "PARENT", + "subasset_longname": null, + "quantity": 100000000, + "divisible": true, + "lock": null, + "reset": null, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "Parent asset", + "status": "valid" + } + } + }, + { + "datahex": "0000001501530821671b10650000000005f5e10001108e90a57dba9967c422e83080f22f0c684368696c64206f6620706172656e74", + "result": { + "message_type": "issuance", + "message_type_id": 21, + "message_data": { + "asset_id": null, + "asset": null, + "subasset_longname": null, + "quantity": null, + "divisible": null, + "lock": null, + "reset": null, + "callable": null, + "call_date": null, + "call_price": null, + "description": null, + "status": "invalid: could not unpack" + } + } + } +] \ No newline at end of file From c2f5a779f5d09e92f7b0e5cf75a53226b04925d7 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 13:54:59 +0200 Subject: [PATCH 067/128] add content-type header --- counterparty-core/counterpartycore/lib/api/api_server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 5e9e496c35..997e31a277 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -90,6 +90,7 @@ def inject_headers(result, return_code=None): response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT + response.headers["Content-Type"] = "application/json" return response From 4962ead703db23c5fd164799dd89b08ff8e3aa19 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 14:51:11 +0200 Subject: [PATCH 068/128] compose routes return json --- .../counterpartycore/lib/transaction.py | 261 ++++++++++++------ 1 file changed, 170 insertions(+), 91 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 60613b9bd4..08d755fae6 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1225,22 +1225,28 @@ def compose_bet( :param leverage: Leverage, as a fraction of 5040 :param expiration: The number of blocks after which the bet expires if it remains unmatched """ - return compose_transaction( + params = { + "source": address, + "feed_address": feed_address, + "bet_type": bet_type, + "deadline": deadline, + "wager_quantity": wager_quantity, + "counterwager_quantity": counterwager_quantity, + "target_value": target_value, + "leverage": leverage, + "expiration": expiration, + } + rawtransaction = compose_transaction( db, name="bet", - params={ - "source": address, - "feed_address": feed_address, - "bet_type": bet_type, - "deadline": deadline, - "wager_quantity": wager_quantity, - "counterwager_quantity": counterwager_quantity, - "target_value": target_value, - "leverage": leverage, - "expiration": expiration, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "bet", + } def compose_broadcast( @@ -1254,18 +1260,24 @@ def compose_broadcast( :param fee_fraction: How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) :param text: The textual part of the broadcast """ - return compose_transaction( + params = { + "source": address, + "timestamp": timestamp, + "value": value, + "fee_fraction": fee_fraction, + "text": text, + } + rawtransaction = compose_transaction( db, name="broadcast", - params={ - "source": address, - "timestamp": timestamp, - "value": value, - "fee_fraction": fee_fraction, - "text": text, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "broadcast", + } def compose_btcpay(db, address: str, order_match_id: str, **construct_args): @@ -1274,12 +1286,18 @@ def compose_btcpay(db, address: str, order_match_id: str, **construct_args): :param address: The address that will be sending the payment :param order_match_id: The ID of the order match to pay for """ - return compose_transaction( + params = {"source": address, "order_match_id": order_match_id} + rawtransaction = compose_transaction( db, name="btcpay", - params={"source": address, "order_match_id": order_match_id}, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "btcpay", + } def compose_burn(db, address: str, quantity: int, overburn: bool = False, **construct_args): @@ -1289,12 +1307,18 @@ def compose_burn(db, address: str, quantity: int, overburn: bool = False, **cons :param quantity: The quantities of BTC to burn (1 BTC maximum burn per address) :param overburn: Whether to allow the burn to exceed 1 BTC for the address """ - return compose_transaction( + params = {"source": address, "quantity": quantity, "overburn": overburn} + rawtransaction = compose_transaction( db, name="burn", - params={"source": address, "quantity": quantity, "overburn": overburn}, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "burn", + } def compose_cancel(db, address: str, offer_hash: str, **construct_args): @@ -1303,12 +1327,18 @@ def compose_cancel(db, address: str, offer_hash: str, **construct_args): :param address: The address that placed the order/bet to be cancelled :param offer_hash: The hash of the order/bet to be cancelled """ - return compose_transaction( + params = {"source": address, "offer_hash": offer_hash} + rawtransaction = compose_transaction( db, name="cancel", - params={"source": address, "offer_hash": offer_hash}, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "cancel", + } def compose_destroy(db, address: str, asset: str, quantity: int, tag: str, **construct_args): @@ -1319,12 +1349,18 @@ def compose_destroy(db, address: str, asset: str, quantity: int, tag: str, **con :param quantity: The quantity of the asset to be destroyed :param tag: A tag for the destruction """ - return compose_transaction( + params = {"source": address, "asset": asset, "quantity": quantity, "tag": tag} + rawtransaction = compose_transaction( db, name="destroy", - params={"source": address, "asset": asset, "quantity": quantity, "tag": tag}, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "destroy", + } def compose_dispenser( @@ -1350,21 +1386,27 @@ def compose_dispenser( :param open_address: The address that you would like to open the dispenser on :param oracle_address: The address that you would like to use as a price oracle for this dispenser """ - return compose_transaction( + params = { + "source": address, + "asset": asset, + "give_quantity": give_quantity, + "escrow_quantity": escrow_quantity, + "mainchainrate": mainchainrate, + "status": status, + "open_address": open_address, + "oracle_address": oracle_address, + } + rawtransaction = compose_transaction( db, name="dispenser", - params={ - "source": address, - "asset": asset, - "give_quantity": give_quantity, - "escrow_quantity": escrow_quantity, - "mainchainrate": mainchainrate, - "status": status, - "open_address": open_address, - "oracle_address": oracle_address, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "dispenser", + } def compose_dividend( @@ -1377,17 +1419,23 @@ def compose_dividend( :param asset: The asset or subasset that the dividends are being rewarded on :param dividend_asset: The asset or subasset that the dividends are paid in """ - return compose_transaction( + params = { + "source": address, + "quantity_per_unit": quantity_per_unit, + "asset": asset, + "dividend_asset": dividend_asset, + } + rawtransaction = compose_transaction( db, name="dividend", - params={ - "source": address, - "quantity_per_unit": quantity_per_unit, - "asset": asset, - "dividend_asset": dividend_asset, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "dividend", + } def compose_issuance( @@ -1413,21 +1461,27 @@ def compose_issuance( :param reset: Wether this issuance should reset any existing supply :param description: A textual description for the asset """ - return compose_transaction( + params = { + "source": address, + "asset": asset, + "quantity": quantity, + "transfer_destination": transfer_destination, + "divisible": divisible, + "lock": lock, + "reset": reset, + "description": description, + } + rawtransaction = compose_transaction( db, name="issuance", - params={ - "source": address, - "asset": asset, - "quantity": quantity, - "transfer_destination": transfer_destination, - "divisible": divisible, - "lock": lock, - "reset": reset, - "description": description, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "issuance", + } def compose_mpma( @@ -1461,17 +1515,24 @@ def compose_mpma( raise exceptions.ComposeError("Quantity must be an integer") asset_dest_quant_list = list(zip(asset_list, destination_list, quantity_list)) - return compose_transaction( + params = { + "source": source, + "asset_dest_quant_list": asset_dest_quant_list, + "memo": memo, + "memo_is_hex": memo_is_hex, + } + + rawtransaction = compose_transaction( db, name="version.mpma", - params={ - "source": source, - "asset_dest_quant_list": asset_dest_quant_list, - "memo": memo, - "memo_is_hex": memo_is_hex, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "mpma", + } def compose_order( @@ -1495,20 +1556,26 @@ def compose_order( :param expiration: The number of blocks for which the order should be valid :param fee_required: The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) """ - return compose_transaction( + params = { + "source": address, + "give_asset": give_asset, + "give_quantity": give_quantity, + "get_asset": get_asset, + "get_quantity": get_quantity, + "expiration": expiration, + "fee_required": fee_required, + } + rawtransaction = compose_transaction( db, name="order", - params={ - "source": address, - "give_asset": give_asset, - "give_quantity": give_quantity, - "get_asset": get_asset, - "get_quantity": get_quantity, - "expiration": expiration, - "fee_required": fee_required, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "order", + } def compose_send( @@ -1532,20 +1599,26 @@ def compose_send( :param memo_is_hex: Whether the memo field is a hexadecimal string :param use_enhanced_send: If this is false, the construct a legacy transaction sending bitcoin dust """ - return compose_transaction( + params = { + "source": address, + "destination": destination, + "asset": asset, + "quantity": quantity, + "memo": memo, + "memo_is_hex": memo_is_hex, + "use_enhanced_send": use_enhanced_send, + } + rawtransaction = compose_transaction( db, name="send", - params={ - "source": address, - "destination": destination, - "asset": asset, - "quantity": quantity, - "memo": memo, - "memo_is_hex": memo_is_hex, - "use_enhanced_send": use_enhanced_send, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "send", + } def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **construct_args): @@ -1559,17 +1632,23 @@ def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **c - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. :param memo: The Memo associated with this transaction """ - return compose_transaction( + params = { + "source": address, + "destination": destination, + "flags": flags, + "memo": memo, + } + rawtransaction = compose_transaction( db, name="sweep", - params={ - "source": address, - "destination": destination, - "flags": flags, - "memo": memo, - }, + params=params, **construct_args, ) + return { + "rawtransaction": rawtransaction, + "params": params, + "name": "sweep", + } def info(db, rawtransaction: str, block_index: int = None): From e2dce1220fa1fa7bd3fa8871822ea38b5374ceca Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 14:59:51 +0200 Subject: [PATCH 069/128] /transactions/info returns also unpacked data --- counterparty-core/counterpartycore/lib/transaction.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 08d755fae6..360529857c 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1660,13 +1660,17 @@ def info(db, rawtransaction: str, block_index: int = None): source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( db, BlockchainParser().deserialize_tx(rawtransaction), block_index=block_index ) - return { + result = { "source": source, "destination": destination, "btc_amount": btc_amount, "fee": fee, "data": util.hexlify(data) if data else "", } + if data: + result["data"] = util.hexlify(data) + result["unpacked_data"] = unpack(db, result["data"], block_index) + return result def unpack(db, datahex: str, block_index: int = None): From 5b5be3ad69053909f74f61408ad3723e18da809a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 17:59:41 +0200 Subject: [PATCH 070/128] script to generate blueprint doc --- counterparty-core/tools/genapidoc.py | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 counterparty-core/tools/genapidoc.py diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py new file mode 100644 index 0000000000..5aea23480f --- /dev/null +++ b/counterparty-core/tools/genapidoc.py @@ -0,0 +1,36 @@ +import os + +from counterpartycore import server + +CURR_DIR = os.path.dirname(os.path.realpath(__file__)) +API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api/rest.md") + +md = """--- +title: REST API V2 +--- + +FORMAT: 1A + +# Counterpaty Core API + +The Counterparty Core API is the recommended way to query the state of a Counterparty node. The following routes are available. +""" + +current_group = None + +for path, route in server.routes.ROUTES.items(): + route_group = path.split("/")[1] + if route_group != current_group: + current_group = route_group + md += f"\n## Group {current_group.capitalize()}\n" + blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") + + title = " ".join([part.capitalize() for part in str(route["function"].__name__).split("_")]) + md += f"\n### {title} [`{blueprint_path}`]\n\n" + md += route["description"] + md += "\n\n+ Parameters\n" + for arg in route["args"]: + md += f" + {arg['name']} ({arg['type']}) - {arg.get('description', '')}\n" + +with open(API_DOC_FILE, "w") as f: + f.write(md) From 1d8812b9948d35fa85237b58feac139622a6b8d2 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 18:19:32 +0200 Subject: [PATCH 071/128] Add required/optional and default value in doc --- counterparty-core/tools/genapidoc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 5aea23480f..3665f5d2b3 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -30,7 +30,11 @@ md += route["description"] md += "\n\n+ Parameters\n" for arg in route["args"]: - md += f" + {arg['name']} ({arg['type']}) - {arg.get('description', '')}\n" + required = "required" if arg["required"] else "optional" + md += f" + {arg['name']} ({arg['type']}, {required}) - {arg.get('description', '')}\n" + if not arg["required"]: + md += f"\n + Default: `{arg.get('default', '')}`\n\n" with open(API_DOC_FILE, "w") as f: f.write(md) + print(f"API documentation written to {API_DOC_FILE}") From c0885bda38eec107e6c621c774aa161242fd0215 Mon Sep 17 00:00:00 2001 From: Adam Krellenstein Date: Tue, 23 Apr 2024 09:32:55 +0200 Subject: [PATCH 072/128] Tweak `genapidoc.py` --- counterparty-core/tools/genapidoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 3665f5d2b3..facbca5e11 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -11,9 +11,9 @@ FORMAT: 1A -# Counterpaty Core API +# Counterparty Core API -The Counterparty Core API is the recommended way to query the state of a Counterparty node. The following routes are available. +The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. The following routes are available: """ current_group = None From 9551a012b94987c50d377b8effd32e7e23039371 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Mon, 22 Apr 2024 18:23:33 +0200 Subject: [PATCH 073/128] remove \n --- counterparty-core/tools/genapidoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index facbca5e11..41e4e7adbd 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -33,7 +33,7 @@ required = "required" if arg["required"] else "optional" md += f" + {arg['name']} ({arg['type']}, {required}) - {arg.get('description', '')}\n" if not arg["required"]: - md += f"\n + Default: `{arg.get('default', '')}`\n\n" + md += f" + Default: `{arg.get('default', '')}`\n" with open(API_DOC_FILE, "w") as f: f.write(md) From 9c12f638481d5ae4b4a286deee6d00acad32df7e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 12:24:56 +0200 Subject: [PATCH 074/128] Use example args to generate output example --- .../counterpartycore/lib/ledger.py | 4 +-- counterparty-core/tools/genapidoc.py | 35 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 7ab0ba2de2..d4a9ca0445 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -1255,8 +1255,8 @@ def get_all_burns(db, status: str = "valid", offset: int = 0, limit: int = 100): def get_blocks(db, last: int = None, limit: int = 10): """ Returns the list of the last ten blocks - :param int last: The index of the most recent block to return - :param int limit: The number of blocks to return + :param int last: The index of the most recent block to return (e.g. 840000) + :param int limit: The number of blocks to return (e.g. 2) """ cursor = db.cursor() bindings = [] diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 41e4e7adbd..cc9fba2175 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -1,9 +1,28 @@ +import json import os +import requests from counterpartycore import server CURR_DIR = os.path.dirname(os.path.realpath(__file__)) API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api/rest.md") +API_ROOT = "http://api:api@localhost:4000" + + +def get_example_output(path, args): + url_keys = [] + for key, value in args.items(): + if f"{key}>" in path: + path = path.replace(f"<{key}>", value) + path = path.replace(f"", value) + url_keys.append(key) + for key in url_keys: + args.pop(key) + url = f"{API_ROOT}{path}" + print(f"GET {url}") + response = requests.get(url, params=args) # noqa S113 + return response.json() + md = """--- title: REST API V2 @@ -29,11 +48,25 @@ md += f"\n### {title} [`{blueprint_path}`]\n\n" md += route["description"] md += "\n\n+ Parameters\n" + example_args = {} for arg in route["args"]: required = "required" if arg["required"] else "optional" - md += f" + {arg['name']} ({arg['type']}, {required}) - {arg.get('description', '')}\n" + description = arg.get("description", "") + example_arg = "" + if "(e.g. " in description: + desc_arr = description.split("(e.g. ") + description = desc_arr[0] + example_args[arg["name"]] = desc_arr[1].replace(")", "") + example_arg = f": `{example_args[arg['name']]}`" + md += f" + {arg['name']}{example_arg} ({arg['type']}, {required}) - {description}\n" if not arg["required"]: md += f" + Default: `{arg.get('default', '')}`\n" + if example_args != {}: + example_output = get_example_output(path, example_args) + example_output_json = json.dumps(example_output, indent=4) + md += "\n+ Response 200 (application/json)\n\n" + for line in example_output_json.split("\n"): + md += f" {line}\n" with open(API_DOC_FILE, "w") as f: f.write(md) From 064b0cbcb217693a268d4a1de8f7123e386d8734 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 12:31:39 +0200 Subject: [PATCH 075/128] Add quotes for docusaurus --- counterparty-core/counterpartycore/server.py | 2 +- counterparty-core/tools/genapidoc.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index eb34ab5e84..54668d0496 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -31,7 +31,7 @@ ) from counterpartycore.lib import kickstart as kickstarter from counterpartycore.lib.api import api_server as api_v2 -from counterpartycore.lib.api import api_v1 +from counterpartycore.lib.api import api_v1, routes # noqa: F401 from counterpartycore.lib.telemetry.client import TelemetryClientLocal from counterpartycore.lib.telemetry.collector import TelemetryCollectorLive from counterpartycore.lib.telemetry.daemon import TelemetryDaemon diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index cc9fba2175..edc90cb94e 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -65,8 +65,10 @@ def get_example_output(path, args): example_output = get_example_output(path, example_args) example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" + md += " ```\n" for line in example_output_json.split("\n"): md += f" {line}\n" + md += " ```\n" with open(API_DOC_FILE, "w") as f: f.write(md) From 129c17fe758438d46a5ce334fa4e59936e25d45c Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 12:51:26 +0200 Subject: [PATCH 076/128] progress in example args/output --- .../counterpartycore/lib/ledger.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index d4a9ca0445..71cb064268 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -108,8 +108,8 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li def get_all_events(db, last: int = None, limit: int = 100): """ Returns all events - :param int last: The last event index to return - :param int limit: The maximum number of events to return + :param int last: The last event index to return (e.g. 10665092) + :param int limit: The maximum number of events to return (e.g. 5) """ return get_events(db, last=last, limit=limit) @@ -117,7 +117,7 @@ def get_all_events(db, last: int = None, limit: int = 100): def get_events_by_block(db, block_index: int): """ Returns the events of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ return get_events(db, block_index=block_index) @@ -125,8 +125,8 @@ def get_events_by_block(db, block_index: int): def get_events_by_block_and_event(db, block_index: int, event: str): """ Returns the events of a block filtered by event - :param int block_index: The index of the block to return - :param str event: The event to filter by + :param int block_index: The index of the block to return (e.g. 840464) + :param str event: The event to filter by (e.g. CREDIT) """ if event == "count": return get_events_counts_by_block(db, block_index=block_index) @@ -136,7 +136,7 @@ def get_events_by_block_and_event(db, block_index: int, event: str): def get_event_by_index(db, event_index: int): """ Returns the event of an index - :param int event_index: The index of the event to return + :param int event_index: The index of the event to return (e.g. 10665092) """ return get_events(db, event_index=event_index) @@ -144,9 +144,9 @@ def get_event_by_index(db, event_index: int): def get_events_by_event(db, event: str, last: int = None, limit: int = 100): """ Returns the events filtered by event name - :param str event: The event to return - :param int last: The last event index to return - :param int limit: The maximum number of events to return + :param str event: The event to return (e.g. CREDIT) + :param int last: The last event index to return (e.g. 10665092) + :param int limit: The maximum number of events to return (e.g. 5) """ return get_events(db, event=event, last=last, limit=limit) @@ -180,12 +180,12 @@ def get_all_mempool_events(db): return get_mempool_events(db) -def get_mempool_events_by_event(db, event_name: str): +def get_mempool_events_by_event(db, event: str): """ Returns the mempool events filtered by event name - :param str event_name: The event to return + :param str event: The event to return (e.g. CREDIT) """ - return get_mempool_events(db, event_name=event_name) + return get_mempool_events(db, event_name=event) def get_events_counts(db, block_index=None): @@ -206,7 +206,7 @@ def get_events_counts(db, block_index=None): def get_events_counts_by_block(db, block_index: int): """ Returns the event counts of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ return get_events_counts(db, block_index=block_index) @@ -526,7 +526,7 @@ def get_credits(db, address=None, asset=None, block_index=None, tx_index=None): def get_credits_by_block(db, block_index: int): """ Returns the credits of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ return get_credits(db, block_index=block_index) @@ -534,7 +534,7 @@ def get_credits_by_block(db, block_index: int): def get_credits_by_address(db, address: str): """ Returns the credits of an address - :param str address: The address to return + :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) """ return get_credits(db, address=address) @@ -542,7 +542,7 @@ def get_credits_by_address(db, address: str): def get_credits_by_asset(db, asset: str): """ Returns the credits of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. UNNEGOTIABLE) """ return get_credits(db, asset=asset) @@ -554,7 +554,7 @@ def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): def get_debits_by_block(db, block_index: int): """ Returns the debits of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ return get_debits(db, block_index=block_index) @@ -562,7 +562,7 @@ def get_debits_by_block(db, block_index: int): def get_debits_by_address(db, address: str): """ Returns the debits of an address - :param str address: The address to return + :param str address: The address to return (e.g. 178etygrwEeeyQso9we85rUqYZbkiqzL4A) """ return get_debits(db, address=address) @@ -570,7 +570,7 @@ def get_debits_by_address(db, address: str): def get_debits_by_asset(db, asset: str): """ Returns the debits of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. UNNEGOTIABLE) """ return get_debits(db, asset=asset) @@ -950,8 +950,8 @@ def get_asset_issued(db, address): def get_asset_balances(db, asset: str, exclude_zero_balances: bool = True): """ Returns the asset balances - :param str asset: The asset to return - :param bool exclude_zero_balances: Whether to exclude zero balances + :param str asset: The asset to return (e.g. UNNEGOTIABLE) + :param bool exclude_zero_balances: Whether to exclude zero balances (e.g. True) """ cursor = db.cursor() query = """ @@ -989,7 +989,7 @@ def get_asset_issuances_quantity(db, asset): def get_asset_info(db, asset: str): """ Returns the asset information - :param str asset: The asset to return + :param str asset: The asset to return (e.g. UNNEGOTIABLE) """ asset_name = resolve_subasset_longname(db, asset) @@ -1275,7 +1275,7 @@ def get_blocks(db, last: int = None, limit: int = 10): def get_block(db, block_index: int): """ Return the information of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ blocks = get_blocks(db, last=block_index, limit=1) if blocks: @@ -1286,7 +1286,7 @@ def get_block(db, block_index: int): def get_transactions_by_block(db, block_index: int): """ Returns the transactions of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ cursor = db.cursor() query = """ @@ -1329,7 +1329,7 @@ def get_transactions(db, tx_hash=None): def get_transaction(db, tx_hash: str): """ Returns the information of a transaction - :param str tx_hash: The hash of the transaction to return + :param str tx_hash: The hash of the transaction to return (e.g. 876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5) """ transactions = get_transactions(db, tx_hash) if transactions: From becf57c40c7b33a724c75baed546266d27997700 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 17:36:38 +0200 Subject: [PATCH 077/128] All routes with output example except compose and backend; Add some limit and offset param --- .../counterpartycore/lib/api/util.py | 7 +- .../counterpartycore/lib/ledger.py | 246 ++++++++++++------ .../counterpartycore/lib/transaction.py | 4 +- counterparty-core/tools/genapidoc.py | 2 +- 4 files changed, 169 insertions(+), 90 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index c74db6c13a..5c793a5583 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -56,12 +56,15 @@ def healthz(db, check_type="heavy"): def handle_healthz_route(db, check_type: str = "heavy"): """ Health check route. - :param check_type: Type of health check to perform. Options are 'light' and 'heavy'. + :param check_type: Type of health check to perform. Options are 'light' and 'heavy' (e.g. light) """ msg, code = "Healthy", 200 if not healthz(db, check_type): msg, code = "Unhealthy", 503 - return flask.Response(msg, code, mimetype="application/json") + result = {"data": msg, "success": code == 200} + if code != 200: + result["error"] = msg + return flask.Response(to_json(result), code, mimetype="application/json") def remove_rowids(query_result): diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 71cb064268..57280f5709 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -442,8 +442,8 @@ def get_balance(db, address, asset, raise_error_if_no_balance=False, return_list def get_balance_object(db, address: str, asset: str): """ Returns the balance of an address and asset - :param str address: The address to return - :param str asset: The asset to return + :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param str asset: The asset to return (e.g. XCP) """ return { "address": address, @@ -455,7 +455,7 @@ def get_balance_object(db, address: str, asset: str): def get_address_balances(db, address: str): """ Returns the balances of an address - :param str address: The address to return + :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) """ cursor = db.cursor() query = """ @@ -497,7 +497,9 @@ def get_balances_count(db, address): return cursor.fetchall() -def get_credits_or_debits(db, table, address=None, asset=None, block_index=None, tx_index=None): +def get_credits_or_debits( + db, table, address=None, asset=None, block_index=None, tx_index=None, offset=0, limit=None +): cursor = db.cursor() where = [] bindings = [] @@ -513,14 +515,24 @@ def get_credits_or_debits(db, table, address=None, asset=None, block_index=None, if tx_index is not None: where.append("tx_index = ?") bindings.append(tx_index) + query_limit = "" + if limit is not None: + query_limit = "LIMIT ?" + bindings.append(limit) + query_offset = "" + if offset > 0: + query_offset = "OFFSET ?" + bindings.append(offset) # no sql injection here - query = f"""SELECT * FROM {table} WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + query = f"""SELECT * FROM {table} WHERE ({" AND ".join(where)}) {query_limit} {query_offset}""" # nosec B608 # noqa: S608 cursor.execute(query, tuple(bindings)) return cursor.fetchall() -def get_credits(db, address=None, asset=None, block_index=None, tx_index=None): - return get_credits_or_debits(db, "credits", address, asset, block_index, tx_index) +def get_credits(db, address=None, asset=None, block_index=None, tx_index=None, limit=100, offset=0): + return get_credits_or_debits( + db, "credits", address, asset, block_index, tx_index, limit=limit, offset=offset + ) def get_credits_by_block(db, block_index: int): @@ -531,24 +543,30 @@ def get_credits_by_block(db, block_index: int): return get_credits(db, block_index=block_index) -def get_credits_by_address(db, address: str): +def get_credits_by_address(db, address: str, limit: int = 100, offset: int = 0): """ Returns the credits of an address :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param int limit: The maximum number of credits to return (e.g. 5) + :param int offset: The offset of the credits to return (e.g. 0) """ - return get_credits(db, address=address) + return get_credits(db, address=address, limit=limit, offset=offset) -def get_credits_by_asset(db, asset: str): +def get_credits_by_asset(db, asset: str, limit: int = 100, offset: int = 0): """ Returns the credits of an asset :param str asset: The asset to return (e.g. UNNEGOTIABLE) + :param int limit: The maximum number of credits to return (e.g. 5) + :param int offset: The offset of the credits to return (e.g. 0) """ - return get_credits(db, asset=asset) + return get_credits(db, asset=asset, limit=limit, offset=offset) -def get_debits(db, address=None, asset=None, block_index=None, tx_index=None): - return get_credits_or_debits(db, "debits", address, asset, block_index, tx_index) +def get_debits(db, address=None, asset=None, block_index=None, tx_index=None, limit=100, offset=0): + return get_credits_or_debits( + db, "debits", address, asset, block_index, tx_index, limit=limit, offset=offset + ) def get_debits_by_block(db, block_index: int): @@ -559,24 +577,35 @@ def get_debits_by_block(db, block_index: int): return get_debits(db, block_index=block_index) -def get_debits_by_address(db, address: str): +def get_debits_by_address(db, address: str, limit: int = 100, offset: int = 0): """ Returns the debits of an address - :param str address: The address to return (e.g. 178etygrwEeeyQso9we85rUqYZbkiqzL4A) + :param str address: The address to return (e.g. bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y) + :param int limit: The maximum number of debits to return (e.g. 5) + :param int offset: The offset of the debits to return (e.g. 0) """ - return get_debits(db, address=address) + return get_debits(db, address=address, limit=limit, offset=offset) -def get_debits_by_asset(db, asset: str): +def get_debits_by_asset(db, asset: str, limit: int = 100, offset: int = 0): """ Returns the debits of an asset - :param str asset: The asset to return (e.g. UNNEGOTIABLE) + :param str asset: The asset to return (e.g. XCP) + :param int limit: The maximum number of debits to return (e.g. 5) + :param int offset: The offset of the debits to return (e.g. 0) """ - return get_debits(db, asset=asset) + return get_debits(db, asset=asset, limit=limit, offset=offset) def get_sends_or_receives( - db, source=None, destination=None, asset=None, block_index=None, status="valid" + db, + source=None, + destination=None, + asset=None, + block_index=None, + status="valid", + limit=None, + offset=0, ): cursor = db.cursor() where = [] @@ -596,72 +625,118 @@ def get_sends_or_receives( if status is not None: where.append("status = ?") bindings.append(status) + query_limit = "" + if limit is not None: + query_limit = "LIMIT ?" + bindings.append(limit) + query_offset = "" + if offset > 0: + query_offset = "OFFSET ?" + bindings.append(offset) # no sql injection here - query = f"""SELECT * FROM sends WHERE ({" AND ".join(where)})""" # nosec B608 # noqa: S608 + query = f"""SELECT * FROM sends WHERE ({" AND ".join(where)}) {query_limit} {query_offset}""" # nosec B608 # noqa: S608 cursor.execute(query, tuple(bindings)) return cursor.fetchall() -def get_sends_or_receives_by_block(db, block_index: int): +def get_sends_or_receives_by_block(db, block_index: int, limit: int = 100, offset: int = 0): """ Returns the sends of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840459) """ - return get_sends_or_receives(db, block_index=block_index) + return get_sends_or_receives(db, block_index=block_index, limit=limit, offset=offset) -def get_sends_or_receives_by_asset(db, asset: str): +def get_sends_or_receives_by_asset(db, asset: str, limit: int = 100, offset: int = 0): """ Returns the sends of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. XCP) + :param int limit: The maximum number of sends to return (e.g. 5) + :param int offset: The offset of the sends to return (e.g. 0) """ - return get_sends_or_receives(db, asset=asset) + return get_sends_or_receives(db, asset=asset, limit=limit, offset=offset) -def get_sends(db, address=None, asset=None, block_index=None, status="valid"): +def get_sends( + db, + address=None, + asset=None, + block_index=None, + status="valid", + limit: int = 100, + offset: int = 0, +): return get_sends_or_receives( - db, source=address, asset=asset, block_index=block_index, status=status + db, + source=address, + asset=asset, + block_index=block_index, + status=status, + limit=limit, + offset=offset, ) -def get_send_by_address(db, address: str): +def get_send_by_address(db, address: str, limit: int = 100, offset: int = 0): """ Returns the sends of an address - :param str address: The address to return + :param str address: The address to return (e.g. 1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W) + :param int limit: The maximum number of sends to return (e.g. 5) + :param int offset: The offset of the sends to return (e.g. 0) """ - return get_sends(db, address=address) + return get_sends(db, address=address, limit=limit, offset=offset) def get_send_by_address_and_asset(db, address: str, asset: str): """ Returns the sends of an address and asset - :param str address: The address to return - :param str asset: The asset to return + :param str address: The address to return (e.g. 1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W) + :param str asset: The asset to return (e.g. XCP) """ return get_sends(db, address=address, asset=asset) -def get_receives(db, address=None, asset=None, block_index=None, status="valid"): +def get_receives( + db, + address=None, + asset=None, + block_index=None, + status="valid", + limit: int = 100, + offset: int = 0, +): return get_sends_or_receives( - db, destination=address, asset=asset, block_index=block_index, status=status + db, + destination=address, + asset=asset, + block_index=block_index, + status=status, + limit=limit, + offset=offset, ) -def get_receive_by_address(db, address: str): +def get_receive_by_address(db, address: str, limit: int = 100, offset: int = 0): """ Returns the receives of an address - :param str address: The address to return + :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param int limit: The maximum number of receives to return (e.g. 5) + :param int offset: The offset of the receives to return (e.g. 0) """ - return get_receives(db, address=address) + return get_receives(db, address=address, limit=limit, offset=offset) -def get_receive_by_address_and_asset(db, address: str, asset: str): +def get_receive_by_address_and_asset( + db, address: str, asset: str, limit: int = 100, offset: int = 0 +): """ Returns the receives of an address and asset - :param str address: The address to return - :param str asset: The asset to return + :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param str asset: The asset to return (e.g. XCP) + :param int limit: The maximum number of receives to return (e.g. 5) + :param int offset: The offset of the receives to return (e.g. 0) """ - return get_receives(db, address=address, asset=asset) + return get_receives(db, address=address, asset=asset, limit=limit, offset=offset) def get_sweeps(db, address=None, block_index=None, status="valid"): @@ -686,7 +761,7 @@ def get_sweeps(db, address=None, block_index=None, status="valid"): def get_sweeps_by_block(db, block_index: int): """ Returns the sweeps of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 836519) """ return get_sweeps(db, block_index=block_index) @@ -694,7 +769,7 @@ def get_sweeps_by_block(db, block_index: int): def get_sweeps_by_address(db, address: str): """ Returns the sweeps of an address - :param str address: The address to return + :param str address: The address to return (e.g. 18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87) """ return get_sweeps(db, address=address) @@ -1072,7 +1147,7 @@ def get_issuances( def get_issuances_by_block(db, block_index: int): """ Returns the issuances of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840464) """ return get_issuances(db, block_index=block_index) @@ -1080,7 +1155,7 @@ def get_issuances_by_block(db, block_index: int): def get_issuances_by_asset(db, asset: str): """ Returns the issuances of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. UNNEGOTIABLE) """ return get_issuances(db, asset=asset) @@ -1099,8 +1174,8 @@ def get_assets_by_longname(db, asset_longname): def get_valid_assets(db, offset: int = 0, limit: int = 100): """ Returns the valid assets - :param int offset: The offset of the assets to return - :param int limit: The limit of the assets to return + :param int offset: The offset of the assets to return (e.g. 0) + :param int limit: The limit of the assets to return (e.g. 5) """ try: int(offset) @@ -1114,15 +1189,16 @@ def get_valid_assets(db, offset: int = 0, limit: int = 100): WHERE status = 'valid' GROUP BY asset ORDER BY asset ASC + LIMIT ? OFFSET ? """ - cursor.execute(query) + cursor.execute(query, (limit, offset)) return cursor.fetchall() def get_dividends(db, asset: str): """ Returns the dividends of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. GMONEYPEPE) """ cursor = db.cursor() query = """ @@ -1172,9 +1248,9 @@ def get_oracle_last_price(db, oracle_address, block_index): def get_broadcasts_by_source(db, address: str, status: str = "valid", order_by: str = "DESC"): """ Returns the broadcasts of a source - :param str address: The address to return - :param str status: The status of the broadcasts to return - :param str order_by: The order of the broadcasts to return + :param str address: The address to return (e.g. 1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk) + :param str status: The status of the broadcasts to return (e.g. valid) + :param str order_by: The order of the broadcasts to return (e.g. ASC) """ if order_by not in ["ASC", "DESC"]: raise exceptions.InvalidArgument("Invalid order_by parameter") @@ -1218,7 +1294,7 @@ def get_burns(db, address: str = None, status: str = "valid"): def get_burns_by_address(db, address: str): """ Returns the burns of an address - :param str address: The address to return + :param str address: The address to return (e.g. 1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W) """ return get_burns(db, address=address) @@ -1226,9 +1302,9 @@ def get_burns_by_address(db, address: str): def get_all_burns(db, status: str = "valid", offset: int = 0, limit: int = 100): """ Returns the burns - :param str status: The status of the burns to return - :param int offset: The offset of the burns to return - :param int limit: The limit of the burns to return + :param str status: The status of the burns to return (e.g. valid) + :param int offset: The offset of the burns to return (e.g. 10) + :param int limit: The limit of the burns to return (e.g. 5) """ try: int(offset) @@ -1361,7 +1437,7 @@ def get_addresses(db, address=None): def get_expirations(db, block_index: int): """ Returns the expirations of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840356) """ cursor = db.cursor() queries = [ @@ -1399,7 +1475,7 @@ def get_expirations(db, block_index: int): def get_cancels(db, block_index: int): """ Returns the cancels of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 839746) """ cursor = db.cursor() query = """ @@ -1414,7 +1490,7 @@ def get_cancels(db, block_index: int): def get_destructions(db, block_index: int): """ Returns the destructions of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 839988) """ cursor = db.cursor() query = """ @@ -1600,7 +1676,7 @@ def get_dispenser_info(db, tx_hash=None, tx_index=None): def get_dispenser_info_by_tx_hash(db, tx_hash: str): """ Returns the dispenser information by tx_hash - :param str tx_hash: The hash of the dispenser to return + :param str tx_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) """ return get_dispenser_info(db, tx_hash=tx_hash) @@ -1777,7 +1853,7 @@ def get_dispensers( def get_dispensers_by_address(db, address: str, status: int = 0): """ Returns the dispensers of an address - :param str address: The address to return + :param str address: The address to return (e.g. bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz) """ return get_dispensers(db, address=address, status=status) @@ -1785,7 +1861,7 @@ def get_dispensers_by_address(db, address: str, status: int = 0): def get_dispensers_by_asset(db, asset: str, status: int = 0): """ Returns the dispensers of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. ERYKAHPEPU) """ return get_dispensers(db, asset=asset, status=status) @@ -1793,8 +1869,8 @@ def get_dispensers_by_asset(db, asset: str, status: int = 0): def get_dispensers_by_address_and_asset(db, address: str, asset: str, status: int = 0): """ Returns the dispensers of an address and an asset - :param str address: The address to return - :param str asset: The asset to return + :param str address: The address to return (e.g. bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz) + :param str asset: The asset to return (e.g. ERYKAHPEPU) """ return get_dispensers(db, address=address, asset=asset, status=status) @@ -1818,7 +1894,7 @@ def get_dispenses(db, dispenser_tx_hash=None, block_index=None): def get_dispenses_by_block(db, block_index: int): """ Returns the dispenses of a block - :param int block_index: The index of the block to return + :param int block_index: The index of the block to return (e.g. 840322) """ return get_dispenses(db, block_index=block_index) @@ -1826,7 +1902,7 @@ def get_dispenses_by_block(db, block_index: int): def get_dispenses_by_dispenser(db, tx_hash: str): """ Returns the dispenses of a dispenser - :param str tx_hash: The hash of the dispenser to return + :param str tx_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) """ return get_dispenses(db, dispenser_tx_hash=tx_hash) @@ -1888,7 +1964,7 @@ def get_bet_matches_to_expire(db, block_time): def get_bet(db, tx_hash: str): """ Returns the information of a bet - :param str tx_hash: The hash of the bet to return + :param str tx_hash: The hash of the bet to return (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) """ cursor = db.cursor() query = """ @@ -1936,8 +2012,8 @@ def get_matching_bets(db, feed_address, bet_type): def get_bet_by_feed(db, address: str, status: str = "open"): """ Returns the bets of a feed - :param str address: The address of the feed - :param str status: The status of the bet + :param str address: The address of the feed (e.g. 1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk) + :param str status: The status of the bet (e.g. filled) """ cursor = db.cursor() query = """ @@ -1957,8 +2033,8 @@ def get_bet_by_feed(db, address: str, status: str = "open"): def get_bet_matches_by_bet(db, tx_hash: str, status: str = "pending"): """ Returns the bet matches of a bet - :param str tx_hash: The hash of the bet - :param str status: The status of the bet matches + :param str tx_hash: The hash of the bet (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) + :param str status: The status of the bet matches (e.g. expired) """ cursor = db.cursor() query = """ @@ -1977,15 +2053,15 @@ def get_bet_matches_by_bet(db, tx_hash: str, status: str = "pending"): def get_resolutions_by_bet(db, tx_hash: str): """ Returns the resolutions of a bet - :param str tx_hash: The hash of the bet + :param str tx_hash: The hash of the bet (e.g. 36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace) """ cursor = db.cursor() query = """ SELECT * FROM bet_match_resolutions - WHERE bet_match_id LIKE '%:tx_hash%' + WHERE bet_match_id LIKE ? """ - bindings = {"tx_hash": tx_hash} + bindings = (f"%{tx_hash}%",) cursor.execute(query, bindings) return cursor.fetchall() @@ -2072,7 +2148,7 @@ def get_order_matches_to_expire(db, block_index): def get_order(db, tx_hash: str): """ Returns the information of an order - :param str tx_hash: The hash of the order to return + :param str tx_hash: The hash of the order to return (e.g. 23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776) """ cursor = db.cursor() query = """ @@ -2147,8 +2223,8 @@ def get_matching_orders(db, tx_hash, give_asset, get_asset): def get_orders_by_asset(db, asset: str, status: str = "open"): """ Returns the orders of an asset - :param str asset: The asset to return - :param str status: The status of the orders to return + :param str asset: The asset to return (e.g. NEEDPEPE) + :param str status: The status of the orders to return (e.g. filled) """ cursor = db.cursor() query = """ @@ -2167,8 +2243,8 @@ def get_orders_by_asset(db, asset: str, status: str = "open"): def get_order_matches_by_order(db, tx_hash: str, status: str = "pending"): """ Returns the order matches of an order - :param str tx_hash: The hash of the order - :param str status: The status of the order matches to return + :param str tx_hash: The hash of the order (e.g. 5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947) + :param str status: The status of the order matches to return (e.g. completed) """ cursor = db.cursor() query = """ @@ -2187,15 +2263,15 @@ def get_order_matches_by_order(db, tx_hash: str, status: str = "pending"): def get_btcpays_by_order(db, tx_hash: str): """ Returns the BTC pays of an order - :param str tx_hash: The hash of the order + :param str tx_hash: The hash of the order (e.g. 299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4) """ cursor = db.cursor() query = """ SELECT * FROM btcpays - WHERE order_match_id LIKE '%:tx_hash%' + WHERE order_match_id LIKE ? """ - bindings = {"tx_hash": tx_hash} + bindings = (f"%{tx_hash}%",) cursor.execute(query, bindings) return cursor.fetchall() @@ -2631,7 +2707,7 @@ def holders(db, asset, exclude_empty_holders=False): def get_asset_holders(db, asset: str): """ Returns the holders of an asset - :param str asset: The asset to return + :param str asset: The asset to return (e.g. ERYKAHPEPU) """ asset_name = resolve_subasset_longname(db, asset) return holders(db, asset_name, True) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 360529857c..5963d828c3 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1654,7 +1654,7 @@ def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **c def info(db, rawtransaction: str, block_index: int = None): """ Returns Counterparty information from a raw transaction in hex format. - :param rawtransaction: Raw transaction in hex format + :param rawtransaction: Raw transaction in hex format (e.g. 01000000017828697743c03aef6a3a8ba54b22bf579ffcab8161faf20e7b20c4ecd75cc986010000006b483045022100d1bd0531bb1ed2dd2cbf77d6933273e792a3dbfa84327d419169850ddd5976f502205d1ab0f7bcbf1a0cc183f0520c9aa8f711d41cb790c0c4ac39da6da4a093d798012103d3b1f711e907acb556e239f6cafb6a4f7fe40d8dd809b0e06e739c2afd73f202ffffffff0200000000000000004d6a4bf29880b93b0711524c7ef9c76835752088db8bd4113a3daf41fc45ffdc8867ebdbf26817fae377696f36790e52f51005806e9399a427172fedf348cf798ed86e548002ee96909eef0775ec3c2b0100000000001976a91443434cf159cc585fbd74daa9c4b833235b19761b88ac00000000) :param block_index: Block index mandatory for transactions before block 335000 """ source, destination, btc_amount, fee, data, extra = gettxinfo.get_tx_info( @@ -1676,7 +1676,7 @@ def info(db, rawtransaction: str, block_index: int = None): def unpack(db, datahex: str, block_index: int = None): """ Unpacks Counterparty data in hex format and returns the message type and data. - :param datahex: Data in hex format + :param datahex: Data in hex format (e.g. 16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245) :param block_index: Block index of the transaction containing this data """ data = binascii.unhexlify(datahex) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index edc90cb94e..b434a5fd34 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -61,7 +61,7 @@ def get_example_output(path, args): md += f" + {arg['name']}{example_arg} ({arg['type']}, {required}) - {description}\n" if not arg["required"]: md += f" + Default: `{arg.get('default', '')}`\n" - if example_args != {}: + if example_args != {} or route["args"] == []: example_output = get_example_output(path, example_args) example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" From 2c37b6589ec5fe08948bf84688477b09d77f7e0f Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 18:45:53 +0200 Subject: [PATCH 078/128] Add cache to generate doc faster --- .../counterpartycore/lib/transaction.py | 18 +- counterparty-core/tools/apicache.json | 1832 +++++++++++++++++ counterparty-core/tools/genapidoc.py | 18 +- 3 files changed, 1857 insertions(+), 11 deletions(-) create mode 100644 counterparty-core/tools/apicache.json diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 5963d828c3..0d721a2b0b 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1210,20 +1210,20 @@ def compose_bet( counterwager_quantity: int, expiration: int, leverage: int = 5040, - target_value: float = None, + target_value: int = None, **construct_args, ): """ Composes a transaction to issue a bet against a feed. - :param address: The address that will make the bet - :param feed_address: The address that hosts the feed to be bet on - :param bet_type: Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual - :param deadline: The time at which the bet should be decided/settled, in Unix time (seconds since epoch) - :param wager_quantity: The quantities of XCP to wager (in satoshis, hence integer). - :param counterwager_quantity: The minimum quantities of XCP to be wagered against, for the bets to match - :param target_value: Target value for Equal/NotEqual bet + :param address: The address that will make the bet (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param feed_address: The address that hosts the feed to be bet on (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param bet_type: Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual (e.g. 2) + :param deadline: The time at which the bet should be decided/settled, in Unix time (seconds since epoch) (e.g. 3000000000) + :param wager_quantity: The quantities of XCP to wager (in satoshis, hence integer) (e.g. 1000) + :param counterwager_quantity: The minimum quantities of XCP to be wagered against, for the bets to match (e.g. 1000) + :param expiration: The number of blocks after which the bet expires if it remains unmatched (e.g. 100) :param leverage: Leverage, as a fraction of 5040 - :param expiration: The number of blocks after which the bet expires if it remains unmatched + :param target_value: Target value for Equal/NotEqual bet (e.g. 1000) """ params = { "source": address, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json new file mode 100644 index 0000000000..d6ce0c4c31 --- /dev/null +++ b/counterparty-core/tools/apicache.json @@ -0,0 +1,1832 @@ +{ + "/blocks": [ + { + "block_index": 840000, + "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", + "block_time": 1713571767, + "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "difficulty": 86388558925171.02, + "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", + "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", + "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" + }, + { + "block_index": 839999, + "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "block_time": 1713571533, + "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", + "difficulty": 86388558925171.02, + "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", + "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", + "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" + } + ], + "/blocks/": { + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", + "difficulty": 86388558925171.02, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + }, + "/blocks//transactions": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + } + ], + "/blocks//events": [ + { + "event_index": 14194760, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 840464, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194759, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194757, + "event": "ASSET_ISSUANCE", + "bindings": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "block_index": 840464, + "call_date": 0, + "call_price": 0.0, + "callable": false, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "divisible": false, + "fee_paid": 50000000, + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "locked": false, + "quantity": 1, + "reset": false, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "status": "valid", + "transfer": false, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194756, + "event": "ASSET_CREATION", + "bindings": { + "asset_id": "75313533584419238", + "asset_longname": null, + "asset_name": "UNNEGOTIABLE", + "block_index": 840464 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194755, + "event": "DEBIT", + "bindings": { + "action": "issuance fee", + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "block_index": 840464, + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 50000000, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194754, + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "btc_amount": 0, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "destination": "", + "fee": 56565, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852779 + }, + { + "event_index": 14194753, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "difficulty": 86388558925171.02, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" + }, + "block_index": 840464, + "timestamp": 1713852779 + } + ], + "/blocks//events/counts": [ + { + "event": "ASSET_CREATION", + "event_count": 1 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 1 + }, + { + "event": "BLOCK_PARSED", + "event_count": 1 + }, + { + "event": "CREDIT", + "event_count": 1 + }, + { + "event": "DEBIT", + "event_count": 1 + }, + { + "event": "NEW_BLOCK", + "event_count": 1 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 1 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 1 + } + ], + "/blocks//events/": [ + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + } + ], + "/blocks//credits": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ], + "/blocks//debits": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ], + "/blocks//expirations": [ + { + "type": "order", + "object_id": "533d5c0ecd8ca9c2946d3298cc5e570eee55b62b887dd85c95de6de4fdc7f441" + }, + { + "type": "order", + "object_id": "b048661afeee3f266792481168024abc0d7648fe0e019e4a1e0fd9867c2c0ffc" + } + ], + "/blocks//cancels": [ + { + "tx_index": 2725738, + "tx_hash": "793af9129c7368f974c3ea0c87ad38131f0d82d19fbaf1adf8aaf2e657ec42b8", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "04b258ac37f73e3b9a8575110320d67c752e1baace0f516da75845f388911735", + "status": "valid" + }, + { + "tx_index": 2725739, + "tx_hash": "2071e8a6fbc0c443b152d513c754356f8f962db2fa694de8c6826b57413cc190", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "b1622dbe4f0ce740cb6c18f6f136876bc4949c40a62bc8cceefa81fd6679a57f", + "status": "valid" + } + ], + "/blocks//destructions": [ + { + "tx_index": 2726496, + "tx_hash": "f5609facc8dac6cdf70b15c514ea15a9acc24a9bd86dcac2b845d5740fbcc50b", + "block_index": 839988, + "source": "1FpLAtreZjTVCMcj1pq1AHWuqcs3n7obMm", + "asset": "COBBEE", + "quantity": 50000, + "tag": "", + "status": "valid" + } + ], + "/blocks//issuances": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", + "quantity": 1, + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ], + "/blocks//sends": [ + { + "tx_index": 2726604, + "tx_hash": "b4bbb14c99dd260eb634243e5c595e1b7213459979857a32850de84989bb71ec", + "block_index": 840459, + "source": "13Hnmhs5gy2yXKVBx4wSM5HCBdKnaSBZJH", + "destination": "1LfT83WAxbN9qKhtrXxcQA6xgdhfZk21Hz", + "asset": "GAMESOFTRUMP", + "quantity": 1, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/blocks//dispenses": [ + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ], + "/blocks//sweeps": [ + { + "tx_index": 2720536, + "tx_hash": "9309a4c0aed426e281a52e5d48acadd1464999269a5e75cf2293edd0277d743d", + "block_index": 836519, + "source": "1DMVnJuqBobXA9xYioabBsR4mN8bvVtCAW", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + }, + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ], + "/transactions/info": { + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "unpacked_data": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } + }, + "/transactions/unpack": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + }, + "/transactions/": { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + }, + "/addresses/
/balances": [ + { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + } + ], + "/addresses/
/balances/": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + }, + "/addresses/
/credits": [ + { + "block_index": 830981, + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "calling_function": "send", + "event": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "tx_index": 2677412 + } + ], + "/addresses/
/debits": [ + { + "block_index": 836949, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 40000000000, + "action": "open dispenser", + "event": "53ed08176d3479f49986e9282293da85cebc03835b128d8e790ee587f9f1c750", + "tx_index": 2721524 + }, + { + "block_index": 840388, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 250000000000, + "action": "send", + "event": "bc54968ba7d0a59a47b276602e2dbdcf01b14009742e0d7b50272cbae529a9a4", + "tx_index": 2726594 + } + ], + "/addresses/
/bets": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + }, + { + "tx_index": 61338, + "tx_hash": "0fcc7f5190c028f6c5534554d10ec5b4a9246d63826421cd58be2d572d11f088", + "block_index": 320704, + "source": "1Ew38GxczvV1KxjzZsq9f8UuRzHkHQrL5C", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 2, + "deadline": 1410728400, + "wager_quantity": 1000000, + "wager_remaining": 0, + "counterwager_quantity": 1999991, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 13, + "expire_index": 320715, + "fee_fraction_int": 1000000, + "status": "filled" + } + ], + "/addresses/
/broadcasts": [ + { + "tx_index": 15055, + "tx_hash": "774887e555a6ae5a8c058ebc0185058307977f01a2d4d326e71f37d6dd977154", + "block_index": 304048, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1401815290, + "value": -1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + }, + { + "tx_index": 61477, + "tx_hash": "5d49993bec727622c7b41c84e2b1e65c368f33390d633d217131ffcc5b592f0d", + "block_index": 320718, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1410732503, + "value": 1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + } + ], + "/addresses/
/burns": [ + { + "tx_index": 3070, + "tx_hash": "4560d0e3d04927108b615ab106040489aca9c4aceedcf69d2b71f63b3139c7ae", + "block_index": 283810, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "burned": 10000000, + "earned": 10000000000, + "status": "valid" + } + ], + "/addresses/
/sends": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/addresses/
/receives": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/addresses/
/sends/": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/addresses/
/receives/": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/addresses/
/dispensers": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ], + "/addresses/
/dispensers/": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ], + "/addresses/
/sweeps": [ + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ], + "/addresses/
/compose/bet": { + "rawtransaction": "01000000019a753a6b8be54cdee2acd408f6199e29092c8c32e13912865a68da8a0d9ae065010000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f88dab644080b5ae64cba1e7f06510694b41e385fc9120e17c1037dd08399355abbaa0401e65959c9caa4dec4efe32189152b00000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac00000000", + "params": { + "source": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "feed_address": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "bet_type": 2, + "deadline": 3000000000, + "wager_quantity": 1000, + "counterwager_quantity": 1000, + "target_value": 1000, + "leverage": 5040, + "expiration": 100 + }, + "name": "bet" + }, + "/assets": [ + { + "asset": "A100000000000000000", + "asset_longname": null + }, + { + "asset": "A1000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000001", + "asset_longname": null + }, + { + "asset": "A10000000000000000002", + "asset_longname": null + } + ], + "/assets/": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "owner": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "divisible": false, + "locked": false, + "supply": 1, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "holder_count": 1 + }, + "/assets//balances": [ + { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1 + } + ], + "/assets//balances/
": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + }, + "/assets//orders": [ + { + "tx_index": 825373, + "tx_hash": "0129611a0aece52adddf6d929e75c703baa9cdcb7e4ce887aa859f9640aa9640", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 400000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 2225134, + "tx_hash": "5b6e0c741d765ebd883dc16eecfb5c340c52865cabf297ca2c1432437c1348b7", + "block_index": 772817, + "source": "1FnM7akSCD8G3fRQHCUEXRCfL35gptsPZB", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 80800000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 777817, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 5544, + "fee_provided_remaining": 5544, + "status": "filled" + }, + { + "tx_index": 1946026, + "tx_hash": "75dc6ee1f67317e674ef33b617d3a9839ee53bf4a2e8274c88d6202d4d89b59a", + "block_index": 727444, + "source": "1GotRejB6XsGgMsM79TvcypeanDJRJbMtg", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 70000000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732381, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 2202451, + "tx_hash": "77f568fc6604dbe209d2ea1b0158d7de20723c0178107eb570f4f2a719b0d7c7", + "block_index": 772817, + "source": "184gKLQTtQU29LXbxbYJkUV4if9SmW6v2d", + "give_asset": "XCP", + "give_quantity": 80800000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 773300, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 825411, + "tx_hash": "7b2369f40078f4d98a3d3a7733315a1c4efd7977c75f7066dd447d5c7eed7f20", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 300000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 825403, + "tx_hash": "7e1abf6ad57eb61227015fc7a333da034b4dd2f1c4e23cf106864b60a20feef7", + "block_index": 455460, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 200000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456460, + "fee_required": 20000, + "fee_required_remaining": 20000, + "fee_provided": 50766, + "fee_provided_remaining": 50766, + "status": "filled" + }, + { + "tx_index": 825370, + "tx_hash": "8e4d324407b62de773af53f8f7a556882ac82a217c216491a28072f293918fe6", + "block_index": 455457, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 100000000000, + "get_remaining": -1100000000, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 75791, + "fee_provided_remaining": 75791, + "status": "filled" + }, + { + "tx_index": 825413, + "tx_hash": "927878fa98edb6d24310c45254c324f3d5a7f625e2a3a0e7fd1e749b49493750", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 400000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 1946587, + "tx_hash": "b747f290cbbad6faa1c1c05d5c6d001b5a3ef487027bb0d4eefcdc9f6e865c39", + "block_index": 727444, + "source": "1AtcSh7uxenQ6AR5xqr6agAegWRUF5N4uh", + "give_asset": "XCP", + "give_quantity": 70000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732444, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 792, + "fee_provided_remaining": 792, + "status": "filled" + }, + { + "tx_index": 825371, + "tx_hash": "b83c96217214decb6316c3619bc88a3471d17e46eb3708406c8f878dedd61610", + "block_index": 455460, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 200000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 825372, + "tx_hash": "e32154f8ade796df0b121604de140703d062d22d1e82e77e629e6096668c812f", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 300000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + } + ], + "/assets//credits": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ], + "/assets//debits": [ + { + "block_index": 280091, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "action": "send", + "event": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "tx_index": 729 + }, + { + "block_index": 280112, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "tx_index": 749 + }, + { + "block_index": 280112, + "address": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "tx_index": 752 + }, + { + "block_index": 280114, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "tx_index": 755 + }, + { + "block_index": 280156, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "tx_index": 766 + } + ], + "/assets//dividends": [ + { + "tx_index": 1914456, + "tx_hash": "30760e413947ebdc80ed7a5ada1bd4466800b87e9976bbe811ad4e2b46546359", + "block_index": 724381, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "ENDTHEFED", + "quantity_per_unit": 1, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1915246, + "tx_hash": "827794cbab3299f80a5b8b8cb8ec29ec3aee1373f7da2c05a156bed902bf4684", + "block_index": 724479, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPDANCING", + "quantity_per_unit": 100, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1920208, + "tx_hash": "7014f1e259531ba9632ca5000c35df5bd47f237318e48955900453ce9c07e917", + "block_index": 724931, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "CTRWOJACK", + "quantity_per_unit": 1111, + "fee_paid": 2700000, + "status": "valid" + }, + { + "tx_index": 1927909, + "tx_hash": "5556fd2b0802cf3bc0abd5001ecbac3adbc5b7c5c46a145a78daeef358c308de", + "block_index": 725654, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "WHITERUSSIAN", + "quantity_per_unit": 1, + "fee_paid": 3220000, + "status": "valid" + }, + { + "tx_index": 1983693, + "tx_hash": "cda646285cc63f758d19b5403070f23e2a6e4b34eb3b86b63a0f56f971345657", + "block_index": 730568, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "A4520591452211866149", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1983842, + "tx_hash": "e4b73dc974cc279b873b78e5dc4a347c08788b02143ae27aa0582f900289be10", + "block_index": 730588, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "NCSWIC", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1996395, + "tx_hash": "b342feb1421df107010ad3c8ee2043ded802bdf6cd619862459da3d0f87d6a99", + "block_index": 731994, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKTHEFED", + "quantity_per_unit": 1, + "fee_paid": 4380000, + "status": "valid" + }, + { + "tx_index": 2035947, + "tx_hash": "02d715fd9e8b7bbc782b1b2d92a1b9ffae9326bfc88ba76c453c515ad7c8c2bc", + "block_index": 738763, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "HOLDTHELINE", + "quantity_per_unit": 1, + "fee_paid": 4940000, + "status": "valid" + }, + { + "tx_index": 2174481, + "tx_hash": "b935a06fc34d8fa4f0c526984085b1b12c78e899415e595b625f1bee84ce3709", + "block_index": 762733, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "EOXIXIZERO", + "quantity_per_unit": 1, + "fee_paid": 6500000, + "status": "valid" + }, + { + "tx_index": 2198534, + "tx_hash": "a063e9a745b9f6bc3201f72abff196de20ec106bcc71d820673d516ddbb3aa90", + "block_index": 767569, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPCARDS", + "quantity_per_unit": 1, + "fee_paid": 6660000, + "status": "valid" + }, + { + "tx_index": 2704948, + "tx_hash": "437102ca4698f63a12e369f6168e3c7f5f8eef3e225395d515775673e33d39c1", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKYOUWAR", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + }, + { + "tx_index": 2704949, + "tx_hash": "7d3807cc58fa2d9751b2b0089bfa8fa86ef795821be6d8e9418ab3a819eba299", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "MEDICINEPEPE", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + } + ], + "/assets//issuances": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", + "quantity": 1, + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ], + "/assets//sends": [ + { + "tx_index": 729, + "tx_hash": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "block_index": 280091, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 749, + "tx_hash": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "block_index": 280112, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 752, + "tx_hash": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "block_index": 280112, + "source": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "destination": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 755, + "tx_hash": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "block_index": 280114, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 766, + "tx_hash": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "block_index": 280156, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ], + "/assets//dispensers": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ], + "/assets//dispensers/
": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ], + "/assets//holders": [ + { + "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "address_quantity": 63, + "escrow": null + }, + { + "address": "16yRstRXStVJJ1TN2S4DCWifyrCsetpma7", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qsvqsa9arwz30g2z0w09twzn8gz3380h36yxacs", + "address_quantity": 2, + "escrow": null + }, + { + "address": "17PnWBjHkekZKQPVagmTR5HiD51pN8WHC8", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1FRxFpP9XoRsvZFVqGtt4fjjgKe1h5tbAh", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1AdHg2q3M2rMFRgZyZ7RQyNHdwjSib7wSZ", + "address_quantity": 2, + "escrow": null + }, + { + "address": "1CTnziWXidHzY3qT8gwLa1ZxZK37A7HreR", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "address_quantity": 25, + "escrow": null + } + ], + "/orders/": [ + { + "tx_index": 2724132, + "tx_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "block_index": 840381, + "source": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "give_asset": "PEPECASH", + "give_quantity": 6966600000000, + "give_remaining": 900000000000, + "get_asset": "XCP", + "get_quantity": 11076894000, + "get_remaining": 1431000000, + "expiration": 5000, + "expire_index": 843055, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 4488, + "fee_provided_remaining": 4488, + "status": "open" + } + ], + "/orders//matches": [ + { + "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx0_index": 2724132, + "tx0_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "tx0_address": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "tx1_index": 2726591, + "tx1_hash": "5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx1_address": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "forward_asset": "PEPECASH", + "forward_quantity": 6066600000000, + "backward_asset": "XCP", + "backward_quantity": 9645894000, + "tx0_block_index": 838055, + "tx1_block_index": 840381, + "block_index": 840381, + "tx0_expiration": 5000, + "tx1_expiration": 8064, + "match_expire_index": 840401, + "fee_paid": 0, + "status": "completed" + } + ], + "/orders//btcpays": [ + { + "tx_index": 2719343, + "tx_hash": "6cfa7f31b43a46e5ad74a9db810bd6cac56235a8ebc73ec63d01b38ea7ea2414", + "block_index": 836188, + "source": "1NfJnJdAdmm2rJCFW54NsAKqqTTMexCNJ3", + "destination": "1BepkwAhEmEuEGF349XjmEUrRvoy9a7Biv", + "btc_amount": 4500000, + "order_match_id": "0a1387df82a8a7e9cec01c52c8fee01f6995c4e39dc5804e1d2bf40d9368f5c5_299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4", + "status": "valid" + } + ], + "/bets/": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + } + ], + "/bets//matches": [ + { + "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx0_index": 15106, + "tx0_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "tx0_address": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "tx1_index": 15108, + "tx1_hash": "cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx1_address": "1PTqJmRCMGs4qBEh2APAFSrBv95Uf1hfiD", + "tx0_bet_type": 3, + "tx1_bet_type": 2, + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "initial_value": -1, + "deadline": 1401828300, + "target_value": 1.0, + "leverage": 5040, + "forward_quantity": 50000000, + "backward_quantity": 50000000, + "tx0_block_index": 304062, + "tx1_block_index": 304063, + "block_index": 306379, + "tx0_expiration": 11, + "tx1_expiration": 1459, + "match_expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "expired" + } + ], + "/bets//resolutions": [ + { + "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", + "bet_match_type_id": 5, + "block_index": 401128, + "winner": "Equal", + "settled": null, + "bull_credit": null, + "bear_credit": null, + "escrow_less_fee": 2000000, + "fee": 0 + } + ], + "/burns": [ + { + "tx_index": 10, + "tx_hash": "41bbe1ec81da008a0e92758efb6084af3a6b6acf483983456ec797ee59c0e0f1", + "block_index": 278511, + "source": "12crRpZpn93PKTQ4WYxHMw4xi6ckh1CFR3", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 11, + "tx_hash": "c403a92281b568c7d428d942354d026594dc54ae35c21f53ecf5c918208c45de", + "block_index": 278511, + "source": "13UXh9dBEhA48gJiegJNodqe91PK88f4pW", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 12, + "tx_hash": "749ba1c2bd314f7b98e9cfb44575495b4ad2cf624901c65488fbc4f57a3dc0ac", + "block_index": 278511, + "source": "19Ht3rkW7JB9VuC7rsZEGZju96ujzchaZZ", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 13, + "tx_hash": "da330160b71138f9bda5e126df0d5d6248c0879d88e16255c74135274d8ebd27", + "block_index": 278511, + "source": "16Fu8Edsvxqixg6VnaHKPWE2TEsqQMwXfV", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 14, + "tx_hash": "66994176733650e77ae0cf34349f63e6538649f40f86d2719013d915bbb7701e", + "block_index": 278517, + "source": "14FFaRsfzYQxhZQv1YsMn65MvMLfJShgM8", + "burned": 99900000, + "earned": 147970063636, + "status": "valid" + } + ], + "/dispensers/": [ + { + "tx_index": 2536311, + "tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "asset": "FLOCK", + "give_quantity": 10000000000, + "escrow_quantity": 250000000000, + "satoshirate": 330000, + "status": 0, + "give_remaining": 140000000000, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "dispense_count": 2, + "asset_longname": null + } + ], + "/dispensers//dispenses": [ + { + "tx_index": 2610745, + "dispense_index": 0, + "tx_hash": "8c95cc6afc8fd466c784fd1c02749c585988999bbc66251b944c443dc31af757", + "block_index": 821450, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "1FKYM1CP9RfttJhNG8HTNQdE2uV3YvwbRB", + "asset": "FLOCK", + "dispense_quantity": 20000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + }, + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ], + "/events": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665091, + "event": "ENHANCED_SEND", + "bindings": { + "asset": "THOTHPEPE", + "block_index": 744232, + "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "memo": null, + "quantity": 1, + "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "status": "valid", + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665089, + "event": "DEBIT", + "bindings": { + "action": "send", + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "THOTHPEPE", + "block_index": 744232, + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665088, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ], + "/events/": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ], + "/events/counts": [ + { + "event": "ASSET_CREATION", + "event_count": 235858 + }, + { + "event": "ASSET_DESTRUCTION", + "event_count": 11141 + }, + { + "event": "ASSET_DIVIDEND", + "event_count": 4092 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 322676 + }, + { + "event": "ASSET_TRANSFER", + "event_count": 10630 + }, + { + "event": "BET_EXPIRATION", + "event_count": 588 + }, + { + "event": "BET_MATCH", + "event_count": 397 + }, + { + "event": "BET_MATCH_EXPIRATION", + "event_count": 9 + }, + { + "event": "BET_MATCH_RESOLUTON", + "event_count": 387 + }, + { + "event": "BET_MATCH_UPDATE", + "event_count": 397 + }, + { + "event": "BET_UPDATE", + "event_count": 1474 + }, + { + "event": "BLOCK_PARSED", + "event_count": 562278 + }, + { + "event": "BROADCAST", + "event_count": 106518 + }, + { + "event": "BTC_PAY", + "event_count": 2921 + }, + { + "event": "BURN", + "event_count": 2576 + }, + { + "event": "CANCEL_BET", + "event_count": 101 + }, + { + "event": "CANCEL_ORDER", + "event_count": 80168 + }, + { + "event": "CREDIT", + "event_count": 3657192 + }, + { + "event": "DEBIT", + "event_count": 2615306 + }, + { + "event": "DISPENSE", + "event_count": 190873 + }, + { + "event": "DISPENSER_UPDATE", + "event_count": 228954 + }, + { + "event": "ENHANCED_SEND", + "event_count": 538418 + }, + { + "event": "MPMA_SEND", + "event_count": 279142 + }, + { + "event": "NEW_BLOCK", + "event_count": 1906 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 4485 + }, + { + "event": "NEW_TRANSACTION_OUTPUT", + "event_count": 596 + }, + { + "event": "OPEN_BET", + "event_count": 1149 + }, + { + "event": "OPEN_DISPENSER", + "event_count": 88228 + }, + { + "event": "OPEN_ORDER", + "event_count": 530117 + }, + { + "event": "OPEN_RPS", + "event_count": 266 + }, + { + "event": "ORDER_EXPIRATION", + "event_count": 195962 + }, + { + "event": "ORDER_FILLED", + "event_count": 805 + }, + { + "event": "ORDER_MATCH", + "event_count": 209415 + }, + { + "event": "ORDER_MATCH_EXPIRATION", + "event_count": 20860 + }, + { + "event": "ORDER_MATCH_UPDATE", + "event_count": 23689 + }, + { + "event": "ORDER_UPDATE", + "event_count": 732640 + }, + { + "event": "REFILL_DISPENSER", + "event_count": 187 + }, + { + "event": "RESET_ISSUANCE", + "event_count": 454 + }, + { + "event": "RPS_EXPIRATION", + "event_count": 59 + }, + { + "event": "RPS_MATCH", + "event_count": 171 + }, + { + "event": "RPS_MATCH_EXPIRATION", + "event_count": 145 + }, + { + "event": "RPS_MATCH_UPDATE", + "event_count": 271 + }, + { + "event": "RPS_RESOLVE", + "event_count": 129 + }, + { + "event": "RPS_UPDATE", + "event_count": 540 + }, + { + "event": "SEND", + "event_count": 805983 + }, + { + "event": "SWEEP", + "event_count": 1018 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 2723789 + } + ], + "/events/": [ + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665085, + "event": "CREDIT", + "bindings": { + "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", + "asset": "XCP", + "block_index": 744232, + "calling_function": "dispense", + "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "quantity": 10000000000, + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665082, + "event": "CREDIT", + "bindings": { + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "FREEDOMKEK", + "block_index": 744232, + "calling_function": "send", + "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", + "quantity": 1, + "tx_index": 2056158 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665078, + "event": "CREDIT", + "bindings": { + "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", + "quantity": 1, + "tx_index": 2056157 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665074, + "event": "CREDIT", + "bindings": { + "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", + "quantity": 2, + "tx_index": 2056156 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ], + "/healthz": { + "data": "Healthy", + "success": true + }, + "/mempool/events": [], + "/mempool/events/": [] +} \ No newline at end of file diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index b434a5fd34..3cf1306928 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -6,7 +6,9 @@ CURR_DIR = os.path.dirname(os.path.realpath(__file__)) API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api/rest.md") +CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" +USE_API_CACHE = False def get_example_output(path, args): @@ -35,8 +37,12 @@ def get_example_output(path, args): The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. The following routes are available: """ -current_group = None +cache = {} +if USE_API_CACHE and os.path.exists(CACHE_FILE): + with open(CACHE_FILE, "r") as f: + cache = json.load(f) +current_group = None for path, route in server.routes.ROUTES.items(): route_group = path.split("/")[1] if route_group != current_group: @@ -62,7 +68,11 @@ def get_example_output(path, args): if not arg["required"]: md += f" + Default: `{arg.get('default', '')}`\n" if example_args != {} or route["args"] == []: - example_output = get_example_output(path, example_args) + if not USE_API_CACHE: + example_output = get_example_output(path, example_args) + cache[path] = example_output + else: + example_output = cache.get(path, {}) example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" md += " ```\n" @@ -70,6 +80,10 @@ def get_example_output(path, args): md += f" {line}\n" md += " ```\n" +if not USE_API_CACHE: + with open(CACHE_FILE, "w") as f: + json.dump(cache, f, indent=4) + with open(API_DOC_FILE, "w") as f: f.write(md) print(f"API documentation written to {API_DOC_FILE}") From 5762f8dac4738c654f8226960a12ec4893136c99 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 18:48:59 +0200 Subject: [PATCH 079/128] tweak gendoc cache --- counterparty-core/tools/apicache.json | 22 +++++++++++----------- counterparty-core/tools/genapidoc.py | 11 +++++------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index d6ce0c4c31..1258df2320 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -21,16 +21,6 @@ "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" } ], - "/blocks/": { - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", - "difficulty": 86388558925171.02, - "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", - "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", - "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" - }, "/blocks//transactions": [ { "tx_index": 2726605, @@ -1828,5 +1818,15 @@ "success": true }, "/mempool/events": [], - "/mempool/events/": [] + "/mempool/events/": [], + "/blocks/": { + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", + "difficulty": 86388558925171.02, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + } } \ No newline at end of file diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 3cf1306928..72044f9060 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -8,7 +8,7 @@ API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api/rest.md") CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" -USE_API_CACHE = False +USE_API_CACHE = True def get_example_output(path, args): @@ -68,11 +68,11 @@ def get_example_output(path, args): if not arg["required"]: md += f" + Default: `{arg.get('default', '')}`\n" if example_args != {} or route["args"] == []: - if not USE_API_CACHE: + if not USE_API_CACHE or path not in cache: example_output = get_example_output(path, example_args) cache[path] = example_output else: - example_output = cache.get(path, {}) + example_output = cache[path] example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" md += " ```\n" @@ -80,9 +80,8 @@ def get_example_output(path, args): md += f" {line}\n" md += " ```\n" -if not USE_API_CACHE: - with open(CACHE_FILE, "w") as f: - json.dump(cache, f, indent=4) +with open(CACHE_FILE, "w") as f: + json.dump(cache, f, indent=4) with open(API_DOC_FILE, "w") as f: f.write(md) From 3b3973a0ec5693dcd0fb14ab0f92a3bf6ed6f295 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 22:48:20 +0200 Subject: [PATCH 080/128] support float args --- counterparty-core/counterpartycore/lib/api/api_server.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 997e31a277..8038accc5b 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -115,6 +115,11 @@ def prepare_args(route, **kwargs): function_args[arg_name] = int(str_arg) except ValueError as e: raise ValueError(f"Invalid integer: {arg_name}") from e + elif arg["type"] == "float": + try: + function_args[arg_name] = float(str_arg) + except ValueError as e: + raise ValueError(f"Invalid float: {arg_name}") from e else: function_args[arg_name] = str_arg return function_args From 0f7ffd37a2036c5d65ccb8317e08d2b310743f7e Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 22:48:44 +0200 Subject: [PATCH 081/128] More output example for compose --- .../counterpartycore/lib/transaction.py | 14 +++++++------- counterparty-core/tools/apicache.json | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 0d721a2b0b..655ad51c2d 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1254,11 +1254,11 @@ def compose_broadcast( ): """ Composes a transaction to broadcast textual and numerical information to the network. - :param address: The address that will be sending (must have the necessary quantity of the specified asset) - :param timestamp: The timestamp of the broadcast, in Unix time - :param value: Numerical value of the broadcast - :param fee_fraction: How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) - :param text: The textual part of the broadcast + :param address: The address that will be sending (must have the necessary quantity of the specified asset) (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param timestamp: The timestamp of the broadcast, in Unix time (e.g. 4003903983) + :param value: Numerical value of the broadcast (e.g. 100) + :param fee_fraction: How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) (e.g. 0.05) + :param text: The textual part of the broadcast (e.g. "Hello, world!") """ params = { "source": address, @@ -1283,8 +1283,8 @@ def compose_broadcast( def compose_btcpay(db, address: str, order_match_id: str, **construct_args): """ Composes a transaction to pay for a BTC order match. - :param address: The address that will be sending the payment - :param order_match_id: The ID of the order match to pay for + :param address: The address that will be sending the payment (e.g. bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l) + :param order_match_id: The ID of the order match to pay for (e.g. e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2) """ params = {"source": address, "order_match_id": order_match_id} rawtransaction = compose_transaction( diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 1258df2320..cffe8c8ac0 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1828,5 +1828,24 @@ "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + }, + "/addresses/
/compose/broadcast": { + "rawtransaction": "01000000019a753a6b8be54cdee2acd408f6199e29092c8c32e13912865a68da8a0d9ae065010000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88acffffffff0200000000000000002b6a2988dab644080b5ae67a54ba74394f5f94b41e385fc911aa5c810c5f98e6f6b17518ca736e94353de8e82a282b00000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac00000000", + "params": { + "source": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "timestamp": 4003903983, + "value": 100.0, + "fee_fraction": 0.05, + "text": "\"Hello, world!\"" + }, + "name": "broadcast" + }, + "/addresses/
/compose/btcpay": { + "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db70bc758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", + "params": { + "source": "bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l", + "order_match_id": "e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2" + }, + "name": "btcpay" } } \ No newline at end of file From 961c332f52dc517d028f744075d840e4eaa7ec50 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 22:55:29 +0200 Subject: [PATCH 082/128] catch correctly compose error --- .../counterpartycore/lib/api/api_server.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 8038accc5b..6040acf7c2 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -11,6 +11,7 @@ blocks, config, database, + exceptions, ledger, ) from counterpartycore.lib.api.routes import ROUTES @@ -138,8 +139,13 @@ def handle_route(**kwargs): try: function_args = prepare_args(route, **kwargs) except ValueError as e: - return inject_headers({"error": str(e)}, return_code=400) - result = route["function"](db, **function_args) + return inject_headers({"success": False, "error": str(e)}, return_code=400) + try: + result = route["function"](db, **function_args) + except (exceptions.ComposeError, exceptions.UnpackError) as e: + return inject_headers({"success": False, "error": str(e)}, return_code=503) + except Exception: + return inject_headers({"success": False, "error": "Unknwon error"}, return_code=503) result = remove_rowids(result) return inject_headers(result) From 66cdc2e96d88beedaf4ea78505469ebc8b0f6bc9 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Tue, 23 Apr 2024 23:02:38 +0200 Subject: [PATCH 083/128] log unexpected API error --- counterparty-core/counterpartycore/lib/api/api_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 6040acf7c2..f51ebe723c 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -144,7 +144,8 @@ def handle_route(**kwargs): result = route["function"](db, **function_args) except (exceptions.ComposeError, exceptions.UnpackError) as e: return inject_headers({"success": False, "error": str(e)}, return_code=503) - except Exception: + except Exception as e: + logger.error("Error in API: %s", e) return inject_headers({"success": False, "error": "Unknwon error"}, return_code=503) result = remove_rowids(result) return inject_headers(result) From e6070ad874a7f6979247a37748e0742ac90449dc Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 10:38:27 +0200 Subject: [PATCH 084/128] Standardize API result --- .../counterpartycore/lib/api/api_server.py | 12 +- .../counterpartycore/test/api_v2_test.py | 16 +- .../test/fixtures/api_v2_fixtures.json | 2850 +++++++++-------- 3 files changed, 1526 insertions(+), 1352 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index f51ebe723c..05dc88d58c 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -78,18 +78,24 @@ def api_root(): def inject_headers(result, return_code=None): - server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT + server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT - 1 + json_result = {"success": True, "result": result} http_code = 200 if return_code: http_code = return_code elif not server_ready: http_code = config.API_NOT_READY_HTTP_CODE + json_result["error"] = "Counterparty not ready" + if http_code != 200: + json_result["success"] = False + if isinstance(result, flask.Response): response = result else: - response = flask.make_response(to_json(result), http_code) + response = flask.make_response(to_json(json_result), http_code) + response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX - response.headers["X-COUNTERPARTY-READY"] = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT + response.headers["X-COUNTERPARTY-READY"] = server_ready response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT response.headers["Content-Type"] = "application/json" return response diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index edb79c6a85..7bd503b9f2 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -77,7 +77,7 @@ def test_api_v2_unpack(request, server_db): for data in datas: result = requests.get(url, params={"datahex": data["datahex"]}) # noqa: S113 assert result.status_code == 200 - assert result.json() == data["result"] + assert result.json()["result"] == data["result"] @pytest.mark.usefixtures("api_server_v2") @@ -85,7 +85,7 @@ def test_new_get_balances_by_address(): alice = ADDR[0] url = f"{API_ROOT}/addresses/{alice}/balances" result = requests.get(url) # noqa: S113 - assert result.json() == [ + assert result.json()["result"] == [ { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "A95428956661682277", @@ -134,7 +134,7 @@ def test_new_get_balances_by_asset(): asset = "XCP" url = f"{API_ROOT}/assets/{asset}/balances" result = requests.get(url) # noqa: S113 - assert result.json() == [ + assert result.json()["result"] == [ { "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", "asset": "XCP", @@ -193,7 +193,7 @@ def test_new_get_balances_by_asset(): def test_new_get_balances_vs_old(): asset = "XCP" url = f"{API_ROOT}/assets/{asset}/balances" - new_balances = requests.get(url).json() # noqa: S113 + new_balances = requests.get(url).json()["result"] # noqa: S113 old_balance = util.api( "get_balances", { @@ -218,7 +218,7 @@ def test_new_get_asset_info(): url = f"{API_ROOT}/assets/{asset}" result = requests.get(url) # noqa: S113 - assert result.json() == { + assert result.json()["result"] == { "asset": "NODIVISIBLE", "asset_longname": None, "description": "No divisible asset", @@ -235,7 +235,7 @@ def test_new_get_asset_info(): def test_new_get_asset_orders(): asset = "XCP" url = f"{API_ROOT}/assets/{asset}/orders" - result = requests.get(url).json() # noqa: S113 + result = requests.get(url).json()["result"] # noqa: S113 assert len(result) == 6 assert result[0] == { "tx_index": 11, @@ -262,7 +262,7 @@ def test_new_get_asset_orders(): def test_new_get_order_info(): tx_hash = "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a" url = f"{API_ROOT}/orders/{tx_hash}" - result = requests.get(url).json() # noqa: S113 + result = requests.get(url).json()["result"] # noqa: S113 assert result[0] == { "tx_index": 11, "tx_hash": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", @@ -288,7 +288,7 @@ def test_new_get_order_info(): def test_new_get_order_matches(): tx_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" url = f"{API_ROOT}/orders/{tx_hash}/matches" - result = requests.get(url).json() # noqa: S113 + result = requests.get(url).json()["result"] # noqa: S113 assert result[0] == { "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", "tx0_index": 492, diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index 463f90c33d..bec05b9699 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -1,96 +1,112 @@ { - "http://api:api@localhost:10009/blocks": [ - { - "block_index": 310500, - "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", - "block_time": 310500000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", - "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1", - "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f" - }, - { - "block_index": 310499, - "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", - "block_time": 310499000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", - "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5", - "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c" - }, - { - "block_index": 310498, - "block_hash": "b7058b6d1ddc325a10bf33144937e06ce6025215b416518ae120da9440ae279e", - "block_time": 310498000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", - "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7", - "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98" - }, - { - "block_index": 310497, - "block_hash": "f1118591fe79b8bf52ccf0c5de9826bfd266b1fdc24b44676cf22bbcc76d464e", - "block_time": 310497000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "28c6e92b2299b9cbbb5953f8b7ff3de0fe962d15642ba27e43faa64e1935e819", - "txlist_hash": "ff8136601b9e0138a999d1f0467af6e8535a2bcdd2b622af7be0178a083b9519", - "messages_hash": "ac8d8759fbddc8f92ad8b5d8b4637a63b8d21a04ba1a4126baf2b42a87edfce3" - }, - { - "block_index": 310496, - "block_hash": "65884816927e8c566655e85c07bc2bc2c7ee26e625742f219939d43238fb31f8", - "block_time": 310496000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "7ac6121c624b634f44695172761830926afe76bb18c4cc9195773f3a26966941", - "txlist_hash": "9eda85cce745579122ba9c6e24b63cd83f2e5161031a34e6ee9bf08b80823cb4", - "messages_hash": "1b1ed76f99d39b36f4d0737299ce15b21fed9e077d0476658c023b09819853a7" - }, - { - "block_index": 310495, - "block_hash": "4769aa7030f28a05a137a85ef4ee0c1765c37013773212b93ec90f1227168b67", - "block_time": 310495000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "5a7e5a36882466373d576bb5f4ccd1bc72ecaf548b9589baa803a7275a7a24cd", - "txlist_hash": "09e9db121649cacd979fd18bbaa35e519361e727e7e072e2f2f86291160cdb29", - "messages_hash": "56518bc9abc11bf108188834058bb9c51c9b8dc3b6276116c5b786b07c797fab" - }, - { - "block_index": 310494, - "block_hash": "7dda1d3e12785313d5651ee5314d0aecf17588196f9150b10c55695dbaebee5d", - "block_time": 310494000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "72d71bd72263699ea9f2b097ad141be5bc394f49d8b0b0a6b2ff6a87b0ee3919", - "txlist_hash": "9350c3ba33d0546d1194c5fa767ced28834b26246aedc56d89b1d48ec4f26014", - "messages_hash": "0d3c87692d49dc033eed975eb8b8ee17ff2f0f877c8ed5a5866f73ce63bf8715" - }, - { - "block_index": 310493, - "block_hash": "c19e2915b750279b2be4b52e57e5ce29f63dffb4e14d9aad30c9e820affc0cbf", - "block_time": 310493000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "29119cd30a4733916fbfd0551506eaa16f7bb1bdfbdf8d17ac4e5bb20d1cb09c", - "txlist_hash": "7ec4cfa94544900c8e8732ad51be7cee6452aa1884ea940cd5c98862fb4aaba6", - "messages_hash": "53a339feb73df3a98bc4acc84af77e111d4780533c8f5a26cf250594d9613cf2" - }, - { - "block_index": 310492, - "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", - "block_time": 310492000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "98af18583618fdeed545347c013763d068e8294405d265911cc5e1bc420bc740", - "txlist_hash": "daf4d2c1a1ad5206abcf7744bdd06fae99c442fb2607a843dcabb5727d02916e", - "messages_hash": "0d34fbc26126add5a57b7e8f6f71bad150d7232abf046f3fa9b1fc72b10d61b2" - }, - { + "http://api:api@localhost:10009/blocks": { + "success": true, + "result": [ + { + "block_index": 310500, + "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", + "block_time": 310500000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", + "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1", + "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f" + }, + { + "block_index": 310499, + "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", + "block_time": 310499000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", + "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5", + "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c" + }, + { + "block_index": 310498, + "block_hash": "b7058b6d1ddc325a10bf33144937e06ce6025215b416518ae120da9440ae279e", + "block_time": 310498000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", + "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7", + "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98" + }, + { + "block_index": 310497, + "block_hash": "f1118591fe79b8bf52ccf0c5de9826bfd266b1fdc24b44676cf22bbcc76d464e", + "block_time": 310497000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "28c6e92b2299b9cbbb5953f8b7ff3de0fe962d15642ba27e43faa64e1935e819", + "txlist_hash": "ff8136601b9e0138a999d1f0467af6e8535a2bcdd2b622af7be0178a083b9519", + "messages_hash": "ac8d8759fbddc8f92ad8b5d8b4637a63b8d21a04ba1a4126baf2b42a87edfce3" + }, + { + "block_index": 310496, + "block_hash": "65884816927e8c566655e85c07bc2bc2c7ee26e625742f219939d43238fb31f8", + "block_time": 310496000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "7ac6121c624b634f44695172761830926afe76bb18c4cc9195773f3a26966941", + "txlist_hash": "9eda85cce745579122ba9c6e24b63cd83f2e5161031a34e6ee9bf08b80823cb4", + "messages_hash": "1b1ed76f99d39b36f4d0737299ce15b21fed9e077d0476658c023b09819853a7" + }, + { + "block_index": 310495, + "block_hash": "4769aa7030f28a05a137a85ef4ee0c1765c37013773212b93ec90f1227168b67", + "block_time": 310495000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "5a7e5a36882466373d576bb5f4ccd1bc72ecaf548b9589baa803a7275a7a24cd", + "txlist_hash": "09e9db121649cacd979fd18bbaa35e519361e727e7e072e2f2f86291160cdb29", + "messages_hash": "56518bc9abc11bf108188834058bb9c51c9b8dc3b6276116c5b786b07c797fab" + }, + { + "block_index": 310494, + "block_hash": "7dda1d3e12785313d5651ee5314d0aecf17588196f9150b10c55695dbaebee5d", + "block_time": 310494000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "72d71bd72263699ea9f2b097ad141be5bc394f49d8b0b0a6b2ff6a87b0ee3919", + "txlist_hash": "9350c3ba33d0546d1194c5fa767ced28834b26246aedc56d89b1d48ec4f26014", + "messages_hash": "0d3c87692d49dc033eed975eb8b8ee17ff2f0f877c8ed5a5866f73ce63bf8715" + }, + { + "block_index": 310493, + "block_hash": "c19e2915b750279b2be4b52e57e5ce29f63dffb4e14d9aad30c9e820affc0cbf", + "block_time": 310493000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "29119cd30a4733916fbfd0551506eaa16f7bb1bdfbdf8d17ac4e5bb20d1cb09c", + "txlist_hash": "7ec4cfa94544900c8e8732ad51be7cee6452aa1884ea940cd5c98862fb4aaba6", + "messages_hash": "53a339feb73df3a98bc4acc84af77e111d4780533c8f5a26cf250594d9613cf2" + }, + { + "block_index": 310492, + "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", + "block_time": 310492000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "98af18583618fdeed545347c013763d068e8294405d265911cc5e1bc420bc740", + "txlist_hash": "daf4d2c1a1ad5206abcf7744bdd06fae99c442fb2607a843dcabb5727d02916e", + "messages_hash": "0d34fbc26126add5a57b7e8f6f71bad150d7232abf046f3fa9b1fc72b10d61b2" + }, + { + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "previous_block_hash": null, + "difficulty": null, + "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", + "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8", + "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994" + } + ] + }, + "http://api:api@localhost:10009/blocks/310491": { + "success": true, + "result": { "block_index": 310491, "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", "block_time": 310491000, @@ -100,19 +116,209 @@ "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8", "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994" } - ], - "http://api:api@localhost:10009/blocks/310491": { - "block_index": 310491, - "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", - "block_time": 310491000, - "previous_block_hash": null, - "difficulty": null, - "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", - "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8", - "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994" }, - "http://api:api@localhost:10009/blocks/310491/transactions": [ - { + "http://api:api@localhost:10009/blocks/310491/transactions": { + "success": true, + "result": [ + { + "tx_index": 492, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "block_index": 310491, + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_time": 310491000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "", + "btc_amount": 0, + "fee": 6800, + "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "supported": 1 + } + ] + }, + "http://api:api@localhost:10009/blocks/310491/events": { + "success": true, + "result": [ + { + "event_index": 1182, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310491, + "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", + "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994", + "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8" + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1181, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1180, + "event": "OPEN_ORDER", + "bindings": { + "block_index": 310491, + "expiration": 2000, + "expire_index": 312491, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "fee_required": 900000, + "fee_required_remaining": 900000, + "get_asset": "BTC", + "get_quantity": 800000, + "get_remaining": 800000, + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 100000000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "status": "open", + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1179, + "event": "DEBIT", + "bindings": { + "action": "open order", + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "block_index": 310491, + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "quantity": 100000000, + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1178, + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_index": 310491, + "block_time": 310491000, + "btc_amount": 0, + "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", + "destination": "", + "fee": 6800, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "supported": true, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + "block_index": 310491, + "timestamp": 0 + }, + { + "event_index": 1177, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", + "block_index": 310491, + "block_time": 310491000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, + "block_index": 310491, + "timestamp": 0 + } + ] + }, + "http://api:api@localhost:10009/blocks/310491/events/counts": { + "success": true, + "result": [ + { + "event": "BLOCK_PARSED", + "event_count": 1 + }, + { + "event": "DEBIT", + "event_count": 1 + }, + { + "event": "NEW_BLOCK", + "event_count": 1 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 1 + }, + { + "event": "OPEN_ORDER", + "event_count": 1 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 1 + } + ] + }, + "http://api:api@localhost:10009/blocks/310491/events/CREDIT": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/credits": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/debits": { + "success": true, + "result": [ + { + "block_index": 310491, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + } + ] + }, + "http://api:api@localhost:10009/blocks/310491/expirations": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/cancels": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/destructions": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/issuances": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/sends": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/dispenses": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/blocks/310491/sweeps": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/transactions/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { + "success": true, + "result": { "tx_index": 492, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "block_index": 310491, @@ -125,1274 +331,1236 @@ "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", "supported": 1 } - ], - "http://api:api@localhost:10009/blocks/310491/events": [ - { - "event_index": 1182, - "event": "BLOCK_PARSED", - "bindings": { - "block_index": 310491, - "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", - "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994", - "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8" + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances": { + "success": true, + "result": [ + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "quantity": 100000000 }, - "block_index": 310491, - "timestamp": 0 - }, - { - "event_index": 1181, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx_index": 492 + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "CALLABLE", + "quantity": 1000 }, - "block_index": 310491, - "timestamp": 0 - }, - { - "event_index": 1180, - "event": "OPEN_ORDER", - "bindings": { - "block_index": 310491, - "expiration": 2000, - "expire_index": 312491, - "fee_provided": 6800, - "fee_provided_remaining": 6800, - "fee_required": 900000, - "fee_required_remaining": 900000, - "get_asset": "BTC", - "get_quantity": 800000, - "get_remaining": 800000, - "give_asset": "XCP", - "give_quantity": 100000000, - "give_remaining": 100000000, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "status": "open", - "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx_index": 492 + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 98800000000 }, - "block_index": 310491, - "timestamp": 0 - }, - { - "event_index": 1179, - "event": "DEBIT", - "bindings": { - "action": "open order", + { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "block_index": 310491, - "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "quantity": 100000000, - "tx_index": 492 + "asset": "LOCKED", + "quantity": 1000 }, - "block_index": 310491, - "timestamp": 0 - }, - { - "event_index": 1178, - "event": "NEW_TRANSACTION", - "bindings": { - "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", - "block_index": 310491, - "block_time": 310491000, - "btc_amount": 0, - "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", - "destination": "", - "fee": 6800, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "supported": true, - "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx_index": 492 + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "MAXI", + "quantity": 9223372036854775807 }, - "block_index": 310491, - "timestamp": 0 - }, - { - "event_index": 1177, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", - "block_index": 310491, - "block_time": 310491000, - "difficulty": null, - "ledger_hash": null, - "previous_block_hash": null, - "txlist_hash": null + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 }, - "block_index": 310491, - "timestamp": 0 - } - ], - "http://api:api@localhost:10009/blocks/310491/events/counts": [ - { - "event": "BLOCK_PARSED", - "event_count": 1 - }, - { - "event": "DEBIT", - "event_count": 1 - }, - { - "event": "NEW_BLOCK", - "event_count": 1 - }, - { - "event": "NEW_TRANSACTION", - "event_count": 1 - }, - { - "event": "OPEN_ORDER", - "event_count": 1 - }, - { - "event": "TRANSACTION_PARSED", - "event_count": 1 - } - ], - "http://api:api@localhost:10009/blocks/310491/events/CREDIT": [], - "http://api:api@localhost:10009/blocks/310491/credits": [], - "http://api:api@localhost:10009/blocks/310491/debits": [ - { - "block_index": 310491, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "open order", - "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx_index": 492 - } - ], - "http://api:api@localhost:10009/blocks/310491/expirations": [], - "http://api:api@localhost:10009/blocks/310491/cancels": [], - "http://api:api@localhost:10009/blocks/310491/destructions": [], - "http://api:api@localhost:10009/blocks/310491/issuances": [], - "http://api:api@localhost:10009/blocks/310491/sends": [], - "http://api:api@localhost:10009/blocks/310491/dispenses": [], - "http://api:api@localhost:10009/blocks/310491/sweeps": [], - "http://api:api@localhost:10009/transactions/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "tx_index": 492, - "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "block_index": 310491, - "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", - "block_time": 310491000, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "", - "btc_amount": 0, - "fee": 6800, - "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", - "supported": 1 + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "quantity": 100000000 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 91875000000 + } + ] }, - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances": [ - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "A95428956661682277", - "quantity": 100000000 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "CALLABLE", - "quantity": 1000 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 98800000000 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "LOCKED", - "quantity": 1000 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "MAXI", - "quantity": 9223372036854775807 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 985 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "PARENT", - "quantity": 100000000 - }, - { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 91875000000 - } - ], "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances/NODIVISIBLE": { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 985 - }, - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/credits": [ - { - "block_index": 310000, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 93000000000, - "calling_function": "burn", - "event": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", - "tx_index": 1 - }, - { - "block_index": 310001, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 100000000000, - "calling_function": "issuance", - "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", - "tx_index": 2 - }, - { - "block_index": 310002, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 1000, - "calling_function": "issuance", - "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", - "tx_index": 3 - }, - { - "block_index": 310003, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "CALLABLE", - "quantity": 1000, - "calling_function": "issuance", - "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", - "tx_index": 4 - }, - { - "block_index": 310004, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "LOCKED", - "quantity": 1000, - "calling_function": "issuance", - "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", - "tx_index": 5 - }, - { - "block_index": 310016, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "MAXI", - "quantity": 9223372036854775807, - "calling_function": "issuance", - "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", - "tx_index": 17 - }, - { - "block_index": 310020, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 0, - "calling_function": "filled", - "event": "5c6562ddad0bc8a1faaded18813a65522cd273709acd190cf9d3271817eefc93", - "tx_index": 21 - }, - { - "block_index": 310102, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 9, - "calling_function": "bet settled", - "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", - "tx_index": 103 - }, - { - "block_index": 310102, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 0, - "calling_function": "feed fee", - "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", - "tx_index": 103 - }, - { - "block_index": 310482, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "calling_function": "send", - "event": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", - "tx_index": 483 - }, - { - "block_index": 310497, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "PARENT", - "quantity": 100000000, - "calling_function": "issuance", - "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", - "tx_index": 498 - }, - { - "block_index": 310498, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "A95428956661682277", - "quantity": 100000000, - "calling_function": "issuance", - "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", - "tx_index": 499 - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/debits": [ - { - "block_index": 310001, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", - "tx_index": 2 - }, - { - "block_index": 310002, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", - "tx_index": 3 - }, - { - "block_index": 310003, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", - "tx_index": 4 - }, - { - "block_index": 310004, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", - "tx_index": 5 - }, - { - "block_index": 310005, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 0, - "action": "issuance fee", - "event": "344dcc8909ca3a137630726d0071dfd2df4f7c855bac150c7d3a8367835c90bc", - "tx_index": 6 - }, - { - "block_index": 310006, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "open order", - "event": "4f0433ba841038e2e16328445930dd7bca35309b14b0da4451c8f94c631368b8", - "tx_index": 7 - }, - { - "block_index": 310007, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 100000000, - "action": "send", - "event": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", - "tx_index": 8 - }, - { - "block_index": 310008, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "send", - "event": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", - "tx_index": 9 - }, - { - "block_index": 310009, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "open order", - "event": "21460d5c07284f9be9baf824927d0d4e4eb790e297f3162305841607b672349b", - "tx_index": 10 - }, - { - "block_index": 310010, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "open order", - "event": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", - "tx_index": 11 - }, - { - "block_index": 310012, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 300000000, - "action": "send", - "event": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", - "tx_index": 13 - }, - { - "block_index": 310013, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 1000000000, - "action": "send", - "event": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", - "tx_index": 14 - }, - { - "block_index": 310014, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 5, - "action": "send", - "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "tx_index": 15 - }, - { - "block_index": 310015, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 10, - "action": "send", - "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "tx_index": 16 - }, - { - "block_index": 310016, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", - "tx_index": 17 - }, - { - "block_index": 310019, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 9, - "action": "bet", - "event": "2a2169991597036b6dad687ea1feffd55465a204466f40c35cbba811cb3109b1", - "tx_index": 20 - }, - { - "block_index": 310110, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "DIVISIBLE", - "quantity": 100000000, - "action": "send", - "event": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", - "tx_index": 111 - }, - { - "block_index": 310481, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "send", - "event": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", - "tx_index": 482 - }, - { - "block_index": 310491, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "action": "open order", - "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx_index": 492 - }, - { - "block_index": 310497, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", - "tx_index": 498 - }, - { - "block_index": 310498, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 25000000, - "action": "issuance fee", - "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", - "tx_index": 499 - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/bets": [ - { - "tx_index": 102, - "tx_hash": "db4ea092bea6036e3d1e5f6ec863db9b900252b4f4d6d9faa6165323f433c51e", - "block_index": 310101, - "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "feed_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "bet_type": 3, - "deadline": 1388000200, - "wager_quantity": 10, - "wager_remaining": 10, - "counterwager_quantity": 10, - "counterwager_remaining": 10, - "target_value": 0.0, - "leverage": 5040, - "expiration": 1000, - "expire_index": 311101, - "fee_fraction_int": 5000000, - "status": "open" - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/broadcasts": [ - { - "tx_index": 103, - "tx_hash": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", - "block_index": 310102, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "timestamp": 1388000002, - "value": 1.0, - "fee_fraction_int": 5000000, - "text": "Unit Test", - "locked": 0, - "status": "valid" - }, - { - "tx_index": 18, - "tx_hash": "d14388b74b63d93e4477b1fe8426028bb8ab20656703e3ce8deeb23c2fe0b8af", - "block_index": 310017, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "timestamp": 1388000000, - "value": 1.0, - "fee_fraction_int": 5000000, - "text": "Unit Test", - "locked": 0, - "status": "valid" - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/burns": [ - { - "tx_index": 1, - "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", - "block_index": 310000, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "burned": 62000000, - "earned": 93000000000, - "status": "valid" - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends": [ - { - "tx_index": 8, - "tx_hash": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", - "block_index": 310007, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "DIVISIBLE", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 9, - "tx_hash": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", - "block_index": 310008, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "XCP", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 13, - "tx_hash": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", - "block_index": 310012, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "XCP", - "quantity": 300000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 14, - "tx_hash": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", - "block_index": 310013, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "DIVISIBLE", - "quantity": 1000000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 15, - "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "block_index": 310014, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "NODIVISIBLE", - "quantity": 5, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 16, - "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "block_index": 310015, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "NODIVISIBLE", - "quantity": 10, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 111, - "tx_hash": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", - "block_index": 310110, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", - "asset": "DIVISIBLE", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 482, - "tx_hash": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", - "block_index": 310481, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "XCP", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": "68656c6c6f" - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives": [ - { - "tx_index": 483, - "tx_hash": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", - "block_index": 310482, - "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "destination": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "XCP", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": "fade0001" - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends/NODIVISIBLE": [ - { - "tx_index": 15, - "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "block_index": 310014, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "NODIVISIBLE", - "quantity": 5, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 16, - "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "block_index": 310015, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "NODIVISIBLE", - "quantity": 10, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives/NODIVISIBLE": [], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers": [], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers/NODIVISIBLE": [], - "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sweeps": [], - "http://api:api@localhost:10009/assets": [ - { - "asset": "A95428956661682277", - "asset_longname": "PARENT.already.issued" - }, - { - "asset": "CALLABLE", - "asset_longname": null - }, - { - "asset": "DIVIDEND", - "asset_longname": null - }, - { - "asset": "DIVISIBLE", - "asset_longname": null - }, - { - "asset": "LOCKED", - "asset_longname": null - }, - { - "asset": "LOCKEDPREV", - "asset_longname": null - }, - { - "asset": "MAXI", - "asset_longname": null - }, - { - "asset": "NODIVISIBLE", - "asset_longname": null - }, - { - "asset": "PARENT", - "asset_longname": null - }, - { - "asset": "PAYTOSCRIPT", - "asset_longname": null - } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE": { - "asset": "NODIVISIBLE", - "asset_longname": null, - "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "divisible": false, - "locked": false, - "supply": 1000, - "description": "No divisible asset", - "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "holder_count": 3 - }, - "http://api:api@localhost:10009/assets/NODIVISIBLE/balances": [ - { - "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "NODIVISIBLE", - "quantity": 10 - }, - { + "success": true, + "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "NODIVISIBLE", "quantity": 985 - }, - { - "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "NODIVISIBLE", - "quantity": 5 } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE/balances/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 985 }, - "http://api:api@localhost:10009/assets/NODIVISIBLE/orders": [], - "http://api:api@localhost:10009/assets/NODIVISIBLE/credits": [ - { - "block_index": 310002, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 1000, - "calling_function": "issuance", - "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", - "tx_index": 3 - }, - { - "block_index": 310014, - "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "NODIVISIBLE", - "quantity": 5, - "calling_function": "send", - "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "tx_index": 15 - }, - { - "block_index": 310015, - "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "NODIVISIBLE", - "quantity": 10, - "calling_function": "send", - "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "tx_index": 16 - } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE/debits": [ - { - "block_index": 310014, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 5, - "action": "send", - "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "tx_index": 15 - }, - { - "block_index": 310015, - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "NODIVISIBLE", - "quantity": 10, - "action": "send", - "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "tx_index": 16 - } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE/dividends": [], - "http://api:api@localhost:10009/assets/NODIVISIBLE/issuances": [ - { - "tx_index": 3, - "tx_hash": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", - "msg_index": 0, - "block_index": 310002, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/credits": { + "success": true, + "result": [ + { + "block_index": 310000, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 93000000000, + "calling_function": "burn", + "event": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "tx_index": 1 + }, + { + "block_index": 310001, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000000, + "calling_function": "issuance", + "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", + "tx_index": 2 + }, + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310003, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "CALLABLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", + "tx_index": 4 + }, + { + "block_index": 310004, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "LOCKED", + "quantity": 1000, + "calling_function": "issuance", + "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", + "tx_index": 5 + }, + { + "block_index": 310016, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "MAXI", + "quantity": 9223372036854775807, + "calling_function": "issuance", + "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", + "tx_index": 17 + }, + { + "block_index": 310020, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "calling_function": "filled", + "event": "5c6562ddad0bc8a1faaded18813a65522cd273709acd190cf9d3271817eefc93", + "tx_index": 21 + }, + { + "block_index": 310102, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 9, + "calling_function": "bet settled", + "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "tx_index": 103 + }, + { + "block_index": 310102, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "calling_function": "feed fee", + "event": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "tx_index": 103 + }, + { + "block_index": 310482, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "calling_function": "send", + "event": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", + "tx_index": 483 + }, + { + "block_index": 310497, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "quantity": 100000000, + "calling_function": "issuance", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "tx_index": 498 + }, + { + "block_index": 310498, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "quantity": 100000000, + "calling_function": "issuance", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "tx_index": 499 + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/debits": { + "success": true, + "result": [ + { + "block_index": 310001, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "1fc2e5a57f584b2f2edd05676e75c33d03eed1d3098cc0550ea33474e3ec9db1", + "tx_index": 2 + }, + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310003, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "c26f3a0c4e57e41919ff27aae95a9a9d4d65d34c6da6f1893884a17c8d407140", + "tx_index": 4 + }, + { + "block_index": 310004, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "90b5734be98b0f2a0bd4b6a269c8db3368e2e387bb890ade239951d05423b4da", + "tx_index": 5 + }, + { + "block_index": 310005, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 0, + "action": "issuance fee", + "event": "344dcc8909ca3a137630726d0071dfd2df4f7c855bac150c7d3a8367835c90bc", + "tx_index": 6 + }, + { + "block_index": 310006, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "4f0433ba841038e2e16328445930dd7bca35309b14b0da4451c8f94c631368b8", + "tx_index": 7 + }, + { + "block_index": 310007, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000, + "action": "send", + "event": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", + "tx_index": 8 + }, + { + "block_index": 310008, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", + "tx_index": 9 + }, + { + "block_index": 310009, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "21460d5c07284f9be9baf824927d0d4e4eb790e297f3162305841607b672349b", + "tx_index": 10 + }, + { + "block_index": 310010, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "1899b2e6ec36ba4bc9d035e6640b0a62b08c3a147c77c89183a77d9ed9081b3a", + "tx_index": 11 + }, + { + "block_index": 310012, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 300000000, + "action": "send", + "event": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", + "tx_index": 13 + }, + { + "block_index": 310013, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 1000000000, + "action": "send", + "event": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", + "tx_index": 14 + }, + { + "block_index": 310014, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 5, + "action": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 10, + "action": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + }, + { + "block_index": 310016, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "bd4e9cbbe69c2db893cd32182a2d315c89c45ba4e31aa5775d1fe42d841cea39", + "tx_index": 17 + }, + { + "block_index": 310019, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 9, + "action": "bet", + "event": "2a2169991597036b6dad687ea1feffd55465a204466f40c35cbba811cb3109b1", + "tx_index": 20 + }, + { + "block_index": 310110, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "DIVISIBLE", + "quantity": 100000000, + "action": "send", + "event": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", + "tx_index": 111 + }, + { + "block_index": 310481, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", + "tx_index": 482 + }, + { + "block_index": 310491, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "action": "open order", + "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx_index": 492 + }, + { + "block_index": 310497, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "tx_index": 498 + }, + { + "block_index": 310498, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 25000000, + "action": "issuance fee", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "tx_index": 499 + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/bets": { + "success": true, + "result": [ + { + "tx_index": 102, + "tx_hash": "db4ea092bea6036e3d1e5f6ec863db9b900252b4f4d6d9faa6165323f433c51e", + "block_index": 310101, + "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "feed_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "bet_type": 3, + "deadline": 1388000200, + "wager_quantity": 10, + "wager_remaining": 10, + "counterwager_quantity": 10, + "counterwager_remaining": 10, + "target_value": 0.0, + "leverage": 5040, + "expiration": 1000, + "expire_index": 311101, + "fee_fraction_int": 5000000, + "status": "open" + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/broadcasts": { + "success": true, + "result": [ + { + "tx_index": 103, + "tx_hash": "16462eac6c795cea6e5985ee063867d8c61ae24373df02048186d28118d25bae", + "block_index": 310102, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "timestamp": 1388000002, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "locked": 0, + "status": "valid" + }, + { + "tx_index": 18, + "tx_hash": "d14388b74b63d93e4477b1fe8426028bb8ab20656703e3ce8deeb23c2fe0b8af", + "block_index": 310017, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "timestamp": 1388000000, + "value": 1.0, + "fee_fraction_int": 5000000, + "text": "Unit Test", + "locked": 0, + "status": "valid" + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/burns": { + "success": true, + "result": [ + { + "tx_index": 1, + "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "block_index": 310000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "burned": 62000000, + "earned": 93000000000, + "status": "valid" + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends": { + "success": true, + "result": [ + { + "tx_index": 8, + "tx_hash": "6e91ae23de2035e3e28c3322712212333592a1f666bcff9dd91aec45d5ea2753", + "block_index": 310007, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "DIVISIBLE", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 9, + "tx_hash": "4fd55abadfdbe77c3bda2431749cca934a29994a179620a62c1b57f28bd62a43", + "block_index": 310008, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 13, + "tx_hash": "698e97e507da8623cf38ab42701853443c8f7fe0d93b4674aabb42f9800ee9d6", + "block_index": 310012, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "XCP", + "quantity": 300000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 14, + "tx_hash": "0cfeeb559ed794d067557df0376a6c213b48b174b80cdb2c3c6d365cf538e132", + "block_index": 310013, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "DIVISIBLE", + "quantity": 1000000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 111, + "tx_hash": "f6a0f819e899b407cbfa07b4eff3d58902af3899abfbaa47d5f31d5b398e76e7", + "block_index": 310110, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", + "asset": "DIVISIBLE", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 482, + "tx_hash": "b00bdf03402d81a2cbdbeac4b0df90cff5ab6bf9688f653383d49fe42b8422a5", + "block_index": 310481, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": "68656c6c6f" + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives": { + "success": true, + "result": [ + { + "tx_index": 483, + "tx_hash": "c8716524f33646b9af94d6f5e52494ff3b34466497094b1db2ab920e4f79bc34", + "block_index": 310482, + "source": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "destination": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": "fade0001" + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends/NODIVISIBLE": { + "success": true, + "result": [ + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives/NODIVISIBLE": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers/NODIVISIBLE": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sweeps": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/assets": { + "success": true, + "result": [ + { + "asset": "A95428956661682277", + "asset_longname": "PARENT.already.issued" + }, + { + "asset": "CALLABLE", + "asset_longname": null + }, + { + "asset": "DIVIDEND", + "asset_longname": null + }, + { + "asset": "DIVISIBLE", + "asset_longname": null + }, + { + "asset": "LOCKED", + "asset_longname": null + }, + { + "asset": "LOCKEDPREV", + "asset_longname": null + }, + { + "asset": "MAXI", + "asset_longname": null + }, + { + "asset": "NODIVISIBLE", + "asset_longname": null + }, + { + "asset": "PARENT", + "asset_longname": null + }, + { + "asset": "PAYTOSCRIPT", + "asset_longname": null + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE": { + "success": true, + "result": { "asset": "NODIVISIBLE", - "quantity": 1000, - "divisible": 0, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "transfer": 0, - "callable": 0, - "call_date": 0, - "call_price": 0.0, - "description": "No divisible asset", - "fee_paid": 50000000, - "locked": 0, - "status": "valid", "asset_longname": null, - "reset": 0 - } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE/sends": [ - { - "tx_index": 15, - "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", - "block_index": 310014, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "asset": "NODIVISIBLE", - "quantity": 5, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 16, - "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", - "block_index": 310015, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "asset": "NODIVISIBLE", - "quantity": 10, - "status": "valid", - "msg_index": 0, - "memo": null + "owner": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "divisible": false, + "locked": false, + "supply": 1000, + "description": "No divisible asset", + "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "holder_count": 3 } - ], - "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers": [], - "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": [], - "http://api:api@localhost:10009/assets/NODIVISIBLE/holders": [ - { + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/balances": { + "success": true, + "result": [ + { + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10 + }, + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 985 + }, + { + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5 + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/balances/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { + "success": true, + "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "address_quantity": 985, - "escrow": null - }, - { - "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "address_quantity": 5, - "escrow": null - }, - { - "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", - "address_quantity": 10, - "escrow": null - } - ], - "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": [ - { - "tx_index": 492, - "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "block_index": 310492, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "give_asset": "XCP", - "give_quantity": 100000000, - "give_remaining": 0, - "get_asset": "BTC", - "get_quantity": 800000, - "get_remaining": 0, - "expiration": 2000, - "expire_index": 312491, - "fee_required": 900000, - "fee_required_remaining": 892800, - "fee_provided": 6800, - "fee_provided_remaining": 6800, - "status": "open" - } - ], - "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/matches": [ - { - "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", - "tx0_index": 492, - "tx0_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", - "tx0_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "tx1_index": 493, - "tx1_hash": "1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", - "tx1_address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", - "forward_asset": "XCP", - "forward_quantity": 100000000, - "backward_asset": "BTC", - "backward_quantity": 800000, - "tx0_block_index": 310491, - "tx1_block_index": 310492, - "block_index": 310492, - "tx0_expiration": 2000, - "tx1_expiration": 2000, - "match_expire_index": 310512, - "fee_paid": 7200, - "status": "pending" - } - ], - "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/btcpays": [], - "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42": [], - "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/matches": [], - "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/resolutions": [], - "http://api:api@localhost:10009/burns": [ - { - "tx_index": 1, - "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", - "block_index": 310000, - "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "burned": 62000000, - "earned": 93000000000, - "status": "valid" - }, - { - "tx_index": 104, - "tx_hash": "65d4048700fb8ae03f321be93c6669b8497f506a1f43920f96d994f43358c35b", - "block_index": 310103, - "source": "myAtcJEHAsDLbTkai6ipWDZeeL7VkxXsiM", - "burned": 62000000, - "earned": 92999138821, - "status": "valid" - }, - { - "tx_index": 105, - "tx_hash": "95332a7e3e2b04f2c10e3027327bfc31b686947fb05381e28903e3ff569bd4ff", - "block_index": 310104, - "source": "munimLLHjPhGeSU5rYB2HN79LJa8bRZr5b", - "burned": 62000000, - "earned": 92999130460, - "status": "valid" - }, - { - "tx_index": 106, - "tx_hash": "e062d1ebf4cb71bd22d80c949b956f5286080838a7607ccf87945b2b3abfcafa", - "block_index": 310105, - "source": "mwtPsLQxW9xpm7gdLmwWvJK5ABdPUVJm42", - "burned": 62000000, - "earned": 92999122099, - "status": "valid" - }, - { - "tx_index": 107, - "tx_hash": "bbf0b9f6992755a3e371fb0c0b72f6828831e81c6f7ada6f95ba1104fb901ac3", - "block_index": 310106, - "source": "mrPk7hTeZWjjSCrMTC2ET4SAUThQt7C4uK", - "burned": 10000, - "earned": 14999857, - "status": "valid" - }, - { - "tx_index": 109, - "tx_hash": "93c6d2499a0536c31c77a3db3fc9fc8456fbd0726c45b8f716af16f938727a73", - "block_index": 310108, - "source": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", - "burned": 31000000, - "earned": 46499548508, - "status": "valid" - }, - { - "tx_index": 117, - "tx_hash": "27929c4fcad307a76ea7da34dd2691084f678a22ee43ce7f3842b78730ee08f9", - "block_index": 310116, - "source": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", - "burned": 62000000, - "earned": 92999030129, - "status": "valid" - }, - { - "tx_index": 494, - "tx_hash": "c0733e1287afb1bb3d2fdacd1db7c74ea84f14362f3a8d1c038e662e1d0b1b1a", - "block_index": 310493, - "source": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", - "burned": 62000000, - "earned": 92995878046, - "status": "valid" + "asset": "NODIVISIBLE", + "quantity": 985 } - ], - "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": [], - "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/dispenses": [], - "http://api:api@localhost:10009/events?limit=5": [ - { - "event_index": 1237, - "event": "BLOCK_PARSED", - "bindings": { + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/orders": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/credits": { + "success": true, + "result": [ + { + "block_index": 310002, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 1000, + "calling_function": "issuance", + "event": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "tx_index": 3 + }, + { + "block_index": 310014, + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "calling_function": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "calling_function": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/debits": { + "success": true, + "result": [ + { + "block_index": 310014, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 5, + "action": "send", + "event": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "tx_index": 15 + }, + { + "block_index": 310015, + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "NODIVISIBLE", + "quantity": 10, + "action": "send", + "event": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "tx_index": 16 + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/dividends": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/issuances": { + "success": true, + "result": [ + { + "tx_index": 3, + "tx_hash": "7b1bf5144346279271b1ff78664f118224fe27fd8679d6c1519345f9c6c54584", + "msg_index": 0, + "block_index": 310002, + "asset": "NODIVISIBLE", + "quantity": 1000, + "divisible": 0, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "issuer": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "No divisible asset", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/sends": { + "success": true, + "result": [ + { + "tx_index": 15, + "tx_hash": "1facb0072f16f6bdca64ea859c82b850f58f0ec7ff410d901679772a4727515a", + "block_index": 310014, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "asset": "NODIVISIBLE", + "quantity": 5, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 16, + "tx_hash": "e3b6667b7baa515048a7fcf2be7818e3e7622371236b78e19b4b08e2d7e7818c", + "block_index": 310015, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "destination": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "asset": "NODIVISIBLE", + "quantity": 10, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/assets/NODIVISIBLE/holders": { + "success": true, + "result": [ + { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "address_quantity": 985, + "escrow": null + }, + { + "address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "address_quantity": 5, + "escrow": null + }, + { + "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", + "address_quantity": 10, + "escrow": null + } + ] + }, + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { + "success": true, + "result": [ + { + "tx_index": 492, + "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "block_index": 310492, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "give_asset": "XCP", + "give_quantity": 100000000, + "give_remaining": 0, + "get_asset": "BTC", + "get_quantity": 800000, + "get_remaining": 0, + "expiration": 2000, + "expire_index": 312491, + "fee_required": 900000, + "fee_required_remaining": 892800, + "fee_provided": 6800, + "fee_provided_remaining": 6800, + "status": "open" + } + ] + }, + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/matches": { + "success": true, + "result": [ + { + "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx0_index": 492, + "tx0_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", + "tx0_address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "tx1_index": 493, + "tx1_hash": "1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", + "tx1_address": "mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns", + "forward_asset": "XCP", + "forward_quantity": 100000000, + "backward_asset": "BTC", + "backward_quantity": 800000, + "tx0_block_index": 310491, + "tx1_block_index": 310492, + "block_index": 310492, + "tx0_expiration": 2000, + "tx1_expiration": 2000, + "match_expire_index": 310512, + "fee_paid": 7200, + "status": "pending" + } + ] + }, + "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/btcpays": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/matches": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/resolutions": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/burns": { + "success": true, + "result": [ + { + "tx_index": 1, + "tx_hash": "6dc5b0a33d4d4297e0f5cc2d23ae307951d32aab2d86b7fa147b385219f3a597", + "block_index": 310000, + "source": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "burned": 62000000, + "earned": 93000000000, + "status": "valid" + }, + { + "tx_index": 104, + "tx_hash": "65d4048700fb8ae03f321be93c6669b8497f506a1f43920f96d994f43358c35b", + "block_index": 310103, + "source": "myAtcJEHAsDLbTkai6ipWDZeeL7VkxXsiM", + "burned": 62000000, + "earned": 92999138821, + "status": "valid" + }, + { + "tx_index": 105, + "tx_hash": "95332a7e3e2b04f2c10e3027327bfc31b686947fb05381e28903e3ff569bd4ff", + "block_index": 310104, + "source": "munimLLHjPhGeSU5rYB2HN79LJa8bRZr5b", + "burned": 62000000, + "earned": 92999130460, + "status": "valid" + }, + { + "tx_index": 106, + "tx_hash": "e062d1ebf4cb71bd22d80c949b956f5286080838a7607ccf87945b2b3abfcafa", + "block_index": 310105, + "source": "mwtPsLQxW9xpm7gdLmwWvJK5ABdPUVJm42", + "burned": 62000000, + "earned": 92999122099, + "status": "valid" + }, + { + "tx_index": 107, + "tx_hash": "bbf0b9f6992755a3e371fb0c0b72f6828831e81c6f7ada6f95ba1104fb901ac3", + "block_index": 310106, + "source": "mrPk7hTeZWjjSCrMTC2ET4SAUThQt7C4uK", + "burned": 10000, + "earned": 14999857, + "status": "valid" + }, + { + "tx_index": 109, + "tx_hash": "93c6d2499a0536c31c77a3db3fc9fc8456fbd0726c45b8f716af16f938727a73", + "block_index": 310108, + "source": "2MyJHMUenMWonC35Yi6PHC7i2tkS7PuomCy", + "burned": 31000000, + "earned": 46499548508, + "status": "valid" + }, + { + "tx_index": 117, + "tx_hash": "27929c4fcad307a76ea7da34dd2691084f678a22ee43ce7f3842b78730ee08f9", + "block_index": 310116, + "source": "tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx", + "burned": 62000000, + "earned": 92999030129, + "status": "valid" + }, + { + "tx_index": 494, + "tx_hash": "c0733e1287afb1bb3d2fdacd1db7c74ea84f14362f3a8d1c038e662e1d0b1b1a", + "block_index": 310493, + "source": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", + "burned": 62000000, + "earned": 92995878046, + "status": "valid" + } + ] + }, + "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/dispenses": { + "success": true, + "result": [] + }, + "http://api:api@localhost:10009/events?limit=5": { + "success": true, + "result": [ + { + "event_index": 1237, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310500, + "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", + "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f", + "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1" + }, "block_index": 310500, - "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", - "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f", - "txlist_hash": "35f4a33840d002ab4e0e44f11c1749ae95b41376927fb346140508b32518edd1" - }, - "block_index": 310500, - "timestamp": 0 - }, - { - "event_index": 1236, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", + "timestamp": 0 + }, + { + "event_index": 1236, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", + "block_index": 310500, + "block_time": 310500000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, "block_index": 310500, - "block_time": 310500000, - "difficulty": null, - "ledger_hash": null, - "previous_block_hash": null, - "txlist_hash": null - }, - "block_index": 310500, - "timestamp": 0 - }, - { - "event_index": 1235, - "event": "BLOCK_PARSED", - "bindings": { + "timestamp": 0 + }, + { + "event_index": 1235, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310499, + "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", + "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c", + "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5" + }, "block_index": 310499, - "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", - "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c", - "txlist_hash": "032166892f568bb97f4f69ef5bdf49cc1b15cc9f8c7f6c1f3e1f9d54816ad7e5" - }, - "block_index": 310499, - "timestamp": 0 - }, - { - "event_index": 1234, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", + "timestamp": 0 + }, + { + "event_index": 1234, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", + "block_index": 310499, + "block_time": 310499000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, "block_index": 310499, - "block_time": 310499000, - "difficulty": null, - "ledger_hash": null, - "previous_block_hash": null, - "txlist_hash": null - }, - "block_index": 310499, - "timestamp": 0 - }, - { - "event_index": 1233, - "event": "BLOCK_PARSED", - "bindings": { + "timestamp": 0 + }, + { + "event_index": 1233, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 310498, + "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", + "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98", + "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7" + }, "block_index": 310498, - "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", - "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98", - "txlist_hash": "b488f6f0e6c233f202ee17c0843236d464144e79c870af88bae56355ae9372b7" + "timestamp": 0 + } + ] + }, + "http://api:api@localhost:10009/events/10?limit=5": { + "success": true, + "result": [ + { + "event_index": 10, + "event": "ASSET_CREATION", + "bindings": { + "asset_id": "697326324582", + "asset_longname": null, + "asset_name": "DIVISIBLE", + "block_index": 310001 + }, + "block_index": 310001, + "timestamp": 0 + } + ] + }, + "http://api:api@localhost:10009/events/counts?limit=5": { + "success": true, + "result": [ + { + "event": "ASSET_CREATION", + "event_count": 10 }, - "block_index": 310498, - "timestamp": 0 - } - ], - "http://api:api@localhost:10009/events/10?limit=5": [ - { - "event_index": 10, - "event": "ASSET_CREATION", - "bindings": { - "asset_id": "697326324582", - "asset_longname": null, - "asset_name": "DIVISIBLE", - "block_index": 310001 + { + "event": "ASSET_ISSUANCE", + "event_count": 13 }, - "block_index": 310001, - "timestamp": 0 - } - ], - "http://api:api@localhost:10009/events/counts?limit=5": [ - { - "event": "ASSET_CREATION", - "event_count": 10 - }, - { - "event": "ASSET_ISSUANCE", - "event_count": 13 - }, - { - "event": "BET_MATCH", - "event_count": 1 - }, - { - "event": "BET_MATCH_RESOLUTON", - "event_count": 1 - }, - { - "event": "BET_MATCH_UPDATE", - "event_count": 1 - }, - { - "event": "BET_UPDATE", - "event_count": 2 - }, - { - "event": "BLOCK_PARSED", - "event_count": 502 - }, - { - "event": "BROADCAST", - "event_count": 8 - }, - { - "event": "BURN", - "event_count": 8 - }, - { - "event": "CREDIT", - "event_count": 34 - }, - { - "event": "DEBIT", - "event_count": 34 - }, - { - "event": "ENHANCED_SEND", - "event_count": 2 - }, - { - "event": "NEW_BLOCK", - "event_count": 502 - }, - { - "event": "NEW_TRANSACTION", - "event_count": 52 - }, - { - "event": "OPEN_BET", - "event_count": 5 - }, - { - "event": "OPEN_DISPENSER", - "event_count": 1 - }, - { - "event": "OPEN_ORDER", - "event_count": 6 - }, - { - "event": "ORDER_MATCH", - "event_count": 1 - }, - { - "event": "ORDER_UPDATE", - "event_count": 2 - }, - { - "event": "SEND", - "event_count": 9 - }, - { - "event": "TRANSACTION_PARSED", - "event_count": 44 - } - ], - "http://api:api@localhost:10009/events/CREDIT?limit=5": [ - { - "event_index": 1231, - "event": "CREDIT", - "bindings": { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "A95428956661682277", + { + "event": "BET_MATCH", + "event_count": 1 + }, + { + "event": "BET_MATCH_RESOLUTON", + "event_count": 1 + }, + { + "event": "BET_MATCH_UPDATE", + "event_count": 1 + }, + { + "event": "BET_UPDATE", + "event_count": 2 + }, + { + "event": "BLOCK_PARSED", + "event_count": 502 + }, + { + "event": "BROADCAST", + "event_count": 8 + }, + { + "event": "BURN", + "event_count": 8 + }, + { + "event": "CREDIT", + "event_count": 34 + }, + { + "event": "DEBIT", + "event_count": 34 + }, + { + "event": "ENHANCED_SEND", + "event_count": 2 + }, + { + "event": "NEW_BLOCK", + "event_count": 502 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 52 + }, + { + "event": "OPEN_BET", + "event_count": 5 + }, + { + "event": "OPEN_DISPENSER", + "event_count": 1 + }, + { + "event": "OPEN_ORDER", + "event_count": 6 + }, + { + "event": "ORDER_MATCH", + "event_count": 1 + }, + { + "event": "ORDER_UPDATE", + "event_count": 2 + }, + { + "event": "SEND", + "event_count": 9 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 44 + } + ] + }, + "http://api:api@localhost:10009/events/CREDIT?limit=5": { + "success": true, + "result": [ + { + "event_index": 1231, + "event": "CREDIT", + "bindings": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "A95428956661682277", + "block_index": 310498, + "calling_function": "issuance", + "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", + "quantity": 100000000, + "tx_index": 499 + }, "block_index": 310498, - "calling_function": "issuance", - "event": "0abfce2662c05852fd8b181a60900678643cedad47b23a853b8c4eda82cb2cbf", - "quantity": 100000000, - "tx_index": 499 + "timestamp": 0 }, - "block_index": 310498, - "timestamp": 0 - }, - { - "event_index": 1223, - "event": "CREDIT", - "bindings": { - "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", - "asset": "PARENT", + { + "event_index": 1223, + "event": "CREDIT", + "bindings": { + "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", + "asset": "PARENT", + "block_index": 310497, + "calling_function": "issuance", + "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", + "quantity": 100000000, + "tx_index": 498 + }, "block_index": 310497, - "calling_function": "issuance", - "event": "076ae3d8eeb7fb40d2ae27692340157c746d9832806766b0dac5adb1526dc78f", - "quantity": 100000000, - "tx_index": 498 + "timestamp": 0 }, - "block_index": 310497, - "timestamp": 0 - }, - { - "event_index": 1214, - "event": "CREDIT", - "bindings": { - "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", - "asset": "XCP", + { + "event_index": 1214, + "event": "CREDIT", + "bindings": { + "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", + "asset": "XCP", + "block_index": 310496, + "calling_function": "send", + "event": "a35ab1736565aceddbd1d71f92fc7f39d1361006aa9099f731e54e762964d5ba", + "quantity": 92945878046, + "tx_index": 497 + }, "block_index": 310496, - "calling_function": "send", - "event": "a35ab1736565aceddbd1d71f92fc7f39d1361006aa9099f731e54e762964d5ba", - "quantity": 92945878046, - "tx_index": 497 - }, - "block_index": 310496, - "timestamp": 0 - }, - { - "event_index": 1207, - "event": "CREDIT", - "bindings": { - "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", - "asset": "DIVIDEND", + "timestamp": 0 + }, + { + "event_index": 1207, + "event": "CREDIT", + "bindings": { + "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", + "asset": "DIVIDEND", + "block_index": 310495, + "calling_function": "send", + "event": "02156b9a1f643fb48330396274a37620c8abbbe5eddb2f8b53dadd135f5d2e2e", + "quantity": 10, + "tx_index": 496 + }, "block_index": 310495, - "calling_function": "send", - "event": "02156b9a1f643fb48330396274a37620c8abbbe5eddb2f8b53dadd135f5d2e2e", - "quantity": 10, - "tx_index": 496 - }, - "block_index": 310495, - "timestamp": 0 - }, - { - "event_index": 1201, - "event": "CREDIT", - "bindings": { - "address": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", - "asset": "DIVIDEND", - "block_index": 310494, - "calling_function": "issuance", - "event": "321bed395482e034f2ce0a4dbf28d1f800592a658e26ea91ae9c5b0928204503", - "quantity": 100, - "tx_index": 495 + "timestamp": 0 }, - "block_index": 310494, - "timestamp": 0 - } - ] + { + "event_index": 1201, + "event": "CREDIT", + "bindings": { + "address": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", + "asset": "DIVIDEND", + "block_index": 310494, + "calling_function": "issuance", + "event": "321bed395482e034f2ce0a4dbf28d1f800592a658e26ea91ae9c5b0928204503", + "quantity": 100, + "tx_index": 495 + }, + "block_index": 310494, + "timestamp": 0 + } + ] + } } \ No newline at end of file From 8a4d60747fed80a273bf3512dbc150cfa0b4f23a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 11:59:20 +0200 Subject: [PATCH 085/128] Output example for all compose routes; fixes --- .../counterpartycore/lib/api/api_server.py | 4 +- .../counterpartycore/lib/api/util.py | 2 +- .../counterpartycore/lib/messages/sweep.py | 1 + .../counterpartycore/lib/transaction.py | 96 +- counterparty-core/tools/apicache.json | 3898 +++++++++-------- 5 files changed, 2176 insertions(+), 1825 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 05dc88d58c..93f9ed4bbe 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -2,6 +2,7 @@ import logging import multiprocessing import signal +import traceback from multiprocessing import Process from threading import Timer @@ -151,7 +152,8 @@ def handle_route(**kwargs): except (exceptions.ComposeError, exceptions.UnpackError) as e: return inject_headers({"success": False, "error": str(e)}, return_code=503) except Exception as e: - logger.error("Error in API: %s", e) + logger.exception("Error in API: %s", e) + traceback.print_exc() return inject_headers({"success": False, "error": "Unknwon error"}, return_code=503) result = remove_rowids(result) return inject_headers(result) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 5c793a5583..99ee1760a2 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -61,7 +61,7 @@ def handle_healthz_route(db, check_type: str = "heavy"): msg, code = "Healthy", 200 if not healthz(db, check_type): msg, code = "Unhealthy", 503 - result = {"data": msg, "success": code == 200} + result = {"result": msg, "success": code == 200} if code != 200: result["error"] = msg return flask.Response(to_json(result), code, mimetype="application/json") diff --git a/counterparty-core/counterpartycore/lib/messages/sweep.py b/counterparty-core/counterpartycore/lib/messages/sweep.py index 9ed916cb8f..89d7427fd6 100644 --- a/counterparty-core/counterpartycore/lib/messages/sweep.py +++ b/counterparty-core/counterpartycore/lib/messages/sweep.py @@ -107,6 +107,7 @@ def compose(db, source: str, destination: str, flags: int, memo: str): if memo is None: memo = b"" elif flags & FLAG_BINARY_MEMO: + print("MEMEOOOO", memo) memo = bytes.fromhex(memo) else: memo = memo.encode("utf-8") diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 655ad51c2d..fe086728e9 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1215,7 +1215,7 @@ def compose_bet( ): """ Composes a transaction to issue a bet against a feed. - :param address: The address that will make the bet (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param address: The address that will make the bet (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) :param feed_address: The address that hosts the feed to be bet on (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) :param bet_type: Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual (e.g. 2) :param deadline: The time at which the bet should be decided/settled, in Unix time (seconds since epoch) (e.g. 3000000000) @@ -1254,7 +1254,7 @@ def compose_broadcast( ): """ Composes a transaction to broadcast textual and numerical information to the network. - :param address: The address that will be sending (must have the necessary quantity of the specified asset) (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param address: The address that will be sending (must have the necessary quantity of the specified asset) (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) :param timestamp: The timestamp of the broadcast, in Unix time (e.g. 4003903983) :param value: Numerical value of the broadcast (e.g. 100) :param fee_fraction: How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) (e.g. 0.05) @@ -1303,8 +1303,8 @@ def compose_btcpay(db, address: str, order_match_id: str, **construct_args): def compose_burn(db, address: str, quantity: int, overburn: bool = False, **construct_args): """ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). - :param address: The address with the BTC to burn - :param quantity: The quantities of BTC to burn (1 BTC maximum burn per address) + :param address: The address with the BTC to burn (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param quantity: The quantities of BTC to burn (1 BTC maximum burn per address) (e.g. 1000) :param overburn: Whether to allow the burn to exceed 1 BTC for the address """ params = {"source": address, "quantity": quantity, "overburn": overburn} @@ -1324,8 +1324,8 @@ def compose_burn(db, address: str, quantity: int, overburn: bool = False, **cons def compose_cancel(db, address: str, offer_hash: str, **construct_args): """ Composes a transaction to cancel an open order or bet. - :param address: The address that placed the order/bet to be cancelled - :param offer_hash: The hash of the order/bet to be cancelled + :param address: The address that placed the order/bet to be cancelled (e.g. 15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA) + :param offer_hash: The hash of the order/bet to be cancelled (e.g. 8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a) """ params = {"source": address, "offer_hash": offer_hash} rawtransaction = compose_transaction( @@ -1344,10 +1344,10 @@ def compose_cancel(db, address: str, offer_hash: str, **construct_args): def compose_destroy(db, address: str, asset: str, quantity: int, tag: str, **construct_args): """ Composes a transaction to destroy a quantity of an asset. - :param address: The address that will be sending the asset to be destroyed - :param asset: The asset to be destroyed - :param quantity: The quantity of the asset to be destroyed - :param tag: A tag for the destruction + :param address: The address that will be sending the asset to be destroyed (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param asset: The asset to be destroyed (e.g. XCP) + :param quantity: The quantity of the asset to be destroyed (e.g. 1000) + :param tag: A tag for the destruction (e.g. "bugs!") """ params = {"source": address, "asset": asset, "quantity": quantity, "tag": tag} rawtransaction = compose_transaction( @@ -1377,12 +1377,12 @@ def compose_dispenser( ): """ Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. - :param address: The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) - :param asset: The asset or subasset to dispense - :param give_quantity: The quantity of the asset to dispense - :param escrow_quantity: The quantity of the asset to reserve for this dispenser - :param mainchainrate: The quantity of the main chain asset (BTC) per dispensed portion - :param status: The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed + :param address: The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param asset: The asset or subasset to dispense (e.g. XCP) + :param give_quantity: The quantity of the asset to dispense (e.g. 1000) + :param escrow_quantity: The quantity of the asset to reserve for this dispenser (e.g. 1000) + :param mainchainrate: The quantity of the main chain asset (BTC) per dispensed portion (e.g. 100) + :param status: The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed (e.g. 0) :param open_address: The address that you would like to open the dispenser on :param oracle_address: The address that you would like to use as a price oracle for this dispenser """ @@ -1414,10 +1414,10 @@ def compose_dividend( ): """ Composes a transaction to issue a dividend to holders of a given asset. - :param address: The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) - :param quantity_per_unit: The amount of dividend_asset rewarded - :param asset: The asset or subasset that the dividends are being rewarded on - :param dividend_asset: The asset or subasset that the dividends are paid in + :param address: The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) (e.g. 1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD) + :param quantity_per_unit: The amount of dividend_asset rewarded (e.g. 1) + :param asset: The asset or subasset that the dividends are being rewarded on (e.g. PEPECASH) + :param dividend_asset: The asset or subasset that the dividends are paid in (e.g. XCP) """ params = { "source": address, @@ -1452,10 +1452,10 @@ def compose_issuance( ): """ Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. - :param address: The address that will be issuing or transfering the asset - :param asset: The assets to issue or transfer. This can also be a subasset longname for new subasset issuances - :param quantity: The quantity of the asset to issue (set to 0 if transferring an asset) - :param transfer_destination: The address to receive the asset + :param address: The address that will be issuing or transfering the asset (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param asset: The assets to issue or transfer. This can also be a subasset longname for new subasset issuances (e.g. XCPTEST) + :param quantity: The quantity of the asset to issue (set to 0 if transferring an asset) (e.g. 1000) + :param transfer_destination: The address to receive the asset (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) :param divisible: Whether this asset is divisible or not (if a transfer, this value must match the value specified when the asset was originally issued) :param lock: Whether this issuance should lock supply of this asset forever :param reset: Wether this issuance should reset any existing supply @@ -1486,7 +1486,7 @@ def compose_issuance( def compose_mpma( db, - source: str, + address: str, assets: str, destinations: str, quantities: str, @@ -1496,12 +1496,12 @@ def compose_mpma( ): """ Composes a transaction to send multiple payments to multiple addresses. - :param source: The address that will be sending (must have the necessary quantity of the specified asset) - :param assets: comma-separated list of assets to send - :param destinations: comma-separated list of addresses to send to - :param quantities: comma-separated list of quantities to send - :param memo: The Memo associated with this transaction - :param memo_is_hex: Whether the memo field is a hexadecimal string + :param address: The address that will be sending (must have the necessary quantity of the specified asset) (e.g. 1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6) + :param assets: comma-separated list of assets to send (e.g. BAABAABLKSHP,BADHAIRDAY,BADWOJAK) + :param destinations: comma-separated list of addresses to send to (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev,1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD,1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param quantities: comma-separated list of quantities to send (e.g. 1,2,3) + :param memo: The Memo associated with this transaction (e.g. "Hello, world!") + :param memo_is_hex: Whether the memo field is a hexadecimal string (e.g. False) """ asset_list = assets.split(",") destination_list = destinations.split(",") @@ -1513,10 +1513,11 @@ def compose_mpma( for quantity in quantity_list: if not quantity.isdigit(): raise exceptions.ComposeError("Quantity must be an integer") + quantity_list = [int(quantity) for quantity in quantity_list] asset_dest_quant_list = list(zip(asset_list, destination_list, quantity_list)) params = { - "source": source, + "source": address, "asset_dest_quant_list": asset_dest_quant_list, "memo": memo, "memo_is_hex": memo_is_hex, @@ -1524,7 +1525,7 @@ def compose_mpma( rawtransaction = compose_transaction( db, - name="version.mpma", + name="versions.mpma", params=params, **construct_args, ) @@ -1548,13 +1549,13 @@ def compose_order( ): """ Composes a transaction to place an order on the distributed exchange. - :param address: The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) - :param give_asset: The asset that will be given in the trade - :param give_quantity: The quantity of the asset that will be given - :param get_asset: The asset that will be received in the trade - :param get_quantity: The quantity of the asset that will be received - :param expiration: The number of blocks for which the order should be valid - :param fee_required: The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + :param address: The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param give_asset: The asset that will be given in the trade (e.g. XCP) + :param give_quantity: The quantity of the asset that will be given (e.g. 1000) + :param get_asset: The asset that will be received in the trade (e.g. PEPECASH) + :param get_quantity: The quantity of the asset that will be received (e.g. 1000) + :param expiration: The number of blocks for which the order should be valid (e.g. 100) + :param fee_required: The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) (e.g. 100) """ params = { "source": address, @@ -1591,10 +1592,10 @@ def compose_send( ): """ Composes a transaction to send a quantity of an asset to another address. - :param address: The address that will be sending (must have the necessary quantity of the specified asset) - :param destination: The address that will be receiving the asset - :param asset: The asset or subasset to send - :param quantity: The quantity of the asset to send + :param address: The address that will be sending (must have the necessary quantity of the specified asset) (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param destination: The address that will be receiving the asset (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) + :param asset: The asset or subasset to send (e.g. XCP) + :param quantity: The quantity of the asset to send (e.g. 1000) :param memo: The Memo associated with this transaction :param memo_is_hex: Whether the memo field is a hexadecimal string :param use_enhanced_send: If this is false, the construct a legacy transaction sending bitcoin dust @@ -1624,13 +1625,14 @@ def compose_send( def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **construct_args): """ Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. - :param address: The address that will be sending - :param destination: The address to receive the assets and/or ownerships + :param address: The address that will be sending (e.g. 1CounterpartyXXXXXXXXXXXXXXXUWLpVr) + :param destination: The address to receive the assets and/or ownerships (e.g. 1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev) :param flags: An OR mask of flags indicating how the sweep should be processed. Possible flags are: - FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. - FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. - :param memo: The Memo associated with this transaction + (e.g. 7) + :param memo: The Memo associated with this transaction in hex format (e.g. FFFF) """ params = { "source": address, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index cffe8c8ac0..58ae122e38 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1,359 +1,438 @@ { - "/blocks": [ - { - "block_index": 840000, - "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", - "block_time": 1713571767, - "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", - "difficulty": 86388558925171.02, - "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", - "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", - "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" - }, - { - "block_index": 839999, - "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", - "block_time": 1713571533, - "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", - "difficulty": 86388558925171.02, - "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", - "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", - "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" - } - ], - "/blocks//transactions": [ - { - "tx_index": 2726605, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "/blocks": { + "success": true, + "result": [ + { + "block_index": 840000, + "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", + "block_time": 1713571767, + "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "difficulty": 86388558925171.02, + "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", + "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", + "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" + }, + { + "block_index": 839999, + "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "block_time": 1713571533, + "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", + "difficulty": 86388558925171.02, + "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", + "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", + "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" + } + ] + }, + "/blocks/": { + "success": true, + "result": { "block_index": 840464, "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_time": 1713852783, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "destination": "", - "btc_amount": 0, - "fee": 56565, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "supported": 1 + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", + "difficulty": 86388558925171.02, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" } - ], - "/blocks//events": [ - { - "event_index": 14194760, - "event": "BLOCK_PARSED", - "bindings": { + }, + "/blocks//transactions": { + "success": true, + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + } + ] + }, + "/blocks//events": { + "success": true, + "result": [ + { + "event_index": 14194760, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 840464, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" + }, "block_index": 840464, - "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", - "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", - "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194759, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 + { + "event_index": 14194759, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194758, - "event": "CREDIT", - "bindings": { - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, "block_index": 840464, - "calling_function": "issuance", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 1, - "tx_index": 2726605 + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194757, - "event": "ASSET_ISSUANCE", - "bindings": { - "asset": "UNNEGOTIABLE", - "asset_longname": null, + { + "event_index": 14194757, + "event": "ASSET_ISSUANCE", + "bindings": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "block_index": 840464, + "call_date": 0, + "call_price": 0.0, + "callable": false, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "divisible": false, + "fee_paid": 50000000, + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "locked": false, + "quantity": 1, + "reset": false, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "status": "valid", + "transfer": false, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, "block_index": 840464, - "call_date": 0, - "call_price": 0.0, - "callable": false, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "divisible": false, - "fee_paid": 50000000, - "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "locked": false, - "quantity": 1, - "reset": false, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "status": "valid", - "transfer": false, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194756, - "event": "ASSET_CREATION", - "bindings": { - "asset_id": "75313533584419238", - "asset_longname": null, - "asset_name": "UNNEGOTIABLE", - "block_index": 840464 + { + "event_index": 14194756, + "event": "ASSET_CREATION", + "bindings": { + "asset_id": "75313533584419238", + "asset_longname": null, + "asset_name": "UNNEGOTIABLE", + "block_index": 840464 + }, + "block_index": 840464, + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194755, - "event": "DEBIT", - "bindings": { - "action": "issuance fee", - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "XCP", + { + "event_index": 14194755, + "event": "DEBIT", + "bindings": { + "action": "issuance fee", + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "block_index": 840464, + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 50000000, + "tx_index": 2726605 + }, "block_index": 840464, - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 50000000, - "tx_index": 2726605 + "timestamp": 1713852780 }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194754, - "event": "NEW_TRANSACTION", - "bindings": { - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + { + "event_index": 14194754, + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "btc_amount": 0, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "destination": "", + "fee": 56565, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, "block_index": 840464, - "block_time": 1713852783, - "btc_amount": 0, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "destination": "", - "fee": 56565, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 + "timestamp": 1713852779 }, - "block_index": 840464, - "timestamp": 1713852779 - }, - { - "event_index": 14194753, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + { + "event_index": 14194753, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "difficulty": 86388558925171.02, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" + }, "block_index": 840464, - "block_time": 1713852783, - "difficulty": 86388558925171.02, - "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" + "timestamp": 1713852779 + } + ] + }, + "/blocks//events/counts": { + "success": true, + "result": [ + { + "event": "ASSET_CREATION", + "event_count": 1 }, - "block_index": 840464, - "timestamp": 1713852779 - } - ], - "/blocks//events/counts": [ - { - "event": "ASSET_CREATION", - "event_count": 1 - }, - { - "event": "ASSET_ISSUANCE", - "event_count": 1 - }, - { - "event": "BLOCK_PARSED", - "event_count": 1 - }, - { - "event": "CREDIT", - "event_count": 1 - }, - { - "event": "DEBIT", - "event_count": 1 - }, - { - "event": "NEW_BLOCK", - "event_count": 1 - }, - { - "event": "NEW_TRANSACTION", - "event_count": 1 - }, - { - "event": "TRANSACTION_PARSED", - "event_count": 1 - } - ], - "/blocks//events/": [ - { - "event_index": 14194758, - "event": "CREDIT", - "bindings": { + { + "event": "ASSET_ISSUANCE", + "event_count": 1 + }, + { + "event": "BLOCK_PARSED", + "event_count": 1 + }, + { + "event": "CREDIT", + "event_count": 1 + }, + { + "event": "DEBIT", + "event_count": 1 + }, + { + "event": "NEW_BLOCK", + "event_count": 1 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 1 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 1 + } + ] + }, + "/blocks//events/": { + "success": true, + "result": [ + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + } + ] + }, + "/blocks//credits": { + "success": true, + "result": [ + { + "block_index": 840464, "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", - "block_index": 840464, + "quantity": 1, "calling_function": "issuance", "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 1, "tx_index": 2726605 + } + ] + }, + "/blocks//debits": { + "success": true, + "result": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ] + }, + "/blocks//expirations": { + "success": true, + "result": [ + { + "type": "order", + "object_id": "533d5c0ecd8ca9c2946d3298cc5e570eee55b62b887dd85c95de6de4fdc7f441" }, - "block_index": 840464, - "timestamp": 1713852780 - } - ], - "/blocks//credits": [ - { - "block_index": 840464, - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", - "quantity": 1, - "calling_function": "issuance", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - } - ], - "/blocks//debits": [ - { - "block_index": 840464, - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "XCP", - "quantity": 50000000, - "action": "issuance fee", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - } - ], - "/blocks//expirations": [ - { - "type": "order", - "object_id": "533d5c0ecd8ca9c2946d3298cc5e570eee55b62b887dd85c95de6de4fdc7f441" - }, - { - "type": "order", - "object_id": "b048661afeee3f266792481168024abc0d7648fe0e019e4a1e0fd9867c2c0ffc" - } - ], - "/blocks//cancels": [ - { - "tx_index": 2725738, - "tx_hash": "793af9129c7368f974c3ea0c87ad38131f0d82d19fbaf1adf8aaf2e657ec42b8", - "block_index": 839746, - "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "offer_hash": "04b258ac37f73e3b9a8575110320d67c752e1baace0f516da75845f388911735", - "status": "valid" - }, - { - "tx_index": 2725739, - "tx_hash": "2071e8a6fbc0c443b152d513c754356f8f962db2fa694de8c6826b57413cc190", - "block_index": 839746, - "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "offer_hash": "b1622dbe4f0ce740cb6c18f6f136876bc4949c40a62bc8cceefa81fd6679a57f", - "status": "valid" - } - ], - "/blocks//destructions": [ - { - "tx_index": 2726496, - "tx_hash": "f5609facc8dac6cdf70b15c514ea15a9acc24a9bd86dcac2b845d5740fbcc50b", - "block_index": 839988, - "source": "1FpLAtreZjTVCMcj1pq1AHWuqcs3n7obMm", - "asset": "COBBEE", - "quantity": 50000, - "tag": "", - "status": "valid" - } - ], - "/blocks//issuances": [ - { - "tx_index": 2726605, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "msg_index": 0, - "block_index": 840464, - "asset": "UNNEGOTIABLE", - "quantity": 1, - "divisible": 0, + { + "type": "order", + "object_id": "b048661afeee3f266792481168024abc0d7648fe0e019e4a1e0fd9867c2c0ffc" + } + ] + }, + "/blocks//cancels": { + "success": true, + "result": [ + { + "tx_index": 2725738, + "tx_hash": "793af9129c7368f974c3ea0c87ad38131f0d82d19fbaf1adf8aaf2e657ec42b8", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "04b258ac37f73e3b9a8575110320d67c752e1baace0f516da75845f388911735", + "status": "valid" + }, + { + "tx_index": 2725739, + "tx_hash": "2071e8a6fbc0c443b152d513c754356f8f962db2fa694de8c6826b57413cc190", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "b1622dbe4f0ce740cb6c18f6f136876bc4949c40a62bc8cceefa81fd6679a57f", + "status": "valid" + } + ] + }, + "/blocks//destructions": { + "success": true, + "result": [ + { + "tx_index": 2726496, + "tx_hash": "f5609facc8dac6cdf70b15c514ea15a9acc24a9bd86dcac2b845d5740fbcc50b", + "block_index": 839988, + "source": "1FpLAtreZjTVCMcj1pq1AHWuqcs3n7obMm", + "asset": "COBBEE", + "quantity": 50000, + "tag": "", + "status": "valid" + } + ] + }, + "/blocks//issuances": { + "success": true, + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", + "quantity": 1, + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ] + }, + "/blocks//sends": { + "success": true, + "result": [ + { + "tx_index": 2726604, + "tx_hash": "b4bbb14c99dd260eb634243e5c595e1b7213459979857a32850de84989bb71ec", + "block_index": 840459, + "source": "13Hnmhs5gy2yXKVBx4wSM5HCBdKnaSBZJH", + "destination": "1LfT83WAxbN9qKhtrXxcQA6xgdhfZk21Hz", + "asset": "GAMESOFTRUMP", + "quantity": 1, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/blocks//dispenses": { + "success": true, + "result": [ + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ] + }, + "/blocks//sweeps": { + "success": true, + "result": [ + { + "tx_index": 2720536, + "tx_hash": "9309a4c0aed426e281a52e5d48acadd1464999269a5e75cf2293edd0277d743d", + "block_index": 836519, + "source": "1DMVnJuqBobXA9xYioabBsR4mN8bvVtCAW", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + }, + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ] + }, + "/transactions/info": { + "success": true, + "result": { "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "transfer": 0, - "callable": 0, - "call_date": 0, - "call_price": 0.0, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "fee_paid": 50000000, - "locked": 0, - "status": "valid", - "asset_longname": null, - "reset": 0 - } - ], - "/blocks//sends": [ - { - "tx_index": 2726604, - "tx_hash": "b4bbb14c99dd260eb634243e5c595e1b7213459979857a32850de84989bb71ec", - "block_index": 840459, - "source": "13Hnmhs5gy2yXKVBx4wSM5HCBdKnaSBZJH", - "destination": "1LfT83WAxbN9qKhtrXxcQA6xgdhfZk21Hz", - "asset": "GAMESOFTRUMP", - "quantity": 1, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/blocks//dispenses": [ - { - "tx_index": 2726580, - "dispense_index": 0, - "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", - "block_index": 840322, - "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", - "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", - "asset": "FLOCK", - "dispense_quantity": 90000000000, - "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" - } - ], - "/blocks//sweeps": [ - { - "tx_index": 2720536, - "tx_hash": "9309a4c0aed426e281a52e5d48acadd1464999269a5e75cf2293edd0277d743d", - "block_index": 836519, - "source": "1DMVnJuqBobXA9xYioabBsR4mN8bvVtCAW", - "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", - "flags": 3, - "status": "valid", - "memo": null, - "fee_paid": 1400000 - }, - { - "tx_index": 2720537, - "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", - "block_index": 836519, - "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", - "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", - "flags": 3, - "status": "valid", - "memo": null, - "fee_paid": 1400000 + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "unpacked_data": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } } - ], - "/transactions/info": { - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "destination": "", - "btc_amount": 0, - "fee": 56565, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "unpacked_data": { + }, + "/transactions/unpack": { + "success": true, + "result": { "message_type": "issuance", "message_type_id": 22, "message_data": { @@ -372,1480 +451,1747 @@ } } }, - "/transactions/unpack": { - "message_type": "issuance", - "message_type_id": 22, - "message_data": { - "asset_id": 75313533584419238, - "asset": "UNNEGOTIABLE", - "subasset_longname": null, - "quantity": 1, - "divisible": false, - "lock": false, - "reset": false, - "callable": false, - "call_date": 0, - "call_price": 0.0, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "status": "valid" - } - }, "/transactions/": { - "tx_index": 2726605, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "destination": "", - "btc_amount": 0, - "fee": 56565, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "supported": 1 - }, - "/addresses/
/balances": [ - { - "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000 - } - ], - "/addresses/
/balances/": { - "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000 - }, - "/addresses/
/credits": [ - { - "block_index": 830981, - "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000, - "calling_function": "send", - "event": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", - "tx_index": 2677412 - } - ], - "/addresses/
/debits": [ - { - "block_index": 836949, - "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", - "asset": "XCP", - "quantity": 40000000000, - "action": "open dispenser", - "event": "53ed08176d3479f49986e9282293da85cebc03835b128d8e790ee587f9f1c750", - "tx_index": 2721524 - }, - { - "block_index": 840388, - "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", - "asset": "XCP", - "quantity": 250000000000, - "action": "send", - "event": "bc54968ba7d0a59a47b276602e2dbdcf01b14009742e0d7b50272cbae529a9a4", - "tx_index": 2726594 - } - ], - "/addresses/
/bets": [ - { - "tx_index": 15106, - "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", - "block_index": 304063, - "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", - "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "bet_type": 3, - "deadline": 1401828300, - "wager_quantity": 50000000, - "wager_remaining": 0, - "counterwager_quantity": 50000000, - "counterwager_remaining": 0, - "target_value": 1.0, - "leverage": 5040, - "expiration": 11, - "expire_index": 304073, - "fee_fraction_int": 1000000, - "status": "filled" - }, - { - "tx_index": 61338, - "tx_hash": "0fcc7f5190c028f6c5534554d10ec5b4a9246d63826421cd58be2d572d11f088", - "block_index": 320704, - "source": "1Ew38GxczvV1KxjzZsq9f8UuRzHkHQrL5C", - "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "bet_type": 2, - "deadline": 1410728400, - "wager_quantity": 1000000, - "wager_remaining": 0, - "counterwager_quantity": 1999991, - "counterwager_remaining": 0, - "target_value": 1.0, - "leverage": 5040, - "expiration": 13, - "expire_index": 320715, - "fee_fraction_int": 1000000, - "status": "filled" - } - ], - "/addresses/
/broadcasts": [ - { - "tx_index": 15055, - "tx_hash": "774887e555a6ae5a8c058ebc0185058307977f01a2d4d326e71f37d6dd977154", - "block_index": 304048, - "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "timestamp": 1401815290, - "value": -1.0, - "fee_fraction_int": 1000000, - "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "locked": 0, - "status": "valid" - }, - { - "tx_index": 61477, - "tx_hash": "5d49993bec727622c7b41c84e2b1e65c368f33390d633d217131ffcc5b592f0d", - "block_index": 320718, - "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "timestamp": 1410732503, - "value": 1.0, - "fee_fraction_int": 1000000, - "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "locked": 0, - "status": "valid" - } - ], - "/addresses/
/burns": [ - { - "tx_index": 3070, - "tx_hash": "4560d0e3d04927108b615ab106040489aca9c4aceedcf69d2b71f63b3139c7ae", - "block_index": 283810, - "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", - "burned": 10000000, - "earned": 10000000000, - "status": "valid" - } - ], - "/addresses/
/sends": [ - { - "tx_index": 163106, - "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", - "block_index": 343049, - "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", - "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", - "asset": "XCP", - "quantity": 10000000000, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/addresses/
/receives": [ - { - "tx_index": 2677412, - "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", - "block_index": 830981, - "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", - "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/addresses/
/sends/": [ - { - "tx_index": 163106, - "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", - "block_index": 343049, - "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", - "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", - "asset": "XCP", - "quantity": 10000000000, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/addresses/
/receives/": [ - { - "tx_index": 2677412, - "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", - "block_index": 830981, - "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", - "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/addresses/
/dispensers": [ - { - "tx_index": 2726460, - "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", - "block_index": 839964, - "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", - "asset": "ERYKAHPEPU", - "give_quantity": 1, - "escrow_quantity": 25, - "satoshirate": 50000, - "status": 0, - "give_remaining": 25, - "oracle_address": null, - "last_status_tx_hash": null, - "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "dispense_count": 0 - } - ], - "/addresses/
/dispensers/": [ - { - "tx_index": 2726460, - "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", - "block_index": 839964, - "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", - "asset": "ERYKAHPEPU", - "give_quantity": 1, - "escrow_quantity": 25, - "satoshirate": 50000, - "status": 0, - "give_remaining": 25, - "oracle_address": null, - "last_status_tx_hash": null, - "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "dispense_count": 0 - } - ], - "/addresses/
/sweeps": [ - { - "tx_index": 2720537, - "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", - "block_index": 836519, - "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", - "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", - "flags": 3, - "status": "valid", - "memo": null, - "fee_paid": 1400000 - } - ], - "/addresses/
/compose/bet": { - "rawtransaction": "01000000019a753a6b8be54cdee2acd408f6199e29092c8c32e13912865a68da8a0d9ae065010000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f88dab644080b5ae64cba1e7f06510694b41e385fc9120e17c1037dd08399355abbaa0401e65959c9caa4dec4efe32189152b00000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac00000000", - "params": { - "source": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", - "feed_address": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", - "bet_type": 2, - "deadline": 3000000000, - "wager_quantity": 1000, - "counterwager_quantity": 1000, - "target_value": 1000, - "leverage": 5040, - "expiration": 100 - }, - "name": "bet" - }, - "/assets": [ - { - "asset": "A100000000000000000", - "asset_longname": null - }, - { - "asset": "A1000000000000000000", - "asset_longname": null - }, - { - "asset": "A10000000000000000000", - "asset_longname": null - }, - { - "asset": "A10000000000000000001", - "asset_longname": null - }, - { - "asset": "A10000000000000000002", - "asset_longname": null - } - ], - "/assets/": { - "asset": "UNNEGOTIABLE", - "asset_longname": null, - "owner": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "divisible": false, - "locked": false, - "supply": 1, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "holder_count": 1 - }, - "/assets//balances": [ - { - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", - "quantity": 1 - } - ], - "/assets//balances/
": { - "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", - "asset": "XCP", - "quantity": 104200000000 - }, - "/assets//orders": [ - { - "tx_index": 825373, - "tx_hash": "0129611a0aece52adddf6d929e75c703baa9cdcb7e4ce887aa859f9640aa9640", - "block_index": 455461, - "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "PEPECASH", - "get_quantity": 400000000000, - "get_remaining": 0, - "expiration": 1000, - "expire_index": 456457, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 46098, - "fee_provided_remaining": 46098, - "status": "filled" - }, - { - "tx_index": 2225134, - "tx_hash": "5b6e0c741d765ebd883dc16eecfb5c340c52865cabf297ca2c1432437c1348b7", - "block_index": 772817, - "source": "1FnM7akSCD8G3fRQHCUEXRCfL35gptsPZB", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "XCP", - "get_quantity": 80800000000, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 777817, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 5544, - "fee_provided_remaining": 5544, - "status": "filled" - }, - { - "tx_index": 1946026, - "tx_hash": "75dc6ee1f67317e674ef33b617d3a9839ee53bf4a2e8274c88d6202d4d89b59a", - "block_index": 727444, - "source": "1GotRejB6XsGgMsM79TvcypeanDJRJbMtg", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "XCP", - "get_quantity": 70000000000, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 732381, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 264, - "fee_provided_remaining": 264, - "status": "filled" - }, - { - "tx_index": 2202451, - "tx_hash": "77f568fc6604dbe209d2ea1b0158d7de20723c0178107eb570f4f2a719b0d7c7", - "block_index": 772817, - "source": "184gKLQTtQU29LXbxbYJkUV4if9SmW6v2d", - "give_asset": "XCP", - "give_quantity": 80800000000, - "give_remaining": 0, - "get_asset": "NEEDPEPE", - "get_quantity": 1, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 773300, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 264, - "fee_provided_remaining": 264, - "status": "filled" - }, - { - "tx_index": 825411, - "tx_hash": "7b2369f40078f4d98a3d3a7733315a1c4efd7977c75f7066dd447d5c7eed7f20", - "block_index": 455461, - "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", - "give_asset": "PEPECASH", - "give_quantity": 300000000000, - "give_remaining": 0, - "get_asset": "NEEDPEPE", - "get_quantity": 1, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 460461, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 40000, - "fee_provided_remaining": 40000, - "status": "filled" - }, - { - "tx_index": 825403, - "tx_hash": "7e1abf6ad57eb61227015fc7a333da034b4dd2f1c4e23cf106864b60a20feef7", - "block_index": 455460, - "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", - "give_asset": "PEPECASH", - "give_quantity": 200000000000, - "give_remaining": 0, - "get_asset": "NEEDPEPE", - "get_quantity": 1, - "get_remaining": 0, - "expiration": 1000, - "expire_index": 456460, - "fee_required": 20000, - "fee_required_remaining": 20000, - "fee_provided": 50766, - "fee_provided_remaining": 50766, - "status": "filled" - }, - { - "tx_index": 825370, - "tx_hash": "8e4d324407b62de773af53f8f7a556882ac82a217c216491a28072f293918fe6", - "block_index": 455457, - "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "PEPECASH", - "get_quantity": 100000000000, - "get_remaining": -1100000000, - "expiration": 1000, - "expire_index": 456457, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 75791, - "fee_provided_remaining": 75791, - "status": "filled" - }, - { - "tx_index": 825413, - "tx_hash": "927878fa98edb6d24310c45254c324f3d5a7f625e2a3a0e7fd1e749b49493750", - "block_index": 455461, - "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", - "give_asset": "PEPECASH", - "give_quantity": 400000000000, - "give_remaining": 0, - "get_asset": "NEEDPEPE", - "get_quantity": 1, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 460461, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 40000, - "fee_provided_remaining": 40000, - "status": "filled" - }, - { - "tx_index": 1946587, - "tx_hash": "b747f290cbbad6faa1c1c05d5c6d001b5a3ef487027bb0d4eefcdc9f6e865c39", - "block_index": 727444, - "source": "1AtcSh7uxenQ6AR5xqr6agAegWRUF5N4uh", - "give_asset": "XCP", - "give_quantity": 70000000000, - "give_remaining": 0, - "get_asset": "NEEDPEPE", - "get_quantity": 1, - "get_remaining": 0, - "expiration": 5000, - "expire_index": 732444, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 792, - "fee_provided_remaining": 792, - "status": "filled" - }, - { - "tx_index": 825371, - "tx_hash": "b83c96217214decb6316c3619bc88a3471d17e46eb3708406c8f878dedd61610", - "block_index": 455460, - "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "PEPECASH", - "get_quantity": 200000000000, - "get_remaining": 0, - "expiration": 1000, - "expire_index": 456457, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 46098, - "fee_provided_remaining": 46098, - "status": "filled" - }, - { - "tx_index": 825372, - "tx_hash": "e32154f8ade796df0b121604de140703d062d22d1e82e77e629e6096668c812f", - "block_index": 455461, - "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", - "give_asset": "NEEDPEPE", - "give_quantity": 1, - "give_remaining": 0, - "get_asset": "PEPECASH", - "get_quantity": 300000000000, - "get_remaining": 0, - "expiration": 1000, - "expire_index": 456457, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 46098, - "fee_provided_remaining": 46098, - "status": "filled" - } - ], - "/assets//credits": [ - { - "block_index": 840464, - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", - "quantity": 1, - "calling_function": "issuance", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - } - ], - "/assets//debits": [ - { - "block_index": 280091, - "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1000000000, - "action": "send", - "event": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", - "tx_index": 729 - }, - { - "block_index": 280112, - "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1100000000, - "action": "send", - "event": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", - "tx_index": 749 - }, - { - "block_index": 280112, - "address": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", - "asset": "XCP", - "quantity": 100000000, - "action": "send", - "event": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", - "tx_index": 752 - }, - { - "block_index": 280114, - "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1100000000, - "action": "send", - "event": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", - "tx_index": 755 - }, - { - "block_index": 280156, - "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1100000000, - "action": "send", - "event": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", - "tx_index": 766 - } - ], - "/assets//dividends": [ - { - "tx_index": 1914456, - "tx_hash": "30760e413947ebdc80ed7a5ada1bd4466800b87e9976bbe811ad4e2b46546359", - "block_index": 724381, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "ENDTHEFED", - "quantity_per_unit": 1, - "fee_paid": 2520000, - "status": "valid" - }, - { - "tx_index": 1915246, - "tx_hash": "827794cbab3299f80a5b8b8cb8ec29ec3aee1373f7da2c05a156bed902bf4684", - "block_index": 724479, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "TRUMPDANCING", - "quantity_per_unit": 100, - "fee_paid": 2520000, - "status": "valid" - }, - { - "tx_index": 1920208, - "tx_hash": "7014f1e259531ba9632ca5000c35df5bd47f237318e48955900453ce9c07e917", - "block_index": 724931, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "CTRWOJACK", - "quantity_per_unit": 1111, - "fee_paid": 2700000, - "status": "valid" - }, - { - "tx_index": 1927909, - "tx_hash": "5556fd2b0802cf3bc0abd5001ecbac3adbc5b7c5c46a145a78daeef358c308de", - "block_index": 725654, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "WHITERUSSIAN", - "quantity_per_unit": 1, - "fee_paid": 3220000, - "status": "valid" - }, - { - "tx_index": 1983693, - "tx_hash": "cda646285cc63f758d19b5403070f23e2a6e4b34eb3b86b63a0f56f971345657", - "block_index": 730568, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "A4520591452211866149", - "quantity_per_unit": 1, - "fee_paid": 4040000, - "status": "valid" - }, - { - "tx_index": 1983842, - "tx_hash": "e4b73dc974cc279b873b78e5dc4a347c08788b02143ae27aa0582f900289be10", - "block_index": 730588, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "NCSWIC", - "quantity_per_unit": 1, - "fee_paid": 4040000, - "status": "valid" - }, - { - "tx_index": 1996395, - "tx_hash": "b342feb1421df107010ad3c8ee2043ded802bdf6cd619862459da3d0f87d6a99", - "block_index": 731994, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "FUCKTHEFED", - "quantity_per_unit": 1, - "fee_paid": 4380000, - "status": "valid" - }, - { - "tx_index": 2035947, - "tx_hash": "02d715fd9e8b7bbc782b1b2d92a1b9ffae9326bfc88ba76c453c515ad7c8c2bc", - "block_index": 738763, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "HOLDTHELINE", - "quantity_per_unit": 1, - "fee_paid": 4940000, - "status": "valid" - }, - { - "tx_index": 2174481, - "tx_hash": "b935a06fc34d8fa4f0c526984085b1b12c78e899415e595b625f1bee84ce3709", - "block_index": 762733, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "EOXIXIZERO", - "quantity_per_unit": 1, - "fee_paid": 6500000, - "status": "valid" - }, - { - "tx_index": 2198534, - "tx_hash": "a063e9a745b9f6bc3201f72abff196de20ec106bcc71d820673d516ddbb3aa90", - "block_index": 767569, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "TRUMPCARDS", - "quantity_per_unit": 1, - "fee_paid": 6660000, - "status": "valid" - }, - { - "tx_index": 2704948, - "tx_hash": "437102ca4698f63a12e369f6168e3c7f5f8eef3e225395d515775673e33d39c1", - "block_index": 832745, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "FUCKYOUWAR", - "quantity_per_unit": 1, - "fee_paid": 6840000, - "status": "valid" - }, - { - "tx_index": 2704949, - "tx_hash": "7d3807cc58fa2d9751b2b0089bfa8fa86ef795821be6d8e9418ab3a819eba299", - "block_index": 832745, - "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", - "asset": "GMONEYPEPE", - "dividend_asset": "MEDICINEPEPE", - "quantity_per_unit": 1, - "fee_paid": 6840000, - "status": "valid" - } - ], - "/assets//issuances": [ - { + "success": true, + "result": { "tx_index": 2726605, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "msg_index": 0, "block_index": 840464, - "asset": "UNNEGOTIABLE", - "quantity": 1, - "divisible": 0, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "transfer": 0, - "callable": 0, - "call_date": 0, - "call_price": 0.0, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "fee_paid": 50000000, - "locked": 0, - "status": "valid", - "asset_longname": null, - "reset": 0 + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 } - ], - "/assets//sends": [ - { - "tx_index": 729, - "tx_hash": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", - "block_index": 280091, - "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1000000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 749, - "tx_hash": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", - "block_index": 280112, - "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 752, - "tx_hash": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", - "block_index": 280112, - "source": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", - "destination": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", - "asset": "XCP", - "quantity": 100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 755, - "tx_hash": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", - "block_index": 280114, - "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "asset": "XCP", - "quantity": 1100000000, - "status": "valid", - "msg_index": 0, - "memo": null - }, - { - "tx_index": 766, - "tx_hash": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", - "block_index": 280156, - "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", - "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + }, + "/addresses/
/balances": { + "success": true, + "result": [ + { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + } + ] + }, + "/addresses/
/balances/": { + "success": true, + "result": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", "asset": "XCP", - "quantity": 1100000000, - "status": "valid", - "msg_index": 0, - "memo": null - } - ], - "/assets//dispensers": [ - { - "tx_index": 2726460, - "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", - "block_index": 839964, - "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", - "asset": "ERYKAHPEPU", - "give_quantity": 1, - "escrow_quantity": 25, - "satoshirate": 50000, - "status": 0, - "give_remaining": 25, - "oracle_address": null, - "last_status_tx_hash": null, - "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "dispense_count": 0 - } - ], - "/assets//dispensers/
": [ - { - "tx_index": 2726460, - "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", - "block_index": 839964, - "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", - "asset": "ERYKAHPEPU", - "give_quantity": 1, - "escrow_quantity": 25, - "satoshirate": 50000, - "status": 0, - "give_remaining": 25, - "oracle_address": null, - "last_status_tx_hash": null, - "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "dispense_count": 0 - } - ], - "/assets//holders": [ - { - "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", - "address_quantity": 63, - "escrow": null - }, - { - "address": "16yRstRXStVJJ1TN2S4DCWifyrCsetpma7", - "address_quantity": 1, - "escrow": null - }, - { - "address": "bc1qsvqsa9arwz30g2z0w09twzn8gz3380h36yxacs", - "address_quantity": 2, - "escrow": null - }, - { - "address": "17PnWBjHkekZKQPVagmTR5HiD51pN8WHC8", - "address_quantity": 1, - "escrow": null - }, - { - "address": "1FRxFpP9XoRsvZFVqGtt4fjjgKe1h5tbAh", - "address_quantity": 1, - "escrow": null - }, - { - "address": "1AdHg2q3M2rMFRgZyZ7RQyNHdwjSib7wSZ", - "address_quantity": 2, - "escrow": null - }, - { - "address": "1CTnziWXidHzY3qT8gwLa1ZxZK37A7HreR", - "address_quantity": 1, - "escrow": null - }, - { - "address": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", - "address_quantity": 25, - "escrow": null - } - ], - "/orders/": [ - { - "tx_index": 2724132, - "tx_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", - "block_index": 840381, - "source": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", - "give_asset": "PEPECASH", - "give_quantity": 6966600000000, - "give_remaining": 900000000000, - "get_asset": "XCP", - "get_quantity": 11076894000, - "get_remaining": 1431000000, - "expiration": 5000, - "expire_index": 843055, - "fee_required": 0, - "fee_required_remaining": 0, - "fee_provided": 4488, - "fee_provided_remaining": 4488, - "status": "open" - } - ], - "/orders//matches": [ - { - "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", - "tx0_index": 2724132, - "tx0_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", - "tx0_address": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", - "tx1_index": 2726591, - "tx1_hash": "5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", - "tx1_address": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", - "forward_asset": "PEPECASH", - "forward_quantity": 6066600000000, - "backward_asset": "XCP", - "backward_quantity": 9645894000, - "tx0_block_index": 838055, - "tx1_block_index": 840381, - "block_index": 840381, - "tx0_expiration": 5000, - "tx1_expiration": 8064, - "match_expire_index": 840401, - "fee_paid": 0, - "status": "completed" - } - ], - "/orders//btcpays": [ - { - "tx_index": 2719343, - "tx_hash": "6cfa7f31b43a46e5ad74a9db810bd6cac56235a8ebc73ec63d01b38ea7ea2414", - "block_index": 836188, - "source": "1NfJnJdAdmm2rJCFW54NsAKqqTTMexCNJ3", - "destination": "1BepkwAhEmEuEGF349XjmEUrRvoy9a7Biv", - "btc_amount": 4500000, - "order_match_id": "0a1387df82a8a7e9cec01c52c8fee01f6995c4e39dc5804e1d2bf40d9368f5c5_299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4", - "status": "valid" + "quantity": 104200000000 } - ], - "/bets/": [ - { - "tx_index": 15106, - "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", - "block_index": 304063, - "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", - "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "bet_type": 3, - "deadline": 1401828300, - "wager_quantity": 50000000, - "wager_remaining": 0, - "counterwager_quantity": 50000000, - "counterwager_remaining": 0, - "target_value": 1.0, - "leverage": 5040, - "expiration": 11, - "expire_index": 304073, - "fee_fraction_int": 1000000, - "status": "filled" - } - ], - "/bets//matches": [ - { - "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", - "tx0_index": 15106, - "tx0_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", - "tx0_address": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", - "tx1_index": 15108, - "tx1_hash": "cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", - "tx1_address": "1PTqJmRCMGs4qBEh2APAFSrBv95Uf1hfiD", - "tx0_bet_type": 3, - "tx1_bet_type": 2, - "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", - "initial_value": -1, - "deadline": 1401828300, - "target_value": 1.0, - "leverage": 5040, - "forward_quantity": 50000000, - "backward_quantity": 50000000, - "tx0_block_index": 304062, - "tx1_block_index": 304063, - "block_index": 306379, - "tx0_expiration": 11, - "tx1_expiration": 1459, - "match_expire_index": 304073, - "fee_fraction_int": 1000000, - "status": "expired" - } - ], - "/bets//resolutions": [ - { - "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", - "bet_match_type_id": 5, - "block_index": 401128, - "winner": "Equal", - "settled": null, - "bull_credit": null, - "bear_credit": null, - "escrow_less_fee": 2000000, - "fee": 0 - } - ], - "/burns": [ - { - "tx_index": 10, - "tx_hash": "41bbe1ec81da008a0e92758efb6084af3a6b6acf483983456ec797ee59c0e0f1", - "block_index": 278511, - "source": "12crRpZpn93PKTQ4WYxHMw4xi6ckh1CFR3", - "burned": 99900000, - "earned": 148024554545, - "status": "valid" - }, - { - "tx_index": 11, - "tx_hash": "c403a92281b568c7d428d942354d026594dc54ae35c21f53ecf5c918208c45de", - "block_index": 278511, - "source": "13UXh9dBEhA48gJiegJNodqe91PK88f4pW", - "burned": 99900000, - "earned": 148024554545, - "status": "valid" - }, - { - "tx_index": 12, - "tx_hash": "749ba1c2bd314f7b98e9cfb44575495b4ad2cf624901c65488fbc4f57a3dc0ac", - "block_index": 278511, - "source": "19Ht3rkW7JB9VuC7rsZEGZju96ujzchaZZ", - "burned": 99900000, - "earned": 148024554545, - "status": "valid" - }, - { - "tx_index": 13, - "tx_hash": "da330160b71138f9bda5e126df0d5d6248c0879d88e16255c74135274d8ebd27", - "block_index": 278511, - "source": "16Fu8Edsvxqixg6VnaHKPWE2TEsqQMwXfV", - "burned": 99900000, - "earned": 148024554545, - "status": "valid" - }, - { - "tx_index": 14, - "tx_hash": "66994176733650e77ae0cf34349f63e6538649f40f86d2719013d915bbb7701e", - "block_index": 278517, - "source": "14FFaRsfzYQxhZQv1YsMn65MvMLfJShgM8", - "burned": 99900000, - "earned": 147970063636, - "status": "valid" + }, + "/addresses/
/credits": { + "success": true, + "result": [ + { + "block_index": 830981, + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "calling_function": "send", + "event": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "tx_index": 2677412 + } + ] + }, + "/addresses/
/debits": { + "success": true, + "result": [ + { + "block_index": 836949, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 40000000000, + "action": "open dispenser", + "event": "53ed08176d3479f49986e9282293da85cebc03835b128d8e790ee587f9f1c750", + "tx_index": 2721524 + }, + { + "block_index": 840388, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 250000000000, + "action": "send", + "event": "bc54968ba7d0a59a47b276602e2dbdcf01b14009742e0d7b50272cbae529a9a4", + "tx_index": 2726594 + } + ] + }, + "/addresses/
/bets": { + "success": true, + "result": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + }, + { + "tx_index": 61338, + "tx_hash": "0fcc7f5190c028f6c5534554d10ec5b4a9246d63826421cd58be2d572d11f088", + "block_index": 320704, + "source": "1Ew38GxczvV1KxjzZsq9f8UuRzHkHQrL5C", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 2, + "deadline": 1410728400, + "wager_quantity": 1000000, + "wager_remaining": 0, + "counterwager_quantity": 1999991, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 13, + "expire_index": 320715, + "fee_fraction_int": 1000000, + "status": "filled" + } + ] + }, + "/addresses/
/broadcasts": { + "success": true, + "result": [ + { + "tx_index": 15055, + "tx_hash": "774887e555a6ae5a8c058ebc0185058307977f01a2d4d326e71f37d6dd977154", + "block_index": 304048, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1401815290, + "value": -1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + }, + { + "tx_index": 61477, + "tx_hash": "5d49993bec727622c7b41c84e2b1e65c368f33390d633d217131ffcc5b592f0d", + "block_index": 320718, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1410732503, + "value": 1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + } + ] + }, + "/addresses/
/burns": { + "success": true, + "result": [ + { + "tx_index": 3070, + "tx_hash": "4560d0e3d04927108b615ab106040489aca9c4aceedcf69d2b71f63b3139c7ae", + "block_index": 283810, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "burned": 10000000, + "earned": 10000000000, + "status": "valid" + } + ] + }, + "/addresses/
/sends": { + "success": true, + "result": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/addresses/
/receives": { + "success": true, + "result": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/addresses/
/sends/": { + "success": true, + "result": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/addresses/
/receives/": { + "success": true, + "result": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/addresses/
/dispensers": { + "success": true, + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + }, + "/addresses/
/dispensers/": { + "success": true, + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + }, + "/addresses/
/sweeps": { + "success": true, + "result": [ + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ] + }, + "/addresses/
/compose/btcpay": { + "success": true, + "result": { + "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", + "params": { + "source": "bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l", + "order_match_id": "e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2" + }, + "name": "btcpay" } - ], - "/dispensers/": [ - { - "tx_index": 2536311, - "tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a", - "block_index": 840322, - "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", - "asset": "FLOCK", - "give_quantity": 10000000000, - "escrow_quantity": 250000000000, - "satoshirate": 330000, - "status": 0, - "give_remaining": 140000000000, - "oracle_address": null, - "last_status_tx_hash": null, - "origin": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", - "dispense_count": 2, - "asset_longname": null + }, + "/assets": { + "success": true, + "result": [ + { + "asset": "A100000000000000000", + "asset_longname": null + }, + { + "asset": "A1000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000001", + "asset_longname": null + }, + { + "asset": "A10000000000000000002", + "asset_longname": null + } + ] + }, + "/assets/": { + "success": true, + "result": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "owner": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "divisible": false, + "locked": false, + "supply": 1, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "holder_count": 1 } - ], - "/dispensers//dispenses": [ - { - "tx_index": 2610745, - "dispense_index": 0, - "tx_hash": "8c95cc6afc8fd466c784fd1c02749c585988999bbc66251b944c443dc31af757", - "block_index": 821450, - "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", - "destination": "1FKYM1CP9RfttJhNG8HTNQdE2uV3YvwbRB", - "asset": "FLOCK", - "dispense_quantity": 20000000000, - "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" - }, - { - "tx_index": 2726580, - "dispense_index": 0, - "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", - "block_index": 840322, - "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", - "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", - "asset": "FLOCK", - "dispense_quantity": 90000000000, - "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + }, + "/assets//balances": { + "success": true, + "result": [ + { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1 + } + ] + }, + "/assets//balances/
": { + "success": true, + "result": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 } - ], - "/events": [ - { - "event_index": 10665092, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665091, - "event": "ENHANCED_SEND", - "bindings": { - "asset": "THOTHPEPE", - "block_index": 744232, - "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "memo": null, + }, + "/assets//orders": { + "success": true, + "result": [ + { + "tx_index": 825373, + "tx_hash": "0129611a0aece52adddf6d929e75c703baa9cdcb7e4ce887aa859f9640aa9640", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 400000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 2225134, + "tx_hash": "5b6e0c741d765ebd883dc16eecfb5c340c52865cabf297ca2c1432437c1348b7", + "block_index": 772817, + "source": "1FnM7akSCD8G3fRQHCUEXRCfL35gptsPZB", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 80800000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 777817, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 5544, + "fee_provided_remaining": 5544, + "status": "filled" + }, + { + "tx_index": 1946026, + "tx_hash": "75dc6ee1f67317e674ef33b617d3a9839ee53bf4a2e8274c88d6202d4d89b59a", + "block_index": 727444, + "source": "1GotRejB6XsGgMsM79TvcypeanDJRJbMtg", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 70000000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732381, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 2202451, + "tx_hash": "77f568fc6604dbe209d2ea1b0158d7de20723c0178107eb570f4f2a719b0d7c7", + "block_index": 772817, + "source": "184gKLQTtQU29LXbxbYJkUV4if9SmW6v2d", + "give_asset": "XCP", + "give_quantity": 80800000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 773300, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 825411, + "tx_hash": "7b2369f40078f4d98a3d3a7733315a1c4efd7977c75f7066dd447d5c7eed7f20", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 300000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 825403, + "tx_hash": "7e1abf6ad57eb61227015fc7a333da034b4dd2f1c4e23cf106864b60a20feef7", + "block_index": 455460, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 200000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456460, + "fee_required": 20000, + "fee_required_remaining": 20000, + "fee_provided": 50766, + "fee_provided_remaining": 50766, + "status": "filled" + }, + { + "tx_index": 825370, + "tx_hash": "8e4d324407b62de773af53f8f7a556882ac82a217c216491a28072f293918fe6", + "block_index": 455457, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 100000000000, + "get_remaining": -1100000000, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 75791, + "fee_provided_remaining": 75791, + "status": "filled" + }, + { + "tx_index": 825413, + "tx_hash": "927878fa98edb6d24310c45254c324f3d5a7f625e2a3a0e7fd1e749b49493750", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 400000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 1946587, + "tx_hash": "b747f290cbbad6faa1c1c05d5c6d001b5a3ef487027bb0d4eefcdc9f6e865c39", + "block_index": 727444, + "source": "1AtcSh7uxenQ6AR5xqr6agAegWRUF5N4uh", + "give_asset": "XCP", + "give_quantity": 70000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732444, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 792, + "fee_provided_remaining": 792, + "status": "filled" + }, + { + "tx_index": 825371, + "tx_hash": "b83c96217214decb6316c3619bc88a3471d17e46eb3708406c8f878dedd61610", + "block_index": 455460, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 200000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 825372, + "tx_hash": "e32154f8ade796df0b121604de140703d062d22d1e82e77e629e6096668c812f", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 300000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + } + ] + }, + "/assets//credits": { + "success": true, + "result": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ] + }, + "/assets//debits": { + "success": true, + "result": [ + { + "block_index": 280091, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "action": "send", + "event": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "tx_index": 729 + }, + { + "block_index": 280112, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "tx_index": 749 + }, + { + "block_index": 280112, + "address": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "tx_index": 752 + }, + { + "block_index": 280114, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "tx_index": 755 + }, + { + "block_index": 280156, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "tx_index": 766 + } + ] + }, + "/assets//dividends": { + "success": true, + "result": [ + { + "tx_index": 1914456, + "tx_hash": "30760e413947ebdc80ed7a5ada1bd4466800b87e9976bbe811ad4e2b46546359", + "block_index": 724381, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "ENDTHEFED", + "quantity_per_unit": 1, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1915246, + "tx_hash": "827794cbab3299f80a5b8b8cb8ec29ec3aee1373f7da2c05a156bed902bf4684", + "block_index": 724479, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPDANCING", + "quantity_per_unit": 100, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1920208, + "tx_hash": "7014f1e259531ba9632ca5000c35df5bd47f237318e48955900453ce9c07e917", + "block_index": 724931, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "CTRWOJACK", + "quantity_per_unit": 1111, + "fee_paid": 2700000, + "status": "valid" + }, + { + "tx_index": 1927909, + "tx_hash": "5556fd2b0802cf3bc0abd5001ecbac3adbc5b7c5c46a145a78daeef358c308de", + "block_index": 725654, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "WHITERUSSIAN", + "quantity_per_unit": 1, + "fee_paid": 3220000, + "status": "valid" + }, + { + "tx_index": 1983693, + "tx_hash": "cda646285cc63f758d19b5403070f23e2a6e4b34eb3b86b63a0f56f971345657", + "block_index": 730568, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "A4520591452211866149", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1983842, + "tx_hash": "e4b73dc974cc279b873b78e5dc4a347c08788b02143ae27aa0582f900289be10", + "block_index": 730588, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "NCSWIC", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1996395, + "tx_hash": "b342feb1421df107010ad3c8ee2043ded802bdf6cd619862459da3d0f87d6a99", + "block_index": 731994, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKTHEFED", + "quantity_per_unit": 1, + "fee_paid": 4380000, + "status": "valid" + }, + { + "tx_index": 2035947, + "tx_hash": "02d715fd9e8b7bbc782b1b2d92a1b9ffae9326bfc88ba76c453c515ad7c8c2bc", + "block_index": 738763, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "HOLDTHELINE", + "quantity_per_unit": 1, + "fee_paid": 4940000, + "status": "valid" + }, + { + "tx_index": 2174481, + "tx_hash": "b935a06fc34d8fa4f0c526984085b1b12c78e899415e595b625f1bee84ce3709", + "block_index": 762733, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "EOXIXIZERO", + "quantity_per_unit": 1, + "fee_paid": 6500000, + "status": "valid" + }, + { + "tx_index": 2198534, + "tx_hash": "a063e9a745b9f6bc3201f72abff196de20ec106bcc71d820673d516ddbb3aa90", + "block_index": 767569, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPCARDS", + "quantity_per_unit": 1, + "fee_paid": 6660000, + "status": "valid" + }, + { + "tx_index": 2704948, + "tx_hash": "437102ca4698f63a12e369f6168e3c7f5f8eef3e225395d515775673e33d39c1", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKYOUWAR", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + }, + { + "tx_index": 2704949, + "tx_hash": "7d3807cc58fa2d9751b2b0089bfa8fa86ef795821be6d8e9418ab3a819eba299", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "MEDICINEPEPE", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + } + ] + }, + "/assets//issuances": { + "success": true, + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", "quantity": 1, - "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, "status": "valid", - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665090, - "event": "CREDIT", - "bindings": { - "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "asset": "THOTHPEPE", + "asset_longname": null, + "reset": 0 + } + ] + }, + "/assets//sends": { + "success": true, + "result": [ + { + "tx_index": 729, + "tx_hash": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "block_index": 280091, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 749, + "tx_hash": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "block_index": 280112, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 752, + "tx_hash": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "block_index": 280112, + "source": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "destination": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 755, + "tx_hash": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "block_index": 280114, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 766, + "tx_hash": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "block_index": 280156, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + }, + "/assets//dispensers": { + "success": true, + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + }, + "/assets//dispensers/
": { + "success": true, + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + }, + "/assets//holders": { + "success": true, + "result": [ + { + "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "address_quantity": 63, + "escrow": null + }, + { + "address": "16yRstRXStVJJ1TN2S4DCWifyrCsetpma7", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qsvqsa9arwz30g2z0w09twzn8gz3380h36yxacs", + "address_quantity": 2, + "escrow": null + }, + { + "address": "17PnWBjHkekZKQPVagmTR5HiD51pN8WHC8", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1FRxFpP9XoRsvZFVqGtt4fjjgKe1h5tbAh", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1AdHg2q3M2rMFRgZyZ7RQyNHdwjSib7wSZ", + "address_quantity": 2, + "escrow": null + }, + { + "address": "1CTnziWXidHzY3qT8gwLa1ZxZK37A7HreR", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "address_quantity": 25, + "escrow": null + } + ] + }, + "/orders/": { + "success": true, + "result": [ + { + "tx_index": 2724132, + "tx_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "block_index": 840381, + "source": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "give_asset": "PEPECASH", + "give_quantity": 6966600000000, + "give_remaining": 900000000000, + "get_asset": "XCP", + "get_quantity": 11076894000, + "get_remaining": 1431000000, + "expiration": 5000, + "expire_index": 843055, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 4488, + "fee_provided_remaining": 4488, + "status": "open" + } + ] + }, + "/orders//matches": { + "success": true, + "result": [ + { + "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx0_index": 2724132, + "tx0_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "tx0_address": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "tx1_index": 2726591, + "tx1_hash": "5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx1_address": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "forward_asset": "PEPECASH", + "forward_quantity": 6066600000000, + "backward_asset": "XCP", + "backward_quantity": 9645894000, + "tx0_block_index": 838055, + "tx1_block_index": 840381, + "block_index": 840381, + "tx0_expiration": 5000, + "tx1_expiration": 8064, + "match_expire_index": 840401, + "fee_paid": 0, + "status": "completed" + } + ] + }, + "/orders//btcpays": { + "success": true, + "result": [ + { + "tx_index": 2719343, + "tx_hash": "6cfa7f31b43a46e5ad74a9db810bd6cac56235a8ebc73ec63d01b38ea7ea2414", + "block_index": 836188, + "source": "1NfJnJdAdmm2rJCFW54NsAKqqTTMexCNJ3", + "destination": "1BepkwAhEmEuEGF349XjmEUrRvoy9a7Biv", + "btc_amount": 4500000, + "order_match_id": "0a1387df82a8a7e9cec01c52c8fee01f6995c4e39dc5804e1d2bf40d9368f5c5_299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4", + "status": "valid" + } + ] + }, + "/bets/": { + "success": true, + "result": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + } + ] + }, + "/bets//matches": { + "success": true, + "result": [ + { + "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx0_index": 15106, + "tx0_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "tx0_address": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "tx1_index": 15108, + "tx1_hash": "cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx1_address": "1PTqJmRCMGs4qBEh2APAFSrBv95Uf1hfiD", + "tx0_bet_type": 3, + "tx1_bet_type": 2, + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "initial_value": -1, + "deadline": 1401828300, + "target_value": 1.0, + "leverage": 5040, + "forward_quantity": 50000000, + "backward_quantity": 50000000, + "tx0_block_index": 304062, + "tx1_block_index": 304063, + "block_index": 306379, + "tx0_expiration": 11, + "tx1_expiration": 1459, + "match_expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "expired" + } + ] + }, + "/bets//resolutions": { + "success": true, + "result": [ + { + "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", + "bet_match_type_id": 5, + "block_index": 401128, + "winner": "Equal", + "settled": null, + "bull_credit": null, + "bear_credit": null, + "escrow_less_fee": 2000000, + "fee": 0 + } + ] + }, + "/burns": { + "success": true, + "result": [ + { + "tx_index": 10, + "tx_hash": "41bbe1ec81da008a0e92758efb6084af3a6b6acf483983456ec797ee59c0e0f1", + "block_index": 278511, + "source": "12crRpZpn93PKTQ4WYxHMw4xi6ckh1CFR3", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 11, + "tx_hash": "c403a92281b568c7d428d942354d026594dc54ae35c21f53ecf5c918208c45de", + "block_index": 278511, + "source": "13UXh9dBEhA48gJiegJNodqe91PK88f4pW", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 12, + "tx_hash": "749ba1c2bd314f7b98e9cfb44575495b4ad2cf624901c65488fbc4f57a3dc0ac", + "block_index": 278511, + "source": "19Ht3rkW7JB9VuC7rsZEGZju96ujzchaZZ", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 13, + "tx_hash": "da330160b71138f9bda5e126df0d5d6248c0879d88e16255c74135274d8ebd27", + "block_index": 278511, + "source": "16Fu8Edsvxqixg6VnaHKPWE2TEsqQMwXfV", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 14, + "tx_hash": "66994176733650e77ae0cf34349f63e6538649f40f86d2719013d915bbb7701e", + "block_index": 278517, + "source": "14FFaRsfzYQxhZQv1YsMn65MvMLfJShgM8", + "burned": 99900000, + "earned": 147970063636, + "status": "valid" + } + ] + }, + "/dispensers/": { + "success": true, + "result": [ + { + "tx_index": 2536311, + "tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "asset": "FLOCK", + "give_quantity": 10000000000, + "escrow_quantity": 250000000000, + "satoshirate": 330000, + "status": 0, + "give_remaining": 140000000000, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "dispense_count": 2, + "asset_longname": null + } + ] + }, + "/dispensers//dispenses": { + "success": true, + "result": [ + { + "tx_index": 2610745, + "dispense_index": 0, + "tx_hash": "8c95cc6afc8fd466c784fd1c02749c585988999bbc66251b944c443dc31af757", + "block_index": 821450, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "1FKYM1CP9RfttJhNG8HTNQdE2uV3YvwbRB", + "asset": "FLOCK", + "dispense_quantity": 20000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + }, + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ] + }, + "/events": { + "success": true, + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, "block_index": 744232, - "calling_function": "send", - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665089, - "event": "DEBIT", - "bindings": { - "action": "send", - "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", - "asset": "THOTHPEPE", + "timestamp": 1712256340 + }, + { + "event_index": 10665091, + "event": "ENHANCED_SEND", + "bindings": { + "asset": "THOTHPEPE", + "block_index": 744232, + "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "memo": null, + "quantity": 1, + "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "status": "valid", + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, "block_index": 744232, - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665088, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", - "tx_index": 2056159 - }, - "block_index": 744232, - "timestamp": 1712256340 - } - ], - "/events/": [ - { - "event_index": 10665092, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - } - ], - "/events/counts": [ - { - "event": "ASSET_CREATION", - "event_count": 235858 - }, - { - "event": "ASSET_DESTRUCTION", - "event_count": 11141 - }, - { - "event": "ASSET_DIVIDEND", - "event_count": 4092 - }, - { - "event": "ASSET_ISSUANCE", - "event_count": 322676 - }, - { - "event": "ASSET_TRANSFER", - "event_count": 10630 - }, - { - "event": "BET_EXPIRATION", - "event_count": 588 - }, - { - "event": "BET_MATCH", - "event_count": 397 - }, - { - "event": "BET_MATCH_EXPIRATION", - "event_count": 9 - }, - { - "event": "BET_MATCH_RESOLUTON", - "event_count": 387 - }, - { - "event": "BET_MATCH_UPDATE", - "event_count": 397 - }, - { - "event": "BET_UPDATE", - "event_count": 1474 - }, - { - "event": "BLOCK_PARSED", - "event_count": 562278 - }, - { - "event": "BROADCAST", - "event_count": 106518 - }, - { - "event": "BTC_PAY", - "event_count": 2921 - }, - { - "event": "BURN", - "event_count": 2576 - }, - { - "event": "CANCEL_BET", - "event_count": 101 - }, - { - "event": "CANCEL_ORDER", - "event_count": 80168 - }, - { - "event": "CREDIT", - "event_count": 3657192 - }, - { - "event": "DEBIT", - "event_count": 2615306 - }, - { - "event": "DISPENSE", - "event_count": 190873 - }, - { - "event": "DISPENSER_UPDATE", - "event_count": 228954 - }, - { - "event": "ENHANCED_SEND", - "event_count": 538418 - }, - { - "event": "MPMA_SEND", - "event_count": 279142 - }, - { - "event": "NEW_BLOCK", - "event_count": 1906 - }, - { - "event": "NEW_TRANSACTION", - "event_count": 4485 - }, - { - "event": "NEW_TRANSACTION_OUTPUT", - "event_count": 596 - }, - { - "event": "OPEN_BET", - "event_count": 1149 - }, - { - "event": "OPEN_DISPENSER", - "event_count": 88228 - }, - { - "event": "OPEN_ORDER", - "event_count": 530117 - }, - { - "event": "OPEN_RPS", - "event_count": 266 - }, - { - "event": "ORDER_EXPIRATION", - "event_count": 195962 - }, - { - "event": "ORDER_FILLED", - "event_count": 805 - }, - { - "event": "ORDER_MATCH", - "event_count": 209415 - }, - { - "event": "ORDER_MATCH_EXPIRATION", - "event_count": 20860 - }, - { - "event": "ORDER_MATCH_UPDATE", - "event_count": 23689 - }, - { - "event": "ORDER_UPDATE", - "event_count": 732640 - }, - { - "event": "REFILL_DISPENSER", - "event_count": 187 - }, - { - "event": "RESET_ISSUANCE", - "event_count": 454 - }, - { - "event": "RPS_EXPIRATION", - "event_count": 59 - }, - { - "event": "RPS_MATCH", - "event_count": 171 - }, - { - "event": "RPS_MATCH_EXPIRATION", - "event_count": 145 - }, - { - "event": "RPS_MATCH_UPDATE", - "event_count": 271 - }, - { - "event": "RPS_RESOLVE", - "event_count": 129 - }, - { - "event": "RPS_UPDATE", - "event_count": 540 - }, - { - "event": "SEND", - "event_count": 805983 - }, - { - "event": "SWEEP", - "event_count": 1018 - }, - { - "event": "TRANSACTION_PARSED", - "event_count": 2723789 - } - ], - "/events/": [ - { - "event_index": 10665090, - "event": "CREDIT", - "bindings": { - "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "asset": "THOTHPEPE", + "timestamp": 1712256340 + }, + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, "block_index": 744232, - "calling_function": "send", - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665085, - "event": "CREDIT", - "bindings": { - "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", - "asset": "XCP", + "timestamp": 1712256340 + }, + { + "event_index": 10665089, + "event": "DEBIT", + "bindings": { + "action": "send", + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "THOTHPEPE", + "block_index": 744232, + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, "block_index": 744232, - "calling_function": "dispense", - "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", - "quantity": 10000000000, - "tx_index": 2056159 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665082, - "event": "CREDIT", - "bindings": { - "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", - "asset": "FREEDOMKEK", + "timestamp": 1712256340 + }, + { + "event_index": 10665088, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "tx_index": 2056159 + }, "block_index": 744232, - "calling_function": "send", - "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", - "quantity": 1, - "tx_index": 2056158 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665078, - "event": "CREDIT", - "bindings": { - "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", - "asset": "PEPEFRIDAY", + "timestamp": 1712256340 + } + ] + }, + "/events/": { + "success": true, + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, "block_index": 744232, - "calling_function": "send", - "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", - "quantity": 1, - "tx_index": 2056157 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665074, - "event": "CREDIT", - "bindings": { - "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", - "asset": "PEPEFRIDAY", + "timestamp": 1712256340 + } + ] + }, + "/events/counts": { + "success": true, + "result": [ + { + "event": "ASSET_CREATION", + "event_count": 235860 + }, + { + "event": "ASSET_DESTRUCTION", + "event_count": 11141 + }, + { + "event": "ASSET_DIVIDEND", + "event_count": 4092 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 322678 + }, + { + "event": "ASSET_TRANSFER", + "event_count": 10639 + }, + { + "event": "BET_EXPIRATION", + "event_count": 588 + }, + { + "event": "BET_MATCH", + "event_count": 397 + }, + { + "event": "BET_MATCH_EXPIRATION", + "event_count": 9 + }, + { + "event": "BET_MATCH_RESOLUTON", + "event_count": 387 + }, + { + "event": "BET_MATCH_UPDATE", + "event_count": 397 + }, + { + "event": "BET_UPDATE", + "event_count": 1474 + }, + { + "event": "BLOCK_PARSED", + "event_count": 562364 + }, + { + "event": "BROADCAST", + "event_count": 106518 + }, + { + "event": "BTC_PAY", + "event_count": 2921 + }, + { + "event": "BURN", + "event_count": 2576 + }, + { + "event": "CANCEL_BET", + "event_count": 101 + }, + { + "event": "CANCEL_ORDER", + "event_count": 80168 + }, + { + "event": "CREDIT", + "event_count": 3659293 + }, + { + "event": "DEBIT", + "event_count": 2617404 + }, + { + "event": "DISPENSE", + "event_count": 190873 + }, + { + "event": "DISPENSER_UPDATE", + "event_count": 228954 + }, + { + "event": "ENHANCED_SEND", + "event_count": 538426 + }, + { + "event": "MPMA_SEND", + "event_count": 279142 + }, + { + "event": "NEW_BLOCK", + "event_count": 1992 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 4498 + }, + { + "event": "NEW_TRANSACTION_OUTPUT", + "event_count": 596 + }, + { + "event": "OPEN_BET", + "event_count": 1149 + }, + { + "event": "OPEN_DISPENSER", + "event_count": 88229 + }, + { + "event": "OPEN_ORDER", + "event_count": 530117 + }, + { + "event": "OPEN_RPS", + "event_count": 266 + }, + { + "event": "ORDER_EXPIRATION", + "event_count": 195968 + }, + { + "event": "ORDER_FILLED", + "event_count": 805 + }, + { + "event": "ORDER_MATCH", + "event_count": 209415 + }, + { + "event": "ORDER_MATCH_EXPIRATION", + "event_count": 20860 + }, + { + "event": "ORDER_MATCH_UPDATE", + "event_count": 23689 + }, + { + "event": "ORDER_UPDATE", + "event_count": 732646 + }, + { + "event": "REFILL_DISPENSER", + "event_count": 187 + }, + { + "event": "RESET_ISSUANCE", + "event_count": 454 + }, + { + "event": "RPS_EXPIRATION", + "event_count": 59 + }, + { + "event": "RPS_MATCH", + "event_count": 171 + }, + { + "event": "RPS_MATCH_EXPIRATION", + "event_count": 145 + }, + { + "event": "RPS_MATCH_UPDATE", + "event_count": 271 + }, + { + "event": "RPS_RESOLVE", + "event_count": 129 + }, + { + "event": "RPS_UPDATE", + "event_count": 540 + }, + { + "event": "SEND", + "event_count": 805983 + }, + { + "event": "SWEEP", + "event_count": 1020 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 2723802 + } + ] + }, + "/events/": { + "success": true, + "result": [ + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, "block_index": 744232, - "calling_function": "send", - "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", - "quantity": 2, - "tx_index": 2056156 + "timestamp": 1712256340 + }, + { + "event_index": 10665085, + "event": "CREDIT", + "bindings": { + "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", + "asset": "XCP", + "block_index": 744232, + "calling_function": "dispense", + "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "quantity": 10000000000, + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 }, - "block_index": 744232, - "timestamp": 1712256340 + { + "event_index": 10665082, + "event": "CREDIT", + "bindings": { + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "FREEDOMKEK", + "block_index": 744232, + "calling_function": "send", + "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", + "quantity": 1, + "tx_index": 2056158 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665078, + "event": "CREDIT", + "bindings": { + "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", + "quantity": 1, + "tx_index": 2056157 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665074, + "event": "CREDIT", + "bindings": { + "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", + "quantity": 2, + "tx_index": 2056156 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + }, + "/addresses/
/compose/broadcast": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "timestamp": 4003903983, + "value": 100.0, + "fee_fraction": 0.05, + "text": "\"Hello, world!\"" + }, + "name": "broadcast" } - ], + }, "/healthz": { "data": "Healthy", "success": true }, - "/mempool/events": [], - "/mempool/events/": [], - "/blocks/": { - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", - "difficulty": 86388558925171.02, - "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", - "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", - "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + "/addresses/
/compose/bet": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "feed_address": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "bet_type": 2, + "deadline": 3000000000, + "wager_quantity": 1000, + "counterwager_quantity": 1000, + "target_value": 1000, + "leverage": 5040, + "expiration": 100 + }, + "name": "bet" + } }, - "/addresses/
/compose/broadcast": { - "rawtransaction": "01000000019a753a6b8be54cdee2acd408f6199e29092c8c32e13912865a68da8a0d9ae065010000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88acffffffff0200000000000000002b6a2988dab644080b5ae67a54ba74394f5f94b41e385fc911aa5c810c5f98e6f6b17518ca736e94353de8e82a282b00000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac00000000", - "params": { - "source": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", - "timestamp": 4003903983, - "value": 100.0, - "fee_fraction": 0.05, - "text": "\"Hello, world!\"" - }, - "name": "broadcast" + "/addresses/
/compose/burn": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "quantity": 1000, + "overburn": false + }, + "name": "burn" + } }, - "/addresses/
/compose/btcpay": { - "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db70bc758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", - "params": { - "source": "bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l", - "order_match_id": "e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2" - }, - "name": "btcpay" + "/mempool/events": { + "success": true, + "result": [] + }, + "/mempool/events/": { + "success": true, + "result": [] + }, + "/addresses/
/compose/cancel": { + "success": true, + "result": { + "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", + "params": { + "source": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "offer_hash": "8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a" + }, + "name": "cancel" + } + }, + "/addresses/
/compose/destroy": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCP", + "quantity": 1000, + "tag": "\"bugs!\"" + }, + "name": "destroy" + } + }, + "/addresses/
/compose/dispenser": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCP", + "give_quantity": 1000, + "escrow_quantity": 1000, + "mainchainrate": 100, + "status": 0, + "open_address": null, + "oracle_address": null + }, + "name": "dispenser" + } + }, + "/addresses/
/compose/dividend": { + "success": true, + "result": { + "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", + "params": { + "source": "1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD", + "quantity_per_unit": 1, + "asset": "PEPECASH", + "dividend_asset": "XCP" + }, + "name": "dividend" + } + }, + "/addresses/
/compose/issuance": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCPTEST", + "quantity": 1000, + "transfer_destination": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "divisible": true, + "lock": false, + "reset": false, + "description": null + }, + "name": "issuance" + } + }, + "/addresses/
/compose/mpma": { + "success": true, + "result": { + "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", + "params": { + "source": "1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6", + "asset_dest_quant_list": [ + [ + "BAABAABLKSHP", + "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + 1 + ], + [ + "BADHAIRDAY", + "1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD", + 2 + ], + [ + "BADWOJAK", + "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + 3 + ] + ], + "memo": "\"Hello, world!\"", + "memo_is_hex": false + }, + "name": "mpma" + } + }, + "/addresses/
/compose/order": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "give_asset": "XCP", + "give_quantity": 1000, + "get_asset": "PEPECASH", + "get_quantity": 1000, + "expiration": 100, + "fee_required": 100 + }, + "name": "order" + } + }, + "/addresses/
/compose/send": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "destination": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "asset": "XCP", + "quantity": 1000, + "memo": null, + "memo_is_hex": false, + "use_enhanced_send": true + }, + "name": "send" + } + }, + "/addresses/
/compose/sweep": { + "success": true, + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "destination": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "flags": 7, + "memo": "FFFF" + }, + "name": "sweep" + } } } \ No newline at end of file From 0f71f80f22b646ebdaaa965a60fcfe2f022c69f7 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 12:22:49 +0200 Subject: [PATCH 086/128] Don't pass db if not needed; Mempool output examples; fixes --- .../counterpartycore/lib/api/api_server.py | 10 +- .../counterpartycore/lib/api/util.py | 4 + .../counterpartycore/lib/backend/__init__.py | 14 +-- .../counterpartycore/lib/ledger.py | 2 +- counterparty-core/tools/apicache.json | 107 ++++++++++++++++-- 5 files changed, 117 insertions(+), 20 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 93f9ed4bbe..6fd35e963c 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -17,6 +17,7 @@ ) from counterpartycore.lib.api.routes import ROUTES from counterpartycore.lib.api.util import ( + function_needs_db, get_backend_height, init_api_access_log, remove_rowids, @@ -104,9 +105,7 @@ def inject_headers(result, return_code=None): def prepare_args(route, **kwargs): function_args = dict(kwargs) - if "pass_all_args" in route and route["pass_all_args"]: - function_args = request.args | function_args - elif "args" in route: + if "args" in route: for arg in route["args"]: arg_name = arg["name"] if arg_name in function_args: @@ -148,7 +147,10 @@ def handle_route(**kwargs): except ValueError as e: return inject_headers({"success": False, "error": str(e)}, return_code=400) try: - result = route["function"](db, **function_args) + if function_needs_db(route["function"]): + result = route["function"](db, **function_args) + else: + result = route["function"](**function_args) except (exceptions.ComposeError, exceptions.UnpackError) as e: return inject_headers({"success": False, "error": str(e)}, return_code=503) except Exception as e: diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 99ee1760a2..9793d7d406 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -150,6 +150,10 @@ def get_args_description(function): return args +def function_needs_db(function): + return "db" in inspect.signature(function).parameters + + def prepare_route_args(function): args = [] function_args = inspect.signature(function).parameters diff --git a/counterparty-core/counterpartycore/lib/backend/__init__.py b/counterparty-core/counterpartycore/lib/backend/__init__.py index 4ed53f602a..6a9e0df37b 100644 --- a/counterparty-core/counterpartycore/lib/backend/__init__.py +++ b/counterparty-core/counterpartycore/lib/backend/__init__.py @@ -210,15 +210,15 @@ class MempoolError(Exception): pass -def get_unspent_txouts(source: str, unconfirmed: bool = False, unspent_tx_hash: str = None): +def get_unspent_txouts(address: str, unconfirmed: bool = False, unspent_tx_hash: str = None): """ Returns a list of unspent outputs for a specific address - :param source: The address to search for + :param address: The address to search for :param unconfirmed: Include unconfirmed transactions :param unspent_tx_hash: Filter by unspent_tx_hash """ - unspent = backend().get_unspent_txouts(source) + unspent = backend().get_unspent_txouts(address) # filter by unspent_tx_hash if unspent_tx_hash is not None: @@ -238,12 +238,12 @@ def get_unspent_txouts(source: str, unconfirmed: bool = False, unspent_tx_hash: return unspent -def search_raw_transactions(address, unconfirmed: bool = True, only_tx_hashes: bool = False): +def search_raw_transactions(address: str, unconfirmed: bool = True, only_tx_hashes: bool = False): """ Returns all transactions involving a given address - :param address: The address to search for - :param unconfirmed: Include unconfirmed transactions - :param only_tx_hashes: Return only the tx hashes + :param address: The address to search for (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param unconfirmed: Include unconfirmed transactions (e.g. True) + :param only_tx_hashes: Return only the tx hashes (e.g. True) """ return backend().search_raw_transactions(address, unconfirmed, only_tx_hashes) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 57280f5709..4926569c04 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -183,7 +183,7 @@ def get_all_mempool_events(db): def get_mempool_events_by_event(db, event: str): """ Returns the mempool events filtered by event name - :param str event: The event to return (e.g. CREDIT) + :param str event: The event to return (e.g. OPEN_ORDER) """ return get_mempool_events(db, event_name=event) diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 58ae122e38..e2e34ee8e3 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -2041,14 +2041,6 @@ "name": "burn" } }, - "/mempool/events": { - "success": true, - "result": [] - }, - "/mempool/events/": { - "success": true, - "result": [] - }, "/addresses/
/compose/cancel": { "success": true, "result": { @@ -2193,5 +2185,104 @@ }, "name": "sweep" } + }, + "/mempool/events": { + "success": true, + "result": [ + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "mempool", + "block_index": 9999999, + "block_time": 1713952590, + "btc_amount": 0, + "data": "0200454ceacf416ccf0000000000000001005461639d06ebc42d541b54b1c5525543ae4d6db3", + "destination": "", + "fee": 9900, + "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "tx_index": 2726767 + }, + "timestamp": 1713952691 + }, + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "ENHANCED_SEND", + "bindings": { + "asset": "FIERCERABBIT", + "destination": "18hARq2fFJxiypHSnZ8yLcbPNpUfaozD8U", + "memo": null, + "quantity": 1, + "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e" + }, + "timestamp": 1713952691 + }, + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "tx_index": 2726767 + }, + "timestamp": 1713952691 + } + ] + }, + "/mempool/events/": { + "success": true, + "result": [ + { + "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", + "event": "OPEN_ORDER", + "bindings": { + "expiration": 5000, + "expire_index": 10004999, + "fee_provided": 5016, + "fee_provided_remaining": 5016, + "fee_required": 0, + "fee_required_remaining": 0, + "get_asset": "XCP", + "get_quantity": 3300000000, + "get_remaining": 3300000000, + "give_asset": "PEPEPASSPORT", + "give_quantity": 100000000, + "give_remaining": 100000000, + "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", + "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81" + }, + "timestamp": 1713952690 + }, + { + "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6", + "event": "OPEN_ORDER", + "bindings": { + "expiration": 5000, + "expire_index": 10004999, + "fee_provided": 5016, + "fee_provided_remaining": 5016, + "fee_required": 0, + "fee_required_remaining": 0, + "get_asset": "XCP", + "get_quantity": 1185000000, + "get_remaining": 1185000000, + "give_asset": "FRATPEPE", + "give_quantity": 3, + "give_remaining": 3, + "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", + "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6" + }, + "timestamp": 1713952690 + } + ] + }, + "/backend/addresses/
/transactions": { + "success": false, + "result": { + "success": false, + "error": "Unknwon error" + } } } \ No newline at end of file From 92968bf35b1fb6f7fbe75515e7bd37d7ee6ecc6a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 12:35:52 +0200 Subject: [PATCH 087/128] All routes with output example --- .../counterpartycore/lib/api/util.py | 6 +- .../counterpartycore/lib/backend/__init__.py | 10 +- counterparty-core/tools/apicache.json | 185 +++++++++++++++++- 3 files changed, 190 insertions(+), 11 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 9793d7d406..fe67a9726b 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -96,7 +96,7 @@ def getrawtransactions(tx_hashes, verbose=False, skip_missing=False, _retry=0): def pubkeyhash_to_pubkey(address: str, provided_pubkeys: str = None): """ Get pubkey for an address. - :param address: Address to get pubkey for. + :param address: Address to get pubkey for. (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param provided_pubkeys: Comma separated list of provided pubkeys. """ if provided_pubkeys: @@ -109,8 +109,8 @@ def pubkeyhash_to_pubkey(address: str, provided_pubkeys: str = None): def get_raw_transaction(tx_hash: str, verbose: bool = False): """ Get a raw transaction from the blockchain - :param tx_hash: The transaction hash - :param verbose: Whether to return JSON output or raw hex + :param tx_hash: The transaction hash (e.g. 3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018) + :param verbose: Whether to return JSON output or raw hex (e.g. True) """ return backend.getrawtransaction(tx_hash, verbose=verbose) diff --git a/counterparty-core/counterpartycore/lib/backend/__init__.py b/counterparty-core/counterpartycore/lib/backend/__init__.py index 6a9e0df37b..802a8eced2 100644 --- a/counterparty-core/counterpartycore/lib/backend/__init__.py +++ b/counterparty-core/counterpartycore/lib/backend/__init__.py @@ -131,8 +131,8 @@ def fee_per_kb( ): """ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. - :param conf_target: Confirmation target in blocks (1 - 1008) - :param mode: The fee estimate mode. + :param conf_target: Confirmation target in blocks (1 - 1008) (e.g. 2) + :param mode: The fee estimate mode. (e.g. CONSERVATIVE) """ return backend().fee_per_kb(conf_target, mode, nblocks=None) @@ -213,7 +213,7 @@ class MempoolError(Exception): def get_unspent_txouts(address: str, unconfirmed: bool = False, unspent_tx_hash: str = None): """ Returns a list of unspent outputs for a specific address - :param address: The address to search for + :param address: The address to search for (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param unconfirmed: Include unconfirmed transactions :param unspent_tx_hash: Filter by unspent_tx_hash """ @@ -241,7 +241,7 @@ def get_unspent_txouts(address: str, unconfirmed: bool = False, unspent_tx_hash: def search_raw_transactions(address: str, unconfirmed: bool = True, only_tx_hashes: bool = False): """ Returns all transactions involving a given address - :param address: The address to search for (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) + :param address: The address to search for (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param unconfirmed: Include unconfirmed transactions (e.g. True) :param only_tx_hashes: Return only the tx hashes (e.g. True) """ @@ -251,7 +251,7 @@ def search_raw_transactions(address: str, unconfirmed: bool = True, only_tx_hash def get_oldest_tx(address: str, block_index: int = None): """ Get the oldest transaction for an address. - :param address: The address to search for. + :param address: The address to search for. (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param block_index: The block index to search from. """ return backend().get_oldest_tx(address, block_index=block_index) diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index e2e34ee8e3..94158f812e 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -2279,10 +2279,189 @@ ] }, "/backend/addresses/
/transactions": { - "success": false, + "success": true, + "result": [ + { + "tx_hash": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" + }, + { + "tx_hash": "7ec16c461e3ba2d3acae48fcc8f58c04fba9f307b00c391eab507337ddc0bf16" + }, + { + "tx_hash": "ad35f05767aadd39019122b4f4828ccb059b8121c07be6d36eb1e2ddbe9ac317" + }, + { + "tx_hash": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018" + }, + { + "tx_hash": "aba5810714aa6196fec5538a83bbc281077a84ef2cbce2045b4c9f3c4439f14f" + }, + { + "tx_hash": "23758832e0fc92a7ea303623b8f743219cb8e637e7e7ac9fb6f90641efac9379" + }, + { + "tx_hash": "98bef616ef265dd2f6004683e908d7df97e0c5f322cdf2fb2ebea9a9131cfa79" + }, + { + "tx_hash": "687b875d1dc472aa2fb994c5753c9b9b56e5c6fd1a6de18a92fcb3dc7ba8067e" + }, + { + "tx_hash": "ec97c11ff5cb318505ebe20d7aa3c033816824a79f9a49821ffb584ed7d6c78f" + }, + { + "tx_hash": "c732f0906eeada2113524c6652c17b2784780110bffd4333eb8f719ac0eff3be" + }, + { + "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + }, + { + "tx_hash": "a209e345549cffef6e2190b53ac0222afc965fd618843df5ccbd645a6a7999ee" + } + ] + }, + "/backend/addresses/
/transactions/oldest": { + "success": true, + "result": { + "block_index": 833187, + "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + } + }, + "/backend/addresses/
/utxos": { + "success": true, + "result": [ + { + "vout": 6, + "height": 833559, + "value": 34611, + "confirmations": 7083, + "amount": 0.00034611, + "txid": "98bef616ef265dd2f6004683e908d7df97e0c5f322cdf2fb2ebea9a9131cfa79" + }, + { + "vout": 0, + "height": 833187, + "value": 619481, + "confirmations": 7455, + "amount": 0.00619481, + "txid": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + }, + { + "vout": 0, + "height": 837379, + "value": 992721, + "confirmations": 3263, + "amount": 0.00992721, + "txid": "ad35f05767aadd39019122b4f4828ccb059b8121c07be6d36eb1e2ddbe9ac317" + }, + { + "vout": 0, + "height": 840640, + "value": 838185, + "confirmations": 2, + "amount": 0.00838185, + "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018" + }, + { + "vout": 0, + "height": 839421, + "value": 336973, + "confirmations": 1221, + "amount": 0.00336973, + "txid": "c732f0906eeada2113524c6652c17b2784780110bffd4333eb8f719ac0eff3be" + }, + { + "vout": 0, + "height": 839462, + "value": 78615, + "confirmations": 1180, + "amount": 0.00078615, + "txid": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" + }, + { + "vout": 0, + "height": 838442, + "value": 557283, + "confirmations": 2200, + "amount": 0.00557283, + "txid": "aba5810714aa6196fec5538a83bbc281077a84ef2cbce2045b4c9f3c4439f14f" + }, + { + "vout": 0, + "height": 838608, + "value": 77148, + "confirmations": 2034, + "amount": 0.00077148, + "txid": "ec97c11ff5cb318505ebe20d7aa3c033816824a79f9a49821ffb584ed7d6c78f" + }, + { + "vout": 0, + "height": 837402, + "value": 70501, + "confirmations": 3240, + "amount": 0.00070501, + "txid": "687b875d1dc472aa2fb994c5753c9b9b56e5c6fd1a6de18a92fcb3dc7ba8067e" + }, + { + "vout": 0, + "height": 839021, + "value": 12354, + "confirmations": 1621, + "amount": 0.00012354, + "txid": "23758832e0fc92a7ea303623b8f743219cb8e637e7e7ac9fb6f90641efac9379" + } + ] + }, + "/backend/addresses/
/pubkey": { + "success": true, + "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" + }, + "/backend/transactions/": { + "success": true, "result": { - "success": false, - "error": "Unknwon error" + "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", + "hash": "417c24d7a5539bc5b8496e26528382ac297a85a1c6b891b220f72712405ec300", + "version": 2, + "size": 195, + "vsize": 113, + "weight": 450, + "locktime": 0, + "vin": [ + { + "txid": "fc940430637d22a3d276bde8f7eb489760265cab642d8392f6017d73df94cd7a", + "vout": 2, + "scriptSig": { + "asm": "", + "hex": "" + }, + "txinwitness": [ + "3045022100e4a30e5c0e0f7a28dfcec566cda00d0775a4207744ed6f223a4234cbed87a8ac02205b2403279ba7d8235ea1e8b6497465b97b46f3b3066a58c326822a9b1c25b4a501", + "020e66cffeb4657b40a89063340cf7066030af3c6ce55744ed3570a7aecaa6b0da" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00838185, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 25f70b0f1512c1742d3301fe34370894c79127bb OP_EQUALVERIFY OP_CHECKSIG", + "desc": "addr(14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS)#68uhm9u9", + "hex": "76a91425f70b0f1512c1742d3301fe34370894c79127bb88ac", + "address": "14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS", + "type": "pubkeyhash" + } + } + ], + "hex": "020000000001017acd94df737d01f692832d64ab5c26609748ebf7e8bd76d2a3227d63300494fc0200000000ffffffff0129ca0c00000000001976a91425f70b0f1512c1742d3301fe34370894c79127bb88ac02483045022100e4a30e5c0e0f7a28dfcec566cda00d0775a4207744ed6f223a4234cbed87a8ac02205b2403279ba7d8235ea1e8b6497465b97b46f3b3066a58c326822a9b1c25b4a50121020e66cffeb4657b40a89063340cf7066030af3c6ce55744ed3570a7aecaa6b0da00000000", + "blockhash": "000000000000000000020f596ed481076b7754143284b47fc8d32642202e5f76", + "confirmations": 2, + "time": 1713951767, + "blocktime": 1713951767 } + }, + "/backend/estimatesmartfee": { + "success": true, + "result": 673559 } } \ No newline at end of file From 8a5837431c5bf9e2b61781297ca00158d8ca7e80 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 14:35:41 +0200 Subject: [PATCH 088/128] Refactor function to return data --- .../counterpartycore/lib/api/api_server.py | 87 ++++++------ .../counterpartycore/lib/api/routes.py | 2 +- .../counterpartycore/lib/api/util.py | 12 +- .../counterpartycore/test/api_v2_test.py | 2 +- counterparty-core/tools/apicache.json | 124 +++++++++--------- 5 files changed, 126 insertions(+), 101 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 6fd35e963c..e371584122 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -79,25 +79,32 @@ def api_root(): } -def inject_headers(result, return_code=None): - server_ready = ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT - 1 - json_result = {"success": True, "result": result} - http_code = 200 - if return_code: - http_code = return_code - elif not server_ready: - http_code = config.API_NOT_READY_HTTP_CODE - json_result["error"] = "Counterparty not ready" - if http_code != 200: - json_result["success"] = False - - if isinstance(result, flask.Response): - response = result - else: - response = flask.make_response(to_json(json_result), http_code) +def is_server_ready(): + return ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT - 1 + + +def is_cachable(rule): + if rule.startswith("/blocks"): + return True + if rule.startswith("/transactions"): + return True + if rule.startswith("/backend"): + return True + + +def return_result_if_not_ready(rule): + return is_cachable(rule) or rule == "/" + +def return_result(success, http_code, result=None, error=None): + api_result = {"success": success} + if result is not None: + api_result["result"] = result + if error is not None: + api_result["error"] = error + response = flask.make_response(to_json(api_result), http_code) response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX - response.headers["X-COUNTERPARTY-READY"] = server_ready + response.headers["X-COUNTERPARTY-READY"] = is_server_ready() response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT response.headers["Content-Type"] = "application/json" return response @@ -137,28 +144,34 @@ def handle_route(**kwargs): db = get_db() # update the current block index ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(db) + rule = str(request.url_rule.rule) - if rule == "/": - result = api_root() + + if not is_server_ready() and not return_result_if_not_ready(rule): + return return_result(False, 503, error="Counterparty not ready") else: - route = ROUTES.get(rule) - try: - function_args = prepare_args(route, **kwargs) - except ValueError as e: - return inject_headers({"success": False, "error": str(e)}, return_code=400) - try: - if function_needs_db(route["function"]): - result = route["function"](db, **function_args) - else: - result = route["function"](**function_args) - except (exceptions.ComposeError, exceptions.UnpackError) as e: - return inject_headers({"success": False, "error": str(e)}, return_code=503) - except Exception as e: - logger.exception("Error in API: %s", e) - traceback.print_exc() - return inject_headers({"success": False, "error": "Unknwon error"}, return_code=503) - result = remove_rowids(result) - return inject_headers(result) + if rule == "/": + return return_result(True, 200, result=api_root()) + else: + route = ROUTES.get(rule) + try: + function_args = prepare_args(route, **kwargs) + except ValueError as e: + return return_result(False, 400, error=str(e)) + try: + if function_needs_db(route["function"]): + result = route["function"](db, **function_args) + else: + result = route["function"](**function_args) + except (exceptions.ComposeError, exceptions.UnpackError) as e: + return return_result(False, 503, error=str(e)) + except Exception as e: + logger.exception("Error in API: %s", e) + traceback.print_exc() + return return_result(False, 503, error="Unknwon error") + + result = remove_rowids(result) + return return_result(True, 200, result=result) def run_api_server(args): diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 74c53a7584..c860d3ab9b 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -90,7 +90,7 @@ "/events/counts": ledger.get_all_events_counts, "/events/": ledger.get_events_by_event, ### /healthz ### - "/healthz": util.handle_healthz_route, + "/healthz": util.handle_healthz_route_v2, ### /backend ### "/backend/addresses/
/transactions": backend.search_raw_transactions, "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index fe67a9726b..4d1a5dee8a 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -40,7 +40,7 @@ def healthz_heavy(db): ) -def healthz(db, check_type="heavy"): +def healthz(db, check_type: str = "heavy"): try: if check_type == "light": healthz_light(db) @@ -67,6 +67,16 @@ def handle_healthz_route(db, check_type: str = "heavy"): return flask.Response(to_json(result), code, mimetype="application/json") +def handle_healthz_route_v2(db, check_type: str = "heavy"): + """ + Health check route. + :param check_type: Type of health check to perform. Options are 'light' and 'heavy' (e.g. light) + """ + if not healthz(db, check_type): + return {"status": "Unhealthy"} + return {"status": "Healthy"} + + def remove_rowids(query_result): """Remove the rowid field from the query result.""" if isinstance(query_result, list): diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 7bd503b9f2..438cb89de0 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -57,7 +57,7 @@ def test_api_v2(request): url = url.replace("", tx_hash) if route.startswith("/events"): url += "?limit=5" - # print(url) + print(url) result = requests.get(url) # noqa: S113 results[url] = result.json() assert result.status_code == 200 diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 94158f812e..d9c4f0d0a3 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1,60 +1,4 @@ { - "/blocks": { - "success": true, - "result": [ - { - "block_index": 840000, - "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", - "block_time": 1713571767, - "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", - "difficulty": 86388558925171.02, - "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", - "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", - "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" - }, - { - "block_index": 839999, - "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", - "block_time": 1713571533, - "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", - "difficulty": 86388558925171.02, - "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", - "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", - "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" - } - ] - }, - "/blocks/": { - "success": true, - "result": { - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", - "difficulty": 86388558925171.02, - "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", - "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", - "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" - } - }, - "/blocks//transactions": { - "success": true, - "result": [ - { - "tx_index": 2726605, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "destination": "", - "btc_amount": 0, - "fee": 56565, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "supported": 1 - } - ] - }, "/blocks//events": { "success": true, "result": [ @@ -2007,10 +1951,6 @@ "name": "broadcast" } }, - "/healthz": { - "data": "Healthy", - "success": true - }, "/addresses/
/compose/bet": { "success": true, "result": { @@ -2460,8 +2400,70 @@ "blocktime": 1713951767 } }, + "/blocks": { + "success": true, + "result": [ + { + "block_index": 840000, + "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", + "block_time": 1713571767, + "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "difficulty": 86388558925171.02, + "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", + "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", + "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" + }, + { + "block_index": 839999, + "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "block_time": 1713571533, + "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", + "difficulty": 86388558925171.02, + "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", + "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", + "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" + } + ] + }, + "/blocks/": { + "success": true, + "result": { + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", + "difficulty": 86388558925171.02, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + } + }, + "/blocks//transactions": { + "success": true, + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + } + ] + }, + "/healthz": { + "success": true, + "result": { + "status": "Healthy" + } + }, "/backend/estimatesmartfee": { "success": true, - "result": 673559 + "result": 295443 } } \ No newline at end of file From 0f1b4ed3cf3c68ff7f5fd7e3e026ab8f0a570328 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 15:10:35 +0200 Subject: [PATCH 089/128] Add notes in API documentation --- .../counterpartycore/lib/api/api_server.py | 53 +++++++++---------- counterparty-core/counterpartycore/server.py | 3 -- .../counterpartycore/test/conftest.py | 1 - counterparty-core/tools/genapidoc.py | 39 +++++++++++++- 4 files changed, 62 insertions(+), 34 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index e371584122..dda3cb6b2b 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -1,7 +1,6 @@ import argparse import logging import multiprocessing -import signal import traceback from multiprocessing import Process from threading import Timer @@ -112,30 +111,30 @@ def return_result(success, http_code, result=None, error=None): def prepare_args(route, **kwargs): function_args = dict(kwargs) - if "args" in route: - for arg in route["args"]: - arg_name = arg["name"] - if arg_name in function_args: - continue - str_arg = request.args.get(arg_name) - if str_arg is None and arg["required"]: - raise ValueError(f"Missing required parameter: {arg_name}") - if str_arg is None: - function_args[arg_name] = arg["default"] - elif arg["type"] == "bool": - function_args[arg_name] = str_arg.lower() in ["true", "1"] - elif arg["type"] == "int": - try: - function_args[arg_name] = int(str_arg) - except ValueError as e: - raise ValueError(f"Invalid integer: {arg_name}") from e - elif arg["type"] == "float": - try: - function_args[arg_name] = float(str_arg) - except ValueError as e: - raise ValueError(f"Invalid float: {arg_name}") from e - else: - function_args[arg_name] = str_arg + # inject args from request.args + for arg in route["args"]: + arg_name = arg["name"] + if arg_name in function_args: + continue + str_arg = request.args.get(arg_name) + if str_arg is None and arg["required"]: + raise ValueError(f"Missing required parameter: {arg_name}") + if str_arg is None: + function_args[arg_name] = arg["default"] + elif arg["type"] == "bool": + function_args[arg_name] = str_arg.lower() in ["true", "1"] + elif arg["type"] == "int": + try: + function_args[arg_name] = int(str_arg) + except ValueError as e: + raise ValueError(f"Invalid integer: {arg_name}") from e + elif arg["type"] == "float": + try: + function_args[arg_name] = float(str_arg) + except ValueError as e: + raise ValueError(f"Invalid float: {arg_name}") from e + else: + function_args[arg_name] = str_arg return function_args @@ -175,10 +174,6 @@ def handle_route(**kwargs): def run_api_server(args): - # default signal handlers - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.default_int_handler) - app = Flask(config.APP_NAME) # Initialise log and config server.initialise_log_and_config(argparse.Namespace(**args)) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 54668d0496..5278df3078 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -162,7 +162,6 @@ def initialise_config( api_user=None, api_password=None, api_no_allow_cors=False, - api_not_ready_http_code=503, force=False, requests_timeout=config.DEFAULT_REQUESTS_TIMEOUT, rpc_batch_size=config.DEFAULT_RPC_BATCH_SIZE, @@ -472,8 +471,6 @@ def initialise_config( else: config.API_PASSWORD = "api" # noqa: S105 - config.API_NOT_READY_HTTP_CODE = api_not_ready_http_code - if api_no_allow_cors: config.API_NO_ALLOW_CORS = api_no_allow_cors else: diff --git a/counterparty-core/counterpartycore/test/conftest.py b/counterparty-core/counterpartycore/test/conftest.py index f196be1d1a..990a028180 100644 --- a/counterparty-core/counterpartycore/test/conftest.py +++ b/counterparty-core/counterpartycore/test/conftest.py @@ -265,7 +265,6 @@ def api_server_v2(request, cp_server): "api_user": "api", "api_password": "api", "api_no_allow_cors": False, - "api_not_ready_http_code": 503, "force": False, "requests_timeout": config.DEFAULT_REQUESTS_TIMEOUT, "rpc_batch_size": config.DEFAULT_RPC_BATCH_SIZE, diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 72044f9060..1a4119bbcf 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -34,7 +34,44 @@ def get_example_output(path, args): # Counterparty Core API -The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. The following routes are available: +The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. + +API routes are divided into 11 groups: + +- [`/blocks`](#group-blocks) +- [`/transactions`](#group-transactions) +- [`/addresses`](#group-addresses) +- [`/assets`](#group-assets) +- [`/orders`](#group-orders) +- [`/bets`](#group-bets) +- [`/dispensers`](#group-dispensers) +- [`/burns`](#group-burns) +- [`/events`](#group-events) +- [`/mempool`](#group-mempool) +- [`/backend`](#group-backend) + +Notes: + +- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. + +- All API responses contain the following 3 headers: + + * `X-COUNTERPARTY-HEIGHT` contains the last block parsed by Counterparty + * `X-BACKEND-HEIGHT` contains the last block known to Bitcoin Core + * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BACKEND-HEIGHT` - 1 + +- All API responses follow the following format: + +``` +{ + "success": , + "error": , + "result": +} +``` + +- Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. + """ cache = {} From 9041dfde3af74cf4be5934da831c1d485e53aa6a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 16:53:38 +0200 Subject: [PATCH 090/128] fix fixtures --- .../test/fixtures/api_v2_fixtures.json | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index bec05b9699..71ea277ec6 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -138,6 +138,21 @@ "http://api:api@localhost:10009/blocks/310491/events": { "success": true, "result": [ + { + "event_index": 1183, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", + "block_index": 310492, + "block_time": 310492000, + "difficulty": null, + "ledger_hash": null, + "previous_block_hash": null, + "txlist_hash": null + }, + "block_index": 310491, + "timestamp": 0 + }, { "event_index": 1182, "event": "BLOCK_PARSED", @@ -219,21 +234,6 @@ }, "block_index": 310491, "timestamp": 0 - }, - { - "event_index": 1177, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", - "block_index": 310491, - "block_time": 310491000, - "difficulty": null, - "ledger_hash": null, - "previous_block_hash": null, - "txlist_hash": null - }, - "block_index": 310491, - "timestamp": 0 } ] }, @@ -1333,7 +1333,7 @@ "previous_block_hash": null, "txlist_hash": null }, - "block_index": 310500, + "block_index": 310499, "timestamp": 0 }, { @@ -1360,7 +1360,7 @@ "previous_block_hash": null, "txlist_hash": null }, - "block_index": 310499, + "block_index": 310498, "timestamp": 0 }, { From d3e13b2481e31109ab0d621b747b4f1e8182a84d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 17:29:53 +0200 Subject: [PATCH 091/128] update relase notes --- release-notes/release-notes-v10.1.2.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/release-notes/release-notes-v10.1.2.md b/release-notes/release-notes-v10.1.2.md index fadded4993..6cc002e42b 100644 --- a/release-notes/release-notes-v10.1.2.md +++ b/release-notes/release-notes-v10.1.2.md @@ -4,6 +4,11 @@ # Upgrading +To continue using the old API you must: +- start `counterparty-server` with the flag `----enable-api-v1` +- replace port `4100` with port `4000` for mainnet and port `14000` with port `14100` +- prefix all endpoints with `/old/` +To easily migrate to the new API, an equivalence table is available in the documentation # ChangeLog @@ -11,6 +16,7 @@ * Fix logging of some raw tracebacks (#1715) ## Codebase +* New REST API ## Command-Line Interface From 4f1b4d600ca283efce45632071eab41333a845e3 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Wed, 24 Apr 2024 18:10:34 +0200 Subject: [PATCH 092/128] lint --- .../counterpartycore/lib/api/api_server.py | 56 +++++++++++-------- .../counterpartycore/lib/api/util.py | 6 +- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index dda3cb6b2b..8f00f00e2e 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -52,8 +52,7 @@ def verify_password(username, password): def api_root(): counterparty_height = blocks.last_db_index(get_db()) routes = [] - for path in ROUTES: - route = ROUTES[path] + for path, route in ROUTES.item(): routes.append( { "path": path, @@ -89,6 +88,7 @@ def is_cachable(rule): return True if rule.startswith("/backend"): return True + return False def return_result_if_not_ready(rule): @@ -117,8 +117,10 @@ def prepare_args(route, **kwargs): if arg_name in function_args: continue str_arg = request.args.get(arg_name) + if str_arg is None and arg["required"]: raise ValueError(f"Missing required parameter: {arg_name}") + if str_arg is None: function_args[arg_name] = arg["default"] elif arg["type"] == "bool": @@ -146,31 +148,37 @@ def handle_route(**kwargs): rule = str(request.url_rule.rule) + # check if server must be ready if not is_server_ready() and not return_result_if_not_ready(rule): return return_result(False, 503, error="Counterparty not ready") - else: - if rule == "/": - return return_result(True, 200, result=api_root()) + + if rule == "/": + return return_result(True, 200, result=api_root()) + + route = ROUTES.get(rule) + + # parse args + try: + function_args = prepare_args(route, **kwargs) + except ValueError as e: + return return_result(False, 400, error=str(e)) + + # call the function + try: + if function_needs_db(route["function"]): + result = route["function"](db, **function_args) else: - route = ROUTES.get(rule) - try: - function_args = prepare_args(route, **kwargs) - except ValueError as e: - return return_result(False, 400, error=str(e)) - try: - if function_needs_db(route["function"]): - result = route["function"](db, **function_args) - else: - result = route["function"](**function_args) - except (exceptions.ComposeError, exceptions.UnpackError) as e: - return return_result(False, 503, error=str(e)) - except Exception as e: - logger.exception("Error in API: %s", e) - traceback.print_exc() - return return_result(False, 503, error="Unknwon error") - - result = remove_rowids(result) - return return_result(True, 200, result=result) + result = route["function"](**function_args) + except (exceptions.ComposeError, exceptions.UnpackError) as e: + return return_result(False, 503, error=str(e)) + except Exception as e: + logger.exception("Error in API: %s", e) + traceback.print_exc() + return return_result(False, 503, error="Unknwon error") + + # clean up and return the result + result = remove_rowids(result) + return return_result(True, 200, result=result) def run_api_server(args): diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 4d1a5dee8a..e7fb53bf8d 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -18,7 +18,7 @@ def check_last_parsed_block(blockcount): logger.debug("Database state check passed.") -def healthz_light(db): +def healthz_light(): logger.debug("Performing light healthz check.") latest_block_index = backend.getblockcount() check_last_parsed_block(latest_block_index) @@ -43,9 +43,9 @@ def healthz_heavy(db): def healthz(db, check_type: str = "heavy"): try: if check_type == "light": - healthz_light(db) + healthz_light() else: - healthz_light(db) + healthz_light() healthz_heavy(db) except Exception as e: logger.error(f"Health check failed: {e}") From f48bc0c4ccd558765c4021b642e41ae42b81c714 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 10:21:41 +0200 Subject: [PATCH 093/128] Remove success from result --- .../counterpartycore/lib/api/api_server.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 8f00f00e2e..ada0732536 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -95,8 +95,9 @@ def return_result_if_not_ready(rule): return is_cachable(rule) or rule == "/" -def return_result(success, http_code, result=None, error=None): - api_result = {"success": success} +def return_result(http_code, result=None, error=None): + assert result is None or error is None + api_result = {} if result is not None: api_result["result"] = result if error is not None: @@ -150,10 +151,10 @@ def handle_route(**kwargs): # check if server must be ready if not is_server_ready() and not return_result_if_not_ready(rule): - return return_result(False, 503, error="Counterparty not ready") + return return_result(503, error="Counterparty not ready") if rule == "/": - return return_result(True, 200, result=api_root()) + return return_result(200, result=api_root()) route = ROUTES.get(rule) @@ -161,7 +162,7 @@ def handle_route(**kwargs): try: function_args = prepare_args(route, **kwargs) except ValueError as e: - return return_result(False, 400, error=str(e)) + return return_result(400, error=str(e)) # call the function try: @@ -170,15 +171,15 @@ def handle_route(**kwargs): else: result = route["function"](**function_args) except (exceptions.ComposeError, exceptions.UnpackError) as e: - return return_result(False, 503, error=str(e)) + return return_result(503, error=str(e)) except Exception as e: logger.exception("Error in API: %s", e) traceback.print_exc() - return return_result(False, 503, error="Unknwon error") + return return_result(503, error="Unknwon error") # clean up and return the result result = remove_rowids(result) - return return_result(True, 200, result=result) + return return_result(200, result=result) def run_api_server(args): From e20681977799bb6d7ffb308b9bd2eafc38f735b4 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 10:40:17 +0200 Subject: [PATCH 094/128] db is already initialized --- counterparty-core/counterpartycore/lib/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index fe086728e9..9bddd5929e 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1146,7 +1146,7 @@ def compose_transaction( del params["segwit"] tx_info = compose_method(db, **params) - initialise(db) + return construct( db, tx_info, From cd0ba21a8e3352022276db337a3db7b9f9bd649c Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 10:41:13 +0200 Subject: [PATCH 095/128] fix typo --- counterparty-core/counterpartycore/lib/api/api_v1.py | 4 ++-- counterparty-core/counterpartycore/lib/transaction.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_v1.py b/counterparty-core/counterpartycore/lib/api/api_v1.py index 0e33cd8bda..55dce5829b 100644 --- a/counterparty-core/counterpartycore/lib/api/api_v1.py +++ b/counterparty-core/counterpartycore/lib/api/api_v1.py @@ -598,7 +598,7 @@ def generate_create_method(tx): def create_method(**kwargs): try: transaction_args, common_args, private_key_wif = ( - transaction.split_compose_arams(**kwargs) + transaction.split_compose_params(**kwargs) ) return transaction.compose_transaction( self.db, name=tx, params=transaction_args, api_v1=True, **common_args @@ -1186,7 +1186,7 @@ def handle_rest(path_args, flask_request): query_data = {} if compose: - transaction_args, common_args, private_key_wif = transaction.split_compose_arams( + transaction_args, common_args, private_key_wif = transaction.split_compose_params( **extra_args ) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 9bddd5929e..b121654326 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1028,7 +1028,7 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): } -def split_compose_arams(**kwargs): +def split_compose_params(**kwargs): transaction_args = {} common_args = {} private_key_wif = None @@ -1193,7 +1193,7 @@ def compose_transaction( def compose(db, source, transaction_name, api_v1=False, **kwargs): if transaction_name not in COMPOSABLE_TRANSACTIONS: raise exceptions.TransactionError("Transaction type not composable.") - transaction_args, common_args, _ = split_compose_arams(**kwargs) + transaction_args, common_args, _ = split_compose_params(**kwargs) transaction_args["source"] = source return compose_transaction( db, name=transaction_name, params=transaction_args, api_v1=api_v1, **common_args From 6f78ef5ef41018ed4b624f0091e73a1bf7a2808a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 10:44:39 +0200 Subject: [PATCH 096/128] _by_event -> _by_name --- counterparty-core/counterpartycore/lib/api/routes.py | 4 ++-- counterparty-core/counterpartycore/lib/ledger.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index c860d3ab9b..93cf7358e5 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -88,7 +88,7 @@ "/events": ledger.get_all_events, "/events/": ledger.get_event_by_index, "/events/counts": ledger.get_all_events_counts, - "/events/": ledger.get_events_by_event, + "/events/": ledger.get_events_by_name, ### /healthz ### "/healthz": util.handle_healthz_route_v2, ### /backend ### @@ -100,6 +100,6 @@ "/backend/estimatesmartfee": backend.fee_per_kb, ### /mempool ### "/mempool/events": ledger.get_all_mempool_events, - "/mempool/events/": ledger.get_mempool_events_by_event, + "/mempool/events/": ledger.get_mempool_events_by_name, } ) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 9e385adaca..d3ab2603a8 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -141,7 +141,7 @@ def get_event_by_index(db, event_index: int): return get_events(db, event_index=event_index) -def get_events_by_event(db, event: str, last: int = None, limit: int = 100): +def get_events_by_name(db, event: str, last: int = None, limit: int = 100): """ Returns the events filtered by event name :param str event: The event to return (e.g. CREDIT) @@ -180,7 +180,7 @@ def get_all_mempool_events(db): return get_mempool_events(db) -def get_mempool_events_by_event(db, event: str): +def get_mempool_events_by_name(db, event: str): """ Returns the mempool events filtered by event name :param str event: The event to return (e.g. OPEN_ORDER) From 09a64a8a31d4f64355de2f0c5a0138a40977271a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 11:13:34 +0200 Subject: [PATCH 097/128] fix fixtures --- .../test/fixtures/api_v2_fixtures.json | 112 ++++++------ counterparty-core/tools/apicache.json | 160 +++++++++--------- 2 files changed, 136 insertions(+), 136 deletions(-) diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index 71ea277ec6..1db6674c03 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -1,6 +1,6 @@ { "http://api:api@localhost:10009/blocks": { - "success": true, + "result": [ { "block_index": 310500, @@ -105,7 +105,7 @@ ] }, "http://api:api@localhost:10009/blocks/310491": { - "success": true, + "result": { "block_index": 310491, "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", @@ -118,7 +118,7 @@ } }, "http://api:api@localhost:10009/blocks/310491/transactions": { - "success": true, + "result": [ { "tx_index": 492, @@ -136,7 +136,7 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events": { - "success": true, + "result": [ { "event_index": 1183, @@ -238,7 +238,7 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events/counts": { - "success": true, + "result": [ { "event": "BLOCK_PARSED", @@ -267,15 +267,15 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events/CREDIT": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/credits": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/debits": { - "success": true, + "result": [ { "block_index": 310491, @@ -289,35 +289,35 @@ ] }, "http://api:api@localhost:10009/blocks/310491/expirations": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/cancels": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/destructions": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/issuances": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/sends": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/dispenses": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/blocks/310491/sweeps": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/transactions/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "success": true, + "result": { "tx_index": 492, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", @@ -333,7 +333,7 @@ } }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances": { - "success": true, + "result": [ { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", @@ -378,7 +378,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances/NODIVISIBLE": { - "success": true, + "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "NODIVISIBLE", @@ -386,7 +386,7 @@ } }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/credits": { - "success": true, + "result": [ { "block_index": 310000, @@ -499,7 +499,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/debits": { - "success": true, + "result": [ { "block_index": 310001, @@ -693,7 +693,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/bets": { - "success": true, + "result": [ { "tx_index": 102, @@ -717,7 +717,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/broadcasts": { - "success": true, + "result": [ { "tx_index": 103, @@ -746,7 +746,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/burns": { - "success": true, + "result": [ { "tx_index": 1, @@ -760,7 +760,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends": { - "success": true, + "result": [ { "tx_index": 8, @@ -861,7 +861,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives": { - "success": true, + "result": [ { "tx_index": 483, @@ -878,7 +878,7 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends/NODIVISIBLE": { - "success": true, + "result": [ { "tx_index": 15, @@ -907,23 +907,23 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives/NODIVISIBLE": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers/NODIVISIBLE": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sweeps": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/assets": { - "success": true, + "result": [ { "asset": "A95428956661682277", @@ -968,7 +968,7 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE": { - "success": true, + "result": { "asset": "NODIVISIBLE", "asset_longname": null, @@ -982,7 +982,7 @@ } }, "http://api:api@localhost:10009/assets/NODIVISIBLE/balances": { - "success": true, + "result": [ { "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", @@ -1002,7 +1002,7 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/balances/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { - "success": true, + "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "NODIVISIBLE", @@ -1010,11 +1010,11 @@ } }, "http://api:api@localhost:10009/assets/NODIVISIBLE/orders": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/credits": { - "success": true, + "result": [ { "block_index": 310002, @@ -1046,7 +1046,7 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/debits": { - "success": true, + "result": [ { "block_index": 310014, @@ -1069,11 +1069,11 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dividends": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/issuances": { - "success": true, + "result": [ { "tx_index": 3, @@ -1099,7 +1099,7 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/sends": { - "success": true, + "result": [ { "tx_index": 15, @@ -1128,15 +1128,15 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/holders": { - "success": true, + "result": [ { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", @@ -1156,7 +1156,7 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "success": true, + "result": [ { "tx_index": 492, @@ -1180,7 +1180,7 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/matches": { - "success": true, + "result": [ { "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", @@ -1206,23 +1206,23 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/btcpays": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/matches": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/resolutions": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/burns": { - "success": true, + "result": [ { "tx_index": 1, @@ -1299,15 +1299,15 @@ ] }, "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/dispenses": { - "success": true, + "result": [] }, "http://api:api@localhost:10009/events?limit=5": { - "success": true, + "result": [ { "event_index": 1237, @@ -1378,7 +1378,7 @@ ] }, "http://api:api@localhost:10009/events/10?limit=5": { - "success": true, + "result": [ { "event_index": 10, @@ -1395,7 +1395,7 @@ ] }, "http://api:api@localhost:10009/events/counts?limit=5": { - "success": true, + "result": [ { "event": "ASSET_CREATION", @@ -1484,7 +1484,7 @@ ] }, "http://api:api@localhost:10009/events/CREDIT?limit=5": { - "success": true, + "result": [ { "event_index": 1231, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index d9c4f0d0a3..e480663355 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1,6 +1,6 @@ { "/blocks//events": { - "success": true, + "result": [ { "event_index": 14194760, @@ -127,7 +127,7 @@ ] }, "/blocks//events/counts": { - "success": true, + "result": [ { "event": "ASSET_CREATION", @@ -164,7 +164,7 @@ ] }, "/blocks//events/": { - "success": true, + "result": [ { "event_index": 14194758, @@ -184,7 +184,7 @@ ] }, "/blocks//credits": { - "success": true, + "result": [ { "block_index": 840464, @@ -198,7 +198,7 @@ ] }, "/blocks//debits": { - "success": true, + "result": [ { "block_index": 840464, @@ -212,7 +212,7 @@ ] }, "/blocks//expirations": { - "success": true, + "result": [ { "type": "order", @@ -225,7 +225,7 @@ ] }, "/blocks//cancels": { - "success": true, + "result": [ { "tx_index": 2725738, @@ -246,7 +246,7 @@ ] }, "/blocks//destructions": { - "success": true, + "result": [ { "tx_index": 2726496, @@ -261,7 +261,7 @@ ] }, "/blocks//issuances": { - "success": true, + "result": [ { "tx_index": 2726605, @@ -287,7 +287,7 @@ ] }, "/blocks//sends": { - "success": true, + "result": [ { "tx_index": 2726604, @@ -304,7 +304,7 @@ ] }, "/blocks//dispenses": { - "success": true, + "result": [ { "tx_index": 2726580, @@ -320,7 +320,7 @@ ] }, "/blocks//sweeps": { - "success": true, + "result": [ { "tx_index": 2720536, @@ -347,7 +347,7 @@ ] }, "/transactions/info": { - "success": true, + "result": { "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "destination": "", @@ -375,7 +375,7 @@ } }, "/transactions/unpack": { - "success": true, + "result": { "message_type": "issuance", "message_type_id": 22, @@ -396,7 +396,7 @@ } }, "/transactions/": { - "success": true, + "result": { "tx_index": 2726605, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", @@ -412,7 +412,7 @@ } }, "/addresses/
/balances": { - "success": true, + "result": [ { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -422,7 +422,7 @@ ] }, "/addresses/
/balances/": { - "success": true, + "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", "asset": "XCP", @@ -430,7 +430,7 @@ } }, "/addresses/
/credits": { - "success": true, + "result": [ { "block_index": 830981, @@ -444,7 +444,7 @@ ] }, "/addresses/
/debits": { - "success": true, + "result": [ { "block_index": 836949, @@ -467,7 +467,7 @@ ] }, "/addresses/
/bets": { - "success": true, + "result": [ { "tx_index": 15106, @@ -510,7 +510,7 @@ ] }, "/addresses/
/broadcasts": { - "success": true, + "result": [ { "tx_index": 15055, @@ -539,7 +539,7 @@ ] }, "/addresses/
/burns": { - "success": true, + "result": [ { "tx_index": 3070, @@ -553,7 +553,7 @@ ] }, "/addresses/
/sends": { - "success": true, + "result": [ { "tx_index": 163106, @@ -570,7 +570,7 @@ ] }, "/addresses/
/receives": { - "success": true, + "result": [ { "tx_index": 2677412, @@ -587,7 +587,7 @@ ] }, "/addresses/
/sends/": { - "success": true, + "result": [ { "tx_index": 163106, @@ -604,7 +604,7 @@ ] }, "/addresses/
/receives/": { - "success": true, + "result": [ { "tx_index": 2677412, @@ -621,7 +621,7 @@ ] }, "/addresses/
/dispensers": { - "success": true, + "result": [ { "tx_index": 2726460, @@ -642,7 +642,7 @@ ] }, "/addresses/
/dispensers/": { - "success": true, + "result": [ { "tx_index": 2726460, @@ -663,7 +663,7 @@ ] }, "/addresses/
/sweeps": { - "success": true, + "result": [ { "tx_index": 2720537, @@ -679,7 +679,7 @@ ] }, "/addresses/
/compose/btcpay": { - "success": true, + "result": { "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", "params": { @@ -690,7 +690,7 @@ } }, "/assets": { - "success": true, + "result": [ { "asset": "A100000000000000000", @@ -715,7 +715,7 @@ ] }, "/assets/": { - "success": true, + "result": { "asset": "UNNEGOTIABLE", "asset_longname": null, @@ -729,7 +729,7 @@ } }, "/assets//balances": { - "success": true, + "result": [ { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", @@ -739,7 +739,7 @@ ] }, "/assets//balances/
": { - "success": true, + "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", "asset": "XCP", @@ -747,7 +747,7 @@ } }, "/assets//orders": { - "success": true, + "result": [ { "tx_index": 825373, @@ -961,7 +961,7 @@ ] }, "/assets//credits": { - "success": true, + "result": [ { "block_index": 840464, @@ -975,7 +975,7 @@ ] }, "/assets//debits": { - "success": true, + "result": [ { "block_index": 280091, @@ -1025,7 +1025,7 @@ ] }, "/assets//dividends": { - "success": true, + "result": [ { "tx_index": 1914456, @@ -1162,7 +1162,7 @@ ] }, "/assets//issuances": { - "success": true, + "result": [ { "tx_index": 2726605, @@ -1188,7 +1188,7 @@ ] }, "/assets//sends": { - "success": true, + "result": [ { "tx_index": 729, @@ -1253,7 +1253,7 @@ ] }, "/assets//dispensers": { - "success": true, + "result": [ { "tx_index": 2726460, @@ -1274,7 +1274,7 @@ ] }, "/assets//dispensers/
": { - "success": true, + "result": [ { "tx_index": 2726460, @@ -1295,7 +1295,7 @@ ] }, "/assets//holders": { - "success": true, + "result": [ { "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", @@ -1340,7 +1340,7 @@ ] }, "/orders/": { - "success": true, + "result": [ { "tx_index": 2724132, @@ -1364,7 +1364,7 @@ ] }, "/orders//matches": { - "success": true, + "result": [ { "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", @@ -1390,7 +1390,7 @@ ] }, "/orders//btcpays": { - "success": true, + "result": [ { "tx_index": 2719343, @@ -1405,7 +1405,7 @@ ] }, "/bets/": { - "success": true, + "result": [ { "tx_index": 15106, @@ -1429,7 +1429,7 @@ ] }, "/bets//matches": { - "success": true, + "result": [ { "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", @@ -1460,7 +1460,7 @@ ] }, "/bets//resolutions": { - "success": true, + "result": [ { "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", @@ -1476,7 +1476,7 @@ ] }, "/burns": { - "success": true, + "result": [ { "tx_index": 10, @@ -1526,7 +1526,7 @@ ] }, "/dispensers/": { - "success": true, + "result": [ { "tx_index": 2536311, @@ -1548,7 +1548,7 @@ ] }, "/dispensers//dispenses": { - "success": true, + "result": [ { "tx_index": 2610745, @@ -1575,7 +1575,7 @@ ] }, "/events": { - "success": true, + "result": [ { "event_index": 10665092, @@ -1649,7 +1649,7 @@ ] }, "/events/": { - "success": true, + "result": [ { "event_index": 10665092, @@ -1665,7 +1665,7 @@ ] }, "/events/counts": { - "success": true, + "result": [ { "event": "ASSET_CREATION", @@ -1858,7 +1858,7 @@ ] }, "/events/": { - "success": true, + "result": [ { "event_index": 10665090, @@ -1938,7 +1938,7 @@ ] }, "/addresses/
/compose/broadcast": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1952,7 +1952,7 @@ } }, "/addresses/
/compose/bet": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1970,7 +1970,7 @@ } }, "/addresses/
/compose/burn": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1982,7 +1982,7 @@ } }, "/addresses/
/compose/cancel": { - "success": true, + "result": { "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", "params": { @@ -1993,7 +1993,7 @@ } }, "/addresses/
/compose/destroy": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2006,7 +2006,7 @@ } }, "/addresses/
/compose/dispenser": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2023,7 +2023,7 @@ } }, "/addresses/
/compose/dividend": { - "success": true, + "result": { "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", "params": { @@ -2036,7 +2036,7 @@ } }, "/addresses/
/compose/issuance": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2053,7 +2053,7 @@ } }, "/addresses/
/compose/mpma": { - "success": true, + "result": { "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", "params": { @@ -2082,7 +2082,7 @@ } }, "/addresses/
/compose/order": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2098,7 +2098,7 @@ } }, "/addresses/
/compose/send": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2114,7 +2114,7 @@ } }, "/addresses/
/compose/sweep": { - "success": true, + "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2127,7 +2127,7 @@ } }, "/mempool/events": { - "success": true, + "result": [ { "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", @@ -2172,7 +2172,7 @@ ] }, "/mempool/events/": { - "success": true, + "result": [ { "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", @@ -2219,7 +2219,7 @@ ] }, "/backend/addresses/
/transactions": { - "success": true, + "result": [ { "tx_hash": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" @@ -2260,14 +2260,14 @@ ] }, "/backend/addresses/
/transactions/oldest": { - "success": true, + "result": { "block_index": 833187, "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" } }, "/backend/addresses/
/utxos": { - "success": true, + "result": [ { "vout": 6, @@ -2352,11 +2352,11 @@ ] }, "/backend/addresses/
/pubkey": { - "success": true, + "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" }, "/backend/transactions/": { - "success": true, + "result": { "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", "hash": "417c24d7a5539bc5b8496e26528382ac297a85a1c6b891b220f72712405ec300", @@ -2401,7 +2401,7 @@ } }, "/blocks": { - "success": true, + "result": [ { "block_index": 840000, @@ -2426,7 +2426,7 @@ ] }, "/blocks/": { - "success": true, + "result": { "block_index": 840464, "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", @@ -2439,7 +2439,7 @@ } }, "/blocks//transactions": { - "success": true, + "result": [ { "tx_index": 2726605, @@ -2457,13 +2457,13 @@ ] }, "/healthz": { - "success": true, + "result": { "status": "Healthy" } }, "/backend/estimatesmartfee": { - "success": true, + "result": 295443 } } \ No newline at end of file From 99ef6006f83125c88dbc6a3a1a5a00343cd96623 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 11:24:32 +0200 Subject: [PATCH 098/128] Remove success and add root path in doc --- counterparty-core/tools/apicache.json | 80 --------------------------- counterparty-core/tools/genapidoc.py | 24 +++++++- 2 files changed, 22 insertions(+), 82 deletions(-) diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index e480663355..328752d285 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1,6 +1,5 @@ { "/blocks//events": { - "result": [ { "event_index": 14194760, @@ -127,7 +126,6 @@ ] }, "/blocks//events/counts": { - "result": [ { "event": "ASSET_CREATION", @@ -164,7 +162,6 @@ ] }, "/blocks//events/": { - "result": [ { "event_index": 14194758, @@ -184,7 +181,6 @@ ] }, "/blocks//credits": { - "result": [ { "block_index": 840464, @@ -198,7 +194,6 @@ ] }, "/blocks//debits": { - "result": [ { "block_index": 840464, @@ -212,7 +207,6 @@ ] }, "/blocks//expirations": { - "result": [ { "type": "order", @@ -225,7 +219,6 @@ ] }, "/blocks//cancels": { - "result": [ { "tx_index": 2725738, @@ -246,7 +239,6 @@ ] }, "/blocks//destructions": { - "result": [ { "tx_index": 2726496, @@ -261,7 +253,6 @@ ] }, "/blocks//issuances": { - "result": [ { "tx_index": 2726605, @@ -287,7 +278,6 @@ ] }, "/blocks//sends": { - "result": [ { "tx_index": 2726604, @@ -304,7 +294,6 @@ ] }, "/blocks//dispenses": { - "result": [ { "tx_index": 2726580, @@ -320,7 +309,6 @@ ] }, "/blocks//sweeps": { - "result": [ { "tx_index": 2720536, @@ -347,7 +335,6 @@ ] }, "/transactions/info": { - "result": { "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "destination": "", @@ -375,7 +362,6 @@ } }, "/transactions/unpack": { - "result": { "message_type": "issuance", "message_type_id": 22, @@ -396,7 +382,6 @@ } }, "/transactions/": { - "result": { "tx_index": 2726605, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", @@ -412,7 +397,6 @@ } }, "/addresses/
/balances": { - "result": [ { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -422,7 +406,6 @@ ] }, "/addresses/
/balances/": { - "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", "asset": "XCP", @@ -430,7 +413,6 @@ } }, "/addresses/
/credits": { - "result": [ { "block_index": 830981, @@ -444,7 +426,6 @@ ] }, "/addresses/
/debits": { - "result": [ { "block_index": 836949, @@ -467,7 +448,6 @@ ] }, "/addresses/
/bets": { - "result": [ { "tx_index": 15106, @@ -510,7 +490,6 @@ ] }, "/addresses/
/broadcasts": { - "result": [ { "tx_index": 15055, @@ -539,7 +518,6 @@ ] }, "/addresses/
/burns": { - "result": [ { "tx_index": 3070, @@ -553,7 +531,6 @@ ] }, "/addresses/
/sends": { - "result": [ { "tx_index": 163106, @@ -570,7 +547,6 @@ ] }, "/addresses/
/receives": { - "result": [ { "tx_index": 2677412, @@ -587,7 +563,6 @@ ] }, "/addresses/
/sends/": { - "result": [ { "tx_index": 163106, @@ -604,7 +579,6 @@ ] }, "/addresses/
/receives/": { - "result": [ { "tx_index": 2677412, @@ -621,7 +595,6 @@ ] }, "/addresses/
/dispensers": { - "result": [ { "tx_index": 2726460, @@ -642,7 +615,6 @@ ] }, "/addresses/
/dispensers/": { - "result": [ { "tx_index": 2726460, @@ -663,7 +635,6 @@ ] }, "/addresses/
/sweeps": { - "result": [ { "tx_index": 2720537, @@ -679,7 +650,6 @@ ] }, "/addresses/
/compose/btcpay": { - "result": { "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", "params": { @@ -690,7 +660,6 @@ } }, "/assets": { - "result": [ { "asset": "A100000000000000000", @@ -715,7 +684,6 @@ ] }, "/assets/": { - "result": { "asset": "UNNEGOTIABLE", "asset_longname": null, @@ -729,7 +697,6 @@ } }, "/assets//balances": { - "result": [ { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", @@ -739,7 +706,6 @@ ] }, "/assets//balances/
": { - "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", "asset": "XCP", @@ -747,7 +713,6 @@ } }, "/assets//orders": { - "result": [ { "tx_index": 825373, @@ -961,7 +926,6 @@ ] }, "/assets//credits": { - "result": [ { "block_index": 840464, @@ -975,7 +939,6 @@ ] }, "/assets//debits": { - "result": [ { "block_index": 280091, @@ -1025,7 +988,6 @@ ] }, "/assets//dividends": { - "result": [ { "tx_index": 1914456, @@ -1162,7 +1124,6 @@ ] }, "/assets//issuances": { - "result": [ { "tx_index": 2726605, @@ -1188,7 +1149,6 @@ ] }, "/assets//sends": { - "result": [ { "tx_index": 729, @@ -1253,7 +1213,6 @@ ] }, "/assets//dispensers": { - "result": [ { "tx_index": 2726460, @@ -1274,7 +1233,6 @@ ] }, "/assets//dispensers/
": { - "result": [ { "tx_index": 2726460, @@ -1295,7 +1253,6 @@ ] }, "/assets//holders": { - "result": [ { "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", @@ -1340,7 +1297,6 @@ ] }, "/orders/": { - "result": [ { "tx_index": 2724132, @@ -1364,7 +1320,6 @@ ] }, "/orders//matches": { - "result": [ { "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", @@ -1390,7 +1345,6 @@ ] }, "/orders//btcpays": { - "result": [ { "tx_index": 2719343, @@ -1405,7 +1359,6 @@ ] }, "/bets/": { - "result": [ { "tx_index": 15106, @@ -1429,7 +1382,6 @@ ] }, "/bets//matches": { - "result": [ { "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", @@ -1460,7 +1412,6 @@ ] }, "/bets//resolutions": { - "result": [ { "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", @@ -1476,7 +1427,6 @@ ] }, "/burns": { - "result": [ { "tx_index": 10, @@ -1526,7 +1476,6 @@ ] }, "/dispensers/": { - "result": [ { "tx_index": 2536311, @@ -1548,7 +1497,6 @@ ] }, "/dispensers//dispenses": { - "result": [ { "tx_index": 2610745, @@ -1575,7 +1523,6 @@ ] }, "/events": { - "result": [ { "event_index": 10665092, @@ -1649,7 +1596,6 @@ ] }, "/events/": { - "result": [ { "event_index": 10665092, @@ -1665,7 +1611,6 @@ ] }, "/events/counts": { - "result": [ { "event": "ASSET_CREATION", @@ -1858,7 +1803,6 @@ ] }, "/events/": { - "result": [ { "event_index": 10665090, @@ -1938,7 +1882,6 @@ ] }, "/addresses/
/compose/broadcast": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1952,7 +1895,6 @@ } }, "/addresses/
/compose/bet": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1970,7 +1912,6 @@ } }, "/addresses/
/compose/burn": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -1982,7 +1923,6 @@ } }, "/addresses/
/compose/cancel": { - "result": { "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", "params": { @@ -1993,7 +1933,6 @@ } }, "/addresses/
/compose/destroy": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2006,7 +1945,6 @@ } }, "/addresses/
/compose/dispenser": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2023,7 +1961,6 @@ } }, "/addresses/
/compose/dividend": { - "result": { "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", "params": { @@ -2036,7 +1973,6 @@ } }, "/addresses/
/compose/issuance": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2053,7 +1989,6 @@ } }, "/addresses/
/compose/mpma": { - "result": { "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", "params": { @@ -2082,7 +2017,6 @@ } }, "/addresses/
/compose/order": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2098,7 +2032,6 @@ } }, "/addresses/
/compose/send": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2114,7 +2047,6 @@ } }, "/addresses/
/compose/sweep": { - "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", "params": { @@ -2127,7 +2059,6 @@ } }, "/mempool/events": { - "result": [ { "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", @@ -2172,7 +2103,6 @@ ] }, "/mempool/events/": { - "result": [ { "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", @@ -2219,7 +2149,6 @@ ] }, "/backend/addresses/
/transactions": { - "result": [ { "tx_hash": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" @@ -2260,14 +2189,12 @@ ] }, "/backend/addresses/
/transactions/oldest": { - "result": { "block_index": 833187, "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" } }, "/backend/addresses/
/utxos": { - "result": [ { "vout": 6, @@ -2352,11 +2279,9 @@ ] }, "/backend/addresses/
/pubkey": { - "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" }, "/backend/transactions/": { - "result": { "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", "hash": "417c24d7a5539bc5b8496e26528382ac297a85a1c6b891b220f72712405ec300", @@ -2401,7 +2326,6 @@ } }, "/blocks": { - "result": [ { "block_index": 840000, @@ -2426,7 +2350,6 @@ ] }, "/blocks/": { - "result": { "block_index": 840464, "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", @@ -2439,7 +2362,6 @@ } }, "/blocks//transactions": { - "result": [ { "tx_index": 2726605, @@ -2457,13 +2379,11 @@ ] }, "/healthz": { - "result": { "status": "Healthy" } }, "/backend/estimatesmartfee": { - "result": 295443 } } \ No newline at end of file diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 1a4119bbcf..471dbb5d41 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -5,7 +5,7 @@ from counterpartycore import server CURR_DIR = os.path.dirname(os.path.realpath(__file__)) -API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api/rest.md") +API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api-v2/rest.md") CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" USE_API_CACHE = True @@ -64,7 +64,6 @@ def get_example_output(path, args): ``` { - "success": , "error": , "result": } @@ -72,6 +71,27 @@ def get_example_output(path, args): - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. +## Root Path + +### Get Server Info [`/`] + +Returns server information and the list of documented routes in JSON format. + ++ Response 200 (application/json) + + ``` + { + "server_ready": true, + "network": "mainnet", + "version": "10.1.1", + "backend_height": 840796, + "counterparty_height": 840796, + "routes": [ + + ] + } + ``` + """ cache = {} From 56ff76a1be541632c9074f2865304d135544b6af Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 11:30:46 +0200 Subject: [PATCH 099/128] Don't indent json result; Fix typo --- counterparty-core/counterpartycore/lib/api/api_server.py | 2 +- counterparty-core/counterpartycore/lib/api/util.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index ada0732536..9919763e8f 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -52,7 +52,7 @@ def verify_password(username, password): def api_root(): counterparty_height = blocks.last_db_index(get_db()) routes = [] - for path, route in ROUTES.item(): + for path, route in ROUTES.items(): routes.append( { "path": path, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index e7fb53bf8d..139fff1fbb 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -223,5 +223,5 @@ def default(self, o): return super().default(o) -def to_json(obj): - return json.dumps(obj, cls=ApiJsonEncoder, indent=4) +def to_json(obj, indent=None): + return json.dumps(obj, cls=ApiJsonEncoder, indent=indent) From 611b2636a87f517f6564f41f63895b7898641116 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 11:33:51 +0200 Subject: [PATCH 100/128] tweak indentation --- counterparty-core/tools/genapidoc.py | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 471dbb5d41..2e647460c4 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -62,12 +62,12 @@ def get_example_output(path, args): - All API responses follow the following format: -``` -{ - "error": , - "result": -} -``` + ``` + { + "error": , + "result": + } + ``` - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. @@ -79,18 +79,18 @@ def get_example_output(path, args): + Response 200 (application/json) - ``` - { - "server_ready": true, - "network": "mainnet", - "version": "10.1.1", - "backend_height": 840796, - "counterparty_height": 840796, - "routes": [ - - ] - } - ``` + ``` + { + "server_ready": true, + "network": "mainnet", + "version": "10.1.1", + "backend_height": 840796, + "counterparty_height": 840796, + "routes": [ + + ] + } + ``` """ @@ -132,10 +132,10 @@ def get_example_output(path, args): example_output = cache[path] example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" - md += " ```\n" + md += " ```\n" for line in example_output_json.split("\n"): md += f" {line}\n" - md += " ```\n" + md += " ```\n" with open(CACHE_FILE, "w") as f: json.dump(cache, f, indent=4) From b71b1fe419920ebb914ac29b19d2b535fda4e389 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 11:59:08 +0200 Subject: [PATCH 101/128] order_hash, bet_hash and dispenser_hash instead tx_hash in routes --- .../counterpartycore/lib/api/routes.py | 16 +++---- .../counterpartycore/lib/ledger.py | 48 +++++++++---------- .../counterpartycore/lib/messages/bet.py | 2 +- .../counterpartycore/lib/messages/cancel.py | 4 +- .../counterpartycore/lib/messages/order.py | 14 +++--- .../counterpartycore/test/api_v2_test.py | 14 ++---- 6 files changed, 47 insertions(+), 51 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 93cf7358e5..db34f1f0a5 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -72,18 +72,18 @@ "/assets//dispensers/
": ledger.get_dispensers_by_address_and_asset, "/assets//holders": ledger.get_asset_holders, ### /orders ### - "/orders/": ledger.get_order, - "/orders//matches": ledger.get_order_matches_by_order, - "/orders//btcpays": ledger.get_btcpays_by_order, + "/orders/": ledger.get_order, + "/orders//matches": ledger.get_order_matches_by_order, + "/orders//btcpays": ledger.get_btcpays_by_order, ### /bets ### - "/bets/": ledger.get_bet, - "/bets//matches": ledger.get_bet_matches_by_bet, - "/bets//resolutions": ledger.get_resolutions_by_bet, + "/bets/": ledger.get_bet, + "/bets//matches": ledger.get_bet_matches_by_bet, + "/bets//resolutions": ledger.get_resolutions_by_bet, ### /burns ### "/burns": ledger.get_all_burns, ### /dispensers ### - "/dispensers/": ledger.get_dispenser_info_by_tx_hash, - "/dispensers//dispenses": ledger.get_dispenses_by_dispenser, + "/dispensers/": ledger.get_dispenser_info_by_hash, + "/dispensers//dispenses": ledger.get_dispenses_by_dispenser, ### /events ### "/events": ledger.get_all_events, "/events/": ledger.get_event_by_index, diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index d3ab2603a8..19f731a1a8 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -1673,12 +1673,12 @@ def get_dispenser_info(db, tx_hash=None, tx_index=None): return cursor.fetchall() -def get_dispenser_info_by_tx_hash(db, tx_hash: str): +def get_dispenser_info_by_hash(db, dispenser_hash: str): """ Returns the dispenser information by tx_hash - :param str tx_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) + :param str dispenser_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) """ - return get_dispenser_info(db, tx_hash=tx_hash) + return get_dispenser_info(db, tx_hash=dispenser_hash) def get_refilling_count(db, dispenser_tx_hash): @@ -1899,12 +1899,12 @@ def get_dispenses_by_block(db, block_index: int): return get_dispenses(db, block_index=block_index) -def get_dispenses_by_dispenser(db, tx_hash: str): +def get_dispenses_by_dispenser(db, dispenser_hash: str): """ Returns the dispenses of a dispenser - :param str tx_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) + :param str dispenser_hash: The hash of the dispenser to return (e.g. 753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a) """ - return get_dispenses(db, dispenser_tx_hash=tx_hash) + return get_dispenses(db, dispenser_tx_hash=dispenser_hash) ### UPDATES ### @@ -1961,10 +1961,10 @@ def get_bet_matches_to_expire(db, block_time): return cursor.fetchall() -def get_bet(db, tx_hash: str): +def get_bet(db, bet_hash: str): """ Returns the information of a bet - :param str tx_hash: The hash of the bet to return (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) + :param str bet_hash: The hash of the transaction that created the bet (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) """ cursor = db.cursor() query = """ @@ -1972,7 +1972,7 @@ def get_bet(db, tx_hash: str): WHERE tx_hash = ? ORDER BY rowid DESC LIMIT 1 """ - bindings = (tx_hash,) + bindings = (bet_hash,) cursor.execute(query, bindings) return cursor.fetchall() @@ -2030,10 +2030,10 @@ def get_bet_by_feed(db, address: str, status: str = "open"): return cursor.fetchall() -def get_bet_matches_by_bet(db, tx_hash: str, status: str = "pending"): +def get_bet_matches_by_bet(db, bet_hash: str, status: str = "pending"): """ Returns the bet matches of a bet - :param str tx_hash: The hash of the bet (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) + :param str bet_hash: The hash of the transaction that created the bet (e.g. 5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed) :param str status: The status of the bet matches (e.g. expired) """ cursor = db.cursor() @@ -2045,15 +2045,15 @@ def get_bet_matches_by_bet(db, tx_hash: str, status: str = "pending"): GROUP BY id ) WHERE status = ? """ - bindings = (tx_hash, tx_hash, status) + bindings = (bet_hash, bet_hash, status) cursor.execute(query, bindings) return cursor.fetchall() -def get_resolutions_by_bet(db, tx_hash: str): +def get_resolutions_by_bet(db, bet_hash: str): """ Returns the resolutions of a bet - :param str tx_hash: The hash of the bet (e.g. 36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace) + :param str bet_hash: The hash of the transaction that created the bet (e.g. 36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace) """ cursor = db.cursor() query = """ @@ -2061,7 +2061,7 @@ def get_resolutions_by_bet(db, tx_hash: str): FROM bet_match_resolutions WHERE bet_match_id LIKE ? """ - bindings = (f"%{tx_hash}%",) + bindings = (f"%{bet_hash}%",) cursor.execute(query, bindings) return cursor.fetchall() @@ -2145,10 +2145,10 @@ def get_order_matches_to_expire(db, block_index): return cursor.fetchall() -def get_order(db, tx_hash: str): +def get_order(db, order_hash: str): """ Returns the information of an order - :param str tx_hash: The hash of the order to return (e.g. 23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776) + :param str order_hash: The hash of the transaction that created the order (e.g. 23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776) """ cursor = db.cursor() query = """ @@ -2156,7 +2156,7 @@ def get_order(db, tx_hash: str): WHERE tx_hash = ? ORDER BY rowid DESC LIMIT 1 """ - bindings = (tx_hash,) + bindings = (order_hash,) cursor.execute(query, bindings) return cursor.fetchall() @@ -2240,10 +2240,10 @@ def get_orders_by_asset(db, asset: str, status: str = "open"): return cursor.fetchall() -def get_order_matches_by_order(db, tx_hash: str, status: str = "pending"): +def get_order_matches_by_order(db, order_hash: str, status: str = "pending"): """ Returns the order matches of an order - :param str tx_hash: The hash of the order (e.g. 5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947) + :param str order_hash: The hash of the transaction that created the order (e.g. 5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947) :param str status: The status of the order matches to return (e.g. completed) """ cursor = db.cursor() @@ -2255,15 +2255,15 @@ def get_order_matches_by_order(db, tx_hash: str, status: str = "pending"): GROUP BY id ) WHERE status = ? """ - bindings = (tx_hash, tx_hash, status) + bindings = (order_hash, order_hash, status) cursor.execute(query, bindings) return cursor.fetchall() -def get_btcpays_by_order(db, tx_hash: str): +def get_btcpays_by_order(db, order_hash: str): """ Returns the BTC pays of an order - :param str tx_hash: The hash of the order (e.g. 299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4) + :param str order_hash: The hash of the transaction that created the order (e.g. 299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4) """ cursor = db.cursor() query = """ @@ -2271,7 +2271,7 @@ def get_btcpays_by_order(db, tx_hash: str): FROM btcpays WHERE order_match_id LIKE ? """ - bindings = (f"%{tx_hash}%",) + bindings = (f"%{order_hash}%",) cursor.execute(query, bindings) return cursor.fetchall() diff --git a/counterparty-core/counterpartycore/lib/messages/bet.py b/counterparty-core/counterpartycore/lib/messages/bet.py index 2af526b8b4..7a9c453e10 100644 --- a/counterparty-core/counterpartycore/lib/messages/bet.py +++ b/counterparty-core/counterpartycore/lib/messages/bet.py @@ -549,7 +549,7 @@ def parse(db, tx, message): def match(db, tx): # Get bet in question. - bets = ledger.get_bet(db, tx_hash=tx["tx_hash"]) + bets = ledger.get_bet(db, bet_hash=tx["tx_hash"]) if not bets: return else: diff --git a/counterparty-core/counterpartycore/lib/messages/cancel.py b/counterparty-core/counterpartycore/lib/messages/cancel.py index bc51437e9f..4ab5bc3ebb 100644 --- a/counterparty-core/counterpartycore/lib/messages/cancel.py +++ b/counterparty-core/counterpartycore/lib/messages/cancel.py @@ -56,8 +56,8 @@ def validate(db, source, offer_hash): problems = [] # TODO: make query only if necessary - orders = ledger.get_order(db, tx_hash=offer_hash) - bets = ledger.get_bet(db, tx_hash=offer_hash) + orders = ledger.get_order(db, order_hash=offer_hash) + bets = ledger.get_bet(db, bet_hash=offer_hash) rps = ledger.get_rps(db, tx_hash=offer_hash) offer_type = None diff --git a/counterparty-core/counterpartycore/lib/messages/order.py b/counterparty-core/counterpartycore/lib/messages/order.py index 3038483614..29d87356eb 100644 --- a/counterparty-core/counterpartycore/lib/messages/order.py +++ b/counterparty-core/counterpartycore/lib/messages/order.py @@ -247,7 +247,7 @@ def cancel_order_match(db, order_match, status, block_index, tx_index): ledger.update_order_match_status(db, order_match["id"], status) # If tx0 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining. - orders = ledger.get_order(db, tx_hash=order_match["tx0_hash"]) + orders = ledger.get_order(db, order_hash=order_match["tx0_hash"]) assert len(orders) == 1 tx0_order = orders[0] if tx0_order["status"] in ("expired", "cancelled"): @@ -290,7 +290,7 @@ def cancel_order_match(db, order_match, status, block_index, tx_index): ledger.update_order(db, order_match["tx0_hash"], set_data) # If tx1 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining. - orders = ledger.get_order(db, tx_hash=order_match["tx1_hash"]) + orders = ledger.get_order(db, order_hash=order_match["tx1_hash"]) assert len(orders) == 1 tx1_order = orders[0] if tx1_order["status"] in ("expired", "cancelled"): @@ -613,7 +613,7 @@ def match(db, tx, block_index=None): cursor = db.cursor() # Get order in question. - orders = ledger.get_order(db, tx_hash=tx["tx_hash"]) + orders = ledger.get_order(db, order_hash=tx["tx_hash"]) if not orders: cursor.close() return @@ -966,14 +966,14 @@ def expire(db, block_index): if order_match["backward_asset"] == "BTC" and order_match["status"] == "expired": cancel_order( db, - ledger.get_order(db, tx_hash=order_match["tx1_hash"])[0], + ledger.get_order(db, order_hash=order_match["tx1_hash"])[0], "expired", block_index, ) if order_match["forward_asset"] == "BTC" and order_match["status"] == "expired": cancel_order( db, - ledger.get_order(db, tx_hash=order_match["tx0_hash"])[0], + ledger.get_order(db, order_hash=order_match["tx0_hash"])[0], "expired", block_index, ) @@ -981,7 +981,7 @@ def expire(db, block_index): if block_index >= 315000 or config.TESTNET or config.REGTEST: # Protocol change. # Re‐match. for order_match in order_matches: - match(db, ledger.get_order(db, tx_hash=order_match["tx0_hash"])[0], block_index) - match(db, ledger.get_order(db, tx_hash=order_match["tx1_hash"])[0], block_index) + match(db, ledger.get_order(db, order_hash=order_match["tx0_hash"])[0], block_index) + match(db, ledger.get_order(db, order_hash=order_match["tx1_hash"])[0], block_index) cursor.close() diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 438cb89de0..a3949efed2 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -28,7 +28,7 @@ def test_api_v2(request): tx_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" order_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" bet_hash = "e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42" - dispsenser_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" + dispenser_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" event = "CREDIT" event_index = 10 exclude_routes = ["compose", "unpack", "info", "mempool", "healthz", "backend"] @@ -47,14 +47,10 @@ def test_api_v2(request): url = url.replace("", asset) url = url.replace("", event) url = url.replace("", str(event_index)) - if route.startswith("/orders"): - url = url.replace("", order_hash) - elif route.startswith("/bets"): - url = url.replace("", bet_hash) - elif route.startswith("/dispensers"): - url = url.replace("", dispsenser_hash) - else: - url = url.replace("", tx_hash) + url = url.replace("", order_hash) + url = url.replace("", bet_hash) + url = url.replace("", dispenser_hash) + url = url.replace("", tx_hash) if route.startswith("/events"): url += "?limit=5" print(url) From 102b9f1df2cf93632f049a918ddfc2aa5483a221 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 12:06:16 +0200 Subject: [PATCH 102/128] get_balance_object -> get_balance_by_address_and_asset --- .../counterpartycore/lib/api/routes.py | 4 +- .../counterpartycore/lib/ledger.py | 2 +- counterparty-core/tools/apicache.json | 177 ++++++++++++++++++ 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index db34f1f0a5..d15e20fdaa 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -30,7 +30,7 @@ "/transactions/": ledger.get_transaction, ### /addresses ### "/addresses/
/balances": ledger.get_address_balances, - "/addresses/
/balances/": ledger.get_balance_object, + "/addresses/
/balances/": ledger.get_balance_by_address_and_asset, "/addresses/
/credits": ledger.get_credits_by_address, "/addresses/
/debits": ledger.get_debits_by_address, "/addresses/
/bets": ledger.get_bet_by_feed, @@ -61,7 +61,7 @@ "/assets": ledger.get_valid_assets, "/assets/": ledger.get_asset_info, "/assets//balances": ledger.get_asset_balances, - "/assets//balances/
": ledger.get_balance_object, + "/assets//balances/
": ledger.get_balance_by_address_and_asset, "/assets//orders": ledger.get_orders_by_asset, "/assets//credits": ledger.get_credits_by_asset, "/assets//debits": ledger.get_debits_by_asset, diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 19f731a1a8..3ebbaee5ce 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -439,7 +439,7 @@ def get_balance(db, address, asset, raise_error_if_no_balance=False, return_list return balances[0]["quantity"] -def get_balance_object(db, address: str, asset: str): +def get_balance_by_address_and_asset(db, address: str, asset: str): """ Returns the balance of an address and asset :param str address: The address to return (e.g. 1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs) diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 328752d285..49ea5365ac 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -2385,5 +2385,182 @@ }, "/backend/estimatesmartfee": { "result": 295443 + }, + "/orders/": { + "result": [ + { + "tx_index": 2724132, + "tx_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "block_index": 840381, + "source": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "give_asset": "PEPECASH", + "give_quantity": 6966600000000, + "give_remaining": 900000000000, + "get_asset": "XCP", + "get_quantity": 11076894000, + "get_remaining": 1431000000, + "expiration": 5000, + "expire_index": 843055, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 4488, + "fee_provided_remaining": 4488, + "status": "open" + } + ] + }, + "/orders//matches": { + "result": [ + { + "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx0_index": 2724132, + "tx0_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "tx0_address": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "tx1_index": 2726591, + "tx1_hash": "5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx1_address": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "forward_asset": "PEPECASH", + "forward_quantity": 6066600000000, + "backward_asset": "XCP", + "backward_quantity": 9645894000, + "tx0_block_index": 838055, + "tx1_block_index": 840381, + "block_index": 840381, + "tx0_expiration": 5000, + "tx1_expiration": 8064, + "match_expire_index": 840401, + "fee_paid": 0, + "status": "completed" + } + ] + }, + "/orders//btcpays": { + "result": [ + { + "tx_index": 2719343, + "tx_hash": "6cfa7f31b43a46e5ad74a9db810bd6cac56235a8ebc73ec63d01b38ea7ea2414", + "block_index": 836188, + "source": "1NfJnJdAdmm2rJCFW54NsAKqqTTMexCNJ3", + "destination": "1BepkwAhEmEuEGF349XjmEUrRvoy9a7Biv", + "btc_amount": 4500000, + "order_match_id": "0a1387df82a8a7e9cec01c52c8fee01f6995c4e39dc5804e1d2bf40d9368f5c5_299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4", + "status": "valid" + } + ] + }, + "/bets/": { + "result": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + } + ] + }, + "/bets//matches": { + "result": [ + { + "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx0_index": 15106, + "tx0_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "tx0_address": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "tx1_index": 15108, + "tx1_hash": "cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx1_address": "1PTqJmRCMGs4qBEh2APAFSrBv95Uf1hfiD", + "tx0_bet_type": 3, + "tx1_bet_type": 2, + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "initial_value": -1, + "deadline": 1401828300, + "target_value": 1.0, + "leverage": 5040, + "forward_quantity": 50000000, + "backward_quantity": 50000000, + "tx0_block_index": 304062, + "tx1_block_index": 304063, + "block_index": 306379, + "tx0_expiration": 11, + "tx1_expiration": 1459, + "match_expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "expired" + } + ] + }, + "/bets//resolutions": { + "result": [ + { + "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", + "bet_match_type_id": 5, + "block_index": 401128, + "winner": "Equal", + "settled": null, + "bull_credit": null, + "bear_credit": null, + "escrow_less_fee": 2000000, + "fee": 0 + } + ] + }, + "/dispensers/": { + "result": [ + { + "tx_index": 2536311, + "tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "asset": "FLOCK", + "give_quantity": 10000000000, + "escrow_quantity": 250000000000, + "satoshirate": 330000, + "status": 0, + "give_remaining": 140000000000, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "dispense_count": 2, + "asset_longname": null + } + ] + }, + "/dispensers//dispenses": { + "result": [ + { + "tx_index": 2610745, + "dispense_index": 0, + "tx_hash": "8c95cc6afc8fd466c784fd1c02749c585988999bbc66251b944c443dc31af757", + "block_index": 821450, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "1FKYM1CP9RfttJhNG8HTNQdE2uV3YvwbRB", + "asset": "FLOCK", + "dispense_quantity": 20000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + }, + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ] } } \ No newline at end of file From a5370ea96a436711f6e5cfe8c6a8c199409c79a6 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 12:13:59 +0200 Subject: [PATCH 103/128] handle_healthz_route_v2 -> check_server_status --- counterparty-core/counterpartycore/lib/api/routes.py | 2 +- counterparty-core/counterpartycore/lib/api/util.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index d15e20fdaa..e9930e2458 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -90,7 +90,7 @@ "/events/counts": ledger.get_all_events_counts, "/events/": ledger.get_events_by_name, ### /healthz ### - "/healthz": util.handle_healthz_route_v2, + "/healthz": util.check_server_status, ### /backend ### "/backend/addresses/
/transactions": backend.search_raw_transactions, "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index 139fff1fbb..e4ab9a1075 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -67,7 +67,7 @@ def handle_healthz_route(db, check_type: str = "heavy"): return flask.Response(to_json(result), code, mimetype="application/json") -def handle_healthz_route_v2(db, check_type: str = "heavy"): +def check_server_status(db, check_type: str = "heavy"): """ Health check route. :param check_type: Type of health check to perform. Options are 'light' and 'heavy' (e.g. light) From 16ae417f1f490f1bb4d55ecbb7f2069102fd75f8 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 13:32:49 +0200 Subject: [PATCH 104/128] generate also apib compatible with apiary --- counterparty-core/counterparty-core.apib | 4027 ++++++++++++++++++++++ counterparty-core/tools/genapidoc.py | 33 +- 2 files changed, 4055 insertions(+), 5 deletions(-) create mode 100644 counterparty-core/counterparty-core.apib diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib new file mode 100644 index 0000000000..9854b1590a --- /dev/null +++ b/counterparty-core/counterparty-core.apib @@ -0,0 +1,4027 @@ + +FORMAT: 1A +HOST: https://api.counterparty.io + +# Counterparty Core API + +The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. + +API routes are divided into 11 groups: + +- [`/blocks`](#group-blocks) +- [`/transactions`](#group-transactions) +- [`/addresses`](#group-addresses) +- [`/assets`](#group-assets) +- [`/orders`](#group-orders) +- [`/bets`](#group-bets) +- [`/dispensers`](#group-dispensers) +- [`/burns`](#group-burns) +- [`/events`](#group-events) +- [`/mempool`](#group-mempool) +- [`/backend`](#group-backend) + +Notes: + +- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. + +- All API responses contain the following 3 headers: + + * `X-COUNTERPARTY-HEIGHT` contains the last block parsed by Counterparty + * `X-BACKEND-HEIGHT` contains the last block known to Bitcoin Core + * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BACKEND-HEIGHT` - 1 + +- All API responses follow the following format: + + ``` + { + "error": , + "result": + } + ``` + +- Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. + +## Root Path + +### Get Server Info [GET /] + +Returns server information and the list of documented routes in JSON format. + ++ Response 200 (application/json) + + ``` + { + "server_ready": true, + "network": "mainnet", + "version": "10.1.1", + "backend_height": 840796, + "counterparty_height": 840796, + "routes": [ + + ] + } + ``` + + +## Group Blocks + +### Get Blocks [GET /blocks] + +Returns the list of the last ten blocks + ++ Parameters + + last: `840000` (int, optional) - The index of the most recent block to return + + Default: `None` + + limit: `2` (int, optional) - The number of blocks to return + + Default: `10` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 840000, + "block_hash": "0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5", + "block_time": 1713571767, + "previous_block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "difficulty": 86388558925171.02, + "ledger_hash": "b91dd54cfbd3aff07b358a038bf6174ddc06f36bd00cdccf048e8281bcd56224", + "txlist_hash": "b641c3e190b9941fcd5c84a7c07e66c03559ef26dcea892e2db1cf1d8392a4f2", + "messages_hash": "5c5de34009839ee66ebc3097ecd28bd5deee9553966b3ee39e8a08e123ac9adc" + }, + { + "block_index": 839999, + "block_hash": "0000000000000000000172014ba58d66455762add0512355ad651207918494ab", + "block_time": 1713571533, + "previous_block_hash": "00000000000000000001dcce6ce7c8a45872cafd1fb04732b447a14a91832591", + "difficulty": 86388558925171.02, + "ledger_hash": "e2b2e23c2ac1060dafe2395da01fe5907f323b5a644816f45f003411c612ac30", + "txlist_hash": "f33f800ef166e6ef5b3df15a0733f9fd3ebb0b799f39ef1951e6709118b7c0fd", + "messages_hash": "16b7d40543b7b80587f4d98c84fcdfdceb2d1c18abba82c7064c09c2795b7ab2" + } + ] + } + ``` + +### Get Block [GET /blocks/{block_index}] + +Return the information of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": { + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d", + "difficulty": 86388558925171.02, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" + } + } + ``` + +### Get Transactions By Block [GET /blocks/{block_index}/transactions] + +Returns the transactions of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + } + ] + } + ``` + +### Get Events By Block [GET /blocks/{block_index}/events] + +Returns the events of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event_index": 14194760, + "event": "BLOCK_PARSED", + "bindings": { + "block_index": 840464, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194759, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194757, + "event": "ASSET_ISSUANCE", + "bindings": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "block_index": 840464, + "call_date": 0, + "call_price": 0.0, + "callable": false, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "divisible": false, + "fee_paid": 50000000, + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "locked": false, + "quantity": 1, + "reset": false, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "status": "valid", + "transfer": false, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194756, + "event": "ASSET_CREATION", + "bindings": { + "asset_id": "75313533584419238", + "asset_longname": null, + "asset_name": "UNNEGOTIABLE", + "block_index": 840464 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194755, + "event": "DEBIT", + "bindings": { + "action": "issuance fee", + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "block_index": 840464, + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 50000000, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + }, + { + "event_index": 14194754, + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "btc_amount": 0, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "destination": "", + "fee": 56565, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852779 + }, + { + "event_index": 14194753, + "event": "NEW_BLOCK", + "bindings": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "difficulty": 86388558925171.02, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" + }, + "block_index": 840464, + "timestamp": 1713852779 + } + ] + } + ``` + +### Get Events Counts By Block [GET /blocks/{block_index}/events/counts] + +Returns the event counts of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event": "ASSET_CREATION", + "event_count": 1 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 1 + }, + { + "event": "BLOCK_PARSED", + "event_count": 1 + }, + { + "event": "CREDIT", + "event_count": 1 + }, + { + "event": "DEBIT", + "event_count": 1 + }, + { + "event": "NEW_BLOCK", + "event_count": 1 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 1 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 1 + } + ] + } + ``` + +### Get Events By Block And Event [GET /blocks/{block_index}/events/{event}] + +Returns the events of a block filtered by event + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + + event: `CREDIT` (str, required) - The event to filter by + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event_index": 14194758, + "event": "CREDIT", + "bindings": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + }, + "block_index": 840464, + "timestamp": 1713852780 + } + ] + } + ``` + +### Get Credits By Block [GET /blocks/{block_index}/credits] + +Returns the credits of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ] + } + ``` + +### Get Debits By Block [GET /blocks/{block_index}/debits] + +Returns the debits of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "quantity": 50000000, + "action": "issuance fee", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ] + } + ``` + +### Get Expirations [GET /blocks/{block_index}/expirations] + +Returns the expirations of a block + ++ Parameters + + block_index: `840356` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "type": "order", + "object_id": "533d5c0ecd8ca9c2946d3298cc5e570eee55b62b887dd85c95de6de4fdc7f441" + }, + { + "type": "order", + "object_id": "b048661afeee3f266792481168024abc0d7648fe0e019e4a1e0fd9867c2c0ffc" + } + ] + } + ``` + +### Get Cancels [GET /blocks/{block_index}/cancels] + +Returns the cancels of a block + ++ Parameters + + block_index: `839746` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2725738, + "tx_hash": "793af9129c7368f974c3ea0c87ad38131f0d82d19fbaf1adf8aaf2e657ec42b8", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "04b258ac37f73e3b9a8575110320d67c752e1baace0f516da75845f388911735", + "status": "valid" + }, + { + "tx_index": 2725739, + "tx_hash": "2071e8a6fbc0c443b152d513c754356f8f962db2fa694de8c6826b57413cc190", + "block_index": 839746, + "source": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "offer_hash": "b1622dbe4f0ce740cb6c18f6f136876bc4949c40a62bc8cceefa81fd6679a57f", + "status": "valid" + } + ] + } + ``` + +### Get Destructions [GET /blocks/{block_index}/destructions] + +Returns the destructions of a block + ++ Parameters + + block_index: `839988` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726496, + "tx_hash": "f5609facc8dac6cdf70b15c514ea15a9acc24a9bd86dcac2b845d5740fbcc50b", + "block_index": 839988, + "source": "1FpLAtreZjTVCMcj1pq1AHWuqcs3n7obMm", + "asset": "COBBEE", + "quantity": 50000, + "tag": "", + "status": "valid" + } + ] + } + ``` + +### Get Issuances By Block [GET /blocks/{block_index}/issuances] + +Returns the issuances of a block + ++ Parameters + + block_index: `840464` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", + "quantity": 1, + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ] + } + ``` + +### Get Sends Or Receives By Block [GET /blocks/{block_index}/sends] + +Returns the sends of a block + ++ Parameters + + block_index: `840459` (int, required) - The index of the block to return + + limit (int, optional) - + + Default: `100` + + offset (int, optional) - + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726604, + "tx_hash": "b4bbb14c99dd260eb634243e5c595e1b7213459979857a32850de84989bb71ec", + "block_index": 840459, + "source": "13Hnmhs5gy2yXKVBx4wSM5HCBdKnaSBZJH", + "destination": "1LfT83WAxbN9qKhtrXxcQA6xgdhfZk21Hz", + "asset": "GAMESOFTRUMP", + "quantity": 1, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Dispenses By Block [GET /blocks/{block_index}/dispenses] + +Returns the dispenses of a block + ++ Parameters + + block_index: `840322` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ] + } + ``` + +### Get Sweeps By Block [GET /blocks/{block_index}/sweeps] + +Returns the sweeps of a block + ++ Parameters + + block_index: `836519` (int, required) - The index of the block to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2720536, + "tx_hash": "9309a4c0aed426e281a52e5d48acadd1464999269a5e75cf2293edd0277d743d", + "block_index": 836519, + "source": "1DMVnJuqBobXA9xYioabBsR4mN8bvVtCAW", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + }, + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ] + } + ``` + +## Group Transactions + +### Info [GET /transactions/info] + +Returns Counterparty information from a raw transaction in hex format. + ++ Parameters + + rawtransaction: `01000000017828697743c03aef6a3a8ba54b22bf579ffcab8161faf20e7b20c4ecd75cc986010000006b483045022100d1bd0531bb1ed2dd2cbf77d6933273e792a3dbfa84327d419169850ddd5976f502205d1ab0f7bcbf1a0cc183f0520c9aa8f711d41cb790c0c4ac39da6da4a093d798012103d3b1f711e907acb556e239f6cafb6a4f7fe40d8dd809b0e06e739c2afd73f202ffffffff0200000000000000004d6a4bf29880b93b0711524c7ef9c76835752088db8bd4113a3daf41fc45ffdc8867ebdbf26817fae377696f36790e52f51005806e9399a427172fedf348cf798ed86e548002ee96909eef0775ec3c2b0100000000001976a91443434cf159cc585fbd74daa9c4b833235b19761b88ac00000000` (str, required) - Raw transaction in hex format + + block_index (int, optional) - Block index mandatory for transactions before block 335000 + + Default: `None` + ++ Response 200 (application/json) + + ``` + { + "result": { + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "unpacked_data": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } + } + } + ``` + +### Unpack [GET /transactions/unpack] + +Unpacks Counterparty data in hex format and returns the message type and data. + ++ Parameters + + datahex: `16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245` (str, required) - Data in hex format + + block_index (int, optional) - Block index of the transaction containing this data + + Default: `None` + ++ Response 200 (application/json) + + ``` + { + "result": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } + } + ``` + +### Get Transaction [GET /transactions/{tx_hash}] + +Returns the information of a transaction + ++ Parameters + + tx_hash: `876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5` (str, required) - The hash of the transaction to return + ++ Response 200 (application/json) + + ``` + { + "result": { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1 + } + } + ``` + +## Group Addresses + +### Get Address Balances [GET /addresses/{address}/balances] + +Returns the balances of an address + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + } + ] + } + ``` + +### Get Balance By Address And Asset [GET /addresses/{address}/balances/{asset}] + +Returns the balance of an address and asset + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + } + } + ``` + +### Get Credits By Address [GET /addresses/{address}/credits] + +Returns the credits of an address + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of credits to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the credits to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 830981, + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "calling_function": "send", + "event": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "tx_index": 2677412 + } + ] + } + ``` + +### Get Debits By Address [GET /addresses/{address}/debits] + +Returns the debits of an address + ++ Parameters + + address: `bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of debits to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the debits to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 836949, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 40000000000, + "action": "open dispenser", + "event": "53ed08176d3479f49986e9282293da85cebc03835b128d8e790ee587f9f1c750", + "tx_index": 2721524 + }, + { + "block_index": 840388, + "address": "bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y", + "asset": "XCP", + "quantity": 250000000000, + "action": "send", + "event": "bc54968ba7d0a59a47b276602e2dbdcf01b14009742e0d7b50272cbae529a9a4", + "tx_index": 2726594 + } + ] + } + ``` + +### Get Bet By Feed [GET /addresses/{address}/bets] + +Returns the bets of a feed + ++ Parameters + + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address of the feed + + status: `filled` (str, optional) - The status of the bet + + Default: `open` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + }, + { + "tx_index": 61338, + "tx_hash": "0fcc7f5190c028f6c5534554d10ec5b4a9246d63826421cd58be2d572d11f088", + "block_index": 320704, + "source": "1Ew38GxczvV1KxjzZsq9f8UuRzHkHQrL5C", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 2, + "deadline": 1410728400, + "wager_quantity": 1000000, + "wager_remaining": 0, + "counterwager_quantity": 1999991, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 13, + "expire_index": 320715, + "fee_fraction_int": 1000000, + "status": "filled" + } + ] + } + ``` + +### Get Broadcasts By Source [GET /addresses/{address}/broadcasts] + +Returns the broadcasts of a source + ++ Parameters + + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address to return + + status: `valid` (str, optional) - The status of the broadcasts to return + + Default: `valid` + + order_by: `ASC` (str, optional) - The order of the broadcasts to return + + Default: `DESC` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 15055, + "tx_hash": "774887e555a6ae5a8c058ebc0185058307977f01a2d4d326e71f37d6dd977154", + "block_index": 304048, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1401815290, + "value": -1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + }, + { + "tx_index": 61477, + "tx_hash": "5d49993bec727622c7b41c84e2b1e65c368f33390d633d217131ffcc5b592f0d", + "block_index": 320718, + "source": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "timestamp": 1410732503, + "value": 1.0, + "fee_fraction_int": 1000000, + "text": "xbet.io/feed/1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "locked": 0, + "status": "valid" + } + ] + } + ``` + +### Get Burns By Address [GET /addresses/{address}/burns] + +Returns the burns of an address + ++ Parameters + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 3070, + "tx_hash": "4560d0e3d04927108b615ab106040489aca9c4aceedcf69d2b71f63b3139c7ae", + "block_index": 283810, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "burned": 10000000, + "earned": 10000000000, + "status": "valid" + } + ] + } + ``` + +### Get Send By Address [GET /addresses/{address}/sends] + +Returns the sends of an address + ++ Parameters + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of sends to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the sends to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Receive By Address [GET /addresses/{address}/receives] + +Returns the receives of an address + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of receives to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the receives to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Send By Address And Asset [GET /addresses/{address}/sends/{asset}] + +Returns the sends of an address and asset + ++ Parameters + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 163106, + "tx_hash": "1c447b41816f1cfbb83f125c8e05faeaae70dbf27255745ba7393f809bd388eb", + "block_index": 343049, + "source": "1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W", + "destination": "16cRBUNnTWiUh2sXWNn1P7KHyJUmyMkdfH", + "asset": "XCP", + "quantity": 10000000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Receive By Address And Asset [GET /addresses/{address}/receives/{asset}] + +Returns the receives of an address and asset + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of receives to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the receives to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2677412, + "tx_hash": "7e4fbb0a1eeeee34bf499955f1027fb78c514d63a3c8ff2e28c6dad005e4d850", + "block_index": 830981, + "source": "bc1qqxr9grqw73dm95cen3g56mzswuj6eqjedu6csx", + "destination": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Dispensers By Address [GET /addresses/{address}/dispensers] + +Returns the dispensers of an address + ++ Parameters + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + status (int, optional) - + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + } + ``` + +### Get Dispensers By Address And Asset [GET /addresses/{address}/dispensers/{asset}] + +Returns the dispensers of an address and an asset + ++ Parameters + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + + status (int, optional) - + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + } + ``` + +### Get Sweeps By Address [GET /addresses/{address}/sweeps] + +Returns the sweeps of an address + ++ Parameters + + address: `18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87` (str, required) - The address to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2720537, + "tx_hash": "d8db6281abffdbf6c320d5ade06aeb6fad2f7bfa1a2c2243c6726020a27107d3", + "block_index": 836519, + "source": "18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87", + "destination": "1HC2q92SfH1ZHzS4CrDwp6KAipV4FqUL4T", + "flags": 3, + "status": "valid", + "memo": null, + "fee_paid": 1400000 + } + ] + } + ``` + +### Compose Bet [GET /addresses/{address}/compose/bet] + +Composes a transaction to issue a bet against a feed. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will make the bet + + feed_address: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that hosts the feed to be bet on + + bet_type: `2` (int, required) - Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual + + deadline: `3000000000` (int, required) - The time at which the bet should be decided/settled, in Unix time (seconds since epoch) + + wager_quantity: `1000` (int, required) - The quantities of XCP to wager (in satoshis, hence integer) + + counterwager_quantity: `1000` (int, required) - The minimum quantities of XCP to be wagered against, for the bets to match + + expiration: `100` (int, required) - The number of blocks after which the bet expires if it remains unmatched + + leverage (int, optional) - Leverage, as a fraction of 5040 + + Default: `5040` + + target_value: `1000` (int, optional) - Target value for Equal/NotEqual bet + + Default: `None` + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "feed_address": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "bet_type": 2, + "deadline": 3000000000, + "wager_quantity": 1000, + "counterwager_quantity": 1000, + "target_value": 1000, + "leverage": 5040, + "expiration": 100 + }, + "name": "bet" + } + } + ``` + +### Compose Broadcast [GET /addresses/{address}/compose/broadcast] + +Composes a transaction to broadcast textual and numerical information to the network. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + timestamp: `4003903983` (int, required) - The timestamp of the broadcast, in Unix time + + value: `100` (float, required) - Numerical value of the broadcast + + fee_fraction: `0.05` (float, required) - How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) + + text: `"Hello, world!"` (str, required) - The textual part of the broadcast + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "timestamp": 4003903983, + "value": 100.0, + "fee_fraction": 0.05, + "text": "\"Hello, world!\"" + }, + "name": "broadcast" + } + } + ``` + +### Compose Btcpay [GET /addresses/{address}/compose/btcpay] + +Composes a transaction to pay for a BTC order match. + ++ Parameters + + address: `bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l` (str, required) - The address that will be sending the payment + + order_match_id: `e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2` (str, required) - The ID of the order match to pay for + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", + "params": { + "source": "bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l", + "order_match_id": "e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2" + }, + "name": "btcpay" + } + } + ``` + +### Compose Burn [GET /addresses/{address}/compose/burn] + +Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address with the BTC to burn + + quantity: `1000` (int, required) - The quantities of BTC to burn (1 BTC maximum burn per address) + + overburn (bool, optional) - Whether to allow the burn to exceed 1 BTC for the address + + Default: `False` + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "quantity": 1000, + "overburn": false + }, + "name": "burn" + } + } + ``` + +### Compose Cancel [GET /addresses/{address}/compose/cancel] + +Composes a transaction to cancel an open order or bet. + ++ Parameters + + address: `15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA` (str, required) - The address that placed the order/bet to be cancelled + + offer_hash: `8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a` (str, required) - The hash of the order/bet to be cancelled + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", + "params": { + "source": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "offer_hash": "8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a" + }, + "name": "cancel" + } + } + ``` + +### Compose Destroy [GET /addresses/{address}/compose/destroy] + +Composes a transaction to destroy a quantity of an asset. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending the asset to be destroyed + + asset: `XCP` (str, required) - The asset to be destroyed + + quantity: `1000` (int, required) - The quantity of the asset to be destroyed + + tag: `"bugs!"` (str, required) - A tag for the destruction + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCP", + "quantity": 1000, + "tag": "\"bugs!\"" + }, + "name": "destroy" + } + } + ``` + +### Compose Dispenser [GET /addresses/{address}/compose/dispenser] + +Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) + + asset: `XCP` (str, required) - The asset or subasset to dispense + + give_quantity: `1000` (int, required) - The quantity of the asset to dispense + + escrow_quantity: `1000` (int, required) - The quantity of the asset to reserve for this dispenser + + mainchainrate: `100` (int, required) - The quantity of the main chain asset (BTC) per dispensed portion + + status: `0` (int, required) - The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed + + open_address (str, optional) - The address that you would like to open the dispenser on + + Default: `None` + + oracle_address (str, optional) - The address that you would like to use as a price oracle for this dispenser + + Default: `None` + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCP", + "give_quantity": 1000, + "escrow_quantity": 1000, + "mainchainrate": 100, + "status": 0, + "open_address": null, + "oracle_address": null + }, + "name": "dispenser" + } + } + ``` + +### Compose Dividend [GET /addresses/{address}/compose/dividend] + +Composes a transaction to issue a dividend to holders of a given asset. + ++ Parameters + + address: `1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD` (str, required) - The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) + + quantity_per_unit: `1` (int, required) - The amount of dividend_asset rewarded + + asset: `PEPECASH` (str, required) - The asset or subasset that the dividends are being rewarded on + + dividend_asset: `XCP` (str, required) - The asset or subasset that the dividends are paid in + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", + "params": { + "source": "1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD", + "quantity_per_unit": 1, + "asset": "PEPECASH", + "dividend_asset": "XCP" + }, + "name": "dividend" + } + } + ``` + +### Compose Issuance [GET /addresses/{address}/compose/issuance] + +Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing or transfering the asset + + asset: `XCPTEST` (str, required) - The assets to issue or transfer. This can also be a subasset longname for new subasset issuances + + quantity: `1000` (int, required) - The quantity of the asset to issue (set to 0 if transferring an asset) + + transfer_destination: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, optional) - The address to receive the asset + + Default: `None` + + divisible (bool, optional) - Whether this asset is divisible or not (if a transfer, this value must match the value specified when the asset was originally issued) + + Default: `True` + + lock (bool, optional) - Whether this issuance should lock supply of this asset forever + + Default: `False` + + reset (bool, optional) - Wether this issuance should reset any existing supply + + Default: `False` + + description (str, optional) - A textual description for the asset + + Default: `None` + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "asset": "XCPTEST", + "quantity": 1000, + "transfer_destination": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "divisible": true, + "lock": false, + "reset": false, + "description": null + }, + "name": "issuance" + } + } + ``` + +### Compose Mpma [GET /addresses/{address}/compose/mpma] + +Composes a transaction to send multiple payments to multiple addresses. + ++ Parameters + + address: `1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + assets: `BAABAABLKSHP,BADHAIRDAY,BADWOJAK` (str, required) - comma-separated list of assets to send + + destinations: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev,1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD,1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - comma-separated list of addresses to send to + + quantities: `1,2,3` (str, required) - comma-separated list of quantities to send + + memo: `"Hello, world!"` (str, required) - The Memo associated with this transaction + + memo_is_hex: `False` (bool, required) - Whether the memo field is a hexadecimal string + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", + "params": { + "source": "1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6", + "asset_dest_quant_list": [ + [ + "BAABAABLKSHP", + "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + 1 + ], + [ + "BADHAIRDAY", + "1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD", + 2 + ], + [ + "BADWOJAK", + "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + 3 + ] + ], + "memo": "\"Hello, world!\"", + "memo_is_hex": false + }, + "name": "mpma" + } + } + ``` + +### Compose Order [GET /addresses/{address}/compose/order] + +Composes a transaction to place an order on the distributed exchange. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) + + give_asset: `XCP` (str, required) - The asset that will be given in the trade + + give_quantity: `1000` (int, required) - The quantity of the asset that will be given + + get_asset: `PEPECASH` (str, required) - The asset that will be received in the trade + + get_quantity: `1000` (int, required) - The quantity of the asset that will be received + + expiration: `100` (int, required) - The number of blocks for which the order should be valid + + fee_required: `100` (int, required) - The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "give_asset": "XCP", + "give_quantity": 1000, + "get_asset": "PEPECASH", + "get_quantity": 1000, + "expiration": 100, + "fee_required": 100 + }, + "name": "order" + } + } + ``` + +### Compose Send [GET /addresses/{address}/compose/send] + +Composes a transaction to send a quantity of an asset to another address. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that will be receiving the asset + + asset: `XCP` (str, required) - The asset or subasset to send + + quantity: `1000` (int, required) - The quantity of the asset to send + + memo (str, optional) - The Memo associated with this transaction + + Default: `None` + + memo_is_hex (bool, optional) - Whether the memo field is a hexadecimal string + + Default: `False` + + use_enhanced_send (bool, optional) - If this is false, the construct a legacy transaction sending bitcoin dust + + Default: `True` + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "destination": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "asset": "XCP", + "quantity": 1000, + "memo": null, + "memo_is_hex": false, + "use_enhanced_send": true + }, + "name": "send" + } + } + ``` + +### Compose Sweep [GET /addresses/{address}/compose/sweep] + +Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. + ++ Parameters + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending + + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address to receive the assets and/or ownerships + + flags: `7` (int, required) - An OR mask of flags indicating how the sweep should be processed. Possible flags are: +- FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. +- FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. +- FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. + + + memo: `FFFF` (str, required) - The Memo associated with this transaction in hex format + + encoding (str, optional) - The encoding method to use + + Default: `auto` + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + Default: `None` + + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + Default: `546` + + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + Default: `1000` + + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + + Default: `0` + + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + + Default: `None` + + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + + Default: `False` + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + Default: `None` + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + Default: `0` + + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + + Default: `None` + + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + + Default: `None` + + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + Default: `False` + + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + + Default: `False` + + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + + Default: `None` + + old_style_api (bool, optional) - Use the old style API + + Default: `True` + + segwit (bool, optional) - Use segwit + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "destination": "1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev", + "flags": 7, + "memo": "FFFF" + }, + "name": "sweep" + } + } + ``` + +## Group Assets + +### Get Valid Assets [GET /assets] + +Returns the valid assets + ++ Parameters + + offset: `0` (int, optional) - The offset of the assets to return + + Default: `0` + + limit: `5` (int, optional) - The limit of the assets to return + + Default: `100` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "asset": "A100000000000000000", + "asset_longname": null + }, + { + "asset": "A1000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000000", + "asset_longname": null + }, + { + "asset": "A10000000000000000001", + "asset_longname": null + }, + { + "asset": "A10000000000000000002", + "asset_longname": null + } + ] + } + ``` + +### Get Asset Info [GET /assets/{asset}] + +Returns the asset information + ++ Parameters + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "owner": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "divisible": false, + "locked": false, + "supply": 1, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "holder_count": 1 + } + } + ``` + +### Get Asset Balances [GET /assets/{asset}/balances] + +Returns the asset balances + ++ Parameters + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + exclude_zero_balances: `True` (bool, optional) - Whether to exclude zero balances + + Default: `True` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1 + } + ] + } + ``` + +### Get Balance By Address And Asset [GET /assets/{asset}/balances/{address}] + +Returns the balance of an address and asset + ++ Parameters + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": { + "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", + "asset": "XCP", + "quantity": 104200000000 + } + } + ``` + +### Get Orders By Asset [GET /assets/{asset}/orders] + +Returns the orders of an asset + ++ Parameters + + asset: `NEEDPEPE` (str, required) - The asset to return + + status: `filled` (str, optional) - The status of the orders to return + + Default: `open` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 825373, + "tx_hash": "0129611a0aece52adddf6d929e75c703baa9cdcb7e4ce887aa859f9640aa9640", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 400000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 2225134, + "tx_hash": "5b6e0c741d765ebd883dc16eecfb5c340c52865cabf297ca2c1432437c1348b7", + "block_index": 772817, + "source": "1FnM7akSCD8G3fRQHCUEXRCfL35gptsPZB", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 80800000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 777817, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 5544, + "fee_provided_remaining": 5544, + "status": "filled" + }, + { + "tx_index": 1946026, + "tx_hash": "75dc6ee1f67317e674ef33b617d3a9839ee53bf4a2e8274c88d6202d4d89b59a", + "block_index": 727444, + "source": "1GotRejB6XsGgMsM79TvcypeanDJRJbMtg", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "XCP", + "get_quantity": 70000000000, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732381, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 2202451, + "tx_hash": "77f568fc6604dbe209d2ea1b0158d7de20723c0178107eb570f4f2a719b0d7c7", + "block_index": 772817, + "source": "184gKLQTtQU29LXbxbYJkUV4if9SmW6v2d", + "give_asset": "XCP", + "give_quantity": 80800000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 773300, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 264, + "fee_provided_remaining": 264, + "status": "filled" + }, + { + "tx_index": 825411, + "tx_hash": "7b2369f40078f4d98a3d3a7733315a1c4efd7977c75f7066dd447d5c7eed7f20", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 300000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 825403, + "tx_hash": "7e1abf6ad57eb61227015fc7a333da034b4dd2f1c4e23cf106864b60a20feef7", + "block_index": 455460, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 200000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456460, + "fee_required": 20000, + "fee_required_remaining": 20000, + "fee_provided": 50766, + "fee_provided_remaining": 50766, + "status": "filled" + }, + { + "tx_index": 825370, + "tx_hash": "8e4d324407b62de773af53f8f7a556882ac82a217c216491a28072f293918fe6", + "block_index": 455457, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 100000000000, + "get_remaining": -1100000000, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 75791, + "fee_provided_remaining": 75791, + "status": "filled" + }, + { + "tx_index": 825413, + "tx_hash": "927878fa98edb6d24310c45254c324f3d5a7f625e2a3a0e7fd1e749b49493750", + "block_index": 455461, + "source": "18cmgoX99Nrm411YKpmTQsp23qczWdxS6w", + "give_asset": "PEPECASH", + "give_quantity": 400000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 460461, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 40000, + "fee_provided_remaining": 40000, + "status": "filled" + }, + { + "tx_index": 1946587, + "tx_hash": "b747f290cbbad6faa1c1c05d5c6d001b5a3ef487027bb0d4eefcdc9f6e865c39", + "block_index": 727444, + "source": "1AtcSh7uxenQ6AR5xqr6agAegWRUF5N4uh", + "give_asset": "XCP", + "give_quantity": 70000000000, + "give_remaining": 0, + "get_asset": "NEEDPEPE", + "get_quantity": 1, + "get_remaining": 0, + "expiration": 5000, + "expire_index": 732444, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 792, + "fee_provided_remaining": 792, + "status": "filled" + }, + { + "tx_index": 825371, + "tx_hash": "b83c96217214decb6316c3619bc88a3471d17e46eb3708406c8f878dedd61610", + "block_index": 455460, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 200000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + }, + { + "tx_index": 825372, + "tx_hash": "e32154f8ade796df0b121604de140703d062d22d1e82e77e629e6096668c812f", + "block_index": 455461, + "source": "1Fpx9NPBJsRbx6RXkvfZ3n1iCYj7n7VaJR", + "give_asset": "NEEDPEPE", + "give_quantity": 1, + "give_remaining": 0, + "get_asset": "PEPECASH", + "get_quantity": 300000000000, + "get_remaining": 0, + "expiration": 1000, + "expire_index": 456457, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 46098, + "fee_provided_remaining": 46098, + "status": "filled" + } + ] + } + ``` + +### Get Credits By Asset [GET /assets/{asset}/credits] + +Returns the credits of an asset + ++ Parameters + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of credits to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the credits to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 840464, + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "quantity": 1, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + ] + } + ``` + +### Get Debits By Asset [GET /assets/{asset}/debits] + +Returns the debits of an asset + ++ Parameters + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of debits to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the debits to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "block_index": 280091, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "action": "send", + "event": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "tx_index": 729 + }, + { + "block_index": 280112, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "tx_index": 749 + }, + { + "block_index": 280112, + "address": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "action": "send", + "event": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "tx_index": 752 + }, + { + "block_index": 280114, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "tx_index": 755 + }, + { + "block_index": 280156, + "address": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "action": "send", + "event": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "tx_index": 766 + } + ] + } + ``` + +### Get Dividends [GET /assets/{asset}/dividends] + +Returns the dividends of an asset + ++ Parameters + + asset: `GMONEYPEPE` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 1914456, + "tx_hash": "30760e413947ebdc80ed7a5ada1bd4466800b87e9976bbe811ad4e2b46546359", + "block_index": 724381, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "ENDTHEFED", + "quantity_per_unit": 1, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1915246, + "tx_hash": "827794cbab3299f80a5b8b8cb8ec29ec3aee1373f7da2c05a156bed902bf4684", + "block_index": 724479, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPDANCING", + "quantity_per_unit": 100, + "fee_paid": 2520000, + "status": "valid" + }, + { + "tx_index": 1920208, + "tx_hash": "7014f1e259531ba9632ca5000c35df5bd47f237318e48955900453ce9c07e917", + "block_index": 724931, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "CTRWOJACK", + "quantity_per_unit": 1111, + "fee_paid": 2700000, + "status": "valid" + }, + { + "tx_index": 1927909, + "tx_hash": "5556fd2b0802cf3bc0abd5001ecbac3adbc5b7c5c46a145a78daeef358c308de", + "block_index": 725654, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "WHITERUSSIAN", + "quantity_per_unit": 1, + "fee_paid": 3220000, + "status": "valid" + }, + { + "tx_index": 1983693, + "tx_hash": "cda646285cc63f758d19b5403070f23e2a6e4b34eb3b86b63a0f56f971345657", + "block_index": 730568, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "A4520591452211866149", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1983842, + "tx_hash": "e4b73dc974cc279b873b78e5dc4a347c08788b02143ae27aa0582f900289be10", + "block_index": 730588, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "NCSWIC", + "quantity_per_unit": 1, + "fee_paid": 4040000, + "status": "valid" + }, + { + "tx_index": 1996395, + "tx_hash": "b342feb1421df107010ad3c8ee2043ded802bdf6cd619862459da3d0f87d6a99", + "block_index": 731994, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKTHEFED", + "quantity_per_unit": 1, + "fee_paid": 4380000, + "status": "valid" + }, + { + "tx_index": 2035947, + "tx_hash": "02d715fd9e8b7bbc782b1b2d92a1b9ffae9326bfc88ba76c453c515ad7c8c2bc", + "block_index": 738763, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "HOLDTHELINE", + "quantity_per_unit": 1, + "fee_paid": 4940000, + "status": "valid" + }, + { + "tx_index": 2174481, + "tx_hash": "b935a06fc34d8fa4f0c526984085b1b12c78e899415e595b625f1bee84ce3709", + "block_index": 762733, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "EOXIXIZERO", + "quantity_per_unit": 1, + "fee_paid": 6500000, + "status": "valid" + }, + { + "tx_index": 2198534, + "tx_hash": "a063e9a745b9f6bc3201f72abff196de20ec106bcc71d820673d516ddbb3aa90", + "block_index": 767569, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "TRUMPCARDS", + "quantity_per_unit": 1, + "fee_paid": 6660000, + "status": "valid" + }, + { + "tx_index": 2704948, + "tx_hash": "437102ca4698f63a12e369f6168e3c7f5f8eef3e225395d515775673e33d39c1", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "FUCKYOUWAR", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + }, + { + "tx_index": 2704949, + "tx_hash": "7d3807cc58fa2d9751b2b0089bfa8fa86ef795821be6d8e9418ab3a819eba299", + "block_index": 832745, + "source": "1JJP986hdU9Qy9b49rafM9FoXdbz1Mgbjo", + "asset": "GMONEYPEPE", + "dividend_asset": "MEDICINEPEPE", + "quantity_per_unit": 1, + "fee_paid": 6840000, + "status": "valid" + } + ] + } + ``` + +### Get Issuances By Asset [GET /assets/{asset}/issuances] + +Returns the issuances of an asset + ++ Parameters + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "msg_index": 0, + "block_index": 840464, + "asset": "UNNEGOTIABLE", + "quantity": 1, + "divisible": 0, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "transfer": 0, + "callable": 0, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "fee_paid": 50000000, + "locked": 0, + "status": "valid", + "asset_longname": null, + "reset": 0 + } + ] + } + ``` + +### Get Sends Or Receives By Asset [GET /assets/{asset}/sends] + +Returns the sends of an asset + ++ Parameters + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of sends to return + + Default: `100` + + offset: `0` (int, optional) - The offset of the sends to return + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 729, + "tx_hash": "1c20d6596f6be031c94def5ad93a52217d76371885adcc53c91c3b1eaf76ccce", + "block_index": 280091, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1000000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 749, + "tx_hash": "4dacd03d73cb497229dbfe2e7209adc4221540efe0e4c57f408b09b2fd36ece6", + "block_index": 280112, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 752, + "tx_hash": "057d10cc33455f4f7af44d2f030b3866e3a16416ecf984e304c76abe98393c1d", + "block_index": 280112, + "source": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "destination": "1PMacKVWDszkBRbb2iWWvX63BwhKUTsSBd", + "asset": "XCP", + "quantity": 100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 755, + "tx_hash": "3ac6ea5b329832e2dc31ead6c5277beccb7d95f0d9f20f256f97067223c81e00", + "block_index": 280114, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + }, + { + "tx_index": 766, + "tx_hash": "66fc1409ac6646bd8c267de89c57d2204e31bb6dfce9ee2a3ab18416fadf9e9c", + "block_index": 280156, + "source": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "destination": "1Pcpxw6wJwXABhjCspe3CNf3gqSeh6eien", + "asset": "XCP", + "quantity": 1100000000, + "status": "valid", + "msg_index": 0, + "memo": null + } + ] + } + ``` + +### Get Dispensers By Asset [GET /assets/{asset}/dispensers] + +Returns the dispensers of an asset + ++ Parameters + + asset: `ERYKAHPEPU` (str, required) - The asset to return + + status (int, optional) - + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + } + ``` + +### Get Dispensers By Address And Asset [GET /assets/{asset}/dispensers/{address}] + +Returns the dispensers of an address and an asset + ++ Parameters + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + + status (int, optional) - + + Default: `0` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2726460, + "tx_hash": "b592d8ca4994d182e4ec63e1659dc4282b1a84466b7d71ed68c281ce63ed4897", + "block_index": 839964, + "source": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "asset": "ERYKAHPEPU", + "give_quantity": 1, + "escrow_quantity": 25, + "satoshirate": 50000, + "status": 0, + "give_remaining": 25, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "dispense_count": 0 + } + ] + } + ``` + +### Get Asset Holders [GET /assets/{asset}/holders] + +Returns the holders of an asset + ++ Parameters + + asset: `ERYKAHPEPU` (str, required) - The asset to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "address": "1E6tyJ2zCyX74XgEK8t9iNMjxjNVLCGR1u", + "address_quantity": 63, + "escrow": null + }, + { + "address": "16yRstRXStVJJ1TN2S4DCWifyrCsetpma7", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qsvqsa9arwz30g2z0w09twzn8gz3380h36yxacs", + "address_quantity": 2, + "escrow": null + }, + { + "address": "17PnWBjHkekZKQPVagmTR5HiD51pN8WHC8", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1FRxFpP9XoRsvZFVqGtt4fjjgKe1h5tbAh", + "address_quantity": 1, + "escrow": null + }, + { + "address": "1AdHg2q3M2rMFRgZyZ7RQyNHdwjSib7wSZ", + "address_quantity": 2, + "escrow": null + }, + { + "address": "1CTnziWXidHzY3qT8gwLa1ZxZK37A7HreR", + "address_quantity": 1, + "escrow": null + }, + { + "address": "bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz", + "address_quantity": 25, + "escrow": null + } + ] + } + ``` + +## Group Orders + +### Get Order [GET /orders/{order_hash}] + +Returns the information of an order + ++ Parameters + + order_hash: `23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776` (str, required) - The hash of the transaction that created the order + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2724132, + "tx_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "block_index": 840381, + "source": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "give_asset": "PEPECASH", + "give_quantity": 6966600000000, + "give_remaining": 900000000000, + "get_asset": "XCP", + "get_quantity": 11076894000, + "get_remaining": 1431000000, + "expiration": 5000, + "expire_index": 843055, + "fee_required": 0, + "fee_required_remaining": 0, + "fee_provided": 4488, + "fee_provided_remaining": 4488, + "status": "open" + } + ] + } + ``` + +### Get Order Matches By Order [GET /orders/{order_hash}/matches] + +Returns the order matches of an order + ++ Parameters + + order_hash: `5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947` (str, required) - The hash of the transaction that created the order + + status: `completed` (str, optional) - The status of the order matches to return + + Default: `pending` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "id": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776_5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx0_index": 2724132, + "tx0_hash": "23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776", + "tx0_address": "15L7U55PAsHLEpQkZqz62e3eqWd9AHb2DH", + "tx1_index": 2726591, + "tx1_hash": "5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947", + "tx1_address": "15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA", + "forward_asset": "PEPECASH", + "forward_quantity": 6066600000000, + "backward_asset": "XCP", + "backward_quantity": 9645894000, + "tx0_block_index": 838055, + "tx1_block_index": 840381, + "block_index": 840381, + "tx0_expiration": 5000, + "tx1_expiration": 8064, + "match_expire_index": 840401, + "fee_paid": 0, + "status": "completed" + } + ] + } + ``` + +### Get Btcpays By Order [GET /orders/{order_hash}/btcpays] + +Returns the BTC pays of an order + ++ Parameters + + order_hash: `299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4` (str, required) - The hash of the transaction that created the order + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2719343, + "tx_hash": "6cfa7f31b43a46e5ad74a9db810bd6cac56235a8ebc73ec63d01b38ea7ea2414", + "block_index": 836188, + "source": "1NfJnJdAdmm2rJCFW54NsAKqqTTMexCNJ3", + "destination": "1BepkwAhEmEuEGF349XjmEUrRvoy9a7Biv", + "btc_amount": 4500000, + "order_match_id": "0a1387df82a8a7e9cec01c52c8fee01f6995c4e39dc5804e1d2bf40d9368f5c5_299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4", + "status": "valid" + } + ] + } + ``` + +## Group Bets + +### Get Bet [GET /bets/{bet_hash}] + +Returns the information of a bet + ++ Parameters + + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 15106, + "tx_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "block_index": 304063, + "source": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "bet_type": 3, + "deadline": 1401828300, + "wager_quantity": 50000000, + "wager_remaining": 0, + "counterwager_quantity": 50000000, + "counterwager_remaining": 0, + "target_value": 1.0, + "leverage": 5040, + "expiration": 11, + "expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "filled" + } + ] + } + ``` + +### Get Bet Matches By Bet [GET /bets/{bet_hash}/matches] + +Returns the bet matches of a bet + ++ Parameters + + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet + + status: `expired` (str, optional) - The status of the bet matches + + Default: `pending` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "id": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed_cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx0_index": 15106, + "tx0_hash": "5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed", + "tx0_address": "18ZNyaAcH4HugeofwbrpLoUNiayxJRH65c", + "tx1_index": 15108, + "tx1_hash": "cb5f888c299a50967d523513daed71636d927e6ef3dbda85feb11ff112ae4330", + "tx1_address": "1PTqJmRCMGs4qBEh2APAFSrBv95Uf1hfiD", + "tx0_bet_type": 3, + "tx1_bet_type": 2, + "feed_address": "1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk", + "initial_value": -1, + "deadline": 1401828300, + "target_value": 1.0, + "leverage": 5040, + "forward_quantity": 50000000, + "backward_quantity": 50000000, + "tx0_block_index": 304062, + "tx1_block_index": 304063, + "block_index": 306379, + "tx0_expiration": 11, + "tx1_expiration": 1459, + "match_expire_index": 304073, + "fee_fraction_int": 1000000, + "status": "expired" + } + ] + } + ``` + +### Get Resolutions By Bet [GET /bets/{bet_hash}/resolutions] + +Returns the resolutions of a bet + ++ Parameters + + bet_hash: `36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace` (str, required) - The hash of the transaction that created the bet + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "bet_match_id": "36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace_d70ee4e44f02fe6258ee0c267f33f304a0fc61d4ce424852f58c28967dc1924f", + "bet_match_type_id": 5, + "block_index": 401128, + "winner": "Equal", + "settled": null, + "bull_credit": null, + "bear_credit": null, + "escrow_less_fee": 2000000, + "fee": 0 + } + ] + } + ``` + +## Group Burns + +### Get All Burns [GET /burns] + +Returns the burns + ++ Parameters + + status: `valid` (str, optional) - The status of the burns to return + + Default: `valid` + + offset: `10` (int, optional) - The offset of the burns to return + + Default: `0` + + limit: `5` (int, optional) - The limit of the burns to return + + Default: `100` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 10, + "tx_hash": "41bbe1ec81da008a0e92758efb6084af3a6b6acf483983456ec797ee59c0e0f1", + "block_index": 278511, + "source": "12crRpZpn93PKTQ4WYxHMw4xi6ckh1CFR3", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 11, + "tx_hash": "c403a92281b568c7d428d942354d026594dc54ae35c21f53ecf5c918208c45de", + "block_index": 278511, + "source": "13UXh9dBEhA48gJiegJNodqe91PK88f4pW", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 12, + "tx_hash": "749ba1c2bd314f7b98e9cfb44575495b4ad2cf624901c65488fbc4f57a3dc0ac", + "block_index": 278511, + "source": "19Ht3rkW7JB9VuC7rsZEGZju96ujzchaZZ", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 13, + "tx_hash": "da330160b71138f9bda5e126df0d5d6248c0879d88e16255c74135274d8ebd27", + "block_index": 278511, + "source": "16Fu8Edsvxqixg6VnaHKPWE2TEsqQMwXfV", + "burned": 99900000, + "earned": 148024554545, + "status": "valid" + }, + { + "tx_index": 14, + "tx_hash": "66994176733650e77ae0cf34349f63e6538649f40f86d2719013d915bbb7701e", + "block_index": 278517, + "source": "14FFaRsfzYQxhZQv1YsMn65MvMLfJShgM8", + "burned": 99900000, + "earned": 147970063636, + "status": "valid" + } + ] + } + ``` + +## Group Dispensers + +### Get Dispenser Info By Hash [GET /dispensers/{dispenser_hash}] + +Returns the dispenser information by tx_hash + ++ Parameters + + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2536311, + "tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "asset": "FLOCK", + "give_quantity": 10000000000, + "escrow_quantity": 250000000000, + "satoshirate": 330000, + "status": 0, + "give_remaining": 140000000000, + "oracle_address": null, + "last_status_tx_hash": null, + "origin": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "dispense_count": 2, + "asset_longname": null + } + ] + } + ``` + +### Get Dispenses By Dispenser [GET /dispensers/{dispenser_hash}/dispenses] + +Returns the dispenses of a dispenser + ++ Parameters + + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_index": 2610745, + "dispense_index": 0, + "tx_hash": "8c95cc6afc8fd466c784fd1c02749c585988999bbc66251b944c443dc31af757", + "block_index": 821450, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "1FKYM1CP9RfttJhNG8HTNQdE2uV3YvwbRB", + "asset": "FLOCK", + "dispense_quantity": 20000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + }, + { + "tx_index": 2726580, + "dispense_index": 0, + "tx_hash": "e7f0f2c9bef7a492b714a5952ec61b283be344419c5bc33f405f9af41ebfa48b", + "block_index": 840322, + "source": "bc1qq735dv8peps2ayr3qwwwdwylq4ddwcgrpyg9r2", + "destination": "bc1qzcdkhnexpjc8wvkyrpyrsn0f5xzcpu877mjmgj", + "asset": "FLOCK", + "dispense_quantity": 90000000000, + "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" + } + ] + } + ``` + +## Group Events + +### Get All Events [GET /events] + +Returns all events + ++ Parameters + + last: `10665092` (int, optional) - The last event index to return + + Default: `None` + + limit: `5` (int, optional) - The maximum number of events to return + + Default: `100` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665091, + "event": "ENHANCED_SEND", + "bindings": { + "asset": "THOTHPEPE", + "block_index": 744232, + "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "memo": null, + "quantity": 1, + "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "status": "valid", + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665089, + "event": "DEBIT", + "bindings": { + "action": "send", + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "THOTHPEPE", + "block_index": 744232, + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665088, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + } + ``` + +### Get Event By Index [GET /events/{event_index}] + +Returns the event of an index + ++ Parameters + + event_index: `10665092` (int, required) - The index of the event to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + } + ``` + +### Get All Events Counts [GET /events/counts] + +Returns the event counts of all blocks + ++ Parameters + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event": "ASSET_CREATION", + "event_count": 235860 + }, + { + "event": "ASSET_DESTRUCTION", + "event_count": 11141 + }, + { + "event": "ASSET_DIVIDEND", + "event_count": 4092 + }, + { + "event": "ASSET_ISSUANCE", + "event_count": 322678 + }, + { + "event": "ASSET_TRANSFER", + "event_count": 10639 + }, + { + "event": "BET_EXPIRATION", + "event_count": 588 + }, + { + "event": "BET_MATCH", + "event_count": 397 + }, + { + "event": "BET_MATCH_EXPIRATION", + "event_count": 9 + }, + { + "event": "BET_MATCH_RESOLUTON", + "event_count": 387 + }, + { + "event": "BET_MATCH_UPDATE", + "event_count": 397 + }, + { + "event": "BET_UPDATE", + "event_count": 1474 + }, + { + "event": "BLOCK_PARSED", + "event_count": 562364 + }, + { + "event": "BROADCAST", + "event_count": 106518 + }, + { + "event": "BTC_PAY", + "event_count": 2921 + }, + { + "event": "BURN", + "event_count": 2576 + }, + { + "event": "CANCEL_BET", + "event_count": 101 + }, + { + "event": "CANCEL_ORDER", + "event_count": 80168 + }, + { + "event": "CREDIT", + "event_count": 3659293 + }, + { + "event": "DEBIT", + "event_count": 2617404 + }, + { + "event": "DISPENSE", + "event_count": 190873 + }, + { + "event": "DISPENSER_UPDATE", + "event_count": 228954 + }, + { + "event": "ENHANCED_SEND", + "event_count": 538426 + }, + { + "event": "MPMA_SEND", + "event_count": 279142 + }, + { + "event": "NEW_BLOCK", + "event_count": 1992 + }, + { + "event": "NEW_TRANSACTION", + "event_count": 4498 + }, + { + "event": "NEW_TRANSACTION_OUTPUT", + "event_count": 596 + }, + { + "event": "OPEN_BET", + "event_count": 1149 + }, + { + "event": "OPEN_DISPENSER", + "event_count": 88229 + }, + { + "event": "OPEN_ORDER", + "event_count": 530117 + }, + { + "event": "OPEN_RPS", + "event_count": 266 + }, + { + "event": "ORDER_EXPIRATION", + "event_count": 195968 + }, + { + "event": "ORDER_FILLED", + "event_count": 805 + }, + { + "event": "ORDER_MATCH", + "event_count": 209415 + }, + { + "event": "ORDER_MATCH_EXPIRATION", + "event_count": 20860 + }, + { + "event": "ORDER_MATCH_UPDATE", + "event_count": 23689 + }, + { + "event": "ORDER_UPDATE", + "event_count": 732646 + }, + { + "event": "REFILL_DISPENSER", + "event_count": 187 + }, + { + "event": "RESET_ISSUANCE", + "event_count": 454 + }, + { + "event": "RPS_EXPIRATION", + "event_count": 59 + }, + { + "event": "RPS_MATCH", + "event_count": 171 + }, + { + "event": "RPS_MATCH_EXPIRATION", + "event_count": 145 + }, + { + "event": "RPS_MATCH_UPDATE", + "event_count": 271 + }, + { + "event": "RPS_RESOLVE", + "event_count": 129 + }, + { + "event": "RPS_UPDATE", + "event_count": 540 + }, + { + "event": "SEND", + "event_count": 805983 + }, + { + "event": "SWEEP", + "event_count": 1020 + }, + { + "event": "TRANSACTION_PARSED", + "event_count": 2723802 + } + ] + } + ``` + +### Get Events By Name [GET /events/{event}] + +Returns the events filtered by event name + ++ Parameters + + event: `CREDIT` (str, required) - The event to return + + last: `10665092` (int, optional) - The last event index to return + + Default: `None` + + limit: `5` (int, optional) - The maximum number of events to return + + Default: `100` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "event_index": 10665090, + "event": "CREDIT", + "bindings": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665085, + "event": "CREDIT", + "bindings": { + "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", + "asset": "XCP", + "block_index": 744232, + "calling_function": "dispense", + "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "quantity": 10000000000, + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665082, + "event": "CREDIT", + "bindings": { + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "FREEDOMKEK", + "block_index": 744232, + "calling_function": "send", + "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", + "quantity": 1, + "tx_index": 2056158 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665078, + "event": "CREDIT", + "bindings": { + "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", + "quantity": 1, + "tx_index": 2056157 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665074, + "event": "CREDIT", + "bindings": { + "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", + "quantity": 2, + "tx_index": 2056156 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + } + ``` + +## Group Healthz + +### Check Server Status [GET /healthz] + +Health check route. + ++ Parameters + + check_type: `light` (str, optional) - Type of health check to perform. Options are 'light' and 'heavy' + + Default: `heavy` + ++ Response 200 (application/json) + + ``` + { + "result": { + "status": "Healthy" + } + } + ``` + +## Group Backend + +### Search Raw Transactions [GET /backend/addresses/{address}/transactions] + +Returns all transactions involving a given address + ++ Parameters + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for + + unconfirmed: `True` (bool, optional) - Include unconfirmed transactions + + Default: `True` + + only_tx_hashes: `True` (bool, optional) - Return only the tx hashes + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_hash": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" + }, + { + "tx_hash": "7ec16c461e3ba2d3acae48fcc8f58c04fba9f307b00c391eab507337ddc0bf16" + }, + { + "tx_hash": "ad35f05767aadd39019122b4f4828ccb059b8121c07be6d36eb1e2ddbe9ac317" + }, + { + "tx_hash": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018" + }, + { + "tx_hash": "aba5810714aa6196fec5538a83bbc281077a84ef2cbce2045b4c9f3c4439f14f" + }, + { + "tx_hash": "23758832e0fc92a7ea303623b8f743219cb8e637e7e7ac9fb6f90641efac9379" + }, + { + "tx_hash": "98bef616ef265dd2f6004683e908d7df97e0c5f322cdf2fb2ebea9a9131cfa79" + }, + { + "tx_hash": "687b875d1dc472aa2fb994c5753c9b9b56e5c6fd1a6de18a92fcb3dc7ba8067e" + }, + { + "tx_hash": "ec97c11ff5cb318505ebe20d7aa3c033816824a79f9a49821ffb584ed7d6c78f" + }, + { + "tx_hash": "c732f0906eeada2113524c6652c17b2784780110bffd4333eb8f719ac0eff3be" + }, + { + "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + }, + { + "tx_hash": "a209e345549cffef6e2190b53ac0222afc965fd618843df5ccbd645a6a7999ee" + } + ] + } + ``` + +### Get Oldest Tx [GET /backend/addresses/{address}/transactions/oldest] + +Get the oldest transaction for an address. + ++ Parameters + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for. + + block_index (int, optional) - The block index to search from. + + Default: `None` + ++ Response 200 (application/json) + + ``` + { + "result": { + "block_index": 833187, + "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + } + } + ``` + +### Get Unspent Txouts [GET /backend/addresses/{address}/utxos] + +Returns a list of unspent outputs for a specific address + ++ Parameters + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for + + unconfirmed (bool, optional) - Include unconfirmed transactions + + Default: `False` + + unspent_tx_hash (str, optional) - Filter by unspent_tx_hash + + Default: `None` + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "vout": 6, + "height": 833559, + "value": 34611, + "confirmations": 7083, + "amount": 0.00034611, + "txid": "98bef616ef265dd2f6004683e908d7df97e0c5f322cdf2fb2ebea9a9131cfa79" + }, + { + "vout": 0, + "height": 833187, + "value": 619481, + "confirmations": 7455, + "amount": 0.00619481, + "txid": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" + }, + { + "vout": 0, + "height": 837379, + "value": 992721, + "confirmations": 3263, + "amount": 0.00992721, + "txid": "ad35f05767aadd39019122b4f4828ccb059b8121c07be6d36eb1e2ddbe9ac317" + }, + { + "vout": 0, + "height": 840640, + "value": 838185, + "confirmations": 2, + "amount": 0.00838185, + "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018" + }, + { + "vout": 0, + "height": 839421, + "value": 336973, + "confirmations": 1221, + "amount": 0.00336973, + "txid": "c732f0906eeada2113524c6652c17b2784780110bffd4333eb8f719ac0eff3be" + }, + { + "vout": 0, + "height": 839462, + "value": 78615, + "confirmations": 1180, + "amount": 0.00078615, + "txid": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" + }, + { + "vout": 0, + "height": 838442, + "value": 557283, + "confirmations": 2200, + "amount": 0.00557283, + "txid": "aba5810714aa6196fec5538a83bbc281077a84ef2cbce2045b4c9f3c4439f14f" + }, + { + "vout": 0, + "height": 838608, + "value": 77148, + "confirmations": 2034, + "amount": 0.00077148, + "txid": "ec97c11ff5cb318505ebe20d7aa3c033816824a79f9a49821ffb584ed7d6c78f" + }, + { + "vout": 0, + "height": 837402, + "value": 70501, + "confirmations": 3240, + "amount": 0.00070501, + "txid": "687b875d1dc472aa2fb994c5753c9b9b56e5c6fd1a6de18a92fcb3dc7ba8067e" + }, + { + "vout": 0, + "height": 839021, + "value": 12354, + "confirmations": 1621, + "amount": 0.00012354, + "txid": "23758832e0fc92a7ea303623b8f743219cb8e637e7e7ac9fb6f90641efac9379" + } + ] + } + ``` + +### Pubkeyhash To Pubkey [GET /backend/addresses/{address}/pubkey] + +Get pubkey for an address. + ++ Parameters + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - Address to get pubkey for. + + provided_pubkeys (str, optional) - Comma separated list of provided pubkeys. + + Default: `None` + ++ Response 200 (application/json) + + ``` + { + "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" + } + ``` + +### Get Raw Transaction [GET /backend/transactions/{tx_hash}] + +Get a raw transaction from the blockchain + ++ Parameters + + tx_hash: `3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018` (str, required) - The transaction hash + + verbose: `True` (bool, optional) - Whether to return JSON output or raw hex + + Default: `False` + ++ Response 200 (application/json) + + ``` + { + "result": { + "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", + "hash": "417c24d7a5539bc5b8496e26528382ac297a85a1c6b891b220f72712405ec300", + "version": 2, + "size": 195, + "vsize": 113, + "weight": 450, + "locktime": 0, + "vin": [ + { + "txid": "fc940430637d22a3d276bde8f7eb489760265cab642d8392f6017d73df94cd7a", + "vout": 2, + "scriptSig": { + "asm": "", + "hex": "" + }, + "txinwitness": [ + "3045022100e4a30e5c0e0f7a28dfcec566cda00d0775a4207744ed6f223a4234cbed87a8ac02205b2403279ba7d8235ea1e8b6497465b97b46f3b3066a58c326822a9b1c25b4a501", + "020e66cffeb4657b40a89063340cf7066030af3c6ce55744ed3570a7aecaa6b0da" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.00838185, + "n": 0, + "scriptPubKey": { + "asm": "OP_DUP OP_HASH160 25f70b0f1512c1742d3301fe34370894c79127bb OP_EQUALVERIFY OP_CHECKSIG", + "desc": "addr(14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS)#68uhm9u9", + "hex": "76a91425f70b0f1512c1742d3301fe34370894c79127bb88ac", + "address": "14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS", + "type": "pubkeyhash" + } + } + ], + "hex": "020000000001017acd94df737d01f692832d64ab5c26609748ebf7e8bd76d2a3227d63300494fc0200000000ffffffff0129ca0c00000000001976a91425f70b0f1512c1742d3301fe34370894c79127bb88ac02483045022100e4a30e5c0e0f7a28dfcec566cda00d0775a4207744ed6f223a4234cbed87a8ac02205b2403279ba7d8235ea1e8b6497465b97b46f3b3066a58c326822a9b1c25b4a50121020e66cffeb4657b40a89063340cf7066030af3c6ce55744ed3570a7aecaa6b0da00000000", + "blockhash": "000000000000000000020f596ed481076b7754143284b47fc8d32642202e5f76", + "confirmations": 2, + "time": 1713951767, + "blocktime": 1713951767 + } + } + ``` + +### Fee Per Kb [GET /backend/estimatesmartfee] + +Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. + ++ Parameters + + conf_target: `2` (int, optional) - Confirmation target in blocks (1 - 1008) + + Default: `3` + + mode: `CONSERVATIVE` (str, optional) - The fee estimate mode. + + Default: `CONSERVATIVE` + ++ Response 200 (application/json) + + ``` + { + "result": 295443 + } + ``` + +## Group Mempool + +### Get All Mempool Events [GET /mempool/events] + +Returns all mempool events + ++ Parameters + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "NEW_TRANSACTION", + "bindings": { + "block_hash": "mempool", + "block_index": 9999999, + "block_time": 1713952590, + "btc_amount": 0, + "data": "0200454ceacf416ccf0000000000000001005461639d06ebc42d541b54b1c5525543ae4d6db3", + "destination": "", + "fee": 9900, + "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "tx_index": 2726767 + }, + "timestamp": 1713952691 + }, + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "ENHANCED_SEND", + "bindings": { + "asset": "FIERCERABBIT", + "destination": "18hARq2fFJxiypHSnZ8yLcbPNpUfaozD8U", + "memo": null, + "quantity": 1, + "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e" + }, + "timestamp": 1713952691 + }, + { + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "event": "TRANSACTION_PARSED", + "bindings": { + "supported": true, + "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", + "tx_index": 2726767 + }, + "timestamp": 1713952691 + } + ] + } + ``` + +### Get Mempool Events By Name [GET /mempool/events/{event}] + +Returns the mempool events filtered by event name + ++ Parameters + + event: `OPEN_ORDER` (str, required) - The event to return + ++ Response 200 (application/json) + + ``` + { + "result": [ + { + "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", + "event": "OPEN_ORDER", + "bindings": { + "expiration": 5000, + "expire_index": 10004999, + "fee_provided": 5016, + "fee_provided_remaining": 5016, + "fee_required": 0, + "fee_required_remaining": 0, + "get_asset": "XCP", + "get_quantity": 3300000000, + "get_remaining": 3300000000, + "give_asset": "PEPEPASSPORT", + "give_quantity": 100000000, + "give_remaining": 100000000, + "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", + "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81" + }, + "timestamp": 1713952690 + }, + { + "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6", + "event": "OPEN_ORDER", + "bindings": { + "expiration": 5000, + "expire_index": 10004999, + "fee_provided": 5016, + "fee_provided_remaining": 5016, + "fee_required": 0, + "fee_required_remaining": 0, + "get_asset": "XCP", + "get_quantity": 1185000000, + "get_remaining": 1185000000, + "give_asset": "FRATPEPE", + "give_quantity": 3, + "give_remaining": 3, + "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", + "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6" + }, + "timestamp": 1713952690 + } + ] + } + ``` diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 2e647460c4..509107ffe2 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -1,15 +1,24 @@ import json import os +import sys import requests from counterpartycore import server CURR_DIR = os.path.dirname(os.path.realpath(__file__)) API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api-v2/rest.md") +API_BLUEPRINT_FILE = os.path.join(CURR_DIR, "../counterparty-core.apib") CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" USE_API_CACHE = True +TARGET_FILE = API_DOC_FILE +TARGET = "docusaurus" + +if len(sys.argv) and sys.argv[1] == "blueprint": + TARGET_FILE = API_BLUEPRINT_FILE + TARGET = "apiary" + def get_example_output(path, args): url_keys = [] @@ -26,11 +35,20 @@ def get_example_output(path, args): return response.json() -md = """--- +root_path = "`/`" if TARGET == "docusaurus" else "/" + +if TARGET == "docusaurus": + md = """--- title: REST API V2 --- +""" +else: + md = "" + +md += """ FORMAT: 1A +HOST: https://api.counterparty.io # Counterparty Core API @@ -73,7 +91,7 @@ def get_example_output(path, args): ## Root Path -### Get Server Info [`/`] +### Get Server Info [GET {root_path}] Returns server information and the list of documented routes in JSON format. @@ -93,6 +111,7 @@ def get_example_output(path, args): ``` """ +md = md.replace("{root_path}", root_path) cache = {} if USE_API_CACHE and os.path.exists(CACHE_FILE): @@ -108,7 +127,11 @@ def get_example_output(path, args): blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") title = " ".join([part.capitalize() for part in str(route["function"].__name__).split("_")]) - md += f"\n### {title} [`{blueprint_path}`]\n\n" + md += f"\n### {title} " + if TARGET == "docusaurus": + md += f"[GET `{blueprint_path}`]\n\n" + else: + md += f"[GET {blueprint_path}]\n\n" md += route["description"] md += "\n\n+ Parameters\n" example_args = {} @@ -140,6 +163,6 @@ def get_example_output(path, args): with open(CACHE_FILE, "w") as f: json.dump(cache, f, indent=4) -with open(API_DOC_FILE, "w") as f: +with open(TARGET_FILE, "w") as f: f.write(md) - print(f"API documentation written to {API_DOC_FILE}") + print(f"API documentation written to {TARGET_FILE}") From 405b2ed1c909ca629ea235608435a47ceeb92c58 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 13:48:26 +0200 Subject: [PATCH 105/128] Introduce counterparty-core.apib; Fix blueprint semantic issues --- counterparty-core/counterparty-core.apib | 105 ++++++++++------------- counterparty-core/tools/genapidoc.py | 38 ++++---- 2 files changed, 70 insertions(+), 73 deletions(-) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib index 9854b1590a..6fc320805b 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core/counterparty-core.apib @@ -1,4 +1,3 @@ - FORMAT: 1A HOST: https://api.counterparty.io @@ -65,7 +64,7 @@ Returns server information and the list of documented routes in JSON format. ## Group Blocks -### Get Blocks [GET /blocks] +### Get Blocks [GET /blocks{?last}{?limit}] Returns the list of the last ten blocks @@ -544,7 +543,7 @@ Returns the issuances of a block } ``` -### Get Sends Or Receives By Block [GET /blocks/{block_index}/sends] +### Get Sends Or Receives By Block [GET /blocks/{block_index}/sends{?limit}{?offset}] Returns the sends of a block @@ -643,7 +642,7 @@ Returns the sweeps of a block ## Group Transactions -### Info [GET /transactions/info] +### Info [GET /transactions/info{?rawtransaction}{?block_index}] Returns Counterparty information from a raw transaction in hex format. @@ -684,7 +683,7 @@ Returns Counterparty information from a raw transaction in hex format. } ``` -### Unpack [GET /transactions/unpack] +### Unpack [GET /transactions/unpack{?datahex}{?block_index}] Unpacks Counterparty data in hex format and returns the message type and data. @@ -788,7 +787,7 @@ Returns the balance of an address and asset } ``` -### Get Credits By Address [GET /addresses/{address}/credits] +### Get Credits By Address [GET /addresses/{address}/credits{?limit}{?offset}] Returns the credits of an address @@ -817,7 +816,7 @@ Returns the credits of an address } ``` -### Get Debits By Address [GET /addresses/{address}/debits] +### Get Debits By Address [GET /addresses/{address}/debits{?limit}{?offset}] Returns the debits of an address @@ -855,7 +854,7 @@ Returns the debits of an address } ``` -### Get Bet By Feed [GET /addresses/{address}/bets] +### Get Bet By Feed [GET /addresses/{address}/bets{?status}] Returns the bets of a feed @@ -911,7 +910,7 @@ Returns the bets of a feed } ``` -### Get Broadcasts By Source [GET /addresses/{address}/broadcasts] +### Get Broadcasts By Source [GET /addresses/{address}/broadcasts{?status}{?order_by}] Returns the broadcasts of a source @@ -980,7 +979,7 @@ Returns the burns of an address } ``` -### Get Send By Address [GET /addresses/{address}/sends] +### Get Send By Address [GET /addresses/{address}/sends{?limit}{?offset}] Returns the sends of an address @@ -1012,7 +1011,7 @@ Returns the sends of an address } ``` -### Get Receive By Address [GET /addresses/{address}/receives] +### Get Receive By Address [GET /addresses/{address}/receives{?limit}{?offset}] Returns the receives of an address @@ -1073,7 +1072,7 @@ Returns the sends of an address and asset } ``` -### Get Receive By Address And Asset [GET /addresses/{address}/receives/{asset}] +### Get Receive By Address And Asset [GET /addresses/{address}/receives/{asset}{?limit}{?offset}] Returns the receives of an address and asset @@ -1106,7 +1105,7 @@ Returns the receives of an address and asset } ``` -### Get Dispensers By Address [GET /addresses/{address}/dispensers] +### Get Dispensers By Address [GET /addresses/{address}/dispensers{?status}] Returns the dispensers of an address @@ -1140,7 +1139,7 @@ Returns the dispensers of an address } ``` -### Get Dispensers By Address And Asset [GET /addresses/{address}/dispensers/{asset}] +### Get Dispensers By Address And Asset [GET /addresses/{address}/dispensers/{asset}{?status}] Returns the dispensers of an address and an asset @@ -1202,7 +1201,7 @@ Returns the sweeps of an address } ``` -### Compose Bet [GET /addresses/{address}/compose/bet] +### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to issue a bet against a feed. @@ -1273,7 +1272,7 @@ Composes a transaction to issue a bet against a feed. } ``` -### Compose Broadcast [GET /addresses/{address}/compose/broadcast] +### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to broadcast textual and numerical information to the network. @@ -1334,7 +1333,7 @@ Composes a transaction to broadcast textual and numerical information to the net } ``` -### Compose Btcpay [GET /addresses/{address}/compose/btcpay] +### Compose Btcpay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to pay for a BTC order match. @@ -1389,7 +1388,7 @@ Composes a transaction to pay for a BTC order match. } ``` -### Compose Burn [GET /addresses/{address}/compose/burn] +### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). @@ -1447,7 +1446,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss } ``` -### Compose Cancel [GET /addresses/{address}/compose/cancel] +### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to cancel an open order or bet. @@ -1502,7 +1501,7 @@ Composes a transaction to cancel an open order or bet. } ``` -### Compose Destroy [GET /addresses/{address}/compose/destroy] +### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to destroy a quantity of an asset. @@ -1561,7 +1560,7 @@ Composes a transaction to destroy a quantity of an asset. } ``` -### Compose Dispenser [GET /addresses/{address}/compose/dispenser] +### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. @@ -1630,7 +1629,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse } ``` -### Compose Dividend [GET /addresses/{address}/compose/dividend] +### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to issue a dividend to holders of a given asset. @@ -1689,7 +1688,7 @@ Composes a transaction to issue a dividend to holders of a given asset. } ``` -### Compose Issuance [GET /addresses/{address}/compose/issuance] +### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. @@ -1761,7 +1760,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo } ``` -### Compose Mpma [GET /addresses/{address}/compose/mpma] +### Compose Mpma [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to send multiple payments to multiple addresses. @@ -1838,7 +1837,7 @@ Composes a transaction to send multiple payments to multiple addresses. } ``` -### Compose Order [GET /addresses/{address}/compose/order] +### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to place an order on the distributed exchange. @@ -1903,7 +1902,7 @@ Composes a transaction to place an order on the distributed exchange. } ``` -### Compose Send [GET /addresses/{address}/compose/send] +### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to send a quantity of an asset to another address. @@ -1971,18 +1970,14 @@ Composes a transaction to send a quantity of an asset to another address. } ``` -### Compose Sweep [GET /addresses/{address}/compose/sweep] +### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. + Parameters + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address to receive the assets and/or ownerships - + flags: `7` (int, required) - An OR mask of flags indicating how the sweep should be processed. Possible flags are: -- FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. -- FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. -- FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. - + + flags: `7` (int, required) - An OR mask of flags indicating how the sweep should be processed. Possible flags are: - FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. - FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. + memo: `FFFF` (str, required) - The Memo associated with this transaction in hex format + encoding (str, optional) - The encoding method to use + Default: `auto` @@ -2036,7 +2031,7 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti ## Group Assets -### Get Valid Assets [GET /assets] +### Get Valid Assets [GET /assets{?offset}{?limit}] Returns the valid assets @@ -2100,7 +2095,7 @@ Returns the asset information } ``` -### Get Asset Balances [GET /assets/{asset}/balances] +### Get Asset Balances [GET /assets/{asset}/balances{?exclude_zero_balances}] Returns the asset balances @@ -2143,7 +2138,7 @@ Returns the balance of an address and asset } ``` -### Get Orders By Asset [GET /assets/{asset}/orders] +### Get Orders By Asset [GET /assets/{asset}/orders{?status}] Returns the orders of an asset @@ -2370,7 +2365,7 @@ Returns the orders of an asset } ``` -### Get Credits By Asset [GET /assets/{asset}/credits] +### Get Credits By Asset [GET /assets/{asset}/credits{?limit}{?offset}] Returns the credits of an asset @@ -2399,7 +2394,7 @@ Returns the credits of an asset } ``` -### Get Debits By Asset [GET /assets/{asset}/debits] +### Get Debits By Asset [GET /assets/{asset}/debits{?limit}{?offset}] Returns the debits of an asset @@ -2649,7 +2644,7 @@ Returns the issuances of an asset } ``` -### Get Sends Or Receives By Asset [GET /assets/{asset}/sends] +### Get Sends Or Receives By Asset [GET /assets/{asset}/sends{?limit}{?offset}] Returns the sends of an asset @@ -2729,7 +2724,7 @@ Returns the sends of an asset } ``` -### Get Dispensers By Asset [GET /assets/{asset}/dispensers] +### Get Dispensers By Asset [GET /assets/{asset}/dispensers{?status}] Returns the dispensers of an asset @@ -2763,7 +2758,7 @@ Returns the dispensers of an asset } ``` -### Get Dispensers By Address And Asset [GET /assets/{asset}/dispensers/{address}] +### Get Dispensers By Address And Asset [GET /assets/{asset}/dispensers/{address}{?status}] Returns the dispensers of an address and an asset @@ -2891,7 +2886,7 @@ Returns the information of an order } ``` -### Get Order Matches By Order [GET /orders/{order_hash}/matches] +### Get Order Matches By Order [GET /orders/{order_hash}/matches{?status}] Returns the order matches of an order @@ -2993,7 +2988,7 @@ Returns the information of a bet } ``` -### Get Bet Matches By Bet [GET /bets/{bet_hash}/matches] +### Get Bet Matches By Bet [GET /bets/{bet_hash}/matches{?status}] Returns the bet matches of a bet @@ -3066,7 +3061,7 @@ Returns the resolutions of a bet ## Group Burns -### Get All Burns [GET /burns] +### Get All Burns [GET /burns{?status}{?offset}{?limit}] Returns the burns @@ -3207,7 +3202,7 @@ Returns the dispenses of a dispenser ## Group Events -### Get All Events [GET /events] +### Get All Events [GET /events{?last}{?limit}] Returns all events @@ -3325,9 +3320,6 @@ Returns the event of an index ### Get All Events Counts [GET /events/counts] Returns the event counts of all blocks - -+ Parameters - + Response 200 (application/json) ``` @@ -3525,7 +3517,7 @@ Returns the event counts of all blocks } ``` -### Get Events By Name [GET /events/{event}] +### Get Events By Name [GET /events/{event}{?last}{?limit}] Returns the events filtered by event name @@ -3622,7 +3614,7 @@ Returns the events filtered by event name ## Group Healthz -### Check Server Status [GET /healthz] +### Check Server Status [GET /healthz{?check_type}] Health check route. @@ -3642,7 +3634,7 @@ Health check route. ## Group Backend -### Search Raw Transactions [GET /backend/addresses/{address}/transactions] +### Search Raw Transactions [GET /backend/addresses/{address}/transactions{?unconfirmed}{?only_tx_hashes}] Returns all transactions involving a given address @@ -3698,7 +3690,7 @@ Returns all transactions involving a given address } ``` -### Get Oldest Tx [GET /backend/addresses/{address}/transactions/oldest] +### Get Oldest Tx [GET /backend/addresses/{address}/transactions/oldest{?block_index}] Get the oldest transaction for an address. @@ -3718,7 +3710,7 @@ Get the oldest transaction for an address. } ``` -### Get Unspent Txouts [GET /backend/addresses/{address}/utxos] +### Get Unspent Txouts [GET /backend/addresses/{address}/utxos{?unconfirmed}{?unspent_tx_hash}] Returns a list of unspent outputs for a specific address @@ -3818,7 +3810,7 @@ Returns a list of unspent outputs for a specific address } ``` -### Pubkeyhash To Pubkey [GET /backend/addresses/{address}/pubkey] +### Pubkeyhash To Pubkey [GET /backend/addresses/{address}/pubkey{?provided_pubkeys}] Get pubkey for an address. @@ -3835,7 +3827,7 @@ Get pubkey for an address. } ``` -### Get Raw Transaction [GET /backend/transactions/{tx_hash}] +### Get Raw Transaction [GET /backend/transactions/{tx_hash}{?verbose}] Get a raw transaction from the blockchain @@ -3893,7 +3885,7 @@ Get a raw transaction from the blockchain } ``` -### Fee Per Kb [GET /backend/estimatesmartfee] +### Fee Per Kb [GET /backend/estimatesmartfee{?conf_target}{?mode}] Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. @@ -3916,9 +3908,6 @@ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` bloc ### Get All Mempool Events [GET /mempool/events] Returns all mempool events - -+ Parameters - + Response 200 (application/json) ``` diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 509107ffe2..71970f7b6b 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -46,8 +46,7 @@ def get_example_output(path, args): else: md = "" -md += """ -FORMAT: 1A +md += """FORMAT: 1A HOST: https://api.counterparty.io # Counterparty Core API @@ -131,22 +130,31 @@ def get_example_output(path, args): if TARGET == "docusaurus": md += f"[GET `{blueprint_path}`]\n\n" else: + for arg in route["args"]: + if f"{{{arg['name']}}}" in blueprint_path: + continue + else: + blueprint_path += f"{{?{arg['name']}}}" md += f"[GET {blueprint_path}]\n\n" + md += route["description"] - md += "\n\n+ Parameters\n" + example_args = {} - for arg in route["args"]: - required = "required" if arg["required"] else "optional" - description = arg.get("description", "") - example_arg = "" - if "(e.g. " in description: - desc_arr = description.split("(e.g. ") - description = desc_arr[0] - example_args[arg["name"]] = desc_arr[1].replace(")", "") - example_arg = f": `{example_args[arg['name']]}`" - md += f" + {arg['name']}{example_arg} ({arg['type']}, {required}) - {description}\n" - if not arg["required"]: - md += f" + Default: `{arg.get('default', '')}`\n" + if len(route["args"]) > 0: + md += "\n\n+ Parameters\n" + for arg in route["args"]: + required = "required" if arg["required"] else "optional" + description = arg.get("description", "") + example_arg = "" + if "(e.g. " in description: + desc_arr = description.split("(e.g. ") + description = desc_arr[0].replace("\n", " ") + example_args[arg["name"]] = desc_arr[1].replace(")", "") + example_arg = f": `{example_args[arg['name']]}`" + md += f" + {arg['name']}{example_arg} ({arg['type']}, {required}) - {description}\n" + if not arg["required"]: + md += f" + Default: `{arg.get('default', '')}`\n" + if example_args != {} or route["args"] == []: if not USE_API_CACHE or path not in cache: example_output = get_example_output(path, example_args) From 06a0309d14ed0d86cb8a42b5f729b9365fa3f4c1 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 14:09:30 +0200 Subject: [PATCH 106/128] Fix API root paragraph --- counterparty-core/counterparty-core.apib | 4 ++-- counterparty-core/tools/genapidoc.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib index 6fc320805b..fa638ad6fc 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core/counterparty-core.apib @@ -40,9 +40,9 @@ Notes: - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. -## Root Path +# Counterparty Core API [/] -### Get Server Info [GET /] +### Get Server Info [GET] Returns server information and the list of documented routes in JSON format. diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 71970f7b6b..659d15fe4f 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -88,9 +88,9 @@ def get_example_output(path, args): - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. -## Root Path +# Counterparty Core API [{root_path}] -### Get Server Info [GET {root_path}] +### Get Server Info [GET] Returns server information and the list of documented routes in JSON format. From ba4e5f86010a5c2f59b9eae296d6073ac27bcac2 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 14:17:09 +0200 Subject: [PATCH 107/128] fix blueprint links in Apiary --- counterparty-core/counterparty-core.apib | 23 +++++++------- counterparty-core/tools/genapidoc.py | 39 +++++++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib index fa638ad6fc..c93a403638 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core/counterparty-core.apib @@ -6,18 +6,17 @@ HOST: https://api.counterparty.io The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. API routes are divided into 11 groups: - -- [`/blocks`](#group-blocks) -- [`/transactions`](#group-transactions) -- [`/addresses`](#group-addresses) -- [`/assets`](#group-assets) -- [`/orders`](#group-orders) -- [`/bets`](#group-bets) -- [`/dispensers`](#group-dispensers) -- [`/burns`](#group-burns) -- [`/events`](#group-events) -- [`/mempool`](#group-mempool) -- [`/backend`](#group-backend) +- [`/blocks`](#/reference/blocks) +- [`/transactions`](#/reference/transactions) +- [`/addresses`](#/reference/addresses) +- [`/assets`](#/reference/assets) +- [`/orders`](#/reference/orders) +- [`/bets`](#/reference/bets) +- [`/dispensers`](#/reference/dispensers) +- [`/burns`](#/reference/burns) +- [`/events`](#/reference/events) +- [`/mempool`](#/reference/mempool) +- [`/backend`](#/reference/backend) Notes: diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 659d15fe4f..ea2303de1e 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -37,6 +37,31 @@ def get_example_output(path, args): root_path = "`/`" if TARGET == "docusaurus" else "/" +GROUPS = [ + "/blocks", + "/transactions", + "/addresses", + "/assets", + "/orders", + "/bets", + "/dispensers", + "/burns", + "/events", + "/mempool", + "/backend", +] + + +def gen_groups_toc(): + toc = "" + for group in GROUPS: + if TARGET == "docusaurus": + toc += f"- [`{group}`](#group{group[1:]})\n" + else: + toc += f"- [`{group}`](#/reference{group})\n" + return toc + + if TARGET == "docusaurus": md = """--- title: REST API V2 @@ -54,19 +79,11 @@ def get_example_output(path, args): The Counterparty Core API is the recommended (and only supported) way to query the state of a Counterparty node. API routes are divided into 11 groups: +""" -- [`/blocks`](#group-blocks) -- [`/transactions`](#group-transactions) -- [`/addresses`](#group-addresses) -- [`/assets`](#group-assets) -- [`/orders`](#group-orders) -- [`/bets`](#group-bets) -- [`/dispensers`](#group-dispensers) -- [`/burns`](#group-burns) -- [`/events`](#group-events) -- [`/mempool`](#group-mempool) -- [`/backend`](#group-backend) +md += gen_groups_toc() +md += """ Notes: - When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. From af4ff3149ad75f12565acc591bd0fc08a2245521 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 14:26:21 +0200 Subject: [PATCH 108/128] fix typos --- counterparty-core/counterparty-core.apib | 2 +- counterparty-core/tools/genapidoc.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib index c93a403638..91eec27b4b 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core/counterparty-core.apib @@ -39,7 +39,7 @@ Notes: - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. -# Counterparty Core API [/] +# Counterparty API Root [/] ### Get Server Info [GET] diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index ea2303de1e..df776bf72a 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -15,7 +15,7 @@ TARGET_FILE = API_DOC_FILE TARGET = "docusaurus" -if len(sys.argv) and sys.argv[1] == "blueprint": +if len(sys.argv) > 1 and sys.argv[1] == "blueprint": TARGET_FILE = API_BLUEPRINT_FILE TARGET = "apiary" @@ -56,7 +56,7 @@ def gen_groups_toc(): toc = "" for group in GROUPS: if TARGET == "docusaurus": - toc += f"- [`{group}`](#group{group[1:]})\n" + toc += f"- [`{group}`](#group-{group[1:]})\n" else: toc += f"- [`{group}`](#/reference{group})\n" return toc @@ -105,7 +105,7 @@ def gen_groups_toc(): - Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. -# Counterparty Core API [{root_path}] +# Counterparty API Root [{root_path}] ### Get Server Info [GET] From 87e367fd5e09f29562710829aa2e777bfd1aed8c Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 14:38:54 +0200 Subject: [PATCH 109/128] Root path always return 200 --- counterparty-core/counterparty-core.apib | 2 +- counterparty-core/tools/genapidoc.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core/counterparty-core.apib index 91eec27b4b..d92e44cd5c 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core/counterparty-core.apib @@ -20,7 +20,7 @@ API routes are divided into 11 groups: Notes: -- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. +- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except `/` and those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. - All API responses contain the following 3 headers: diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index df776bf72a..28ef35b017 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -86,7 +86,7 @@ def gen_groups_toc(): md += """ Notes: -- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. +- When the server is not ready, that is to say when all the blocks are not yet parsed, all routes return a 503 error except `/` and those in the `/blocks`, `/transactions` and `/backend` groups which always return a result. - All API responses contain the following 3 headers: From 016b3a939e902e3ee480fc1f23cad43f9f72ff86 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 15:17:13 +0200 Subject: [PATCH 110/128] Rename blueprint; Include unpacked data in get transaction by hash result --- ...arty-core.apib => counterparty-core.apiary | 26 +++++-- .../counterpartycore/lib/api/routes.py | 2 +- .../counterpartycore/lib/transaction.py | 11 +++ .../counterpartycore/test/api_v2_test.py | 3 +- .../test/fixtures/api_v2_fixtures.json | 71 ++++--------------- counterparty-core/tools/apicache.json | 48 +++++++++---- counterparty-core/tools/genapidoc.py | 2 +- 7 files changed, 84 insertions(+), 79 deletions(-) rename counterparty-core/counterparty-core.apib => counterparty-core.apiary (99%) diff --git a/counterparty-core/counterparty-core.apib b/counterparty-core.apiary similarity index 99% rename from counterparty-core/counterparty-core.apib rename to counterparty-core.apiary index d92e44cd5c..86499b82b5 100644 --- a/counterparty-core/counterparty-core.apib +++ b/counterparty-core.apiary @@ -716,12 +716,12 @@ Unpacks Counterparty data in hex format and returns the message type and data. } ``` -### Get Transaction [GET /transactions/{tx_hash}] +### Get Transaction By Hash [GET /transactions/{tx_hash}] -Returns the information of a transaction +Returns a transaction by its hash. + Parameters - + tx_hash: `876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5` (str, required) - The hash of the transaction to return + + tx_hash: `876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5` (str, required) - The hash of the transaction + Response 200 (application/json) @@ -738,7 +738,25 @@ Returns the information of a transaction "btc_amount": 0, "fee": 56565, "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "supported": 1 + "supported": 1, + "unpacked_data": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } } } ``` diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index e9930e2458..828716933e 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -27,7 +27,7 @@ ### /transactions ### "/transactions/info": transaction.info, "/transactions/unpack": transaction.unpack, - "/transactions/": ledger.get_transaction, + "/transactions/": transaction.get_transaction_by_hash, ### /addresses ### "/addresses/
/balances": ledger.get_address_balances, "/addresses/
/balances/": ledger.get_balance_by_address_and_asset, diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index b121654326..5cc4923c8c 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1653,6 +1653,17 @@ def compose_sweep(db, address: str, destination: str, flags: int, memo: str, **c } +def get_transaction_by_hash(db, tx_hash: str): + """ + Returns a transaction by its hash. + :param tx_hash: The hash of the transaction (e.g. 876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5) + """ + tx = ledger.get_transaction(db, tx_hash) + if tx and tx["data"]: + tx["unpacked_data"] = unpack(db, binascii.hexlify(tx["data"]), tx["block_index"]) + return tx + + def info(db, rawtransaction: str, block_index: int = None): """ Returns Counterparty information from a raw transaction in hex format. diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index a3949efed2..e4fd79a843 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -57,7 +57,8 @@ def test_api_v2(request): result = requests.get(url) # noqa: S113 results[url] = result.json() assert result.status_code == 200 - assert results[url] == fixtures[url] + if not request.config.getoption("saveapifixtures"): + assert results[url] == fixtures[url] if request.config.getoption("saveapifixtures"): with open(API_V2_FIXTURES, "w") as f: diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index 1db6674c03..ebbd0f3106 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -1,6 +1,5 @@ { "http://api:api@localhost:10009/blocks": { - "result": [ { "block_index": 310500, @@ -105,7 +104,6 @@ ] }, "http://api:api@localhost:10009/blocks/310491": { - "result": { "block_index": 310491, "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", @@ -118,7 +116,6 @@ } }, "http://api:api@localhost:10009/blocks/310491/transactions": { - "result": [ { "tx_index": 492, @@ -136,7 +133,6 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events": { - "result": [ { "event_index": 1183, @@ -238,7 +234,6 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events/counts": { - "result": [ { "event": "BLOCK_PARSED", @@ -267,15 +262,12 @@ ] }, "http://api:api@localhost:10009/blocks/310491/events/CREDIT": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/credits": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/debits": { - "result": [ { "block_index": 310491, @@ -289,35 +281,27 @@ ] }, "http://api:api@localhost:10009/blocks/310491/expirations": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/cancels": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/destructions": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/issuances": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/sends": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/dispenses": { - "result": [] }, "http://api:api@localhost:10009/blocks/310491/sweeps": { - "result": [] }, "http://api:api@localhost:10009/transactions/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "result": { "tx_index": 492, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", @@ -329,11 +313,23 @@ "btc_amount": 0, "fee": 6800, "data": "0000000a00000000000000010000000005f5e100000000000000000000000000000c350007d000000000000dbba0", - "supported": 1 + "supported": 1, + "unpacked_data": { + "message_type": "order", + "message_type_id": 10, + "message_data": { + "give_asset": "XCP", + "give_quantity": 100000000, + "get_asset": "BTC", + "get_quantity": 800000, + "expiration": 2000, + "fee_required": 900000, + "status": "open" + } + } } }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances": { - "result": [ { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", @@ -378,7 +374,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/balances/NODIVISIBLE": { - "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "NODIVISIBLE", @@ -386,7 +381,6 @@ } }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/credits": { - "result": [ { "block_index": 310000, @@ -499,7 +493,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/debits": { - "result": [ { "block_index": 310001, @@ -693,7 +686,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/bets": { - "result": [ { "tx_index": 102, @@ -717,7 +709,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/broadcasts": { - "result": [ { "tx_index": 103, @@ -746,7 +737,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/burns": { - "result": [ { "tx_index": 1, @@ -760,7 +750,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends": { - "result": [ { "tx_index": 8, @@ -861,7 +850,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives": { - "result": [ { "tx_index": 483, @@ -878,7 +866,6 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sends/NODIVISIBLE": { - "result": [ { "tx_index": 15, @@ -907,23 +894,18 @@ ] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/receives/NODIVISIBLE": { - "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers": { - "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/dispensers/NODIVISIBLE": { - "result": [] }, "http://api:api@localhost:10009/addresses/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc/sweeps": { - "result": [] }, "http://api:api@localhost:10009/assets": { - "result": [ { "asset": "A95428956661682277", @@ -968,7 +950,6 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE": { - "result": { "asset": "NODIVISIBLE", "asset_longname": null, @@ -982,7 +963,6 @@ } }, "http://api:api@localhost:10009/assets/NODIVISIBLE/balances": { - "result": [ { "address": "1_mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc_mtQheFaSfWELRB2MyMBaiWjdDm6ux9Ezns_2", @@ -1002,7 +982,6 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/balances/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { - "result": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "NODIVISIBLE", @@ -1010,11 +989,9 @@ } }, "http://api:api@localhost:10009/assets/NODIVISIBLE/orders": { - "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/credits": { - "result": [ { "block_index": 310002, @@ -1046,7 +1023,6 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/debits": { - "result": [ { "block_index": 310014, @@ -1069,11 +1045,9 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dividends": { - "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/issuances": { - "result": [ { "tx_index": 3, @@ -1099,7 +1073,6 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/sends": { - "result": [ { "tx_index": 15, @@ -1128,15 +1101,12 @@ ] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers": { - "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/dispensers/mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc": { - "result": [] }, "http://api:api@localhost:10009/assets/NODIVISIBLE/holders": { - "result": [ { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", @@ -1156,7 +1126,6 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "result": [ { "tx_index": 492, @@ -1180,7 +1149,6 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/matches": { - "result": [ { "id": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498_1b294dd8592e76899b1c106782e4c96e63114abd8e3fa09ab6d2d52496b5bf81", @@ -1206,23 +1174,18 @@ ] }, "http://api:api@localhost:10009/orders/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/btcpays": { - "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42": { - "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/matches": { - "result": [] }, "http://api:api@localhost:10009/bets/e566ab052d414d2c9b9d6ffc643bc5d2b31d80976dffe7acceaf2576246f9e42/resolutions": { - "result": [] }, "http://api:api@localhost:10009/burns": { - "result": [ { "tx_index": 1, @@ -1299,15 +1262,12 @@ ] }, "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498": { - "result": [] }, "http://api:api@localhost:10009/dispensers/74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498/dispenses": { - "result": [] }, "http://api:api@localhost:10009/events?limit=5": { - "result": [ { "event_index": 1237, @@ -1378,7 +1338,6 @@ ] }, "http://api:api@localhost:10009/events/10?limit=5": { - "result": [ { "event_index": 10, @@ -1395,7 +1354,6 @@ ] }, "http://api:api@localhost:10009/events/counts?limit=5": { - "result": [ { "event": "ASSET_CREATION", @@ -1484,7 +1442,6 @@ ] }, "http://api:api@localhost:10009/events/CREDIT?limit=5": { - "result": [ { "event_index": 1231, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 49ea5365ac..636107c57b 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -381,21 +381,6 @@ } } }, - "/transactions/": { - "result": { - "tx_index": 2726605, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "block_index": 840464, - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_time": 1713852783, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "destination": "", - "btc_amount": 0, - "fee": 56565, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "supported": 1 - } - }, "/addresses/
/balances": { "result": [ { @@ -2562,5 +2547,38 @@ "dispenser_tx_hash": "753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a" } ] + }, + "/transactions/": { + "result": { + "tx_index": 2726605, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "block_index": 840464, + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_time": 1713852783, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "destination": "", + "btc_amount": 0, + "fee": 56565, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "supported": 1, + "unpacked_data": { + "message_type": "issuance", + "message_type_id": 22, + "message_data": { + "asset_id": 75313533584419238, + "asset": "UNNEGOTIABLE", + "subasset_longname": null, + "quantity": 1, + "divisible": false, + "lock": false, + "reset": false, + "callable": false, + "call_date": 0, + "call_price": 0.0, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "status": "valid" + } + } + } } } \ No newline at end of file diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 28ef35b017..1713567afc 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -7,7 +7,7 @@ CURR_DIR = os.path.dirname(os.path.realpath(__file__)) API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api-v2/rest.md") -API_BLUEPRINT_FILE = os.path.join(CURR_DIR, "../counterparty-core.apib") +API_BLUEPRINT_FILE = os.path.join(CURR_DIR, "../../counterparty-core.apiary") CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" USE_API_CACHE = True From 67f0b9541b356c07ea65d0fcb8d4bfde3be899c5 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 16:06:43 +0200 Subject: [PATCH 111/128] fix apiary filename --- counterparty-core.apiary => apiary.apib | 0 counterparty-core/tools/genapidoc.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename counterparty-core.apiary => apiary.apib (100%) diff --git a/counterparty-core.apiary b/apiary.apib similarity index 100% rename from counterparty-core.apiary rename to apiary.apib diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 1713567afc..ee0b315b12 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -7,7 +7,7 @@ CURR_DIR = os.path.dirname(os.path.realpath(__file__)) API_DOC_FILE = os.path.join(CURR_DIR, "../../../Documentation/docs/advanced/api-v2/rest.md") -API_BLUEPRINT_FILE = os.path.join(CURR_DIR, "../../counterparty-core.apiary") +API_BLUEPRINT_FILE = os.path.join(CURR_DIR, "../../apiary.apib") CACHE_FILE = os.path.join(CURR_DIR, "apicache.json") API_ROOT = "http://api:api@localhost:4000" USE_API_CACHE = True From 8ff2f0761d42250dd778d614d5fe7dccba84ea43 Mon Sep 17 00:00:00 2001 From: Adam Krellenstein Date: Thu, 25 Apr 2024 16:08:18 +0200 Subject: [PATCH 112/128] Add Port to Host --- apiary.apib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiary.apib b/apiary.apib index 86499b82b5..44b1ee448e 100644 --- a/apiary.apib +++ b/apiary.apib @@ -1,5 +1,5 @@ FORMAT: 1A -HOST: https://api.counterparty.io +HOST: https://api.counterparty.io:4000 # Counterparty Core API From 6a9f477d893f65998048e16da61510322bbef229 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 16:19:51 +0200 Subject: [PATCH 113/128] replace /old by /v1 --- counterparty-core/counterpartycore/lib/api/api_v1.py | 6 +++--- counterparty-core/counterpartycore/server.py | 2 +- release-notes/release-notes-v10.1.2.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_v1.py b/counterparty-core/counterpartycore/lib/api/api_v1.py index 55dce5829b..cfb03dcd0d 100644 --- a/counterparty-core/counterpartycore/lib/api/api_v1.py +++ b/counterparty-core/counterpartycore/lib/api/api_v1.py @@ -1074,8 +1074,8 @@ def handle_root(args_path): request_path = args_path.lower() if ( request_path == "old" - or request_path.startswith("old/api/") - or request_path.startswith("old/rpc/") + or request_path.startswith("v1/api/") + or request_path.startswith("v1/rpc/") ): if flask.request.method == "POST": # Need to get those here because it might not be available in this aux function. @@ -1088,7 +1088,7 @@ def handle_root(args_path): else: error = "Invalid method." return flask.Response(error, 405, mimetype="application/json") - elif request_path.startswith("old/rest/"): + elif request_path.startswith("v1/rest/"): if flask.request.method == "GET" or flask.request.method == "POST": # Pass the URL path without /REST/ part and Flask request object. rest_path = args_path.split("/", 1)[1] diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 6739ffd2b7..89270296f0 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -373,7 +373,7 @@ def initialise_config( config.RPC_HOST = "localhost" # The web root directory for API calls, eg. localhost:14000/rpc/ - config.RPC_WEBROOT = "/old/rpc/" + config.RPC_WEBROOT = "/v1/rpc/" # Server API RPC port if rpc_port: diff --git a/release-notes/release-notes-v10.1.2.md b/release-notes/release-notes-v10.1.2.md index 6cc002e42b..d54c75b652 100644 --- a/release-notes/release-notes-v10.1.2.md +++ b/release-notes/release-notes-v10.1.2.md @@ -7,7 +7,7 @@ To continue using the old API you must: - start `counterparty-server` with the flag `----enable-api-v1` - replace port `4100` with port `4000` for mainnet and port `14000` with port `14100` -- prefix all endpoints with `/old/` +- prefix all endpoints with `/v1/` To easily migrate to the new API, an equivalence table is available in the documentation # ChangeLog From 40ea0606101442d1f4aa188d1e0c1666834b88bf Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 16:49:16 +0200 Subject: [PATCH 114/128] backend -> bitcoin --- apiary.apib | 4 ++-- .../counterpartycore/lib/api/api_server.py | 2 +- .../counterpartycore/lib/api/routes.py | 14 +++++++------- counterparty-core/tools/apicache.json | 12 ++++++------ counterparty-core/tools/genapidoc.py | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apiary.apib b/apiary.apib index 86499b82b5..809b91dfd5 100644 --- a/apiary.apib +++ b/apiary.apib @@ -25,8 +25,8 @@ Notes: - All API responses contain the following 3 headers: * `X-COUNTERPARTY-HEIGHT` contains the last block parsed by Counterparty - * `X-BACKEND-HEIGHT` contains the last block known to Bitcoin Core - * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BACKEND-HEIGHT` - 1 + * `X-BITCOIN-HEIGHT` contains the last block known to Bitcoin Core + * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BITCOIN-HEIGHT` - 1 - All API responses follow the following format: diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 9919763e8f..3f5844018b 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -105,7 +105,7 @@ def return_result(http_code, result=None, error=None): response = flask.make_response(to_json(api_result), http_code) response.headers["X-COUNTERPARTY-HEIGHT"] = ledger.CURRENT_BLOCK_INDEX response.headers["X-COUNTERPARTY-READY"] = is_server_ready() - response.headers["X-BACKEND-HEIGHT"] = BACKEND_HEIGHT + response.headers["X-BITCOIN-HEIGHT"] = BACKEND_HEIGHT response.headers["Content-Type"] = "application/json" return response diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 828716933e..2c97b0f617 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -91,13 +91,13 @@ "/events/": ledger.get_events_by_name, ### /healthz ### "/healthz": util.check_server_status, - ### /backend ### - "/backend/addresses/
/transactions": backend.search_raw_transactions, - "/backend/addresses/
/transactions/oldest": backend.get_oldest_tx, - "/backend/addresses/
/utxos": backend.get_unspent_txouts, - "/backend/addresses/
/pubkey": util.pubkeyhash_to_pubkey, - "/backend/transactions/": util.get_raw_transaction, - "/backend/estimatesmartfee": backend.fee_per_kb, + ### /bitcoin ### + "/bitcoin/addresses/
/transactions": backend.search_raw_transactions, + "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_tx, + "/bitcoin/addresses/
/utxos": backend.get_unspent_txouts, + "/bitcoin/addresses/
/pubkey": util.pubkeyhash_to_pubkey, + "/bitcoin/transactions/": util.get_raw_transaction, + "/bitcoin/estimatesmartfee": backend.fee_per_kb, ### /mempool ### "/mempool/events": ledger.get_all_mempool_events, "/mempool/events/": ledger.get_mempool_events_by_name, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 636107c57b..5846ea6a7f 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -2133,7 +2133,7 @@ } ] }, - "/backend/addresses/
/transactions": { + "/bitcoin/addresses/
/transactions": { "result": [ { "tx_hash": "eae4f1dba4d75bda9dd0de12f69a980be267bbc16b7a280a2a4b40c4b3bbb70a" @@ -2173,13 +2173,13 @@ } ] }, - "/backend/addresses/
/transactions/oldest": { + "/bitcoin/addresses/
/transactions/oldest": { "result": { "block_index": 833187, "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" } }, - "/backend/addresses/
/utxos": { + "/bitcoin/addresses/
/utxos": { "result": [ { "vout": 6, @@ -2263,10 +2263,10 @@ } ] }, - "/backend/addresses/
/pubkey": { + "/bitcoin/addresses/
/pubkey": { "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" }, - "/backend/transactions/": { + "/bitcoin/transactions/": { "result": { "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", "hash": "417c24d7a5539bc5b8496e26528382ac297a85a1c6b891b220f72712405ec300", @@ -2368,7 +2368,7 @@ "status": "Healthy" } }, - "/backend/estimatesmartfee": { + "/bitcoin/estimatesmartfee": { "result": 295443 }, "/orders/": { diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index ee0b315b12..4c15651c0f 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -48,7 +48,7 @@ def get_example_output(path, args): "/burns", "/events", "/mempool", - "/backend", + "/bitcoin", ] @@ -91,8 +91,8 @@ def gen_groups_toc(): - All API responses contain the following 3 headers: * `X-COUNTERPARTY-HEIGHT` contains the last block parsed by Counterparty - * `X-BACKEND-HEIGHT` contains the last block known to Bitcoin Core - * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BACKEND-HEIGHT` - 1 + * `X-BITCOIN-HEIGHT` contains the last block known to Bitcoin Core + * `X-COUNTERPARTY-READY` contains true if `X-COUNTERPARTY-HEIGHT` >= `X-BITCOIN-HEIGHT` - 1 - All API responses follow the following format: From 85c9ff7a4c699be49dd92439d846592776e2b8e2 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 17:07:39 +0200 Subject: [PATCH 115/128] fix tests --- counterparty-core/counterpartycore/test/api_v2_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index e4fd79a843..6ca80c2551 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -31,7 +31,7 @@ def test_api_v2(request): dispenser_hash = "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498" event = "CREDIT" event_index = 10 - exclude_routes = ["compose", "unpack", "info", "mempool", "healthz", "backend"] + exclude_routes = ["compose", "unpack", "info", "mempool", "healthz", "bitcoin"] results = {} fixtures = {} with open(API_V2_FIXTURES, "r") as f: From fe5343ab2e790da31cdee908042dca3020bbc4c7 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 17:11:00 +0200 Subject: [PATCH 116/128] AddrindexRS -> Bitcoin Core --- apiary.apib | 18 +++++++++--------- counterparty-core/tools/genapidoc.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apiary.apib b/apiary.apib index 809b91dfd5..347f795fc1 100644 --- a/apiary.apib +++ b/apiary.apib @@ -16,7 +16,7 @@ API routes are divided into 11 groups: - [`/burns`](#/reference/burns) - [`/events`](#/reference/events) - [`/mempool`](#/reference/mempool) -- [`/backend`](#/reference/backend) +- [`/bitcoin`](#/reference/bitcoin) Notes: @@ -37,7 +37,7 @@ Notes: } ``` -- Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. +- Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. # Counterparty API Root [/] @@ -3649,9 +3649,9 @@ Health check route. } ``` -## Group Backend +## Group Bitcoin -### Search Raw Transactions [GET /backend/addresses/{address}/transactions{?unconfirmed}{?only_tx_hashes}] +### Search Raw Transactions [GET /bitcoin/addresses/{address}/transactions{?unconfirmed}{?only_tx_hashes}] Returns all transactions involving a given address @@ -3707,7 +3707,7 @@ Returns all transactions involving a given address } ``` -### Get Oldest Tx [GET /backend/addresses/{address}/transactions/oldest{?block_index}] +### Get Oldest Tx [GET /bitcoin/addresses/{address}/transactions/oldest{?block_index}] Get the oldest transaction for an address. @@ -3727,7 +3727,7 @@ Get the oldest transaction for an address. } ``` -### Get Unspent Txouts [GET /backend/addresses/{address}/utxos{?unconfirmed}{?unspent_tx_hash}] +### Get Unspent Txouts [GET /bitcoin/addresses/{address}/utxos{?unconfirmed}{?unspent_tx_hash}] Returns a list of unspent outputs for a specific address @@ -3827,7 +3827,7 @@ Returns a list of unspent outputs for a specific address } ``` -### Pubkeyhash To Pubkey [GET /backend/addresses/{address}/pubkey{?provided_pubkeys}] +### Pubkeyhash To Pubkey [GET /bitcoin/addresses/{address}/pubkey{?provided_pubkeys}] Get pubkey for an address. @@ -3844,7 +3844,7 @@ Get pubkey for an address. } ``` -### Get Raw Transaction [GET /backend/transactions/{tx_hash}{?verbose}] +### Get Raw Transaction [GET /bitcoin/transactions/{tx_hash}{?verbose}] Get a raw transaction from the blockchain @@ -3902,7 +3902,7 @@ Get a raw transaction from the blockchain } ``` -### Fee Per Kb [GET /backend/estimatesmartfee{?conf_target}{?mode}] +### Fee Per Kb [GET /bitcoin/estimatesmartfee{?conf_target}{?mode}] Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 4c15651c0f..f4b6109fd6 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -103,7 +103,7 @@ def gen_groups_toc(): } ``` -- Routes in the `/backend` group serve as a proxy to make requests to AddrindexRS. +- Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. # Counterparty API Root [{root_path}] From f8cb3a13f80ead68004df0d900659f75f873c65a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 22:10:44 +0200 Subject: [PATCH 117/128] search_raw_transactions -> get_transactions_by_address --- apiary.apib | 2 +- counterparty-core/counterpartycore/lib/api/routes.py | 2 +- .../counterpartycore/lib/transaction.py | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apiary.apib b/apiary.apib index 347f795fc1..a8424a4e70 100644 --- a/apiary.apib +++ b/apiary.apib @@ -3651,7 +3651,7 @@ Health check route. ## Group Bitcoin -### Search Raw Transactions [GET /bitcoin/addresses/{address}/transactions{?unconfirmed}{?only_tx_hashes}] +### Get Transactions By Address [GET /bitcoin/addresses/{address}/transactions{?unconfirmed}{?only_tx_hashes}] Returns all transactions involving a given address diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 2c97b0f617..d6e107e7e9 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -92,7 +92,7 @@ ### /healthz ### "/healthz": util.check_server_status, ### /bitcoin ### - "/bitcoin/addresses/
/transactions": backend.search_raw_transactions, + "/bitcoin/addresses/
/transactions": transaction.get_transactions_by_address, "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_tx, "/bitcoin/addresses/
/utxos": backend.get_unspent_txouts, "/bitcoin/addresses/
/pubkey": util.pubkeyhash_to_pubkey, diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 5cc4923c8c..2cf6c5ead9 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1777,3 +1777,15 @@ def unpack(db, datahex: str, block_index: int = None): "message_type_id": message_type_id, "message_data": message_data, } + + +def get_transactions_by_address( + address: str, unconfirmed: bool = True, only_tx_hashes: bool = False +): + """ + Returns all transactions involving a given address + :param address: The address to search for (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) + :param unconfirmed: Include unconfirmed transactions (e.g. True) + :param only_tx_hashes: Return only the tx hashes (e.g. True) + """ + return backend.search_raw_transactions(address, unconfirmed, only_tx_hashes) From 06d51bcfd599214395cd07e95f7b4ab83de9fc0a Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 22:15:01 +0200 Subject: [PATCH 118/128] get_oldest_tx -> get_oldest_transaction_by_address --- apiary.apib | 2 +- .../counterpartycore/lib/api/routes.py | 4 ++-- .../counterpartycore/lib/backend/__init__.py | 14 ++++++++++++-- .../counterpartycore/lib/transaction.py | 12 ------------ 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/apiary.apib b/apiary.apib index a8424a4e70..dc8fd23d6e 100644 --- a/apiary.apib +++ b/apiary.apib @@ -3707,7 +3707,7 @@ Returns all transactions involving a given address } ``` -### Get Oldest Tx [GET /bitcoin/addresses/{address}/transactions/oldest{?block_index}] +### Get Oldest Transaction By Address [GET /bitcoin/addresses/{address}/transactions/oldest{?block_index}] Get the oldest transaction for an address. diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index d6e107e7e9..edc28c2b91 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -92,8 +92,8 @@ ### /healthz ### "/healthz": util.check_server_status, ### /bitcoin ### - "/bitcoin/addresses/
/transactions": transaction.get_transactions_by_address, - "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_tx, + "/bitcoin/addresses/
/transactions": backend.get_transactions_by_address, + "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_transaction_by_address, "/bitcoin/addresses/
/utxos": backend.get_unspent_txouts, "/bitcoin/addresses/
/pubkey": util.pubkeyhash_to_pubkey, "/bitcoin/transactions/": util.get_raw_transaction, diff --git a/counterparty-core/counterpartycore/lib/backend/__init__.py b/counterparty-core/counterpartycore/lib/backend/__init__.py index 802a8eced2..e99bde9b37 100644 --- a/counterparty-core/counterpartycore/lib/backend/__init__.py +++ b/counterparty-core/counterpartycore/lib/backend/__init__.py @@ -239,22 +239,32 @@ def get_unspent_txouts(address: str, unconfirmed: bool = False, unspent_tx_hash: def search_raw_transactions(address: str, unconfirmed: bool = True, only_tx_hashes: bool = False): + return backend().search_raw_transactions(address, unconfirmed, only_tx_hashes) + + +def get_transactions_by_address( + address: str, unconfirmed: bool = True, only_tx_hashes: bool = False +): """ Returns all transactions involving a given address :param address: The address to search for (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param unconfirmed: Include unconfirmed transactions (e.g. True) :param only_tx_hashes: Return only the tx hashes (e.g. True) """ - return backend().search_raw_transactions(address, unconfirmed, only_tx_hashes) + return search_raw_transactions(address, unconfirmed, only_tx_hashes) def get_oldest_tx(address: str, block_index: int = None): + return backend().get_oldest_tx(address, block_index=block_index) + + +def get_oldest_transaction_by_address(address: str, block_index: int = None): """ Get the oldest transaction for an address. :param address: The address to search for. (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) :param block_index: The block index to search from. """ - return backend().get_oldest_tx(address, block_index=block_index) + return get_oldest_tx(address, block_index=block_index) class UnknownPubKeyError(Exception): diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 2cf6c5ead9..5cc4923c8c 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -1777,15 +1777,3 @@ def unpack(db, datahex: str, block_index: int = None): "message_type_id": message_type_id, "message_data": message_data, } - - -def get_transactions_by_address( - address: str, unconfirmed: bool = True, only_tx_hashes: bool = False -): - """ - Returns all transactions involving a given address - :param address: The address to search for (e.g. 14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS) - :param unconfirmed: Include unconfirmed transactions (e.g. True) - :param only_tx_hashes: Return only the tx hashes (e.g. True) - """ - return backend.search_raw_transactions(address, unconfirmed, only_tx_hashes) From 0b77eb9f38281d69ce2e08e740bc8d0db3f29462 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Thu, 25 Apr 2024 22:17:11 +0200 Subject: [PATCH 119/128] remove raw --- apiary.apib | 4 ++-- counterparty-core/counterpartycore/lib/api/routes.py | 2 +- counterparty-core/counterpartycore/lib/api/util.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apiary.apib b/apiary.apib index dc8fd23d6e..70c35ce6b7 100644 --- a/apiary.apib +++ b/apiary.apib @@ -3844,9 +3844,9 @@ Get pubkey for an address. } ``` -### Get Raw Transaction [GET /bitcoin/transactions/{tx_hash}{?verbose}] +### Get Transaction [GET /bitcoin/transactions/{tx_hash}{?verbose}] -Get a raw transaction from the blockchain +Get a transaction from the blockchain + Parameters + tx_hash: `3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018` (str, required) - The transaction hash diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index edc28c2b91..1eaebb17f6 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -96,7 +96,7 @@ "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_transaction_by_address, "/bitcoin/addresses/
/utxos": backend.get_unspent_txouts, "/bitcoin/addresses/
/pubkey": util.pubkeyhash_to_pubkey, - "/bitcoin/transactions/": util.get_raw_transaction, + "/bitcoin/transactions/": util.get_transaction, "/bitcoin/estimatesmartfee": backend.fee_per_kb, ### /mempool ### "/mempool/events": ledger.get_all_mempool_events, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index e4ab9a1075..c95016ca9d 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -116,9 +116,9 @@ def pubkeyhash_to_pubkey(address: str, provided_pubkeys: str = None): return backend.pubkeyhash_to_pubkey(address, provided_pubkeys=provided_pubkeys_list) -def get_raw_transaction(tx_hash: str, verbose: bool = False): +def get_transaction(tx_hash: str, verbose: bool = False): """ - Get a raw transaction from the blockchain + Get a transaction from the blockchain :param tx_hash: The transaction hash (e.g. 3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018) :param verbose: Whether to return JSON output or raw hex (e.g. True) """ From 94bfee08619bb59f7d35848b931b2240bbfed846 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 09:33:06 +0200 Subject: [PATCH 120/128] tweaks and typos --- apiary.apib | 214 ++++++++---------- .../counterpartycore/lib/api/routes.py | 2 +- .../counterpartycore/lib/ledger.py | 4 +- .../counterpartycore/lib/transaction.py | 13 +- counterparty-core/tools/genapidoc.py | 5 +- release-notes/release-notes-v10.1.2.md | 2 +- 6 files changed, 108 insertions(+), 132 deletions(-) diff --git a/apiary.apib b/apiary.apib index 70c35ce6b7..788168c19c 100644 --- a/apiary.apib +++ b/apiary.apib @@ -293,7 +293,7 @@ Returns the events of a block } ``` -### Get Events Counts By Block [GET /blocks/{block_index}/events/counts] +### Get Event Counts By Block [GET /blocks/{block_index}/events/counts] Returns the event counts of a block @@ -1218,7 +1218,7 @@ Returns the sweeps of an address } ``` -### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to issue a bet against a feed. @@ -1236,11 +1236,11 @@ Composes a transaction to issue a bet against a feed. + Default: `None` + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1248,22 +1248,20 @@ Composes a transaction to issue a bet against a feed. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1289,7 +1287,7 @@ Composes a transaction to issue a bet against a feed. } ``` -### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to broadcast textual and numerical information to the network. @@ -1301,11 +1299,11 @@ Composes a transaction to broadcast textual and numerical information to the net + text: `"Hello, world!"` (str, required) - The textual part of the broadcast + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1313,22 +1311,20 @@ Composes a transaction to broadcast textual and numerical information to the net + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1350,7 +1346,7 @@ Composes a transaction to broadcast textual and numerical information to the net } ``` -### Compose Btcpay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose BTCPay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to pay for a BTC order match. @@ -1359,11 +1355,11 @@ Composes a transaction to pay for a BTC order match. + order_match_id: `e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2` (str, required) - The ID of the order match to pay for + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1371,22 +1367,20 @@ Composes a transaction to pay for a BTC order match. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1405,7 +1399,7 @@ Composes a transaction to pay for a BTC order match. } ``` -### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). @@ -1416,11 +1410,11 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss + Default: `False` + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1428,22 +1422,20 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1463,7 +1455,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss } ``` -### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to cancel an open order or bet. @@ -1472,11 +1464,11 @@ Composes a transaction to cancel an open order or bet. + offer_hash: `8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a` (str, required) - The hash of the order/bet to be cancelled + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1484,22 +1476,20 @@ Composes a transaction to cancel an open order or bet. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1518,7 +1508,7 @@ Composes a transaction to cancel an open order or bet. } ``` -### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to destroy a quantity of an asset. @@ -1529,11 +1519,11 @@ Composes a transaction to destroy a quantity of an asset. + tag: `"bugs!"` (str, required) - A tag for the destruction + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1541,22 +1531,20 @@ Composes a transaction to destroy a quantity of an asset. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1577,7 +1565,7 @@ Composes a transaction to destroy a quantity of an asset. } ``` -### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. @@ -1594,11 +1582,11 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse + Default: `None` + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1606,22 +1594,20 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1646,7 +1632,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse } ``` -### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to issue a dividend to holders of a given asset. @@ -1657,11 +1643,11 @@ Composes a transaction to issue a dividend to holders of a given asset. + dividend_asset: `XCP` (str, required) - The asset or subasset that the dividends are paid in + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1669,22 +1655,20 @@ Composes a transaction to issue a dividend to holders of a given asset. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1705,7 +1689,7 @@ Composes a transaction to issue a dividend to holders of a given asset. } ``` -### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. @@ -1725,11 +1709,11 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo + Default: `None` + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1737,22 +1721,20 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1777,7 +1759,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo } ``` -### Compose Mpma [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose MPMA [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to send multiple payments to multiple addresses. @@ -1790,11 +1772,11 @@ Composes a transaction to send multiple payments to multiple addresses. + memo_is_hex: `False` (bool, required) - Whether the memo field is a hexadecimal string + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1802,22 +1784,20 @@ Composes a transaction to send multiple payments to multiple addresses. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1854,7 +1834,7 @@ Composes a transaction to send multiple payments to multiple addresses. } ``` -### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to place an order on the distributed exchange. @@ -1868,11 +1848,11 @@ Composes a transaction to place an order on the distributed exchange. + fee_required: `100` (int, required) - The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1880,22 +1860,20 @@ Composes a transaction to place an order on the distributed exchange. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1919,7 +1897,7 @@ Composes a transaction to place an order on the distributed exchange. } ``` -### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to send a quantity of an asset to another address. @@ -1936,11 +1914,11 @@ Composes a transaction to send a quantity of an asset to another address. + Default: `True` + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -1948,22 +1926,20 @@ Composes a transaction to send a quantity of an asset to another address. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -1987,7 +1963,7 @@ Composes a transaction to send a quantity of an asset to another address. } ``` -### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?old_style_api}{?segwit}] +### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. @@ -1998,11 +1974,11 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti + memo: `FFFF` (str, required) - The Memo associated with this transaction in hex format + encoding (str, optional) - The encoding method to use + Default: `auto` - + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi) + + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) + Default: `None` - + regular_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output. + + regular_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output. + Default: `546` - + multisig_dust_size (int, optional) - Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output + + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away + Default: `0` @@ -2010,22 +1986,20 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs + Default: `False` - + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose + + fee (int, optional) - If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose + Default: `None` - + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value + + fee_provided (int, optional) - If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value + Default: `0` + unspent_tx_hash (str, optional) - When compiling the UTXOs to use as inputs for the transaction being created, only consider unspent outputs from this specific transaction hash. Defaults to null to consider all UTXOs for the address. Do not use this parameter if you are specifying custom_inputs + Default: `None` + dust_return_pubkey (str, optional) - The dust return pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. By default, this pubkey is taken from the pubkey used in the first transaction input. However, it can be overridden here (and is required to be specified if a P2SH input is used and multisig is used as the data output encoding.) If specified, specify the public key (in hex format) where dust will be returned to so that it can be reclaimed. Only valid/useful when used with transactions that utilize multisig data encoding. Note that if this value is set to false, this instructs counterparty-server to use the default dust return pubkey configured at the node level. If this default is not set at the node level, the call will generate an exception + Default: `None` - + disable_utxo_locks (bool, optional) - By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + + disable_utxo_locks (bool, optional) - By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs + Default: `False` + extended_tx_info (bool, optional) - When this is not specified or false, the create_ calls return only a hex-encoded string. If this is true, the create_ calls return a data object with the following keys: tx_hex, btc_in, btc_out, btc_change, and btc_fee + Default: `False` + p2sh_pretx_txid (str, optional) - The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction + Default: `None` - + old_style_api (bool, optional) - Use the old style API - + Default: `True` + segwit (bool, optional) - Use segwit + Default: `False` @@ -2942,7 +2916,7 @@ Returns the order matches of an order } ``` -### Get Btcpays By Order [GET /orders/{order_hash}/btcpays] +### Get BTCPays By Order [GET /orders/{order_hash}/btcpays] Returns the BTC pays of an order @@ -3827,7 +3801,7 @@ Returns a list of unspent outputs for a specific address } ``` -### Pubkeyhash To Pubkey [GET /bitcoin/addresses/{address}/pubkey{?provided_pubkeys}] +### PubKeyHash To Pubkey [GET /bitcoin/addresses/{address}/pubkey{?provided_pubkeys}] Get pubkey for an address. diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 1eaebb17f6..1488c7e800 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -13,7 +13,7 @@ "/blocks/": ledger.get_block, "/blocks//transactions": ledger.get_transactions_by_block, "/blocks//events": ledger.get_events_by_block, - "/blocks//events/counts": ledger.get_events_counts_by_block, + "/blocks//events/counts": ledger.get_event_counts_by_block, "/blocks//events/": ledger.get_events_by_block_and_event, "/blocks//credits": ledger.get_credits_by_block, "/blocks//debits": ledger.get_debits_by_block, diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 3ebbaee5ce..f6fb562887 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -129,7 +129,7 @@ def get_events_by_block_and_event(db, block_index: int, event: str): :param str event: The event to filter by (e.g. CREDIT) """ if event == "count": - return get_events_counts_by_block(db, block_index=block_index) + return get_event_counts_by_block(db, block_index=block_index) return get_events(db, block_index=block_index, event=event) @@ -203,7 +203,7 @@ def get_events_counts(db, block_index=None): return cursor.fetchall() -def get_events_counts_by_block(db, block_index: int): +def get_event_counts_by_block(db, block_index: int): """ Returns the event counts of a block :param int block_index: The index of the block to return (e.g. 840464) diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index 5cc4923c8c..b29a6506c4 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -961,17 +961,17 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): "fee_per_kb": ( int, None, - "The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshi)", + "The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis)", ), "regular_dust_size": ( int, config.DEFAULT_REGULAR_DUST_SIZE, - "Specify (in satoshi) to override the (dust) amount of BTC used for each non-(bare) multisig output.", + "Specify (in satoshis) to override the (dust) amount of BTC used for each non-(bare) multisig output.", ), "multisig_dust_size": ( int, config.DEFAULT_MULTISIG_DUST_SIZE, - "Specify (in satoshi) to override the (dust) amount of BTC used for each (bare) multisig output", + "Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output", ), "op_return_value": ( int, @@ -991,12 +991,12 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): "fee": ( int, None, - "If you'd like to specify a custom miners' fee, specify it here (in satoshi). Leave as default for the server to automatically choose", + "If you'd like to specify a custom miners' fee, specify it here (in satoshis). Leave as default for the server to automatically choose", ), "fee_provided": ( int, 0, - "If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshi). This differs from fee in that this is an upper bound value, which fee is an exact value", + "If you would like to specify a maximum fee (up to and including which may be used as the transaction fee), specify it here (in satoshis). This differs from fee in that this is an upper bound value, which fee is an exact value", ), "unspent_tx_hash": ( str, @@ -1011,7 +1011,7 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): "disable_utxo_locks": ( bool, False, - "By default, UTXO's utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs", + "By default, UTXOs utilized when creating a transaction are 'locked' for a few seconds, to prevent a case where rapidly generating create_ calls reuse UTXOs due to their spent status not being updated in bitcoind yet. Specify true for this parameter to disable this behavior, and not temporarily lock UTXOs", ), "extended_tx_info": ( bool, @@ -1023,7 +1023,6 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): None, "The previous transaction txid for a two part P2SH message. This txid must be taken from the signed transaction", ), - "old_style_api": (bool, True, "Use the old style API"), "segwit": (bool, False, "Use segwit"), } diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index f4b6109fd6..8800ddc6a6 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -64,7 +64,7 @@ def gen_groups_toc(): if TARGET == "docusaurus": md = """--- -title: REST API V2 +title: ReST API V2 --- """ @@ -143,6 +143,9 @@ def gen_groups_toc(): blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") title = " ".join([part.capitalize() for part in str(route["function"].__name__).split("_")]) + title = title.replace("Pubkeyhash", "PubKeyHash") + title = title.replace("Mpma", "MPMA") + title = title.replace("Btcpay", "BTCPay") md += f"\n### {title} " if TARGET == "docusaurus": md += f"[GET `{blueprint_path}`]\n\n" diff --git a/release-notes/release-notes-v10.1.2.md b/release-notes/release-notes-v10.1.2.md index d54c75b652..14afd14532 100644 --- a/release-notes/release-notes-v10.1.2.md +++ b/release-notes/release-notes-v10.1.2.md @@ -16,7 +16,7 @@ To easily migrate to the new API, an equivalence table is available in the docum * Fix logging of some raw tracebacks (#1715) ## Codebase -* New REST API +* New ReST API ## Command-Line Interface From f175dd01634bf61f2f34b7fdf1a6bc67172cfd8d Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 10:16:39 +0200 Subject: [PATCH 121/128] more tweaks and typos --- apiary.apib | 340 +++++++++--------- .../counterpartycore/lib/api/routes.py | 6 +- .../counterpartycore/lib/api/util.py | 2 +- .../counterpartycore/lib/ledger.py | 6 +- counterparty-core/tools/genapidoc.py | 8 +- 5 files changed, 183 insertions(+), 179 deletions(-) diff --git a/apiary.apib b/apiary.apib index 788168c19c..cbed6f4f77 100644 --- a/apiary.apib +++ b/apiary.apib @@ -68,9 +68,9 @@ Returns server information and the list of documented routes in JSON format. Returns the list of the last ten blocks + Parameters - + last: `840000` (int, optional) - The index of the most recent block to return + + last: `840000` (int, optional) - The index of the most recent block to return + Default: `None` - + limit: `2` (int, optional) - The number of blocks to return + + limit: `2` (int, optional) - The number of blocks to return + Default: `10` + Response 200 (application/json) @@ -107,7 +107,7 @@ Returns the list of the last ten blocks Return the information of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -131,7 +131,7 @@ Return the information of a block Returns the transactions of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -160,7 +160,7 @@ Returns the transactions of a block Returns the events of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -298,7 +298,7 @@ Returns the events of a block Returns the event counts of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -346,8 +346,8 @@ Returns the event counts of a block Returns the events of a block filtered by event + Parameters - + block_index: `840464` (int, required) - The index of the block to return - + event: `CREDIT` (str, required) - The event to filter by + + block_index: `840464` (int, required) - The index of the block to return + + event: `CREDIT` (str, required) - The event to filter by + Response 200 (application/json) @@ -378,7 +378,7 @@ Returns the events of a block filtered by event Returns the credits of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -403,7 +403,7 @@ Returns the credits of a block Returns the debits of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -428,7 +428,7 @@ Returns the debits of a block Returns the expirations of a block + Parameters - + block_index: `840356` (int, required) - The index of the block to return + + block_index: `840356` (int, required) - The index of the block to return + Response 200 (application/json) @@ -452,7 +452,7 @@ Returns the expirations of a block Returns the cancels of a block + Parameters - + block_index: `839746` (int, required) - The index of the block to return + + block_index: `839746` (int, required) - The index of the block to return + Response 200 (application/json) @@ -484,7 +484,7 @@ Returns the cancels of a block Returns the destructions of a block + Parameters - + block_index: `839988` (int, required) - The index of the block to return + + block_index: `839988` (int, required) - The index of the block to return + Response 200 (application/json) @@ -510,7 +510,7 @@ Returns the destructions of a block Returns the issuances of a block + Parameters - + block_index: `840464` (int, required) - The index of the block to return + + block_index: `840464` (int, required) - The index of the block to return + Response 200 (application/json) @@ -542,15 +542,15 @@ Returns the issuances of a block } ``` -### Get Sends Or Receives By Block [GET /blocks/{block_index}/sends{?limit}{?offset}] +### Get Sends By Block [GET /blocks/{block_index}/sends{?limit}{?offset}] Returns the sends of a block + Parameters - + block_index: `840459` (int, required) - The index of the block to return - + limit (int, optional) - + + block_index: `840459` (int, required) - The index of the block to return + + limit: `5` (int, optional) - The maximum number of sends to return + Default: `100` - + offset (int, optional) - + + offset: `0` (int, optional) - The offset of the sends to return + Default: `0` + Response 200 (application/json) @@ -579,7 +579,7 @@ Returns the sends of a block Returns the dispenses of a block + Parameters - + block_index: `840322` (int, required) - The index of the block to return + + block_index: `840322` (int, required) - The index of the block to return + Response 200 (application/json) @@ -606,7 +606,7 @@ Returns the dispenses of a block Returns the sweeps of a block + Parameters - + block_index: `836519` (int, required) - The index of the block to return + + block_index: `836519` (int, required) - The index of the block to return + Response 200 (application/json) @@ -646,7 +646,7 @@ Returns the sweeps of a block Returns Counterparty information from a raw transaction in hex format. + Parameters - + rawtransaction: `01000000017828697743c03aef6a3a8ba54b22bf579ffcab8161faf20e7b20c4ecd75cc986010000006b483045022100d1bd0531bb1ed2dd2cbf77d6933273e792a3dbfa84327d419169850ddd5976f502205d1ab0f7bcbf1a0cc183f0520c9aa8f711d41cb790c0c4ac39da6da4a093d798012103d3b1f711e907acb556e239f6cafb6a4f7fe40d8dd809b0e06e739c2afd73f202ffffffff0200000000000000004d6a4bf29880b93b0711524c7ef9c76835752088db8bd4113a3daf41fc45ffdc8867ebdbf26817fae377696f36790e52f51005806e9399a427172fedf348cf798ed86e548002ee96909eef0775ec3c2b0100000000001976a91443434cf159cc585fbd74daa9c4b833235b19761b88ac00000000` (str, required) - Raw transaction in hex format + + rawtransaction: `01000000017828697743c03aef6a3a8ba54b22bf579ffcab8161faf20e7b20c4ecd75cc986010000006b483045022100d1bd0531bb1ed2dd2cbf77d6933273e792a3dbfa84327d419169850ddd5976f502205d1ab0f7bcbf1a0cc183f0520c9aa8f711d41cb790c0c4ac39da6da4a093d798012103d3b1f711e907acb556e239f6cafb6a4f7fe40d8dd809b0e06e739c2afd73f202ffffffff0200000000000000004d6a4bf29880b93b0711524c7ef9c76835752088db8bd4113a3daf41fc45ffdc8867ebdbf26817fae377696f36790e52f51005806e9399a427172fedf348cf798ed86e548002ee96909eef0775ec3c2b0100000000001976a91443434cf159cc585fbd74daa9c4b833235b19761b88ac00000000` (str, required) - Raw transaction in hex format + block_index (int, optional) - Block index mandatory for transactions before block 335000 + Default: `None` @@ -687,7 +687,7 @@ Returns Counterparty information from a raw transaction in hex format. Unpacks Counterparty data in hex format and returns the message type and data. + Parameters - + datahex: `16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245` (str, required) - Data in hex format + + datahex: `16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245` (str, required) - Data in hex format + block_index (int, optional) - Block index of the transaction containing this data + Default: `None` @@ -721,7 +721,7 @@ Unpacks Counterparty data in hex format and returns the message type and data. Returns a transaction by its hash. + Parameters - + tx_hash: `876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5` (str, required) - The hash of the transaction + + tx_hash: `876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5` (str, required) - The hash of the transaction + Response 200 (application/json) @@ -768,7 +768,7 @@ Returns a transaction by its hash. Returns the balances of an address + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + Response 200 (application/json) @@ -789,8 +789,8 @@ Returns the balances of an address Returns the balance of an address and asset + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return - + asset: `XCP` (str, required) - The asset to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + Response 200 (application/json) @@ -809,10 +809,10 @@ Returns the balance of an address and asset Returns the credits of an address + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return - + limit: `5` (int, optional) - The maximum number of credits to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of credits to return + Default: `100` - + offset: `0` (int, optional) - The offset of the credits to return + + offset: `0` (int, optional) - The offset of the credits to return + Default: `0` + Response 200 (application/json) @@ -838,10 +838,10 @@ Returns the credits of an address Returns the debits of an address + Parameters - + address: `bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y` (str, required) - The address to return - + limit: `5` (int, optional) - The maximum number of debits to return + + address: `bc1q7787j6msqczs58asdtetchl3zwe8ruj57p9r9y` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of debits to return + Default: `100` - + offset: `0` (int, optional) - The offset of the debits to return + + offset: `0` (int, optional) - The offset of the debits to return + Default: `0` + Response 200 (application/json) @@ -876,8 +876,8 @@ Returns the debits of an address Returns the bets of a feed + Parameters - + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address of the feed - + status: `filled` (str, optional) - The status of the bet + + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address of the feed + + status: `filled` (str, optional) - The status of the bet + Default: `open` + Response 200 (application/json) @@ -932,10 +932,10 @@ Returns the bets of a feed Returns the broadcasts of a source + Parameters - + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address to return - + status: `valid` (str, optional) - The status of the broadcasts to return + + address: `1QKEpuxEmdp428KEBSDZAKL46noSXWJBkk` (str, required) - The address to return + + status: `valid` (str, optional) - The status of the broadcasts to return + Default: `valid` - + order_by: `ASC` (str, optional) - The order of the broadcasts to return + + order_by: `ASC` (str, optional) - The order of the broadcasts to return + Default: `DESC` + Response 200 (application/json) @@ -976,7 +976,7 @@ Returns the broadcasts of a source Returns the burns of an address + Parameters - + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + Response 200 (application/json) @@ -1001,10 +1001,10 @@ Returns the burns of an address Returns the sends of an address + Parameters - + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return - + limit: `5` (int, optional) - The maximum number of sends to return + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of sends to return + Default: `100` - + offset: `0` (int, optional) - The offset of the sends to return + + offset: `0` (int, optional) - The offset of the sends to return + Default: `0` + Response 200 (application/json) @@ -1033,10 +1033,10 @@ Returns the sends of an address Returns the receives of an address + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return - + limit: `5` (int, optional) - The maximum number of receives to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + limit: `5` (int, optional) - The maximum number of receives to return + Default: `100` - + offset: `0` (int, optional) - The offset of the receives to return + + offset: `0` (int, optional) - The offset of the receives to return + Default: `0` + Response 200 (application/json) @@ -1065,8 +1065,8 @@ Returns the receives of an address Returns the sends of an address and asset + Parameters - + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return - + asset: `XCP` (str, required) - The asset to return + + address: `1HVgrYx3U258KwvBEvuG7R8ss1RN2Z9J1W` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + Response 200 (application/json) @@ -1094,11 +1094,11 @@ Returns the sends of an address and asset Returns the receives of an address and asset + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return - + asset: `XCP` (str, required) - The asset to return - + limit: `5` (int, optional) - The maximum number of receives to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of receives to return + Default: `100` - + offset: `0` (int, optional) - The offset of the receives to return + + offset: `0` (int, optional) - The offset of the receives to return + Default: `0` + Response 200 (application/json) @@ -1127,7 +1127,7 @@ Returns the receives of an address and asset Returns the dispensers of an address + Parameters - + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + status (int, optional) - + Default: `0` @@ -1161,8 +1161,8 @@ Returns the dispensers of an address Returns the dispensers of an address and an asset + Parameters - + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return - + asset: `ERYKAHPEPU` (str, required) - The asset to return + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + status (int, optional) - + Default: `0` @@ -1196,7 +1196,7 @@ Returns the dispensers of an address and an asset Returns the sweeps of an address + Parameters - + address: `18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87` (str, required) - The address to return + + address: `18szqTVJUWwYrtRHq98Wn4DhCGGiy3jZ87` (str, required) - The address to return + Response 200 (application/json) @@ -1223,16 +1223,16 @@ Returns the sweeps of an address Composes a transaction to issue a bet against a feed. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will make the bet - + feed_address: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that hosts the feed to be bet on - + bet_type: `2` (int, required) - Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual - + deadline: `3000000000` (int, required) - The time at which the bet should be decided/settled, in Unix time (seconds since epoch) - + wager_quantity: `1000` (int, required) - The quantities of XCP to wager (in satoshis, hence integer) - + counterwager_quantity: `1000` (int, required) - The minimum quantities of XCP to be wagered against, for the bets to match - + expiration: `100` (int, required) - The number of blocks after which the bet expires if it remains unmatched + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will make the bet + + feed_address: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that hosts the feed to be bet on + + bet_type: `2` (int, required) - Bet 0 for Bullish CFD (deprecated), 1 for Bearish CFD (deprecated), 2 for Equal, 3 for NotEqual + + deadline: `3000000000` (int, required) - The time at which the bet should be decided/settled, in Unix time (seconds since epoch) + + wager_quantity: `1000` (int, required) - The quantities of XCP to wager (in satoshis, hence integer) + + counterwager_quantity: `1000` (int, required) - The minimum quantities of XCP to be wagered against, for the bets to match + + expiration: `100` (int, required) - The number of blocks after which the bet expires if it remains unmatched + leverage (int, optional) - Leverage, as a fraction of 5040 + Default: `5040` - + target_value: `1000` (int, optional) - Target value for Equal/NotEqual bet + + target_value: `1000` (int, optional) - Target value for Equal/NotEqual bet + Default: `None` + encoding (str, optional) - The encoding method to use + Default: `auto` @@ -1292,11 +1292,11 @@ Composes a transaction to issue a bet against a feed. Composes a transaction to broadcast textual and numerical information to the network. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) - + timestamp: `4003903983` (int, required) - The timestamp of the broadcast, in Unix time - + value: `100` (float, required) - Numerical value of the broadcast - + fee_fraction: `0.05` (float, required) - How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) - + text: `"Hello, world!"` (str, required) - The textual part of the broadcast + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + timestamp: `4003903983` (int, required) - The timestamp of the broadcast, in Unix time + + value: `100` (float, required) - Numerical value of the broadcast + + fee_fraction: `0.05` (float, required) - How much of every bet on this feed should go to its operator; a fraction of 1, (i.e. 0.05 is five percent) + + text: `"Hello, world!"` (str, required) - The textual part of the broadcast + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1351,8 +1351,8 @@ Composes a transaction to broadcast textual and numerical information to the net Composes a transaction to pay for a BTC order match. + Parameters - + address: `bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l` (str, required) - The address that will be sending the payment - + order_match_id: `e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2` (str, required) - The ID of the order match to pay for + + address: `bc1qsteve3tfxfg9pcmvzw645sr9zy7es5rx645p6l` (str, required) - The address that will be sending the payment + + order_match_id: `e470416a9500fb046835192da013f48e6468a07dba1bede4a0b68e666ed23c8d_4953bde3d9417b103615c2d3d4b284d4fcf7cbd820e5dd19ac0084e9ebd090b2` (str, required) - The ID of the order match to pay for + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1404,8 +1404,8 @@ Composes a transaction to pay for a BTC order match. Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address with the BTC to burn - + quantity: `1000` (int, required) - The quantities of BTC to burn (1 BTC maximum burn per address) + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address with the BTC to burn + + quantity: `1000` (int, required) - The quantities of BTC to burn (1 BTC maximum burn per address) + overburn (bool, optional) - Whether to allow the burn to exceed 1 BTC for the address + Default: `False` + encoding (str, optional) - The encoding method to use @@ -1460,8 +1460,8 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss Composes a transaction to cancel an open order or bet. + Parameters - + address: `15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA` (str, required) - The address that placed the order/bet to be cancelled - + offer_hash: `8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a` (str, required) - The hash of the order/bet to be cancelled + + address: `15e15ua6A3FJqjMevtrWcFSzKn9k6bMQeA` (str, required) - The address that placed the order/bet to be cancelled + + offer_hash: `8ce3335391bf71f8f12c0573b4f85b9adc4882a9955d9f8e5ababfdd0060279a` (str, required) - The hash of the order/bet to be cancelled + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1513,10 +1513,10 @@ Composes a transaction to cancel an open order or bet. Composes a transaction to destroy a quantity of an asset. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending the asset to be destroyed - + asset: `XCP` (str, required) - The asset to be destroyed - + quantity: `1000` (int, required) - The quantity of the asset to be destroyed - + tag: `"bugs!"` (str, required) - A tag for the destruction + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending the asset to be destroyed + + asset: `XCP` (str, required) - The asset to be destroyed + + quantity: `1000` (int, required) - The quantity of the asset to be destroyed + + tag: `"bugs!"` (str, required) - A tag for the destruction + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1570,12 +1570,12 @@ Composes a transaction to destroy a quantity of an asset. Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) - + asset: `XCP` (str, required) - The asset or subasset to dispense - + give_quantity: `1000` (int, required) - The quantity of the asset to dispense - + escrow_quantity: `1000` (int, required) - The quantity of the asset to reserve for this dispenser - + mainchainrate: `100` (int, required) - The quantity of the main chain asset (BTC) per dispensed portion - + status: `0` (int, required) - The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be dispensing (must have the necessary escrow_quantity of the specified asset) + + asset: `XCP` (str, required) - The asset or subasset to dispense + + give_quantity: `1000` (int, required) - The quantity of the asset to dispense + + escrow_quantity: `1000` (int, required) - The quantity of the asset to reserve for this dispenser + + mainchainrate: `100` (int, required) - The quantity of the main chain asset (BTC) per dispensed portion + + status: `0` (int, required) - The state of the dispenser. 0 for open, 1 for open using open_address, 10 for closed + open_address (str, optional) - The address that you would like to open the dispenser on + Default: `None` + oracle_address (str, optional) - The address that you would like to use as a price oracle for this dispenser @@ -1637,10 +1637,10 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse Composes a transaction to issue a dividend to holders of a given asset. + Parameters - + address: `1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD` (str, required) - The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) - + quantity_per_unit: `1` (int, required) - The amount of dividend_asset rewarded - + asset: `PEPECASH` (str, required) - The asset or subasset that the dividends are being rewarded on - + dividend_asset: `XCP` (str, required) - The asset or subasset that the dividends are paid in + + address: `1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD` (str, required) - The address that will be issuing the dividend (must have the ownership of the asset which the dividend is being issued on) + + quantity_per_unit: `1` (int, required) - The amount of dividend_asset rewarded + + asset: `PEPECASH` (str, required) - The asset or subasset that the dividends are being rewarded on + + dividend_asset: `XCP` (str, required) - The asset or subasset that the dividends are paid in + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1694,10 +1694,10 @@ Composes a transaction to issue a dividend to holders of a given asset. Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing or transfering the asset - + asset: `XCPTEST` (str, required) - The assets to issue or transfer. This can also be a subasset longname for new subasset issuances - + quantity: `1000` (int, required) - The quantity of the asset to issue (set to 0 if transferring an asset) - + transfer_destination: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, optional) - The address to receive the asset + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing or transfering the asset + + asset: `XCPTEST` (str, required) - The assets to issue or transfer. This can also be a subasset longname for new subasset issuances + + quantity: `1000` (int, required) - The quantity of the asset to issue (set to 0 if transferring an asset) + + transfer_destination: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, optional) - The address to receive the asset + Default: `None` + divisible (bool, optional) - Whether this asset is divisible or not (if a transfer, this value must match the value specified when the asset was originally issued) + Default: `True` @@ -1764,12 +1764,12 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo Composes a transaction to send multiple payments to multiple addresses. + Parameters - + address: `1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) - + assets: `BAABAABLKSHP,BADHAIRDAY,BADWOJAK` (str, required) - comma-separated list of assets to send - + destinations: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev,1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD,1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - comma-separated list of addresses to send to - + quantities: `1,2,3` (str, required) - comma-separated list of quantities to send - + memo: `"Hello, world!"` (str, required) - The Memo associated with this transaction - + memo_is_hex: `False` (bool, required) - Whether the memo field is a hexadecimal string + + address: `1Fv87qmdtjQDP9d4p9E5ncBQvYB4a3Rhy6` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + assets: `BAABAABLKSHP,BADHAIRDAY,BADWOJAK` (str, required) - comma-separated list of assets to send + + destinations: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev,1GQhaWqejcGJ4GhQar7SjcCfadxvf5DNBD,1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - comma-separated list of addresses to send to + + quantities: `1,2,3` (str, required) - comma-separated list of quantities to send + + memo: `"Hello, world!"` (str, required) - The Memo associated with this transaction + + memo_is_hex: `False` (bool, required) - Whether the memo field is a hexadecimal string + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1839,13 +1839,13 @@ Composes a transaction to send multiple payments to multiple addresses. Composes a transaction to place an order on the distributed exchange. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) - + give_asset: `XCP` (str, required) - The asset that will be given in the trade - + give_quantity: `1000` (int, required) - The quantity of the asset that will be given - + get_asset: `PEPECASH` (str, required) - The asset that will be received in the trade - + get_quantity: `1000` (int, required) - The quantity of the asset that will be received - + expiration: `100` (int, required) - The number of blocks for which the order should be valid - + fee_required: `100` (int, required) - The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be issuing the order request (must have the necessary quantity of the specified asset to give) + + give_asset: `XCP` (str, required) - The asset that will be given in the trade + + give_quantity: `1000` (int, required) - The quantity of the asset that will be given + + get_asset: `PEPECASH` (str, required) - The asset that will be received in the trade + + get_quantity: `1000` (int, required) - The quantity of the asset that will be received + + expiration: `100` (int, required) - The number of blocks for which the order should be valid + + fee_required: `100` (int, required) - The miners’ fee required to be paid by orders for them to match this one; in BTC; required only if buying BTC (may be zero, though) + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -1902,10 +1902,10 @@ Composes a transaction to place an order on the distributed exchange. Composes a transaction to send a quantity of an asset to another address. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) - + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that will be receiving the asset - + asset: `XCP` (str, required) - The asset or subasset to send - + quantity: `1000` (int, required) - The quantity of the asset to send + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending (must have the necessary quantity of the specified asset) + + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address that will be receiving the asset + + asset: `XCP` (str, required) - The asset or subasset to send + + quantity: `1000` (int, required) - The quantity of the asset to send + memo (str, optional) - The Memo associated with this transaction + Default: `None` + memo_is_hex (bool, optional) - Whether the memo field is a hexadecimal string @@ -1968,10 +1968,10 @@ Composes a transaction to send a quantity of an asset to another address. Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. + Parameters - + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending - + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address to receive the assets and/or ownerships - + flags: `7` (int, required) - An OR mask of flags indicating how the sweep should be processed. Possible flags are: - FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. - FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. - + memo: `FFFF` (str, required) - The Memo associated with this transaction in hex format + + address: `1CounterpartyXXXXXXXXXXXXXXXUWLpVr` (str, required) - The address that will be sending + + destination: `1JDogZS6tQcSxwfxhv6XKKjcyicYA4Feev` (str, required) - The address to receive the assets and/or ownerships + + flags: `7` (int, required) - An OR mask of flags indicating how the sweep should be processed. Possible flags are: - FLAG_BALANCES: (integer) 1, specifies that all balances should be transferred. - FLAG_OWNERSHIP: (integer) 2, specifies that all ownerships should be transferred. - FLAG_BINARY_MEMO: (integer) 4, specifies that the memo is in binary/hex form. + + memo: `FFFF` (str, required) - The Memo associated with this transaction in hex format + encoding (str, optional) - The encoding method to use + Default: `auto` + fee_per_kb (int, optional) - The fee per kilobyte of transaction data constant that the server uses when deciding on the dynamic fee to use (in satoshis) @@ -2027,9 +2027,9 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti Returns the valid assets + Parameters - + offset: `0` (int, optional) - The offset of the assets to return + + offset: `0` (int, optional) - The offset of the assets to return + Default: `0` - + limit: `5` (int, optional) - The limit of the assets to return + + limit: `5` (int, optional) - The limit of the assets to return + Default: `100` + Response 200 (application/json) @@ -2066,7 +2066,7 @@ Returns the valid assets Returns the asset information + Parameters - + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + Response 200 (application/json) @@ -2091,8 +2091,8 @@ Returns the asset information Returns the asset balances + Parameters - + asset: `UNNEGOTIABLE` (str, required) - The asset to return - + exclude_zero_balances: `True` (bool, optional) - Whether to exclude zero balances + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + exclude_zero_balances: `True` (bool, optional) - Whether to exclude zero balances + Default: `True` + Response 200 (application/json) @@ -2114,8 +2114,8 @@ Returns the asset balances Returns the balance of an address and asset + Parameters - + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return - + asset: `XCP` (str, required) - The asset to return + + address: `1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs` (str, required) - The address to return + + asset: `XCP` (str, required) - The asset to return + Response 200 (application/json) @@ -2134,8 +2134,8 @@ Returns the balance of an address and asset Returns the orders of an asset + Parameters - + asset: `NEEDPEPE` (str, required) - The asset to return - + status: `filled` (str, optional) - The status of the orders to return + + asset: `NEEDPEPE` (str, required) - The asset to return + + status: `filled` (str, optional) - The status of the orders to return + Default: `open` + Response 200 (application/json) @@ -2361,10 +2361,10 @@ Returns the orders of an asset Returns the credits of an asset + Parameters - + asset: `UNNEGOTIABLE` (str, required) - The asset to return - + limit: `5` (int, optional) - The maximum number of credits to return + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of credits to return + Default: `100` - + offset: `0` (int, optional) - The offset of the credits to return + + offset: `0` (int, optional) - The offset of the credits to return + Default: `0` + Response 200 (application/json) @@ -2390,10 +2390,10 @@ Returns the credits of an asset Returns the debits of an asset + Parameters - + asset: `XCP` (str, required) - The asset to return - + limit: `5` (int, optional) - The maximum number of debits to return + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of debits to return + Default: `100` - + offset: `0` (int, optional) - The offset of the debits to return + + offset: `0` (int, optional) - The offset of the debits to return + Default: `0` + Response 200 (application/json) @@ -2455,7 +2455,7 @@ Returns the debits of an asset Returns the dividends of an asset + Parameters - + asset: `GMONEYPEPE` (str, required) - The asset to return + + asset: `GMONEYPEPE` (str, required) - The asset to return + Response 200 (application/json) @@ -2603,7 +2603,7 @@ Returns the dividends of an asset Returns the issuances of an asset + Parameters - + asset: `UNNEGOTIABLE` (str, required) - The asset to return + + asset: `UNNEGOTIABLE` (str, required) - The asset to return + Response 200 (application/json) @@ -2635,15 +2635,15 @@ Returns the issuances of an asset } ``` -### Get Sends Or Receives By Asset [GET /assets/{asset}/sends{?limit}{?offset}] +### Get Sends By Asset [GET /assets/{asset}/sends{?limit}{?offset}] Returns the sends of an asset + Parameters - + asset: `XCP` (str, required) - The asset to return - + limit: `5` (int, optional) - The maximum number of sends to return + + asset: `XCP` (str, required) - The asset to return + + limit: `5` (int, optional) - The maximum number of sends to return + Default: `100` - + offset: `0` (int, optional) - The offset of the sends to return + + offset: `0` (int, optional) - The offset of the sends to return + Default: `0` + Response 200 (application/json) @@ -2720,7 +2720,7 @@ Returns the sends of an asset Returns the dispensers of an asset + Parameters - + asset: `ERYKAHPEPU` (str, required) - The asset to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + status (int, optional) - + Default: `0` @@ -2754,8 +2754,8 @@ Returns the dispensers of an asset Returns the dispensers of an address and an asset + Parameters - + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return - + asset: `ERYKAHPEPU` (str, required) - The asset to return + + address: `bc1qlzkcy8c5fa6y6xvd8zn4axnvmhndfhku3hmdpz` (str, required) - The address to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + status (int, optional) - + Default: `0` @@ -2789,7 +2789,7 @@ Returns the dispensers of an address and an asset Returns the holders of an asset + Parameters - + asset: `ERYKAHPEPU` (str, required) - The asset to return + + asset: `ERYKAHPEPU` (str, required) - The asset to return + Response 200 (application/json) @@ -2847,7 +2847,7 @@ Returns the holders of an asset Returns the information of an order + Parameters - + order_hash: `23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776` (str, required) - The hash of the transaction that created the order + + order_hash: `23f68fdf934e81144cca31ce8ef69062d553c521321a039166e7ba99aede0776` (str, required) - The hash of the transaction that created the order + Response 200 (application/json) @@ -2882,8 +2882,8 @@ Returns the information of an order Returns the order matches of an order + Parameters - + order_hash: `5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947` (str, required) - The hash of the transaction that created the order - + status: `completed` (str, optional) - The status of the order matches to return + + order_hash: `5461e6f99a37a7167428b4a720a52052cd9afed43905f818f5d7d4f56abd0947` (str, required) - The hash of the transaction that created the order + + status: `completed` (str, optional) - The status of the order matches to return + Default: `pending` + Response 200 (application/json) @@ -2921,7 +2921,7 @@ Returns the order matches of an order Returns the BTC pays of an order + Parameters - + order_hash: `299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4` (str, required) - The hash of the transaction that created the order + + order_hash: `299b5b648f54eacb839f3487232d49aea373cdd681b706d4cc0b5e0b03688db4` (str, required) - The hash of the transaction that created the order + Response 200 (application/json) @@ -2949,7 +2949,7 @@ Returns the BTC pays of an order Returns the information of a bet + Parameters - + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet + + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet + Response 200 (application/json) @@ -2984,8 +2984,8 @@ Returns the information of a bet Returns the bet matches of a bet + Parameters - + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet - + status: `expired` (str, optional) - The status of the bet matches + + bet_hash: `5d097b4729cb74d927b4458d365beb811a26fcee7f8712f049ecbe780eb496ed` (str, required) - The hash of the transaction that created the bet + + status: `expired` (str, optional) - The status of the bet matches + Default: `pending` + Response 200 (application/json) @@ -3028,7 +3028,7 @@ Returns the bet matches of a bet Returns the resolutions of a bet + Parameters - + bet_hash: `36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace` (str, required) - The hash of the transaction that created the bet + + bet_hash: `36bbbb7dbd85054dac140a8ad8204eda2ee859545528bd2a9da69ad77c277ace` (str, required) - The hash of the transaction that created the bet + Response 200 (application/json) @@ -3057,11 +3057,11 @@ Returns the resolutions of a bet Returns the burns + Parameters - + status: `valid` (str, optional) - The status of the burns to return + + status: `valid` (str, optional) - The status of the burns to return + Default: `valid` - + offset: `10` (int, optional) - The offset of the burns to return + + offset: `10` (int, optional) - The offset of the burns to return + Default: `0` - + limit: `5` (int, optional) - The limit of the burns to return + + limit: `5` (int, optional) - The limit of the burns to return + Default: `100` + Response 200 (application/json) @@ -3125,7 +3125,7 @@ Returns the burns Returns the dispenser information by tx_hash + Parameters - + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + Response 200 (application/json) @@ -3158,7 +3158,7 @@ Returns the dispenser information by tx_hash Returns the dispenses of a dispenser + Parameters - + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + + dispenser_hash: `753787004d6e93e71f6e0aa1e0932cc74457d12276d53856424b2e4088cc542a` (str, required) - The hash of the dispenser to return + Response 200 (application/json) @@ -3198,9 +3198,9 @@ Returns the dispenses of a dispenser Returns all events + Parameters - + last: `10665092` (int, optional) - The last event index to return + + last: `10665092` (int, optional) - The last event index to return + Default: `None` - + limit: `5` (int, optional) - The maximum number of events to return + + limit: `5` (int, optional) - The maximum number of events to return + Default: `100` + Response 200 (application/json) @@ -3286,7 +3286,7 @@ Returns all events Returns the event of an index + Parameters - + event_index: `10665092` (int, required) - The index of the event to return + + event_index: `10665092` (int, required) - The index of the event to return + Response 200 (application/json) @@ -3513,10 +3513,10 @@ Returns the event counts of all blocks Returns the events filtered by event name + Parameters - + event: `CREDIT` (str, required) - The event to return - + last: `10665092` (int, optional) - The last event index to return + + event: `CREDIT` (str, required) - The event to return + + last: `10665092` (int, optional) - The last event index to return + Default: `None` - + limit: `5` (int, optional) - The maximum number of events to return + + limit: `5` (int, optional) - The maximum number of events to return + Default: `100` + Response 200 (application/json) @@ -3603,14 +3603,14 @@ Returns the events filtered by event name } ``` -## Group Healthz +## Group Z-pages -### Check Server Status [GET /healthz{?check_type}] +### Check Server Health [GET /healthz{?check_type}] Health check route. + Parameters - + check_type: `light` (str, optional) - Type of health check to perform. Options are 'light' and 'heavy' + + check_type: `light` (str, optional) - Type of health check to perform. Options are 'light' and 'heavy' + Default: `heavy` + Response 200 (application/json) @@ -3630,10 +3630,10 @@ Health check route. Returns all transactions involving a given address + Parameters - + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for - + unconfirmed: `True` (bool, optional) - Include unconfirmed transactions + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for + + unconfirmed: `True` (bool, optional) - Include unconfirmed transactions + Default: `True` - + only_tx_hashes: `True` (bool, optional) - Return only the tx hashes + + only_tx_hashes: `True` (bool, optional) - Return only the tx hashes + Default: `False` + Response 200 (application/json) @@ -3686,7 +3686,7 @@ Returns all transactions involving a given address Get the oldest transaction for an address. + Parameters - + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for. + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for. + block_index (int, optional) - The block index to search from. + Default: `None` @@ -3706,7 +3706,7 @@ Get the oldest transaction for an address. Returns a list of unspent outputs for a specific address + Parameters - + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - The address to search for + unconfirmed (bool, optional) - Include unconfirmed transactions + Default: `False` + unspent_tx_hash (str, optional) - Filter by unspent_tx_hash @@ -3806,7 +3806,7 @@ Returns a list of unspent outputs for a specific address Get pubkey for an address. + Parameters - + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - Address to get pubkey for. + + address: `14TjwxgnuqgB4HcDcSZk2m7WKwcGVYxRjS` (str, required) - Address to get pubkey for. + provided_pubkeys (str, optional) - Comma separated list of provided pubkeys. + Default: `None` @@ -3823,8 +3823,8 @@ Get pubkey for an address. Get a transaction from the blockchain + Parameters - + tx_hash: `3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018` (str, required) - The transaction hash - + verbose: `True` (bool, optional) - Whether to return JSON output or raw hex + + tx_hash: `3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018` (str, required) - The transaction hash + + verbose: `True` (bool, optional) - Whether to return JSON output or raw hex + Default: `False` + Response 200 (application/json) @@ -3881,9 +3881,9 @@ Get a transaction from the blockchain Get the fee per kilobyte for a transaction to be confirmed in `conf_target` blocks. + Parameters - + conf_target: `2` (int, optional) - Confirmation target in blocks (1 - 1008) + + conf_target: `2` (int, optional) - Confirmation target in blocks (1 - 1008) + Default: `3` - + mode: `CONSERVATIVE` (str, optional) - The fee estimate mode. + + mode: `CONSERVATIVE` (str, optional) - The fee estimate mode. + Default: `CONSERVATIVE` + Response 200 (application/json) @@ -3953,7 +3953,7 @@ Returns all mempool events Returns the mempool events filtered by event name + Parameters - + event: `OPEN_ORDER` (str, required) - The event to return + + event: `OPEN_ORDER` (str, required) - The event to return + Response 200 (application/json) diff --git a/counterparty-core/counterpartycore/lib/api/routes.py b/counterparty-core/counterpartycore/lib/api/routes.py index 1488c7e800..6141db227d 100644 --- a/counterparty-core/counterpartycore/lib/api/routes.py +++ b/counterparty-core/counterpartycore/lib/api/routes.py @@ -21,7 +21,7 @@ "/blocks//cancels": ledger.get_cancels, "/blocks//destructions": ledger.get_destructions, "/blocks//issuances": ledger.get_issuances_by_block, - "/blocks//sends": ledger.get_sends_or_receives_by_block, + "/blocks//sends": ledger.get_sends_by_block, "/blocks//dispenses": ledger.get_dispenses_by_block, "/blocks//sweeps": ledger.get_sweeps_by_block, ### /transactions ### @@ -67,7 +67,7 @@ "/assets//debits": ledger.get_debits_by_asset, "/assets//dividends": ledger.get_dividends, "/assets//issuances": ledger.get_issuances_by_asset, - "/assets//sends": ledger.get_sends_or_receives_by_asset, + "/assets//sends": ledger.get_sends_by_asset, "/assets//dispensers": ledger.get_dispensers_by_asset, "/assets//dispensers/
": ledger.get_dispensers_by_address_and_asset, "/assets//holders": ledger.get_asset_holders, @@ -90,7 +90,7 @@ "/events/counts": ledger.get_all_events_counts, "/events/": ledger.get_events_by_name, ### /healthz ### - "/healthz": util.check_server_status, + "/healthz": util.check_server_health, ### /bitcoin ### "/bitcoin/addresses/
/transactions": backend.get_transactions_by_address, "/bitcoin/addresses/
/transactions/oldest": backend.get_oldest_transaction_by_address, diff --git a/counterparty-core/counterpartycore/lib/api/util.py b/counterparty-core/counterpartycore/lib/api/util.py index c95016ca9d..3692993889 100644 --- a/counterparty-core/counterpartycore/lib/api/util.py +++ b/counterparty-core/counterpartycore/lib/api/util.py @@ -67,7 +67,7 @@ def handle_healthz_route(db, check_type: str = "heavy"): return flask.Response(to_json(result), code, mimetype="application/json") -def check_server_status(db, check_type: str = "heavy"): +def check_server_health(db, check_type: str = "heavy"): """ Health check route. :param check_type: Type of health check to perform. Options are 'light' and 'heavy' (e.g. light) diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index f6fb562887..585925d4a6 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -639,15 +639,17 @@ def get_sends_or_receives( return cursor.fetchall() -def get_sends_or_receives_by_block(db, block_index: int, limit: int = 100, offset: int = 0): +def get_sends_by_block(db, block_index: int, limit: int = 100, offset: int = 0): """ Returns the sends of a block :param int block_index: The index of the block to return (e.g. 840459) + :param int limit: The maximum number of sends to return (e.g. 5) + :param int offset: The offset of the sends to return (e.g. 0) """ return get_sends_or_receives(db, block_index=block_index, limit=limit, offset=offset) -def get_sends_or_receives_by_asset(db, asset: str, limit: int = 100, offset: int = 0): +def get_sends_by_asset(db, asset: str, limit: int = 100, offset: int = 0): """ Returns the sends of an asset :param str asset: The asset to return (e.g. XCP) diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index 8800ddc6a6..e1e56c14e2 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -139,6 +139,8 @@ def gen_groups_toc(): route_group = path.split("/")[1] if route_group != current_group: current_group = route_group + if current_group == "healthz": + current_group = "Z-Pages" md += f"\n## Group {current_group.capitalize()}\n" blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") @@ -146,7 +148,7 @@ def gen_groups_toc(): title = title.replace("Pubkeyhash", "PubKeyHash") title = title.replace("Mpma", "MPMA") title = title.replace("Btcpay", "BTCPay") - md += f"\n### {title} " + md += f"\n### {title.strip()} " if TARGET == "docusaurus": md += f"[GET `{blueprint_path}`]\n\n" else: @@ -157,7 +159,7 @@ def gen_groups_toc(): blueprint_path += f"{{?{arg['name']}}}" md += f"[GET {blueprint_path}]\n\n" - md += route["description"] + md += route["description"].strip() example_args = {} if len(route["args"]) > 0: @@ -168,7 +170,7 @@ def gen_groups_toc(): example_arg = "" if "(e.g. " in description: desc_arr = description.split("(e.g. ") - description = desc_arr[0].replace("\n", " ") + description = desc_arr[0].replace("\n", " ").strip() example_args[arg["name"]] = desc_arr[1].replace(")", "") example_arg = f": `{example_args[arg['name']]}`" md += f" + {arg['name']}{example_arg} ({arg['type']}, {required}) - {description}\n" From 5d8c7381bcb0d89ef0cd418ddeda3e66555d1b27 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 11:18:44 +0200 Subject: [PATCH 122/128] Remove block_index and timestamp when query by block; tweaks and typos --- apiary.apib | 216 ++---- .../counterpartycore/lib/ledger.py | 15 +- .../counterpartycore/lib/transaction.py | 5 - .../test/fixtures/api_v2_fixtures.json | 58 +- counterparty-core/tools/apicache.json | 720 ++++++++---------- 5 files changed, 385 insertions(+), 629 deletions(-) diff --git a/apiary.apib b/apiary.apib index cbed6f4f77..4555cc6ac4 100644 --- a/apiary.apib +++ b/apiary.apib @@ -170,30 +170,26 @@ Returns the events of a block { "event_index": 14194760, "event": "BLOCK_PARSED", - "bindings": { + "payload": { "block_index": 840464, "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194759, "event": "TRANSACTION_PARSED", - "bindings": { + "payload": { "supported": true, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194758, "event": "CREDIT", - "bindings": { + "payload": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -201,14 +197,12 @@ Returns the events of a block "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "quantity": 1, "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194757, "event": "ASSET_ISSUANCE", - "bindings": { + "payload": { "asset": "UNNEGOTIABLE", "asset_longname": null, "block_index": 840464, @@ -227,26 +221,22 @@ Returns the events of a block "transfer": false, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194756, "event": "ASSET_CREATION", - "bindings": { + "payload": { "asset_id": "75313533584419238", "asset_longname": null, "asset_name": "UNNEGOTIABLE", "block_index": 840464 - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194755, "event": "DEBIT", - "bindings": { + "payload": { "action": "issuance fee", "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "XCP", @@ -254,14 +244,12 @@ Returns the events of a block "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "quantity": 50000000, "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 + } }, { "event_index": 14194754, "event": "NEW_TRANSACTION", - "bindings": { + "payload": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, @@ -272,22 +260,18 @@ Returns the events of a block "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852779 + } }, { "event_index": 14194753, "event": "NEW_BLOCK", - "bindings": { + "payload": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, "difficulty": 86388558925171.02, "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" - }, - "block_index": 840464, - "timestamp": 1713852779 + } } ] } @@ -357,7 +341,7 @@ Returns the events of a block filtered by event { "event_index": 14194758, "event": "CREDIT", - "bindings": { + "payload": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -365,9 +349,7 @@ Returns the events of a block filtered by event "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "quantity": 1, "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 + } } ] } @@ -1218,7 +1200,7 @@ Returns the sweeps of an address } ``` -### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to issue a bet against a feed. @@ -1242,8 +1224,6 @@ Composes a transaction to issue a bet against a feed. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1287,7 +1267,7 @@ Composes a transaction to issue a bet against a feed. } ``` -### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to broadcast textual and numerical information to the network. @@ -1305,8 +1285,6 @@ Composes a transaction to broadcast textual and numerical information to the net + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1346,7 +1324,7 @@ Composes a transaction to broadcast textual and numerical information to the net } ``` -### Compose BTCPay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose BTCPay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to pay for a BTC order match. @@ -1361,8 +1339,6 @@ Composes a transaction to pay for a BTC order match. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1399,7 +1375,7 @@ Composes a transaction to pay for a BTC order match. } ``` -### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, possible between blocks 278310 and 283810; on testnet it is still available). @@ -1416,8 +1392,6 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1455,7 +1429,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss } ``` -### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to cancel an open order or bet. @@ -1470,8 +1444,6 @@ Composes a transaction to cancel an open order or bet. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1508,7 +1480,7 @@ Composes a transaction to cancel an open order or bet. } ``` -### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to destroy a quantity of an asset. @@ -1525,8 +1497,6 @@ Composes a transaction to destroy a quantity of an asset. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1565,7 +1535,7 @@ Composes a transaction to destroy a quantity of an asset. } ``` -### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Opens or closes a dispenser for a given asset at a given rate of main chain asset (BTC). Escrowed quantity on open must be equal or greater than give_quantity. It is suggested that you escrow multiples of give_quantity to ease dispenser operation. @@ -1588,8 +1558,6 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1632,7 +1600,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse } ``` -### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to issue a dividend to holders of a given asset. @@ -1649,8 +1617,6 @@ Composes a transaction to issue a dividend to holders of a given asset. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1689,7 +1655,7 @@ Composes a transaction to issue a dividend to holders of a given asset. } ``` -### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to Issue a new asset, issue more of an existing asset, lock an asset, reset existing supply, or transfer the ownership of an asset. @@ -1715,8 +1681,6 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1759,7 +1723,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo } ``` -### Compose MPMA [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose MPMA [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to send multiple payments to multiple addresses. @@ -1778,8 +1742,6 @@ Composes a transaction to send multiple payments to multiple addresses. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1834,7 +1796,7 @@ Composes a transaction to send multiple payments to multiple addresses. } ``` -### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to place an order on the distributed exchange. @@ -1854,8 +1816,6 @@ Composes a transaction to place an order on the distributed exchange. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1897,7 +1857,7 @@ Composes a transaction to place an order on the distributed exchange. } ``` -### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to send a quantity of an asset to another address. @@ -1920,8 +1880,6 @@ Composes a transaction to send a quantity of an asset to another address. + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -1963,7 +1921,7 @@ Composes a transaction to send a quantity of an asset to another address. } ``` -### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?op_return_value}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] +### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] Composes a transaction to Sends all assets and/or transfer ownerships to a destination address. @@ -1980,8 +1938,6 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti + Default: `546` + multisig_dust_size (int, optional) - Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output + Default: `1000` - + op_return_value (int, optional) - The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away - + Default: `0` + pubkey (str, optional) - The hexadecimal public key of the source address (or a list of the keys, if multi-sig). Required when using encoding parameter values of multisig or pubkeyhash. + Default: `None` + allow_unconfirmed_inputs (bool, optional) - Set to true to allow this transaction to utilize unconfirmed UTXOs as inputs @@ -3211,7 +3167,7 @@ Returns all events { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "bindings": { + "payload": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -3222,7 +3178,7 @@ Returns all events { "event_index": 10665091, "event": "ENHANCED_SEND", - "bindings": { + "payload": { "asset": "THOTHPEPE", "block_index": 744232, "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", @@ -3239,7 +3195,7 @@ Returns all events { "event_index": 10665090, "event": "CREDIT", - "bindings": { + "payload": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -3254,7 +3210,7 @@ Returns all events { "event_index": 10665089, "event": "DEBIT", - "bindings": { + "payload": { "action": "send", "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "THOTHPEPE", @@ -3269,7 +3225,7 @@ Returns all events { "event_index": 10665088, "event": "TRANSACTION_PARSED", - "bindings": { + "payload": { "supported": true, "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", "tx_index": 2056159 @@ -3296,7 +3252,7 @@ Returns the event of an index { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "bindings": { + "payload": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -3527,7 +3483,7 @@ Returns the events filtered by event name { "event_index": 10665090, "event": "CREDIT", - "bindings": { + "payload": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -3542,7 +3498,7 @@ Returns the events filtered by event name { "event_index": 10665085, "event": "CREDIT", - "bindings": { + "payload": { "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", "asset": "XCP", "block_index": 744232, @@ -3557,7 +3513,7 @@ Returns the events filtered by event name { "event_index": 10665082, "event": "CREDIT", - "bindings": { + "payload": { "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "FREEDOMKEK", "block_index": 744232, @@ -3572,7 +3528,7 @@ Returns the events filtered by event name { "event_index": 10665078, "event": "CREDIT", - "bindings": { + "payload": { "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", "asset": "PEPEFRIDAY", "block_index": 744232, @@ -3587,7 +3543,7 @@ Returns the events filtered by event name { "event_index": 10665074, "event": "CREDIT", - "bindings": { + "payload": { "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", "asset": "PEPEFRIDAY", "block_index": 744232, @@ -3903,48 +3859,7 @@ Returns all mempool events ``` { - "result": [ - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "NEW_TRANSACTION", - "bindings": { - "block_hash": "mempool", - "block_index": 9999999, - "block_time": 1713952590, - "btc_amount": 0, - "data": "0200454ceacf416ccf0000000000000001005461639d06ebc42d541b54b1c5525543ae4d6db3", - "destination": "", - "fee": 9900, - "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "tx_index": 2726767 - }, - "timestamp": 1713952691 - }, - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "ENHANCED_SEND", - "bindings": { - "asset": "FIERCERABBIT", - "destination": "18hARq2fFJxiypHSnZ8yLcbPNpUfaozD8U", - "memo": null, - "quantity": 1, - "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e" - }, - "timestamp": 1713952691 - }, - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "tx_index": 2726767 - }, - "timestamp": 1713952691 - } - ] + "result": [] } ``` @@ -3959,49 +3874,6 @@ Returns the mempool events filtered by event name ``` { - "result": [ - { - "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", - "event": "OPEN_ORDER", - "bindings": { - "expiration": 5000, - "expire_index": 10004999, - "fee_provided": 5016, - "fee_provided_remaining": 5016, - "fee_required": 0, - "fee_required_remaining": 0, - "get_asset": "XCP", - "get_quantity": 3300000000, - "get_remaining": 3300000000, - "give_asset": "PEPEPASSPORT", - "give_quantity": 100000000, - "give_remaining": 100000000, - "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", - "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81" - }, - "timestamp": 1713952690 - }, - { - "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6", - "event": "OPEN_ORDER", - "bindings": { - "expiration": 5000, - "expire_index": 10004999, - "fee_provided": 5016, - "fee_provided_remaining": 5016, - "fee_required": 0, - "fee_required_remaining": 0, - "get_asset": "XCP", - "get_quantity": 1185000000, - "get_remaining": 1185000000, - "give_asset": "FRATPEPE", - "give_quantity": 3, - "give_remaining": 3, - "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", - "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6" - }, - "timestamp": 1713952690 - } - ] + "result": [] } ``` diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index 585925d4a6..c48d089ee0 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -87,10 +87,13 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li else: limit = "" # no sql injection here - query = """ - SELECT message_index AS event_index, event, bindings, block_index, timestamp + select_fields = "message_index AS event_index, event, bindings AS payload" + if block_index is None: + select_fields += ", block_index, timestamp" + query = f""" + SELECT {select_fields} FROM messages - """ + """ # noqa S608 if len(where) > 0: query += f""" WHERE ({" AND ".join(where)}) @@ -101,7 +104,7 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li cursor.execute(query, tuple(bindings)) events = cursor.fetchall() for i, _ in enumerate(events): - events[i]["bindings"] = json.loads(events[i]["bindings"]) + events[i]["payload"] = json.loads(events[i]["payload"]) return events @@ -160,7 +163,7 @@ def get_mempool_events(db, event_name=None): bindings.append(event_name) # no sql injection here query = """ - SELECT tx_hash, event, bindings, timestamp + SELECT tx_hash, event, bindings AS payload, timestamp FROM mempool """ if event_name is not None: @@ -169,7 +172,7 @@ def get_mempool_events(db, event_name=None): cursor.execute(query, tuple(bindings)) events = cursor.fetchall() for i, _ in enumerate(events): - events[i]["bindings"] = json.loads(events[i]["bindings"]) + events[i]["payload"] = json.loads(events[i]["payload"]) return events diff --git a/counterparty-core/counterpartycore/lib/transaction.py b/counterparty-core/counterpartycore/lib/transaction.py index b29a6506c4..94854a7ec3 100644 --- a/counterparty-core/counterpartycore/lib/transaction.py +++ b/counterparty-core/counterpartycore/lib/transaction.py @@ -973,11 +973,6 @@ def get_dust_return_pubkey(source, provided_pubkeys, encoding): config.DEFAULT_MULTISIG_DUST_SIZE, "Specify (in satoshis) to override the (dust) amount of BTC used for each (bare) multisig output", ), - "op_return_value": ( - int, - config.DEFAULT_OP_RETURN_VALUE, - "The value (in satoshis) to use with any OP_RETURN outputs in the generated transaction. Defaults to 0. Don't use this, unless you like throwing your money away", - ), "pubkey": ( str, None, diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index ebbd0f3106..4631558d73 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -137,7 +137,7 @@ { "event_index": 1183, "event": "NEW_BLOCK", - "bindings": { + "payload": { "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", "block_index": 310492, "block_time": 310492000, @@ -145,37 +145,31 @@ "ledger_hash": null, "previous_block_hash": null, "txlist_hash": null - }, - "block_index": 310491, - "timestamp": 0 + } }, { "event_index": 1182, "event": "BLOCK_PARSED", - "bindings": { + "payload": { "block_index": 310491, "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994", "txlist_hash": "f065728a3544adc085fae976759c0d040a34ca0a8ddd39260b55f0262cd5baa8" - }, - "block_index": 310491, - "timestamp": 0 + } }, { "event_index": 1181, "event": "TRANSACTION_PARSED", - "bindings": { + "payload": { "supported": true, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "tx_index": 492 - }, - "block_index": 310491, - "timestamp": 0 + } }, { "event_index": 1180, "event": "OPEN_ORDER", - "bindings": { + "payload": { "block_index": 310491, "expiration": 2000, "expire_index": 312491, @@ -193,14 +187,12 @@ "status": "open", "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "tx_index": 492 - }, - "block_index": 310491, - "timestamp": 0 + } }, { "event_index": 1179, "event": "DEBIT", - "bindings": { + "payload": { "action": "open order", "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "XCP", @@ -208,14 +200,12 @@ "event": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "quantity": 100000000, "tx_index": 492 - }, - "block_index": 310491, - "timestamp": 0 + } }, { "event_index": 1178, "event": "NEW_TRANSACTION", - "bindings": { + "payload": { "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", "block_index": 310491, "block_time": 310491000, @@ -227,9 +217,7 @@ "supported": true, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "tx_index": 492 - }, - "block_index": 310491, - "timestamp": 0 + } } ] }, @@ -1272,7 +1260,7 @@ { "event_index": 1237, "event": "BLOCK_PARSED", - "bindings": { + "payload": { "block_index": 310500, "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f", @@ -1284,7 +1272,7 @@ { "event_index": 1236, "event": "NEW_BLOCK", - "bindings": { + "payload": { "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", "block_index": 310500, "block_time": 310500000, @@ -1299,7 +1287,7 @@ { "event_index": 1235, "event": "BLOCK_PARSED", - "bindings": { + "payload": { "block_index": 310499, "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c", @@ -1311,7 +1299,7 @@ { "event_index": 1234, "event": "NEW_BLOCK", - "bindings": { + "payload": { "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", "block_index": 310499, "block_time": 310499000, @@ -1326,7 +1314,7 @@ { "event_index": 1233, "event": "BLOCK_PARSED", - "bindings": { + "payload": { "block_index": 310498, "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98", @@ -1342,7 +1330,7 @@ { "event_index": 10, "event": "ASSET_CREATION", - "bindings": { + "payload": { "asset_id": "697326324582", "asset_longname": null, "asset_name": "DIVISIBLE", @@ -1446,7 +1434,7 @@ { "event_index": 1231, "event": "CREDIT", - "bindings": { + "payload": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "A95428956661682277", "block_index": 310498, @@ -1461,7 +1449,7 @@ { "event_index": 1223, "event": "CREDIT", - "bindings": { + "payload": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "PARENT", "block_index": 310497, @@ -1476,7 +1464,7 @@ { "event_index": 1214, "event": "CREDIT", - "bindings": { + "payload": { "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", "asset": "XCP", "block_index": 310496, @@ -1491,7 +1479,7 @@ { "event_index": 1207, "event": "CREDIT", - "bindings": { + "payload": { "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", "asset": "DIVIDEND", "block_index": 310495, @@ -1506,7 +1494,7 @@ { "event_index": 1201, "event": "CREDIT", - "bindings": { + "payload": { "address": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", "asset": "DIVIDEND", "block_index": 310494, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 5846ea6a7f..0fc73ef18b 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -1,130 +1,4 @@ { - "/blocks//events": { - "result": [ - { - "event_index": 14194760, - "event": "BLOCK_PARSED", - "bindings": { - "block_index": 840464, - "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", - "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", - "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194759, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194758, - "event": "CREDIT", - "bindings": { - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", - "block_index": 840464, - "calling_function": "issuance", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 1, - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194757, - "event": "ASSET_ISSUANCE", - "bindings": { - "asset": "UNNEGOTIABLE", - "asset_longname": null, - "block_index": 840464, - "call_date": 0, - "call_price": 0.0, - "callable": false, - "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", - "divisible": false, - "fee_paid": 50000000, - "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "locked": false, - "quantity": 1, - "reset": false, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "status": "valid", - "transfer": false, - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194756, - "event": "ASSET_CREATION", - "bindings": { - "asset_id": "75313533584419238", - "asset_longname": null, - "asset_name": "UNNEGOTIABLE", - "block_index": 840464 - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194755, - "event": "DEBIT", - "bindings": { - "action": "issuance fee", - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "XCP", - "block_index": 840464, - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 50000000, - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 - }, - { - "event_index": 14194754, - "event": "NEW_TRANSACTION", - "bindings": { - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_index": 840464, - "block_time": 1713852783, - "btc_amount": 0, - "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", - "destination": "", - "fee": 56565, - "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852779 - }, - { - "event_index": 14194753, - "event": "NEW_BLOCK", - "bindings": { - "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", - "block_index": 840464, - "block_time": 1713852783, - "difficulty": 86388558925171.02, - "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" - }, - "block_index": 840464, - "timestamp": 1713852779 - } - ] - }, "/blocks//events/counts": { "result": [ { @@ -161,25 +35,6 @@ } ] }, - "/blocks//events/": { - "result": [ - { - "event_index": 14194758, - "event": "CREDIT", - "bindings": { - "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", - "asset": "UNNEGOTIABLE", - "block_index": 840464, - "calling_function": "issuance", - "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", - "quantity": 1, - "tx_index": 2726605 - }, - "block_index": 840464, - "timestamp": 1713852780 - } - ] - }, "/blocks//credits": { "result": [ { @@ -1507,94 +1362,6 @@ } ] }, - "/events": { - "result": [ - { - "event_index": 10665092, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665091, - "event": "ENHANCED_SEND", - "bindings": { - "asset": "THOTHPEPE", - "block_index": 744232, - "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "memo": null, - "quantity": 1, - "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", - "status": "valid", - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665090, - "event": "CREDIT", - "bindings": { - "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "asset": "THOTHPEPE", - "block_index": 744232, - "calling_function": "send", - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665089, - "event": "DEBIT", - "bindings": { - "action": "send", - "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", - "asset": "THOTHPEPE", - "block_index": 744232, - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665088, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", - "tx_index": 2056159 - }, - "block_index": 744232, - "timestamp": 1712256340 - } - ] - }, - "/events/": { - "result": [ - { - "event_index": 10665092, - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - } - ] - }, "/events/counts": { "result": [ { @@ -1787,94 +1554,15 @@ } ] }, - "/events/": { - "result": [ - { - "event_index": 10665090, - "event": "CREDIT", - "bindings": { - "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", - "asset": "THOTHPEPE", - "block_index": 744232, - "calling_function": "send", - "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", - "quantity": 1, - "tx_index": 2056160 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665085, - "event": "CREDIT", - "bindings": { - "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", - "asset": "XCP", - "block_index": 744232, - "calling_function": "dispense", - "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", - "quantity": 10000000000, - "tx_index": 2056159 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665082, - "event": "CREDIT", - "bindings": { - "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", - "asset": "FREEDOMKEK", - "block_index": 744232, - "calling_function": "send", - "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", - "quantity": 1, - "tx_index": 2056158 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665078, - "event": "CREDIT", - "bindings": { - "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", - "asset": "PEPEFRIDAY", - "block_index": 744232, - "calling_function": "send", - "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", - "quantity": 1, - "tx_index": 2056157 - }, - "block_index": 744232, - "timestamp": 1712256340 - }, - { - "event_index": 10665074, - "event": "CREDIT", - "bindings": { - "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", - "asset": "PEPEFRIDAY", - "block_index": 744232, - "calling_function": "send", - "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", - "quantity": 2, - "tx_index": 2056156 - }, - "block_index": 744232, - "timestamp": 1712256340 - } - ] - }, - "/addresses/
/compose/broadcast": { - "result": { - "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", - "params": { - "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", - "timestamp": 4003903983, - "value": 100.0, - "fee_fraction": 0.05, - "text": "\"Hello, world!\"" + "/addresses/
/compose/broadcast": { + "result": { + "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", + "params": { + "source": "1CounterpartyXXXXXXXXXXXXXXXUWLpVr", + "timestamp": 4003903983, + "value": 100.0, + "fee_fraction": 0.05, + "text": "\"Hello, world!\"" }, "name": "broadcast" } @@ -2043,96 +1731,6 @@ "name": "sweep" } }, - "/mempool/events": { - "result": [ - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "NEW_TRANSACTION", - "bindings": { - "block_hash": "mempool", - "block_index": 9999999, - "block_time": 1713952590, - "btc_amount": 0, - "data": "0200454ceacf416ccf0000000000000001005461639d06ebc42d541b54b1c5525543ae4d6db3", - "destination": "", - "fee": 9900, - "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "tx_index": 2726767 - }, - "timestamp": 1713952691 - }, - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "ENHANCED_SEND", - "bindings": { - "asset": "FIERCERABBIT", - "destination": "18hARq2fFJxiypHSnZ8yLcbPNpUfaozD8U", - "memo": null, - "quantity": 1, - "source": "14PxDTVUMCjLoAcGPZGQf6cEtn7yLzdHp1", - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e" - }, - "timestamp": 1713952691 - }, - { - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "event": "TRANSACTION_PARSED", - "bindings": { - "supported": true, - "tx_hash": "8a154886671713cae6d72d2996f07fac61529c0d8d1ebc476f448212ff3d535e", - "tx_index": 2726767 - }, - "timestamp": 1713952691 - } - ] - }, - "/mempool/events/": { - "result": [ - { - "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81", - "event": "OPEN_ORDER", - "bindings": { - "expiration": 5000, - "expire_index": 10004999, - "fee_provided": 5016, - "fee_provided_remaining": 5016, - "fee_required": 0, - "fee_required_remaining": 0, - "get_asset": "XCP", - "get_quantity": 3300000000, - "get_remaining": 3300000000, - "give_asset": "PEPEPASSPORT", - "give_quantity": 100000000, - "give_remaining": 100000000, - "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", - "tx_hash": "90ba95c4578b9ab7866515d66736c5b4132e88a0bd9b0fca7b2f1be830a1bb81" - }, - "timestamp": 1713952690 - }, - { - "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6", - "event": "OPEN_ORDER", - "bindings": { - "expiration": 5000, - "expire_index": 10004999, - "fee_provided": 5016, - "fee_provided_remaining": 5016, - "fee_required": 0, - "fee_required_remaining": 0, - "get_asset": "XCP", - "get_quantity": 1185000000, - "get_remaining": 1185000000, - "give_asset": "FRATPEPE", - "give_quantity": 3, - "give_remaining": 3, - "source": "1A36UrLHxeg9ABoS4zPsRUegyCWTWER2kF", - "tx_hash": "bc553f3d4349a266b70e7ed98e2198a18d634a5b247997f59817f69e19de2ad6" - }, - "timestamp": 1713952690 - } - ] - }, "/bitcoin/addresses/
/transactions": { "result": [ { @@ -2580,5 +2178,305 @@ } } } + }, + "/blocks//events": { + "result": [ + { + "event_index": 14194760, + "event": "BLOCK_PARSED", + "payload": { + "block_index": 840464, + "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", + "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", + "txlist_hash": "84bdc5b9073f775a2b65de7da2b10b89a2235f3501883b0a836e41e68cd00d46" + } + }, + { + "event_index": 14194759, + "event": "TRANSACTION_PARSED", + "payload": { + "supported": true, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + }, + { + "event_index": 14194758, + "event": "CREDIT", + "payload": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + } + }, + { + "event_index": 14194757, + "event": "ASSET_ISSUANCE", + "payload": { + "asset": "UNNEGOTIABLE", + "asset_longname": null, + "block_index": 840464, + "call_date": 0, + "call_price": 0.0, + "callable": false, + "description": "UNNEGOTIABLE WE MUST BECOME UNNEGOTIABLE WE ARE", + "divisible": false, + "fee_paid": 50000000, + "issuer": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "locked": false, + "quantity": 1, + "reset": false, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "status": "valid", + "transfer": false, + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + }, + { + "event_index": 14194756, + "event": "ASSET_CREATION", + "payload": { + "asset_id": "75313533584419238", + "asset_longname": null, + "asset_name": "UNNEGOTIABLE", + "block_index": 840464 + } + }, + { + "event_index": 14194755, + "event": "DEBIT", + "payload": { + "action": "issuance fee", + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "XCP", + "block_index": 840464, + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 50000000, + "tx_index": 2726605 + } + }, + { + "event_index": 14194754, + "event": "NEW_TRANSACTION", + "payload": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "btc_amount": 0, + "data": "16010b9142801429a60000000000000001000000554e4e45474f544941424c45205745204d555354204245434f4d4520554e4e45474f544941424c4520574520415245", + "destination": "", + "fee": 56565, + "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "tx_index": 2726605 + } + }, + { + "event_index": 14194753, + "event": "NEW_BLOCK", + "payload": { + "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", + "block_index": 840464, + "block_time": 1713852783, + "difficulty": 86388558925171.02, + "previous_block_hash": "00000000000000000002db1e5aa19784eec3de949f98ec757e7a7f2fc392079d" + } + } + ] + }, + "/blocks//events/": { + "result": [ + { + "event_index": 14194758, + "event": "CREDIT", + "payload": { + "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", + "asset": "UNNEGOTIABLE", + "block_index": 840464, + "calling_function": "issuance", + "event": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", + "quantity": 1, + "tx_index": 2726605 + } + } + ] + }, + "/events": { + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "payload": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665091, + "event": "ENHANCED_SEND", + "payload": { + "asset": "THOTHPEPE", + "block_index": 744232, + "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "memo": null, + "quantity": 1, + "source": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "status": "valid", + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665090, + "event": "CREDIT", + "payload": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665089, + "event": "DEBIT", + "payload": { + "action": "send", + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "THOTHPEPE", + "block_index": 744232, + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665088, + "event": "TRANSACTION_PARSED", + "payload": { + "supported": true, + "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + }, + "/events/": { + "result": [ + { + "event_index": 10665092, + "event": "TRANSACTION_PARSED", + "payload": { + "supported": true, + "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + }, + "/events/": { + "result": [ + { + "event_index": 10665090, + "event": "CREDIT", + "payload": { + "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", + "asset": "THOTHPEPE", + "block_index": 744232, + "calling_function": "send", + "event": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", + "quantity": 1, + "tx_index": 2056160 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665085, + "event": "CREDIT", + "payload": { + "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", + "asset": "XCP", + "block_index": 744232, + "calling_function": "dispense", + "event": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", + "quantity": 10000000000, + "tx_index": 2056159 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665082, + "event": "CREDIT", + "payload": { + "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", + "asset": "FREEDOMKEK", + "block_index": 744232, + "calling_function": "send", + "event": "b419d19729c2be813405c548431f4840d5c909b875f94b7c56aeca134e328ef6", + "quantity": 1, + "tx_index": 2056158 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665078, + "event": "CREDIT", + "payload": { + "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "145ebf6c563c4e91a2bc488954ef701dad730fc065697979c80d6d85cbba63e1", + "quantity": 1, + "tx_index": 2056157 + }, + "block_index": 744232, + "timestamp": 1712256340 + }, + { + "event_index": 10665074, + "event": "CREDIT", + "payload": { + "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", + "asset": "PEPEFRIDAY", + "block_index": 744232, + "calling_function": "send", + "event": "388c7208d52bf617c1a3eef238a668f694a4f72dc97b3be92562fe636ca646fa", + "quantity": 2, + "tx_index": 2056156 + }, + "block_index": 744232, + "timestamp": 1712256340 + } + ] + }, + "/mempool/events": { + "result": [] + }, + "/mempool/events/": { + "result": [] } } \ No newline at end of file From e0afc267195be9cc51126e87e348adfa857fb488 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 13:30:38 +0200 Subject: [PATCH 123/128] payload -> params --- apiary.apib | 40 +++++++++---------- .../counterpartycore/lib/ledger.py | 8 ++-- .../test/fixtures/api_v2_fixtures.json | 34 ++++++++-------- counterparty-core/tools/apicache.json | 40 +++++++++---------- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/apiary.apib b/apiary.apib index 4555cc6ac4..81b2b57862 100644 --- a/apiary.apib +++ b/apiary.apib @@ -170,7 +170,7 @@ Returns the events of a block { "event_index": 14194760, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 840464, "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", @@ -180,7 +180,7 @@ Returns the events of a block { "event_index": 14194759, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "tx_index": 2726605 @@ -189,7 +189,7 @@ Returns the events of a block { "event_index": 14194758, "event": "CREDIT", - "payload": { + "params": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -202,7 +202,7 @@ Returns the events of a block { "event_index": 14194757, "event": "ASSET_ISSUANCE", - "payload": { + "params": { "asset": "UNNEGOTIABLE", "asset_longname": null, "block_index": 840464, @@ -226,7 +226,7 @@ Returns the events of a block { "event_index": 14194756, "event": "ASSET_CREATION", - "payload": { + "params": { "asset_id": "75313533584419238", "asset_longname": null, "asset_name": "UNNEGOTIABLE", @@ -236,7 +236,7 @@ Returns the events of a block { "event_index": 14194755, "event": "DEBIT", - "payload": { + "params": { "action": "issuance fee", "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "XCP", @@ -249,7 +249,7 @@ Returns the events of a block { "event_index": 14194754, "event": "NEW_TRANSACTION", - "payload": { + "params": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, @@ -265,7 +265,7 @@ Returns the events of a block { "event_index": 14194753, "event": "NEW_BLOCK", - "payload": { + "params": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, @@ -341,7 +341,7 @@ Returns the events of a block filtered by event { "event_index": 14194758, "event": "CREDIT", - "payload": { + "params": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -3167,7 +3167,7 @@ Returns all events { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -3178,7 +3178,7 @@ Returns all events { "event_index": 10665091, "event": "ENHANCED_SEND", - "payload": { + "params": { "asset": "THOTHPEPE", "block_index": 744232, "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", @@ -3195,7 +3195,7 @@ Returns all events { "event_index": 10665090, "event": "CREDIT", - "payload": { + "params": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -3210,7 +3210,7 @@ Returns all events { "event_index": 10665089, "event": "DEBIT", - "payload": { + "params": { "action": "send", "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "THOTHPEPE", @@ -3225,7 +3225,7 @@ Returns all events { "event_index": 10665088, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", "tx_index": 2056159 @@ -3252,7 +3252,7 @@ Returns the event of an index { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -3483,7 +3483,7 @@ Returns the events filtered by event name { "event_index": 10665090, "event": "CREDIT", - "payload": { + "params": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -3498,7 +3498,7 @@ Returns the events filtered by event name { "event_index": 10665085, "event": "CREDIT", - "payload": { + "params": { "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", "asset": "XCP", "block_index": 744232, @@ -3513,7 +3513,7 @@ Returns the events filtered by event name { "event_index": 10665082, "event": "CREDIT", - "payload": { + "params": { "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "FREEDOMKEK", "block_index": 744232, @@ -3528,7 +3528,7 @@ Returns the events filtered by event name { "event_index": 10665078, "event": "CREDIT", - "payload": { + "params": { "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", "asset": "PEPEFRIDAY", "block_index": 744232, @@ -3543,7 +3543,7 @@ Returns the events filtered by event name { "event_index": 10665074, "event": "CREDIT", - "payload": { + "params": { "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", "asset": "PEPEFRIDAY", "block_index": 744232, diff --git a/counterparty-core/counterpartycore/lib/ledger.py b/counterparty-core/counterpartycore/lib/ledger.py index c48d089ee0..def7708f8f 100644 --- a/counterparty-core/counterpartycore/lib/ledger.py +++ b/counterparty-core/counterpartycore/lib/ledger.py @@ -87,7 +87,7 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li else: limit = "" # no sql injection here - select_fields = "message_index AS event_index, event, bindings AS payload" + select_fields = "message_index AS event_index, event, bindings AS params" if block_index is None: select_fields += ", block_index, timestamp" query = f""" @@ -104,7 +104,7 @@ def get_events(db, block_index=None, event=None, event_index=None, last=None, li cursor.execute(query, tuple(bindings)) events = cursor.fetchall() for i, _ in enumerate(events): - events[i]["payload"] = json.loads(events[i]["payload"]) + events[i]["params"] = json.loads(events[i]["params"]) return events @@ -163,7 +163,7 @@ def get_mempool_events(db, event_name=None): bindings.append(event_name) # no sql injection here query = """ - SELECT tx_hash, event, bindings AS payload, timestamp + SELECT tx_hash, event, bindings AS params, timestamp FROM mempool """ if event_name is not None: @@ -172,7 +172,7 @@ def get_mempool_events(db, event_name=None): cursor.execute(query, tuple(bindings)) events = cursor.fetchall() for i, _ in enumerate(events): - events[i]["payload"] = json.loads(events[i]["payload"]) + events[i]["params"] = json.loads(events[i]["params"]) return events diff --git a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json index 4631558d73..41b89d29c6 100644 --- a/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json +++ b/counterparty-core/counterpartycore/test/fixtures/api_v2_fixtures.json @@ -137,7 +137,7 @@ { "event_index": 1183, "event": "NEW_BLOCK", - "payload": { + "params": { "block_hash": "8a09b2faf0a7ad67eb4ab5c948b9769fc87eb2ec5e16108f2cde8bd9e6cf7607", "block_index": 310492, "block_time": 310492000, @@ -150,7 +150,7 @@ { "event_index": 1182, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 310491, "ledger_hash": "3114d8091cfcaa9944c6fab49d51950535c4ef269877d58c372ed80b2b472ec6", "messages_hash": "9671cfedb3124b67ed996c547cb26a32e95490009ad56065c79be54a28c45994", @@ -160,7 +160,7 @@ { "event_index": 1181, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "74db175c4669a3d3a59e3fcddce9e97fcd7d12c35b58ef31845a1b20a1739498", "tx_index": 492 @@ -169,7 +169,7 @@ { "event_index": 1180, "event": "OPEN_ORDER", - "payload": { + "params": { "block_index": 310491, "expiration": 2000, "expire_index": 312491, @@ -192,7 +192,7 @@ { "event_index": 1179, "event": "DEBIT", - "payload": { + "params": { "action": "open order", "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "XCP", @@ -205,7 +205,7 @@ { "event_index": 1178, "event": "NEW_TRANSACTION", - "payload": { + "params": { "block_hash": "811abd7cf2b768cfdaa84ab44c63f4463c96a368ead52125bf149cf0c7447b16", "block_index": 310491, "block_time": 310491000, @@ -1260,7 +1260,7 @@ { "event_index": 1237, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 310500, "ledger_hash": "5ffefc7a2724be6bd697796bb82638ec913c5cbb73627153d1a13b48c7a6c02d", "messages_hash": "45f296a535c13129cb1aaeb4e28a03e04ad902917891c39ae59ea2894e9f868f", @@ -1272,7 +1272,7 @@ { "event_index": 1236, "event": "NEW_BLOCK", - "payload": { + "params": { "block_hash": "54aeaf47d5387964e2d51617bf3af50520a0449410e0d096cf8c2aa9dad5550b", "block_index": 310500, "block_time": 310500000, @@ -1287,7 +1287,7 @@ { "event_index": 1235, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 310499, "ledger_hash": "b9fcbdafddd46fdda061f6e9f8744b426b6ca37e32b315df1098cbc7899ae9b9", "messages_hash": "d6aedacd4f81520d86ae47c9c776d17a213c706a5cf7c91203a4299261d1648c", @@ -1299,7 +1299,7 @@ { "event_index": 1234, "event": "NEW_BLOCK", - "payload": { + "params": { "block_hash": "1950e1a4d7fc820ed9603f6df6819c3c953c277c726340dec2a4253e261a1764", "block_index": 310499, "block_time": 310499000, @@ -1314,7 +1314,7 @@ { "event_index": 1233, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 310498, "ledger_hash": "5fe6cdb0828379bf240fad99c68bba34e1889bbc19605ce5c297b82352264414", "messages_hash": "113207bd13dda56b5e5edf305f70a56e62cc861184e1e95a64e79ce100462c98", @@ -1330,7 +1330,7 @@ { "event_index": 10, "event": "ASSET_CREATION", - "payload": { + "params": { "asset_id": "697326324582", "asset_longname": null, "asset_name": "DIVISIBLE", @@ -1434,7 +1434,7 @@ { "event_index": 1231, "event": "CREDIT", - "payload": { + "params": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "A95428956661682277", "block_index": 310498, @@ -1449,7 +1449,7 @@ { "event_index": 1223, "event": "CREDIT", - "payload": { + "params": { "address": "mn6q3dS2EnDUx3bmyWc6D4szJNVGtaR7zc", "asset": "PARENT", "block_index": 310497, @@ -1464,7 +1464,7 @@ { "event_index": 1214, "event": "CREDIT", - "payload": { + "params": { "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", "asset": "XCP", "block_index": 310496, @@ -1479,7 +1479,7 @@ { "event_index": 1207, "event": "CREDIT", - "payload": { + "params": { "address": "mqPCfvqTfYctXMUfmniXeG2nyaN8w6tPmj", "asset": "DIVIDEND", "block_index": 310495, @@ -1494,7 +1494,7 @@ { "event_index": 1201, "event": "CREDIT", - "payload": { + "params": { "address": "mnfAHmddVibnZNSkh8DvKaQoiEfNsxjXzH", "asset": "DIVIDEND", "block_index": 310494, diff --git a/counterparty-core/tools/apicache.json b/counterparty-core/tools/apicache.json index 0fc73ef18b..ced56dfecc 100644 --- a/counterparty-core/tools/apicache.json +++ b/counterparty-core/tools/apicache.json @@ -2184,7 +2184,7 @@ { "event_index": 14194760, "event": "BLOCK_PARSED", - "payload": { + "params": { "block_index": 840464, "ledger_hash": "b3f8cbb50b0705a5c4a8495f8b5128de13a32daebd8ac5e8316a010f0d203584", "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540", @@ -2194,7 +2194,7 @@ { "event_index": 14194759, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "876a6cfbd4aa22ba4fa85c2e1953a1c66649468a43a961ad16ea4d5329e3e4c5", "tx_index": 2726605 @@ -2203,7 +2203,7 @@ { "event_index": 14194758, "event": "CREDIT", - "payload": { + "params": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -2216,7 +2216,7 @@ { "event_index": 14194757, "event": "ASSET_ISSUANCE", - "payload": { + "params": { "asset": "UNNEGOTIABLE", "asset_longname": null, "block_index": 840464, @@ -2240,7 +2240,7 @@ { "event_index": 14194756, "event": "ASSET_CREATION", - "payload": { + "params": { "asset_id": "75313533584419238", "asset_longname": null, "asset_name": "UNNEGOTIABLE", @@ -2250,7 +2250,7 @@ { "event_index": 14194755, "event": "DEBIT", - "payload": { + "params": { "action": "issuance fee", "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "XCP", @@ -2263,7 +2263,7 @@ { "event_index": 14194754, "event": "NEW_TRANSACTION", - "payload": { + "params": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, @@ -2279,7 +2279,7 @@ { "event_index": 14194753, "event": "NEW_BLOCK", - "payload": { + "params": { "block_hash": "00000000000000000001093d4d6b21b80800fff6e5ea15cce6d65066f482cce9", "block_index": 840464, "block_time": 1713852783, @@ -2294,7 +2294,7 @@ { "event_index": 14194758, "event": "CREDIT", - "payload": { + "params": { "address": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", "asset": "UNNEGOTIABLE", "block_index": 840464, @@ -2311,7 +2311,7 @@ { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -2322,7 +2322,7 @@ { "event_index": 10665091, "event": "ENHANCED_SEND", - "payload": { + "params": { "asset": "THOTHPEPE", "block_index": 744232, "destination": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", @@ -2339,7 +2339,7 @@ { "event_index": 10665090, "event": "CREDIT", - "payload": { + "params": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -2354,7 +2354,7 @@ { "event_index": 10665089, "event": "DEBIT", - "payload": { + "params": { "action": "send", "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "THOTHPEPE", @@ -2369,7 +2369,7 @@ { "event_index": 10665088, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "bbb2dfa7e7a32288a702ef0091ece8b2a929f94fd967a18e6071cd9c2b085eaf", "tx_index": 2056159 @@ -2384,7 +2384,7 @@ { "event_index": 10665092, "event": "TRANSACTION_PARSED", - "payload": { + "params": { "supported": true, "tx_hash": "7b39d3ebd9fe8293004a1a8b8eb2d01f1664e5d8b05e8cb94f30b1da2c2f9650", "tx_index": 2056160 @@ -2399,7 +2399,7 @@ { "event_index": 10665090, "event": "CREDIT", - "payload": { + "params": { "address": "13re7J5Y5a8nZZSp8o1a3sEUqGik4NMXhS", "asset": "THOTHPEPE", "block_index": 744232, @@ -2414,7 +2414,7 @@ { "event_index": 10665085, "event": "CREDIT", - "payload": { + "params": { "address": "1LfDk3Ex9KPYS6L1WGwNdt1TvEg6Le8uq", "asset": "XCP", "block_index": 744232, @@ -2429,7 +2429,7 @@ { "event_index": 10665082, "event": "CREDIT", - "payload": { + "params": { "address": "173cE6ScUFCmBLCqZeG18ij6r9KHRPbAjC", "asset": "FREEDOMKEK", "block_index": 744232, @@ -2444,7 +2444,7 @@ { "event_index": 10665078, "event": "CREDIT", - "payload": { + "params": { "address": "1P8nYZwLmecAkQUHsx2H9Nkxd51UJ2Asau", "asset": "PEPEFRIDAY", "block_index": 744232, @@ -2459,7 +2459,7 @@ { "event_index": 10665074, "event": "CREDIT", - "payload": { + "params": { "address": "1NzDQ7HLm6PqJ2Wy6jEKMT7Zw1UbtjUV5a", "asset": "PEPEFRIDAY", "block_index": 744232, From 9f686bf9f6e59b75466e50b2cd9847ef46334e27 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 13:59:30 +0200 Subject: [PATCH 124/128] Add doc for encoding parameter --- apiary.apib | 604 +++++++++++++++++++-------- counterparty-core/tools/genapidoc.py | 45 +- 2 files changed, 478 insertions(+), 171 deletions(-) diff --git a/apiary.apib b/apiary.apib index 81b2b57862..72dd24d4a7 100644 --- a/apiary.apib +++ b/apiary.apib @@ -9,6 +9,7 @@ API routes are divided into 11 groups: - [`/blocks`](#/reference/blocks) - [`/transactions`](#/reference/transactions) - [`/addresses`](#/reference/addresses) +- [`/compose`](#/reference/compose) - [`/assets`](#/reference/assets) - [`/orders`](#/reference/orders) - [`/bets`](#/reference/bets) @@ -30,12 +31,12 @@ Notes: - All API responses follow the following format: - ``` + `` { "error": , "result": } - ``` + `` - Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. @@ -47,7 +48,7 @@ Returns server information and the list of documented routes in JSON format. + Response 200 (application/json) - ``` + `` { "server_ready": true, "network": "mainnet", @@ -58,7 +59,7 @@ Returns server information and the list of documented routes in JSON format. ] } - ``` + `` ## Group Blocks @@ -75,7 +76,7 @@ Returns the list of the last ten blocks + Response 200 (application/json) - ``` + `` { "result": [ { @@ -100,7 +101,7 @@ Returns the list of the last ten blocks } ] } - ``` + `` ### Get Block [GET /blocks/{block_index}] @@ -111,7 +112,7 @@ Return the information of a block + Response 200 (application/json) - ``` + `` { "result": { "block_index": 840464, @@ -124,7 +125,7 @@ Return the information of a block "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" } } - ``` + `` ### Get Transactions By Block [GET /blocks/{block_index}/transactions] @@ -135,7 +136,7 @@ Returns the transactions of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -153,7 +154,7 @@ Returns the transactions of a block } ] } - ``` + `` ### Get Events By Block [GET /blocks/{block_index}/events] @@ -164,7 +165,7 @@ Returns the events of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -275,7 +276,7 @@ Returns the events of a block } ] } - ``` + `` ### Get Event Counts By Block [GET /blocks/{block_index}/events/counts] @@ -286,7 +287,7 @@ Returns the event counts of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -323,7 +324,7 @@ Returns the event counts of a block } ] } - ``` + `` ### Get Events By Block And Event [GET /blocks/{block_index}/events/{event}] @@ -335,7 +336,7 @@ Returns the events of a block filtered by event + Response 200 (application/json) - ``` + `` { "result": [ { @@ -353,7 +354,7 @@ Returns the events of a block filtered by event } ] } - ``` + `` ### Get Credits By Block [GET /blocks/{block_index}/credits] @@ -364,7 +365,7 @@ Returns the credits of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -378,7 +379,7 @@ Returns the credits of a block } ] } - ``` + `` ### Get Debits By Block [GET /blocks/{block_index}/debits] @@ -389,7 +390,7 @@ Returns the debits of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -403,7 +404,7 @@ Returns the debits of a block } ] } - ``` + `` ### Get Expirations [GET /blocks/{block_index}/expirations] @@ -414,7 +415,7 @@ Returns the expirations of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -427,7 +428,7 @@ Returns the expirations of a block } ] } - ``` + `` ### Get Cancels [GET /blocks/{block_index}/cancels] @@ -438,7 +439,7 @@ Returns the cancels of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -459,7 +460,7 @@ Returns the cancels of a block } ] } - ``` + `` ### Get Destructions [GET /blocks/{block_index}/destructions] @@ -470,7 +471,7 @@ Returns the destructions of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -485,7 +486,7 @@ Returns the destructions of a block } ] } - ``` + `` ### Get Issuances By Block [GET /blocks/{block_index}/issuances] @@ -496,7 +497,7 @@ Returns the issuances of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -522,7 +523,7 @@ Returns the issuances of a block } ] } - ``` + `` ### Get Sends By Block [GET /blocks/{block_index}/sends{?limit}{?offset}] @@ -537,7 +538,7 @@ Returns the sends of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -554,7 +555,7 @@ Returns the sends of a block } ] } - ``` + `` ### Get Dispenses By Block [GET /blocks/{block_index}/dispenses] @@ -565,7 +566,7 @@ Returns the dispenses of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -581,7 +582,7 @@ Returns the dispenses of a block } ] } - ``` + `` ### Get Sweeps By Block [GET /blocks/{block_index}/sweeps] @@ -592,7 +593,7 @@ Returns the sweeps of a block + Response 200 (application/json) - ``` + `` { "result": [ { @@ -619,7 +620,7 @@ Returns the sweeps of a block } ] } - ``` + `` ## Group Transactions @@ -634,7 +635,7 @@ Returns Counterparty information from a raw transaction in hex format. + Response 200 (application/json) - ``` + `` { "result": { "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", @@ -662,7 +663,7 @@ Returns Counterparty information from a raw transaction in hex format. } } } - ``` + `` ### Unpack [GET /transactions/unpack{?datahex}{?block_index}] @@ -675,7 +676,7 @@ Unpacks Counterparty data in hex format and returns the message type and data. + Response 200 (application/json) - ``` + `` { "result": { "message_type": "issuance", @@ -696,7 +697,7 @@ Unpacks Counterparty data in hex format and returns the message type and data. } } } - ``` + `` ### Get Transaction By Hash [GET /transactions/{tx_hash}] @@ -707,7 +708,7 @@ Returns a transaction by its hash. + Response 200 (application/json) - ``` + `` { "result": { "tx_index": 2726605, @@ -741,7 +742,7 @@ Returns a transaction by its hash. } } } - ``` + `` ## Group Addresses @@ -754,7 +755,7 @@ Returns the balances of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -764,7 +765,7 @@ Returns the balances of an address } ] } - ``` + `` ### Get Balance By Address And Asset [GET /addresses/{address}/balances/{asset}] @@ -776,7 +777,7 @@ Returns the balance of an address and asset + Response 200 (application/json) - ``` + `` { "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -784,7 +785,7 @@ Returns the balance of an address and asset "quantity": 104200000000 } } - ``` + `` ### Get Credits By Address [GET /addresses/{address}/credits{?limit}{?offset}] @@ -799,7 +800,7 @@ Returns the credits of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -813,7 +814,7 @@ Returns the credits of an address } ] } - ``` + `` ### Get Debits By Address [GET /addresses/{address}/debits{?limit}{?offset}] @@ -828,7 +829,7 @@ Returns the debits of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -851,7 +852,7 @@ Returns the debits of an address } ] } - ``` + `` ### Get Bet By Feed [GET /addresses/{address}/bets{?status}] @@ -864,7 +865,7 @@ Returns the bets of a feed + Response 200 (application/json) - ``` + `` { "result": [ { @@ -907,7 +908,7 @@ Returns the bets of a feed } ] } - ``` + `` ### Get Broadcasts By Source [GET /addresses/{address}/broadcasts{?status}{?order_by}] @@ -922,7 +923,7 @@ Returns the broadcasts of a source + Response 200 (application/json) - ``` + `` { "result": [ { @@ -951,7 +952,7 @@ Returns the broadcasts of a source } ] } - ``` + `` ### Get Burns By Address [GET /addresses/{address}/burns] @@ -962,7 +963,7 @@ Returns the burns of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -976,7 +977,7 @@ Returns the burns of an address } ] } - ``` + `` ### Get Send By Address [GET /addresses/{address}/sends{?limit}{?offset}] @@ -991,7 +992,7 @@ Returns the sends of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1008,7 +1009,7 @@ Returns the sends of an address } ] } - ``` + `` ### Get Receive By Address [GET /addresses/{address}/receives{?limit}{?offset}] @@ -1023,7 +1024,7 @@ Returns the receives of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1040,7 +1041,7 @@ Returns the receives of an address } ] } - ``` + `` ### Get Send By Address And Asset [GET /addresses/{address}/sends/{asset}] @@ -1052,7 +1053,7 @@ Returns the sends of an address and asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1069,7 +1070,7 @@ Returns the sends of an address and asset } ] } - ``` + `` ### Get Receive By Address And Asset [GET /addresses/{address}/receives/{asset}{?limit}{?offset}] @@ -1085,7 +1086,7 @@ Returns the receives of an address and asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1102,7 +1103,7 @@ Returns the receives of an address and asset } ] } - ``` + `` ### Get Dispensers By Address [GET /addresses/{address}/dispensers{?status}] @@ -1115,7 +1116,7 @@ Returns the dispensers of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1136,7 +1137,7 @@ Returns the dispensers of an address } ] } - ``` + `` ### Get Dispensers By Address And Asset [GET /addresses/{address}/dispensers/{asset}{?status}] @@ -1150,7 +1151,7 @@ Returns the dispensers of an address and an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1171,7 +1172,7 @@ Returns the dispensers of an address and an asset } ] } - ``` + `` ### Get Sweeps By Address [GET /addresses/{address}/sweeps] @@ -1182,7 +1183,7 @@ Returns the sweeps of an address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -1198,7 +1199,30 @@ Returns the sweeps of an address } ] } - ``` + `` + +## Group Compose + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Bet [GET /addresses/{address}/compose/bet{?feed_address}{?bet_type}{?deadline}{?wager_quantity}{?counterwager_quantity}{?expiration}{?leverage}{?target_value}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1247,7 +1271,7 @@ Composes a transaction to issue a bet against a feed. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1265,7 +1289,28 @@ Composes a transaction to issue a bet against a feed. "name": "bet" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Broadcast [GET /addresses/{address}/compose/broadcast{?timestamp}{?value}{?fee_fraction}{?text}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1308,7 +1353,7 @@ Composes a transaction to broadcast textual and numerical information to the net + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1322,7 +1367,28 @@ Composes a transaction to broadcast textual and numerical information to the net "name": "broadcast" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose BTCPay [GET /addresses/{address}/compose/btcpay{?order_match_id}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1362,7 +1428,7 @@ Composes a transaction to pay for a BTC order match. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", @@ -1373,7 +1439,28 @@ Composes a transaction to pay for a BTC order match. "name": "btcpay" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Burn [GET /addresses/{address}/compose/burn{?quantity}{?overburn}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1415,7 +1502,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1427,7 +1514,28 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss "name": "burn" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Cancel [GET /addresses/{address}/compose/cancel{?offer_hash}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1467,7 +1575,7 @@ Composes a transaction to cancel an open order or bet. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", @@ -1478,7 +1586,28 @@ Composes a transaction to cancel an open order or bet. "name": "cancel" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Destroy [GET /addresses/{address}/compose/destroy{?asset}{?quantity}{?tag}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1520,7 +1649,7 @@ Composes a transaction to destroy a quantity of an asset. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1533,7 +1662,28 @@ Composes a transaction to destroy a quantity of an asset. "name": "destroy" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Dispenser [GET /addresses/{address}/compose/dispenser{?asset}{?give_quantity}{?escrow_quantity}{?mainchainrate}{?status}{?open_address}{?oracle_address}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1581,7 +1731,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1598,7 +1748,28 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse "name": "dispenser" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Dividend [GET /addresses/{address}/compose/dividend{?quantity_per_unit}{?asset}{?dividend_asset}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1640,7 +1811,7 @@ Composes a transaction to issue a dividend to holders of a given asset. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", @@ -1653,7 +1824,28 @@ Composes a transaction to issue a dividend to holders of a given asset. "name": "dividend" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Issuance [GET /addresses/{address}/compose/issuance{?asset}{?quantity}{?transfer_destination}{?divisible}{?lock}{?reset}{?description}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1704,7 +1896,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1721,7 +1913,28 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo "name": "issuance" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose MPMA [GET /addresses/{address}/compose/mpma{?assets}{?destinations}{?quantities}{?memo}{?memo_is_hex}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1765,7 +1978,7 @@ Composes a transaction to send multiple payments to multiple addresses. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", @@ -1794,7 +2007,28 @@ Composes a transaction to send multiple payments to multiple addresses. "name": "mpma" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Order [GET /addresses/{address}/compose/order{?give_asset}{?give_quantity}{?get_asset}{?get_quantity}{?expiration}{?fee_required}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1839,7 +2073,7 @@ Composes a transaction to place an order on the distributed exchange. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1855,7 +2089,28 @@ Composes a transaction to place an order on the distributed exchange. "name": "order" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Send [GET /addresses/{address}/compose/send{?destination}{?asset}{?quantity}{?memo}{?memo_is_hex}{?use_enhanced_send}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1903,7 +2158,7 @@ Composes a transaction to send a quantity of an asset to another address. + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1919,7 +2174,28 @@ Composes a transaction to send a quantity of an asset to another address. "name": "send" } } - ``` + `` + + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + ### Compose Sweep [GET /addresses/{address}/compose/sweep{?destination}{?flags}{?memo}{?encoding}{?fee_per_kb}{?regular_dust_size}{?multisig_dust_size}{?pubkey}{?allow_unconfirmed_inputs}{?fee}{?fee_provided}{?unspent_tx_hash}{?dust_return_pubkey}{?disable_utxo_locks}{?extended_tx_info}{?p2sh_pretx_txid}{?segwit}] @@ -1961,7 +2237,7 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti + Response 200 (application/json) - ``` + `` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1974,7 +2250,7 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti "name": "sweep" } } - ``` + `` ## Group Assets @@ -1990,7 +2266,7 @@ Returns the valid assets + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2015,7 +2291,7 @@ Returns the valid assets } ] } - ``` + `` ### Get Asset Info [GET /assets/{asset}] @@ -2026,7 +2302,7 @@ Returns the asset information + Response 200 (application/json) - ``` + `` { "result": { "asset": "UNNEGOTIABLE", @@ -2040,7 +2316,7 @@ Returns the asset information "holder_count": 1 } } - ``` + `` ### Get Asset Balances [GET /assets/{asset}/balances{?exclude_zero_balances}] @@ -2053,7 +2329,7 @@ Returns the asset balances + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2063,7 +2339,7 @@ Returns the asset balances } ] } - ``` + `` ### Get Balance By Address And Asset [GET /assets/{asset}/balances/{address}] @@ -2075,7 +2351,7 @@ Returns the balance of an address and asset + Response 200 (application/json) - ``` + `` { "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -2083,7 +2359,7 @@ Returns the balance of an address and asset "quantity": 104200000000 } } - ``` + `` ### Get Orders By Asset [GET /assets/{asset}/orders{?status}] @@ -2096,7 +2372,7 @@ Returns the orders of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2310,7 +2586,7 @@ Returns the orders of an asset } ] } - ``` + `` ### Get Credits By Asset [GET /assets/{asset}/credits{?limit}{?offset}] @@ -2325,7 +2601,7 @@ Returns the credits of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2339,7 +2615,7 @@ Returns the credits of an asset } ] } - ``` + `` ### Get Debits By Asset [GET /assets/{asset}/debits{?limit}{?offset}] @@ -2354,7 +2630,7 @@ Returns the debits of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2404,7 +2680,7 @@ Returns the debits of an asset } ] } - ``` + `` ### Get Dividends [GET /assets/{asset}/dividends] @@ -2415,7 +2691,7 @@ Returns the dividends of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2552,7 +2828,7 @@ Returns the dividends of an asset } ] } - ``` + `` ### Get Issuances By Asset [GET /assets/{asset}/issuances] @@ -2563,7 +2839,7 @@ Returns the issuances of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2589,7 +2865,7 @@ Returns the issuances of an asset } ] } - ``` + `` ### Get Sends By Asset [GET /assets/{asset}/sends{?limit}{?offset}] @@ -2604,7 +2880,7 @@ Returns the sends of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2669,7 +2945,7 @@ Returns the sends of an asset } ] } - ``` + `` ### Get Dispensers By Asset [GET /assets/{asset}/dispensers{?status}] @@ -2682,7 +2958,7 @@ Returns the dispensers of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2703,7 +2979,7 @@ Returns the dispensers of an asset } ] } - ``` + `` ### Get Dispensers By Address And Asset [GET /assets/{asset}/dispensers/{address}{?status}] @@ -2717,7 +2993,7 @@ Returns the dispensers of an address and an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2738,7 +3014,7 @@ Returns the dispensers of an address and an asset } ] } - ``` + `` ### Get Asset Holders [GET /assets/{asset}/holders] @@ -2749,7 +3025,7 @@ Returns the holders of an asset + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2794,7 +3070,7 @@ Returns the holders of an asset } ] } - ``` + `` ## Group Orders @@ -2807,7 +3083,7 @@ Returns the information of an order + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2831,7 +3107,7 @@ Returns the information of an order } ] } - ``` + `` ### Get Order Matches By Order [GET /orders/{order_hash}/matches{?status}] @@ -2844,7 +3120,7 @@ Returns the order matches of an order + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2870,7 +3146,7 @@ Returns the order matches of an order } ] } - ``` + `` ### Get BTCPays By Order [GET /orders/{order_hash}/btcpays] @@ -2881,7 +3157,7 @@ Returns the BTC pays of an order + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2896,7 +3172,7 @@ Returns the BTC pays of an order } ] } - ``` + `` ## Group Bets @@ -2909,7 +3185,7 @@ Returns the information of a bet + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2933,7 +3209,7 @@ Returns the information of a bet } ] } - ``` + `` ### Get Bet Matches By Bet [GET /bets/{bet_hash}/matches{?status}] @@ -2946,7 +3222,7 @@ Returns the bet matches of a bet + Response 200 (application/json) - ``` + `` { "result": [ { @@ -2977,7 +3253,7 @@ Returns the bet matches of a bet } ] } - ``` + `` ### Get Resolutions By Bet [GET /bets/{bet_hash}/resolutions] @@ -2988,7 +3264,7 @@ Returns the resolutions of a bet + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3004,7 +3280,7 @@ Returns the resolutions of a bet } ] } - ``` + `` ## Group Burns @@ -3022,7 +3298,7 @@ Returns the burns + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3072,7 +3348,7 @@ Returns the burns } ] } - ``` + `` ## Group Dispensers @@ -3085,7 +3361,7 @@ Returns the dispenser information by tx_hash + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3107,7 +3383,7 @@ Returns the dispenser information by tx_hash } ] } - ``` + `` ### Get Dispenses By Dispenser [GET /dispensers/{dispenser_hash}/dispenses] @@ -3118,7 +3394,7 @@ Returns the dispenses of a dispenser + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3145,7 +3421,7 @@ Returns the dispenses of a dispenser } ] } - ``` + `` ## Group Events @@ -3161,7 +3437,7 @@ Returns all events + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3235,7 +3511,7 @@ Returns all events } ] } - ``` + `` ### Get Event By Index [GET /events/{event_index}] @@ -3246,7 +3522,7 @@ Returns the event of an index + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3262,14 +3538,14 @@ Returns the event of an index } ] } - ``` + `` ### Get All Events Counts [GET /events/counts] Returns the event counts of all blocks + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3462,7 +3738,7 @@ Returns the event counts of all blocks } ] } - ``` + `` ### Get Events By Name [GET /events/{event}{?last}{?limit}] @@ -3477,7 +3753,7 @@ Returns the events filtered by event name + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3557,7 +3833,7 @@ Returns the events filtered by event name } ] } - ``` + `` ## Group Z-pages @@ -3571,13 +3847,13 @@ Health check route. + Response 200 (application/json) - ``` + `` { "result": { "status": "Healthy" } } - ``` + `` ## Group Bitcoin @@ -3594,7 +3870,7 @@ Returns all transactions involving a given address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3635,7 +3911,7 @@ Returns all transactions involving a given address } ] } - ``` + `` ### Get Oldest Transaction By Address [GET /bitcoin/addresses/{address}/transactions/oldest{?block_index}] @@ -3648,14 +3924,14 @@ Get the oldest transaction for an address. + Response 200 (application/json) - ``` + `` { "result": { "block_index": 833187, "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" } } - ``` + `` ### Get Unspent Txouts [GET /bitcoin/addresses/{address}/utxos{?unconfirmed}{?unspent_tx_hash}] @@ -3670,7 +3946,7 @@ Returns a list of unspent outputs for a specific address + Response 200 (application/json) - ``` + `` { "result": [ { @@ -3755,7 +4031,7 @@ Returns a list of unspent outputs for a specific address } ] } - ``` + `` ### PubKeyHash To Pubkey [GET /bitcoin/addresses/{address}/pubkey{?provided_pubkeys}] @@ -3768,11 +4044,11 @@ Get pubkey for an address. + Response 200 (application/json) - ``` + `` { "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" } - ``` + `` ### Get Transaction [GET /bitcoin/transactions/{tx_hash}{?verbose}] @@ -3785,7 +4061,7 @@ Get a transaction from the blockchain + Response 200 (application/json) - ``` + `` { "result": { "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", @@ -3830,7 +4106,7 @@ Get a transaction from the blockchain "blocktime": 1713951767 } } - ``` + `` ### Fee Per Kb [GET /bitcoin/estimatesmartfee{?conf_target}{?mode}] @@ -3844,11 +4120,11 @@ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` bloc + Response 200 (application/json) - ``` + `` { "result": 295443 } - ``` + `` ## Group Mempool @@ -3857,11 +4133,11 @@ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` bloc Returns all mempool events + Response 200 (application/json) - ``` + `` { "result": [] } - ``` + `` ### Get Mempool Events By Name [GET /mempool/events/{event}] @@ -3872,8 +4148,8 @@ Returns the mempool events filtered by event name + Response 200 (application/json) - ``` + `` { "result": [] } - ``` + `` diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index e1e56c14e2..b3a38717ed 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -41,6 +41,7 @@ def get_example_output(path, args): "/blocks", "/transactions", "/addresses", + "/compose", "/assets", "/orders", "/bets", @@ -51,6 +52,31 @@ def get_example_output(path, args): "/bitcoin", ] +GROUP_DOCS = { + "Compose": """ + +**Notes about optional parameter `encoding`.** + +By default the default value of the `encoding` parameter detailed above is `auto`, which means that `counterparty-server` automatically determines the best way to encode the Counterparty protocol data into a new transaction. If you know what you are doing and would like to explicitly specify an encoding: + +- To return the transaction as an **OP_RETURN** transaction, specify `opreturn` for the `encoding` parameter. + - **OP_RETURN** transactions cannot have more than 80 bytes of data. If you force OP_RETURN encoding and your transaction would have more than this amount, an exception will be generated. +- To return the transaction as a **multisig** transaction, specify `multisig` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. + - Note that with the newest versions of Bitcoin (0.12.1 onward), bare multisig encoding does not reliably propagate. More information on this is documented [here](https://github.com/rubensayshi/counterparty-core/pull/9). +- To return the transaction as a **pubkeyhash** transaction, specify `pubkeyhash` for the `encoding` parameter. + - `pubkey` should be set to the hex-encoded public key of the source address. +- To return the transaction as a 2 part **P2SH** transaction, specify `P2SH` for the encoding parameter. + - First call the `create_` method with the `encoding` set to `P2SH`. + - Sign the transaction as usual and broadcast it. It's recommended but not required to wait the transaction to confirm as malleability is an issue here (P2SH isn't yet supported on segwit addresses). + - The resulting `txid` must be passed again on an identic call to the `create_` method, but now passing an additional parameter `p2sh_pretx_txid` with the value of the previous transaction's id. + - The resulting transaction is a `P2SH` encoded message, using the redeem script on the transaction inputs as data carrying mechanism. + - Sign the transaction following the `Bitcoinjs-lib on javascript, signing a P2SH redeeming transaction` section + - **NOTE**: Don't leave pretxs hanging without transmitting the second transaction as this pollutes the UTXO set and risks making bitcoin harder to run on low spec nodes. + +""" +} + def gen_groups_toc(): toc = "" @@ -96,12 +122,12 @@ def gen_groups_toc(): - All API responses follow the following format: - ``` + `` { "error": , "result": } - ``` + `` - Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. @@ -113,7 +139,7 @@ def gen_groups_toc(): + Response 200 (application/json) - ``` + `` { "server_ready": true, "network": "mainnet", @@ -124,7 +150,7 @@ def gen_groups_toc(): ] } - ``` + `` """ md = md.replace("{root_path}", root_path) @@ -137,13 +163,18 @@ def gen_groups_toc(): current_group = None for path, route in server.routes.ROUTES.items(): route_group = path.split("/")[1] + if "compose" in path: + route_group = "Compose" if route_group != current_group: current_group = route_group if current_group == "healthz": current_group = "Z-Pages" md += f"\n## Group {current_group.capitalize()}\n" - blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") + if current_group in GROUP_DOCS: + md += GROUP_DOCS[current_group] + + blueprint_path = path.replace("<", "{").replace(">", "}").replace("int:", "") title = " ".join([part.capitalize() for part in str(route["function"].__name__).split("_")]) title = title.replace("Pubkeyhash", "PubKeyHash") title = title.replace("Mpma", "MPMA") @@ -185,10 +216,10 @@ def gen_groups_toc(): example_output = cache[path] example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" - md += " ```\n" + md += " ``\n" for line in example_output_json.split("\n"): md += f" {line}\n" - md += " ```\n" + md += " ``\n" with open(CACHE_FILE, "w") as f: json.dump(cache, f, indent=4) From 6b2d0090014db03918e37b6b0103f1de545dcf2f Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 14:02:25 +0200 Subject: [PATCH 125/128] fix typos --- apiary.apib | 328 +++++++++++++-------------- counterparty-core/tools/genapidoc.py | 12 +- 2 files changed, 170 insertions(+), 170 deletions(-) diff --git a/apiary.apib b/apiary.apib index 72dd24d4a7..d439534b0c 100644 --- a/apiary.apib +++ b/apiary.apib @@ -31,12 +31,12 @@ Notes: - All API responses follow the following format: - `` + ``` { "error": , "result": } - `` + ``` - Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. @@ -48,7 +48,7 @@ Returns server information and the list of documented routes in JSON format. + Response 200 (application/json) - `` + ``` { "server_ready": true, "network": "mainnet", @@ -59,7 +59,7 @@ Returns server information and the list of documented routes in JSON format. ] } - `` + ``` ## Group Blocks @@ -76,7 +76,7 @@ Returns the list of the last ten blocks + Response 200 (application/json) - `` + ``` { "result": [ { @@ -101,7 +101,7 @@ Returns the list of the last ten blocks } ] } - `` + ``` ### Get Block [GET /blocks/{block_index}] @@ -112,7 +112,7 @@ Return the information of a block + Response 200 (application/json) - `` + ``` { "result": { "block_index": 840464, @@ -125,7 +125,7 @@ Return the information of a block "messages_hash": "801d961c45a257f85ef0f10a6a8fdf048a520ae4861c0903f26365b3eaaaf540" } } - `` + ``` ### Get Transactions By Block [GET /blocks/{block_index}/transactions] @@ -136,7 +136,7 @@ Returns the transactions of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -154,7 +154,7 @@ Returns the transactions of a block } ] } - `` + ``` ### Get Events By Block [GET /blocks/{block_index}/events] @@ -165,7 +165,7 @@ Returns the events of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -276,7 +276,7 @@ Returns the events of a block } ] } - `` + ``` ### Get Event Counts By Block [GET /blocks/{block_index}/events/counts] @@ -287,7 +287,7 @@ Returns the event counts of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -324,7 +324,7 @@ Returns the event counts of a block } ] } - `` + ``` ### Get Events By Block And Event [GET /blocks/{block_index}/events/{event}] @@ -336,7 +336,7 @@ Returns the events of a block filtered by event + Response 200 (application/json) - `` + ``` { "result": [ { @@ -354,7 +354,7 @@ Returns the events of a block filtered by event } ] } - `` + ``` ### Get Credits By Block [GET /blocks/{block_index}/credits] @@ -365,7 +365,7 @@ Returns the credits of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -379,7 +379,7 @@ Returns the credits of a block } ] } - `` + ``` ### Get Debits By Block [GET /blocks/{block_index}/debits] @@ -390,7 +390,7 @@ Returns the debits of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -404,7 +404,7 @@ Returns the debits of a block } ] } - `` + ``` ### Get Expirations [GET /blocks/{block_index}/expirations] @@ -415,7 +415,7 @@ Returns the expirations of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -428,7 +428,7 @@ Returns the expirations of a block } ] } - `` + ``` ### Get Cancels [GET /blocks/{block_index}/cancels] @@ -439,7 +439,7 @@ Returns the cancels of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -460,7 +460,7 @@ Returns the cancels of a block } ] } - `` + ``` ### Get Destructions [GET /blocks/{block_index}/destructions] @@ -471,7 +471,7 @@ Returns the destructions of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -486,7 +486,7 @@ Returns the destructions of a block } ] } - `` + ``` ### Get Issuances By Block [GET /blocks/{block_index}/issuances] @@ -497,7 +497,7 @@ Returns the issuances of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -523,7 +523,7 @@ Returns the issuances of a block } ] } - `` + ``` ### Get Sends By Block [GET /blocks/{block_index}/sends{?limit}{?offset}] @@ -538,7 +538,7 @@ Returns the sends of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -555,7 +555,7 @@ Returns the sends of a block } ] } - `` + ``` ### Get Dispenses By Block [GET /blocks/{block_index}/dispenses] @@ -566,7 +566,7 @@ Returns the dispenses of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -582,7 +582,7 @@ Returns the dispenses of a block } ] } - `` + ``` ### Get Sweeps By Block [GET /blocks/{block_index}/sweeps] @@ -593,7 +593,7 @@ Returns the sweeps of a block + Response 200 (application/json) - `` + ``` { "result": [ { @@ -620,7 +620,7 @@ Returns the sweeps of a block } ] } - `` + ``` ## Group Transactions @@ -635,7 +635,7 @@ Returns Counterparty information from a raw transaction in hex format. + Response 200 (application/json) - `` + ``` { "result": { "source": "178etygrwEeeyQso9we85rUqYZbkiqzL4A", @@ -663,7 +663,7 @@ Returns Counterparty information from a raw transaction in hex format. } } } - `` + ``` ### Unpack [GET /transactions/unpack{?datahex}{?block_index}] @@ -676,7 +676,7 @@ Unpacks Counterparty data in hex format and returns the message type and data. + Response 200 (application/json) - `` + ``` { "result": { "message_type": "issuance", @@ -697,7 +697,7 @@ Unpacks Counterparty data in hex format and returns the message type and data. } } } - `` + ``` ### Get Transaction By Hash [GET /transactions/{tx_hash}] @@ -708,7 +708,7 @@ Returns a transaction by its hash. + Response 200 (application/json) - `` + ``` { "result": { "tx_index": 2726605, @@ -742,7 +742,7 @@ Returns a transaction by its hash. } } } - `` + ``` ## Group Addresses @@ -755,7 +755,7 @@ Returns the balances of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -765,7 +765,7 @@ Returns the balances of an address } ] } - `` + ``` ### Get Balance By Address And Asset [GET /addresses/{address}/balances/{asset}] @@ -777,7 +777,7 @@ Returns the balance of an address and asset + Response 200 (application/json) - `` + ``` { "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -785,7 +785,7 @@ Returns the balance of an address and asset "quantity": 104200000000 } } - `` + ``` ### Get Credits By Address [GET /addresses/{address}/credits{?limit}{?offset}] @@ -800,7 +800,7 @@ Returns the credits of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -814,7 +814,7 @@ Returns the credits of an address } ] } - `` + ``` ### Get Debits By Address [GET /addresses/{address}/debits{?limit}{?offset}] @@ -829,7 +829,7 @@ Returns the debits of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -852,7 +852,7 @@ Returns the debits of an address } ] } - `` + ``` ### Get Bet By Feed [GET /addresses/{address}/bets{?status}] @@ -865,7 +865,7 @@ Returns the bets of a feed + Response 200 (application/json) - `` + ``` { "result": [ { @@ -908,7 +908,7 @@ Returns the bets of a feed } ] } - `` + ``` ### Get Broadcasts By Source [GET /addresses/{address}/broadcasts{?status}{?order_by}] @@ -923,7 +923,7 @@ Returns the broadcasts of a source + Response 200 (application/json) - `` + ``` { "result": [ { @@ -952,7 +952,7 @@ Returns the broadcasts of a source } ] } - `` + ``` ### Get Burns By Address [GET /addresses/{address}/burns] @@ -963,7 +963,7 @@ Returns the burns of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -977,7 +977,7 @@ Returns the burns of an address } ] } - `` + ``` ### Get Send By Address [GET /addresses/{address}/sends{?limit}{?offset}] @@ -992,7 +992,7 @@ Returns the sends of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1009,7 +1009,7 @@ Returns the sends of an address } ] } - `` + ``` ### Get Receive By Address [GET /addresses/{address}/receives{?limit}{?offset}] @@ -1024,7 +1024,7 @@ Returns the receives of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1041,7 +1041,7 @@ Returns the receives of an address } ] } - `` + ``` ### Get Send By Address And Asset [GET /addresses/{address}/sends/{asset}] @@ -1053,7 +1053,7 @@ Returns the sends of an address and asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1070,7 +1070,7 @@ Returns the sends of an address and asset } ] } - `` + ``` ### Get Receive By Address And Asset [GET /addresses/{address}/receives/{asset}{?limit}{?offset}] @@ -1086,7 +1086,7 @@ Returns the receives of an address and asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1103,7 +1103,7 @@ Returns the receives of an address and asset } ] } - `` + ``` ### Get Dispensers By Address [GET /addresses/{address}/dispensers{?status}] @@ -1116,7 +1116,7 @@ Returns the dispensers of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1137,7 +1137,7 @@ Returns the dispensers of an address } ] } - `` + ``` ### Get Dispensers By Address And Asset [GET /addresses/{address}/dispensers/{asset}{?status}] @@ -1151,7 +1151,7 @@ Returns the dispensers of an address and an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1172,7 +1172,7 @@ Returns the dispensers of an address and an asset } ] } - `` + ``` ### Get Sweeps By Address [GET /addresses/{address}/sweeps] @@ -1183,7 +1183,7 @@ Returns the sweeps of an address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -1199,7 +1199,7 @@ Returns the sweeps of an address } ] } - `` + ``` ## Group Compose @@ -1271,7 +1271,7 @@ Composes a transaction to issue a bet against a feed. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914bce6191bf2fd5981313cae869e9fafe164f7dbaf88ac0000000000000000316a2f0d1e454cefefcbe14dffa4c01ecd608ec45d2594e5d27c699f4ef2725648c509bf828ec195ee18f83e052061236deff2db0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1289,7 +1289,7 @@ Composes a transaction to issue a bet against a feed. "name": "bet" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1353,7 +1353,7 @@ Composes a transaction to broadcast textual and numerical information to the net + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002b6a290d1e454cefefcbe17b1100cb21d3398ec45d2594e5d1d822df41d03a332741261ce2f9aee7827cd91c340c0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1367,7 +1367,7 @@ Composes a transaction to broadcast textual and numerical information to the net "name": "broadcast" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1428,7 +1428,7 @@ Composes a transaction to pay for a BTC order match. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "0200000000010161101e1990879ee64168cce92c9caf338bb571e9cb246b1c2ab87124b95091900200000016001482f2ccc569325050e36c13b55a4065113d985066ffffffff0383c3040000000000160014a9943f67bcd30331d5a4ec6d902cbe03789a1b9700000000000000004b6a49aae396d448ed266a7785be1f6fcfa38dbe3e6e043e3d67691f678d6aa3b30e423f66ffad71eaf3231ef8f05dd5cc2f5b1ea14d33274b9cddacca5bd816a1ce6d5b4d498eb66a981db7add758000000000016001482f2ccc569325050e36c13b55a4065113d98506602000000000000", @@ -1439,7 +1439,7 @@ Composes a transaction to pay for a BTC order match. "name": "btcpay" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1502,7 +1502,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff02e8030000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ace61b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1514,7 +1514,7 @@ Composes a transaction to burn a given quantity of BTC for XCP (on mainnet, poss "name": "burn" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1575,7 +1575,7 @@ Composes a transaction to cancel an open order or bet. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000014709bd6af5d4d7f518f80539d4fe9acd5220a520a7b4287416a7379af9e66154020000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988acffffffff0200000000000000002b6a292f3720d2b8ae7343c6d0456802c531e1216f466ceb12b96c6fbe417a97291a0660e51fc47fcc1ee1a878667900000000001976a91432dff6deb7ca3bbc14f7037fa6ef8a8cf8e39fb988ac00000000", @@ -1586,7 +1586,7 @@ Composes a transaction to cancel an open order or bet. "name": "cancel" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1649,7 +1649,7 @@ Composes a transaction to destroy a quantity of an asset. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000226a200d1e454cefefcbe10bffa672ce93608ec55d2594e5d1946a776c900731380c6b94160406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1662,7 +1662,7 @@ Composes a transaction to destroy a quantity of an asset. "name": "destroy" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1731,7 +1731,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0200000000000000002c6a2a0d1e454cefefcbe169ffa672ce93608ec55d2594e5d1946a774ef272564b2d4ad8c28ec195ee18f85a160c0b0406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1748,7 +1748,7 @@ Opens or closes a dispenser for a given asset at a given rate of main chain asse "name": "dispenser" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1811,7 +1811,7 @@ Composes a transaction to issue a dividend to holders of a given asset. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000010af94458ae5aa794c49cd27f7b800a7c68c8dd4f59ff66c99db4e9e353c06d93010000001976a914a9055398b92818794b38b15794096f752167e25f88acffffffff020000000000000000236a21068a00268d252c3a8ed0bddb5ef79f823894aa7de1e196c005510f4d787c936a979b230000000000001976a914a9055398b92818794b38b15794096f752167e25f88ac00000000", @@ -1824,7 +1824,7 @@ Composes a transaction to issue a dividend to holders of a given asset. "name": "dividend" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1896,7 +1896,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff0322020000000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac0000000000000000236a210d1e454cefefcbe173ffa672cf3a36751b5d2594e5d1946a774ff272960578057c17ec0306000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -1913,7 +1913,7 @@ Composes a transaction to Issue a new asset, issue more of an existing asset, lo "name": "issuance" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -1978,7 +1978,7 @@ Composes a transaction to send multiple payments to multiple addresses. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "0100000001fc9b7b3a0552bdfc3c62096e9d7669fb72d5482c7b4f9618138fdffdc831d60b000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88acffffffff04e80300000000000069512103ce014780415d0eafbdadfacfa0cf2604a005a87157042f277627c952eedcbb1f2103abf2b72459ee70e6240a7b2ade1a6fa41c7f38cc1db5e63c6f92c01b859017ee2102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512102ce014780415d0eafbd2fcbf00e308d420b59df89ebba83369fea96a9a06fcf562102373ec5e1389ccadf0a972ec451f8aea015104ded7a57b936d374d0ecfe8067412102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53aee80300000000000069512103d0014780415d0eafbd76dacca0b613dda4b8f37e3015031f11220ac5cf43ef4e21034051b78cdcbde85f0c120261e6ab383015104ded7a57b93cd374d900776d4e132102e849a65234e77627daab722dd75aee7a8f35981ec1dbd5ec5ee7220075b2cd2d53ae22fd0200000000001976a914a39dbfab6f1da182af53a4d14799ee545a6176be88ac00000000", @@ -2007,7 +2007,7 @@ Composes a transaction to send multiple payments to multiple addresses. "name": "mpma" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -2073,7 +2073,7 @@ Composes a transaction to place an order on the distributed exchange. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000356a330d1e454cefefcbe16fffa672ce93608ec55d2594e5d1946a774ef2724a2a4f457bc28ec195ee18fbd616f461236d8be718616dac000406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -2089,7 +2089,7 @@ Composes a transaction to place an order on the distributed exchange. "name": "order" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -2158,7 +2158,7 @@ Composes a transaction to send a quantity of an asset to another address. + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000306a2e0d1e454cefefcbe167ffa672ce93608ec55d2594e5d1946a774e4e944f50dfb46943bffd3b68866791f7f496f8c270060406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -2174,7 +2174,7 @@ Composes a transaction to send a quantity of an asset to another address. "name": "send" } } - `` + ``` **Notes about optional parameter `encoding`.** @@ -2237,7 +2237,7 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti + Response 200 (application/json) - `` + ``` { "result": { "rawtransaction": "01000000017004c1186a4a6a11708e1739839488180dbb6dbf4a9bf52228faa5b3173cdb05000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188acffffffff020000000000000000236a210d1e454cefefcbe161ff1a94d78892739ddc14a84b570af630af96858de42ab6cf6e150406000000001976a914818895f3dc2c178629d3d2d8fa3ec4a3f817982188ac00000000", @@ -2250,7 +2250,7 @@ Composes a transaction to Sends all assets and/or transfer ownerships to a desti "name": "sweep" } } - `` + ``` ## Group Assets @@ -2266,7 +2266,7 @@ Returns the valid assets + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2291,7 +2291,7 @@ Returns the valid assets } ] } - `` + ``` ### Get Asset Info [GET /assets/{asset}] @@ -2302,7 +2302,7 @@ Returns the asset information + Response 200 (application/json) - `` + ``` { "result": { "asset": "UNNEGOTIABLE", @@ -2316,7 +2316,7 @@ Returns the asset information "holder_count": 1 } } - `` + ``` ### Get Asset Balances [GET /assets/{asset}/balances{?exclude_zero_balances}] @@ -2329,7 +2329,7 @@ Returns the asset balances + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2339,7 +2339,7 @@ Returns the asset balances } ] } - `` + ``` ### Get Balance By Address And Asset [GET /assets/{asset}/balances/{address}] @@ -2351,7 +2351,7 @@ Returns the balance of an address and asset + Response 200 (application/json) - `` + ``` { "result": { "address": "1C3uGcoSGzKVgFqyZ3kM2DBq9CYttTMAVs", @@ -2359,7 +2359,7 @@ Returns the balance of an address and asset "quantity": 104200000000 } } - `` + ``` ### Get Orders By Asset [GET /assets/{asset}/orders{?status}] @@ -2372,7 +2372,7 @@ Returns the orders of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2586,7 +2586,7 @@ Returns the orders of an asset } ] } - `` + ``` ### Get Credits By Asset [GET /assets/{asset}/credits{?limit}{?offset}] @@ -2601,7 +2601,7 @@ Returns the credits of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2615,7 +2615,7 @@ Returns the credits of an asset } ] } - `` + ``` ### Get Debits By Asset [GET /assets/{asset}/debits{?limit}{?offset}] @@ -2630,7 +2630,7 @@ Returns the debits of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2680,7 +2680,7 @@ Returns the debits of an asset } ] } - `` + ``` ### Get Dividends [GET /assets/{asset}/dividends] @@ -2691,7 +2691,7 @@ Returns the dividends of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2828,7 +2828,7 @@ Returns the dividends of an asset } ] } - `` + ``` ### Get Issuances By Asset [GET /assets/{asset}/issuances] @@ -2839,7 +2839,7 @@ Returns the issuances of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2865,7 +2865,7 @@ Returns the issuances of an asset } ] } - `` + ``` ### Get Sends By Asset [GET /assets/{asset}/sends{?limit}{?offset}] @@ -2880,7 +2880,7 @@ Returns the sends of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2945,7 +2945,7 @@ Returns the sends of an asset } ] } - `` + ``` ### Get Dispensers By Asset [GET /assets/{asset}/dispensers{?status}] @@ -2958,7 +2958,7 @@ Returns the dispensers of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -2979,7 +2979,7 @@ Returns the dispensers of an asset } ] } - `` + ``` ### Get Dispensers By Address And Asset [GET /assets/{asset}/dispensers/{address}{?status}] @@ -2993,7 +2993,7 @@ Returns the dispensers of an address and an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3014,7 +3014,7 @@ Returns the dispensers of an address and an asset } ] } - `` + ``` ### Get Asset Holders [GET /assets/{asset}/holders] @@ -3025,7 +3025,7 @@ Returns the holders of an asset + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3070,7 +3070,7 @@ Returns the holders of an asset } ] } - `` + ``` ## Group Orders @@ -3083,7 +3083,7 @@ Returns the information of an order + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3107,7 +3107,7 @@ Returns the information of an order } ] } - `` + ``` ### Get Order Matches By Order [GET /orders/{order_hash}/matches{?status}] @@ -3120,7 +3120,7 @@ Returns the order matches of an order + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3146,7 +3146,7 @@ Returns the order matches of an order } ] } - `` + ``` ### Get BTCPays By Order [GET /orders/{order_hash}/btcpays] @@ -3157,7 +3157,7 @@ Returns the BTC pays of an order + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3172,7 +3172,7 @@ Returns the BTC pays of an order } ] } - `` + ``` ## Group Bets @@ -3185,7 +3185,7 @@ Returns the information of a bet + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3209,7 +3209,7 @@ Returns the information of a bet } ] } - `` + ``` ### Get Bet Matches By Bet [GET /bets/{bet_hash}/matches{?status}] @@ -3222,7 +3222,7 @@ Returns the bet matches of a bet + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3253,7 +3253,7 @@ Returns the bet matches of a bet } ] } - `` + ``` ### Get Resolutions By Bet [GET /bets/{bet_hash}/resolutions] @@ -3264,7 +3264,7 @@ Returns the resolutions of a bet + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3280,7 +3280,7 @@ Returns the resolutions of a bet } ] } - `` + ``` ## Group Burns @@ -3298,7 +3298,7 @@ Returns the burns + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3348,7 +3348,7 @@ Returns the burns } ] } - `` + ``` ## Group Dispensers @@ -3361,7 +3361,7 @@ Returns the dispenser information by tx_hash + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3383,7 +3383,7 @@ Returns the dispenser information by tx_hash } ] } - `` + ``` ### Get Dispenses By Dispenser [GET /dispensers/{dispenser_hash}/dispenses] @@ -3394,7 +3394,7 @@ Returns the dispenses of a dispenser + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3421,7 +3421,7 @@ Returns the dispenses of a dispenser } ] } - `` + ``` ## Group Events @@ -3437,7 +3437,7 @@ Returns all events + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3511,7 +3511,7 @@ Returns all events } ] } - `` + ``` ### Get Event By Index [GET /events/{event_index}] @@ -3522,7 +3522,7 @@ Returns the event of an index + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3538,14 +3538,14 @@ Returns the event of an index } ] } - `` + ``` ### Get All Events Counts [GET /events/counts] Returns the event counts of all blocks + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3738,7 +3738,7 @@ Returns the event counts of all blocks } ] } - `` + ``` ### Get Events By Name [GET /events/{event}{?last}{?limit}] @@ -3753,7 +3753,7 @@ Returns the events filtered by event name + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3833,7 +3833,7 @@ Returns the events filtered by event name } ] } - `` + ``` ## Group Z-pages @@ -3847,13 +3847,13 @@ Health check route. + Response 200 (application/json) - `` + ``` { "result": { "status": "Healthy" } } - `` + ``` ## Group Bitcoin @@ -3870,7 +3870,7 @@ Returns all transactions involving a given address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -3911,7 +3911,7 @@ Returns all transactions involving a given address } ] } - `` + ``` ### Get Oldest Transaction By Address [GET /bitcoin/addresses/{address}/transactions/oldest{?block_index}] @@ -3924,14 +3924,14 @@ Get the oldest transaction for an address. + Response 200 (application/json) - `` + ``` { "result": { "block_index": 833187, "tx_hash": "2c8bc3eede9ec60d26c6fd7f44829adc64da593552044a28c673022220f560c3" } } - `` + ``` ### Get Unspent Txouts [GET /bitcoin/addresses/{address}/utxos{?unconfirmed}{?unspent_tx_hash}] @@ -3946,7 +3946,7 @@ Returns a list of unspent outputs for a specific address + Response 200 (application/json) - `` + ``` { "result": [ { @@ -4031,7 +4031,7 @@ Returns a list of unspent outputs for a specific address } ] } - `` + ``` ### PubKeyHash To Pubkey [GET /bitcoin/addresses/{address}/pubkey{?provided_pubkeys}] @@ -4044,11 +4044,11 @@ Get pubkey for an address. + Response 200 (application/json) - `` + ``` { "result": "0388ef0905568d425f1ffd4031d93dda4ef0e220c9b5fc4a6cbaf11544c4a5ca49" } - `` + ``` ### Get Transaction [GET /bitcoin/transactions/{tx_hash}{?verbose}] @@ -4061,7 +4061,7 @@ Get a transaction from the blockchain + Response 200 (application/json) - `` + ``` { "result": { "txid": "3190047bf2320bdcd0fade655ae49be309519d151330aa478573815229cc0018", @@ -4106,7 +4106,7 @@ Get a transaction from the blockchain "blocktime": 1713951767 } } - `` + ``` ### Fee Per Kb [GET /bitcoin/estimatesmartfee{?conf_target}{?mode}] @@ -4120,11 +4120,11 @@ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` bloc + Response 200 (application/json) - `` + ``` { "result": 295443 } - `` + ``` ## Group Mempool @@ -4133,11 +4133,11 @@ Get the fee per kilobyte for a transaction to be confirmed in `conf_target` bloc Returns all mempool events + Response 200 (application/json) - `` + ``` { "result": [] } - `` + ``` ### Get Mempool Events By Name [GET /mempool/events/{event}] @@ -4148,8 +4148,8 @@ Returns the mempool events filtered by event name + Response 200 (application/json) - `` + ``` { "result": [] } - `` + ``` diff --git a/counterparty-core/tools/genapidoc.py b/counterparty-core/tools/genapidoc.py index b3a38717ed..e467ccf869 100644 --- a/counterparty-core/tools/genapidoc.py +++ b/counterparty-core/tools/genapidoc.py @@ -122,12 +122,12 @@ def gen_groups_toc(): - All API responses follow the following format: - `` + ``` { "error": , "result": } - `` + ``` - Routes in the `/bitcoin` group serve as a proxy to make requests to Bitcoin Core. @@ -139,7 +139,7 @@ def gen_groups_toc(): + Response 200 (application/json) - `` + ``` { "server_ready": true, "network": "mainnet", @@ -150,7 +150,7 @@ def gen_groups_toc(): ] } - `` + ``` """ md = md.replace("{root_path}", root_path) @@ -216,10 +216,10 @@ def gen_groups_toc(): example_output = cache[path] example_output_json = json.dumps(example_output, indent=4) md += "\n+ Response 200 (application/json)\n\n" - md += " ``\n" + md += " ```\n" for line in example_output_json.split("\n"): md += f" {line}\n" - md += " ``\n" + md += " ```\n" with open(CACHE_FILE, "w") as f: json.dump(cache, f, indent=4) From 57facf72adf9d6f0956da2cb19c456c05b4367a1 Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 14:27:56 +0200 Subject: [PATCH 126/128] 404 error if function return None --- counterparty-core/counterpartycore/lib/api/api_server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 3f5844018b..16218bce3e 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -178,6 +178,8 @@ def handle_route(**kwargs): return return_result(503, error="Unknwon error") # clean up and return the result + if result is None: + return return_result(404, error="Not found") result = remove_rowids(result) return return_result(200, result=result) From 9d61360bad2af407fa9e504948bef9c18a2f31bc Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 17:00:41 +0200 Subject: [PATCH 127/128] Start API server before connecting to backend --- .../counterpartycore/lib/api/api_server.py | 20 ++++++++++++++----- counterparty-core/counterpartycore/server.py | 18 +++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index 16218bce3e..e4f1ffd91f 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -32,7 +32,7 @@ logger = logging.getLogger(config.LOGGER_NAME) auth = HTTPBasicAuth() -BACKEND_HEIGHT = 0 +BACKEND_HEIGHT = None REFRESH_BACKEND_HEIGHT_INTERVAL = 10 BACKEND_HEIGHT_TIMER = None @@ -78,6 +78,8 @@ def api_root(): def is_server_ready(): + if BACKEND_HEIGHT is None: + return False return ledger.CURRENT_BLOCK_INDEX >= BACKEND_HEIGHT - 1 @@ -143,6 +145,8 @@ def prepare_args(route, **kwargs): @auth.login_required def handle_route(**kwargs): + if BACKEND_HEIGHT is None: + return return_result(503, error="Backend still not ready. Please retry later.") db = get_db() # update the current block index ledger.CURRENT_BLOCK_INDEX = blocks.last_db_index(db) @@ -202,19 +206,25 @@ def run_api_server(args): # run the scheduler to refresh the backend height # `no_refresh_backend_height` used only for testing. TODO: find a way to mock it if "no_refresh_backend_height" not in args or not args["no_refresh_backend_height"]: - refresh_backend_height() + refresh_backend_height(start=True) try: # Start the API server - app.run(host=config.API_HOST, port=config.API_PORT, debug=False) + app.run(host=config.API_HOST, port=config.API_PORT, debug=False, threaded=True) finally: # ensure timer is cancelled if BACKEND_HEIGHT_TIMER: BACKEND_HEIGHT_TIMER.cancel() -def refresh_backend_height(): +def refresh_backend_height(start=False): global BACKEND_HEIGHT, BACKEND_HEIGHT_TIMER # noqa F811 - BACKEND_HEIGHT = get_backend_height() + if not start: + BACKEND_HEIGHT = get_backend_height() + else: + # starting the timer is not blocking in case of Addrindexrs is not ready + BACKEND_HEIGHT_TIMER = Timer(0.5, refresh_backend_height) + BACKEND_HEIGHT_TIMER.start() + return if BACKEND_HEIGHT_TIMER: BACKEND_HEIGHT_TIMER.cancel() BACKEND_HEIGHT_TIMER = Timer(REFRESH_BACKEND_HEIGHT_INTERVAL, refresh_backend_height) diff --git a/counterparty-core/counterpartycore/server.py b/counterparty-core/counterpartycore/server.py index 89270296f0..9cfcea909a 100755 --- a/counterparty-core/counterpartycore/server.py +++ b/counterparty-core/counterpartycore/server.py @@ -680,9 +680,7 @@ def start_all(args): api_status_poller = None api_server_v1 = None api_server_v2 = None - - # Backend. - connect_to_backend() + telemetry_daemon = None try: if not os.path.exists(config.DATABASE) and args.catch_up == "bootstrap": @@ -691,6 +689,13 @@ def start_all(args): db = initialise_db() blocks.initialise(db) + # API Server v2. + api_server_v2 = api_v2.APIServer() + api_server_v2.start(args) + + # Backend. + connect_to_backend() + telemetry_daemon = TelemetryDaemon( interval=60, collector=TelemetryCollectorLive(db=database.get_connection(read_only=True)), @@ -714,16 +719,13 @@ def start_all(args): api_server_v1.daemon = True api_server_v1.start() - # API Server v2. - api_server_v2 = api_v2.APIServer() - api_server_v2.start(args) - # Server blocks.follow(db) except KeyboardInterrupt: pass finally: - telemetry_daemon.stop() + if telemetry_daemon: + telemetry_daemon.stop() if args.enable_api_v1: if api_status_poller: api_status_poller.stop() From 44f02cabbf7fb2a3f797c8cb21c9d9111813accc Mon Sep 17 00:00:00 2001 From: Ouziel Slama Date: Fri, 26 Apr 2024 17:09:39 +0200 Subject: [PATCH 128/128] fix test --- counterparty-core/counterpartycore/lib/api/api_server.py | 3 +++ counterparty-core/counterpartycore/test/api_v2_test.py | 1 + 2 files changed, 4 insertions(+) diff --git a/counterparty-core/counterpartycore/lib/api/api_server.py b/counterparty-core/counterpartycore/lib/api/api_server.py index e4f1ffd91f..afcfdf29f2 100644 --- a/counterparty-core/counterpartycore/lib/api/api_server.py +++ b/counterparty-core/counterpartycore/lib/api/api_server.py @@ -207,6 +207,9 @@ def run_api_server(args): # `no_refresh_backend_height` used only for testing. TODO: find a way to mock it if "no_refresh_backend_height" not in args or not args["no_refresh_backend_height"]: refresh_backend_height(start=True) + else: + global BACKEND_HEIGHT # noqa F811 + BACKEND_HEIGHT = 0 try: # Start the API server app.run(host=config.API_HOST, port=config.API_PORT, debug=False, threaded=True) diff --git a/counterparty-core/counterpartycore/test/api_v2_test.py b/counterparty-core/counterpartycore/test/api_v2_test.py index 6ca80c2551..28299e31fd 100644 --- a/counterparty-core/counterpartycore/test/api_v2_test.py +++ b/counterparty-core/counterpartycore/test/api_v2_test.py @@ -56,6 +56,7 @@ def test_api_v2(request): print(url) result = requests.get(url) # noqa: S113 results[url] = result.json() + print(result.json()) assert result.status_code == 200 if not request.config.getoption("saveapifixtures"): assert results[url] == fixtures[url]