JoystreamClassic / extension-cpp

C++ libtorrent extension library
MIT License
0 stars 3 forks source link

Simplifying buying #6

Open bedeho opened 7 years ago

bedeho commented 7 years ago

Background

This proposal is a deep fix to the issue of having the user construct the contract transaction in javascript. We have been discussing moving this into the addon, or the wrapper, to hide some of it from the end user, but at the deepest level, there is a redundancy which this proposal tries to look at the lowest level where it exists.


When trying to start downloading, using the extension::request::StartDownloading, the user of the extension has the following pieces of non-trivial information

Importantly, the extension knows everything it needs to know to construct contractTx on it own, it only requires some means of financing it.

Problem

Requiring user to construct contractTx leaks lots of details about the payment channel into user code, which both makes it more complicated, to manage, test and change. It really should be avoidable to have the user to this redundant work, and instead give the extension the minimal amount of information.

Solutions

Replace contractTx in the extension::request::StartDownloading request with

struct Financing {
  std::vector<Coin::TxIn>> inputs;
  boost::option<Coin::TxOut> possible_change;
}

@brief Gets financing for given outputs and total fee rate constraint.
@param outputs ordered set of outputs, to be financed
@param fee minimum satoshies per Kb constraint required on final transaction
@return financing of the given outputs with given fee constraint
typedef std::function<Financing(const std::vector<Coin::TxOut> & outputs,int64_t fee)> Financier;

which is called by the extension to construct the final contract transaction.

Two approaches

There are two approaches to implementing a Financier instance.

The second option is better, as it avoids any need for synchronization, however, the ability to do so requires a sufficiently flexible signing infrastructure in the application.

Example: joystream-node-cpp

The first approach would require synchronizing with the nodejs thread, which is possible, but non-trivial.

The second approach would require altering the start_download binding in the addon to take some sort of utxo representation. This representation could be converted into an Coin::UnspentOutputSet instance, and capture this inside the Financier. This will require fixing the representation to a predetermined set of UTXO types, e.g. only p2sh, or p2pkh. So a user of the js wrapper would do something like

// p2pkh utxo
var utxo_1 = {
outpoint: ...
sk: ...
value: ...
};

plugin.start_download(..., [utxo_1]);
mnaamani commented 7 years ago

I agree with the need to hide the paymentchannel details from the application layer, and the second approach to your proposed solution is arguably better than the first.

Although there is additional complexity introduced by having a UTXO manager, it does eliminate the construction of the transaction at the application layer, and any need to make calls into the wallet from the plugin to sign/fund a transaction.

The start_download will need to provide the serialized transaction in the callback so the application can broadcast it? Or if it's available in the "DownloadStarted" event payload, the application can get the transaction from there I suppose.

The details of payment channels can creep up again when pre-allocating the utxos however: when selecting the total funds required to make a utxo set which will be captured by the financier it needs to account for the fee that is based on the estimated size of the contract Tx.

Another approach is possible, by not having to pre-allocate utxos. Add a new plugin function

plugin.prepare_contract(PeerToStartDownloadInformationMap, function callback(contracTx, feeRate){

})

which submits a new plugin request "PrepareContract" that will create a partial contract transaction (containing commitment outputs, no inputs, no change output).

The application can then fund and sign the contract with a given feeRate, and make a subsequent call to plugin.start_downloading, passing in the signed transaction and the same peerToStartDownloadInformationMap.

This solves the need to know details about how to construct the transaction in javascript, but still has to be aware of the fact that a contract needs to be prepared.

bedeho commented 7 years ago

Continuation from our Slack discussion

GOAL: minimize buying complexity for user of javascript developer, and perhaps also extension-cpp

FIX 1: Plugin builds contract, signs using callback c++ callback (which captures nodejs utxo state)

extension-cpp

joystream-node-cpp

joystream-node-npm

user/utility code

FIX 2: Native addon builds contract, signs using js callback

extension-cpp

joystream-node-cpp

joystream-node-npm

FIX 3: Javascript wrapper builds contract, and signs using js callback

extension-cpp

joystream-node-cpp

joystream-node-npm

FIX 4: Plugin does it all