3

Is there a service that lets you send Bitcoins or more interestingly Altcoins to an address over a set amount of time. Say for example I wanted to give allowance to my daughter every week for the next year at $10 each time. This might have to be done centralized or how would this be implemented on a decentralized manner?

1

You can just publish a bunch of transactions with a particular nlocktime. Say you wanted to give your daughter an allowance of 0.1 BTC/week for a whole year. You can create one transaction with a locktime of next monday for 0.1 BTC, another transaction with a locktime of the monday after that for 0.1 BTC and so on for a whole year. The way bitcoin works, she'll receive the amounts every monday.

At any time, you can create a transaction to yourself to nullify some or all of the future transactions. Many altcoins will also have this feature.

Now, if you want a constant dollar amount and not a bitcoin amount, that actually will require a service. Doing that in a decentralized way is going to be really tough since exchange rates by their nature tend to be centralized.

Here's how you can make an nlocktime transaction using the excellent btcd library in go:

package main

import (
    "bytes"
    "encoding/hex"
    "fmt"

    "github.com/btcsuite/btcd/btcec"
    "github.com/btcsuite/btcd/chaincfg"
    "github.com/btcsuite/btcd/txscript"
    "github.com/btcsuite/btcd/wire"
    "github.com/btcsuite/btcutil"
)

func main() {

    // Ordinarily the private key would come from whatever storage mechanism
    // is being used, but for this example just hard code it.
    wif, err := btcutil.DecodeWIF(
        "<testnet private key in WIF format here>")
    )
    if err != nil {
        fmt.Println(err)
        return
    }

    compressed := false

    privKey := wif.PrivKey
    pubKey := privKey.PubKey()
    serializedPubKey := pubKey.SerializeCompressed()
    if !compressed {
        serializedPubKey = pubKey.SerializeUncompressed()
    }
    pubKeyHash := btcutil.Hash160(serializedPubKey)
    addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash,
        &chaincfg.TestNet3Params)
    if err != nil {
        fmt.Println(err)
        return
    }

    txStr, err := hex.DecodeString("<testnet tx that contains the utxo in hex here>")

    if err != nil {
        fmt.Println(err)
        return
    }

    rbuf := bytes.NewReader(txStr)
    var originTx wire.MsgTx
    err = originTx.Deserialize(rbuf)
    if err != nil {
        fmt.Printf("Deserialize error %v\n", err)
        return
    }
    originTxHash := originTx.TxSha()

    // Create the transaction to redeem the fake transaction.
    redeemTx := wire.NewMsgTx()

    // Add the input(s) the redeeming transaction will spend.  There is no
    // signature script at this point since it hasn't been created or signed
    // yet, hence nil is provided for it.
    prevOut := wire.NewOutPoint(&originTxHash, 0)
    txIn := wire.NewTxIn(prevOut, nil)
    txIn.Sequence = 0
    redeemTx.AddTxIn(txIn)

    addr2, err := btcutil.DecodeAddress("<destination testnet addr here>",
        &chaincfg.TestNet3Params)
    if err != nil {
        fmt.Println(err)
        return
    }
    pkScript, err := txscript.PayToAddrScript(addr2)
    if err != nil {
        fmt.Println(err)
        return
    }

    txOut := wire.NewTxOut(93889960000, pkScript)
    redeemTx.AddTxOut(txOut)

    // unix time of when the tx output will be unlocked here
    redeemTx.LockTime = 1442802900

    // Sign the redeeming transaction.
    lookupKey := func(a btcutil.Address) (*btcec.PrivateKey, bool, error) {
        // Ordinarily this function would involve looking up the private
        // key for the provided address, but since the only thing being
        // signed in this example uses the address associated with the
        // private key from above, simply return it with the compressed
        // flag set since the address is using the associated compressed
        // public key.
        //
        // NOTE: If you want to prove the code is actually signing the
        // transaction properly, uncomment the following line which
        // intentionally returns an invalid key to sign with, which in
        // turn will result in a failure during the script execution
        // when verifying the signature.
        //
        // privKey.D.SetInt64(12345)
        return privKey, compressed, nil
    }
    // Notice that the script database parameter is nil here since it isn't
    // used.  It must be specified when pay-to-script-hash transactions are
    // being signed.
    sigScript, err := txscript.SignTxOutput(&chaincfg.TestNet3Params,
        redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll,
        txscript.KeyClosure(lookupKey), nil, nil)
    if err != nil {
        fmt.Println(err)
        return
    }
    redeemTx.TxIn[0].SignatureScript = sigScript

    var buf bytes.Buffer
    err = redeemTx.Serialize(&buf)
    if err != nil {
        fmt.Printf("Serialize error %v", err)
        return
    }

    // Prove that the transaction has been validly signed by executing the
    // script pair.
    flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures |
        txscript.ScriptStrictMultiSig |
        txscript.ScriptDiscourageUpgradableNops
    vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0,
        flags)
    if err != nil {
        fmt.Println(err)
        return
    }
    if err := vm.Execute(); err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("Transaction successfully signed %v\n", hex.EncodeToString(buf.Bytes()))

}
  • isnt nlocktime still pending to be released in a later version? or can u use it already? do you know if there is a sample code of how to do this? – Patoshi パトシ Nov 5 '15 at 19:35
  • possibly relevant? bitcoin.stackexchange.com/questions/5783/… – Charles S Nov 5 '15 at 20:49
  • nlocktime has worked for a while. I'll add some sample code in the answer. – Jimmy Song Nov 5 '15 at 23:23
  • when creating this transaction. does this mean this future transaction gets stored somewhere until it hits the block height before it gets executed? – Patoshi パトシ Nov 6 '15 at 0:59
  • Generally, it stays in the mempool until it can be included in a block on the network. It's possible for it to disappear, so you would need to rebroadcast right before the right time. – Jimmy Song Nov 6 '15 at 1:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.