Elements testing (#1212)

This commit is contained in:
Kim Neunert 2021-06-16 15:33:35 +02:00 committed by GitHub
parent ed393b0e51
commit 2b9e02f2fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 697 additions and 180 deletions

1
.gitignore vendored
View file

@ -27,6 +27,7 @@ cypress/videos
cypress/screenshots
node_modules
btcd-conn.json
elmd-conn.json
tests/bitcoin
tests/bitcoin.binary
tests/bitcoin.compile

View file

@ -3,9 +3,11 @@
"spec_setup_wizard.js",
"spec_setup_tor.js",
"spec_empty_specter_home.js",
"spec_configures_nodes.js",
"spec_node_configured.js",
"spec_wallet_send.js",
"spec_wallet_utxo.js"
"spec_wallet_utxo.js",
"spec_elm_wallet_send.js"
],
"baseUrl": "http://localhost:25444"
}

View file

@ -0,0 +1,89 @@
describe('Configuring nodes', () => {
it('Configures the bitcoin-node in Specter', () => {
cy.viewport(1200,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.get('[href="/nodes/node/default/"]').first().click()
cy.get('#datadir-container').then(($datadir) => {
cy.log($datadir)
if (!Cypress.dom.isVisible($datadir)) {
cy.get('.slider').click()
}
})
cy.get('.slider').click()
cy.get('#username').clear()
cy.get('#username').type("bitcoin")
cy.get('#password').clear()
cy.get('#password').type("wrongPassword") // wrong Password
cy.get('#host').clear()
// This is hopefully correct for some longer time. If the connection fails, check the
// output of python3 -m cryptoadvance.specter bitcoind (in the CI-output !!) for a better ip-address.
// AUtomating that is probably simply not worth it.
cy.readFile('btcd-conn.json').then((conn) => {
cy.get('#host').type("http://"+conn["host"])
})
cy.get('#port').clear()
cy.get('#port').type("18443")
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(255, 0, 0)') // Credentials: red
cy.get('message-box').shadow().find('div.error > a').click()
cy.get('#password').clear()
cy.get('#password').type("secret")
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Credentials: green
cy.get(':nth-child(8) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Version green
cy.get(':nth-child(11) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Walletsenabled green
cy.get('message-box').shadow().find('div.main > a').click()
cy.get('[value="save"]').click()
})
it('Configures the elements-node in Specter', () => {
cy.viewport(1200,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.get('#btn_new_node').click()
cy.get('[href="/nodes/new_node/"]').click()
cy.get('#name').clear()
cy.get('#name').type("Elements Node")
cy.get('.slider').click()
cy.readFile('elmd-conn.json').then((conn) => {
cy.get('#username').clear()
cy.get('#username').type("liquid")
cy.get('#password').clear()
cy.get('#password').type("wrongPassword") // wrong Password
cy.get('#host').clear()
cy.get('#host').type("http://"+conn["host"])
cy.get('#port').clear()
cy.get('#port').type(conn["port"])
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(255, 0, 0)') // Credentials: red
cy.get('message-box').shadow().find('div.error > a').click()
cy.get('#password').clear()
cy.get('#password').type("secret")
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Credentials: green
cy.get(':nth-child(8) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Version green
cy.get(':nth-child(11) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Walletsenabled green
cy.get('message-box').shadow().find('div.main > a').click()
cy.get('[value="save"]').click()
})
})
it('Choose Bitcoin Core Node', () => {
// switch back to bitcoin-node
cy.viewport(1200,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.get('#default-select-node-form > .item > div').click()
})
})

View file

@ -0,0 +1,29 @@
describe('Send transactions from elements wallets', () => {
it('Creates a single sig elements hot wallet on specter and send transaction', () => {
cy.viewport(1200,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.get('#elements_node-select-node-form > .item > div').click()
// empty so far
cy.addHotDevice("Hot Elements Device 1","elements")
//cy.addHotWallet("Test Elements Hot Wallet","elements")
cy.addHotWallet("Test Elements Hot Wallet 1","elements")
cy.get('#btn_send').click()
cy.get('#address_0').type("el1qqdsywea5scrn7t9q83fd540pw447h0uae30pdp82rzgkl7yzvjz6gra9ls8qu6sslw4s0ck48we06zhqd6kwjy2quh69zwxwn")
cy.get('#label_0').type("Burn address")
cy.get('#send_max_0').click()
cy.get('#create_psbt_btn').click()
// To be done:
/* cy.get('body').contains("Paste signed transaction")
cy.get('#hot_elements_device_1_tx_sign_btn').click()
cy.get('#hot_elements_device_1_hot_sign_btn').click()
cy.get('#hot_enter_passphrase__submit').click()
cy.get('#broadcast_local_btn').click()
cy.get('#fullbalance_amount')
.should(($div) => {
const n = parseFloat($div.text())
expect(n).to.be.equals(0)
}) */
})
})

View file

@ -27,47 +27,12 @@ describe('Completely empty specter-home', () => {
cy.addDevice("Some Device")
})
it('Configures the node in Specter', () => {
it('Dummytest to enforce remove of device', () => {
cy.viewport(1200,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.get('[href="/nodes/node/default/"]').first().click()
cy.get('#datadir-container').then(($datadir) => {
cy.log($datadir)
if (!Cypress.dom.isVisible($datadir)) {
cy.get('.slider').click()
}
})
cy.get('.slider').click()
cy.get('#username').clear()
cy.get('#username').type("bitcoin")
cy.get('#password').clear()
cy.get('#password').type("wrongPassword") // wrong Password
cy.get('#host').clear()
// This is hopefully correct for some longer time. If the connection fails, check the
// output of python3 -m cryptoadvance.specter bitcoind (in the CI-output !!) for a better ip-address.
// AUtomating that is probably simply not worth it.
cy.readFile('btcd-conn.json').then((conn) => {
cy.get('#host').type("http://"+conn["host"])
})
cy.get('#port').clear()
cy.get('#port').type("18443")
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(255, 0, 0)') // Credentials: red
cy.get('message-box').shadow().find('div.error > a').click()
cy.get('#password').clear()
cy.get('#password').type("secret")
cy.get('[value="test"]').click()
cy.get(':nth-child(2) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // connectable: green
cy.get(':nth-child(5) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Credentials: green
cy.get(':nth-child(8) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Version green
cy.get(':nth-child(11) > button > div').should('have.css', 'color', 'rgb(0, 128, 0)') // Walletsenabled green
cy.get('message-box').shadow().find('div.main > a').click()
cy.get('[value="save"]').click()
})
})

View file

@ -20,7 +20,7 @@ describe('Node Configured', () => {
// Download PDF
// unfortunately this results in weird effects in cypress run
//cy.get('#pdf-wallet-download > img').click()
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.get('#btn_continue').click()
cy.get('#btn_transactions').click()

View file

@ -1,22 +1,25 @@
describe('Setup Tor and test connection', () => {
it('Setup Tor', () => {
cy.viewport(1200,660)
cy.visit('/settings/tor')
// Tor testing is flaky on cirrus
if (Cypress.env("CI")) {
it('Setup Tor', () => {
cy.viewport(1200,660)
cy.visit('/settings/tor')
cy.get('#setup-tor-button').click()
cy.get('#setup-tor-button').click()
cy.contains('Setup Tor daemon')
cy.get('#setup-tor-button').click()
cy.contains('Setup Tor daemon')
cy.get('#setup-tor-button').click()
cy.wait(60000)
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="test_tor"]').click({ timeout: 60000 })
cy.contains('Tor requests test completed successfully!')
cy.get('[value="stoptor"]').click()
cy.get('#tor-status-text').contains('Status: Down')
cy.get('[value="starttor"]').click()
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="uninstalltor"]').click()
cy.get('#setup-tor-button').click()
})
cy.wait(60000)
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="test_tor"]').click({ timeout: 60000 })
cy.contains('Tor requests test completed successfully!')
cy.get('[value="stoptor"]').click()
cy.get('#tor-status-text').contains('Status: Down')
cy.get('[value="starttor"]').click()
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="uninstalltor"]').click()
cy.get('#setup-tor-button').click()
})
}
})

View file

@ -1,77 +1,80 @@
describe('Setup wizard', () => {
it('Setup Bitcoin Core and Tor', () => {
cy.viewport(1200,660)
cy.visit('/about')
// Tor testing is flaky on cirrus
if (Cypress.env("CI")) {
it('Setup Bitcoin Core and Tor', () => {
cy.viewport(1200,660)
cy.visit('/about')
cy.get('[href="/setup/start/"]').click()
cy.contains('Specter Quickstart!')
cy.get('#start-setup-btn').click()
cy.contains('Setup Tor daemon')
cy.get('#setup-tor-button').click()
cy.contains('Would you like to setup a new Bitcoin node or connect to an existing one?', { timeout: 60000 })
cy.get('#setup-node-btn').click()
cy.contains('Setup Bitcoin Core')
cy.get('#setup-bitcoind-button').click()
cy.contains('Configure your node', { timeout: 60000 })
cy.get('#quicksync-switch').click()
cy.get('#setup-bitcoind-dir-button').click()
cy.contains('Setup Completed Successfully!', { timeout: 60000 })
cy.get('#finish-setup-btn').click()
cy.contains('Connect Specter with Bitcoin Core node.')
cy.get('[href="/setup/start/"]').click()
cy.contains('Specter Quickstart!')
cy.get('#start-setup-btn').click()
cy.contains('Setup Tor daemon')
cy.get('#setup-tor-button').click()
cy.contains('Would you like to setup a new Bitcoin node or connect to an existing one?', { timeout: 60000 })
cy.get('#setup-node-btn').click()
cy.contains('Setup Bitcoin Core')
cy.get('#setup-bitcoind-button').click()
cy.contains('Configure your node', { timeout: 60000 })
cy.get('#quicksync-switch').click()
cy.get('#setup-bitcoind-dir-button').click()
cy.contains('Setup Completed Successfully!', { timeout: 60000 })
cy.get('#finish-setup-btn').click()
cy.contains('Connect Specter with Bitcoin Core node.')
cy.get('#node-switch-icon').click()
cy.get('#btn_new_node').click()
cy.get('#setup-node-btn').click()
cy.get('#toggle_advanced').click()
cy.get('#select-network-btn').click()
cy.get('[href="/setup/bitcoind_datadir/signet"]').click()
cy.get('#setup-bitcoind-dir-button').click()
cy.contains('Specter Signet', { timeout: 60000 })
cy.get('#node-switch-icon').click()
cy.get('#specter_signet-select-node-form').click()
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('#active-node').click()
cy.get('#node-info-specter-chain').contains('signet')
cy.get('#page_overlay_popup_cancel_button').click()
cy.get('#node-switch-icon').click()
cy.get('#specter_bitcoin-select-node-form').click()
cy.get('#node-switch-icon').click()
cy.get('#btn_new_node').click()
cy.get('#setup-node-btn').click()
cy.get('#toggle_advanced').click()
cy.get('#select-network-btn').click()
cy.get('[href="/setup/bitcoind_datadir/signet"]').click()
cy.get('#setup-bitcoind-dir-button').click()
cy.contains('Specter Signet', { timeout: 60000 })
cy.get('#node-switch-icon').click()
cy.get('#specter_signet-select-node-form').click()
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('#active-node').click()
cy.get('#node-info-specter-chain').contains('signet')
cy.get('#page_overlay_popup_cancel_button').click()
cy.get('#node-switch-icon').click()
cy.get('#specter_bitcoin-select-node-form').click()
cy.get('#active-node').click()
cy.get('#node-info-specter-chain').contains('main')
cy.get('#active-node-settings-btn').click()
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('[value="stopbitcoind"]').click()
cy.wait(10000)
cy.reload()
cy.contains('Built in Bitcoin Node Status: Down')
cy.get('#active-node').click()
cy.get('#node-info-specter-chain').contains('main')
cy.get('#active-node-settings-btn').click()
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('[value="stopbitcoind"]').click()
cy.wait(10000)
cy.reload()
cy.contains('Built in Bitcoin Node Status: Down')
cy.get('[value="startbitcoind"]').click({force: true, timeout: 60000})
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('[name="remove_datadir"]').click()
cy.get('[value="forget"]').click()
cy.get('[value="startbitcoind"]').click({force: true, timeout: 60000})
cy.contains('Built in Bitcoin Node Status: Running')
cy.get('[name="remove_datadir"]').click()
cy.get('[value="forget"]').click()
cy.get('#node-switch-icon').click()
cy.get('#specter_signet-select-node-form').click()
cy.get('[name="remove_datadir"]').click()
cy.get('[value="uninstall_bitcoind"]').click()
cy.get('#node-switch-icon').click()
cy.get('#specter_signet-select-node-form').click()
cy.get('[name="remove_datadir"]').click()
cy.get('[value="uninstall_bitcoind"]').click()
cy.get('#active-node').click()
cy.get('#title').contains('Bitcoin Core')
cy.get('#active-node').click()
cy.get('#title').contains('Bitcoin Core')
cy.visit('/settings/tor')
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="stoptor"]').click()
cy.get('#tor-status-text').contains('Status: Down')
cy.get('[value="starttor"]').click()
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="test_tor"]').click({ timeout: 60000 })
cy.contains('Tor requests test completed successfully!')
cy.get('[value="uninstalltor"]').click()
cy.get('#setup-tor-button').click()
cy.visit('/settings/tor')
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="stoptor"]').click()
cy.get('#tor-status-text').contains('Status: Down')
cy.get('[value="starttor"]').click()
cy.get('#tor-status-text').contains('Status: Running')
cy.get('[value="test_tor"]').click({ timeout: 60000 })
cy.contains('Tor requests test completed successfully!')
cy.get('[value="uninstalltor"]').click()
cy.get('#setup-tor-button').click()
cy.visit('/about')
cy.visit('/about')
cy.get('[href="/setup/start/"]').click()
})
cy.get('[href="/setup/start/"]').click()
})
}
})

View file

@ -1,9 +1,9 @@
describe('Send transactions from wallets', () => {
it('Creates a single sig wallet on specter and send transaction', () => {
describe('Send transactions from bitcoin hotwallets', () => {
it('Creates a single sig bitcoin hotwallet on specter and send transaction', () => {
cy.viewport(1200,660)
cy.visit('/')
// empty so far
cy.addHotDevice("Hot Device 1")
cy.addHotDevice("Hot Device 1","bitcoin")
cy.get('body').then(($body) => {
if ($body.text().includes('Test Hot Wallet 1')) {
cy.get('#wallets_list > .item > svg').click()
@ -23,7 +23,7 @@ describe('Send transactions from wallets', () => {
//cy.get('#pdf-wallet-download > img').click()
cy.get('#btn_continue').click()
cy.get('#btn_transactions').click()
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.get('#wallets-loading-done-refresh', { timeout: 10000 }).click()
cy.get('#fullbalance_amount')
@ -70,7 +70,7 @@ describe('Send transactions from wallets', () => {
cy.get('body').contains("New wallet was created successfully!")
cy.get('#page_overlay_popup_cancel_button').click()
// Send transaction
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.get('#btn_transactions').click()
cy.get('#wallets-loading-done-refresh', { timeout: 10000 }).click()

View file

@ -1,11 +1,11 @@
describe('Send transactions from wallets', () => {
it('Freeze and unfreeze UTXO', () => {
cy.viewport(1200,660)
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.task("node:mine")
cy.task("btc:mine")
cy.wait(10000)
cy.visit('/wallets/wallet/test_hot_wallet_1/history')

View file

@ -19,12 +19,14 @@ const fs = require('fs');
module.exports = (on, config) => {
// `config` is the resolved Cypress config
const conn_file = fs.readFileSync('btcd-conn.json');
const conn = JSON.parse(conn_file);
const btc_conn_file = fs.readFileSync('btcd-conn.json');
const btc_conn = JSON.parse(btc_conn_file);
const elm_conn_file = fs.readFileSync('elmd-conn.json');
const elm_conn = JSON.parse(elm_conn_file);
on('task', {
'clear:specter-home': () => {
console.log('Removing and recreating Specter-data-folder %s', conn["specter_data_folder"])
const specter_home=conn["specter_data_folder"];
console.log('Removing and recreating Specter-data-folder %s', btc_conn["specter_data_folder"])
const specter_home=btc_conn["specter_data_folder"];
var rimraf = require("rimraf");
rimraf.sync(specter_home);
fs.mkdirSync(specter_home);
@ -36,11 +38,32 @@ module.exports = (on, config) => {
})
on('task', {
'node:mine': () => {
'delete:elements-hotwallet': (name) => {
console.log('connection details: %s', elm_conn)
const elements_data_dir=elm_conn["elements_data_dir"];
var rimraf = require("rimraf");
console.log('Removing all wallets in %s', elements_data_dir+"/elreg/wallets/specter123456_hotstorage")
rimraf.sync(elements_data_dir+"/elreg/wallets/specter123456_hotstorage");
return null
}
})
on('task', {
'btc:mine': () => {
// sending the bitcoind-process a signal SIGUSR1 (10) will cause mining towards all specter-wallets
// See the signal-handler in bitcoind
console.log('Sending SIGUSR1 to '+conn["pid"])
process.kill(parseInt(conn["pid"], 10), 'SIGUSR1');
console.log('Sending SIGUSR1 to '+btc_conn["pid"]+ ' to mine some btc')
process.kill(parseInt(btc_conn["pid"], 10), 'SIGUSR1');
return null
}
})
on('task', {
'elm:mine': () => {
// sending the bitcoind-process a signal SIGUSR1 (10) will cause mining towards all specter-wallets
// See the signal-handler in bitcoind
console.log('Sending SIGUSR1 to '+elm_conn["pid"] + ' to mine some lbtc')
process.kill(parseInt(elm_conn["pid"], 10), 'SIGUSR1');
return null
}
})

View file

@ -53,20 +53,55 @@ Cypress.Commands.add("addDevice", (name) => {
})
})
Cypress.Commands.add("addHotDevice", (name) => {
Cypress.Commands.add("addHotDevice", (name, node_type) => {
// node_type is either elements or bitcoin
cy.get('body').then(($body) => {
cy.task("delete:elements-hotwallet")
if ($body.text().includes(name)) {
cy.get('#devices_list > .item > div').click()
cy.get('#forget_device').click()
}
cy.get('#side-content').click()
cy.get('#btn_new_device').click()
// Creating a Device
cy.contains('Select Your Device Type')
cy.get('#bitcoincore_device_card').click()
cy.get(`#${node_type}core_device_card`).click()
cy.get('#submit-mnemonic').click()
cy.get('#device_name').type(name)
cy.get('#submit-keys').click()
cy.get('#devices_list > .item > div').contains(name)
})
})
Cypress.Commands.add("addHotWallet", (name, node_type, single_multi) => {
cy.get('body').then(($body) => {
if ($body.text().includes(name)) {
cy.contains(name).click()
cy.get('#btn_settings' ).click( {force: true})
cy.get('#advanced_settings_tab_btn').click()
cy.get('#delete_wallet').click()
}
cy.get('#side-content').click()
cy.get('#btn_new_wallet').click()
cy.get('[href="./simple/"]').click()
cy.get('#hot_elements_device_1').click()
cy.get('#wallet_name').type(name)
cy.get('#keysform > .centered').click()
cy.get('body').contains("New wallet was created successfully!")
// // Download PDF
// // unfortunately this results in weird effects in cypress run
// //cy.get('#pdf-wallet-download > img').click()
cy.get('#btn_continue').click()
cy.get('#btn_transactions').click()
cy.task("elm:mine")
cy.wait(4000)
cy.reload()
cy.get('#fullbalance_amount')
.should(($div) => {
const n = parseFloat($div.text())
expect(n).to.be.gt(0).and.be.lte(50)
})
})
})

View file

@ -287,7 +287,7 @@ def noded(
echo(f"user, password: { my_node.rpcconn.rpcuser }, secret")
echo(f" host, port: localhost, {my_node.rpcconn.rpcport}")
echo(
f" {node_impl}-cli: {node_impl}-cli -regtest -rpcport={my_node.rpcconn.rpcport} -rpcuser={ node_impl } -rpcpassword=secret getblockchaininfo "
f" {node_impl}-cli: {node_impl}-cli -regtest -rpcport={my_node.rpcconn.rpcport} -rpcuser={ my_node.rpcconn.rpcuser } -rpcpassword=secret getblockchaininfo "
)
if create_conn_json:
@ -296,6 +296,7 @@ def noded(
conn["specter_data_folder"] = config_obj[
"SPECTER_DATA_FOLDER"
] # e.g. cypress might want to know where we're mining to
conn[f"{node_impl}_data_dir"] = data_dir
conn_file = f"{'btcd' if node_impl == 'bitcoin' else 'elmd'}-conn.json"
with open(conn_file, "w") as file:
file.write(json.dumps(conn))

View file

@ -130,6 +130,10 @@ class CypressTestConfig(TestConfig):
"BTCD_REGTEST_DATA_DIR", "/tmp/specter_cypress_btc_regtest_plain_datadir"
)
BTCD_REGTEST_DATA_DIR = os.getenv(
"BTCD_REGTEST_DATA_DIR", "/tmp/specter_cypress_elm_regtest_plain_datadir"
)
class ProductionConfig(BaseConfig):
SECRET_KEY = secrets.token_urlsafe(16)

View file

@ -65,17 +65,16 @@ class InternalNode(Node):
"/testnet3"
):
self.datadir = os.path.join(self.datadir, "testnet3")
write_node(self, self.fullpath)
elif self.bitcoind_network == "regtest" and not self.datadir.endswith(
"/regtest"
):
self.datadir = os.path.join(self.datadir, "regtest")
write_node(self, self.fullpath)
elif self.bitcoind_network == "signet" and not self.datadir.endswith(
"/signet"
):
self.datadir = os.path.join(self.datadir, "signet")
write_node(self, self.fullpath)
logger.info(f"persisting {self} in __init__")
write_node(self, self.fullpath)
@classmethod
def from_json(cls, node_dict, manager, default_alias="", default_fullpath=""):

View file

@ -409,6 +409,9 @@ class LiquidRPC(BitcoinRPC):
logger.warn(f"Failed at unblinding transaction: {e}")
return obj
def __repr__(self) -> str:
return f"<LiquidRpc {self.url}>"
@classmethod
def from_bitcoin_rpc(cls, rpc):
"""Convert BitcoinRPC to LiquidRPC"""

View file

@ -111,6 +111,7 @@ class NodeManager:
setup_bitcoind_thread(specter, version)
for node in (node for node in self.nodes.values() if not node.external_node):
node.version = version
logger.info(f"persisting {node} in update_bitcoind_version")
write_node(node, node.fullpath)
for node_alias in stopped_nodes:
self.get_by_alias(node_alias).start(timeout=60)
@ -153,6 +154,7 @@ class NodeManager:
fullpath,
self,
)
logger.info(f"persisting {node} in add_node")
write_node(node, fullpath)
self.update() # reload files
logger.info("Added new node {}".format(node.alias))
@ -192,6 +194,7 @@ class NodeManager:
network,
self.internal_bitcoind_version,
)
logger.info(f"persisting {node} in add_internal_node")
write_node(node, fullpath)
self.update() # reload files
logger.info("Added new internal node {}".format(node.alias))

View file

@ -100,8 +100,13 @@ class WalletManager:
if self.chain is not None and self.data_folder is not None:
self.working_folder = os.path.join(self.data_folder, self.chain)
pathlib.Path(self.working_folder).mkdir(parents=True, exist_ok=True)
if rpc is not None:
if rpc is not None and rpc.test_connection():
self.rpc = rpc
else:
if rpc:
logger.error(
f"Prevented Trying to update wallet_Manager with broken {rpc}"
)
self.wallets_update_list = {}
if self.working_folder is not None and self.rpc is not None:
wallets_files = load_jsons(self.working_folder, key="name")
@ -286,6 +291,21 @@ class WalletManager:
def wallets_names(self):
return sorted(self.wallets.keys())
@property
def rpc(self):
if not hasattr(self, "_rpc"):
return None
else:
return self._rpc
@rpc.setter
def rpc(self, value):
if hasattr(self, "_rpc") and self._rpc != value:
logger.debug(f"Updating WalletManager rpc {self._rpc} with {value}")
if hasattr(self, "_rpc") and value == None:
logger.debug(f"Updating WalletManager rpc {self._rpc} with None")
self._rpc = value
def create_wallet(self, name, sigs_required, key_type, keys, devices):
try:
walletsindir = [

View file

@ -18,9 +18,11 @@ logger = logging.getLogger(__name__)
class Node:
"""A NodeManager represents the connection to a Bitcoin and/o Liquid Node (Full-) node.
"""A Node represents the connection to a Bitcoin and/or Liquid (Full-) node.
It can be created via Constructor or from_json, and mainly it can give you A
RPC-object to use the API.
On top of the RPC-connection it manages the stability of the rpc. It will only
persist healthy connections.
One or many Nodes are managed via the NodeManager
"""
@ -141,6 +143,8 @@ class Node:
rpc = BitcoinRPC(
**rpc_conf_arr[0], proxy_url=self.proxy_url, only_tor=self.only_tor
)
# autodetect won't result in any logging, even if None
return rpc
else:
# if autodetect is disabled and port is not defined
# we use default port 8332
@ -157,6 +161,7 @@ class Node:
)
if rpc == None:
logger.error(f"connection results to None in get_rpc")
return None
# check if it's liquid
try:
@ -166,7 +171,13 @@ class Node:
rpc = LiquidRPC.from_bitcoin_rpc(rpc)
except Exception as e:
return rpc
return rpc
if rpc.test_connection():
return rpc
else:
logger.debug(
f"connection {rpc} fails test_connection() returning None in get_rpc"
)
return None
def update_rpc(
self,
@ -178,44 +189,57 @@ class Node:
host=None,
protocol=None,
):
"""Changes the attributes of that node but only persists it, if the rpc.test_connection succeeds"""
update_rpc = self.rpc is None or not self.rpc.test_connection()
if autodetect is not None and self.autodetect != autodetect:
logger.debug(f"{self} updating autodetect to {autodetect}")
self.autodetect = autodetect
update_rpc = True
if datadir is not None and self.datadir != datadir:
logger.debug(f"{self} updating datadir to {datadir}")
self.datadir = datadir
update_rpc = True
if user is not None and self.user != user:
logger.debug(f"{self} updating user to {user}")
self.user = user
update_rpc = True
if password is not None and self.password != password:
logger.debug(f"{self} updating password to XXXXXXXX")
self.password = password
update_rpc = True
if port is not None and self.port != port:
logger.debug(f"{self} updating port to {port}")
self.port = port
update_rpc = True
if host is not None and self.host != host:
logger.debug(f"{self} updating host to {host}")
self.host = host
update_rpc = True
if protocol is not None and self.protocol != protocol:
logger.debug(f"{self} updating protocol to {protocol}")
self.protocol = protocol
update_rpc = True
if update_rpc:
self.rpc = self.get_rpc()
write_node(self, self.fullpath)
if self.rpc and self.rpc.test_connection():
logger.info(f"persisting {self} in update_rpc")
write_node(self, self.fullpath)
else:
logger.error(f"not persisting broken {self.rpc} in update_rpc")
self.check_info()
return False if not self.rpc else self.rpc.test_connection()
def rename(self, new_name):
logger.info("Renaming {}".format(self.alias))
self.name = new_name
logger.info(f"persisting {self} in rename")
write_node(self, self.fullpath)
self.manager.update()
def check_info(self):
self._is_configured = self.rpc is not None
self._is_running = False
if self._is_configured:
if self.rpc is not None and self.rpc.test_connection():
try:
res = [
r["result"]
@ -252,8 +276,28 @@ class Node:
self._info = {"chain": None}
self._network_info = {"subversion": "", "version": 999999}
self._network_parameters = get_network("main")
logger.error("Exception %s while check_info()" % e)
logger.error(f"connection {self.rpc} could not suceed check_info")
logger.exception("Exception %s while check_info()" % e)
else:
if self.rpc is None:
logger.error(f"connection of {self} is None in check_info")
elif not self.rpc.test_connection():
logger.error(
f"connection {self.rpc} failed test_connection in check_info:"
)
try:
self.rpc.multi(
[
("getblockchaininfo", None),
("getnetworkinfo", None),
("getmempoolinfo", None),
("uptime", None),
("getblockhash", 0),
("scantxoutset", "status", []),
]
)
except Exception as e:
logger.error(e)
self._info = {"chain": None}
self._network_info = {"subversion": "", "version": 999999}
@ -332,6 +376,9 @@ class Node:
def check_blockheight(self):
return self.info["blocks"] != self.rpc.getblockcount()
def is_liquid(self):
return is_liquid(self.chain)
@property
def is_running(self):
return self._is_running
@ -374,5 +421,19 @@ class Node:
return is_testnet(self.chain)
@property
def is_liquid(self):
return is_liquid(self.chain)
def rpc(self):
if not hasattr(self, "_rpc"):
return None
else:
return self._rpc
@rpc.setter
def rpc(self, value):
if hasattr(self, "_rpc") and self._rpc != value:
logger.debug(f"Updating {self}.rpc {self._rpc} with {value} (setter)")
if hasattr(self, "_rpc") and value == None:
logger.debug(f"Updating {self}.rpc {self._rpc} with None (setter)")
self._rpc = value
def __repr__(self) -> str:
return f"<{self.__class__.__name__} name={self.name} fullpath={self.fullpath}>"

View file

@ -17,7 +17,7 @@ from ..util.shell import which, get_last_lines_from_file
from ..rpc import RpcError
from ..rpc import BitcoinRPC
from ..helpers import load_jsons
from ..specter_error import ExtProcTimeoutException
from ..specter_error import ExtProcTimeoutException, SpecterError
from urllib3.exceptions import NewConnectionError, MaxRetryError
from requests.exceptions import ConnectionError
@ -119,7 +119,7 @@ class NodeController:
Specify a longer timeout for slower devices (e.g. Raspberry Pi)
"""
if self.check_existing() != None:
logger.warn(f"Reusing existing {self.node_impl}d")
logger.warning(f"Reusing existing {self.node_impl}d")
return self.rpcconn
logger.debug(f"Starting {self.node_impl}d")
@ -228,7 +228,9 @@ class NodeController:
logger.debug("balance:" + str(balance))
default_address = default_rpc.getnewaddress("")
if self.node_impl == "elements":
default_address = rpc.getaddressinfo(default_address)["unconfidential"]
default_address = default_rpc.getaddressinfo(default_address)[
"unconfidential"
]
if balance < amount:
rpc.generatetoaddress(102, default_address)
default_rpc.sendtoaddress(address, amount)
@ -373,7 +375,7 @@ class NodePlainController(NodeController):
)
time.sleep(0.2) # sleep 200ms (catch stdout of stupid errors)
if not self.node_proc.poll() is None:
raise Exception(f"Could not start node due to:" + self.get_debug_log())
raise SpecterError(f"Could not start node due to:" + self.get_debug_log())
logger.debug(
f"Running {self.node_impl}d-process with pid {self.node_proc.pid} in datadir {datadir}"
)

View file

@ -208,6 +208,7 @@ class RpcError(Exception):
self.error_code = error["error"]["code"]
self.error_msg = error["error"]["message"]
except Exception as e:
self.error_code = -99
self.error = "UNKNOWN API-ERROR:%s" % response.text
@ -352,6 +353,9 @@ class BitcoinRPC:
return fn
def __repr__(self) -> str:
return f"<BitcoinRpc {self.url}>"
if __name__ == "__main__":

View file

@ -175,6 +175,8 @@ class Specter:
try:
return self.node_manager.active_node
except SpecterError as e:
logger.error("SpecterError while accessing active_node")
logger.exception(e)
self.update_active_node(list(self.node_manager.nodes.values())[0].alias)
return self.node_manager.active_node
@ -629,6 +631,7 @@ class Specter:
"mainnet",
"0.20.1",
)
logger.info(f"persisting {internal_node} in migrate_old_node_format")
write_node(
internal_node,
os.path.join(
@ -654,6 +657,7 @@ class Specter:
os.path.join(os.path.join(self.data_folder, "nodes"), "default.json"),
self,
)
logger.info(f"persisting {node} in migrate_old_node_format")
write_node(
node,
os.path.join(os.path.join(self.data_folder, "nodes"), "default.json"),

View file

@ -252,7 +252,7 @@
<h2 class="subtitle">Delete wallet</h2>
<form action="." method="POST" class="padded" style="max-width: 500px">
<input type="hidden" class="csrf-token" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" name="action" value="deletewallet" class="btn danger centered" style="max-width: 160px;">Delete Wallet</button>
<button type="submit" id="delete_wallet" name="action" value="deletewallet" class="btn danger centered" style="max-width: 160px;">Delete Wallet</button>
</form>
</div>
</div>

View file

@ -16,6 +16,9 @@ from cryptoadvance.specter.process_controller.bitcoind_controller import (
from cryptoadvance.specter.process_controller.bitcoind_docker_controller import (
BitcoindDockerController,
)
from cryptoadvance.specter.process_controller.elementsd_controller import (
ElementsPlainController,
)
from cryptoadvance.specter.server import create_app, init_app
from cryptoadvance.specter.specter import Specter
@ -88,9 +91,41 @@ def instantiate_bitcoind_controller(docker, request, rpcport=18543, extra_args=[
return bitcoind_controller
def instantiate_elementsd_controller(request, rpcport=18643, extra_args=[]):
if os.path.isfile("tests/elements/src/elementsd"):
elementsd_controller = ElementsPlainController(
elementsd_path="tests/elements/src/elementsd", rpcport=rpcport
) # always prefer the self-compiled bitcoind if existing
elif os.path.isfile("tests/elements/bin/elementsd"):
elementsd_controller = ElementsPlainController(
elementsd_path="tests/elements/bin/elementsd", rpcport=rpcport
) # next take the self-installed binary if existing
else:
elementsd_controller = ElementsPlainController(
rpcport=rpcport
) # Alternatively take the one on the path for now
elementsd_controller.start_elementsd(
cleanup_at_exit=True, cleanup_hard=True, extra_args=extra_args
)
running_version = elementsd_controller.version()
requested_version = request.config.getoption("--elementsd-version")
assert running_version == requested_version, (
"Please make sure that the elementsd-version (%s) matches with the version in pytest.ini (%s)"
% (running_version, requested_version)
)
return elementsd_controller
@pytest.fixture(scope="module")
def bitcoin_regtest(docker, request):
return instantiate_bitcoind_controller(docker, request, extra_args=None)
bitcoind_regtest = instantiate_bitcoind_controller(docker, request, extra_args=None)
yield bitcoind_regtest
bitcoin_regtest: BitcoindPlainController.stop_bitcoind()
@pytest.fixture(scope="module")
def elements_elreg(request):
return instantiate_elementsd_controller(request, extra_args=None)
@pytest.fixture
@ -267,8 +302,6 @@ def device_manager(devices_filled_data_folder):
@pytest.fixture
def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
# Make sure that this folder never ever gets a reasonable non-testing use-case
data_folder = "./test_specter_data_3456778"
shutil.rmtree(data_folder, ignore_errors=True)
config = {
"rpc": {
"autodetect": False,
@ -287,7 +320,6 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
specter.check()
assert not specter.wallet_manager.working_folder is None
yield specter
shutil.rmtree(data_folder, ignore_errors=True)
@pytest.fixture

View file

@ -38,7 +38,7 @@ def test_elements(caplog):
print(result.exception, file=sys.stderr)
assert result.exit_code == 0
assert (
"elements-cli: elements-cli -regtest -rpcport=18555 -rpcuser=elements -rpcpassword=secret getblockchaininfo"
"elements-cli: elements-cli -regtest -rpcport=18555 -rpcuser=liquid -rpcpassword=secret getblockchaininfo"
in result.output
)
# This might take a lot of time because we're waiting on the bitcoind to terminate

View file

@ -0,0 +1,45 @@
from enum import auto
import tempfile
from cryptoadvance.specter.managers.node_manager import NodeManager
from cryptoadvance.specter.process_controller.bitcoind_controller import (
BitcoindPlainController,
)
from cryptoadvance.specter.process_controller.elementsd_controller import (
ElementsPlainController,
)
def test_NodeManager(
bitcoin_regtest: BitcoindPlainController, elements_elreg: ElementsPlainController
):
with tempfile.TemporaryDirectory("_some_datafolder_tmp") as data_folder:
print(f"data_folder={data_folder}")
nm = NodeManager(data_folder=data_folder)
nm.add_node(
"bitcoin_regtest",
False,
"",
bitcoin_regtest.rpcconn.rpcuser,
bitcoin_regtest.rpcconn.rpcpassword,
bitcoin_regtest.rpcconn.rpcport,
bitcoin_regtest.rpcconn._ipaddress,
"http",
external_node=True,
)
assert nm.nodes_names == ["Bitcoin Core", "bitcoin_regtest"]
nm.switch_node("bitcoin_regtest")
assert nm.active_node.get_rpc().getblockchaininfo()["chain"] == "regtest"
nm.add_node(
"elements_elreg",
False,
"",
elements_elreg.rpcconn.rpcuser,
elements_elreg.rpcconn.rpcpassword,
elements_elreg.rpcconn.rpcport,
elements_elreg.rpcconn._ipaddress,
"http",
external_node=True,
)
assert nm.nodes_names == ["Bitcoin Core", "bitcoin_regtest", "elements_elreg"]
nm.switch_node("elements_elreg")
assert nm.active_node.get_rpc().getblockchaininfo()["chain"] == "elreg"

125
tests/test_node.py Normal file
View file

@ -0,0 +1,125 @@
import os
import tempfile
from cryptoadvance.specter.node import Node
from mock import MagicMock, call, patch
def test_Node_btc(bitcoin_regtest):
with tempfile.TemporaryDirectory("_some_datafolder_tmp") as data_folder:
node = Node.from_json(
{
"autodetect": False,
"datadir": "",
"user": bitcoin_regtest.rpcconn.rpcuser,
"password": bitcoin_regtest.rpcconn.rpcpassword,
"port": bitcoin_regtest.rpcconn.rpcport,
"host": bitcoin_regtest.rpcconn.ipaddress,
"protocol": "http",
},
manager=MagicMock(),
default_fullpath=os.path.join(data_folder, "a_testfile.json"),
)
result = node.test_rpc()
assert result["tests"]["connectable"] == True
assert result["tests"]["recent_version"] == True
assert result["tests"]["credentials"] == True
assert result["tests"]["wallets"] == True
node_json = node.json
del node_json["fullpath"] # This is very different because of the tempfile
assert node_json == {
"name": "",
"alias": "",
"autodetect": False,
"datadir": "",
"user": "bitcoin",
"password": "secret",
"port": 18543,
"host": "localhost",
"protocol": "http",
"external_node": True, # 'fullpath': ''
}
rpc = node.get_rpc()
assert rpc.getblockchaininfo()["chain"] == "regtest"
node.rename("some_new_name")
assert node.json["name"] == "some_new_name"
node.check_info()
assert node.is_configured == True
assert node.is_running == True
assert node.is_testnet == True
print(f"info = {node.info}")
# something like:
# {'chain': 'regtest', 'blocks': 100, 'headers': 100, 'bestblockhash': '5277c27b3b5e8aad8e079a928e4675931ea1638a28c7a33bae4ef26425402259', 'difficulty': 4.656542373906925e-10, 'mediantime': 1622635607, 'verificationprogress': 1, 'initialblockdownload': False, 'chainwork': '00000000000000000000000000000000000000000000000000000000000000ca', 'size_on_disk': 30477, 'pruned': False, 'softforks': {'bip34': {'type': 'buried', 'active': False, 'height': 500}, 'bip66': {'type': 'buried', 'active': False, 'height': 1251}, 'bip65': {'type': 'buried', 'active': False, 'height': 1351}, 'csv': {'type': 'buried', 'active': False, 'height': 432}, 'segwit': {'type': 'buried', 'active': True, 'height': 0}, 'testdummy': {'type': 'bip9', 'bip9': {'status': 'defined', 'start_time': 0, 'timeout': 9223372036854775807, 'since': 0}, 'active': False}}, 'warnings': '', 'mempool_info': {'loaded': True, 'size': 0, 'bytes': 0, 'usage': 0, 'maxmempool': 300000000, 'mempoolminfee': 1e-05, 'minrelaytxfee': 1e-05}, 'uptime': 480, 'blockfilterindex': False, 'utxorescan': None}
assert node.info["chain"] == "regtest"
print(f"network_info = {node.network_info}")
# something like:
# {'version': 200100, 'subversion': '/Satoshi:0.20.1/', 'protocolversion': 70015, 'localservices': '0000000000000409', 'localservicesnames': ['NETWORK', 'WITNESS', 'NETWORK_LIMITED'], 'localrelay': True, 'timeoffset': 0, 'networkactive': True, 'connections': 0, 'networks': [{'name': 'ipv4', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'ipv6', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'onion', 'limited': True, 'reachable': False, 'proxy': '', 'proxy_randomize_credentials': False}], 'relayfee': 1e-05, 'incrementalfee': 1e-05, 'localaddresses': [{'address': '2a02:810d:d00:7700:233e:a7e:ded8:f2da', 'port': 18542, 'score': 1}, {'address': '2a02:810d:d00:7700:29ec:5c5b:196b:78b2', 'port': 18542, 'score': 1}], 'warnings': ''}
assert node.network_info["connections"] == 0
assert node.network_info["warnings"] == ""
def test_Node_elm(elements_elreg):
with tempfile.TemporaryDirectory("_some_datafolder_tmp") as data_folder:
node = Node.from_json(
{
"autodetect": False,
"datadir": "",
"user": elements_elreg.rpcconn.rpcuser,
"password": elements_elreg.rpcconn.rpcpassword,
"port": elements_elreg.rpcconn.rpcport,
"host": elements_elreg.rpcconn.ipaddress,
"protocol": "http",
},
manager=MagicMock(),
default_fullpath=os.path.join(data_folder, "a_testfile.json"),
)
result = node.test_rpc()
assert result["tests"]["connectable"] == True
assert result["tests"]["recent_version"] == True
assert result["tests"]["credentials"] == True
assert result["tests"]["wallets"] == True
node_json = node.json
del node_json["fullpath"] # This is very different because of the tempfile
print(f"node.json = {node.json}")
assert node_json == {
"name": "",
"alias": "",
"autodetect": False,
"datadir": "",
"user": "liquid",
"password": "secret",
"port": 18643,
"host": "localhost",
"protocol": "http",
"external_node": True, # 'fullpath': ''
}
rpc = node.get_rpc()
assert rpc.getblockchaininfo()["chain"] == "elreg"
node.rename("some_new_name")
assert node.json["name"] == "some_new_name"
node.check_info()
assert node.is_configured == True
assert node.is_running == True
assert node.is_testnet == True
print(f"info = {node.info}")
# something like:
# {'chain': 'regtest', 'blocks': 100, 'headers': 100, 'bestblockhash': '5277c27b3b5e8aad8e079a928e4675931ea1638a28c7a33bae4ef26425402259', 'difficulty': 4.656542373906925e-10, 'mediantime': 1622635607, 'verificationprogress': 1, 'initialblockdownload': False, 'chainwork': '00000000000000000000000000000000000000000000000000000000000000ca', 'size_on_disk': 30477, 'pruned': False, 'softforks': {'bip34': {'type': 'buried', 'active': False, 'height': 500}, 'bip66': {'type': 'buried', 'active': False, 'height': 1251}, 'bip65': {'type': 'buried', 'active': False, 'height': 1351}, 'csv': {'type': 'buried', 'active': False, 'height': 432}, 'segwit': {'type': 'buried', 'active': True, 'height': 0}, 'testdummy': {'type': 'bip9', 'bip9': {'status': 'defined', 'start_time': 0, 'timeout': 9223372036854775807, 'since': 0}, 'active': False}}, 'warnings': '', 'mempool_info': {'loaded': True, 'size': 0, 'bytes': 0, 'usage': 0, 'maxmempool': 300000000, 'mempoolminfee': 1e-05, 'minrelaytxfee': 1e-05}, 'uptime': 480, 'blockfilterindex': False, 'utxorescan': None}
assert node.info["chain"] == "elreg"
print(f"network_info = {node.network_info}")
# something like:
# {'version': 200100, 'subversion': '/Satoshi:0.20.1/', 'protocolversion': 70015, 'localservices': '0000000000000409', 'localservicesnames': ['NETWORK', 'WITNESS', 'NETWORK_LIMITED'], 'localrelay': True, 'timeoffset': 0, 'networkactive': True, 'connections': 0, 'networks': [{'name': 'ipv4', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'ipv6', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'onion', 'limited': True, 'reachable': False, 'proxy': '', 'proxy_randomize_credentials': False}], 'relayfee': 1e-05, 'incrementalfee': 1e-05, 'localaddresses': [{'address': '2a02:810d:d00:7700:233e:a7e:ded8:f2da', 'port': 18542, 'score': 1}, {'address': '2a02:810d:d00:7700:29ec:5c5b:196b:78b2', 'port': 18542, 'score': 1}], 'warnings': ''}
assert node.network_info["connections"] == 0
# currently:
assert (
node.network_info["warnings"]
== "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"
)
# assert node.network_info["warnings"] == ""

View file

@ -6,6 +6,8 @@ PORT=25444
# This needs to be the same than in config.py CypressTestConfig BTCD_REGTEST_DATA_DIR
# As we don't want to speculate here, we're injecting it via Env-var
export BTCD_REGTEST_DATA_DIR=/tmp/specter_cypress_btc_regtest_plain_datadir
# same with this
export ELMD_REGTEST_DATA_DIR=/tmp/specter_cypress_elm_regtest_plain_datadir
# same with SPECTER_DATA_FOLDER
export SPECTER_DATA_FOLDER=~/.specter-cypress
# We'll might change that on the "dev-function"
@ -96,47 +98,76 @@ function send_signal() {
fi
}
function start_bitcoind {
function start_node {
addopts=""
while [[ $# -gt 0 ]]; do
arg="$1"
case $arg in
--bitcoin)
node_impl=bitcoind
shift
;;
--elements)
node_impl=elementsd
shift
;;
--reset)
echo "--> Purging $BTCD_REGTEST_DATA_DIR"
rm -rf $BTCD_REGTEST_DATA_DIR
RESET=true
shift
;;
--cleanuphard)
addopts="--cleanuphard"
CLEANUPHARD=true
shift
;;
*)
echo "unrecognized argument for start_bitcoind: $1 "
echo "unrecognized argument for start_node: $1 "
exit 1
shift
;;
esac
done
if [ "$1" = "--reset" ]; then
if [ "$RESET" = "true" ]; then
if [ "$node_impl" = "bitcoind" ]; then
echo "--> Purging $BTCD_REGTEST_DATA_DIR"
rm -rf $BTCD_REGTEST_DATA_DIR
else
echo "--> Purging $ELMD_REGTEST_DATA_DIR"
rm -rf $ELMD_REGTEST_DATA_DIR
fi
fi
if [ "$1" = "--cleanuphard" ]; then
if [ "$CLEANUPHARD" = "true" ]; then
addopts="--cleanuphard"
fi
if [ "$DOCKER" != "true" ]; then
if [ "$node_impl" != "elementsd" ]; then # no docker for elementsd yet
addopts="$addopts --nodocker"
fi
fi
echo "--> Starting bitcoind with $addopts..."
python3 -m cryptoadvance.specter $DEBUG bitcoind $addopts --create-conn-json --config $SPECTER_CONFIG &
bitcoind_pid=$!
echo "--> Starting $node_impl with $addopts ..."
python3 -m cryptoadvance.specter $DEBUG $node_impl $addopts --create-conn-json --config $SPECTER_CONFIG &
if [ "$node_impl" = "bitcoind" ]; then
bitcoind_pid=$!
else
elementsd_pid=$!
fi
while ! [ -f ./btcd-conn.json ] ; do
sleep 0.5
done
}
function start_bitcoind {
start_node --bitcoin $*
}
function start_elementsd {
start_node --elements $*
}
function stop_bitcoind {
if [ ! -z ${bitcoind_pid+x} ]; then
echo "--> Killing/Terminating bitcoindwrapper with PID $bitcoind_pid ..."
@ -146,6 +177,15 @@ function stop_bitcoind {
fi
}
function stop_elementsd {
if [ ! -z ${elementsd_pid+x} ]; then
echo "--> Killing/Terminating elementsdwrapper with PID $elementsd_pid ..."
send_signal SIGTERM $elementsd_pid
wait $elementsd_pid
unset elementsd_pid
fi
}
function start_specter {
if [ "$1" = "--reset" ]; then
echo "--> Purging $SPECTER_DATA_FOLDER"
@ -154,6 +194,8 @@ function start_specter {
echo "--> Starting specter ..."
python3 -m cryptoadvance.specter $DEBUG server --config $SPECTER_CONFIG --debug > /dev/null &
specter_pid=$!
# Simulate slower machines with uncommenting this (-l 10 means using 10% cpu):
#cpulimit -p $specter_pid -l 10 -b
$(npm bin)/wait-on http://localhost:${PORT}
}
@ -168,8 +210,9 @@ function stop_specter {
function cleanup()
{
stop_specter || :
stop_bitcoind || :
stop_specter || :
stop_bitcoind || :
stop_elementsd || :
}
@ -200,13 +243,20 @@ function restore_snapshot {
exit 1
fi
done
rm -rf ${BTCD_REGTEST_DATA_DIR}
mkdir ${BTCD_REGTEST_DATA_DIR}
rm -rf $SPECTER_DATA_FOLDER
mkdir $SPECTER_DATA_FOLDER
echo "--> Unpacking ./cypress/fixtures/${spec_file}_btcdir.tar.gz ... "
tar -xzf ./cypress/fixtures/${spec_file}_btcdir.tar.gz -C ${BTCD_REGTEST_DATA_DIR} --strip-components=1
rm -rf ${ELMD_REGTEST_DATA_DIR}
mkdir ${ELMD_REGTEST_DATA_DIR}
echo "--> Unpacking ./cypress/fixtures/${spec_file}_elmdir.tar.gz ... "
tar -xzf ./cypress/fixtures/${spec_file}_elmdir.tar.gz -C ${ELMD_REGTEST_DATA_DIR} --strip-components=1
echo "--> Unpacking ./cypress/fixtures/${spec_file}_specterdir.tar.gz ... "
rm -rf $SPECTER_DATA_FOLDER
mkdir $SPECTER_DATA_FOLDER
tar -xzf ./cypress/fixtures/${spec_file}_specterdir.tar.gz -C $SPECTER_DATA_FOLDER --strip-components=1
}
@ -223,9 +273,11 @@ function sub_dev {
if [ -n "${spec_file}" ]; then
restore_snapshot ${spec_file}
start_bitcoind --cleanuphard
start_elementsd --cleanuphard
start_specter
else
start_bitcoind --reset
stop_elementsd --reset
start_specter --reset
fi
open http://localhost:${PORT}
@ -237,9 +289,11 @@ function sub_open {
if [ -n "${spec_file}" ]; then
restore_snapshot ${spec_file}
start_bitcoind --cleanuphard --reset
start_elementsd --cleanuphard --reset
start_specter
else
start_bitcoind --reset
start_elementsd --reset
start_specter --reset
fi
start_specter
@ -251,11 +305,15 @@ function sub_run {
if [ -f ./cypress/integration/${spec_file} ]; then
restore_snapshot ${spec_file}
start_bitcoind --cleanuphard --reset
start_elementsd --cleanuphard --reset
start_specter
# Run $spec_file and all of the others coming later which come later!
$(npm bin)/cypress run --spec $(./utils/calc_cypress_test_spec.py --run $spec_file)
# Run $spec_file and all of the others coming later!
#$(npm bin)/cypress run --spec $(./utils/calc_cypress_test_spec.py --run $spec_file)
# Run $spec_file and only that spec-file!
$(npm bin)/cypress run --spec ./cypress/integration/${spec_file}
else
start_bitcoind --reset
start_elementsd --reset
start_specter --reset
$(npm bin)/cypress run
fi
@ -270,15 +328,21 @@ function sub_snapshot {
exit 2
fi
start_bitcoind --reset
start_elementsd --reset
start_specter --reset
$(npm bin)/cypress run --spec $(./utils/calc_cypress_test_spec.py $spec_file)
echo "--> stopping specter"
stop_specter
echo "--> stopping bitcoind gracefully ... won't take long ..."
stop_bitcoind
echo "--> Creating snapshot $BTCD_REGTEST_DATA_DIR)"
echo "--> stopping elementsd gracefully ... won't take long ..."
stop_elementsd
echo "--> Creating snapshot $BTCD_REGTEST_DATA_DIR)"
rm ./cypress/fixtures/${spec_file}_btcdir.tar.gz 2> /dev/null 1>&2 || :
tar -czf ./cypress/fixtures/${spec_file}_btcdir.tar.gz -C /tmp $(basename $BTCD_REGTEST_DATA_DIR)
echo "--> Creating snapshot $ELMD_REGTEST_DATA_DIR)"
rm ./cypress/fixtures/${spec_file}_elmdir.tar.gz 2> /dev/null 1>&2 || :
tar -czf ./cypress/fixtures/${spec_file}_elmdir.tar.gz -C /tmp $(basename $ELMD_REGTEST_DATA_DIR)
echo "--> Creating snapshot of $SPECTER_DATA_FOLDER"
rm ./cypress/fixtures/${spec_file}_specterdir.tar.gz 2> /dev/null 1>&2 || :
tar -czf ./cypress/fixtures/${spec_file}_specterdir.tar.gz -C ~ $(basename $SPECTER_DATA_FOLDER)