Pages:
Author

Topic: Transaction script with block height as condition (Read 3189 times)

legendary
Activity: 1792
Merit: 1087
I've been reading some threads on allowing checks of block height in scripts, and I still don't understand what's so dramatically bad about it. It's clear enough that a previously valid transaction can become invalid after a reorganisation, but that is solved in most cases if you wait for enough confirmations. The exception appears to be that after a lengthy segmentation you would get much more collateral damage than usual.

There was some suggestion that it might also allow people to deliberately create reorganisations, but I don't understand how that would work. Also, some developers seem to have changed their minds a bit. So, what's current thinking about a block height opcode and what exactly are the scenarios people are worried about?

With the random malleability attack, the refund transaction in micro-payment channel is no longer reliable, as the txid is not fixed until confirmed. Pushing block height and block timestamp in the script will be very useful.

By the way, malleability will also make any previously valid transaction becoming invalid in case of reorg.
hero member
Activity: 714
Merit: 500
Martijn Meijering
I've been reading some threads on allowing checks of block height in scripts, and I still don't understand what's so dramatically bad about it. It's clear enough that a previously valid transaction can become invalid after a reorganisation, but that is solved in most cases if you wait for enough confirmations. The exception appears to be that after a lengthy segmentation you would get much more collateral damage than usual.

There was some suggestion that it might also allow people to deliberately create reorganisations, but I don't understand how that would work. Also, some developers seem to have changed their minds a bit. So, what's current thinking about a block height opcode and what exactly are the scenarios people are worried about?
legendary
Activity: 1204
Merit: 1015
If you held a pre-signed transaction that sends the funds back to you with a lockTime of 1 Jan 2013 that would work.

Lets see... thinking out loud...

Start by asking the exchange for a brand-new public key to use for their half of the 2-of-2 transaction. Call the send-coins-into-2-of-2 transaction "F" (for Fund).

You create and sign that transaction, but don't broadcast it yet.

Use it's transaction id to create a second, one-input-two-signature, lockTime=1/1/2013 transaction that refunds the coins to you.  Call that "R" (for Refund).

Send R to the exchange and ask them to sign it using that brand-new public key they gave you. The exchange checks the lockTime and then returns R and the signature to you. You check the signature, and if it is good, broadcast F (and keep the half-signed R someplace safe).

If 1/1/2013 rolls around and you want your coins back, you sign your half of R and broadcast it.



I'd have to think a little harder than I want to right now about whether or not signing R knowing only txid==HASH(F) opens up the exchange to attacks. I can't think of any, but the exchange providing a signature when it doesn't know the details of exactly what it is signing makes me nervous.
That should be safe if and only if a unique key is used to sign, and you can be absolutely certain that it has not nor will not be used for anything else until the exchange receives the signed transaction F. That means that the key can't be from a pre-generated list that may have been backed up, because that backup might eventually need to be used and the key could unknowingly be used for something else. It'd be a hard attack to pull off, but the potential for an attack is certainly there. So, the exchange would need to generate the key for signing transaction R on-the-fly and mark it specifically for this purpose. Thus, there's no risk that the key might be used for something else once it is backed up. If F is never sent then, the worst-case scenario is that a brand-new key will have only ever signed one transaction and never received anything. Since the key would have to be newly-generated, the worst-case scenario is that the exchange loses that key but you still have a signed transaction to get your money back later, anyway.

I think holding on to pre-signed-but-not-broadcast-yet transactions is a technique "we" don't think about enough.
That being said, I also have a more general-case solution that will allow this technique to work in all cases...
You could send the unsigned R and the signed-but-not-broadcast F to the exchange and trust that the exchange will not broadcast F unless they agree to sign R.
Generally that may be safe, but there's a chance that your money might fall into the void. So, the problem here is that F needs to be invalid when we send it to the exchange, but needs to be able to be made valid without changing anything in the transaction, meaning that we can't withhold signatures. So, let's just not withhold anything in this transaction at all. The very problem we're seeing here is actually it's own solution: the exchange doesn't want to sign something when it only knows the hash of an input transaction, but not the input transaction itself. It's the same for miners: they need the input transactions, not just the hashes, to enter a transaction into the blockchain.

So, the user just needs to not tell anyone about "Fi", an input to "F", until they get back "R". If "F" is broadcast and "R" is never signed, it's okay, because "F" was just an orphan and will never hit the blockchain unless the user broadcasts "Fi".
legendary
Activity: 1792
Merit: 1087
It seems that my question was not answered. At least I would like to know why it does not work.

I have modified my proposed scheme. A script could be constructed like this:

1. At any time, the coin can be spent by satisfying condition A (e.g. signatures from X and Y)
2. If block height >= n, the coin can be spent by satisfying condition A OR condition B (e.g. signature from Z)

Therefore, the condition A permanently valid. The condition B is always invalid before block height = n, and permanently true after. It is equivalent to an nLockTime tx, signed by condition A, and send to an output for condition B.

It is better than using nLockTime because the user can deposit to the script hash at any time, without the need of sending nLockTime transaction back-and-forth and storing them.
legendary
Activity: 1792
Merit: 1087
So you're saying I should put my money into a state where if I want to send it to MtGox, that's fast, but if I want to use it for something else, I have to wait. I don't see why this is better than "if I want to use my money for something else, I can do so immediately, and if I want to trade, I have to wait". Given that in a healthy system most users will be using Bitcoin to buy goods/services rather than trying to exploit currency fluctuations ...

You can still use it for something else. As long as MtGox is working properly, they will send the coins in the 2-by-2 address to any address based on your instruction. The nlocktime transaction sending back to you is only an "emergency exit": if MtGox is suddenly closed, you will still get your coins back some time in the future. In 99.9% cases, you don't actually need to use this nlocktime tx.
legendary
Activity: 1204
Merit: 1015
It buys instant confirmation for the receiver if the 2-of-2 escrow with refund was staged before it was needed, since the receiver would have to know of any double-spends simply because they would have had to sign them. This is perfect for staging Bitcoins on an exchange so that you don't have to wait 6 confirmations before you can make a trade, but without the risk of them being stolen in a hack or lost due to the site going down.

How does it make a difference? I have a payment from me to {me,mtgox} and a signed payment back from {me,mtgox} to me, then when I decide to send that money to MtGox so I can trade with it, they still have to wait the 6 confirmations to be sure I don't double-spend with the refund tx.
As jl2012 mentioned, that isn't the case at all. As long as the locktime is sufficiently in the future, there is little concern that the refund transaction will get into a block before the spending transaction does. This is completely different than the risks associated with accepting regular zero-confirmation transactions. And in the end, all that it comes down to is risk.

If I don't have a signed refund TX then if the site goes offline, I can't get my money back either.
Even without a refund transaction, this is still better than the current situation since the bitcoins would be provably in "cold storage" (in a sense, not literally, since both keys could be online) and unspendable by hackers. This is compared to us just having to trust that a cold storage exists.

You're right, you could try and do a Finney attack against your own transaction. However this is a bizarre hack that tries to recreate transaction replacement. It'd be better to just re-enable that feature, and then the whole scheme makes sense (my point was specifically about the suggestion of doing it before tx replacement is re-activated). We need replacement for other things anyway. The recovery transaction can have sequence numbers of zero. Because only somebody who can decrypt the wallet can create a replacement for it, as long as you notice the recovery transaction was broadcast by a thief it is safe to have it lying around. It's a neat idea.
Oh, yes, it is totally a hack. But, it would also be totally compatible with transaction replacement if it is re-enabled.

I think in future it would make sense to change miners so they discourage blocks that include double spends against their memory pool. Finney attacks aren't helpful today to the network and discouraging blocks that contain them would help many people. It has to be thought about carefully to avoid opening up new attacks though.
Agreed. However, in addition to making sure not to open up new attacks, we also have to be careful and not prevent future legitimate reasons for doing a Finney attack.

So you're saying I should put my money into a state where if I want to send it to MtGox, that's fast, but if I want to use it for something else, I have to wait.
Not at all. At least, no more than you have to wait for a normal transaction. Remember, we're assuming that we still trust our example of MtGox in general, just not 100% of the time (which is what we must currently accept if we want to stage funds there). So, you release the funds to MtGox and immediately withdraw through their interface. If the withdrawal exceed their hot wallet, there will have to be a transaction dependent on the release transaction, true, making things slower. However, if MtGox is willing to hold some of their own funds in a hot wallet for their customer's convenience, it'd be no different than sending a transaction from MtGox now.

Using the release transaction as a dependency for withdrawal like this would certainly decrease the risks, though. In fact, a withdrawal could even be sent directly from the staged transaction, making the withdrawal transaction just a regular bitcoin transaction (with all the risks that entails) that MtGox signed off on to acknowledge that the funds are no longer staged.

I don't see why this is better than "if I want to use my money for something else, I can do so immediately, and if I want to trade, I have to wait". Given that in a healthy system most users will be using Bitcoin to buy goods/services rather than trying to exploit currency fluctuations ...
The only downside to this over storing bitcoins in your own wallet is that you have to trust that when you want to spend the bitcoins that whoever you have them staged with will let you. If they don't let you, you'll just have to wait until the refund transaction matures.

The upside, however, is that it can also be a green address that was used to sign off on the release of the bitcoins, with all the benefits that entails.
legendary
Activity: 1526
Merit: 1129
So you're saying I should put my money into a state where if I want to send it to MtGox, that's fast, but if I want to use it for something else, I have to wait. I don't see why this is better than "if I want to use my money for something else, I can do so immediately, and if I want to trade, I have to wait". Given that in a healthy system most users will be using Bitcoin to buy goods/services rather than trying to exploit currency fluctuations ...
legendary
Activity: 1792
Merit: 1087
It buys instant confirmation for the receiver if the 2-of-2 escrow with refund was staged before it was needed, since the receiver would have to know of any double-spends simply because they would have had to sign them. This is perfect for staging Bitcoins on an exchange so that you don't have to wait 6 confirmations before you can make a trade, but without the risk of them being stolen in a hack or lost due to the site going down.

How does it make a difference? I have a payment from me to {me,mtgox} and a signed payment back from {me,mtgox} to me, then when I decide to send that money to MtGox so I can trade with it, they still have to wait the 6 confirmations to be sure I don't double-spend with the refund tx.

If I don't have a signed refund TX then if the site goes offline, I can't get my money back either.



Use the nlocktime flag so the refund TX won't be valid before certain time in the future
legendary
Activity: 1526
Merit: 1129
It buys instant confirmation for the receiver if the 2-of-2 escrow with refund was staged before it was needed, since the receiver would have to know of any double-spends simply because they would have had to sign them. This is perfect for staging Bitcoins on an exchange so that you don't have to wait 6 confirmations before you can make a trade, but without the risk of them being stolen in a hack or lost due to the site going down.

How does it make a difference? I have a payment from me to {me,mtgox} and a signed payment back from {me,mtgox} to me, then when I decide to send that money to MtGox so I can trade with it, they still have to wait the 6 confirmations to be sure I don't double-spend with the refund tx.

If I don't have a signed refund TX then if the site goes offline, I can't get my money back either.

"Once broadcast the inputs that transaction spends are used and double spends won't be accepted" ... into the memory pool. Unless I'm totally wrong, that means that all you need to do is manage to get your replacement transaction into a block before the time locked transaction can be entered into a block (which if you did it right, should be several months). For an entity like MtGox, even without transaction replacement being generally enabled on the network, this would be no problem.

You're right, you could try and do a Finney attack against your own transaction. However this is a bizarre hack that tries to recreate transaction replacement. It'd be better to just re-enable that feature, and then the whole scheme makes sense (my point was specifically about the suggestion of doing it before tx replacement is re-activated). We need replacement for other things anyway. The recovery transaction can have sequence numbers of zero. Because only somebody who can decrypt the wallet can create a replacement for it, as long as you notice the recovery transaction was broadcast by a thief it is safe to have it lying around. It's a neat idea.

I think in future it would make sense to change miners so they discourage blocks that include double spends against their memory pool. Finney attacks aren't helpful today to the network and discouraging blocks that contain them would help many people. It has to be thought about carefully to avoid opening up new attacks though.

flipperfish, your understanding is correct.
legendary
Activity: 1204
Merit: 1015
I don't think 2-of-2 escrow buys you anything, which is why I haven't discussed it in any of my posts/talks/wiki pages.
It buys instant confirmation for the receiver if the 2-of-2 escrow with refund was staged before it was needed, since the receiver would have to know of any double-spends simply because they would have had to sign them. This is perfect for staging Bitcoins on an exchange so that you don't have to wait 6 confirmations before you can make a trade, but without the risk of them being stolen in a hack or lost due to the site going down.

The "dead mans switch" idea doesn't work either. If the key the recovery transaction to is also encrypted then it doesn't help if you forget the password. If it's not encrypted then if somebody gains access to your wallet, they can broadcast the recovery transaction and make your wallet unspendable, then broadcast a spend of the recovery transactions output. The thief would have to wait a few months to claim the money but they'd get it eventually. Not to mention that the recovery transaction would have to be rebuilt every time the wallet contents changed!

Anyone thinking about nLockTime should remember this - a transaction that is time locked enters the memory pool and stays there until the lock time is reached. Once broadcast the inputs that transaction spends are used and double spends won't be accepted.
"Once broadcast the inputs that transaction spends are used and double spends won't be accepted" ... into the memory pool. Unless I'm totally wrong, that means that all you need to do is manage to get your replacement transaction into a block before the time locked transaction can be entered into a block (which if you did it right, should be several months). For an entity like MtGox, even without transaction replacement being generally enabled on the network, this would be no problem.
sr. member
Activity: 350
Merit: 251
Dolphie Selfie
Thank you for taking your time to explain these issues to me. But unfortunateley I still can't follow you're explanaitions.

Let's leave the more advanced scenarios aside for the moment and concentrate just on replacing a single broadcasted, but time-locked transaction. With transaction replacement enabled, it seems possible to do just that, as long as the same inputs are used within the replacing transaction.

So assume this order of actions:

Pre-Conditions:
- Transaction replacement is enabled.

Invariants:
- Input A is not used in any other not explicitly listed transaction.

1.) Create and broadcast a time-locked (until t_lock) transaction with input A and Output 1
2a.) Before t_lock create and broadcast a non-time-locked transaction with input A and Output 2 (different from Output 1)
2b.) After t_lock create and broadcast a non-time-locked transaction with input A and Output 3 (different form Output 1 and 2)

What happens in 2a.) and 2b.)?

My guess:
2a.) The transaction from 1.) is replaced in the memory pool. As the time-constraint is now removed it can be included in a block. The transaction becomes valid, outputs of input A become available at Output 2.
2b.) The transaction from 1.) is included in a block because the time-constraint is met. The transaction from 2b.) will be considered invalid because the inputs have been already spent. The outputs of input A are availible at Output 1.


What happens if there is no transaction replacement (= current implementation?)?

My guess:
2a. and 2b.) The transaction from 1.) is in the memory pool and can't be replaced. It stays there until the time-constraint is met. The outputs of input A finally become availiable at Output 1.
legendary
Activity: 1526
Merit: 1129
Transaction replacement lets you replace transactions in the memory pool, yes. However that transaction must have the same inputs as the previous transaction. Perhaps I misunderstood what you're saying. Even with transaction replacement enabled, once broadcast some version of the transaction is in the memory pool at all times and all the inputs are marked as spent. You can't change which inputs are spent by replacing it.
sr. member
Activity: 350
Merit: 251
Dolphie Selfie
Replacement transactions need to have the same inputs as the transaction they are replacing, so no, it makes no difference.
Of course I want to replace a transaction with the same inputs. How else would it be possible to invalidate the time-locked transaction? I thought "transaction replacement" enables the replacement (or better "update") of pending transactions in the memory pool. Or is this another unlucky naming of things, which actually do something else?
legendary
Activity: 1526
Merit: 1129
Replacement transactions need to have the same inputs as the transaction they are replacing, so no, it makes no difference.
sr. member
Activity: 350
Merit: 251
Dolphie Selfie
a transaction that is time locked enters the memory pool and stays there until the lock time is reached. Once broadcast the inputs that transaction spends are used and double spends won't be accepted.

This could be changed by implementing transaction replacement (correct me if I'm wrong) and would make time-locked transactions (and both mentioned use cases) useful. That's why I'm asking, if there is actively being worked on this.
legendary
Activity: 1526
Merit: 1129
I don't think 2-of-2 escrow buys you anything, which is why I haven't discussed it in any of my posts/talks/wiki pages.

If I want to screw the seller, then I'll just broadcast the refund transaction after driving away and it'll enter the memory pool, preventing any other transactions from spending the 2-of-2 output. I get the money back (after a few months).

If the seller wants to screw me, after creating the 2-of-2 transaction and we agree on the sale by both signing for the multisig output, he broadcasts the tx giving himself the money and then refuses to give me the car keys. He drives away and I lose the money.

In no case are we better off than just using a regular payment.

2-of-3 with a mediator doesn't have these problems, nor does a smart property construction in which the transaction making the payment also transfers control of the car atomically.

The "dead mans switch" idea doesn't work either. If the key the recovery transaction to is also encrypted then it doesn't help if you forget the password. If it's not encrypted then if somebody gains access to your wallet, they can broadcast the recovery transaction and make your wallet unspendable, then broadcast a spend of the recovery transactions output. The thief would have to wait a few months to claim the money but they'd get it eventually. Not to mention that the recovery transaction would have to be rebuilt every time the wallet contents changed!

Anyone thinking about nLockTime should remember this - a transaction that is time locked enters the memory pool and stays there until the lock time is reached. Once broadcast the inputs that transaction spends are used and double spends won't be accepted.
sr. member
Activity: 350
Merit: 251
Dolphie Selfie
You don't need the whole network to support transaction replacement to do that; just write some code that holds a time-locked transaction, has a way of replacing it with another time-locked transaction, and have it automatically broadcast the transaction onto the network if not replaced before the time-lock expires.

I had in mind to not broadcast the time-locked tx until it's needed. But what happens, if it gets broadcasted somehow before I can replace it? Does it get included into a block? Does it hang around in the network as some zombie-tx, which can not yet be included in a block, but is valid and can't be thrown away?

As far as I know the only way to replace such a transaction is by spending the same outputs without nLockTime set. But then the client would not relay the transaction because replacement is not enabled and it would be considered as double-spend, right?
legendary
Activity: 1652
Merit: 2216
Chief Scientist
... But the best solution I can imagine, is to store a signed transaction with nLockTime set to some time in the future, which sends the coins in the encrypted wallet to some other address....

You don't need the whole network to support transaction replacement to do that; just write some code that holds a time-locked transaction, has a way of replacing it with another time-locked transaction, and have it automatically broadcast the transaction onto the network if not replaced before the time-lock expires.

It would be more convenient to have the whole network support transaction replacement, but I don't think it is reasonable for the whole network to remember everybody's dead-man-switch transactions. And even if they did, you'd run the risk that a miner eager to get an extra transaction fee would ignore a newer version of the transaction and mine the old transaction.
sr. member
Activity: 350
Merit: 251
Dolphie Selfie
Is there actively being worked on enabling replacement of transactions?

A great application for nLockTime would be a dead-man-switch for encrypted wallets. The main problem I see with encrypted wallets for the average user is, that if the password is lost, the coins are lost, too. I have been thinking for a long time about a solution, where to store the password (besides my brain), so that in case I suffer from amnesia, I still have access to my coins. But the best solution I can imagine, is to store a signed transaction with nLockTime set to some time in the future, which sends the coins in the encrypted wallet to some other address. The private key to this new address could be given to someone/stored somewhere you trust in the case you suffer from amnesia, but do not trust for day-to-day usage. As long as you remember the password to the wallet, the nLockTime-Transaction can be refresehed, so it never gets locked. In case you forget the password, the coins become availiable after the nLockTime-Transaction locks.

I believe this idea is not new, but it would be nice to have it implemented soon, because it helps to make bitcoin more reliable and thus more trusted by average people.
staff
Activity: 4172
Merit: 8419
Yes, he is correct. So it is possible to incorporate the idea of nLockTime into script? For example:

1. The coin can be spent by 2-by-2 multisig with key E and key U at ANY TIME
2. If block height >= x, the coin can be spent by key U. The 2-by-2 multisig, however, is still a vaild way to spend the coin.
Validating that use of height is actually stateless in script would be horrific and more or less intractable. If script were designed so that there was a table of redemption options which had their own lock time then it could have been done... but alas.

It's no big deal in any case:

You are selling me your car. We decide to use a 2-of-2 escrow.  But I'm afraid you'll hold up the funds if the deal falls through.

I compute the payment into the escrow and sign it. But I do not announce it yet.  I also compute a transaction spending the escrow transaction and paying it all back to me with the locktime set some months from now.  I sign that transaction.  I give it to you (along with the txout its spending but not the whole payment). You can look at it and confirm that it is indeed unlocked until whenever, so sign it and give it back.  Then I announce the payment into the escrow. You don't need height in scripts to get refund timeouts. QED.
Pages:
Jump to: