Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero Confirmation Forfeits (ZCF) #964

Open
wants to merge 29 commits into
base: master
Choose a base branch
from

Conversation

awemany
Copy link

@awemany awemany commented Nov 20, 2018

Hey guys,

here's my very preliminary code to do zero confirmation forfeits with Electron Cash. This should serve both as a test bed for development of the concept, as well as later on a reference implementation from which a specification can be derived.

I like to do the bottom-up approach of first writing code and the writing a specification that matches the code and all the pitfalls that were encountered writing it.

What kind-of works:

  • Transactions can be generated with a forfeit output. The forfeit output is currently set to 1.0 the send transaction amount.
  • Receiving transactions and extracting the forfeit amount, checking for enough
  • Display of forfeits in the transaction dialog, history list tab and addresses tab
  • Spending forward of the forfeits by assembling the right inputs to the the P2SH forfeit contract

There are lots of bugs still. Sometimes the value parser seems to miss forfeits. Sometimes I get bugs when starting up. It is all in a state of flux. I am publishing this PR early as @HostFat and some others were curious about this. That said, I tried to clean up my code as much as possible and separate it into a series of clean commits.

BIG FAT WARNING: This code is by no means production ready!! If you want to try it out, use test net or create a wallet with some miniscule amount of BCH in it. Losing money is still likely.

EDIT: For those curious to try this out, here's a walk through the functionality with pictures: https://github.com/awemany/electron-cash-zcf-demo/blob/master/README.md

@cculianu
Copy link
Collaborator

That was fast. You rock man. Thank you.

@awemany
Copy link
Author

awemany commented Nov 21, 2018

@cculianu: Thanks, but this is far from finished!

This is the code to generate the forfeit P2SH smart contract
scripts.
Add parsing of generic P2SH contract addresses to the transaction code.
Note that this still needs a check (maybe?) whether the previous transaction
actually had a P2SH output.

It is important to notice that a transaction's TXID is recalculated from
these values. As unknown scriptSigs need to still produce the right serialization,
the serialization code has likewise been changed to always use an input's scriptSig,
if available.
This adds support for handling forfeit addresses to the wallet.

Two new class members are introduced:

forfeit_map: This maps forfeit addresses to their underlying P2PKH
address (for which the public key also matches the forfeit address if
using the correct P2SH script for scriptSig generation).

forfeit_scripts:
This maps forfeit addresses to their corresponding P2SH scripts that
allow to spend the forfeit addresses as normal P2PKH (or by two sigs
in case a miner detects a double-spend).

Both of these objects are persisted to the wallet JSON file for easier
handling and to cache the transaction parsing results.

It also adds support to the various accessor function to more properly
represent forfeit addresses.

It still misses the transaction generating code as well as the code to
extract forfeits from incoming transactions that match the template
forfeit P2SH code. This will come in an upcoming commit.
This checks incoming transactions in add_transaction() for forfeit
outputs and, if such outputs are found, adds them to the tracking
data structures self.forfeit_map and self.forfeit_scripts.
If the configuration variable use_forfeits is set to true, this
will generate transaction that have a "ZCF\x00" OP_RETURN marker
indicating the use of forfeits as well as having the special
forfeit smart contract output to a corresponding P2SH address.

The P2SH address is deterministically generated from the transaction's
inputs and the change address that is alsoused for the "forfeit
change". This means that it is possible to recreate the P2SH contents
just by trying reconstruction of all available outputs and checking
for a matching P2SH hash.
Note that this comes with a considerable rework and simplification
of the listing logic. This might miss some subtlety regarding the
address listing and therefore needs careful review.
This shows a green "F" for unconfirmed transactions that have
enough forfeit amount for the given received value.

FIXME: The amount is currently hardcoded to be a minimum of 1.0 the
received value. It probably makes sense to add a configuration dialog
for this.
FIXME: This directly changes the configuration variable. Is this
an acceptable way to do it?
For signalling problems when generating forfeit transactions. Also
remove a superfluous return after an exception is raised.
... instead of throwing an uncontrolled exception later on.
- this show forfeits already when creating a new transaction
- it also shows one's owns and other's forfeit contract P2SH addresses in
  different colors (light blue for own forfeit outputs, pink for those from
  others)
This is necessary as to not forget about the forfeit addresses so
that the balance is shown correctly.
@awemany
Copy link
Author

awemany commented Nov 21, 2018

Ok, updates:

  • Important fixes. The correct forfeit and amount display seems to be a lot more stable now. Still, wouldn't be surprised if there's still bugs lurking along these lines. For any new forfeit detected, save_addresses() is called, which might be overdoing it but it seems to work.
  • wallet upgrade from non-forfeit to forfeit-mode supported
  • fixed a stupid bug that actually prevented proper display of forfeit-enabled transactions with the special "green F marker"
  • both own forfeit contracts (back as 'forfeit change' to oneself) as well as to others are displayed in the transaction dialog now, also when creating a fresh TXN. Own and other forfeits are displayed in different colors (lightblue, pink)
  • Better, though improvable parsing of forfeit inputs as P2SH. Note that Electrum/EC has some weird assumptions internally so far that P2SH is always multisig
  • display error messages instead of 'not enough funds' for the various ways forfeit creation might fail

To have it displayed correctly on the preview tab.
FIXME: This might create some noise when aborting the transaction generation and
using different inputs down the road.
@cculianu cculianu added the Do Not Merge (Yet!) Pull requests that we intend to merge in the future but that should not yet be merged. label Nov 25, 2018
@awemany
Copy link
Author

awemany commented Nov 27, 2018

Updated with a configuration dialog for the send and accepted receive forfeit fractions. Needs more testing.

Copy link

@gasull gasull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three tiny improvements with intention of being "right work".

for addr_list, name in [
(self.wallet.get_receiving_addresses(), _("Receiving")),
(self.wallet.get_change_addresses(), _("Change")),
(self.wallet.get_forfeit_addresses(), _("Forfeit"))]:
Copy link

@gasull gasull Aug 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, and since the iterating list is never modified, I'd suggest using a tuple and defining it before the loop:

addresses_to_name_pairs = (
    (self.wallet.get_receiving_addresses(), _("Receiving")),
    (self.wallet.get_change_addresses(), _("Change")),
    (self.wallet.get_forfeit_addresses(), _("Forfeit")),
)
for addr_list, name in addresses_to_name_pairs:

@@ -3084,12 +3093,57 @@ def on_fiat_address(checked):
fiat_widgets.append((QLabel(_('Show Fiat balance for addresses')), fiat_address_checkbox))
fiat_widgets.append((QLabel(_('Source')), ex_combo))


Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicking: why the extra blank line here?

continue
seq_item = QTreeWidgetItem( [ name, '', '', '', '', ''] )
account_item.addChild(seq_item)
if not had_item_count: # first time we create this widget, auto-expand the default address list
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 8 nitpicking:

Inline comments should be separated by at least two spaces from the statement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Do Not Merge (Yet!) Pull requests that we intend to merge in the future but that should not yet be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants