Skip to content

Commit

Permalink
Bugfix: Clarification of Amounts in tables (fixes #1861) (#2026)
Browse files Browse the repository at this point in the history
* refactoring categories and amounts + improvements of amounts

* clear cache functionality in wallet settings

* refactoring of wallet settings

* clean up and frontend wallet-refactoring

* some documentation about psbt and embit

* intermediate commit

* moar testing

* proper descriptor

* updated tests

* finalize WalletAwareTxItem Test

* coming closer

* fix tests

* Update src/cryptoadvance/specter/templates/includes/tx-table.html

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/templates/includes/tx-table.html

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/templates/wallet/settings/wallet_settings.jinja

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/templates/wallet/settings/wallet_settings.jinja

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* Update src/cryptoadvance/specter/server_endpoints/wallets/wallets.py

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>

* fix cypress

* refactoring  some parts of the psbt-creation-process and wallet.txlist

* fix cypress

* fix broken csv-download

* bugfix confirmations: null

* Bugfix: accidental Speed-up-button on confirmed TXs

* fix test

* fix test (part2)

* Liquid specific fixes

* improve cache usage

* refactoring wallet.check_utxo()

* reimplemented the whole check_utxo method

Co-authored-by: Manolis Mandrapilias <70536101+moneymanolis@users.noreply.github.com>
  • Loading branch information
k9ert and moneymanolis authored Jan 20, 2023
1 parent e928113 commit fa9605d
Show file tree
Hide file tree
Showing 27 changed files with 1,648 additions and 683 deletions.
329 changes: 166 additions & 163 deletions cypress/integration/spec_elm_multi_segwit_wallet.js

Large diffs are not rendered by default.

170 changes: 86 additions & 84 deletions cypress/integration/spec_elm_single_segwit_wallet.js
Original file line number Diff line number Diff line change
@@ -1,104 +1,106 @@
describe('Operating with an elements singlesig wallet', () => {
it('Creates a single sig elements hot wallet', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.contains('Elements Node').click()
if (Cypress.env("CI")) {

it('Creates a single sig elements hot wallet', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.get('#node-switch-icon').click()
cy.contains('Elements Node').click()

// Delete Wallet if existing
cy.deleteWallet("Elm Single Segwit Hot Wallet")
cy.deleteWallet("Elm Single Nested Hot Wallet")
// Delete Wallet if existing
cy.deleteWallet("Elm Single Segwit Hot Wallet")
cy.deleteWallet("Elm Single Nested Hot Wallet")

cy.addHotDevice("Hot Elements Device 1","elements")

// Segwit Wallet
cy.addHotWallet("Elm Single Segwit Hot Wallet","Hot Elements Device 1", "elements", "segwit")
cy.addHotDevice("Hot Elements Device 1","elements")

// Nested Segwit Wallet
cy.addHotWallet("Elm Single Nested Hot Wallet","Hot Elements Device 1", "elements", "nested_segwit")
})
// Segwit Wallet
cy.addHotWallet("Elm Single Segwit Hot Wallet","Hot Elements Device 1", "elements", "segwit")

// Nested Segwit Wallet
cy.addHotWallet("Elm Single Nested Hot Wallet","Hot Elements Device 1", "elements", "nested_segwit")
})

it('send confidential transaction from segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Segwit Hot Wallet").click()
it('send confidential transaction from segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Segwit Hot Wallet").click()

cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("el1qqdsywea5scrn7t9q83fd540pw447h0uae30pdp82rzgkl7yzvjz6gra9ls8qu6sslw4s0ck48we06zhqd6kwjy2quh69zwxwn", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("el1qqdsywea5scrn7t9q83fd540pw447h0uae30pdp82rzgkl7yzvjz6gra9ls8qu6sslw4s0ck48we06zhqd6kwjy2quh69zwxwn", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
})
})
})
})

it('send unconfidential transaction from segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Segwit Hot Wallet").click()
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("ert1q38la37ulxgc0uwt334he46eua7h8qagqnlm5phcqk7ntgv3x73cqjtr2fa", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
it('send unconfidential transaction from segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Segwit Hot Wallet").click()
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("ert1q38la37ulxgc0uwt334he46eua7h8qagqnlm5phcqk7ntgv3x73cqjtr2fa", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
})
})
})
})

it('send confidential transaction from nested segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Nested Hot Wallet").click()
it('send confidential transaction from nested segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Nested Hot Wallet").click()

cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("el1qqdsywea5scrn7t9q83fd540pw447h0uae30pdp82rzgkl7yzvjz6gra9ls8qu6sslw4s0ck48we06zhqd6kwjy2quh69zwxwn", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("el1qqdsywea5scrn7t9q83fd540pw447h0uae30pdp82rzgkl7yzvjz6gra9ls8qu6sslw4s0ck48we06zhqd6kwjy2quh69zwxwn", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
})
})
})
})

it('send unconfidential transaction from nested segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Nested Hot Wallet").click()
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("ert1q38la37ulxgc0uwt334he46eua7h8qagqnlm5phcqk7ntgv3x73cqjtr2fa", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
it('send unconfidential transaction from nested segwit', () => {
cy.viewport(1300,660)
cy.visit('/')
cy.contains("Elm Single Nested Hot Wallet").click()
cy.get('#fullbalance_amount').then(($div) => {
const oldBalance = parseFloat($div.text())
expect(oldBalance).to.be.gte(1.5)
cy.createPsbt("ert1q38la37ulxgc0uwt334he46eua7h8qagqnlm5phcqk7ntgv3x73cqjtr2fa", "Burn address","1.5")
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', { timeout: Cypress.env("broadcast_timeout") })
.should(($div) => {
const newBalance = parseFloat($div.text())
expect(newBalance).to.be.lte(oldBalance - 1.5)
})
})
})
})


}
})
15 changes: 14 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,17 @@ This is also a pattern used for plugins.

The more specific a functionality became, the more awkward it felt to integrate it in the core architecture. When we started to make exchange specific functionality, we wanted to protect the core architecture. Therefore, we created a plugin concept which allows to have the above concepts replicated in their own self-contained standalone units.

We now try to implement bigger chunks of functionality in plugins. Maybe, even core functionality might be implemented in "core plugins" in the future. Internal plugins are placed in `src/specterext`. But, plugins can also live in there own repos, have their own release lifecycle and be used by Specter like any other dependency. For more information about plugins see [Third Party Service Integrations](./extensions.md) where we also discuss the nuances between plugins and extensions.
We now try to implement bigger chunks of functionality in plugins. Maybe, even core functionality might be implemented in "core plugins" in the future. Internal plugins are placed in `src/specterext`. But, plugins can also live in there own repos, have their own release lifecycle and be used by Specter like any other dependency. For more information about plugins see [Third Party Service Integrations](./extensions.md) where we also discuss the nuances between plugins and extensions.

# The Crypto Engine
The Crypto Engine consists of a bunch of classes which partially are based on Embit.
The documentation here is incomplete but will better over time.

## psbt classes

[![](https://mermaid.ink/img/pako:eNqVU11PgzAU_StNnzTO_QDiizoffNKEPRkS0rWXrRF6SXurLHP_3QKOsjHNhIRczj0997M7LlEBT7gshXMLLdZWVJlh4blfObJC0rJ5REPQELv7ur1laQ2SwC6bS1ipxBp64hg54jyb2tMlxBdPx8y_Y7-mD8vM9G_L7iqcntn1XsZulJbEDNAn2vcBXICTVteElqlo9u79WHjoSxT80CYnzFvdq-uIoqcz8AlyTrsrPcqvEEumXV5pAxNQboRZT2ELEnTIaz14QjuYUMqCcwOmTcGcoFxU6A0NcFGiINZ9T1wXJB_HHCuIGNOmHoWnc-FXWwLHqNGKHVHbhv4jkdEaxUxGIBvJTVL5XbZdt6g3n88H29dKEIxGXVisTsffYWEzjQvLqdEMFfAZr8BWQqtwS7sAGacNVJDxJJgKCuFLynhmWmof60npsKU8KUTpYMaFJ0y3RvKErIcD6eeyD6xamDfEw__-G-B6VVQ?type=png)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqVU11PgzAU_StNnzTO_QDiizoffNKEPRkS0rWXrRF6SXurLHP_3QKOsjHNhIRczj0997M7LlEBT7gshXMLLdZWVJlh4blfObJC0rJ5REPQELv7ur1laQ2SwC6bS1ipxBp64hg54jyb2tMlxBdPx8y_Y7-mD8vM9G_L7iqcntn1XsZulJbEDNAn2vcBXICTVteElqlo9u79WHjoSxT80CYnzFvdq-uIoqcz8AlyTrsrPcqvEEumXV5pAxNQboRZT2ELEnTIaz14QjuYUMqCcwOmTcGcoFxU6A0NcFGiINZ9T1wXJB_HHCuIGNOmHoWnc-FXWwLHqNGKHVHbhv4jkdEaxUxGIBvJTVL5XbZdt6g3n88H29dKEIxGXVisTsffYWEzjQvLqdEMFfAZr8BWQqtwS7sAGacNVJDxJJgKCuFLynhmWmof60npsKU8KUTpYMaFJ0y3RvKErIcD6eeyD6xamDfEw__-G-B6VVQ)

## embit classes
Here is a diagram of all the classes from the embit library. No properties/attributes or methods are in there, yet.

[![](https://mermaid.ink/img/pako:eNqFk19rgzAQwL-K5Ln9ArKnYtnGNiqzbDDycsZrDcREkstGcX73xT9tLejqSzx_Py935NIwYQpkMRMKnEskHC1UXBfSoiBpdPT6znUUnm2VS9qAw-jhd70ewhc8TWCIBvaU3ILrX5k8aiBvcZ6KUhtrgzSXNfW5kmJhy9TKbyBc2jfBnod-UqByVkmzzT4Tph5Lu4QDfda1p2W883TLZ5IvtGxlTbPoU5JG52bZ3oJ20B_QPd5Xfk8a6udjsn4UokdlclAT6UPiT3N22qnatbdMsxIs_oMypCllK1ahrUAWYSqb7jNnVGKFnMXhtcADeEWccd0G1ddFOPdtIclYFh9AOVwx8GSykxYsJuvxLI3DfbFq0F_GXGPsk7yN16Fb2j-RrgZR?type=png)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqFk19rgzAQwL-K5Ln9ArKnYtnGNiqzbDDycsZrDcREkstGcX73xT9tLejqSzx_Py935NIwYQpkMRMKnEskHC1UXBfSoiBpdPT6znUUnm2VS9qAw-jhd70ewhc8TWCIBvaU3ILrX5k8aiBvcZ6KUhtrgzSXNfW5kmJhy9TKbyBc2jfBnod-UqByVkmzzT4Tph5Lu4QDfda1p2W883TLZ5IvtGxlTbPoU5JG52bZ3oJ20B_QPd5Xfk8a6udjsn4UokdlclAT6UPiT3N22qnatbdMsxIs_oMypCllK1ahrUAWYSqb7jNnVGKFnMXhtcADeEWccd0G1ddFOPdtIclYFh9AOVwx8GSykxYsJuvxLI3DfbFq0F_GXGPsk7yN16Fb2j-RrgZR)
14 changes: 11 additions & 3 deletions src/cryptoadvance/specter/commands/psbt_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from math import isnan

import requests
from cryptoadvance.specter.util.psbt import SpecterPSBT
from cryptoadvance.specter.wallet import Wallet
from cryptoadvance.specter.specter_error import SpecterError
from cryptoadvance.specter.util.common import str2bool

Expand All @@ -14,7 +16,10 @@


class PsbtCreator:
"""A class to create PSBTs easily out of stuff coming from the frontend"""
"""A class to create PSBTs easily out of stuff coming from the frontend
For an overview of the overall workflow, checkout e.g.
https://github.com/bitcoin/bitcoin/blob/master/doc/psbt.md
"""

def __init__(
self,
Expand Down Expand Up @@ -108,11 +113,14 @@ def validate_before_creation(self):
additional_data,
)

def create_psbt(self, wallet):
def create_psbt(self, wallet: Wallet) -> dict:
"""creates the PSBT via the wallet and modifies it for if substract is true
If there was a "estimate_fee" in the request_form, the PSBT will not get persisted
"""
self.psbt = wallet.createpsbt(self.addresses, self.amounts, **self.kwargs)
self.psbt_as_object: SpecterPSBT = wallet.createpsbt(
self.addresses, self.amounts, **self.kwargs
)
self.psbt = self.psbt_as_object.to_dict()
if self.psbt is None:
raise SpecterError(
"Probably you don't have enough funds, or something else..."
Expand Down
23 changes: 22 additions & 1 deletion src/cryptoadvance/specter/liquid/txlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from embit.psbt import read_string


class LTxItem(TxItem):
class LTxItem(WalletAwareTxItem):
TransactionCls = LTransaction
columns = [
"txid", # str, txid in hex
Expand Down Expand Up @@ -148,6 +148,27 @@ def vsize(self):
vsize = math.ceil(weight / 4)
return vsize

# Three properties which are defined in WalletAwareTxItem which we can't calculate here but which need to
# return something as the getter is called in WalletAwareTxItem`#s constructor
# This is definitely not a good way to fix this.
# ToDo: Do it properly if we have more time for Liquid

@property
def category(self):
return None

@property
def address(self):
return None

@property
def flow_amount(self):
return None

@property
def ismine(self):
return None

def __dict__(self):
return {
"txid": self["txid"],
Expand Down
4 changes: 2 additions & 2 deletions src/cryptoadvance/specter/liquid/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def createpsbt(

if not readonly:
self.save_pending_psbt(psbt)
return psbt.to_dict()
return psbt

def canceltx(self, *args, **kwargs):
raise SpecterError("RBF is not implemented on Liquid")
Expand Down Expand Up @@ -279,7 +279,7 @@ def addresses_info(self, is_change):
for utxo in self.full_utxo
if to_unconfidential(utxo["address"]) == to_unconfidential(addr.address)
]:
addr_amount = addr_amount + utxo["amount"]
addr_amount = addr_amount + utxo.utxo_amount
addr_utxo = addr_utxo + 1
addr_assets[utxo.get("asset")] = (
addr_assets.get(utxo.get("asset"), 0) + utxo["amount"]
Expand Down
1 change: 0 additions & 1 deletion src/cryptoadvance/specter/managers/wallet_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ def delete_wallet(self, wallet, node=None) -> tuple:
wallet.delete_files()
# Remove the wallet instance
del self.wallets[wallet.name]
self.update()
specter_wallet_deleted = True
except KeyError:
raise SpecterError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,11 @@ def testcoin_faucet(self, address, amount=20, confirm_payment=True):
rpc.generatetoaddress(102, default_address)
btc_balance = default_rpc.getbalance()

default_rpc.sendtoaddress(address, amount)
result = default_rpc.sendtoaddress(address, amount)
if confirm_payment:
# confirm it
rpc.generatetoaddress(1, default_address)
return result

@staticmethod
def check_node(rpcconn, raise_exception=False):
Expand Down
7 changes: 6 additions & 1 deletion src/cryptoadvance/specter/server_endpoints/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,12 @@ def server_error_405(e):
app.logger.error(trace)
if request.headers.get("Accept") == "application/json":
return {"error": error_msg}
flash(_("Session expired. Please refresh and try again."), "error")
flash(
_(
"Method not allowed. Probably the session expired. Please refresh and try again."
),
"error",
)
return redirect(request.url)


Expand Down
Loading

0 comments on commit fa9605d

Please sign in to comment.