Migrated to postgres and added pub32 tests

This commit is contained in:
kexkey
2021-11-05 22:46:36 -04:00
parent da35fc50e3
commit 8a62c146a9
44 changed files with 2299 additions and 530 deletions

View File

@@ -17,6 +17,9 @@
"gatekeeper_edit_apiproperties": "If you know what you are doing, it is possible to manually edit the API endpoints/groups authorization. (Not recommended)",
"gatekeeper_apiproperties": "You are about to edit the api.properties file. The format of the file is pretty simple: for each action, you will find what access group can access it. <font color='# 0000ff'>Admin</font> group can do what <font color='# 0000ff'>Spender</font> group can, and <font color='# 0000ff'>Spender</font> group can do what <font color='# 0000ff'>Watcher</font> group can. <font color='# 0000ff'>Internal</font> group is for the endpoints accessible only within the Docker network, like the backoffice tasks used by the Cron container. The access groups for each API id/key are found in the <font color='# 0000ff'>keys.properties</font> file.",
"gatekeeper_cns": "I use <font underline='true'>domain names</font> and/or <font underline='true'>IP addresses</font> to create valid TLS certificates. For example, if <font color='# 0000ff'>https://cyphernodehost/getbestblockhash</font> and <font color='# 0000ff'>https://192.168.7.44/getbestblockhash</font> will be used, enter <font color='# 0000ff'>cyphernodehost, 192.168.7.44</font> as a possible domains. <font color='# 0000ff'>127.0.0.1, localhost, gatekeeper</font> will be automatically added to your list. Make sure the provided domain names are in your DNS or client's hosts file and is reachable.",
"postgres_datapath": "The Cyphernode's Postgres files will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"postgres_datapath_custom": "Provide the <font underline='true'>full path name</font> where Postgres files will be saved.",
"postgres_password": "PostgreSQL cyphernode's <font underline='true'>password</font> used by Cyphernode when calling the database.",
"logs_datapath": "The Cyphernode's log files will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"logs_datapath_custom": "Provide the <font underline='true'>full path name</font> where Cyphernodes log files will be saved.",
"traefik_datapath": "The Traefik's files will be stored in a container's mounted directory. Please provide the <font underline='true'>local mounted path</font> to that directory. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",

View File

@@ -87,6 +87,7 @@ module.exports = class App {
proxy_version: process.env.PROXY_VERSION,
proxycron_version: process.env.PROXYCRON_VERSION,
pycoin_version: process.env.PYCOIN_VERSION,
postgres_version: process.env.POSTGRES_VERSION,
traefik_version: process.env.TRAEFIK_VERSION,
mosquitto_version: process.env.MOSQUITTO_VERSION,
otsclient_version: process.env.OTSCLIENT_VERSION,
@@ -148,6 +149,7 @@ module.exports = class App {
'cyphernode/proxy': this.sessionData.proxy_version,
'cyphernode/proxycron': this.sessionData.proxycron_version,
'cyphernode/pycoin': this.sessionData.pycoin_version,
'cyphernode/postgres': this.sessionData.postgres_version,
'cyphernode/otsclient': this.sessionData.otsclient_version,
'traefik': this.sessionData.traefik_version,
'cyphernode/clightning': this.sessionData.lightning_version,
@@ -359,6 +361,7 @@ module.exports = class App {
const pathProps = [
'gatekeeper_datapath',
'postgres_datapath',
'logs_datapath',
'traefik_datapath',
'tor_datapath',
@@ -483,6 +486,13 @@ module.exports = class App {
networks: ['cyphernodenet'],
docker: 'cyphernode/pycoin:'+this.config.docker_versions['cyphernode/pycoin']
},
{
name: 'Postgres',
label: 'postgres',
host: 'postgres',
networks: ['cyphernodenet'],
docker: 'postgres:'+this.config.docker_versions['cyphernode/postgres']
},
{
name: 'Notifier',
label: 'notifier',

View File

@@ -12,10 +12,11 @@ const schemas = {
'0.2.2': require('../schema/config-v0.2.2.json'),
'0.2.3': require('../schema/config-v0.2.3.json'),
'0.2.4': require('../schema/config-v0.2.4.json'),
'0.2.5': require('../schema/config-v0.2.5.json')
'0.2.5': require('../schema/config-v0.2.5.json'),
'0.2.6': require('../schema/config-v0.2.6.json')
};
const versionHistory = [ '0.1.0', '0.2.0', '0.2.2', '0.2.3', '0.2.4', '0.2.5' ];
const versionHistory = [ '0.1.0', '0.2.0', '0.2.2', '0.2.3', '0.2.4', '0.2.5', '0.2.6' ];
const defaultSchemaVersion=versionHistory[0];
const latestSchemaVersion=versionHistory[versionHistory.length-1];
@@ -46,7 +47,8 @@ module.exports = class Config {
'0.2.0->0.2.2': this.migrate_0_2_0_to_0_2_2,
'0.2.2->0.2.3': this.migrate_0_2_2_to_0_2_3,
'0.2.3->0.2.4': this.migrate_0_2_3_to_0_2_4,
'0.2.4->0.2.5': this.migrate_0_2_4_to_0_2_5
'0.2.4->0.2.5': this.migrate_0_2_4_to_0_2_5,
'0.2.5->0.2.6': this.migrate_0_2_5_to_0_2_6
};
this.setData( { schema_version: latestSchemaVersion } );
@@ -247,4 +249,12 @@ module.exports = class Config {
this.data.schema_version = '0.2.5';
}
async migrate_0_2_5_to_0_2_6() {
const currentVersion = this.data.schema_version;
if( currentVersion != '0.2.5' ) {
return;
}
this.data.schema_version = '0.2.6';
}
};

View File

@@ -0,0 +1,30 @@
const chalk = require('chalk');
const name = 'postgres';
const capitalise = function( txt ) {
return txt.charAt(0).toUpperCase() + txt.substr(1);
};
const prefix = function() {
return chalk.green(capitalise(name)+': ');
};
module.exports = {
name: function() {
return name;
},
prompts: function( utils ) {
return [
{
type: 'password',
name: 'postgres_password',
default: utils.getDefault( 'postgres_password' ),
message: prefix()+'Password of Postgres cyphernode user?'+utils.getHelp('postgres_password'),
filter: utils.trimFilter,
}];
},
templates: function( props ) {
return ['pgpass'];
}
};

View File

@@ -30,6 +30,44 @@ module.exports = {
value: "docker"
}]
},
{
when: installerDocker,
type: 'list',
name: 'postgres_datapath',
default: utils.getDefault( 'postgres_datapath' ),
choices: [
{
name: utils.setupDir()+"/cyphernode/postgres",
value: utils.setupDir()+"/cyphernode/postgres"
},
{
name: utils.defaultDataDirBase()+"/cyphernode/postgres",
value: utils.defaultDataDirBase()+"/cyphernode/postgres"
},
{
name: utils.defaultDataDirBase()+"/.cyphernode/postgres",
value: utils.defaultDataDirBase()+"/.cyphernode/postgres"
},
{
name: utils.defaultDataDirBase()+"/postgres",
value: utils.defaultDataDirBase()+"/postgres"
},
{
name: "Custom path",
value: "_custom"
}
],
message: prefix()+'Where do you want to store your Postgres files?'+utils.getHelp('postgres_datapath'),
},
{
when: (props)=>{ return installerDocker(props) && (props.postgres_datapath === '_custom') },
type: 'input',
name: 'postgres_datapath_custom',
default: utils.getDefault( 'postgres_datapath_custom' ),
filter: utils.trimFilter,
validate: utils.pathValidator,
message: prefix()+'Custom path for Postgres files?'+utils.getHelp('postgres_datapath_custom'),
},
{
when: installerDocker,
type: 'list',

View File

@@ -0,0 +1,726 @@
{
"definitions": {},
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://cyphernode.io/config-v0.2.6.json",
"type": "object",
"title": "Cyphernode config file structure v0.2.6",
"additionalProperties": false,
"required": [
"schema_version",
"setup_version",
"features",
"net",
"use_xpub",
"installer_mode",
"run_as_different_user",
"docker_mode",
"docker_versions",
"adminhash",
"bitcoin_rpcuser",
"bitcoin_rpcpassword",
"bitcoin_prune",
"bitcoin_datapath",
"bitcoin_mode",
"bitcoin_expose",
"gatekeeper_expose",
"gatekeeper_keys",
"gatekeeper_sslcert",
"gatekeeper_sslkey",
"gatekeeper_cns",
"gatekeeper_clientkeyspassword",
"gatekeeper_datapath",
"gatekeeper_port",
"proxy_datapath",
"postgres_password",
"postgres_datapath",
"logs_datapath",
"traefik_datapath",
"traefik_http_port",
"traefik_https_port"
],
"allOf": [
{
"if": {
"properties": {
"run_as_different_user": {
"enum": [
true
]
}
}
},
"then": {
"required": [
"username"
]
}
},
{
"if": {
"properties": {
"use_xpub": {
"enum": [
true
]
}
}
},
"then": {
"required": [
"xpub",
"derivation_path"
]
}
},
{
"if": {
"properties": {
"bitcoin_prune": {
"enum": [
true
]
}
}
},
"then": {
"required": [
"bitcoin_prune_size"
]
}
},
{
"if": {
"properties": {
"features": {
"contains": {
"enum": [
"tor"
]
}
}
}
},
"then": {
"required": [
"tor_datapath",
"torifyables",
"clearnet"
]
}
},
{
"if": {
"properties": {
"features": {
"contains": {
"enum": [
"lightning"
]
}
}
}
},
"then": {
"required": [
"lightning_announce",
"lightning_expose",
"lightning_implementation",
"lightning_datapath",
"lightning_nodename",
"lightning_nodecolor"
]
}
},
{
"if": {
"properties": {
"features": {
"contains": {
"enum": [
"otsclient"
]
}
}
}
},
"then": {
"required": [
"otsclient_datapath"
]
}
}
],
"properties": {
"schema_version": {
"type": "string",
"enum": [
"0.2.5"
],
"default": "0.3.0",
"examples": [
"0.2.5"
]
},
"setup_version": {
"type": "string",
"examples": [
"v0.2.0"
]
},
"docker_versions": {
"$id": "#/properties/dockerVersions",
"type": "object",
"title": "All versions of the docker containers",
"default": {},
"additionalProperties": {
"type": "string"
}
},
"features": {
"$id": "#/properties/features",
"type": "array",
"title": "The optional features of this cyphernode",
"default": ["specter"],
"items": {
"$id": "#/properties/features/items",
"type": "string",
"enum": [
"tor",
"lightning",
"otsclient",
"batcher",
"specter"
],
"title": "The feature",
"default": "",
"examples": [
"tor",
"lightning",
"otsclient",
"batcher",
"specter"
]
}
},
"torifyables": {
"$id": "#/properties/torifyables",
"type": "array",
"title": "The Torified features of this cyphernode",
"default": [],
"items": {
"$id": "#/properties/torifyables/items",
"type": "string",
"enum": [
"tor_traefik",
"tor_bitcoin",
"tor_lightning",
"tor_otsoperations",
"tor_otswebhooks",
"tor_addrwatcheswebhooks",
"tor_txidwatcheswebhooks"
],
"title": "The Torified feature",
"default": "",
"examples": [
"tor_traefik",
"tor_bitcoin",
"tor_lightning",
"tor_otsoperations",
"tor_otswebhooks",
"tor_addrwatcheswebhooks",
"tor_txidwatcheswebhooks"
]
}
},
"clearnet": {
"$id": "#/properties/clearnet",
"type": "array",
"title": "The clearnet-allowed Torified features of this cyphernode",
"default": [],
"items": {
"$id": "#/properties/clearnet/items",
"type": "string",
"enum": [
"clearnet_bitcoin",
"clearnet_lightning"
],
"title": "The clearnet-allowed Torified feature",
"default": "",
"examples": [
"clearnet_bitcoin",
"clearnet_lightning"
]
}
},
"net": {
"$id": "#/properties/net",
"type": "string",
"enum": [
"testnet",
"mainnet",
"regtest"
],
"title": "The net cyphernode is running on",
"default": "testnet",
"examples": [
"testnet"
]
},
"use_xpub": {
"$id": "#/properties/use_xpub",
"type": "boolean",
"title": "Use xpub key?",
"default": false,
"examples": [
false
]
},
"xpub": {
"$id": "#/properties/xpub",
"type": "string",
"title": "Default xpub to derive addresses from",
"pattern": "^(\\w+)$"
},
"derivation_path": {
"$id": "#/properties/derivation_path",
"type": "string",
"title": "Default derivation path",
"default": "0/n",
"examples": [
"0/n"
]
},
"installer_mode": {
"$id": "#/properties/installer_mode",
"type": "string",
"enum": [
"docker"
],
"title": "Install mode",
"default": "docker",
"examples": [
"docker"
]
},
"run_as_different_user": {
"$id": "#/properties/run_as_different_user",
"type": "boolean",
"title": "Run as different user",
"default": true,
"examples": [
true
]
},
"username": {
"$id": "#/properties/username",
"type": "string",
"title": "Username to run under",
"default": "cyphernode",
"examples": [
"cyphernode"
]
},
"docker_mode": {
"$id": "#/properties/docker_mode",
"type": "string",
"enum": [
"swarm",
"compose"
],
"title": "How to run the containers",
"default": "swarm",
"examples": [
"compose"
]
},
"bitcoin_rpcuser": {
"$id": "#/properties/bitcoin_rpcuser",
"type": "string",
"title": "Bitcoin rpc user",
"default": "bitcoin",
"examples": [
"bitcoin"
]
},
"bitcoin_rpcpassword": {
"$id": "#/properties/bitcoin_rpcpassword",
"type": "string",
"title": "Bitcoin rpc password",
"default": "CHANGEME",
"examples": [
"CHANGEME"
]
},
"bitcoin_uacomment": {
"$id": "#/properties/bitcoin_uacomment",
"type": "string",
"title": "Bitcoin user agent comment",
"examples": [
"cyphernode"
]
},
"bitcoin_prune": {
"$id": "#/properties/bitcoin_prune",
"type": "boolean",
"title": "Bitcoin prune",
"default": false,
"examples": [
"false"
]
},
"bitcoin_prune_size": {
"$id": "#/properties/bitcoin_prune_size",
"type": "integer",
"title": "Bitcoin prune size",
"default": 550,
"examples": [
550
]
},
"bitcoin_datapath": {
"$id": "#/properties/bitcoin_datapath",
"type": "string",
"title": "Bitcoin datapath",
"examples": [
"/tmp/cyphernode/bitcoin"
]
},
"bitcoin_datapath_custom": {
"$id": "#/properties/bitcoin_datapath_custom",
"type": "string",
"title": "Bitcoin custom datapath",
"examples": [
"/tmp/cyphernode/bitcoin"
]
},
"lightning_datapath": {
"$id": "#/properties/lightning_datapath",
"type": "string",
"title": "Lightning datapath",
"examples": [
"/tmp/cyphernode/lightning"
]
},
"lightning_datapath_custom": {
"$id": "#/properties/lightning_datapath_custom",
"type": "string",
"title": "Lightning custom datapath",
"examples": [
"/tmp/cyphernode/lightning"
]
},
"proxy_datapath": {
"$id": "#/properties/proxy_datapath",
"type": "string",
"title": "Proxy datapath",
"examples": [
"/tmp/cyphernode/proxy"
]
},
"proxy_datapath_custom": {
"$id": "#/properties/proxy_datapath_custom",
"type": "string",
"title": "Proxy custom datapath",
"examples": [
"/tmp/cyphernode/proxy"
]
},
"otsclient_datapath": {
"$id": "#/properties/otsclient_datapath",
"type": "string",
"title": "OTS Client datapath",
"examples": [
"/tmp/cyphernode/otsclient"
]
},
"otsclient_datapath_custom": {
"$id": "#/properties/otsclient_datapath_custom",
"type": "string",
"title": "OTS Client custom datapath",
"examples": [
"/tmp/cyphernode/otsclient"
]
},
"traefik_http_port": {
"$id": "#/properties/traefik_port",
"type": "integer",
"title": "Traefik HTTP port",
"default": 80,
"examples": [
80
]
},
"traefik_https_port": {
"$id": "#/properties/traefik_https_port",
"type": "integer",
"title": "Traefik HTTPS port",
"default": 443,
"examples": [
443
]
},
"traefik_datapath": {
"$id": "#/properties/traefik_datapath",
"type": "string",
"title": "Traefik datapath",
"examples": [
"/tmp/cyphernode/traefik"
]
},
"traefik_datapath_custom": {
"$id": "#/properties/traefik_datapath_custom",
"type": "string",
"title": "Traefik custom datapath",
"examples": [
"/tmp/cyphernode/traefik"
]
},
"postgres_password": {
"$id": "#/properties/postgres_password",
"type": "string",
"title": "Postgres cyphernode's password",
"default": "CHANGEME",
"examples": [
"CHANGEME"
]
},
"postgres_datapath": {
"$id": "#/properties/postgres_datapath",
"type": "string",
"title": "Postgres datapath",
"examples": [
"/tmp/cyphernode/postgres"
]
},
"postgres_datapath_custom": {
"$id": "#/properties/postgres_datapath_custom",
"type": "string",
"title": "Postgres custom datapath",
"examples": [
"/tmp/cyphernode/postgres"
]
},
"logs_datapath": {
"$id": "#/properties/logs_datapath",
"type": "string",
"title": "Logs datapath",
"examples": [
"/tmp/cyphernode/logs"
]
},
"logs_datapath_custom": {
"$id": "#/properties/logs_datapath_custom",
"type": "string",
"title": "Logs custom datapath",
"examples": [
"/tmp/cyphernode/logs"
]
},
"tor_datapath": {
"$id": "#/properties/tor_datapath",
"type": "string",
"title": "Tor datapath",
"examples": [
"/tmp/cyphernode/tor"
]
},
"tor_datapath_custom": {
"$id": "#/properties/tor_datapath_custom",
"type": "string",
"title": "Tor custom datapath",
"examples": [
"/tmp/cyphernode/tor"
]
},
"lightning_announce": {
"$id": "#/properties/lightning_announce",
"type": "boolean",
"title": "Announce lightning ip",
"default": false,
"examples": [
false
]
},
"lightning_external_ip": {
"$id": "#/properties/lightning_external_ip",
"type": "string",
"format": "ipv4",
"title": "External lightning node ip",
"examples": [
"123.123.123.123"
]
},
"bitcoin_mode": {
"$id": "#/properties/bitcoin_mode",
"type": "string",
"enum": [
"internal"
],
"title": "Bitcoin mode",
"default": "internal",
"examples": [
"internal"
]
},
"bitcoin_expose": {
"$id": "#/properties/bitcoin_expose",
"type": "boolean",
"title": "Expose bitcoin node",
"default": false,
"examples": [
true
]
},
"lightning_expose": {
"$id": "#/properties/lightning_expose",
"type": "boolean",
"title": "Expose lightning node",
"default": true,
"examples": [
false
]
},
"gatekeeper_expose": {
"$id": "#/properties/gatekeeper_expose",
"type": "boolean",
"title": "Expose gatekeeper port",
"default": false,
"examples": [
true
]
},
"gatekeeper_datapath": {
"$id": "#/properties/gatekeeper_datapath",
"type": "string",
"title": "Gatekeeper datapath",
"examples": [
"/tmp/cyphernode/gatekeeper"
]
},
"gatekeeper_datapath_custom": {
"$id": "#/properties/gatekeeper_datapath_custom",
"type": "string",
"title": "Gatekeeper custom datapath",
"examples": [
"/tmp/cyphernode/gatekeeper"
]
},
"gatekeeper_port": {
"$id": "#/properties/gatekeeper_port",
"type": "integer",
"title": "Gatekeeper port",
"default": 2009,
"examples": [
2009
]
},
"gatekeeper_keys": {
"$id": "#/properties/gatekeeper_keys",
"type": "object",
"title": "Gatekeeper keys",
"default": {
"configEntries": [],
"clientInformation": []
},
"required": [
"configEntries",
"clientInformation"
],
"properties": {
"configEntries": {
"$id": "#/properties/gatekeeper_keys/configEntries",
"type": "array",
"items": {
"$id": "#/properties/gatekeeper_keys/configEntries/entry",
"type": "string",
"pattern": "^kapi_id=\".+\";kapi_key=\".+\";kapi_groups=\".+\";.+$"
},
"examples": [
[
"kapi_id=\"000\";kapi_key=\"a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
"kapi_id=\"001\";kapi_key=\"a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a\";kapi_groups=\"stats,watcher\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
"kapi_id=\"002\";kapi_key=\"fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d\";kapi_groups=\"stats,watcher,spender\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}",
"kapi_id=\"003\";kapi_key=\"f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417\";kapi_groups=\"stats,watcher,spender,admin\";eval ugroups_${kapi_id}=${kapi_groups};eval ukey_${kapi_id}=${kapi_key}"
]
]
},
"clientInformation": {
"$id": "#/properties/gatekeeper_keys/clientInformation",
"type": "array",
"items": {
"$id": "#/properties/gatekeeper_keys/clientInformation/entry",
"type": "string",
"pattern": "^.+=.+$"
},
"examples": [
[
"000=a27f9e73fdde6a5005879c259c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
"001=a27f9e73fdde6a5005879c273c9aea5e8d917eec77bbdfd73272c0af9b4c6b7a",
"002=fe58ddbb66d7302a7087af3242a98b6326c51a257f5eab1c06bb8cc02e25890d",
"003=f0b8bb52f4c7007938757bcdfc73b452d6ce08cc0c660ce57c5464ae95f35417"
]
]
}
}
},
"gatekeeper_sslcert": {
"$id": "#/properties/gatekeeper_sslcert",
"type": "string",
"title": "Gatekeeper SSL Cert"
},
"gatekeeper_sslkey": {
"$id": "#/properties/gatekeeper_sslkey",
"type": "string",
"title": "Gatekeeper SSL Key"
},
"gatekeeper_cns": {
"$id": "#/properties/gatekeeper_cns",
"type": "string",
"title": "Gatekeeper cns",
"examples": [
"myhost.mydomain.com,*.myotherdomain.com,123.123.123.123"
]
},
"gatekeeper_clientkeyspassword": {
"$id": "#/properties/gatekeeper_clientkeyspassword",
"type": "string",
"title": "Password for the encrypted client keys archive"
},
"adminhash": {
"$id": "#/properties/adminhash",
"type": "string",
"title": "Bcrypted hash of admin password"
},
"lightning_implementation": {
"$id": "#/properties/lightning_implementation",
"type": "string",
"enum": [
"c-lightning"
],
"title": "The lightning implementation",
"default": "c-lightning",
"examples": [
"c-lightning"
]
},
"lightning_nodename": {
"$id": "#/properties/lightning_nodename",
"type": "string",
"title": "The lightning node name",
"examples": [
"🚀 Mighty Moose 🚀"
]
},
"lightning_nodecolor": {
"$id": "#/properties/lightning_nodecolor",
"type": "string",
"pattern": "^[0-9A-Fa-f]{6}$",
"title": "The lightning node color",
"examples": [
"ff0000",
"00ff00",
"00ffff"
]
}
}
}

View File

@@ -8,6 +8,7 @@ LIGHTNING_IMPLEMENTATION=<%= lightning_implementation %>
PROXY_DATAPATH=<%= proxy_datapath %>
GATEKEEPER_DATAPATH=<%= gatekeeper_datapath %>
GATEKEEPER_PORT=<%= gatekeeper_port %>
POSTGRES_DATAPATH=<%= postgres_datapath %>
LOGS_DATAPATH=<%= logs_datapath %>
TRAEFIK_DATAPATH=<%= traefik_datapath %>
FEATURE_TOR=<%= (features.indexOf('tor') != -1)?'true':'false' %>

View File

@@ -2,6 +2,36 @@ version: "3"
services:
##########################
# POSTGRESQL #
##########################
postgres:
image: postgres:<%= postgres_version %>
environment:
- "POSTGRES_USER=cyphernode"
- "POSTGRES_PASSWORD=<%= postgres_password %>"
- "POSTGRES_DB=cyphernode"
- "PGDATA=/var/lib/postgresql/data/pgdata"
volumes:
- "<%= postgres_datapath %>:/var/lib/postgresql/data"
networks:
- cyphernodenet
<% if ( docker_mode === 'swarm' ) { %>
deploy:
replicas: 1
placement:
constraints:
- node.labels.io.cyphernode == true
restart_policy:
condition: "any"
delay: 1s
update_config:
parallelism: 1
<% } else { %>
restart: always
<% } %>
<% if ( features.indexOf('tor') !== -1 ) { %>
##########################
# TOR #
@@ -156,6 +186,7 @@ services:
- "OTSCLIENT_CONTAINER=otsclient:6666"
- "OTS_FILES=/proxy/otsfiles"
- "XPUB_DERIVATION_GAP=100"
- "PGPASSFILE=/proxy/db/pgpass"
<% if ( devmode ) { %>
ports:
- "8888:8888"
@@ -174,6 +205,8 @@ services:
<% } %>
networks:
- cyphernodenet
depends_on:
- postgres
<% if ( docker_mode === 'swarm' ) { %>
deploy:
replicas: 1

View File

@@ -80,6 +80,19 @@ checkpycoin() {
return 0
}
checkpostgres() {
echo -en "\r\n\e[1;36mTesting Postgres... " > /dev/console
local rc
# getbatcher needs the database to return correctly...
rc=$(curl -s -o /dev/null -w "%{http_code}" http://proxy:8888/getbatcher)
[ "${rc}" -ne "200" ] && return 105
echo -e "\e[1;36mPostgres rocks!" > /dev/console
return 0
}
checkbroker() {
echo -en "\r\n\e[1;36mTesting Broker... " > /dev/console
local rc
@@ -170,12 +183,12 @@ checkservice() {
while :
do
outcome=0
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
echo -e " \e[0;32mVerifying \e[0;33m${container}\e[0;32m..." > /dev/console
(ping -c 10 ${container} 2> /dev/null | grep "0% packet loss" > /dev/null) &
eval ${container}=$!
done
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
eval wait '$'${container} ; returncode=$? ; outcome=$((${outcome} + ${returncode}))
eval c_${container}=${returncode}
done
@@ -193,12 +206,13 @@ checkservice() {
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "postgres", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "tor", "active":true },
# { "name": "bitcoin", "active":true },
# { "name": "lightning", "active":true },
# ]
for container in gatekeeper proxy proxycron broker notifier pycoin <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
for container in gatekeeper proxy proxycron broker notifier pycoin postgres <%= (features.indexOf('otsclient') != -1)?'otsclient ':'' %><%= (features.indexOf('tor') != -1)?'tor ':'' %>bitcoin <%= (features.indexOf('lightning') != -1)?'lightning ':'' %>; do
[ -n "${result}" ] && result="${result},"
result="${result}{\"name\":\"${container}\",\"active\":"
eval "returncode=\$c_${container}"
@@ -254,6 +268,7 @@ feature_status() {
# { "name": "proxy", "active":true },
# { "name": "proxycron", "active":true },
# { "name": "pycoin", "active":true },
# { "name": "postgres", "active":true },
# { "name": "otsclient", "active":true },
# { "name": "tor", "active":true },
# { "name": "bitcoin", "active":true },
@@ -262,6 +277,7 @@ feature_status() {
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "postgres", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "tor", "working":true },
# { "name": "bitcoin", "working":true },
@@ -296,6 +312,7 @@ fi
# "features": [
# { "name": "gatekeeper", "working":true },
# { "name": "pycoin", "working":true },
# { "name": "postgres", "working":true },
# { "name": "otsclient", "working":true },
# { "name": "tor", "working":true },
# { "name": "bitcoin", "working":true },
@@ -362,6 +379,21 @@ fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Pycoin error!')}"
#############################
# POSTGRES #
#############################
result="${result},{\"coreFeature\":true, \"name\":\"postgres\",\"working\":"
status=$(echo "{${containers}}" | jq ".containers[] | select(.name == \"postgres\") | .active")
if [[ "${workingproxy}" = "true" && "${status}" = "true" ]]; then
timeout_feature checkpostgres
returncode=$?
else
returncode=1
fi
finalreturncode=$((${returncode} | ${finalreturncode}))
result="${result}$(feature_status ${returncode} 'Postgres error!')}"
<% if (features.indexOf('otsclient') != -1) { %>
#############################
# OTSCLIENT #

View File

@@ -0,0 +1 @@
postgres:5432:cyphernode:cyphernode:<%= postgres_password %>

23
dist/setup.sh vendored
View File

@@ -110,7 +110,7 @@ sudo_if_required() {
}
modify_permissions() {
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$POSTGRES_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
for d in "${directories[@]}"
do
if [[ -e $d ]]; then
@@ -122,7 +122,7 @@ modify_permissions() {
}
modify_owner() {
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$OTSCLIENT_DATAPATH" "$POSTGRES_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local user=$(id -u $RUN_AS_USER):$(id -g $RUN_AS_USER)
for d in "${directories[@]}"
do
@@ -193,6 +193,7 @@ configure() {
-e PROXYCRON_VERSION=$PROXYCRON_VERSION \
-e OTSCLIENT_VERSION=$OTSCLIENT_VERSION \
-e PYCOIN_VERSION=$PYCOIN_VERSION \
-e POSTGRES_VERSION=$POSTGRES_VERSION \
-e BITCOIN_VERSION=$BITCOIN_VERSION \
-e LIGHTNING_VERSION=$LIGHTNING_VERSION \
-e CONF_VERSION=$CONF_VERSION \
@@ -385,9 +386,16 @@ install_docker() {
copy_file $cyphernodeconf_filepath/traefik/htpasswd $GATEKEEPER_DATAPATH/htpasswd 1 $SUDO_REQUIRED
if [ ! -d $POSTGRES_DATAPATH ]; then
step " create $POSTGRES_DATAPATH"
sudo_if_required mkdir -p $POSTGRES_DATAPATH
next
fi
if [ ! -d $LOGS_DATAPATH ]; then
step " create $LOGS_DATAPATH"
sudo_if_required mkdir -p $LOGS_DATAPATH
step " create $POSTGRES_DATAPATH"
sudo_if_required mkdir -p $POSTGRES_DATAPATH
next
fi
@@ -461,6 +469,8 @@ install_docker() {
copy_file $cyphernodeconf_filepath/installer/config.sh $PROXY_DATAPATH/config.sh 1 $SUDO_REQUIRED
copy_file $cyphernodeconf_filepath/cyphernode/info.json $PROXY_DATAPATH/info.json 1 $SUDO_REQUIRED
copy_file $cyphernodeconf_filepath/postgres/pgpass $PROXY_DATAPATH/pgpass 1 $SUDO_REQUIRED
sudo_if_required chmod 0600 $PROXY_DATAPATH/pgpass
if [[ $BITCOIN_INTERNAL == true ]]; then
if [ ! -d $BITCOIN_DATAPATH ]; then
@@ -652,7 +662,7 @@ install_docker() {
check_directory_owner() {
# if one directory does not have access rights for $RUN_AS_USER, we echo 1, else we echo 0
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$POSTGRES_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local status=0
for d in "${directories[@]}"
do
@@ -756,7 +766,7 @@ sanity_checks_pre_install() {
if [[ $sudo_reason == 'directories' ]]; then
echo " or check your data volumes if they have the right owner."
echo " The owner of the following folders should be '$RUN_AS_USER':"
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local directories=("$BITCOIN_DATAPATH" "$LIGHTNING_DATAPATH" "$PROXY_DATAPATH" "$GATEKEEPER_DATAPATH" "$POSTGRES_DATAPATH" "$LOGS_DATAPATH" "$TRAEFIK_DATAPATH" "$TOR_DATAPATH")
local status=0
for d in "${directories[@]}"
do
@@ -859,6 +869,7 @@ BITCOIN_VERSION="v0.21.1"
LIGHTNING_VERSION="v0.10.1"
TRAEFIK_VERSION="v1.7.9-alpine"
MOSQUITTO_VERSION="1.6-openssl"
POSTGRES_VERSION="14.0-alpine"
SETUP_DIR=$(dirname $(realpath $0))

View File

@@ -224,8 +224,6 @@ paths:
- "pub32"
- "path"
- "nstart"
- "unconfirmedCallbackURL"
- "confirmedCallbackURL"
properties:
label:
description: "Label for that xpub. Can be used, instead for xpub, for future references in xpub-related endpoints."

View File

@@ -8,7 +8,8 @@ RUN apk add --update --no-cache \
curl \
su-exec \
py3-pip \
xxd
xxd \
postgresql
WORKDIR ${HOME}

View File

@@ -0,0 +1,164 @@
BEGIN;
CREATE TABLE watching_by_pub32 (
id SERIAL PRIMARY KEY,
pub32 VARCHAR UNIQUE,
label VARCHAR UNIQUE,
derivation_path VARCHAR,
callback0conf VARCHAR,
callback1conf VARCHAR,
last_imported_n INTEGER,
watching BOOLEAN DEFAULT FALSE,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE watching (
id SERIAL PRIMARY KEY,
address VARCHAR,
label VARCHAR,
watching BOOLEAN DEFAULT FALSE,
callback0conf VARCHAR,
calledback0conf BOOLEAN DEFAULT FALSE,
callback1conf VARCHAR,
calledback1conf BOOLEAN DEFAULT FALSE,
imported BOOLEAN DEFAULT FALSE,
watching_by_pub32_id INTEGER REFERENCES watching_by_pub32,
pub32_index INTEGER,
event_message VARCHAR,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_watching_address ON watching (address);
CREATE UNIQUE INDEX idx_watching_01 ON watching (address, COALESCE(callback0conf, ''), COALESCE(callback1conf, ''));
CREATE INDEX idx_watching_label ON watching (label);
CREATE INDEX idx_watching_watching ON watching (watching);
CREATE INDEX idx_watching_imported ON watching (imported);
CREATE INDEX idx_watching_watching_by_pub32_id ON watching (watching_by_pub32_id);
CREATE TABLE tx (
id SERIAL PRIMARY KEY,
txid VARCHAR UNIQUE,
hash VARCHAR UNIQUE,
confirmations INTEGER DEFAULT 0,
timereceived INTEGER,
fee REAL,
size INTEGER,
vsize INTEGER,
is_replaceable BOOLEAN,
blockhash VARCHAR,
blockheight INTEGER,
blocktime INTEGER,
conf_target INTEGER,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_tx_timereceived ON tx (timereceived);
CREATE INDEX idx_tx_fee ON tx (fee);
CREATE INDEX idx_tx_size ON tx (size);
CREATE INDEX idx_tx_vsize ON tx (vsize);
CREATE INDEX idx_tx_blockhash ON tx (blockhash);
CREATE INDEX idx_tx_blockheight ON tx (blockheight);
CREATE INDEX idx_tx_blocktime ON tx (blocktime);
CREATE INDEX idx_tx_confirmations ON tx (confirmations);
CREATE TABLE watching_tx (
watching_id INTEGER REFERENCES watching,
tx_id INTEGER REFERENCES tx,
vout INTEGER,
amount REAL
);
CREATE UNIQUE INDEX idx_watching_tx ON watching_tx (watching_id, tx_id);
CREATE INDEX idx_watching_tx_watching_id ON watching_tx (watching_id);
CREATE INDEX idx_watching_tx_tx_id ON watching_tx (tx_id);
CREATE TABLE batcher (
id SERIAL PRIMARY KEY,
label VARCHAR UNIQUE,
conf_target INTEGER,
feerate REAL,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO batcher (id, label, conf_target, feerate) VALUES (1, 'default', 6, NULL);
CREATE TABLE recipient (
id SERIAL PRIMARY KEY,
address VARCHAR,
amount REAL,
tx_id INTEGER REFERENCES tx,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
webhook_url VARCHAR,
calledback BOOLEAN DEFAULT FALSE,
calledback_ts TIMESTAMP,
batcher_id INTEGER REFERENCES batcher,
label VARCHAR
);
CREATE INDEX idx_recipient_address ON recipient (address);
CREATE INDEX idx_recipient_label ON recipient (label);
CREATE INDEX idx_recipient_calledback ON recipient (calledback);
CREATE INDEX idx_recipient_webhook_url ON recipient (webhook_url);
CREATE INDEX idx_recipient_tx_id ON recipient (tx_id);
CREATE INDEX idx_recipient_batcher_id ON recipient (batcher_id);
CREATE TABLE watching_by_txid (
id SERIAL PRIMARY KEY,
txid VARCHAR,
watching BOOLEAN DEFAULT FALSE,
callback1conf VARCHAR,
calledback1conf BOOLEAN DEFAULT FALSE,
callbackxconf VARCHAR,
calledbackxconf BOOLEAN DEFAULT FALSE,
nbxconf INTEGER,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_watching_by_txid_txid ON watching_by_txid (txid);
CREATE UNIQUE INDEX idx_watching_by_txid_1x ON watching_by_txid (txid, callback1conf, callbackxconf);
CREATE INDEX idx_watching_by_txid_watching ON watching_by_txid (watching);
CREATE INDEX idx_watching_by_txid_callback1conf ON watching_by_txid (callback1conf);
CREATE INDEX idx_watching_by_txid_calledback1conf ON watching_by_txid (calledback1conf);
CREATE INDEX idx_watching_by_txid_callbackxconf ON watching_by_txid (callbackxconf);
CREATE INDEX idx_watching_by_txid_calledbackxconf ON watching_by_txid (calledbackxconf);
CREATE TABLE stamp (
id SERIAL PRIMARY KEY,
hash VARCHAR UNIQUE,
callbackUrl VARCHAR,
requested BOOLEAN DEFAULT FALSE,
upgraded BOOLEAN DEFAULT FALSE,
calledback BOOLEAN DEFAULT FALSE,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_stamp_calledback ON stamp (calledback);
CREATE TABLE cyphernode_props (
id SERIAL PRIMARY KEY,
property VARCHAR,
value VARCHAR,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_cp_property ON cyphernode_props (property);
CREATE UNIQUE INDEX idx_cp_propval ON cyphernode_props (property, value);
INSERT INTO cyphernode_props (property, value) VALUES ('version', '0.1');
INSERT INTO cyphernode_props (property, value) VALUES ('pay_index', '0');
CREATE TABLE ln_invoice (
id SERIAL PRIMARY KEY,
label VARCHAR UNIQUE,
bolt11 VARCHAR UNIQUE,
payment_hash VARCHAR,
msatoshi INTEGER,
status VARCHAR,
pay_index INTEGER,
msatoshi_received INTEGER,
paid_at INTEGER,
description VARCHAR,
expires_at INTEGER,
callback_url VARCHAR,
calledback BOOLEAN DEFAULT FALSE,
callback_failed BOOLEAN DEFAULT FALSE,
inserted_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_lninvoice_label ON ln_invoice (label);
CREATE INDEX idx_lninvoice_bolt11 ON ln_invoice (bolt11);
CREATE INDEX idx_lninvoice_calledback ON ln_invoice (calledback);
CREATE INDEX idx_lninvoice_callback_failed ON ln_invoice (callback_failed);
COMMIT;

View File

@@ -1,27 +0,0 @@
PRAGMA foreign_keys = ON;
CREATE TABLE rawtx (
id INTEGER PRIMARY KEY AUTOINCREMENT,
txid TEXT UNIQUE,
hash TEXT UNIQUE,
confirmations INTEGER DEFAULT 0,
timereceived INTEGER,
fee REAL,
size INTEGER,
vsize INTEGER,
is_replaceable INTEGER,
blockhash TEXT,
blockheight INTEGER,
blocktime INTEGER,
conf_target INTEGER,
raw_tx TEXT,
inserted_ts INTEGER DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_rawtx_timereceived ON rawtx (timereceived);
CREATE INDEX idx_rawtx_fee ON rawtx (fee);
CREATE INDEX idx_rawtx_size ON rawtx (size);
CREATE INDEX idx_rawtx_vsize ON rawtx (vsize);
CREATE INDEX idx_rawtx_blockhash ON rawtx (blockhash);
CREATE INDEX idx_rawtx_blockheight ON rawtx (blockheight);
CREATE INDEX idx_rawtx_blocktime ON rawtx (blocktime);
CREATE INDEX idx_rawtx_confirmations ON rawtx (confirmations);

View File

@@ -1,17 +1,14 @@
#!/bin/sh
echo "Checking for rawtx database support in DB..."
if [ ! -e ${DB_FILE}_rawtx ]; then
# rawtx database not found
echo "Migrating database for rawtx database support..."
echo "Checking for new indexes in DB..."
sqlite3 $DB_FILE ".indexes" | grep "idx_watching_watching" > /dev/null
if [ "$?" -eq "1" ]; then
# idx_watching_watching index not found
echo "Migrating database with new indexes..."
echo "Backing up current DB..."
cp $DB_FILE $DB_FILE-sqlmigrate20210928_0.7.0-0.8.0
echo "Altering DB..."
cat sqlmigrate20210928_0.7.0-0.8.0.sql | sqlite3 $DB_FILE
echo "Creating new DB..."
cat rawtx.sql | sqlite3 ${DB_FILE}_rawtx
echo "Inserting table in new DB..."
sqlite3 -cmd ".timeout 25000" ${DB_FILE} "ATTACH DATABASE \"${DB_FILE}_rawtx\" AS other; INSERT INTO other.rawtx SELECT * FROM tx; DETACH other;"
else
echo "rawtx database support migration already done, skipping!"
echo "New indexes migration already done, skipping!"
fi

View File

@@ -0,0 +1,36 @@
#!/bin/sh
echo "Waiting for postgres to be ready..."
(while true ; do psql -h postgres -U cyphernode -c "select 1;" ; [ "$?" -eq "0" ] && break ; sleep 10; done) &
wait
echo "Checking if postgres is setup..."
psql -h postgres -U cyphernode -c "\d" | grep "cyphernode_props" > /dev/null
if [ "$?" -eq "1" ]; then
# if cyphernode_props table doesn't exist, it's probably because database hasn't been setup yet
echo "Creating postgres database..."
psql -h postgres -f cyphernode.postgresql -U cyphernode
echo "Extracting and converting sqlite3 data..."
cat sqlmigrate20211105_0.7.0-0.8.0_sqlite3-extract.sql | sqlite3 $DB_FILE
sed -ie 's/^\(INSERT.*\);$/\1 ON CONFLICT DO NOTHING;/g' sqlmigrate20211105_0.7.0-0.8.0_sqlite3-extracted-data.sql
echo "...appending postgresql sequences..."
echo "
select setval('cyphernode_props_id_seq', (SELECT MAX(id) FROM cyphernode_props));
select setval('ln_invoice_id_seq', (SELECT MAX(id) FROM ln_invoice));
select setval('recipient_id_seq', (SELECT MAX(id) FROM recipient));
select setval('stamp_id_seq', (SELECT MAX(id) FROM stamp));
select setval('tx_id_seq', (SELECT MAX(id) FROM tx));
select setval('watching_by_pub32_id_seq', (SELECT MAX(id) FROM watching_by_pub32));
select setval('watching_by_txid_id_seq', (SELECT MAX(id) FROM watching_by_txid));
select setval('watching_id_seq', (SELECT MAX(id) FROM watching));
select setval('batcher_id_seq', (SELECT MAX(id) FROM batcher));
commit;
" >> sqlmigrate20211105_0.7.0-0.8.0_sqlite3-extracted-data.sql
echo "Importing sqlite3 data into postgresql..."
psql -h postgres -f sqlmigrate20211105_0.7.0-0.8.0_sqlite3-extracted-data.sql -U cyphernode
else
echo "New indexes migration already done, skipping!"
fi

View File

@@ -0,0 +1,24 @@
.output sqlmigrate20211105_0.7.0-0.8.0_sqlite3-extracted-data.sql
select "BEGIN;";
.headers on
.mode insert watching_by_pub32
select id,pub32,label,derivation_path,callback0conf,callback1conf,last_imported_n,case when watching=1 then 'TRUE' else 'FALSE' end as watching,inserted_ts from watching_by_pub32;
.mode insert watching
select id,address,label,case when watching=1 then 'TRUE' else 'FALSE' end as watching,callback0conf,case when calledback0conf=1 then 'TRUE' else 'FALSE' end as calledback0conf,callback1conf,case when calledback1conf=1 then 'TRUE' else 'FALSE' end as calledback1conf,case when imported=1 then 'TRUE' else 'FALSE' end as imported,watching_by_pub32_id,pub32_index,event_message,inserted_ts from watching;
.mode insert tx
select id,txid,hash,confirmations,timereceived,fee,size,vsize,case when is_replaceable=1 then 'TRUE' else 'FALSE' end as is_replaceable,blockhash,blockheight,blocktime,conf_target,inserted_ts from tx;
.mode insert watching_tx
select * from watching_tx;
.mode insert batcher
select * from batcher;
.mode insert recipient
select id,address,amount,tx_id,inserted_ts,webhook_url,case when calledback=1 then 'TRUE' else 'FALSE' end as calledback,calledback_ts,batcher_id,label from recipient;
.mode insert watching_by_txid
select id,txid,case when watching=1 then 'TRUE' else 'FALSE' end as watching,callback1conf,case when calledback1conf=1 then 'TRUE' else 'FALSE' end as calledback1conf,callbackxconf,case when calledbackxconf=1 then 'TRUE' else 'FALSE' end as calledbackxconf,nbxconf,inserted_ts from watching_by_txid;
.mode insert stamp
select id,hash,callbackUrl,case when requested=1 then 'TRUE' else 'FALSE' end as requested,case when upgraded=1 then 'TRUE' else 'FALSE' end as upgraded,case when calledback=1 then 'TRUE' else 'FALSE' end as calledback,inserted_ts from stamp;
.mode insert cyphernode_props
select * from cyphernode_props;
.mode insert ln_invoice
select id,label,bolt11,payment_hash,msatoshi,status,pay_index,msatoshi_received,paid_at,description,expires_at,callback_url,case when calledback=1 then 'TRUE' else 'FALSE' end as calledback,case when callback_failed=1 then 'TRUE' else 'FALSE' end as callback_failed,inserted_ts from ln_invoice;
.quit

View File

@@ -22,7 +22,8 @@ createbatcher() {
local request=${1}
local response
local label=$(echo "${request}" | jq ".batcherLabel")
local returncode
local label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[createbatcher] label=${label}"
local conf_target=$(echo "${request}" | jq ".confTarget")
trace "[createbatcher] conf_target=${conf_target}"
@@ -37,13 +38,18 @@ createbatcher() {
local batcher_id
batcher_id=$(sql "INSERT OR IGNORE INTO batcher (label, conf_target, feerate) VALUES (${label}, ${conf_target}, ${feerate}); SELECT LAST_INSERT_ROWID();")
batcher_id=$(sql "INSERT INTO batcher (label, conf_target, feerate)"\
" VALUES ('${label}', ${conf_target}, ${feerate})"\
" RETURNING id" \
"SELECT id FROM batcher WHERE label='${label}'")
returncode=$?
trace_rc ${returncode}
if ("${batcher_id}" -eq "0"); then
if ("${returncode}" -ne "0"); then
trace "[createbatcher] Could not insert"
response='{"result":null,"error":{"code":-32700,"message":"Could not create batcher, label probably already exists","data":'${request}'}}'
else
trace "[createbatcher] Inserted"
trace "[createbatcher] Inserted, response=${batcher_id}"
response='{"result":{"batcherId":'${batcher_id}'},"error":null}'
fi
@@ -79,7 +85,7 @@ updatebatcher() {
local id=$(echo "${request}" | jq ".batcherId")
trace "[updatebatcher] id=${id}"
local label=$(echo "${request}" | jq ".batcherLabel")
local label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[updatebatcher] label=${label}"
local conf_target=$(echo "${request}" | jq ".confTarget")
trace "[updatebatcher] conf_target=${conf_target}"
@@ -99,12 +105,12 @@ updatebatcher() {
# fi
if [ "${id}" = "null" ]; then
whereclause="label=${label}"
whereclause="label='${label}'"
else
whereclause="id = ${id}"
fi
sql "UPDATE batcher set label=${label}, conf_target=${conf_target}, feerate=${feerate} WHERE ${whereclause}"
sql "UPDATE batcher set label='${label}', conf_target=${conf_target}, feerate=${feerate} WHERE ${whereclause}"
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -ne 0 ]; then
@@ -151,13 +157,13 @@ addtobatch() {
trace "[addtobatch] address=${address}"
local amount=$(echo "${request}" | jq ".amount")
trace "[addtobatch] amount=${amount}"
local label=$(echo "${request}" | jq ".outputLabel")
local label=$(echo "${request}" | jq -r ".outputLabel")
trace "[addtobatch] label=${label}"
local batcher_id=$(echo "${request}" | jq ".batcherId")
trace "[addtobatch] batcher_id=${batcher_id}"
local batcher_label=$(echo "${request}" | jq ".batcherLabel")
local batcher_label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[addtobatch] batcher_label=${batcher_label}"
local webhook_url=$(echo "${request}" | jq ".webhookUrl")
local webhook_url=$(echo "${request}" | jq -r ".webhookUrl")
trace "[addtobatch] webhook_url=${webhook_url}"
# Let's lowercase bech32 addresses
@@ -185,7 +191,7 @@ addtobatch() {
if [ "${batcher_id}" = "null" ]; then
# Using batcher_label
batcher_id=$(sql "SELECT id FROM batcher WHERE label=${batcher_label}")
batcher_id=$(sql "SELECT id FROM batcher WHERE label='${batcher_label}'")
returncode=$?
trace_rc ${returncode}
fi
@@ -195,7 +201,7 @@ addtobatch() {
response='{"result":null,"error":{"code":-32700,"message":"batcher not found","data":'${request}'}}'
else
# Check if address already pending for this batcher...
inserted_id=$(sql "SELECT id FROM recipient WHERE LOWER(address)=LOWER(\"${address}\") AND tx_id IS NULL AND batcher_id=${batcher_id}")
inserted_id=$(sql "SELECT id FROM recipient WHERE LOWER(address)=LOWER('${address}') AND tx_id IS NULL AND batcher_id=${batcher_id}")
returncode=$?
trace_rc ${returncode}
@@ -211,7 +217,9 @@ addtobatch() {
fi
# Insert the new destination
inserted_id=$(sql "INSERT INTO recipient (address, amount, webhook_url, batcher_id, label) VALUES (\"${address}\", ${amount}, ${webhook_url}, ${batcher_id}, ${label}); SELECT LAST_INSERT_ROWID();")
inserted_id=$(sql "INSERT INTO recipient (address, amount, webhook_url, batcher_id, label)"\
" VALUES ('${address}', ${amount}, '${webhook_url}', ${batcher_id}, '${label}')"\
" RETURNING id")
returncode=$?
trace_rc ${returncode}
@@ -280,7 +288,7 @@ removefrombatch() {
if [ "${returncode}" -ne 0 ]; then
response='{"result":null,"error":{"code":-32700,"message":"Output was not removed","data":'${request}'}}'
else
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), 0), COALESCE(SUM(amount), 0.00000000) FROM recipient WHERE tx_id IS NULL AND batcher_id=${batcher_id}")
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), DATE '0001-01-01'), COALESCE(SUM(amount), 0.00000000) FROM recipient WHERE tx_id IS NULL AND batcher_id=${batcher_id}")
returncode=$?
trace_rc ${returncode}
@@ -336,7 +344,7 @@ batchspend() {
local batcher_id=$(echo "${request}" | jq ".batcherId")
trace "[batchspend] batcher_id=${batcher_id}"
local batcher_label=$(echo "${request}" | jq ".batcherLabel")
local batcher_label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[batchspend] batcher_label=${batcher_label}"
local conf_target=$(echo "${request}" | jq ".confTarget")
trace "[batchspend] conf_target=${conf_target}"
@@ -351,7 +359,7 @@ batchspend() {
if [ "${batcher_id}" = "null" ]; then
# Using batcher_label
whereclause="label=${batcher_label}"
whereclause="label='${batcher_label}'"
else
whereclause="id=${batcher_id}"
fi
@@ -423,11 +431,11 @@ batchspend() {
trace "[batchspend] webhook_url=${webhook_url}"
if [ -z "${recipientsjson}" ]; then
whereclause="\"${recipient_id}\""
whereclause="${recipient_id}"
recipientsjson="\"${address}\":${amount}"
webhooks_data="{\"outputId\":${recipient_id},\"address\":\"${address}\",\"amount\":${amount},\"webhookUrl\":\"${webhook_url}\"}"
else
whereclause="${whereclause},\"${recipient_id}\""
whereclause="${whereclause},${recipient_id}"
recipientsjson="${recipientsjson},\"${address}\":${amount}"
webhooks_data="${webhooks_data},{\"outputId\":${recipient_id},\"address\":\"${address}\",\"amount\":${amount},\"webhookUrl\":\"${webhook_url}\"}"
fi
@@ -452,7 +460,7 @@ batchspend() {
tx_raw_details=$(get_rawtransaction ${txid} | tr -d '\n')
# Amounts and fees are negative when spending so we absolute those fields
local tx_hash=$(echo "${tx_raw_details}" | jq '.result.hash')
local tx_hash=$(echo "${tx_raw_details}" | jq -r '.result.hash')
local tx_ts_firstseen=$(echo "${tx_details}" | jq '.result.timereceived')
local tx_amount=$(echo "${tx_details}" | jq '.result.amount | fabs' | awk '{ printf "%.8f", $0 }')
local tx_size=$(echo "${tx_raw_details}" | jq '.result.size')
@@ -462,25 +470,20 @@ batchspend() {
tx_replaceable=$([ "${tx_replaceable}" = "yes" ] && echo "true" || echo "false")
trace "[batchspend] tx_replaceable=${tx_replaceable}"
local fees=$(echo "${tx_details}" | jq '.result.fee | fabs' | awk '{ printf "%.8f", $0 }')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
echo "${tx_raw_details}" > batchspend-rawtx-${txid}-$$.blob
# Get the info on the batch before setting it to done
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), 0), COALESCE(SUM(amount), 0.00000000) FROM recipient WHERE tx_id IS NULL AND batcher_id=${batcher_id}")
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), DATE '0001-01-01'), COALESCE(SUM(amount), 0.00000000) FROM recipient WHERE tx_id IS NULL AND batcher_id=${batcher_id}")
returncode=$?
trace_rc ${returncode}
# Let's insert the txid in our little DB -- then we'll already have it when receiving confirmation
sql_rawtx "INSERT OR IGNORE INTO rawtx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target}, readfile('batchspend-rawtx-${txid}-$$.blob'))"
trace_rc $?
id_inserted=$(sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target}); SELECT LAST_INSERT_ROWID();")
id_inserted=$(sql "INSERT INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target)"\
" VALUES ('${txid}', '${tx_hash}', 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target})"\
" RETURNING id" \
"SELECT id FROM tx WHERE txid='${txid}'")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
if [ "${id_inserted}" -eq 0 ]; then
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
fi
trace "[batchspend] id_inserted: ${id_inserted}"
sql "UPDATE recipient SET tx_id=${id_inserted} WHERE id IN (${whereclause})"
trace_rc $?
@@ -495,13 +498,10 @@ batchspend() {
trace "[batchspend] total=${total}"
response='{"result":{"batcherId":'${batcher_id}',"confTarget":'${conf_target}',"nbOutputs":'${count}',"oldest":"'${oldest}'","total":'${total}
response="${response},\"status\":\"accepted\",\"txid\":\"${txid}\",\"hash\":${tx_hash},\"details\":{\"firstseen\":${tx_ts_firstseen},\"size\":${tx_size},\"vsize\":${tx_vsize},\"replaceable\":${tx_replaceable},\"fee\":${fees}},\"outputs\":[${webhooks_data}]}"
response="${response},\"status\":\"accepted\",\"txid\":\"${txid}\",\"hash\":\"${tx_hash}\",\"details\":{\"firstseen\":${tx_ts_firstseen},\"size\":${tx_size},\"vsize\":${tx_vsize},\"replaceable\":${tx_replaceable},\"fee\":${fees}},\"outputs\":[${webhooks_data}]}"
response="${response},\"error\":null}"
# Delete the temp file containing the raw tx (see above)
rm batchspend-rawtx-${txid}-$$.blob
batch_webhooks "[${webhooks_data}]" '"batcherId":'${batcher_id}',"confTarget":'${conf_target}',"nbOutputs":'${count}',"oldest":"'${oldest}'","total":'${total}',"status":"accepted","txid":"'${txid}'","hash":'${tx_hash}',"details":{"firstseen":'${tx_ts_firstseen}',"size":'${tx_size}',"vsize":'${tx_vsize}',"replaceable":'${tx_replaceable}',"fee":'${fees}'}'
batch_webhooks "[${webhooks_data}]" '"batcherId":'${batcher_id}',"confTarget":'${conf_target}',"nbOutputs":'${count}',"oldest":"'${oldest}'","total":'${total}',"status":"accepted","txid":"'${txid}'","hash":"'${tx_hash}'","details":{"firstseen":'${tx_ts_firstseen}',"size":'${tx_size}',"vsize":'${tx_vsize}',"replaceable":'${tx_replaceable}',"fee":'${fees}'}'
else
local message=$(echo "${data}" | jq -e ".error.message")
@@ -578,7 +578,7 @@ batch_check_webhooks() {
# I know this query for each output is not very efficient, but this function should not execute often, only in case of
# failed callbacks on batches...
# Get the info on the batch
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), 0), COALESCE(SUM(amount), 0.00000000) FROM recipient r WHERE tx_id=\"${tx_id}\"")
row=$(sql "SELECT COUNT(id), COALESCE(MIN(inserted_ts), DATE '0001-01-01'), COALESCE(SUM(amount), 0.00000000) FROM recipient r WHERE tx_id='${tx_id}'")
# Use the selected row above
count=$(echo "${row}" | cut -d '|' -f1)
@@ -654,7 +654,7 @@ batch_webhooks() {
fi
done
sql "UPDATE recipient SET calledback=1, calledback_ts=CURRENT_TIMESTAMP WHERE id IN (${successful_recipient_ids})"
sql "UPDATE recipient SET calledback=true, calledback_ts=CURRENT_TIMESTAMP WHERE id IN (${successful_recipient_ids})"
trace_rc $?
}
@@ -671,7 +671,7 @@ listbatchers() {
# "error":null}
local batchers=$(sql "SELECT b.id, '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), 0) || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) || '}' FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id AND r.tx_id IS NULL GROUP BY b.id")
local batchers=$(sql "SELECT b.id, '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), DATE '0001-01-01') || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) || '}' FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id AND r.tx_id IS NULL GROUP BY b.id ORDER BY b.id")
trace "[listbatchers] batchers=${batchers}"
local returncode
@@ -717,7 +717,7 @@ getbatcher() {
local batcher_id=$(echo "${request}" | jq ".batcherId")
trace "[getbatcher] batcher_id=${batcher_id}"
local batcher_label=$(echo "${request}" | jq ".batcherLabel")
local batcher_label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[getbatcher] batcher_label=${batcher_label}"
if [ "${batcher_id}" = "null" ] && [ "${batcher_label}" = "null" ]; then
@@ -728,13 +728,13 @@ getbatcher() {
if [ "${batcher_id}" = "null" ]; then
# Using batcher_label
whereclause="b.label=${batcher_label}"
whereclause="b.label='${batcher_label}'"
else
# Using batcher_id
whereclause="b.id=${batcher_id}"
fi
batcher=$(sql "SELECT b.id, '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), 0) || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) || '}' FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id AND r.tx_id IS NULL WHERE ${whereclause} GROUP BY b.id")
batcher=$(sql "SELECT b.id, '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), DATE '0001-01-01') || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) || '}' FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id AND r.tx_id IS NULL WHERE ${whereclause} GROUP BY b.id")
trace "[getbatcher] batcher=${batcher}"
if [ -n "${batcher}" ]; then
@@ -797,9 +797,9 @@ getbatchdetails() {
local batcher_id=$(echo "${request}" | jq ".batcherId")
trace "[getbatchdetails] batcher_id=${batcher_id}"
local batcher_label=$(echo "${request}" | jq ".batcherLabel")
local batcher_label=$(echo "${request}" | jq -r ".batcherLabel")
trace "[getbatchdetails] batcher_label=${batcher_label}"
local txid=$(echo "${request}" | jq ".txid")
local txid=$(echo "${request}" | jq -r ".txid")
trace "[getbatchdetails] txid=${txid}"
if [ "${batcher_id}" = "null" ] && [ "${batcher_label}" = "null" ]; then
@@ -810,7 +810,7 @@ getbatchdetails() {
if [ "${batcher_id}" = "null" ]; then
# Using batcher_label
whereclause="b.label=${batcher_label}"
whereclause="b.label='${batcher_label}'"
else
# Using batcher_id
whereclause="b.id=${batcher_id}"
@@ -818,7 +818,7 @@ getbatchdetails() {
if [ "${txid}" != "null" ]; then
# Using txid
whereclause="${whereclause} AND t.txid=${txid}"
whereclause="${whereclause} AND t.txid='${txid}'"
else
# null txid
whereclause="${whereclause} AND t.txid IS NULL"
@@ -826,7 +826,7 @@ getbatchdetails() {
fi
# First get the batch summary
batch=$(sql "SELECT b.id, COALESCE(t.id, NULL), '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || b.conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), 0) || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id ${outerclause} LEFT JOIN tx t ON t.id=r.tx_id WHERE ${whereclause} GROUP BY b.id")
batch=$(sql "SELECT b.id, COALESCE(t.id, NULL), '{\"batcherId\":' || b.id || ',\"batcherLabel\":\"' || b.label || '\",\"confTarget\":' || b.conf_target || ',\"nbOutputs\":' || COUNT(r.id) || ',\"oldest\":\"' ||COALESCE(MIN(r.inserted_ts), DATE '0001-01-01') || '\",\"total\":' ||COALESCE(SUM(amount), 0.00000000) FROM batcher b LEFT JOIN recipient r ON r.batcher_id=b.id ${outerclause} LEFT JOIN tx t ON t.id=r.tx_id WHERE ${whereclause} GROUP BY b.id, t.id")
trace "[getbatchdetails] batch=${batch}"
if [ -n "${batch}" ]; then
@@ -839,7 +839,7 @@ getbatchdetails() {
# Using txid
outerclause="AND r.tx_id=${tx_id}"
tx=$(sql "SELECT '\"txid\":\"' || txid || '\",\"hash\":\"' || hash || '\",\"details\":{\"firstseen\":' || timereceived || ',\"size\":' || size || ',\"vsize\":' || vsize || ',\"replaceable\":' || CASE is_replaceable WHEN 1 THEN 'true' ELSE 'false' END || ',\"fee\":' || fee || '}' FROM tx WHERE id=${tx_id}")
tx=$(sql "SELECT '\"txid\":\"' || txid || '\",\"hash\":\"' || hash || '\",\"details\":{\"firstseen\":' || timereceived || ',\"size\":' || size || ',\"vsize\":' || vsize || ',\"replaceable\":' || CASE is_replaceable WHEN true THEN 'true' ELSE 'false' END || ',\"fee\":' || fee || '}' FROM tx WHERE id=${tx_id}")
else
# null txid
outerclause="AND r.tx_id IS NULL"

View File

@@ -78,6 +78,14 @@ convert_pub32() {
local checksum
local pub32_dest
case "${pub32_from}" in
${to_type}*)
trace "[convert_pub32] Already in the right format, exiting"
echo "${pub32_from}"
return
;;
esac
case "${to_type}" in
tpub)
versionbytes="043587cf"

View File

@@ -8,8 +8,8 @@ ln_call_lightningd() {
local response
local returncode
trace "[ln_call_lightningd] ./lightning-cli $@"
response=$(./lightning-cli $@)
trace "[ln_call_lightningd] ./lightning-cli $(printf " \"%s\"" "$@")"
response=$(./lightning-cli "$@")
returncode=$?
trace_rc ${returncode}
@@ -39,7 +39,7 @@ ln_create_invoice() {
if [ "${callback_url}" != "null" ]; then
# If not null, let's add double-quotes so we don't need to add the double-quotes in the sql insert,
# so if it's null, it will insert the actual sql NULL value.
callback_url="\"${callback_url}\""
callback_url="'${callback_url}'"
fi
#/proxy $ ./lightning-cli invoice 10000 "t1" "t1d" 60
@@ -71,36 +71,33 @@ ln_create_invoice() {
# Let's get the connect string if provided in configuration
local connectstring=$(get_connection_string)
if [ "${msatoshi}" = "null" ]; then
sql "INSERT OR IGNORE INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, description, status) VALUES (\"${label}\", \"${bolt11}\", ${callback_url}, \"${payment_hash}\", ${expires_at}, \"${description}\", \"unpaid\")"
else
sql "INSERT OR IGNORE INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, msatoshi, description, status) VALUES (\"${label}\", \"${bolt11}\", ${callback_url}, \"${payment_hash}\", ${expires_at}, ${msatoshi}, \"${description}\", \"unpaid\")"
fi
trace_rc $?
id=$(sql "SELECT id FROM ln_invoice WHERE bolt11=\"${bolt11}\"")
id=$(sql "INSERT INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, msatoshi, description, status)"\
" VALUES ('${label}','${bolt11}', ${callback_url},'${payment_hash}', ${expires_at}, ${msatoshi}, '${description}', 'unpaid')"\
" RETURNING id" \
"SELECT id FROM ln_invoice WHERE bolt11='${bolt11}'")
trace_rc $?
# {
# "id":"",
# "id":123,
# "label":"",
# "bolt11":"",
# "connectstring":"",
# "callbackUrl":"",
# "payment_hash":"",
# "msatoshi":,
# "msatoshi":123456,
# "status":"unpaid",
# "description":"",
# "expires_at":
# "expires_at":21312312
# }
data="{\"id\":\"${id}\","
data="{\"id\":${id},"
data="${data}\"label\":\"${label}\","
data="${data}\"bolt11\":\"${bolt11}\","
if [ -n "${connectstring}" ]; then
data="${data}\"connectstring\":\"${connectstring}\","
fi
if [ "${callback_url}" != "null" ]; then
data="${data}\"callbackUrl\":${callback_url},"
data="${data}\"callbackUrl\":\"${callback_url}\","
fi
data="${data}\"payment_hash\":\"${payment_hash}\","
if [ "${msatoshi}" != "null" ]; then

View File

@@ -10,8 +10,17 @@ do_callbacks() {
trace "Entering do_callbacks()..."
# If called because we received a confirmation for a specific txid, let's only
# process that txid-related callbacks...
local txid=${1}
local txid_where
if [ -n "${txid}" ]; then
trace "[do_callbacks] txid=${txid}"
txid_where=" AND txid='${txid}'"
fi
# Let's fetch all the watching addresses still being watched but not called back
local callbacks=$(sql 'SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id NOT NULL AND w.callback0conf NOT NULL AND w.watching')
local callbacks=$(sql "SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON w.watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id IS NOT NULL AND w.callback0conf IS NOT NULL AND w.watching${txid_where}")
trace "[do_callbacks] callbacks0conf=${callbacks}"
local returncode
@@ -25,12 +34,12 @@ do_callbacks() {
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
address=$(echo "${row}" | cut -d '|' -f2)
sql "UPDATE watching SET calledback0conf=1 WHERE address=\"${address}\""
sql "UPDATE watching SET calledback0conf=true WHERE address='${address}'"
trace_rc $?
fi
done
callbacks=$(sql 'SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w, watching_tx wt, tx t LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf AND confirmations>0 AND w.callback1conf NOT NULL AND w.watching')
callbacks=$(sql "SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w JOIN watching_tx wt ON w.id = wt.watching_id JOIN tx t ON wt.tx_id = t.id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback1conf AND confirmations>0 AND w.callback1conf IS NOT NULL AND w.watching${txid_where}")
trace "[do_callbacks] callbacks1conf=${callbacks}"
for row in ${callbacks}
@@ -39,19 +48,25 @@ do_callbacks() {
returncode=$?
if [ "${returncode}" -eq 0 ]; then
address=$(echo "${row}" | cut -d '|' -f2)
sql "UPDATE watching SET calledback1conf=1, watching=0 WHERE address=\"${address}\""
sql "UPDATE watching SET calledback1conf=true, watching=false WHERE address='${address}'"
trace_rc $?
fi
done
callbacks=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE NOT calledback AND callback_failed")
trace "[do_callbacks] ln_callbacks=${callbacks}"
if [ -z "${txid}" ]; then
trace "[do_callbacks] Processing LN callbacks..."
for row in ${callbacks}
do
ln_manage_callback ${row}
trace_rc $?
done
callbacks=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE NOT calledback AND callback_failed")
trace "[do_callbacks] ln_callbacks=${callbacks}"
for row in ${callbacks}
do
ln_manage_callback ${row}
trace_rc $?
done
else
trace "[do_callbacks] called for a specific txid, skipping LN callbacks"
fi
) 200>./.callbacks.lock
}
@@ -70,7 +85,7 @@ ln_manage_callback() {
if [ -z "${callback_url}" ]; then
# No callback url provided for that invoice
trace "[ln_manage_callback] No callback url provided for that invoice"
sql "UPDATE ln_invoice SET calledback=1 WHERE id=\"${id}\""
sql "UPDATE ln_invoice SET calledback=true WHERE id=${id}"
trace_rc $?
return
fi
@@ -112,7 +127,7 @@ ln_manage_callback() {
# "expires_at":
# }
data="{\"id\":\"${id}\","
data="{\"id\":${id},"
data="${data}\"label\":\"${label}\","
data="${data}\"bolt11\":\"${bolt11}\","
data="${data}\"callback_url\":\"${callback_url}\","
@@ -132,11 +147,11 @@ ln_manage_callback() {
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
sql "UPDATE ln_invoice SET calledback=1 WHERE id=\"${id}\""
sql "UPDATE ln_invoice SET calledback=true WHERE id=${id}"
trace_rc $?
else
trace "[ln_manage_callback] callback failed: ${callback_url}"
sql "UPDATE ln_invoice SET callback_failed=1 WHERE id=\"${id}\""
sql "UPDATE ln_invoice SET callback_failed=true WHERE id=${id}"
trace_rc $?
fi
@@ -212,7 +227,7 @@ build_callback() {
vsize=$(echo "${row}" | cut -d '|' -f10)
trace "[build_callback] vsize=${vsize}"
is_replaceable=$(echo "${row}" | cut -d '|' -f15)
is_replaceable=$([ "${is_replaceable}" -eq "1" ] && echo "true" || echo "false")
is_replaceable=$([ "${is_replaceable}" = "t" ] && echo "true" || echo "false")
trace "[build_callback] is_replaceable=${is_replaceable}"
blockhash=$(echo "${row}" | cut -d '|' -f11)
trace "[build_callback] blockhash=${blockhash}"
@@ -234,7 +249,7 @@ build_callback() {
event_message=$(echo "${row}" | cut -d '|' -f20)
trace "[build_callback] event_message=${event_message}"
data="{\"id\":\"${id}\","
data="{\"id\":${id},"
data="${data}\"address\":\"${address}\","
data="${data}\"txid\":\"${txid}\","
data="${data}\"hash\":\"${hash}\","

View File

@@ -9,8 +9,10 @@ do_callbacks_txid() {
trace "Entering do_callbacks_txid()..."
# Let's check the 1-conf (newly mined) watched txid that are included in the new block...
# Let's fetch all the watching txid still being watched but not called back
local callbacks=$(sql 'SELECT id, txid, callback1conf, 1 FROM watching_by_txid WHERE watching AND callback1conf NOT NULL AND NOT calledback1conf')
local callbacks=$(sql "SELECT id, txid, callback1conf, 1 FROM watching_by_txid WHERE watching AND callback1conf IS NOT NULL AND NOT calledback1conf")
trace "[do_callbacks_txid] callbacks1conf=${callbacks}"
local returncode
@@ -25,14 +27,16 @@ do_callbacks_txid() {
trace_rc ${returncode}
if [ "${returncode}" -eq "0" ]; then
id=$(echo "${row}" | cut -d '|' -f1)
sql "UPDATE watching_by_txid SET calledback1conf=1 WHERE id=\"${id}\""
sql "UPDATE watching_by_txid SET calledback1conf=true WHERE id=${id}"
trace_rc $?
else
trace "[do_callbacks_txid] callback returncode has error, we don't flag as calledback yet."
fi
done
local callbacks=$(sql 'SELECT id, txid, callbackxconf, nbxconf FROM watching_by_txid WHERE watching AND calledback1conf AND callbackxconf NOT NULL AND NOT calledbackxconf')
# For the n-conf, let's only check the watched txids that are already at least 1-conf...
local callbacks=$(sql "SELECT id, txid, callbackxconf, nbxconf FROM watching_by_txid WHERE watching AND calledback1conf AND callbackxconf IS NOT NULL AND NOT calledbackxconf")
trace "[do_callbacks_txid] callbacksxconf=${callbacks}"
for row in ${callbacks}
@@ -42,7 +46,7 @@ do_callbacks_txid() {
trace_rc ${returncode}
if [ "${returncode}" -eq "0" ]; then
id=$(echo "${row}" | cut -d '|' -f1)
sql "UPDATE watching_by_txid SET calledbackxconf=1, watching=0 WHERE id=\"${id}\""
sql "UPDATE watching_by_txid SET calledbackxconf=true, watching=false WHERE id=${id}"
trace_rc $?
else
trace "[do_callbacks_txid] callback returncode has error, we don't flag as calledback yet."

View File

@@ -64,16 +64,10 @@ compute_vin_total_amount()
for vin_txid_vout in ${vin_txids_vout}
do
vin_txid=$(echo "${vin_txid_vout}" | tr -d '"' | cut -d '-' -f1)
# Check if we already have the tx in our DB
vin_raw_tx=$(sql_rawtx "SELECT raw_tx FROM rawtx WHERE txid=\"${vin_txid}\"")
trace_rc $?
if [ -z "${vin_raw_tx}" ]; then
txid_already_inserted=false
vin_raw_tx=$(get_rawtransaction "${vin_txid}" | tr -d '\n')
returncode=$?
if [ "${returncode}" -ne 0 ]; then
return ${returncode}
fi
vin_raw_tx=$(get_rawtransaction "${vin_txid}" | tr -d '\n')
returncode=$?
if [ "${returncode}" -ne 0 ]; then
return ${returncode}
fi
vout=$(echo "${vin_txid_vout}" | tr -d '"' | cut -d '-' -f2)
trace "[compute_vin_total_amount] vout=${vout}"
@@ -81,27 +75,23 @@ compute_vin_total_amount()
trace "[compute_vin_total_amount] vin_vout_amount=${vin_vout_amount}"
vin_total_amount=$(awk "BEGIN { printf(\"%.8f\", ${vin_total_amount}+${vin_vout_amount}); exit}")
trace "[compute_vin_total_amount] vin_total_amount=${vin_total_amount}"
vin_hash=$(echo "${vin_raw_tx}" | jq ".result.hash")
vin_hash=$(echo "${vin_raw_tx}" | jq -r ".result.hash")
vin_confirmations=$(echo "${vin_raw_tx}" | jq ".result.confirmations")
vin_timereceived=$(echo "${vin_raw_tx}" | jq ".result.time")
vin_size=$(echo "${vin_raw_tx}" | jq ".result.size")
vin_vsize=$(echo "${vin_raw_tx}" | jq ".result.vsize")
vin_blockhash=$(echo "${vin_raw_tx}" | jq ".result.blockhash")
vin_blockhash=$(echo "${vin_raw_tx}" | jq -r ".result.blockhash")
vin_blockheight=$(echo "${vin_raw_tx}" | jq ".result.blockheight")
vin_blocktime=$(echo "${vin_raw_tx}" | jq ".result.blocktime")
# Let's insert the vin tx in the DB just in case it would be useful
if ! ${txid_already_inserted}; then
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
echo "${vin_raw_tx}" > vin-rawtx-${vin_txid}-$$.blob
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, size, vsize, blockhash, blockheight, blocktime) VALUES (\"${vin_txid}\", ${vin_hash}, ${vin_confirmations}, ${vin_timereceived}, ${vin_size}, ${vin_vsize}, ${vin_blockhash}, ${vin_blockheight}, ${vin_blocktime})"
trace_rc $?
sql_rawtx "INSERT OR IGNORE INTO rawtx (txid, hash, confirmations, timereceived, size, vsize, blockhash, blockheight, blocktime, raw_tx) VALUES (\"${vin_txid}\", ${vin_hash}, ${vin_confirmations}, ${vin_timereceived}, ${vin_size}, ${vin_vsize}, ${vin_blockhash}, ${vin_blockheight}, ${vin_blocktime}, readfile('vin-rawtx-${vin_txid}-$$.blob'))"
trace_rc $?
rm vin-rawtx-${vin_txid}-$$.blob
txid_already_inserted=true
fi
sql "INSERT INTO tx (txid, hash, confirmations, timereceived, size, vsize, blockhash, blockheight, blocktime)"\
" VALUES ('${vin_txid}', '${vin_hash}', ${vin_confirmations}, ${vin_timereceived}, ${vin_size}, ${vin_vsize}, '${vin_blockhash}', ${vin_blockheight}, ${vin_blocktime})"\
" ON CONFLICT (txid) DO"\
" UPDATE SET blockhash='${vin_blockhash}', blockheight=${vin_blockheight}, blocktime=${vin_blocktime}, confirmations=${vin_confirmations}"\
" RETURNING id" \
"SELECT id FROM tx WHERE txid='${vin_txid}'"
trace_rc $?
done
echo "${vin_total_amount}"

View File

@@ -44,7 +44,7 @@ confirmation() {
# First of all, let's make sure we're working on watched addresses...
local address
local addresseswhere
local addresses=$(echo "${tx_details}" | jq ".result.details[].address")
local addresses=$(echo "${tx_details}" | jq -r ".result.details[].address")
local notfirst=false
local IFS=$'\n'
@@ -53,9 +53,9 @@ confirmation() {
trace "[confirmation] address=${address}"
if ${notfirst}; then
addresseswhere="${addresseswhere},${address}"
addresseswhere="${addresseswhere},'${address}'"
else
addresseswhere="${address}"
addresseswhere="'${address}'"
notfirst=true
fi
done
@@ -66,11 +66,11 @@ confirmation() {
fi
########################################################################################################
local tx=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
local tx=$(sql "SELECT id FROM tx WHERE txid='${txid}'")
local id_inserted
local tx_raw_details=$(get_rawtransaction ${txid} | tr -d '\n')
local tx_nb_conf=$(echo "${tx_details}" | jq -r '.result.confirmations // 0')
local tx_hash=$(echo "${tx_raw_details}" | jq '.result.hash')
local tx_hash=$(echo "${tx_raw_details}" | jq -r '.result.hash')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
@@ -100,45 +100,33 @@ confirmation() {
local tx_blocktime=null
if [ "${tx_nb_conf}" -gt "0" ]; then
trace "[confirmation] tx_nb_conf=${tx_nb_conf}"
tx_blockhash=$(echo "${tx_details}" | jq '.result.blockhash')
tx_blockheight=$(get_block_info $(echo ${tx_blockhash} | tr -d '"') | jq '.result.height')
tx_blockhash="$(echo "${tx_details}" | jq -r '.result.blockhash')"
tx_blockheight=$(get_block_info ${tx_blockhash} | jq '.result.height')
tx_blockhash="'${tx_blockhash}'"
tx_blocktime=$(echo "${tx_details}" | jq '.result.blocktime')
fi
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, blockhash, blockheight, blocktime) VALUES (\"${txid}\", ${tx_hash}, ${tx_nb_conf}, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${tx_blockhash}, ${tx_blockheight}, ${tx_blocktime})"
trace_rc $?
sql_rawtx "INSERT OR IGNORE INTO rawtx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, blockhash, blockheight, blocktime, raw_tx) VALUES (\"${txid}\", ${tx_hash}, ${tx_nb_conf}, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${tx_blockhash}, ${tx_blockheight}, ${tx_blocktime}, readfile('rawtx-${txid}-$$.blob'))"
trace_rc $?
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
id_inserted=$(sql "INSERT INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, blockhash, blockheight, blocktime)"\
" VALUES ('${txid}', '${tx_hash}', ${tx_nb_conf}, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${tx_blockhash}, ${tx_blockheight}, ${tx_blocktime})"\
" ON CONFLICT (txid) DO"\
" UPDATE SET blockhash=${tx_blockhash}, blockheight=${tx_blockheight}, blocktime=${tx_blocktime}, confirmations=${tx_nb_conf}"\
" RETURNING id" \
"SELECT id FROM tx WHERE txid='${txid}'")
trace_rc $?
else
# TX found in our DB.
# 1-conf or executecallbacks on an unconfirmed tx or spending watched address (in this case, we probably missed conf) or spending to a watched address (in this case, spend inserted the tx in the DB)
local tx_blockhash=$(echo "${tx_details}" | jq '.result.blockhash')
local tx_blockhash=$(echo "${tx_details}" | jq -r '.result.blockhash')
trace "[confirmation] tx_blockhash=${tx_blockhash}"
if [ "${tx_blockhash}" = "null" ]; then
trace "[confirmation] probably being called by executecallbacks without any confirmations since the last time we checked"
else
local tx_blockheight=$(get_block_info $(echo "${tx_blockhash}" | tr -d '"') | jq '.result.height')
local tx_blockheight=$(get_block_info "${tx_blockhash}" | jq '.result.height')
local tx_blocktime=$(echo "${tx_details}" | jq '.result.blocktime')
sql "UPDATE tx SET
confirmations=${tx_nb_conf},
blockhash=${tx_blockhash},
blockheight=${tx_blockheight},
blocktime=${tx_blocktime}
WHERE txid=\"${txid}\""
trace_rc $?
sql_rawtx "UPDATE rawtx SET
confirmations=${tx_nb_conf},
blockhash=${tx_blockhash},
blockheight=${tx_blockheight},
blocktime=${tx_blocktime},
raw_tx=readfile('rawtx-${txid}-$$.blob')
WHERE txid=\"${txid}\""
sql "UPDATE tx SET confirmations=${tx_nb_conf}, blockhash='${tx_blockhash}', blockheight=${tx_blockheight}, blocktime=${tx_blocktime} WHERE txid='${txid}'"
trace_rc $?
fi
id_inserted=${tx}
@@ -171,7 +159,8 @@ confirmation() {
# If the tx is batched and pays multiple watched addresses, we have to insert
# those additional addresses in watching_tx!
watching_id=$(echo "${row}" | cut -d '|' -f1)
sql "INSERT OR IGNORE INTO watching_tx (watching_id, tx_id, vout, amount) VALUES (${watching_id}, ${id_inserted}, ${tx_vout_n}, ${tx_vout_amount})"
sql "INSERT INTO watching_tx (watching_id, tx_id, vout, amount) VALUES (${watching_id}, ${id_inserted}, ${tx_vout_n}, ${tx_vout_amount})"\
" ON CONFLICT DO NOTHING"
trace_rc $?
else
trace "[confirmation] For this tx, there's already watching_tx rows"
@@ -211,7 +200,7 @@ confirmation() {
# for next cron.
if [ -z "${bypass_callbacks}" ]; then
trace "[confirmation] Let's do the callbacks!"
do_callbacks
do_callbacks "${txid}"
fi
echo '{"result":"confirmed"}'

View File

@@ -12,8 +12,8 @@ get_txns_by_watchlabel(){
INNER JOIN watching AS w ON w32.id = w.watching_by_pub32_id
INNER JOIN watching_tx AS wtxn ON w.id = wtxn.watching_id
INNER JOIN tx AS tx ON wtxn.tx_id = tx.id
WHERE w32.label="$1"
LIMIT 0,${2-10}
WHERE w32.label='${1}'
LIMIT ${2-10} OFFSET 0
HERE
)
label_txns=$(sql "$query")
@@ -38,12 +38,12 @@ get_unused_addresses_by_watchlabel(){
SELECT w32.id, w32.label, w32.pub32, w.pub32_index, w.address
FROM watching as w
INNER JOIN watching_by_pub32 AS w32 ON w.watching_by_pub32_id = w32.id
WHERE w32.label="$1"
WHERE w32.label='${1}'
AND NOT EXISTS (
SELECT 1 FROM watching_tx WHERE watching_id = w.id
)
ORDER BY w.pub32_index ASC
LIMIT 0,${2-10}
LIMIT ${2-10} OFFSET 0
HERE
)
label_unused_addrs=$(sql "$query")
@@ -67,7 +67,7 @@ getactivewatches() {
local watches
# Let's build the string directly with sqlite instead of manipulating multiple strings afterwards, it's faster.
# {"id":"${id}","address":"${address}","imported":"${imported}","unconfirmedCallbackURL":"${cb0conf_url}","confirmedCallbackURL":"${cb1conf_url}","watching_since":"${timestamp}"}
watches=$(sql "SELECT '{\"id\":' || id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":\"' || COALESCE(callback0conf, '') || '\",\"confirmedCallbackURL\":\"' || COALESCE(callback1conf, '') || '\",\"label\":\"' || COALESCE(label, '') || '\",\"watching_since\":\"' || inserted_ts || '\"}' FROM watching WHERE watching AND NOT calledback1conf")
watches=$(sql "SELECT '{\"id\":' || id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":' || CASE WHEN callback0conf IS NULL THEN 'null' ELSE ('\"' || callback0conf || '\"') END || ',\"confirmedCallbackURL\":' || CASE WHEN callback1conf IS NULL THEN 'null' ELSE ('\"' || callback1conf || '\"') END || ',\"label\":\"' || COALESCE(label, '') || '\",\"watching_since\":\"' || inserted_ts || '\"}' FROM watching WHERE watching AND NOT calledback1conf ORDER BY id")
returncode=$?
trace_rc ${returncode}
@@ -99,7 +99,7 @@ getactivewatchesbyxpub() {
local xpub=${1}
local returncode
getactivewatchesxpub "pub32" ${xpub}
getactivewatchesxpub "pub32" "${xpub}"
returncode=$?
trace_rc ${returncode}
@@ -112,7 +112,7 @@ getactivewatchesbylabel() {
local label=${1}
local returncode
getactivewatchesxpub "label" ${label}
getactivewatchesxpub "label" "${label}"
returncode=$?
trace_rc ${returncode}
@@ -130,7 +130,7 @@ getactivewatchesxpub() {
# Let's build the string directly with sqlite instead of manipulating multiple strings afterwards, it's faster.
# {"id":"${id}","address":"${address}","imported":"${imported}","unconfirmedCallbackURL":"${cb0conf_url}","confirmedCallbackURL":"${cb1conf_url}","watching_since":"${timestamp}","derivation_path":"${derivation_path}","pub32_index":"${pub32_index}"}
watches=$(sql "SELECT '{\"id\":' || w.id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":\"' || COALESCE(w.callback0conf, '') || '\",\"confirmedCallbackURL\":\"' || COALESCE(w.callback1conf, '') || '\",\"watching_since\":\"' || w.inserted_ts || '\",\"derivation_path\":\"' || derivation_path || '\",\"pub32_index\":' || pub32_index || '}' FROM watching w, watching_by_pub32 w32 WHERE watching_by_pub32_id = w32.id AND ${where} = \"${value}\" AND w.watching AND NOT calledback1conf")
watches=$(sql "SELECT '{\"id\":' || w.id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":' || CASE WHEN w.callback0conf IS NULL THEN 'null' ELSE ('\"' || w.callback0conf || '\"') END || ',\"confirmedCallbackURL\":' || CASE WHEN w.callback1conf IS NULL THEN 'null' ELSE ('\"' || w.callback1conf || '\"') END || ',\"watching_since\":\"' || w.inserted_ts || '\",\"derivation_path\":\"' || derivation_path || '\",\"pub32_index\":' || pub32_index || '}' FROM watching w, watching_by_pub32 w32 WHERE watching_by_pub32_id = w32.id AND w32.${where} = '${value}' AND w.watching AND NOT calledback1conf ORDER BY w.id")
returncode=$?
trace_rc ${returncode}
@@ -162,7 +162,7 @@ getactivexpubwatches() {
local watches
# Let's build the string directly with sqlite instead of manipulating multiple strings afterwards, it's faster.
# {"id":"${id}","pub32":"${pub32}","label":"${label}","derivation_path":"${derivation_path}","last_imported_n":${last_imported_n},"unconfirmedCallbackURL":"${cb0conf_url}","confirmedCallbackURL":"${cb1conf_url}","watching_since":"${timestamp}"}
watches=$(sql "SELECT '{\"id\":' || id || ',\"pub32\":\"' || pub32 || '\",\"label\":\"' || label || '\",\"derivation_path\":\"' || derivation_path || '\",\"last_imported_n\":' || last_imported_n || ',\"unconfirmedCallbackURL\":\"' || COALESCE(callback0conf, '') || '\",\"confirmedCallbackURL\":\"' || COALESCE(callback1conf, '') || '\",\"watching_since\":\"' || inserted_ts || '\"}' FROM watching_by_pub32 WHERE watching")
watches=$(sql "SELECT '{\"id\":' || id || ',\"pub32\":\"' || pub32 || '\",\"label\":\"' || label || '\",\"derivation_path\":\"' || derivation_path || '\",\"last_imported_n\":' || last_imported_n || ',\"unconfirmedCallbackURL\":' || CASE WHEN callback0conf IS NULL THEN 'null' ELSE ('\"' || callback0conf || '\"') END || ',\"confirmedCallbackURL\":' || CASE WHEN callback1conf IS NULL THEN 'null' ELSE ('\"' || callback1conf || '\"') END || ',\"watching_since\":\"' || inserted_ts || '\"}' FROM watching_by_pub32 WHERE watching ORDER BY id")
returncode=$?
trace_rc ${returncode}

View File

@@ -11,7 +11,7 @@ importaddress_rpc() {
if [ -z "${label}" ]; then
label="null"
fi
local data='{"method":"importaddress","params":{"address":"'${address}'","label":'${label}',"rescan":false}}'
local data='{"method":"importaddress","params":{"address":"'${address}'","label":"'${label}'","rescan":false}}'
# local data="{\"method\":\"importaddress\",\"params\":[\"${address}\",\"\",false]}"
local result
result=$(send_to_watcher_node ${data})
@@ -39,7 +39,7 @@ importmulti_rpc() {
# {"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},
# {"scriptPubKey":{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},"timestamp":"now","watchonly":true,"label":"xpub"},
addresses=$(echo "${addresses}" | sed "s/\"address\"/\"scriptPubKey\":\{\"address\"/g" | sed "s/}/},\"timestamp\":\"now\",\"watchonly\":true,\"label\":${label}}/g")
addresses=$(echo "${addresses}" | sed "s/\"address\"/\"scriptPubKey\":\{\"address\"/g" | sed "s/}/},\"timestamp\":\"now\",\"watchonly\":true,\"label\":\"${label}\"}/g")
# trace "[importmulti_rpc] addresses=${addresses}"
# Now we use that in the RPC string

View File

@@ -25,7 +25,7 @@ manage_not_imported() {
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
sql "UPDATE watching SET imported=1 WHERE address=\"${address}\""
sql "UPDATE watching SET imported=true WHERE address='${address}'"
fi
done
@@ -41,8 +41,8 @@ manage_missed_conf() {
trace "[Entering manage_missed_conf()]"
local watches=$(sql 'SELECT DISTINCT address FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx t ON t.id = tx_id WHERE watching AND imported AND (tx_id IS NULL OR t.confirmations=0) ORDER BY address')
trace "[manage_missed_conf] watches=${watches}"
local watches=$(sql "SELECT DISTINCT address FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx t ON t.id = tx_id WHERE watching AND imported AND (tx_id IS NULL OR t.confirmations=0) ORDER BY address")
# trace "[manage_missed_conf] watches=${watches}"
if [ ${#watches} -eq 0 ]; then
trace "[manage_missed_conf] Nothing missed!"
return 0
@@ -55,7 +55,7 @@ manage_missed_conf() {
data='{"method":"listreceivedbyaddress","params":[0,false,true]}'
received=$(send_to_watcher_node "${data}")
received_addresses=$(echo "${received}" | jq -r ".result[].address" | sort)
trace "[manage_missed_conf] received_addresses=${received_addresses}"
# trace "[manage_missed_conf] received_addresses=${received_addresses}"
# Let's extract addresses that are in the watches list as well as in the received_addresses list
echo "${watches}" > watches-$$
@@ -81,7 +81,7 @@ manage_missed_conf() {
local IFS=$'\n'
for address in ${received_watches}
do
watching=$(sql 'SELECT address, inserted_ts FROM watching WHERE address="'${address}'"')
watching=$(sql "SELECT address, inserted_ts FROM watching WHERE address='${address}'")
trace "[manage_missed_conf] watching=${watching}"
if [ ${#watching} -eq 0 ]; then
trace "[manage_missed_conf] Nothing missed!"
@@ -90,7 +90,7 @@ manage_missed_conf() {
# Let's get confirmed received txs for the address
# address=$(echo "${watches}" | cut -d '|' -f1)
inserted_ts=$(date -d "$(echo "${watching}" | cut -d '|' -f2)" +"%s")
inserted_ts=$(date -d "$(echo "${watching}" | cut -d '|' -f2)" -D '%Y-%m-%d %H:%M:%S' +"%s")
trace "[manage_missed_conf] inserted_ts=${inserted_ts}"
received_address=$(echo "${received}" | jq -Mc ".result | map(select(.address==\"${address}\" and .confirmations>0))[0]")

View File

@@ -25,6 +25,7 @@ newblock() {
returncode=$?
trace_rc ${returncode}
# do_callbacks_txid "$(echo "${blockinfo}" | jq ".result.tx[]")"
do_callbacks_txid
batch_check_webhooks

View File

@@ -49,18 +49,20 @@ serve_ots_stamp() {
returncode=$?
fi
else
sql "INSERT OR IGNORE INTO stamp (hash, callbackUrl) VALUES (\"${hash}\", \"${callbackUrl}\")"
id_inserted=$(sql "INSERT INTO stamp (hash, callbackUrl)"\
" VALUES ('${hash}','${callbackUrl}')"\
" RETURNING id" \
"SELECT id FROM stamp WHERE hash='${hash}'")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq "0" ]; then
id_inserted=$(sql "SELECT id FROM stamp WHERE hash='${hash}'")
trace_rc $?
errorstring=$(request_ots_stamp "${hash}" ${id_inserted})
returncode=$?
trace_rc ${returncode}
else
trace "[serve_ots_stamp] Stamp request could not be inserted in DB"
errorstring="Stamp request could not be inserted in DB, please retry later"
id_inserted=null
returncode=1
fi
fi
@@ -114,7 +116,7 @@ request_ots_stamp() {
if [ "${returncode}" -eq "0" ]; then
# "already exists" found, let's try updating DB again
trace "[request_ots_stamp] was already requested to the OTS server... let's update the DB, looks like it didn't work on first try"
sql "UPDATE stamp SET requested=1 WHERE id=${id}"
sql "UPDATE stamp SET requested=true WHERE id=${id}"
errorstring="Duplicate stamping request, hash already exists in DB and been OTS requested"
returncode=1
else
@@ -125,7 +127,7 @@ request_ots_stamp() {
fi
else
trace "[request_ots_stamp] Stamping request sent successfully!"
sql "UPDATE stamp SET requested=1 WHERE id=${id}"
sql "UPDATE stamp SET requested=true WHERE id=${id}"
errorstring=""
returncode=0
fi
@@ -198,7 +200,7 @@ serve_ots_backoffice() {
else
# No failure, upgraded
trace "[serve_ots_backoffice] just upgraded!"
sql "UPDATE stamp SET upgraded=1 WHERE id=${id}"
sql "UPDATE stamp SET upgraded=true WHERE id=${id}"
trace_rc $?
upgraded=1
@@ -221,13 +223,13 @@ serve_ots_backoffice() {
# Even if curl executed ok, we need to make sure the http return code is also ok
if [ "${returncode}" -eq "0" ]; then
sql "UPDATE stamp SET calledback=1 WHERE id=${id}"
sql "UPDATE stamp SET calledback=true WHERE id=${id}"
trace_rc $?
fi
else
trace "[serve_ots_backoffice] url is empty, obviously won't try to call it!"
sql "UPDATE stamp SET calledback=1 WHERE id=${id}"
sql "UPDATE stamp SET calledback=true WHERE id=${id}"
trace_rc $?
fi
fi

View File

@@ -76,8 +76,10 @@ main() {
case "${cmd}" in
helloworld)
# GET http://192.168.111.152:8080/helloworld
response_to_client "Hello, world!" 0
break
response="Hello, world!"
returncode=0
# response_to_client "Hello, world!" 0
# break
;;
installation_info)
# GET http://192.168.111.152:8080/info
@@ -86,8 +88,7 @@ main() {
else
response='{ "error": "missing installation data" }'
fi
response_to_client "${response}" ${?}
break
returncode=$?
;;
watch)
# POST http://192.168.111.152:8080/watch
@@ -96,8 +97,7 @@ main() {
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K","label":"myLabel"}
response=$(watchrequest "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
unwatch)
# curl (GET) 192.168.111.152:8080/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
@@ -122,16 +122,15 @@ main() {
# Let's make it work even for a GET request (equivalent to a POST with empty json object body)
if [ "$http_method" = "POST" ]; then
address=$(echo "${line}" | jq -r ".address")
unconfirmedCallbackURL=$(echo "${line}" | jq ".unconfirmedCallbackURL")
confirmedCallbackURL=$(echo "${line}" | jq ".confirmedCallbackURL")
unconfirmedCallbackURL=$(echo "${line}" | jq -r ".unconfirmedCallbackURL")
confirmedCallbackURL=$(echo "${line}" | jq -r ".confirmedCallbackURL")
watchid=$(echo "${line}" | jq ".id")
else
address=$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)
fi
response=$(unwatchrequest "${watchid}" "${address}" "${unconfirmedCallbackURL}" "${confirmedCallbackURL}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
watchxpub)
# POST http://192.168.111.152:8080/watchxpub
@@ -139,43 +138,37 @@ main() {
# curl -H "Content-Type: application/json" -d '{"label":"2219","pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/1/n","nstart":55,"unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}' proxy:8888/watchxpub
response=$(watchpub32request "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
unwatchxpubbyxpub)
# GET http://192.168.111.152:8080/unwatchxpubbyxpub/tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk
response=$(unwatchpub32request "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
unwatchxpubbylabel)
# GET http://192.168.111.152:8080/unwatchxpubbylabel/4421
response=$(unwatchpub32labelrequest "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getactivewatchesbyxpub)
# GET http://192.168.111.152:8080/getactivewatchesbyxpub/tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk
response=$(getactivewatchesbyxpub "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getactivewatchesbylabel)
# GET http://192.168.111.152:8080/getactivewatchesbylabel/4421
response=$(getactivewatchesbylabel "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getactivexpubwatches)
# GET http://192.168.111.152:8080/getactivexpubwatches
response=$(getactivexpubwatches)
response_to_client "${response}" ${?}
break
returncode=$?
;;
watchtxid)
# POST http://192.168.111.152:8080/watchtxid
@@ -183,8 +176,7 @@ main() {
# curl -H "Content-Type: application/json" -d '{"txid":"b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","xconfCallbackURL":"192.168.111.233:1111/callbackXconf","nbxconf":6}' proxy:8888/watchtxid
response=$(watchtxidrequest "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
unwatchtxid)
# POST http://192.168.111.152:8080/unwatchtxid
@@ -200,87 +192,76 @@ main() {
# - id: the id returned by watchtxid
local txid=$(echo "${line}" | jq -r ".txid")
local unconfirmedCallbackURL=$(echo "${line}" | jq ".unconfirmedCallbackURL")
local confirmedCallbackURL=$(echo "${line}" | jq ".confirmedCallbackURL")
local unconfirmedCallbackURL=$(echo "${line}" | jq -r ".unconfirmedCallbackURL")
local confirmedCallbackURL=$(echo "${line}" | jq -r ".confirmedCallbackURL")
local watchid=$(echo "${line}" | jq ".id")
response=$(unwatchtxidrequest "${watchid}" "${txid}" "${unconfirmedCallbackURL}" "${confirmedCallbackURL}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getactivewatches)
# curl (GET) 192.168.111.152:8080/getactivewatches
response=$(getactivewatches)
response_to_client "${response}" ${?}
break
returncode=$?
;;
get_txns_by_watchlabel)
# curl (GET) 192.168.111.152:8080/get_txns_by_watchlabel/<label>/<count>
response=$(get_txns_by_watchlabel "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)" "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
get_unused_addresses_by_watchlabel)
# curl (GET) 192.168.111.152:8080/get_unused_addresses_by_watchlabel/<label>/<count>
response=$(get_unused_addresses_by_watchlabel "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)" "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
conf)
# curl (GET) 192.168.111.152:8080/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387
response=$(confirmation_request "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
newblock)
# curl (GET) 192.168.111.152:8080/newblock/000000000000005c987120f3b6f995c95749977ef1a109c89aa74ce4bba97c1f
response=$(newblock "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbestblockhash)
# curl (GET) http://192.168.111.152:8080/getbestblockhash
response=$(get_best_block_hash)
response_to_client "${response}" ${?}
break
returncode=$?
;;
getblockhash)
# curl (GET) http://192.168.111.152:8080/getblockhash/522322
response=$(get_blockhash "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getblockinfo)
# curl (GET) http://192.168.111.152:8080/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea
response=$(get_block_info "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getblockchaininfo)
# http://192.168.111.152:8080/getblockchaininfo
response=$(get_blockchain_info)
response_to_client "${response}" ${?}
returncode=$?
;;
gettransaction)
# curl (GET) http://192.168.111.152:8080/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
response=$(get_rawtransaction "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbestblockinfo)
# curl (GET) http://192.168.111.152:8080/getbestblockinfo
response=$(get_best_block_info)
response_to_client "${response}" ${?}
break
returncode=$?
;;
executecallbacks)
# curl (GET) http://192.168.111.152:8080/executecallbacks
@@ -288,43 +269,37 @@ main() {
manage_not_imported
manage_missed_conf
response=$(do_callbacks)
response_to_client "${response}" ${?}
break
returncode=$?
;;
get_txns_spending)
# curl (GET) http://192.168.111.152:8080/get_txns_spending/20/10
response=$(get_txns_spending "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)" "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbalance)
# curl (GET) http://192.168.111.152:8080/getbalance
response=$(getbalance)
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbalances)
# curl (GET) http://192.168.111.152:8080/getbalances
response=$(getbalances)
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbalancebyxpub)
# curl (GET) http://192.168.111.152:8080/getbalancebyxpub/upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb
response=$(getbalancebyxpub "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbalancebyxpublabel)
# curl (GET) http://192.168.111.152:8080/getbalancebyxpublabel/2219
response=$(getbalancebyxpublabel "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getnewaddress)
# curl (GET) http://192.168.111.152:8080/getnewaddress
@@ -346,8 +321,7 @@ main() {
fi
response=$(getnewaddress "${address_type}" "${label}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
validateaddress)
# GET http://192.168.111.152:8080/validateaddress/tb1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqp3mvzv
@@ -361,8 +335,7 @@ main() {
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233,"eventMessage":"eyJ3aGF0ZXZlciI6MTIzfQo=","confTarget":6,"replaceable":true,"subtractfeefromamount":false}
response=$(spend "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
bumpfee)
# POST http://192.168.111.152:8080/bumpfee
@@ -370,8 +343,7 @@ main() {
# BODY {"txid":"af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648"}
response=$(bumpfee "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
createbatcher)
# POST http://192.168.111.152:8080/createbatcher
@@ -388,8 +360,7 @@ main() {
# NOTYET BODY {"batcherLabel":"highfees","feeRate":231.8}
response=$(createbatcher "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
updatebatcher)
# POST http://192.168.111.152:8080/updatebatcher
@@ -412,8 +383,7 @@ main() {
# BODY {"batcherLabel":"fast","confTarget":2}
response=$(updatebatcher "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
addtobatch)
# POST http://192.168.111.152:8080/addtobatch
@@ -439,8 +409,7 @@ main() {
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233,"batcherId":34,"webhookUrl":"https://myCypherApp:3000/batchExecuted"}
response=$(addtobatch "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
removefrombatch)
# POST http://192.168.111.152:8080/removefrombatch
@@ -458,8 +427,7 @@ main() {
# BODY {"outputId":72}
response=$(removefrombatch "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
batchspend)
# POST http://192.168.111.152:8080/batchspend
@@ -511,8 +479,7 @@ main() {
# BODY {"batcherId":411,"confTarget":6}
response=$(batchspend "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbatcher)
# POST (GET) http://192.168.111.152:8080/getbatcher
@@ -528,14 +495,13 @@ main() {
# BODY {"batcherId":34}
response=$(getbatcher "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getbatchdetails)
# POST (GET) http://192.168.111.152:8080/getbatchdetails
#
# args:
# - batcherId, optional, id of the batcher, overrides batcherLabel, default batcher will be spent if not supplied
# - batcherId, optional, id of the batcher, overrides batcherLabel, default batcher will be used if not supplied
# - batcherLabel, optional, label of the batcher, default batcher will be used if not supplied
# - txid, optional, if you want the details of an executed batch, supply the batch txid, will return current pending batch
# if not supplied
@@ -570,8 +536,7 @@ main() {
# BODY {"batcherId":34}
response=$(getbatchdetails "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
listbatchers)
# curl (GET) http://192.168.111.152:8080/listbatchers
@@ -585,24 +550,21 @@ main() {
# "error":null}
response=$(listbatchers)
response_to_client "${response}" ${?}
break
returncode=$?
;;
bitcoin_estimatesmartfee)
# POST http://192.168.111.152:8080/bitcoin_estimatesmartfee
# BODY {"confTarget":2}
response=$(bitcoin_estimatesmartfee "$(echo "${line}" | jq -r ".confTarget")")
response_to_client "${response}" ${?}
break
returncode=$?
;;
deriveindex)
# curl GET http://192.168.111.152:8080/deriveindex/25-30
# curl GET http://192.168.111.152:8080/deriveindex/34
response=$(deriveindex "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
derivepubpath)
# POST http://192.168.111.152:8080/derivepubpath
@@ -611,16 +573,14 @@ main() {
# BODY {"pub32":"vpub5SLqN2bLY4WeZF3kL4VqiWF1itbf3A6oRrq9aPf16AZMVWYCuN9TxpAZwCzVgW94TNzZPNc9XAHD4As6pdnExBtCDGYRmNJrcJ4eV9hNqcv","path":"0/25-30"}
response=$(derivepubpath "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
deriveindex_bitcoind)
# curl GET http://192.168.111.152:8080/deriveindex_bitcoind/25-30
# curl GET http://192.168.111.152:8080/deriveindex_bitcoind/34
response=$(deriveindex_bitcoind "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
derivepubpath_bitcoind)
# POST http://192.168.111.152:8080/derivepubpath_bitcoind
@@ -629,45 +589,39 @@ main() {
# BODY {"pub32":"vpub5SLqN2bLY4WeZF3kL4VqiWF1itbf3A6oRrq9aPf16AZMVWYCuN9TxpAZwCzVgW94TNzZPNc9XAHD4As6pdnExBtCDGYRmNJrcJ4eV9hNqcv","path":"0/25-30"}
response=$(derivepubpath_bitcoind "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
getmempoolinfo)
# curl GET http://192.168.111.152:8080/getmempoolinfo
response=$(get_mempool_info)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_getinfo)
# GET http://192.168.111.152:8080/ln_getinfo
response=$(ln_getinfo)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_getconnectionstring)
# GET http://192.168.111.152:8080/ln_getconnectionstring
response=$(ln_get_connection_string)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_create_invoice)
# POST http://192.168.111.152:8080/ln_create_invoice
# BODY {"msatoshi":"10000","label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":"900","callback_url":"http://192.168.122.159"}
response=$(ln_create_invoice "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_pay)
# POST http://192.168.111.152:8080/ln_pay
# BODY {"bolt11":"lntb1pdca82tpp5gv8mn5jqlj6xztpnt4r472zcyrwf3y2c3cvm4uzg2gqcnj90f83qdp2gf5hgcm0d9hzqnm4w3kx2apqdaexgetjyq3nwvpcxgcqp2g3d86wwdfvyxcz7kce7d3n26d2rw3wf5tzpm2m5fl2z3mm8msa3xk8nv2y32gmzlhwjved980mcmkgq83u9wafq9n4w28amnmwzujgqpmapcr3","expected_msatoshi":"10000","expected_description":"Bitcoin Outlet order #7082"}
response=$(ln_pay "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_listpays)
# GET http://192.168.111.152:8080/ln_listpays
@@ -683,8 +637,7 @@ main() {
fi
response=$(ln_listpays "${bolt11}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_paystatus)
# GET http://192.168.111.152:8080/ln_paystatus
@@ -700,15 +653,13 @@ main() {
fi
response=$(ln_paystatus "${bolt11}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_newaddr)
# GET http://192.168.111.152:8080/ln_newaddr
response=$(ln_newaddr)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_connectfund)
# POST http://192.168.111.152:8080/ln_connectfund
@@ -716,64 +667,50 @@ main() {
# curl -H "Content-Type: application/json" -d '{"peer":"nodeId@ip:port","msatoshi":"100000","callbackUrl":"https://callbackUrl/?channelReady=f3y2c3cvm4uzg2gq"}' proxy:8888/ln_connectfund
response=$(ln_connectfund "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_getinvoice)
# GET http://192.168.111.152:8080/ln_getinvoice/label
# GET http://192.168.111.152:8080/ln_getinvoice/koNCcrSvhX3dmyFhW
response=$(ln_getinvoice "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_delinvoice)
# GET http://192.168.111.152:8080/ln_delinvoice/label
# GET http://192.168.111.152:8080/ln_delinvoice/koNCcrSvhX3dmyFhW
response=$(ln_delinvoice "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_decodebolt11)
# GET http://192.168.111.152:8080/ln_decodebolt11/bolt11
# GET http://192.168.111.152:8080/ln_decodebolt11/lntb1pdca82tpp5gv8mn5jqlj6xztpnt4r472zcyrwf3y2c3cvm4uzg2gqcnj90f83qdp2gf5hgcm0d9hzqnm4w3kx2apqdaexgetjyq3nwvpcxgcqp2g3d86wwdfvyxcz7kce7d3n26d2rw3wf5tzpm2m5fl2z3mm8msa3xk8nv2y32gmzlhwjved980mcmkgq83u9wafq9n4w28amnmwzujgqpmapcr3
response=$(ln_decodebolt11 "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_listpeers)
# GET http://192.168.111.152:8080/ln_listpeers
response=$(ln_listpeers)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_listfunds)
# GET http://192.168.111.152:8080/ln_listfunds
response=$(ln_listfunds)
response_to_client "${response}" ${?}
break
returncode=$?
;;
# ln_listpays)
# # GET http://192.168.111.152:8080/ln_listpays
# response=$(ln_listpays)
# response_to_client "${response}" ${?}
# break
# ;;
ln_getroute)
# GET http://192.168.111.152:8080/ln_getroute/<node_id>/<msatoshi>/<riskfactor>
response=$(ln_getroute "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)" "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4)" "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f5)")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ln_withdraw)
# POST http://192.168.111.152:8080/ln_withdraw
# BODY {"destination":"segwitAddress","satoshi":"100000","feerate":0,all: false}
response=$(ln_withdraw "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ots_stamp)
# POST http://192.168.111.152:8080/ots_stamp
@@ -782,15 +719,13 @@ main() {
# curl -v -d "{\"hash\":\"a6ea81a46fec3d02d40815b8667b388351edecedc1cc9f97aab55b566db7aac8\"}" localhost:8888/ots_stamp
response=$(serve_ots_stamp "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ots_backoffice)
# curl (GET) http://192.168.111.152:8080/ots_upgradeandcallback
response=$(serve_ots_backoffice)
response_to_client "${response}" ${?}
break
returncode=$?
;;
ots_getfile)
# curl (GET) http://192.168.111.152:8080/ots_getfile/1ddfb769eb0b8876bc570e25580e6a53afcf973362ee1ee4b54a807da2e5eed7
@@ -807,8 +742,7 @@ main() {
# curl -v -d "{\"hash\":\"a6ea81a46fec3d02d40815b8667b388351edecedc1cc9f97aab55b566db7aac8\",\"base64otsfile\":\"$(cat a6ea81a46fec3d02d40815b8667b388351edecedc1cc9f97aab55b566db7aac8.ots | base64 | tr -d '\n')\"}" localhost:8888/ots_verify
response=$(serve_ots_verify "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
ots_info)
# POST http://192.168.111.152:8080/ots_info
@@ -821,10 +755,15 @@ main() {
# curl -v -d "{\"hash\":\"a6ea81a46fec3d02d40815b8667b388351edecedc1cc9f97aab55b566db7aac8\",\"base64otsfile\":\"$(cat a6ea81a46fec3d02d40815b8667b388351edecedc1cc9f97aab55b566db7aac8.ots | base64 | tr -d '\n')\"}" localhost:8888/ots_info
response=$(serve_ots_info "${line}")
response_to_client "${response}" ${?}
break
returncode=$?
;;
*)
response='{"error": {"code": -32601, "message": "Method not found"}, "id": "1"}'
returncode=1
;;
esac
response=$(echo "${response}" | jq -Mc)
response_to_client "${response}" ${returncode}
break
fi
done

View File

@@ -3,6 +3,35 @@
. ./trace.sh
sql() {
trace "Entering sql()..."
local select_id=${2}
local response
local inserted_id
trace "[sql] psql -qAtX -h postgres -U cyphernode -c \"${1}\""
response=$(psql -qAtX -h postgres -U cyphernode -c "${1}")
returncode=$?
trace_rc ${returncode}
if [ -n "${select_id}" ]; then
if [ "${returncode}" -eq "0" ]; then
inserted_id=$(echo "${response}" | cut -d ' ' -f1)
else
trace "[sql] psql -qAtX -h postgres -U cyphernode -c \"${select_id}\""
inserted_id=$(psql -qAtX -h postgres -U cyphernode -c "${select_id}")
returncode=$?
trace_rc ${returncode}
fi
echo -n "${inserted_id}"
else
echo -n "${response}"
fi
return ${returncode}
}
sql_sqlite() {
trace "sqlite3 -cmd \".timeout 40000\" ${DB_FILE} \"${1}\""
sqlite3 -cmd ".timeout 40000" ${DB_FILE} "${1}"
@@ -14,16 +43,3 @@ sql() {
return $?
}
sql_rawtx() {
trace "sqlite3 -cmd \".timeout 40000\" ${DB_FILE}_rawtx \"${1}\""
sqlite3 -cmd ".timeout 40000" ${DB_FILE}_rawtx "${1}"
if [ "$?" -ne 0 ]; then
# SQL didn't work, let's retry to be sure...
trace "SQL didn't work, let's retry..."
sqlite3 -cmd ".timeout 40000" ${DB_FILE}_rawtx "${1}"
fi
return $?
}

View File

@@ -33,7 +33,6 @@ createCurlConfig() {
if [ ! -e ${DB_FILE} ]; then
echo "DB not found, creating..."
cat cyphernode.sql | sqlite3 $DB_FILE
cat rawtx.sql | sqlite3 ${DB_FILE}_rawtx
else
echo "DB found, migrating..."
for script in sqlmigrate*.sh; do
@@ -42,7 +41,6 @@ else
fi
chmod 0600 $DB_FILE
chmod 0600 ${DB_FILE}_rawtx
createCurlConfig ${WATCHER_BTC_NODE_RPC_CFG} ${WATCHER_BTC_NODE_RPC_USER}
createCurlConfig ${SPENDER_BTC_NODE_RPC_CFG} ${SPENDER_BTC_NODE_RPC_USER}

View File

@@ -16,10 +16,10 @@ unwatchrequest() {
# Let's lowercase bech32 addresses
address=$(lowercase_if_bech32 "${address}")
trace "[unwatchrequest] Unwatch request id ${watchid} on address ${address} with url0conf ${unconfirmedCallbackURL} and url1conf ${confirmedCallbackURL}"
trace "[unwatchrequest] Unwatch request id ${watchid} on address \"${address}\" with url0conf \"${unconfirmedCallbackURL}\" and url1conf \"${confirmedCallbackURL}\""
if [ "${watchid}" != "null" ]; then
sql "UPDATE watching SET watching=0 WHERE id=${watchid}"
sql "UPDATE watching SET watching=false WHERE id=${watchid}"
returncode=$?
trace_rc ${returncode}
@@ -35,11 +35,11 @@ unwatchrequest() {
cb1_where=" AND callback1conf='${confirmedCallbackURL}'"
fi
sql "UPDATE watching SET watching=0 WHERE address='${address}'${cb0_where}${cb1_where}"
sql "UPDATE watching SET watching=false WHERE address='${address}'${cb0_where}${cb1_where}"
returncode=$?
trace_rc ${returncode}
data="{\"event\":\"unwatch\",\"address\":\"${address}\",\"unconfirmedCallbackURL\":${unconfirmedCallbackURL},\"confirmedCallbackURL\":${confirmedCallbackURL}}"
data="{\"event\":\"unwatch\",\"address\":\"${address}\",\"unconfirmedCallbackURL\":\"${unconfirmedCallbackURL}\",\"confirmedCallbackURL\":\"${confirmedCallbackURL}\"}"
fi
trace "[unwatchrequest] responding=${data}"
@@ -52,20 +52,19 @@ unwatchrequest() {
unwatchpub32request() {
trace "Entering unwatchpub32request()..."
# GET http://192.168.111.152:8080/unwatchxpubbyxpub/tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk
local request=${1}
local pub32=$(echo "${request}" | cut -d ' ' -f2 | cut -d '/' -f3)
local id
local returncode
trace "[unwatchpub32request] Unwatch pub32 ${pub32}"
id=$(sql "SELECT id FROM watching_by_pub32 WHERE pub32='${pub32}'")
trace "[unwatchpub32request] id: ${id}"
sql "UPDATE watching_by_pub32 SET watching=0 WHERE id=${id}"
sql "UPDATE watching w SET watching=false FROM watching_by_pub32 w32 WHERE w.watching_by_pub32_id=w32.id AND pub32='${pub32}'"
returncode=$?
trace_rc ${returncode}
sql "UPDATE watching SET watching=0 WHERE watching_by_pub32_id=\"${id}\""
sql "UPDATE watching_by_pub32 SET watching=false WHERE pub32='${pub32}'"
returncode=$?
trace_rc ${returncode}
@@ -86,16 +85,11 @@ unwatchpub32labelrequest() {
local returncode
trace "[unwatchpub32labelrequest] Unwatch xpub label ${label}"
id=$(sql "SELECT id FROM watching_by_pub32 WHERE label='${label}'")
returncode=$?
trace_rc ${returncode}
trace "[unwatchpub32labelrequest] id: ${id}"
sql "UPDATE watching_by_pub32 SET watching=0 WHERE id=${id}"
sql "UPDATE watching w SET watching=false FROM watching_by_pub32 w32 WHERE w.watching_by_pub32_id=w32.id AND w32.label='${label}'"
returncode=$?
trace_rc ${returncode}
sql "UPDATE watching SET watching=0 WHERE watching_by_pub32_id=\"${id}\""
sql "UPDATE watching_by_pub32 SET watching=false WHERE label='${label}'"
returncode=$?
trace_rc ${returncode}
@@ -112,23 +106,30 @@ unwatchtxidrequest() {
local watchid=${1}
local txid=${2}
local unconfirmedCallbackURL=${3}
local uc_pg uc_json
[ "${unconfirmedCallbackURL}" = "null" ] && uc_pg=" IS NULL" && uc_json="null" || uc_pg="='${unconfirmedCallbackURL}'" && uc_json="\"${unconfirmedCallbackURL}\""
local confirmedCallbackURL=${4}
local c_pg c_json
[ "${confirmedCallbackURL}" = "null" ] && c_pg=" IS NULL" && c_json="null" || c_pg="='${confirmedCallbackURL}'" && c_json="\"${confirmedCallbackURL}\""
local returncode
trace "[unwatchtxidrequest] Unwatch request id ${watchid} on txid ${txid} with url0conf ${unconfirmedCallbackURL} and url1conf ${confirmedCallbackURL}"
trace "[unwatchtxidrequest] Unwatch request id ${watchid} on txid \"${txid}\" with url0conf \"${unconfirmedCallbackURL}\" and url1conf \"${confirmedCallbackURL}\""
if [ "${watchid}" != "null" ]; then
sql "UPDATE watching_by_txid SET watching=0 WHERE id=${watchid}"
sql "UPDATE watching_by_txid SET watching=false WHERE id=${watchid}"
returncode=$?
trace_rc ${returncode}
data="{\"event\":\"unwatchtxid\",\"id\":${watchid}}"
else
sql "UPDATE watching_by_txid SET watching=0 WHERE txid='${txid}' AND callback0conf=${unconfirmedCallbackURL} AND callback1conf=${confirmedCallbackURL}"
sql "UPDATE watching_by_txid SET watching=false WHERE txid='${txid}' AND callback0conf${uc_pg} AND callback1conf${c_pg}"
returncode=$?
trace_rc ${returncode}
data="{\"event\":\"unwatchtxid\",\"txid\":\"${txid}\",\"unconfirmedCallbackURL\":${unconfirmedCallbackURL},\"confirmedCallbackURL\":${confirmedCallbackURL}}"
data="{\"event\":\"unwatchtxid\",\"txid\":\"${txid}\",\"unconfirmedCallbackURL\":${uc_json},\"confirmedCallbackURL\":${c_json}}"
fi
trace "[unwatchtxidrequest] responding=${data}"

View File

@@ -42,14 +42,14 @@ ln_waitanyinvoice() {
status=$(echo "${result}" | jq -r ".status")
paid_at=$(echo "${result}" | jq -r ".paid_at")
sql "UPDATE ln_invoice SET status=\"${status}\", pay_index=${pay_index}, msatoshi_received=${msatoshi_received}, paid_at=${paid_at} WHERE bolt11=\"${bolt11}\""
row=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE callback_url<>\"\" AND NOT calledback AND bolt11=\"${bolt11}\"")
sql "UPDATE ln_invoice SET status='${status}', pay_index=${pay_index}, msatoshi_received=${msatoshi_received}, paid_at=${paid_at} WHERE bolt11='${bolt11}'"
row=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE callback_url<>'' AND NOT calledback AND bolt11='${bolt11}'")
if [ -n "${row}" ]; then
ln_manage_callback ${row}
fi
sql "UPDATE cyphernode_props SET value="${pay_index}" WHERE property=\"pay_index\""
sql "UPDATE cyphernode_props SET value='${pay_index}' WHERE property='pay_index'"
fi
}

View File

@@ -42,7 +42,7 @@ spend() {
tx_raw_details=$(get_rawtransaction ${txid} | tr -d '\n')
# Amounts and fees are negative when spending so we absolute those fields
local tx_hash=$(echo "${tx_raw_details}" | jq '.result.hash')
local tx_hash=$(echo "${tx_raw_details}" | jq -r '.result.hash')
local tx_ts_firstseen=$(echo "${tx_details}" | jq '.result.timereceived')
local tx_amount=$(echo "${tx_details}" | jq '.result.amount | fabs' | awk '{ printf "%.8f", $0 }')
local tx_size=$(echo "${tx_raw_details}" | jq '.result.size')
@@ -50,9 +50,6 @@ spend() {
local tx_replaceable=$(echo "${tx_details}" | jq -r '.result."bip125-replaceable"')
tx_replaceable=$([ ${tx_replaceable} = "yes" ] && echo "true" || echo "false")
local fees=$(echo "${tx_details}" | jq '.result.fee | fabs' | awk '{ printf "%.8f", $0 }')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
echo "${tx_raw_details}" > spend-rawtx-${txid}-$$.blob
########################################################################################################
# Let's publish the event if needed
@@ -73,20 +70,17 @@ spend() {
########################################################################################################
# Let's insert the txid in our little DB -- then we'll already have it when receiving confirmation
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target})"
id_inserted=$(sql "INSERT INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target)"\
" VALUES ('${txid}', '${tx_hash}', 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target})"\
" RETURNING id" \
"SELECT id FROM tx WHERE txid='${txid}'")
trace_rc $?
sql_rawtx "INSERT OR IGNORE INTO rawtx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, conf_target, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${conf_target}, readfile('spend-rawtx-${txid}-$$.blob'))"
trace_rc $?
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
trace_rc $?
sql "INSERT OR IGNORE INTO recipient (address, amount, tx_id) VALUES (\"${address}\", ${amount}, ${id_inserted})"
sql "INSERT INTO recipient (address, amount, tx_id) VALUES ('${address}', ${amount}, ${id_inserted})"\
" ON CONFLICT DO NOTHING"
trace_rc $?
data="{\"status\":\"accepted\""
data="${data},\"txid\":\"${txid}\",\"hash\":${tx_hash},\"details\":{\"address\":\"${address}\",\"amount\":${amount},\"firstseen\":${tx_ts_firstseen},\"size\":${tx_size},\"vsize\":${tx_vsize},\"replaceable\":${tx_replaceable},\"fee\":${fees},\"subtractfeefromamount\":${subtractfeefromamount}}}"
# Delete the temp file containing the raw tx (see above)
rm spend-rawtx-${txid}-$$.blob
data="${data},\"txid\":\"${txid}\",\"hash\":\"${tx_hash}\",\"details\":{\"address\":\"${address}\",\"amount\":${amount},\"firstseen\":${tx_ts_firstseen},\"size\":${tx_size},\"vsize\":${tx_vsize},\"replaceable\":${tx_replaceable},\"fee\":${fees},\"subtractfeefromamount\":${subtractfeefromamount}}}"
else
local message=$(echo "${response}" | jq -e ".error.message")
data="{\"message\":${message}}"
@@ -222,7 +216,7 @@ getbalancebyxpublabel() {
trace "[getbalancebyxpublabel] label=${label}"
local xpub
xpub=$(sql "SELECT pub32 FROM watching_by_pub32 WHERE label=\"${label}\"")
xpub=$(sql "SELECT pub32 FROM watching_by_pub32 WHERE label='${label}'")
trace "[getbalancebyxpublabel] xpub=${xpub}"
getbalancebyxpub ${xpub} "getbalancebyxpublabel"

View File

@@ -11,11 +11,72 @@ watchrequest() {
local returncode
local request=${1}
local address=$(echo "${request}" | jq -r ".address")
local cb0conf_url=$(echo "${request}" | jq ".unconfirmedCallbackURL")
local cb1conf_url=$(echo "${request}" | jq ".confirmedCallbackURL")
local event_message=$(echo "${request}" | jq ".eventMessage")
local label=$(echo "${request}" | jq ".label")
local address address_pg
address=$(echo "${request}" | jq -re ".address")
if [ "$?" -ne "0" ]; then
# address not found or null
result='{"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"address required"}}'
trace "[watchrequest] address required"
trace "[watchrequest] responding=${result}"
echo "${result}"
return 1
else
address_pg="'${address}'"
fi
local cb0conf_url cb0conf_url_pg cb0conf_url_pg_where cb0conf_url_json
cb0conf_url=$(echo "${request}" | jq -re ".unconfirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# unconfirmedCallbackURL not found or null
cb0conf_url_json="null"
cb0conf_url_pg="null"
cb0conf_url_pg_where=" IS NULL"
else
cb0conf_url_json="\"${cb0conf_url}\""
cb0conf_url_pg="'${cb0conf_url}'"
cb0conf_url_pg_where="=${cb0conf_url_pg}"
fi
local cb1conf_url cb1conf_url_pg cb1conf_url_pg_where cb1conf_url_json
cb1conf_url=$(echo "${request}" | jq -re ".confirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# confirmedCallbackURL not found or null
cb1conf_url_json="null"
cb1conf_url_pg="null"
cb1conf_url_pg_where=" IS NULL"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
cb1conf_url_pg_where="=${cb1conf_url_pg}"
fi
local event_message event_message_pg event_message_json
event_message=$(echo "${request}" | jq -re ".eventMessage")
if [ "$?" -ne "0" ]; then
# eventMessage not found or null
event_message_json="null"
event_message_pg="null"
else
event_message_json="\"${event_message}\""
event_message_pg="'${event_message}'"
fi
local label label_pg label_json
label=$(echo "${request}" | jq -re ".label")
if [ "$?" -ne "0" ]; then
# label not found or null
label_json="null"
label_pg="null"
else
label_json="\"${label}\""
label_pg="'${label}'"
fi
local imported
local inserted
local id_inserted
@@ -24,23 +85,23 @@ watchrequest() {
# Let's lowercase bech32 addresses
address=$(lowercase_if_bech32 "${address}")
trace "[watchrequest] Watch request on address (\"${address}\"), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url}) with event_message=${event_message} and label=${label}"
trace "[watchrequest] Watch request on address (${address}), cb 0-conf (${cb0conf_url_json}), cb 1-conf (${cb1conf_url_json}) with event_message=${event_message_json} and label=${label_json}"
local isvalid
isvalid=$(validateaddress "${address}" | jq ".result.isvalid")
if [ "${isvalid}" != "true" ]; then
result="{
\"result\":null,
\"error\":{
\"code\":-5,
\"message\":\"Invalid address\",
\"data\":{
\"event\":\"watch\",
\"address\":\"${address}\",
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url},
\"label\":${label},
\"eventMessage\":${event_message}}}}"
result='{'\
'"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"Invalid address",'\
'"data":{'\
'"event":"watch",'\
'"address":'"${address}"','\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"label":'${label_json}','\
'"eventMessage":'${event_message_json}'}}}'
trace "[watchrequest] Invalid address"
trace "[watchrequest] responding=${result}"
@@ -53,21 +114,26 @@ watchrequest() {
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
imported=1
imported=true
else
imported=0
imported=false
fi
sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, event_message, label) VALUES (\"${address}\", 1, ${cb0conf_url}, ${cb1conf_url}, ${imported}, ${event_message}, ${label}) ON CONFLICT(address,callback0conf,callback1conf) DO UPDATE SET watching=1, event_message=${event_message}, calledback0conf=0, calledback1conf=0, label=${label}"
id_inserted=$(sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, event_message, label)"\
" VALUES (${address_pg}, true, ${cb0conf_url_pg}, ${cb1conf_url_pg}, ${imported}, ${event_message_pg}, ${label_pg})"\
" ON CONFLICT (address, COALESCE(callback0conf, ''), COALESCE(callback1conf, '')) DO"\
" UPDATE SET watching=true, event_message=${event_message_pg}, calledback0conf=false, calledback1conf=false, label=${label_pg}"\
" RETURNING id" \
"SELECT id FROM watching WHERE address=${address_pg} AND callback0conf${cb0conf_url_pg_where} AND callback1conf${cb1conf_url_pg_where}")
returncode=$?
trace_rc ${returncode}
trace "[watchrequest] id_inserted=${id_inserted}"
if [ "${returncode}" -eq 0 ]; then
inserted=1
id_inserted=$(sql "SELECT id FROM watching WHERE address='${address}' AND callback0conf=${cb0conf_url} AND callback1conf=${cb1conf_url}")
inserted=true
trace "[watchrequest] id_inserted: ${id_inserted}"
else
inserted=0
inserted=false
fi
local fees2blocks
@@ -83,19 +149,19 @@ watchrequest() {
fees144blocks=$(getestimatesmartfee 144)
trace_rc $?
result="{\"id\":\"${id_inserted}\",
\"event\":\"watch\",
\"imported\":${imported},
\"inserted\":${inserted},
\"address\":\"${address}\",
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url},
\"label\":${label},
\"estimatesmartfee2blocks\":${fees2blocks},
\"estimatesmartfee6blocks\":${fees6blocks},
\"estimatesmartfee36blocks\":${fees36blocks},
\"estimatesmartfee144blocks\":${fees144blocks},
\"eventMessage\":${event_message}}"
result='{"id":'${id_inserted}','\
'"event":"watch",'\
'"imported":'${imported}','\
'"inserted":'${inserted}','\
'"address":"'${address}'",'\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"label":'${label_json}','\
'"estimatesmartfee2blocks":'${fees2blocks}','\
'"estimatesmartfee6blocks":'${fees6blocks}','\
'"estimatesmartfee36blocks":'${fees36blocks}','\
'"estimatesmartfee144blocks":'${fees144blocks}','\
'"eventMessage":'${event_message_json}'}'
trace "[watchrequest] responding=${result}"
echo "${result}"
@@ -106,19 +172,56 @@ watchrequest() {
watchpub32request() {
trace "Entering watchpub32request()..."
# BODY {"label":"4421","pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/n","nstart":0,"unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
# Required:
# - "label"
# - "pub32"
# - "path"
# - "nstart"
local returncode
local request=${1}
local label=$(echo "${request}" | jq ".label")
local label=$(echo "${request}" | jq -er ".label")
if [ "$?" -ne "0" ]; then
# label not found or null
trace "[watchpub32request] label required"
echo '{"error":"label required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] label=${label}"
local pub32=$(echo "${request}" | jq ".pub32")
local pub32=$(echo "${request}" | jq -er ".pub32")
if [ "$?" -ne "0" ]; then
# pub32 not found or null
trace "[watchpub32request] pub32 required"
echo '{"error":"pub32 required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] pub32=${pub32}"
local path=$(echo "${request}" | jq ".path")
local path=$(echo "${request}" | jq -er ".path")
if [ "$?" -ne "0" ]; then
# path not found or null
trace "[watchpub32request] path required"
echo '{"error":"path required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] path=${path}"
local nstart=$(echo "${request}" | jq ".nstart")
local nstart=$(echo "${request}" | jq -er ".nstart")
if [ "$?" -ne "0" ]; then
# nstart not found or null
trace "[watchpub32request] nstart required"
echo '{"error":"nstart required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] nstart=${nstart}"
local cb0conf_url=$(echo "${request}" | jq ".unconfirmedCallbackURL")
local cb0conf_url=$(echo "${request}" | jq -r ".unconfirmedCallbackURL // empty")
trace "[watchpub32request] cb0conf_url=${cb0conf_url}"
local cb1conf_url=$(echo "${request}" | jq ".confirmedCallbackURL")
local cb1conf_url=$(echo "${request}" | jq -r ".confirmedCallbackURL // empty")
trace "[watchpub32request] cb1conf_url=${cb1conf_url}"
watchpub32 "${label}" "${pub32}" "${path}" "${nstart}" "${cb0conf_url}" "${cb1conf_url}"
@@ -131,21 +234,46 @@ watchpub32request() {
watchpub32() {
trace "Entering watchpub32()..."
# Expecting args without quotes
# label, pub32, path and nstart are required
# When cb0conf_url and cb1conf_url are empty, means null
local returncode
local label=${1}
trace "[watchpub32] label=${label}"
local label_pg="'${label}'"
trace "[watchpub32] label=${label}, label_pg=${label_pg}"
local pub32=${2}
trace "[watchpub32] pub32=${pub32}"
local pub32_pg="'${pub32}'"
trace "[watchpub32] pub32=${pub32}, pub32_pg=${pub32_pg}"
local path=${3}
trace "[watchpub32] path=${path}"
local path_pg="'${path}'"
trace "[watchpub32] path=${path}, path_pg=${path_pg}"
local nstart=${4}
trace "[watchpub32] nstart=${nstart}"
local last_n=$((${nstart}+${XPUB_DERIVATION_GAP}))
trace "[watchpub32] last_n=${last_n}"
local cb0conf_url=${5}
trace "[watchpub32] cb0conf_url=${cb0conf_url}"
local cb0conf_url_pg cb0conf_url_json
if [ -z "${cb0conf_url}" ]; then
# Empty url
cb0conf_url_json="null"
cb0conf_url_pg="null"
else
cb0conf_url_json="\"${cb0conf_url}\""
cb0conf_url_pg="'${cb0conf_url}'"
fi
trace "[watchpub32] cb0conf_url=${cb0conf_url}, cb0conf_url_pg=${cb0conf_url_pg}"
local cb1conf_url=${6}
trace "[watchpub32] cb1conf_url=${cb1conf_url}"
local cb1conf_url_pg cb1conf_url_json
if [ -z "${cb1conf_url}" ]; then
# Empty url
cb1conf_url_json="null"
cb1conf_url_pg="null"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
fi
trace "[watchpub32] cb1conf_url=${cb1conf_url}, cb1conf_url_pg=${cb1conf_url_pg}"
# upto_n is used when extending the watching window
local upto_n=${7}
@@ -156,7 +284,7 @@ watchpub32() {
local error_msg
local data
# Derive with pycoin...
# Derive with bitcoind...
# {"pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/25-30"}
if [ -n "${upto_n}" ]; then
# If upto_n provided, then we create from nstart to upto_n (instead of + GAP)
@@ -165,7 +293,7 @@ watchpub32() {
local subspath=$(echo -e $path | sed -En "s/n/${nstart}-${last_n}/p")
trace "[watchpub32] subspath=${subspath}"
local addresses
addresses=$(derivepubpath "{\"pub32\":${pub32},\"path\":${subspath}}")
addresses=$(derivepubpath '{"pub32":"'${pub32}'","path":"'${subspath}'"}')
returncode=$?
trace_rc ${returncode}
# trace "[watchpub32] addresses=${addresses}"
@@ -179,7 +307,7 @@ watchpub32() {
if [ "${returncode}" -eq 0 ]; then
# Importmulti in Bitcoin Core...
result=$(importmulti_rpc "${WATCHER_BTC_NODE_XPUB_WALLET}" ${pub32} "${addresses}")
result=$(importmulti_rpc "${WATCHER_BTC_NODE_XPUB_WALLET}" "${pub32}" "${addresses}")
returncode=$?
trace_rc ${returncode}
trace "[watchpub32] result=${result}"
@@ -187,29 +315,31 @@ watchpub32() {
if [ "${returncode}" -eq 0 ]; then
if [ -n "${upto_n}" ]; then
# Update existing row, we are extending the watching window
sql "UPDATE watching_by_pub32 set last_imported_n=${upto_n} WHERE pub32=${pub32}"
id_inserted=$(sql "UPDATE watching_by_pub32 set last_imported_n=${upto_n} WHERE pub32=${pub32_pg} RETURNING id")
returncode=$?
trace_rc ${returncode}
else
# Insert in our DB...
sql "INSERT INTO watching_by_pub32 (pub32, label, derivation_path, watching, callback0conf, callback1conf, last_imported_n) VALUES (${pub32}, ${label}, ${path}, 1, ${cb0conf_url}, ${cb1conf_url}, ${last_n})"
id_inserted=$(sql "INSERT INTO watching_by_pub32 (pub32, label, derivation_path, watching, callback0conf, callback1conf, last_imported_n)"\
" VALUES (${pub32_pg}, ${label_pg}, ${path_pg}, true, ${cb0conf_url_pg}, ${cb1conf_url_pg}, ${last_n})"\
" ON CONFLICT (pub32) DO"\
" UPDATE SET watching=true, label=${label_pg}, callback0conf=${cb0conf_url_pg}, callback1conf=${cb1conf_url_pg}, derivation_path=${path_pg}, last_imported_n=${last_n}"\
" RETURNING id" \
"SELECT id FROM watching_by_pub32 WHERE pub32=${pub32_pg}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -ne "0" ]; then
trace "[watchpub32] xpub or label already being watched, updating with new values based on supplied xpub..."
sql "UPDATE watching_by_pub32 SET watching=1, label=${label}, callback0conf=${cb0conf_url}, callback1conf=${cb1conf_url} WHERE pub32=${pub32}"
returncode=$?
trace_rc ${returncode}
fi
fi
if [ "${returncode}" -eq 0 ]; then
id_inserted=$(sql "SELECT id FROM watching_by_pub32 WHERE pub32=${pub32}")
trace "[watchpub32] id_inserted: ${id_inserted}"
addresses=$(echo ${addresses} | jq ".addresses[].address")
insert_watches "${addresses}" "${cb0conf_url}" "${cb1conf_url}" "${id_inserted}" "${nstart}"
addresses=$(echo ${addresses} | jq -r ".addresses[].address")
insert_watches "${addresses}" "${label}" "${cb0conf_url}" "${cb1conf_url}" "${id_inserted}" "${nstart}"
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -ne 0 ]; then
error_msg="Can't insert xpub watches in DB"
fi
else
error_msg="Can't insert xpub watcher in DB"
fi
@@ -224,25 +354,25 @@ watchpub32() {
fi
if [ -z "${error_msg}" ]; then
data="{\"id\":${id_inserted},
\"event\":\"watchxpub\",
\"pub32\":${pub32},
\"label\":${label},
\"path\":${path},
\"nstart\":${nstart},
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url}}"
data='{"id":'${id_inserted}','\
'"event":"watchxpub",'\
'"pub32":"'${pub32}'",'\
'"label":"'${label}'",'\
'"path":"'${path}'",'\
'"nstart":'${nstart}','\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}'}'
returncode=0
else
data="{\"error\":\"${error_msg}\",
\"event\":\"watchxpub\",
\"pub32\":${pub32},
\"label\":${label},
\"path\":${path},
\"nstart\":${nstart},
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url}}"
data='{"error":"'${error_msg}'",'\
'"event":"watchxpub",'\
'"pub32":"'${pub32}'",'
'"label":"'${label}'",'\
'"path":"'${path}'",'\
'"nstart":${nstart},'\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}'}'
returncode=1
fi
@@ -256,29 +386,54 @@ watchpub32() {
insert_watches() {
trace "Entering insert_watches()..."
# Expecting args without quotes
# When callback0conf and callback1conf are empty, means null
local addresses=${1}
local callback0conf=${2}
local callback1conf=${3}
local xpub_id=${4}
local nstart=${5}
local inserted_values=""
local label=${2}
local label_pg
if [ -z "${label}" ]; then
# Empty url
label_pg="null"
else
label_pg="'${label}'"
fi
local callback0conf=${3}
local callback0conf_pg
if [ -z "${callback0conf}" ]; then
# Empty url
callback0conf_pg="null"
else
callback0conf_pg="'${callback0conf}'"
fi
local callback1conf=${4}
local callback1conf_pg
if [ -z "${callback1conf}" ]; then
# Empty url
callback1conf_pg="null"
else
callback1conf_pg="'${callback1conf}'"
fi
local xpub_id=${5}
local nstart=${6}
local inserted_values
local IFS=$'\n'
for address in ${addresses}
do
# (address, watching, callback0conf, callback1conf, imported, watching_by_pub32_id)
# (address, label, watching, callback0conf, callback1conf, imported, watching_by_pub32_id)
if [ -n "${inserted_values}" ]; then
inserted_values="${inserted_values},"
fi
inserted_values="${inserted_values}(${address}, 1, ${callback0conf}, ${callback1conf}, 1"
if [ -n "${xpub_id}" ]; then
inserted_values="${inserted_values}, ${xpub_id}, ${nstart}"
nstart=$((${nstart} + 1))
fi
inserted_values="${inserted_values})"
inserted_values="${inserted_values}('${address}', ${label_pg}, true, ${callback0conf_pg}, ${callback1conf_pg}, true, ${xpub_id}, ${nstart})"
nstart=$((${nstart} + 1))
done
sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, watching_by_pub32_id, pub32_index) VALUES ${inserted_values} ON CONFLICT(address,callback0conf,callback1conf) DO UPDATE SET watching=1, event_message=${event_message}, calledback0conf=0, calledback1conf=0"
sql "INSERT INTO watching (address, label, watching, callback0conf, callback1conf, imported, watching_by_pub32_id, pub32_index)"\
" VALUES ${inserted_values}"\
" ON CONFLICT (address, COALESCE(callback0conf, ''), COALESCE(callback1conf, '')) DO"\
" UPDATE SET watching=true, calledback0conf=false, calledback1conf=false, label=${label_pg}"
returncode=$?
trace_rc ${returncode}
@@ -288,6 +443,8 @@ insert_watches() {
extend_watchers() {
trace "Entering extend_watchers()..."
# Expecting args without quotes
local watching_by_pub32_id=${1}
trace "[extend_watchers] watching_by_pub32_id=${watching_by_pub32_id}"
local pub32_index=${2}
@@ -297,7 +454,8 @@ extend_watchers() {
local last_imported_n
local row
row=$(sql "SELECT COALESCE('\"'||pub32||'\"', 'null'), COALESCE('\"'||label||'\"', 'null'), COALESCE('\"'||derivation_path||'\"', 'null'), COALESCE('\"'||callback0conf||'\"', 'null'), COALESCE('\"'||callback1conf||'\"', 'null'), last_imported_n FROM watching_by_pub32 WHERE id=${watching_by_pub32_id} AND watching")
# row=$(sql "SELECT COALESCE('\"'||pub32||'\"', 'null'), COALESCE('\"'||label||'\"', 'null'), COALESCE('\"'||derivation_path||'\"', 'null'), COALESCE('\"'||callback0conf||'\"', 'null'), COALESCE('\"'||callback1conf||'\"', 'null'), last_imported_n FROM watching_by_pub32 WHERE id=${watching_by_pub32_id} AND watching")
row=$(sql "SELECT pub32, label, derivation_path, callback0conf, callback1conf, last_imported_n FROM watching_by_pub32 WHERE id=${watching_by_pub32_id} AND watching")
returncode=$?
trace_rc ${returncode}
@@ -335,52 +493,88 @@ watchtxidrequest() {
trace "Entering watchtxidrequest()..."
local returncode
local result
local request=${1}
trace "[watchtxidrequest] request=${request}"
local txid=$(echo "${request}" | jq ".txid")
trace "[watchtxidrequest] txid=${txid}"
local cb1conf_url=$(echo "${request}" | jq ".confirmedCallbackURL")
trace "[watchtxidrequest] cb1conf_url=${cb1conf_url}"
local cbxconf_url=$(echo "${request}" | jq ".xconfCallbackURL")
trace "[watchtxidrequest] cbxconf_url=${cbxconf_url}"
local txid txid_pg txid_pg_where
txid=$(echo "${request}" | jq -re ".txid")
if [ "$?" -ne "0" ]; then
# txid not found or null
result='{"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"txid required"}}'
trace "[watchrequest] txid required"
trace "[watchrequest] responding=${result}"
echo "${result}"
return 1
else
txid_pg="'${address}'"
fi
trace "[watchtxidrequest] txid=${txid}, txid_pg=${txid_pg}"
local cb1conf_url cb1conf_url_pg cb1conf_url_pg_where cb1conf_url_json
cb1conf_url=$(echo "${request}" | jq -re ".confirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# cb1conf_url not found or null
cb1conf_url_json="null"
cb1conf_url_pg="null"
cb1conf_url_pg_where=" IS NULL"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
cb1conf_url_pg_where="=${cb1conf_url_pg}"
fi
trace "[watchtxidrequest] cb1conf_url=${cb1conf_url}, cb1conf_url_pg=${cb1conf_url_pg}, cb1conf_url_pg_where=${cb1conf_url_pg_where}, cb1conf_url_json=${cb1conf_url_json}"
local cbxconf_url cbxconf_url_pg cbxconf_url_pg_where
cbxconf_url=$(echo "${request}" | jq -e ".xconfCallbackURL")
if [ "$?" -ne "0" ]; then
# cbxconf_url not found or null
cbxconf_url_json="null"
cbxconf_url_pg="null"
cbxconf_url_pg_where=" IS NULL"
else
cbxconf_url_json="\"${cbxconf_url}\""
cbxconf_url_pg="'${cbxconf_url}'"
cbxconf_url_pg_where="=${cbxconf_url_pg}"
fi
trace "[watchtxidrequest] cbxconf_url=${cbxconf_url}, cbxconf_url_pg=${cbxconf_url_pg}, cbxconf_url_pg_where=${cbxconf_url_pg_where}, cbxconf_url_json=${cbxconf_url_json}"
local nbxconf=$(echo "${request}" | jq ".nbxconf")
trace "[watchtxidrequest] nbxconf=${nbxconf}"
local cb1cond
local cbxcond
local inserted
local id_inserted
local result
trace "[watchtxidrequest] Watch request on txid (${txid}), cb 1-conf (${cb1conf_url}) and cb x-conf (${cbxconf_url}) on ${nbxconf} confirmations."
sql "INSERT INTO watching_by_txid (txid, watching, callback1conf, callbackxconf, nbxconf) VALUES (${txid}, 1, ${cb1conf_url}, ${cbxconf_url}, ${nbxconf}) ON CONFLICT(txid, callback1conf, callbackxconf) DO UPDATE SET watching=1, nbxconf=${nbxconf}, calledback1conf=0, calledbackxconf=0"
id_inserted=$(sql "INSERT INTO watching_by_txid (txid, watching, callback1conf, callbackxconf, nbxconf)"\
" VALUES (${txid_pg}, true, ${cb1conf_url_pg}, ${cbxconf_url_pg}, ${nbxconf})"\
" ON CONFLICT (txid, COALESCE(callback1conf, ''), COALESCE(callbackxconf, '')) DO"\
" UPDATE SET watching=true, nbxconf=${nbxconf}, calledback1conf=false, calledbackxconf=false"\
" RETURNING id" \
"SELECT id FROM watching_by_txid WHERE txid=${txid_pg} AND callback1conf${cb1conf_url_pg_where} AND callbackxconf${cbxconf_url_pg_where}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
inserted=1
if [ "${cb1conf_url}" = "null" ]; then
cb1cond=" IS NULL"
else
cb1cond="=${cb1conf_url}"
fi
if [ "${cbxconf_url}" = "null" ]; then
cbxcond=" IS NULL"
else
cbxcond="=${cbxconf_url}"
fi
id_inserted=$(sql "SELECT id FROM watching_by_txid WHERE txid=${txid} AND callback1conf${cb1cond} AND callbackxconf${cbxcond}")
inserted=true
trace "[watchtxidrequest] id_inserted: ${id_inserted}"
else
inserted=0
inserted=false
id_inserted=null
fi
local data="{\"id\":${id_inserted},
\"event\":\"watchtxid\",
\"inserted\":${inserted},
\"txid\":${txid},
\"confirmedCallbackURL\":${cb1conf_url},
\"xconfCallbackURL\":${cbxconf_url},
\"nbxconf\":${nbxconf}}"
local data='{"id":'${id_inserted}','\
'"event":"watchtxid",'\
'"inserted":'${inserted}','\
'"txid":"'${txid}'",'\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"xconfCallbackURL":'${cbxconf_url_json}','\
'"nbxconf":'${nbxconf}'}'
trace "[watchtxidrequest] responding=${data}"
echo "${data}"

View File

@@ -1,5 +1,9 @@
#!/bin/sh
# This needs to be run in regtest
# This will mine n blocks. If n is not supplied, will mine 1 block.
# Mine
mine() {
local nbblocks=${1:-1}

View File

@@ -0,0 +1,15 @@
#!/bin/sh
date
callbackservername=${1:-"tests-manage-missed"}
callbackserverport=${2:-"1111"}
callbackserverport2=${3:-"1112"}
callbackserverport3=${4:-"1113"}
callbackserverport4=${5:-"1114"}
docker run --rm -d --network cyphernodeappsnet --name ${callbackservername} alpine sh -c "nc -vlkp${callbackserverport} -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'"
docker exec -d ${callbackservername} sh -c "nc -vlkp${callbackserverport2} -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'"
docker exec -d ${callbackservername} sh -c "nc -vlkp${callbackserverport3} -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'"
docker exec -d ${callbackservername} sh -c "nc -vlkp${callbackserverport4} -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'"

View File

@@ -2,6 +2,26 @@
# docker exec -it $(docker ps -q -f "name=proxy\.") ./tests/test-batching.sh
# This needs to be run in regtest
# This will test:
#
# - listbatchers
# - getbatcher
# - getbatchdetails
# - getnewaddress
# - addtobatch
# - batchspend
# - removefrombatch
# - createbatcher
#
# Notes:
# curl localhost:8888/listbatchers | jq
# curl -d '{}' localhost:8888/getbatcher | jq
# curl -d '{}' localhost:8888/getbatchdetails | jq
@@ -316,6 +336,10 @@ testbatching() {
# List batchers
# Remove from batch
# List batchers
echo
echo "Tests successful!"
}
wait_for_callbacks() {

View File

@@ -1,5 +1,17 @@
#!/bin/sh
# docker run -it --rm -it --name test-derive --network=cyphernodenet -v "$PWD/test-derive.sh:/test-derive.sh" alpine /test-derive.sh
# This will test:
#
# - deriveindex
# - derivepubpath
# - deriveindex_bitcoind
# - derivepubpath_bitcoind
#
# ...and it will compare performance between Pycoin et Bitcoin Core's address derivations...
#
test_derive() {
local address
local address1

View File

@@ -3,6 +3,16 @@
. ./colors.sh
. ./mine.sh
# This needs to be run in regtest
# This will test the missed watched transactions mechanisms by broadcasting
# transactions on watched addresses while the proxy is shut down...
#
# - getnewaddress
# - watch
# - executecallbacks
#
trace() {
if [ "${1}" -le "${TRACING}" ]; then
echo "$(date -u +%FT%TZ) ${2}" 1>&2
@@ -46,10 +56,11 @@ test_manage_missed_0_conf() {
trace 3 "[test_manage_missed_0_conf] response=${response}"
trace 3 "[test_manage_missed_0_conf] Shutting down the proxy..."
docker stop $(docker ps -q -f "name=proxy")
docker stop $(docker ps -q -f "name=proxy\.")
trace 3 "[test_manage_missed_0_conf] Sending coins to watched address while proxy is down..."
docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address} 0.0001
# txid1=$(exec_in_test_container curl -d '{"address":"'${address}'","amount":0.0001}' proxy:8888/spend | jq -r ".txid")
trace 3 "[test_manage_missed_0_conf] Sleeping for 10 seconds to let the proxy restart..."
sleep 10
@@ -87,12 +98,13 @@ test_manage_missed_1_conf() {
trace 3 "[test_manage_missed_1_conf] Sending coins to watched address while proxy is up..."
docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address} 0.0001
# txid1=$(exec_in_test_container curl -d '{"address":"'${address}'","amount":0.0001}' proxy:8888/spend | jq -r ".txid")
trace 3 "[test_manage_missed_1_conf] Sleeping for 10 seconds to let the 0-conf callbacks to happen..."
sleep 10
trace 3 "[test_manage_missed_1_conf] Shutting down the proxy..."
docker stop $(docker ps -q -f "name=proxy")
docker stop $(docker ps -q -f "name=proxy\.")
trace 3 "[test_manage_missed_1_conf] Mine a new block..."
mine
@@ -116,6 +128,7 @@ wait_for_callbacks() {
TRACING=3
stop_test_container
start_test_container
wait_for_callbacks

View File

@@ -0,0 +1,445 @@
#!/bin/sh
. ./colors.sh
. ./mine.sh
# This needs to be run in regtest
# This will test:
#
# - watchxpub
# - get_unused_addresses_by_watchlabel
# - derivepubpath_bitcoind
# - getactivexpubwatches
# - getactivewatchesbyxpub
# - getactivewatchesbylabel
# - spend
# - get_txns_by_watchlabel
# - unwatchxpubbyxpub
# - unwatchxpubbylabel
#
trace() {
if [ "${1}" -le "${TRACING}" ]; then
echo "$(date -u +%FT%TZ) ${2}" 1>&2
fi
}
start_test_container() {
docker run -d --rm -it --name tests-watch-pub32 --network=cyphernodenet alpine
}
stop_test_container() {
trace 1 "\n\n[test_watch_pub32] ${BCyan}Stopping existing containers if they are running...${Color_Off}\n"
docker stop tests-watch-pub32
docker stop tests-watch-pub32-cb
}
exec_in_test_container() {
docker exec -it tests-watch-pub32 "$@"
}
test_watch_pub32() {
# Watch an xpub
# 1. Call watchxpub with xpub1 label1 with url1 as callback
# 2. Call watchxpub with xpub2 label2 with url2 as callback
# 3. Call get_unused_addresses_by_watchlabel with label1 /10, take last as address1, save index1
# 4. Call get_unused_addresses_by_watchlabel with label2 /10, take last as address2, save index2
# 5. Call derivepubpath_bitcoind with xpub1 path 0/index1, compare with address1
# 6. Call derivepubpath_bitcoind with xpub2 path 0/index2, compare with address2
# 7. Call getactivexpubwatches
# 8. Call getactivewatchesbyxpub with xpub1, compare index1'th address with address1
# 9. Call getactivewatchesbyxpub with xpub2, compare index2'th address with address2
# 10. Call getactivewatchesbylabel with label1, compare index1'th address with address1
# 11. Call getactivewatchesbylabel with label2, compare index2'th address with address2
# 12. Send coins to address1, wait for callback
# 13. Send coins to address2, wait for callback
# 14. Call get_txns_by_watchlabel for label1, search sent tx
# 15. Call get_txns_by_watchlabel for label2, search sent tx
# 16. Call get_unused_addresses_by_watchlabel with label1 /10, check address1 is NOT there
# 17. Call get_unused_addresses_by_watchlabel with label2 /10, check address2 is NOT there
# 18. Call getactivexpubwatches
# 19. Call getactivewatchesbyxpub with xpub1, last n should be 10 more (index1 + 100)
# 20. Call getactivewatchesbyxpub with xpub2, last n should be 10 more (index2 + 100)
# 21. Call unwatchxpubbyxpub with xpub1
# 22. Call unwatchxpubbylabel with label2
# 23. Call getactivewatchesbyxpub with xpub1, should be empty
# 24. Call getactivewatchesbyxpub with xpub2, should be empty
local xpub1="upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb"
local xpub2="tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk"
local label1="label$RANDOM"
local label2="label$RANDOM"
local path1="0/n"
local path2="0/n"
local path_a1
local path_a2
local callbackurl0conf1="http://${callbackservername}:1111/callbackurl0conf1"
local callbackurl1conf1="http://${callbackservername}:1112/callbackurl1conf1"
local callbackurl0conf2="http://${callbackservername}:1113/callbackurl0conf2"
local callbackurl1conf2="http://${callbackservername}:1114/callbackurl1conf2"
local address
local address1
local address2
local last_imported_n1
local last_imported_n2
local last_imported_n1_x
local last_imported_n2_x
local index
local index1
local index2
local txid
local txid1
local txid2
local data
local response
trace 1 "\n\n[test_watch_pub32] ${BCyan}Let's test \"watch by xpub\" features!...${Color_Off}\n"
# 1. Call watchxpub with xpub1 label1 with url1 as callback
trace 2 "\n\n[test_watch_pub32] ${BCyan}1. watchxpub 1...${Color_Off}\n"
data='{"label":"'${label1}'","pub32":"'${xpub1}'","path":"'${path1}'","nstart":0,"unconfirmedCallbackURL":"'${callbackurl0conf1}'","confirmedCallbackURL":"'${callbackurl1conf1}'"}'
trace 3 "[test_watch_pub32] data=${data}"
response=$(exec_in_test_container curl -d "${data}" proxy:8888/watchxpub)
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq -re ".label")
if [ "${label1}" != "${data}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 1. watchxpub 1 failed! ${Color_Off}\n"
return 10
fi
# 2. Call watchxpub with xpub2 label2 with url2 as callback
trace 2 "\n\n[test_watch_pub32] ${BCyan}2. watchxpub 2...${Color_Off}\n"
data='{"label":"'${label2}'","pub32":"'${xpub2}'","path":"'${path2}'","nstart":0,"unconfirmedCallbackURL":"'${callbackurl0conf2}'","confirmedCallbackURL":"'${callbackurl1conf2}'"}'
trace 3 "[test_watch_pub32] data=${data}"
response=$(exec_in_test_container curl -d "${data}" proxy:8888/watchxpub)
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq -re ".label")
if [ "${label2}" != "${data}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 2. watchxpub 2 failed! ${Color_Off}\n"
return 20
fi
# 3. Call get_unused_addresses_by_watchlabel with label1 /10, take last as address1, save index1
trace 2 "\n\n[test_watch_pub32] ${BCyan}3. Call get_unused_addresses_by_watchlabel with label1 /10...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_unused_addresses_by_watchlabel/${label1}/10)
trace 3 "[test_watch_pub32] response=${response}"
address1=$(echo "${response}" | jq -r ".label_unused_addresses[9] | .address")
trace 3 "[test_watch_pub32] address1=${address1}"
index1=$(echo "${response}" | jq -r ".label_unused_addresses[9] | .address_pub32_index")
trace 3 "[test_watch_pub32] index1=${index1}"
if [ "${address1}" = "null" ] || [ "${index1}" = "null" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 3. Call get_unused_addresses_by_watchlabel with label1 /10! ${Color_Off}\n"
return 87
fi
# 4. Call get_unused_addresses_by_watchlabel with label2 /10, take last as address2, save index2
trace 2 "\n\n[test_watch_pub32] ${BCyan}4. Call get_unused_addresses_by_watchlabel with label2 /10...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_unused_addresses_by_watchlabel/${label2}/10)
trace 3 "[test_watch_pub32] response=${response}"
address2=$(echo "${response}" | jq -r ".label_unused_addresses[9] | .address")
trace 3 "[test_watch_pub32] address2=${address2}"
index2=$(echo "${response}" | jq -r ".label_unused_addresses[9] | .address_pub32_index")
trace 3 "[test_watch_pub32] index2=${index2}"
if [ "${address2}" = "null" ] || [ "${index2}" = "null" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 4. Call get_unused_addresses_by_watchlabel with label2 /10! ${Color_Off}\n"
return 87
fi
# 5. Call derivepubpath_bitcoind with xpub1 path 0/index1, compare with address1
trace 2 "\n\n[test_watch_pub32] ${BCyan}5. Call derivepubpath_bitcoind with xpub1 path 0/index1...${Color_Off}\n"
path_a1=$(echo "${path1}" | sed -En "s/n/${index1}/p")
data='{"pub32":"'${xpub1}'","path":"'${path_a1}'"}'
trace 3 "[test_watch_pub32] data=${data}"
response=$(exec_in_test_container curl -d "${data}" proxy:8888/derivepubpath_bitcoind)
trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".[0]")
trace 3 "[test_watch_pub32] address=${address}"
if [ "${address}" != "${address1}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 5. Call derivepubpath_bitcoind with xpub1 path 0/index1! ${Color_Off}\n"
return 30
fi
# 6. Call derivepubpath_bitcoind with xpub2 path 0/index2, compare with address2
trace 2 "\n\n[test_watch_pub32] ${BCyan}6. Call derivepubpath_bitcoind with xpub2 path 0/index2...${Color_Off}\n"
path_a2=$(echo "${path2}" | sed -En "s/n/${index2}/p")
data='{"pub32":"'${xpub2}'","path":"'${path_a2}'"}'
trace 3 "[test_watch_pub32] data=${data}"
response=$(exec_in_test_container curl -d "${data}" proxy:8888/derivepubpath_bitcoind)
trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".[0]")
trace 3 "[test_watch_pub32] address=${address}"
if [ "${address}" != "${address2}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 6. Call derivepubpath_bitcoind with xpub2 path 0/index2! ${Color_Off}\n"
return 30
fi
# 7. Call getactivexpubwatches
trace 2 "\n\n[test_watch_pub32] ${BCyan}7. getactivexpubwatches...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivexpubwatches)
trace 3 "[test_watch_pub32] response=${response}"
last_imported_n1=$(echo "${response}" | jq -r ".watches | map(select(.pub32 == \"${xpub1}\"))[0] | .last_imported_n")
trace 3 "[test_watch_pub32] last_imported_n1=${last_imported_n1}"
last_imported_n2=$(echo "${response}" | jq -r ".watches | map(select(.pub32 == \"${xpub2}\"))[0] | .last_imported_n")
trace 3 "[test_watch_pub32] last_imported_n2=${last_imported_n2}"
if [ "${last_imported_n1}" -ne "100" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 7. \"${last_imported_n1}\" -ne \"100\"! ${Color_Off}\n"
return 50
fi
if [ "${last_imported_n2}" -ne "100" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 7. \"${last_imported_n2}\" -ne \"100\"! ${Color_Off}\n"
return 55
fi
# 8. Call getactivewatchesbyxpub with xpub1, compare index1'th address with address1
trace 2 "\n\n[test_watch_pub32] ${BCyan}8. Call getactivewatchesbyxpub with xpub1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub1})
# trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".watches | map(select(.pub32_index == ${index1}))[0] | .address")
trace 3 "[test_watch_pub32] ${index1}th address=${address}"
if [ "${address}" != "${address1}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 8. Call getactivewatchesbyxpub with xpub1: \"${address}\" != \"${address1}\"! ${Color_Off}\n"
return 60
fi
# Check if last_imported_n1 exists in watched list
index=$(echo "${response}" | jq -r "[.watches[].pub32_index] | index(${last_imported_n1})")
trace 3 "[test_watch_pub32] index=${index}"
if [ "${index}" != "100" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 8. Call getactivewatchesbyxpub with xpub1: \"${index}\" != \"100\"! ${Color_Off}\n"
return 65
fi
# 9. Call getactivewatchesbyxpub with xpub2, compare index2'th address with address2
trace 2 "\n\n[test_watch_pub32] ${BCyan}9. Call getactivewatchesbyxpub with xpub2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub2})
# trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".watches | map(select(.pub32_index == ${index2}))[0] | .address")
trace 3 "[test_watch_pub32] ${index2}th address=${address}"
if [ "${address}" != "${address2}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 9. Call getactivewatchesbyxpub with xpub2: \"${address}\" != \"${address2}\"! ${Color_Off}\n"
return 60
fi
# Check if last_imported_n1 exists in watched list
index=$(echo "${response}" | jq -r "[.watches[].pub32_index] | index(${last_imported_n2})")
trace 3 "[test_watch_pub32] index=${index}"
if [ "${index}" != "100" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 9. Call getactivewatchesbyxpub with xpub2: \"${index}\" != \"100\"! ${Color_Off}\n"
return 65
fi
# 10. Call getactivewatchesbylabel with label1, compare index1'th address with address1
trace 2 "\n\n[test_watch_pub32] ${BCyan}10. Call getactivewatchesbylabel with label1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbylabel/${label1})
# trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".watches | map(select(.pub32_index == ${index1}))[0] | .address")
trace 3 "[test_watch_pub32] ${index1}th address=${address}"
if [ "${address}" != "${address1}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 10. Call getactivewatchesbylabel with label1: \"${address}\" != \"${address1}\"! ${Color_Off}\n"
return 80
fi
# 11. Call getactivewatchesbylabel with label2, compare index2'th address with address2
trace 2 "\n\n[test_watch_pub32] ${BCyan}11. Call getactivewatchesbylabel with label2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbylabel/${label2})
# trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".watches | map(select(.pub32_index == ${index2}))[0] | .address")
trace 3 "[test_watch_pub32] ${index2}th address=${address}"
if [ "${address}" != "${address2}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 11. Call getactivewatchesbylabel with label2: \"${address}\" != \"${address2}\"! ${Color_Off}\n"
return 80
fi
# 12. Send coins to address1, wait for callback
trace 2 "\n\n[test_watch_pub32] ${BCyan}12. Send coins to address1...${Color_Off}\n"
start_callback_server 1111
# Let's use the bitcoin node directly to better simulate an external spend
txid1=$(docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address1} 0.0001 | tr -d "\r\n")
# txid1=$(exec_in_test_container curl -d '{"address":"'${address1}'","amount":0.001}' proxy:8888/spend | jq -r ".txid")
trace 3 "[test_watch_pub32] txid1=${txid1}"
trace 3 "[test_watch_pub32] Waiting for 0-conf callback on address1..."
wait
# 13. Send coins to address2, wait for callback
trace 2 "\n\n[test_watch_pub32] ${BCyan}13. Send coins to address2...${Color_Off}\n"
start_callback_server 1113
# Let's use the bitcoin node directly to better simulate an external spend
txid2=$(docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address2} 0.0001 | tr -d "\r\n")
# txid2=$(exec_in_test_container curl -d '{"address":"'${address2}'","amount":0.001}' proxy:8888/spend | jq -r ".txid")
trace 3 "[test_watch_pub32] txid2=${txid2}"
trace 3 "[test_watch_pub32] Waiting for 0-conf callback on address2..."
wait
# 14. Call get_txns_by_watchlabel for label1, search sent tx
trace 2 "\n\n[test_watch_pub32] ${BCyan}14. Call get_txns_by_watchlabel for label1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_txns_by_watchlabel/${label1}/1000)
trace 3 "[test_watch_pub32] response=${response}"
txid=$(echo "${response}" | jq -r ".label_txns | map(select(.txid == \"${txid1}\"))[0] | .txid")
trace 3 "[test_watch_pub32] txid searched=${txid}"
if [ "${txid}" != "${txid1}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 14. Call get_txns_by_watchlabel for label1: \"${txid}\" != \"${txid1}\"! ${Color_Off}\n"
return 88
fi
# 15. Call get_txns_by_watchlabel for label2, search sent tx
trace 2 "\n\n[test_watch_pub32] ${BCyan}15. Call get_txns_by_watchlabel for label2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_txns_by_watchlabel/${label2}/1000)
trace 3 "[test_watch_pub32] response=${response}"
txid=$(echo "${response}" | jq -r ".label_txns | map(select(.txid == \"${txid2}\"))[0] | .txid")
trace 3 "[test_watch_pub32] txid searched=${txid}"
if [ "${txid}" != "${txid2}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 15. Call get_txns_by_watchlabel for label2: \"${txid}\" != \"${txid2}\"! ${Color_Off}\n"
return 88
fi
# 16. Call get_unused_addresses_by_watchlabel with label1 /10, check address1 is NOT there
trace 2 "\n\n[test_watch_pub32] ${BCyan}16. Call get_unused_addresses_by_watchlabel with label1 /10...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_unused_addresses_by_watchlabel/${label1}/10)
trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".label_unused_addresses | map(select(.address == \"${address1}\"))[0] | .address")
trace 3 "[test_watch_pub32] ${index1}th address searched=${address}"
if [ "${address}" = "${address1}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 16. Call get_unused_addresses_by_watchlabel with label1 /10: \"${address}\" = \"${address1}\"! ${Color_Off}\n"
return 87
fi
# 17. Call get_unused_addresses_by_watchlabel with label2 /10, check address2 is NOT there
trace 2 "\n\n[test_watch_pub32] ${BCyan}17. Call get_unused_addresses_by_watchlabel with label2 /10...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/get_unused_addresses_by_watchlabel/${label2}/10)
trace 3 "[test_watch_pub32] response=${response}"
address=$(echo "${response}" | jq -r ".label_unused_addresses | map(select(.address == \"${address2}\"))[0] | .address")
trace 3 "[test_watch_pub32] ${index2}th address searched=${address}"
if [ "${address}" = "${address2}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 17. Call get_unused_addresses_by_watchlabel with label2 /10: \"${address}\" = \"${address2}\"! ${Color_Off}\n"
return 87
fi
# 18. Call getactivexpubwatches
trace 2 "\n\n[test_watch_pub32] ${BCyan}18. getactivexpubwatches...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivexpubwatches)
trace 3 "[test_watch_pub32] response=${response}"
last_imported_n1_x=$(echo "${response}" | jq -r ".watches | map(select(.pub32 == \"${xpub1}\"))[0] | .last_imported_n")
trace 3 "[test_watch_pub32] last_imported_n1_x=${last_imported_n1_x}"
last_imported_n2_x=$(echo "${response}" | jq -r ".watches | map(select(.pub32 == \"${xpub2}\"))[0] | .last_imported_n")
trace 3 "[test_watch_pub32] last_imported_n2_x=${last_imported_n2_x}"
if [ "${last_imported_n1_x}" -ne "$((100+${index1}))" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 18. Call getactivexpubwatches: \"${last_imported_n1_x}\" -ne \"$((100+${index1}))\"! ${Color_Off}\n"
return 90
fi
if [ "${last_imported_n2_x}" -ne "$((100+${index2}))" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 18. Call getactivexpubwatches \"${last_imported_n2_x}\" -ne \"$((100+${index2}))\"! ${Color_Off}\n"
return 95
fi
# 19. Call getactivewatchesbyxpub with xpub1, last n should be 10 more (index1 + 100)
trace 2 "\n\n[test_watch_pub32] ${BCyan}19. Call getactivewatchesbyxpub with xpub1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub1})
# trace 3 "[test_watch_pub32] response=${response}"
index=$(echo "${response}" | jq -r "[.watches[].pub32_index] | index(${last_imported_n1_x})")
trace 3 "[test_watch_pub32] index=${index}"
if [ "${index}" != "${last_imported_n1_x}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 19. Call getactivewatchesbyxpub with xpub1: \"${index}\" != \"${last_imported_n1_x}\"! ${Color_Off}\n"
return 100
fi
# 20. Call getactivewatchesbyxpub with xpub2, last n should be 10 more (index2 + 100)
trace 2 "\n\n[test_watch_pub32] ${BCyan}20. Call getactivewatchesbyxpub with xpub2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub2})
# trace 3 "[test_watch_pub32] response=${response}"
index=$(echo "${response}" | jq -r "[.watches[].pub32_index] | index(${last_imported_n2_x})")
trace 3 "[test_watch_pub32] index=${index}"
if [ "${index}" != "${last_imported_n2_x}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 20. Call getactivewatchesbyxpub with xpub2: \"${index}\" != \"${last_imported_n2_x}\"! ${Color_Off}\n"
return 100
fi
# 21. Call unwatchxpubbyxpub with xpub1
trace 2 "\n\n[test_watch_pub32] ${BCyan}21. unwatchxpubbyxpub with xpub1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/unwatchxpubbyxpub/${xpub1})
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq -re ".pub32")
if [ "${xpub1}" != "${data}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 21. Call unwatchxpubbyxpub with xpub1! ${Color_Off}\n"
return 120
fi
# 22. Call unwatchxpubbylabel with label2
trace 2 "\n\n[test_watch_pub32] ${BCyan}22. unwatchxpubbylabel with label2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/unwatchxpubbylabel/${label2})
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq -re ".label")
if [ "${label2}" != "${data}" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 22. Call unwatchxpubbylabel with label2 failed! ${Color_Off}\n"
return 130
fi
# 23. Call getactivewatchesbyxpub with xpub1, should be empty
trace 2 "\n\n[test_watch_pub32] ${BCyan}23. getactivewatchesbyxpub with xpub1...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub1})
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq ".watches | length")
if [ "${data}" -ne "0" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 23. getactivewatchesbyxpub xpub1 still watching! ${Color_Off}\n"
return 140
fi
# 24. Call getactivewatchesbyxpub with xpub2, should be empty
trace 2 "\n\n[test_watch_pub32] ${BCyan}24. getactivewatchesbyxpub with xpub2...${Color_Off}\n"
response=$(exec_in_test_container curl proxy:8888/getactivewatchesbyxpub/${xpub2})
trace 3 "[test_watch_pub32] response=${response}"
data=$(echo "${response}" | jq ".watches | length")
if [ "${data}" -ne "0" ]; then
trace 1 "\n\n[test_watch_pub32] ${On_Red}${BBlack} 24. getactivewatchesbyxpub xpub2 still watching! ${Color_Off}\n"
return 150
fi
trace 1 "\n\n[test_watch_pub32] ${On_IGreen}${BBlack} ALL GOOD! Yayyyy! ${Color_Off}\n"
}
start_callback_server() {
trace 1 "\n\n[start_callback_server] ${BCyan}Let's start a callback server!...${Color_Off}\n"
port=${1:-${callbackserverport}}
docker run --rm -t --name tests-watch-pub32-cb --network=cyphernodenet alpine sh -c "nc -vlp${port} -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; echo -en \"\\033[40m\\033[0;37m\" >&2 ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo -e \"\033[0m\" >&2'" &
# docker run --rm -it --name tests-watch-pub32-cb --network=cyphernodenet alpine sh -c "nc -vlkp1111 -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; echo -en \"\\033[40m\\033[0;37m\" >&2 ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo -e \"\033[0m\" >&2'"
}
TRACING=3
stop_test_container
start_test_container
callbackserverport="1111"
callbackservername="tests-watch-pub32-cb"
trace 1 "\n\n[test_watch_pub32] ${BCyan}Installing needed packages...${Color_Off}\n"
exec_in_test_container apk add curl
test_watch_pub32
trace 1 "\n\n[test_watch_pub32] ${BCyan}Tearing down...${Color_Off}\n"
wait
stop_test_container
trace 1 "\n\n[test_watch_pub32] ${BCyan}See ya!${Color_Off}\n"

View File

@@ -2,8 +2,28 @@
# . /mine.sh
# This should be run in regtest
# docker run -it --rm -it --name cn-tests --network=cyphernodenet -v "$PWD/mine.sh:/mine.sh" -v "$PWD/tests.sh:/tests.sh" -v "$PWD/tests-cb.sh:/tests-cb.sh" alpine /tests.sh
# This will test:
#
# - getbestblockhash
# - getbestblockinfo
# - getblockinfo
# - getnewaddress
# - getbalance
# - watch and callbacks
# - getactivewatches
# - unwatch
# - deriveindex
# - derivepubpath
# - spend
# - gettransaction
# - ln_getinfo
# - ln_newaddr
#
#
tests()
{
@@ -108,12 +128,12 @@ tests()
fi
local imported=$(echo "${response}" | jq ".imported" | tr -d '\"')
echo "imported=${imported}"
if [ "${imported}" != "1" ]; then
if [ "${imported}" != "true" ]; then
exit 30
fi
local inserted=$(echo "${response}" | jq ".inserted" | tr -d '\"')
echo "inserted=${inserted}"
if [ "${inserted}" != "1" ]; then
if [ "${inserted}" != "true" ]; then
exit 40
fi
local unconfirmedCallbackURL=$(echo "${response}" | jq ".unconfirmedCallbackURL" | tr -d '\"')