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

Initial interface design #1

Merged
merged 48 commits into from
Nov 9, 2016
Merged

Initial interface design #1

merged 48 commits into from
Nov 9, 2016

Conversation

TimDaub
Copy link
Contributor

@TimDaub TimDaub commented Oct 10, 2016

This was part of a first effort to make it possible for users of BigchainDB to create and sign transactions independently from the programming language they'd like to use.

The motivation of existence behind this tool was outlined by me in the following two comments:

TL;DR:

The idea is to have a command line tool that allows to write simple wrappers in the user's programming language of choice. I was inspired by bitpay's bitcoin-rpc lib: https://github.com/bitpay/bitcoind-rpc/blob/master/lib/index.js

With this PR, I've been mainly following the API that is provided by BDB-C. Specifically the following methods are of interest:

What has been done:

  • Private and public keys can be generated using $ bdb generate keys
  • A CREATE transaction can be generated using $ bdb generate create
  • Any Ed25519 or ThresholdSha256 Condition can be fulfilled (signed) using $ bdb sign
  • Any transactions' conditions can be transformed to "inputs" (for using them in a new transaction) using $ bdb spend

What is left to be done:

(https://docs.bigchaindb.com/projects/server/en/latest/drivers-clients/python-server-api-examples.html) and here

  • Tests for everything (We should aim for 100% code coverage, CI using Travis, ...)
  • Resolve all outstanding TODOs and FIXMEs in the code
  • Refactor the $ bdb generate create command such that it takes an arbitrary amount of JSON conditions instead of the current owner_after argument
  • Implement the command $ bdb generate transfer that takes inputs taken from bdb spend, as well as an arbitrary amount of JSON conditions (Note: that the command requires two unlimited variadic arguments which might be tricky to achieve)
  • Review cli method names (create/generate_tx, sign, etc)
  • Review cli api for security (should private keys be in argv really?)

Make tickets for:

  • Documentation by "real-life" examples, similar to [here]
  • Integration tests (@sbellem should probably be consulted before doing that)
  • Write a guideline how developers can use this to easily implement wrappers for the programming language of their choice
  • Support all cryptoconditions BigchainDB currently supports
  • Use pyinstaller to automatically create an executable (and if possible automatically deploy that to OS-package-manager-repositories)

Obviously all the TODOs mentioned here do not have to be handled in this PR, but instead tickets can be created to enable iterative development.

click.echo('{} {}'.format(private_key, public_key))


# TODO:
Copy link
Contributor Author

@TimDaub TimDaub Oct 10, 2016

Choose a reason for hiding this comment

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

The two TODOs here might be ignored initially.

@spend_group.command()
@click.argument('transaction', type=DictParamType())
@click.argument('condition_id', required=False, type=click.INT, nargs=-1)
# TODO: option to output JSON list
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Optional requirement.

@ssadler ssadler mentioned this pull request Oct 12, 2016
@ssadler
Copy link
Contributor

ssadler commented Oct 13, 2016

s/bdb-transactions-cli/bdb-transaction-cli/g

* rename  (back) to
* create utils module for helpers like json_param
]

test_requirements = [
# TODO: put package test requirements here
]
Copy link

Choose a reason for hiding this comment

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

Why not put the test requirements?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll make an issue about this!

Generates a Ed25119 condition from a OWNER_AFTER or a ThresholdSha256
Condition from more than one OWNER_AFTER.
"""
condition = Condition.generate(list(owner_after))
Copy link

Choose a reason for hiding this comment

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

Why not [] instead of list()?

Copy link
Contributor

Choose a reason for hiding this comment

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

[] and list() don't do the same thing, the first wraps, the second converts.

The conversion is because the owners_after argument is variadic and click is providing a tuple, and in transactions.py you have the following, so it breaks:

         if isinstance(owners_after, tuple):         
             owners_after, threshold = owners_after  
         else:                                       
             threshold = len(owners_after)           

""" Decorator for a JSON command line argument """
kwargs['type'] = JsonParamType()
if 'help' in kwargs:
del kwargs['help']
Copy link

Choose a reason for hiding this comment

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

Is there a reason we remove the help? Why can't we just do something like @click.argument(..., type=JSON_TYPE), where JSON_TYPE is an exported instance of JsonParamType?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ohhhhh... Actually I had forgotten about that.

Yes, I created that function to reduce the boilerplate, and deleting that key was a temporary workaround for a strange bug. Now I'll revisit it, and I might well just remove the function as you say.

@json_argument('transaction')
def get_asset(transaction):
"""
Return the asset from a transaction for the purpose of providing as an
Copy link

Choose a reason for hiding this comment

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

providing it as

Copy link
Contributor

Choose a reason for hiding this comment

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

Changed. No shorthand, generally?

Copy link

Choose a reason for hiding this comment

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

Maybe it's my Canadian English, but it read awkwardly without it 😉

Return the asset from a transaction for the purpose of providing as an
input to `transfer`.
"""
click.echo(json.dumps({"id": transaction['transaction']['asset']['id']}))
Copy link

Choose a reason for hiding this comment

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

From the docstrings, it sounds like we want the full asset, not just the id?

Copy link
Contributor

@ssadler ssadler Oct 19, 2016

Choose a reason for hiding this comment

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

Actually, I want to make another method, generate_asset for this purpose. AFAIK, in a TRANSFER tx you just want the ID (tests pass anyhow), but in a CREATE tx you want a full asset.

Outputs a signed transaction.
"""
transaction = Transaction.from_dict(transaction)
transaction = transaction.sign(list([private_key]))
Copy link

Choose a reason for hiding this comment

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

Unnecessary list().

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep... yep, yep.

ffill = Fulfillment(Ed25519Fulfillment(public_key=author_pubkey),
[author_pubkey])
conditions = [Condition.from_dict(c) for c in listify(conditions)]
asset = asset and Asset.from_dict(asset)
Copy link

Choose a reason for hiding this comment

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

This might get you in trouble, e.g. if I use {} as asset, {} will get passed to the Transaction init. Using something like

if asset is not None:
   asset = Asset.from_dict(asset)

is likely better.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I think asset = Asset.from_dict(asset) if asset else None would be even better. Protects against other values which do not evaluate to True, like an empty space.

Copy link

Choose a reason for hiding this comment

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

Some of these other bad values should get picked up by the json validator, but I agree.

name = 'JSON'

def convert(self, value, param, ctx):
# No try-except in case `value is None`, as it is a valid value.
Copy link

Choose a reason for hiding this comment

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

Not sure how click's other types behave, but it might be useful to name this JsonOrNoneParamType for clarity.

}'


$ bdb create 35qDXhZTUvna23NLc1hMfmrgPniBwPgNjko1VfQuD3vF '$CONDITIONS'
Copy link

@sohkai sohkai Oct 19, 2016

Choose a reason for hiding this comment

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

Should be double (") quotes, not single (') quotes for $CONDITIONS

(and for the other examples too)

Copy link
Contributor

Choose a reason for hiding this comment

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

Not if you're passing a JSON object, I would have thought. This is shell script remember.

Copy link

Choose a reason for hiding this comment

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

But using single quotes doesn't expand the variable; so cat '$CONDITIONS' would just print $CONDITIONS. Unless I'm missing something?

}'


$ bdb create 35qDXhZTUvna23NLc1hMfmrgPniBwPgNjko1VfQuD3vF '$CONDITIONS' --asset={"divisible": false, "data": {"b": 1}, "refillable": false, "id": "a", "updatable": true}
Copy link

Choose a reason for hiding this comment

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

The json should be surrounded by single (') quotes.

}'


$ bdb create 35qDXhZTUvna23NLc1hMfmrgPniBwPgNjko1VfQuD3vF '$CONDITIONS' --asset={"data": {"b": 1}, "divisible": false, "refillable": false, "id": "a", "updatable": true}
Copy link

@sohkai sohkai Oct 19, 2016

Choose a reason for hiding this comment

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

The json here should be enclosed in single quotes.

And for the other examples too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, this is a bug I don't actually have time to fix right now, so I'll make an issue.

@@ -2,6 +2,120 @@
Usage
=====

To use bdb-transaction-cli in a project::
Here is demonstrated how Alice can make two transactions, first to
Copy link

@sohkai sohkai Oct 19, 2016

Choose a reason for hiding this comment

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

Slight wording (or similar):

Here we demonstrate how Alice can make two transactions:


The required inputs to be able to do this are the public key of the
validating node (server) that the transaction will be first submitted
to.
Copy link

Choose a reason for hiding this comment

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

Paragraph needs some rewording.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I updated the graph and the comment is redundant.

@sohkai
Copy link

sohkai commented Oct 20, 2016

LGTM -- waiting on @sbellem.

Copy link
Contributor Author

@TimDaub TimDaub left a comment

Choose a reason for hiding this comment

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

Created #10 for all bigchaindb.common related comments.



@main.command()
@click.argument('author_pubkey')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In all other packages, we call this owner_before or owners_before.
Additionally, a lot of blood has been shed about the naming of these variables in Github issues. A consensus yet as to emerge, so before it does I'd prefer to call this by it's current name: owner_before.

Convert a transaction's outputs to inputs.

Convert conditions in TRANSACTION (json) to signable/spendable
fulfillments. Conditions can individually selected by passing one or more
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Conditions can be individually selected by passing

cryptography==1.4
PyYAML==3.11
pytest==2.9.2
.[dev]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TIL, nice trick.


import pytest

from contextlib import contextmanager
from bigchaindb_common.exceptions import KeypairMismatchException
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't this be bigchaindb.common now?


import click
from bigchaindb_common.crypto import generate_key_pair
Copy link
Contributor Author

Choose a reason for hiding this comment

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

bigchaindb.common


import click
from bigchaindb_common.crypto import generate_key_pair
from bigchaindb_common.transaction import Transaction, \
Copy link
Contributor Author

Choose a reason for hiding this comment

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

bigchaindb.common

RECORD_EXAMPLES = 'RECORD_EXAMPLES' in os.environ


@patch('bigchaindb_common.transaction.gen_timestamp', lambda: 42)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

bigchaindb.common



@patch('bigchaindb_common.transaction.gen_timestamp', lambda: 42)
@patch('bigchaindb_common.transaction.Asset.to_hash', lambda self: ASSET['id'])
Copy link
Contributor Author

Choose a reason for hiding this comment

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

bigchaindb.common

@@ -11,29 +11,42 @@

requirements = [
'Click>=6.0',
# TODO: put package requirements here
'bigchaindb-common>=0.0.3',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

import bigchaindb here

@TimDaub TimDaub merged commit 81d9eee into master Nov 9, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants