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

Feature/create tx #160

Merged
merged 23 commits into from
Mar 18, 2014
Merged

Feature/create tx #160

merged 23 commits into from
Mar 18, 2014

Conversation

matiu
Copy link
Contributor

@matiu matiu commented Mar 15, 2014

This implements the method #create in Transactions. Its aims to provide a much simpler interface to create new transactions. Some auxiliary functions were added in Transaction and Address. The test were updated to cover the new code.

The raw output transaction from some given inputs are compared in the tests with the (hardcoded) output of createrawtransaction from bitcoind. The parameters to generate those outputs in bitcoind are included for validation.

In a future PR the transaction signing (#sign) will be implemented
.sign is provided to sign a transaction using a given key array.


return answer;
};

Copy link
Contributor

Choose a reason for hiding this comment

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

Good idea. It might also be a good idea to be able to set the network. The way the version() function works is that you can either get or set the network. The same thing could work here. If arguments[0] is set, set the network to that network. Otherwise, just get the current network.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setting (changing) the network of an existing address seems strange to me :) Do you think that is a real use case?

I added the same to PrivateKey.js

Copy link
Contributor

Choose a reason for hiding this comment

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

It's exactly the same use case as setting the version. You probably wouldn't use it to set the network after, say, creating an address from an address string. But it would be a convenient way to set the network after creating an address from a hash.

For instance, rather than this:

var addr = new bitcore.Address(network.addressVersion, hash);

...which is confusing and has to be looked up every time, to this:

var addr = new bitcore.Address(hash);
addr.network('testnet');

This would require changing the constructor to detect that a buffer was being input as the first argument.

Another possible interface:

var addr = new bitcore.Address('testnet', hash);

Then self.network('testnet') would be called from within the constructor.

I'm not committed to doing this now, maybe this should be in a different PR. But it just occurred to me because the functionality of network() mirrors that of version(), but is more user-friendly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. I usually create address which address string, in which the network info is embedded, i didn't think about initializing with a hash.

I like both:
var addr = new bitcore.Address(hash);
addr.network('testnet');
and
var addr = new bitcore.Address('testnet', hash);

both seems simple and straightforward to implement. Will create a new ticket for that, ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added here:

#161

@matiu
Copy link
Contributor Author

matiu commented Mar 16, 2014

updated with signing capabilities.

answer = testnet;

return answer;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

See my above comment for Address, which is the same for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added here:
#162

@ryanxcharles
Copy link
Contributor

Great work on this @matiu . Will continue code reviewing today.

@matiu
Copy link
Contributor Author

matiu commented Mar 17, 2014

Updated:

  • better coin selection for inputs (yet, there are a lot of enhancements that can be done)
  • dynamic fees (that could trigger rescaning utxos)
  • simplified interface:
    • .create (utxos, outs, keys, opts) will select the coins, generate the tx and sign it.
    • .selectUnspent, .prepare and .sign are used by .create internally. They can be used from the outside to customize the txs.
    • more tests were added.
    • tests pass in browser.
    • CreateTX example / Readme were not updated yet. They will be updated once there is consensus about the API (Update CreateTX.js and Readme example once .create() is merged  #166)

@jgarzik
Copy link
Contributor

jgarzik commented Mar 17, 2014

Mostly sounds good. I thought it was agreed that signing was a separate operation from create?

@matiu
Copy link
Contributor Author

matiu commented Mar 17, 2014

@jgarzik it an be done in 1 step:

   vat tx = Transaction.create(utxos, outs,keys);

Or in 2 (or more):

   vat tx = Transaction.create(utxos,outs);
   tx.sign(utxos, keys);

or

   vat tx = Transaction.create(utxos,outs);
   tx.sign(utxos, key1);
   tx.sign(utxos, key2);
   [..]
   if (tx.isComplete()) {
      ...
   }

The 1 step creation seems to be a good idea for simpler use cases.

@maraoz
Copy link
Contributor

maraoz commented Mar 17, 2014

Awesome work @matiu! This will be a great addition to bitcore. I like the 1-step transaction creation. Is there a reason for requiring 2 steps @jgarzik ?

@ryanxcharles
Copy link
Contributor

Certainly for multisig you need to be able to sign it just once, and then pass the partially signed transaction onto others to continue signing. Seems like "signing" should be a separate event that follows creation of the transaction.

@jgarzik
Copy link
Contributor

jgarzik commented Mar 17, 2014

The logic behind making signing a separate step is that it is security-sensitive. Technically signing is easy to incorporate, but making it a separate step forces people to think about what they are signing, IMO (or forces programmers to think about what they are designing). Making it a separate step is easier to test, and more in line with multisig workflow.

(note: this is not "do it that way" message, just explaining the rationale)

@maraoz
Copy link
Contributor

maraoz commented Mar 17, 2014

Good point. Would you say it shouldn't be possible to make it in one step? I like @matiu's idea of having two ways: the simplified one, with just 1 step:

vat tx = Transaction.create(utxos, outs,keys);

and the separated one, with:

vat tx = Transaction.create(utxos,outs);
tx.sign(utxos, key1);
tx.sign(utxos, key2);

The other issue I see with this API is that it assumes ouput scripts are pubkeyhash (which is OK for a first simple API, but we should at least be thinking how we would incorporate other tx output types)

@ryanxcharles
Copy link
Contributor

Still code reviewing. I definitely think the .create function should not include the .sign function. Instead, .create should only create, but not sign, the transaction. The two could be combined into a .createAndSign function for convenience.

@jrpereira
Copy link

How about making it so you can chain the calls, monad-style? All it takes is for the Transaction to keep a pointer to the utxos (it's already in memory, anyway). That way you could chain the calls and have "sign" only take the keys, which considering the above examples:

vat tx = Transaction.create(utxos, outs).sign(keys);

or

vat tx = Transaction.create(utxos,outs)
         .sign(key1)
         .sign(key2);

@matiu
Copy link
Contributor Author

matiu commented Mar 18, 2014

@jrpereira a similar schema was the original proposal, but was discarded to prevent Transaction storing the utxos silently.

@ryanxcharles Do you think the most common use case will be to first create and the sign the tx? I like you idea, seems simple to understand, and friendly to new users. Will do that now.

@ryanxcharles
Copy link
Contributor

Since I think the bitcoin world is going to move to multisig, I think the most standard use-case will be to create the transaction first, and then sign it later. Signing it upon creation only makes sense if you have all the keys.

@matiu
Copy link
Contributor Author

matiu commented Mar 18, 2014

updated with @ryanxcharles comments.

Current constructors are:

  • createAndSign
  • create

related methods: (these needed to be updated to support p2sh)

  • sign (will throw error if try to sign anything other that TX_PUBKEYHASH)
  • isComplete

Used Internally:

  • createWithFee (creates a TX with fixed fee, and previously selected utxos)
  • calcSize
  • getSize
  • _sumOutputs
  • _scriptForAddress (this supports p2sh)

@jrpereira
Copy link

@ryanxcharles, that makes sense, but doesn't that mean you'll still need the utxos somewhere? Seeing as we're not copying the object (js is copy-on-write, so just storing a pointer to it), isn't the Transaction object an appropriate place to store a reference to this data, particularly if you'll be needing it for later sign operations?

There could be something like a .finalize() call that "seals" the transaction and releases the utxos reference.

@matiu
Copy link
Contributor Author

matiu commented Mar 18, 2014

@jrpereira I think the current PR address all all the comments:

utxos are not stored on the Transaction object. create returns the new tx, and the list of the selected utxos for later signing. That list can also be used in wallet software to flag those outputs, even if the tx is not broadcasted yet.

Finally createAndSign provides a simpler interface for certain use cases.

@ryanxcharles
Copy link
Contributor

@matiu, ACK code changes. Unfortunately there is now a conflict, probably because I pulled in your other PR first. Can you fix the conflicts and update the PR so I can pull it in?

@matiu
Copy link
Contributor Author

matiu commented Mar 18, 2014

no prob, working on it.

On Tue, Mar 18, 2014 at 12:54 PM, Ryan X. Charles
notifications@github.comwrote:

@matiu https://github.com/matiu, ACK code changes. Unfortunately there
is now a conflict, probably because I pulled in your other PR first. Can
you fix the conflicts and update the PR so I can pull it in?


Reply to this email directly or view it on GitHubhttps://github.com//pull/160#issuecomment-37949812
.

Matías Alejo Garcia
Skype/Twitter: @EMATIU
Roads? Where we're going, we don't need roads!

@matiu
Copy link
Contributor Author

matiu commented Mar 18, 2014

updated.

ryanxcharles pushed a commit that referenced this pull request Mar 18, 2014
@ryanxcharles ryanxcharles merged commit 806e424 into bitpay:master Mar 18, 2014
This pull request was closed.
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.

6 participants