Merge pull request #690 from eordano/feature/miscupdates
Miscelaneus updates to docs and bufferreader/writer
This commit is contained in:
commit
b8601ecd46
|
@ -27,7 +27,7 @@
|
||||||
"maxstatements": 15, // Maximum number of statements in a function
|
"maxstatements": 15, // Maximum number of statements in a function
|
||||||
"maxcomplexity": 4, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
|
"maxcomplexity": 4, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity)
|
||||||
"maxdepth": 4, // Maximum depth of nested control structures
|
"maxdepth": 4, // Maximum depth of nested control structures
|
||||||
"maxlen": 600, // Maximum number of lines of code in a file
|
"maxlen": 120, // Maximum number of cols in a line
|
||||||
|
|
||||||
"predef": [ // Extra globals.
|
"predef": [ // Extra globals.
|
||||||
"after",
|
"after",
|
||||||
|
|
|
@ -19,7 +19,7 @@ own.
|
||||||
So, in order to transmit a valid transaction, you must know what other transactions
|
So, in order to transmit a valid transaction, you must know what other transactions
|
||||||
on the network store outputs that have not been spent and that are available
|
on the network store outputs that have not been spent and that are available
|
||||||
for you to spend (meaning that you have the set of keys that can validate you
|
for you to spend (meaning that you have the set of keys that can validate you
|
||||||
own those funds). The unspent outputs are usually refered to as "utxo"s.
|
own those funds). The unspent outputs are usually referred to as "utxo"s.
|
||||||
|
|
||||||
Let's take a look at some very simple transactions:
|
Let's take a look at some very simple transactions:
|
||||||
|
|
||||||
|
@ -46,26 +46,7 @@ bitcoin-cli sendrawtransaction <serialized transaction>
|
||||||
## Transaction API
|
## Transaction API
|
||||||
|
|
||||||
You can take a look at the javadocs for the [Transaction class here](link
|
You can take a look at the javadocs for the [Transaction class here](link
|
||||||
missing). This document will go over the expected high level use cases.
|
missing).
|
||||||
|
|
||||||
* from(utxo)
|
|
||||||
* from(utxo, pubkeys, threshold)
|
|
||||||
* change(address)
|
|
||||||
* fee(amount)
|
|
||||||
* usingStrategy(strategy)
|
|
||||||
* to(address, amount)
|
|
||||||
* to(pubKeySet, amount)
|
|
||||||
* addData(opReturnValue)
|
|
||||||
* sign(privKey)
|
|
||||||
* sign(privKeySet)
|
|
||||||
* applySignature(signature)
|
|
||||||
* missingSignatures()
|
|
||||||
* isValidSignature(signature)
|
|
||||||
* getSignatures(privKey)
|
|
||||||
* getSignatures(privKeySet)
|
|
||||||
* isFullySigned()
|
|
||||||
* toBuffer()
|
|
||||||
* toJSON()
|
|
||||||
|
|
||||||
## Multisig Transactions
|
## Multisig Transactions
|
||||||
|
|
||||||
|
@ -77,7 +58,7 @@ output.
|
||||||
```javascript
|
```javascript
|
||||||
var multiSigTx = new Transaction()
|
var multiSigTx = new Transaction()
|
||||||
.fromMultisig(utxo, publicKeys, threshold)
|
.fromMultisig(utxo, publicKeys, threshold)
|
||||||
.spendAllTo(address, amount)
|
.change(address)
|
||||||
.sign(myKeys);
|
.sign(myKeys);
|
||||||
|
|
||||||
var serialized = multiSigTx.serialize();
|
var serialized = multiSigTx.serialize();
|
||||||
|
@ -95,52 +76,61 @@ signatures:
|
||||||
|
|
||||||
## Advanced topics
|
## Advanced topics
|
||||||
|
|
||||||
|
### Internal Workings
|
||||||
|
|
||||||
|
There are a number of data structures being stored internally in a
|
||||||
|
`Transaction` instance. These are kept up to date and change through successive
|
||||||
|
calls to its methods.
|
||||||
|
|
||||||
|
* `inputs`: The ordered set of inputs for this transaction
|
||||||
|
* `outputs`: This is the ordered set of output scripts
|
||||||
|
* `_outpoints`: The ordered set of outpoints (a string that combines a
|
||||||
|
transaction hash and output index), UTXOs that will be inputs to this
|
||||||
|
transaction. This gets populated on calls to `from` and also when
|
||||||
|
deserializing from a serialized transaction.
|
||||||
|
* `_utxos`: Maps an outpoint to information regarding the output on that
|
||||||
|
transaction. The values of an output are:
|
||||||
|
- script: the associated script for this output. Will be an instance of
|
||||||
|
`Script`
|
||||||
|
- satoshis: amount of satoshis associated with this output
|
||||||
|
- txId: the transaction id from the outpoint
|
||||||
|
- outputIndex: the index of this output in the previous transaction
|
||||||
|
* `_inputAmount`: sum the amount for all the inputs
|
||||||
|
* `_signatures`: This is the ordered set of `scriptSig`s that are going to be
|
||||||
|
included in the serialized transaction. These are objects with the following
|
||||||
|
values:
|
||||||
|
- publicKey: the public key that generated the signature
|
||||||
|
- prevTxId: the previous transaction hash
|
||||||
|
- outputIndex: the index for the output that this input is signing
|
||||||
|
- signature: the `Signature` for that public key
|
||||||
|
- sigtype: the type of the signature (`Signature.SIGHASH_ALL` is the only one implemented)
|
||||||
|
* `_change`: stores the value provided by calling the `change` method.
|
||||||
|
|
||||||
|
TO BE IMPLEMENTED YET:
|
||||||
|
* `_fee`: if user specified a non-standard fee, the amount (in satoshis) will
|
||||||
|
be stored in this variable so the change amount can be calculated.
|
||||||
|
* `_p2shMap`: For the case of P2SH spending, the user needs to supply
|
||||||
|
information about the script that hashes to the hash of an UTXO. This map
|
||||||
|
goes from the hash of the spend script to that script. When using the
|
||||||
|
`from(utxo, publicKeys, threshold)` function, this map gets populated with a
|
||||||
|
standard multisig spend script with public keys in the order provided.
|
||||||
|
|
||||||
### Unspent Output Selection
|
### Unspent Output Selection
|
||||||
|
|
||||||
If you have a larger set of unspent outputs, only some of them will be selected
|
If you have a larger set of unspent outputs, only some of them will be selected
|
||||||
to fulfill the amount. This is done by storing a cache of unspent outputs in a
|
to fulfill the amount. This is done by storing a cache of unspent outputs in a
|
||||||
protected member called `_utxos`. When the `to()` method is called, some of
|
protected member called `_utxos`. When the `to()` method is called, some of
|
||||||
these outputs will be selected to pay the requested amount to the appropiate
|
these outputs will be selected to pay the requested amount to the appropriate
|
||||||
address.
|
address.
|
||||||
|
|
||||||
There are some nits that you should have in mind when using this API:
|
A nit that you should have in mind is that when the transaction is serialized,
|
||||||
|
this cache can't be included in the serialized form.
|
||||||
* When a signature is added, the corresponding utxo is removed from the cache.
|
|
||||||
* When the transaction is serialized, this cache is not included in the
|
|
||||||
serialized form.
|
|
||||||
|
|
||||||
#### Spending Strategies
|
|
||||||
|
|
||||||
We have implemented partially Merge Avoidance for the change
|
|
||||||
addresses of a transaction with a simple API:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var mergeAvoidance = new Transaction.Strategy.MergeAvoidance({
|
|
||||||
change: ['1bitcoinChange...', '3bitcoinChange...']
|
|
||||||
});
|
|
||||||
var transaction = new Transaction()
|
|
||||||
.usingStrategy(mergeAvoidance)
|
|
||||||
.to(['1target...', '3anaddress...'], amount)
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that this will not create multiple transactions, which would increase
|
|
||||||
privacy. The `MergeAvoidance` API will take care that the target and change
|
|
||||||
addresses provided will receive as equally distributed outputs as possible,
|
|
||||||
using a simple algorithm.
|
|
||||||
|
|
||||||
In the future, if a Stealth Address is provided to the `to` method and the
|
|
||||||
strategy being used is `MergeAvoidance`, it will derive as many addresses as
|
|
||||||
needed according to the utxos received. In a similar fashion, we are discussing
|
|
||||||
how to provide an API for using an extended public key to derive change
|
|
||||||
addresses, but as the user of the library should be in control of the policy
|
|
||||||
for deriving keys (so no transaction outputs gets unnoticed), it's proving to
|
|
||||||
be a hard problem to solve and it may end up being the user's responsability.
|
|
||||||
|
|
||||||
## Upcoming changes
|
## Upcoming changes
|
||||||
|
|
||||||
We're debating an API for full Merge Avoidance, CoinJoin, Smart contracts,
|
We're debating an API for Merge Avoidance, CoinJoin, Smart contracts, CoinSwap,
|
||||||
CoinSwap, and Stealth Addresses. We're expecting to have all of them by some
|
and Stealth Addresses. We're expecting to have all of them by some time in
|
||||||
time in 2015.
|
early 2015.
|
||||||
|
|
||||||
A first draft of a Payment Channel smart contract modular extension to this
|
A first draft of a Payment Channel smart contract modular extension to this
|
||||||
library is being implemented independently
|
library is being implemented independently
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
var BN = require('../crypto/bn');
|
var BN = require('../crypto/bn');
|
||||||
|
|
||||||
var BufferReader = function BufferReader(buf) {
|
var BufferReader = function BufferReader(buf) {
|
||||||
if (!(this instanceof BufferReader))
|
if (!(this instanceof BufferReader)) {
|
||||||
return new BufferReader(buf);
|
return new BufferReader(buf);
|
||||||
|
}
|
||||||
if (Buffer.isBuffer(buf)) {
|
if (Buffer.isBuffer(buf)) {
|
||||||
this.set({buf: buf});
|
this.set({buf: buf});
|
||||||
}
|
}
|
||||||
|
@ -25,8 +27,11 @@ BufferReader.prototype.eof = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
BufferReader.prototype.read = function(len) {
|
BufferReader.prototype.read = function(len) {
|
||||||
if (!len)
|
if (_.isUndefined(len)) {
|
||||||
var len = this.buf.length;
|
len = this.buf.length;
|
||||||
|
console.error('No length provided');
|
||||||
|
console.trace();
|
||||||
|
}
|
||||||
var buf = this.buf.slice(this.pos, this.pos + len);
|
var buf = this.buf.slice(this.pos, this.pos + len);
|
||||||
this.pos = this.pos + len;
|
this.pos = this.pos + len;
|
||||||
return buf;
|
return buf;
|
||||||
|
@ -71,7 +76,7 @@ BufferReader.prototype.readUInt64BEBN = function() {
|
||||||
|
|
||||||
BufferReader.prototype.readUInt64LEBN = function() {
|
BufferReader.prototype.readUInt64LEBN = function() {
|
||||||
var buf = this.buf.slice(this.pos, this.pos + 8);
|
var buf = this.buf.slice(this.pos, this.pos + 8);
|
||||||
var reversebuf = BufferReader({buf: buf}).reverse().read();
|
var reversebuf = BufferReader({buf: buf}).readReverse();
|
||||||
var bn = BN().fromBuffer(reversebuf);
|
var bn = BN().fromBuffer(reversebuf);
|
||||||
this.pos = this.pos + 8;
|
this.pos = this.pos + 8;
|
||||||
return bn;
|
return bn;
|
||||||
|
@ -87,10 +92,12 @@ BufferReader.prototype.readVarintNum = function() {
|
||||||
case 0xFF:
|
case 0xFF:
|
||||||
var bn = this.readUInt64LEBN();
|
var bn = this.readUInt64LEBN();
|
||||||
var n = bn.toNumber();
|
var n = bn.toNumber();
|
||||||
if (n <= Math.pow(2, 53))
|
if (n <= Math.pow(2, 53)) {
|
||||||
return n;
|
return n;
|
||||||
else
|
} else {
|
||||||
throw new Error('number too large to retain precision - use readVarintBN');
|
throw new Error('number too large to retain precision - use readVarintBN');
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
@ -126,10 +133,24 @@ BufferReader.prototype.readVarintBN = function() {
|
||||||
|
|
||||||
BufferReader.prototype.reverse = function() {
|
BufferReader.prototype.reverse = function() {
|
||||||
var buf = new Buffer(this.buf.length);
|
var buf = new Buffer(this.buf.length);
|
||||||
for (var i = 0; i < buf.length; i++)
|
for (var i = 0; i < buf.length; i++) {
|
||||||
buf[i] = this.buf[this.buf.length - 1 - i];
|
buf[i] = this.buf[this.buf.length - 1 - i];
|
||||||
|
}
|
||||||
this.buf = buf;
|
this.buf = buf;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferReader.prototype.readReverse = function(len) {
|
||||||
|
if (_.isUndefined(len)) {
|
||||||
|
len = this.buf.length;
|
||||||
|
}
|
||||||
|
var buf = this.buf.slice(this.pos, this.pos + len);
|
||||||
|
this.pos = this.pos + len;
|
||||||
|
var buf2 = new Buffer(buf.length);
|
||||||
|
for (var i = 0; i < buf2.length; i++) {
|
||||||
|
buf2[i] = buf[buf.length - 1 - i];
|
||||||
|
}
|
||||||
|
return buf2;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = BufferReader;
|
module.exports = BufferReader;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var bufferUtil = require('../util/buffer');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
var BufferWriter = function BufferWriter(obj) {
|
var BufferWriter = function BufferWriter(obj) {
|
||||||
if (!(this instanceof BufferWriter))
|
if (!(this instanceof BufferWriter))
|
||||||
return new BufferWriter(obj);
|
return new BufferWriter(obj);
|
||||||
|
@ -23,6 +26,7 @@ BufferWriter.prototype.concat = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
BufferWriter.prototype.write = function(buf) {
|
BufferWriter.prototype.write = function(buf) {
|
||||||
|
assert(bufferUtil.isBuffer(buf));
|
||||||
this.bufs.push(buf);
|
this.bufs.push(buf);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
@ -55,6 +59,13 @@ BufferWriter.prototype.writeUInt32BE = function(n) {
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferWriter.prototype.writeInt32LE = function(n) {
|
||||||
|
var buf = new Buffer(4);
|
||||||
|
buf.writeInt32LE(n, 0);
|
||||||
|
this.write(buf);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
BufferWriter.prototype.writeUInt32LE = function(n) {
|
BufferWriter.prototype.writeUInt32LE = function(n) {
|
||||||
var buf = new Buffer(4);
|
var buf = new Buffer(4);
|
||||||
buf.writeUInt32LE(n, 0);
|
buf.writeUInt32LE(n, 0);
|
||||||
|
|
|
@ -5,6 +5,19 @@ var assert = require('assert');
|
||||||
|
|
||||||
var js = require('./js');
|
var js = require('./js');
|
||||||
|
|
||||||
|
function equals(a, b) {
|
||||||
|
if (a.length !== b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var length = a.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
if (a[i] !== b[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* Returns true if the given argument is an instance of a buffer. Tests for
|
* Returns true if the given argument is an instance of a buffer. Tests for
|
||||||
|
@ -38,6 +51,9 @@ module.exports = {
|
||||||
*/
|
*/
|
||||||
concat: buffer.Buffer.concat,
|
concat: buffer.Buffer.concat,
|
||||||
|
|
||||||
|
equals: equals,
|
||||||
|
equal: equals,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms a number from 0 to 255 into a Buffer of size 1 with that value
|
* Transforms a number from 0 to 255 into a Buffer of size 1 with that value
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue