Skip to content

Conversation

@dcousens
Copy link
Contributor

@dcousens dcousens commented Nov 9, 2016

edit: For future reference, the merged constant is a maximum, minimum fee-rate, of 1000 satoshis per byte.

}

TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, sequence, scriptSig, prevOutScript) {
TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) {
Copy link
Contributor Author

@dcousens dcousens Nov 9, 2016

Choose a reason for hiding this comment

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

Maybe this is how we should handle addInput universally for SegWit and the upcoming changes?
Looks like a good balance of positional and named IMHO

Copy link
Contributor

Choose a reason for hiding this comment

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

indeed, looks good!

txb.__addInputUnsafe(txIn.hash, txIn.index, {
sequence: txIn.sequence,
script: txIn.script
})
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 could probably just pass txIn here as options, haha


// its not fool-proof, but, it might help somebody
// fee > 0.2BTC
return fee > (0.2 * 1e8)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should this be TransactionBuilder.ABSURD_FEE or similar?

Copy link
Contributor Author

@dcousens dcousens Nov 9, 2016

Choose a reason for hiding this comment

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

I also don't think anything beyond a hard-coded constant is reasonable without being annoying in actual use cases

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, is this value suitable?

Copy link
Contributor

Choose a reason for hiding this comment

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

  • 👍 for exporting the constant.
  • Not sure if it's suitable at all :D

If it's not a requirement to provide a value, it seems fee can be negative which would cause the function to declare an absurd fee. Is it intended to introduce this behavior in here? https://github.com/bitcoinjs/bitcoinjs-lib/pull/696/files#diff-52af0f8b5da51627aa8d01193636ba73R384

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it seems fee can be negative which would cause the function to declare an absurd fee

What? How?
The fee can be negative, but that is fine. It will still return false as it it less than absurd.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, my mistake

BIP32Path.toJSON = function () { return 'BIP32 derivation path' }

var SATOSHI_MAX = 2.1 * 1e15
var SATOSHI_MAX = 21 * 1e14
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was annoying me

"exception": "Transaction w/ absurd fees",
"inputs": [
{
"txHex": "01000000000100e1f505000000000000000000",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Smallest transaction I've ever seen

@dcousens
Copy link
Contributor Author

dcousens commented Nov 9, 2016

ping @afk11 for ACK/NACK

}

TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, sequence, scriptSig, prevOutScript) {
TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) {
Copy link
Contributor

Choose a reason for hiding this comment

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

indeed, looks good!

if (!this.tx.outs.length) throw new Error('Transaction has no outputs')

// do not rely on this, its merely a last resort
if (this.__hasAbsurdFee()) throw new Error('Transaction has absurd fees')
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any way to disable 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.

No, but why would you?
This is assuming you are building a "final" transaction.
There is never a case where this isn't suitable?

Copy link
Contributor

Choose a reason for hiding this comment

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

The fee wouldn't be absurd if the transaction is very large, or you really want it to confirm. It feels it should be possible without setting allowIncomplete = true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@afk11 if you're doing 150kb transactions 120 satoshis/byte, only then, will you hit this limit.
With a constant TransactionBuilder.ABSURD_FEERATE, a user could change that to by-pass these limits themselves, as they will be a very select few people.


// its not fool-proof, but, it might help somebody
// fee > 0.2BTC
return fee > (0.2 * 1e8)
Copy link
Contributor

Choose a reason for hiding this comment

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

  • 👍 for exporting the constant.
  • Not sure if it's suitable at all :D

If it's not a requirement to provide a value, it seems fee can be negative which would cause the function to declare an absurd fee. Is it intended to introduce this behavior in here? https://github.com/bitcoinjs/bitcoinjs-lib/pull/696/files#diff-52af0f8b5da51627aa8d01193636ba73R384

@afk11
Copy link
Contributor

afk11 commented Nov 11, 2016

I guess my questions are:

  • Can the check be disabled?
  • Should we require the value going forward?
    The output value is optional even with segwit (it's not always required, so can safely allow the user not to provide it in those cases), but we can make it required going forward since we're introducing a break?
  • It doesn't seem to check the transaction size, which could explain such a large fee. Could maybe use ABSURD_FEERATE instead?
    We can determine what the signature size should be for each standard signable type (knowing the scriptPubKey/rs/ws). Can we infer the full size of a transaction, and if provided all values we check if the fee-rate is like 100x some current minimum fee rate?
@dcousens
Copy link
Contributor Author

dcousens commented Nov 11, 2016

Can the check be disabled?

In the cases where you wouldn't want it (incomplete transactions), it is avoided by using buildIncomplete.

Should we require the value going forward?

No? Its optional obviously.

We can determine what the signature size should be for each standard signable type (knowing the scriptPubKey/rs/ws). Can we infer the full size of a transaction, and if provided all values we check if the fee-rate is like 100x some current minimum fee rate?
... It doesn't seem to check the transaction size, which could explain such a large fee. Could maybe use ABSURD_FEERATE instead?

We have the signatures at this point, so it should be straight forward to use fee rate instead.

@dcousens
Copy link
Contributor Author

dcousens commented Nov 11, 2016

  • Change to ABSURD_FEERATE instead of ABSURD_FEE
  • Expose as constant on TransactionBuilder
  • Add canModifyOutputs check which would stop this from being correct
@dcousens
Copy link
Contributor Author

dcousens commented Nov 11, 2016

The assumptions this makes is:

  • Inputs values are undefined or > 0
  • Output values are all > 0, with no more outputs to be added (may need to add a !canModifyOutputs() before checking)
  • fee = incoming - outgoing

If we have any input values, and their sum is larger than the outgoing, we can determine the minimum fee for the transaction (not the actual fee, the minimum).

At that point, we can say, if the minimum is absurd, it doesn't matter what the rest of the inputs are, because they only make it worse.

Naturally, in most cases, the [detected] minimum fee for the transaction is negative, because .value is undefined (and treated as 0).
But, that is fine, because this isn't to be relied on, its simply an aid to prevent [potential] algorithmic mistakes if a user did provide us that information.

@dcousens
Copy link
Contributor Author

dcousens commented Nov 12, 2016

I think an absurd fee rate of >1000 satoshis per byte is a good limit for now.
That is 20 times the average fee rate for the last 6 months (~50 satoshis/byte).

We could easily adjust it up-wards in patch releases.

@dcousens
Copy link
Contributor Author

The only remaining part of the TODO for this PR is:

Add canModifyOutputs check which would stop this from being correct

Personally, I can't decide if this is a good or a bad thing... so I'm going to leave it to a patch PR later rather than do it.

@dcousens
Copy link
Contributor Author

Merging once Travis passes.

@dcousens
Copy link
Contributor Author

dcousens commented Nov 12, 2016

@afk11 this works a treat 👍
See a93e82c

@dcousens
Copy link
Contributor Author

dcousens commented Nov 12, 2016

ping @afk11 to review 6a019ee and then merge (if OK).

@afk11
Copy link
Contributor

afk11 commented Nov 12, 2016

ACK 6a019ee

@afk11 afk11 merged commit aeeee4f into master Nov 12, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3 participants