2

I'm attempting to make a transaction on the testnet with an OP_RETURN and a change address in it, and then make a second OP_RETURN transaction from first transaction's change address in the same block. The way my code is currently written the second transaction shouldn't rely on the first transaction being processed (it gets coins from a faucet).

Here is a simplified version of my code that maintains the problem (my "write" function is a modified version of this bitcoinjs-lib example):

var assert = require('assert'),
    bitcoin = require('bitcoinjs-lib'),
    blockchain = new (require('cb-helloblock'))('testnet'),
    dynamicKey,
    dynamicChangeKey;

connect = function (callback) {
    dynamicKey = bitcoin.ECKey.makeRandom();
    dynamicChangeKey = bitcoin.ECKey.makeRandom();

    write("Connecting to blockchain!", callback);
};

message = function (callback) {
    dynamicKey = dynamicChangeKey;
    dynamicChangeKey = bitcoin.ECKey.makeRandom();

    write("Posting to blockchain!", callback);
};

write = function (arbitraryData, callback) {
    var address = dynamicKey.pub.getAddress(bitcoin.networks.testnet).
            toString();

    blockchain.addresses.__faucetWithdraw(address, 2e4, function (err) {

        if (err) return callback(err);

        blockchain.addresses.unspents(address, function (err, unspents) {
            var tx,
                data,
                dataScript,
                unspent,
                txBuilt;

            if (err) return callback(err);

            tx = new bitcoin.TransactionBuilder();
            data = new Buffer(arbitraryData.toString());
            dataScript = bitcoin.scripts.nullDataOutput(data);
            unspent = unspents.pop();

            tx.addInput(unspent.txId, unspent.vout);
            tx.addOutput(dataScript, 1000);
            tx.addOutput(dynamicChangeKey.pub.getAddress(bitcoin.networks.
                    testnet).toString(), 1000);
            tx.sign(0, dynamicKey);
            txBuilt = tx.build();

            blockchain.transactions.propagate(txBuilt.toHex(), function (err) {

                if (err) return callback(err);

                // check that the message was propagated
                blockchain.transactions.get(txBuilt.getId(), function (err, 
                        transaction) {
                    var actual,
                        dataScript2,
                        data2;

                    if (err) return callback(err);

                    actual = bitcoin.Transaction.fromHex(transaction.txHex);
                    dataScript2 = actual.outs[0].script;
                    data2 = dataScript2.chunks[1];

                    assert.deepEqual(dataScript, dataScript2);
                    assert.deepEqual(data, data2);

                    callback();
                });
            });
        });
    });
};

log = function (value) {
    console.log(value);
};

connect(message.bind(undefined, log));

When running this code the first transaction processes correctly, but the second one returns this error:

{ [AssertionError: Invalid JSend Response {"status":"fail","message":"Propagation unsuccessful","details":"TX rejected"}]
  name: 'AssertionError',
  actual: false,
  expected: true,
  operator: '==',
  message: 'Invalid JSend Response {"status":"fail","message":"Propagation unsuccessful","details":"TX rejected"}' }

If I change line 15 to read

    dynamicKey = bitcoin.ECKey.makeRandom();

instead of

    dynamicKey = dynamicChangeKey;

both transactions process correctly, so the problem is definitely in my use of the change key as the next transaction key.

Does anybody know where I'm messing up?

1 Answer 1

2

I think my issue was that I was accidentally trying to spend the output from my first transaction (which had insufficient funds for both the given fee and the given amount of change) instead of the output I was getting from the faucet, which I meant to spend. I haven't written a good solution to this problem yet, but I got around it in this simplified instance by building both transactions before propagating either.

Here is one of the transactions, and here is the other.

Here is the horrible code that made them:

var assert = require('assert'),
    bitcoin = require('bitcoinjs-lib'),
    blockchain = new (require('cb-helloblock'))('testnet'),
    dynamicKey,
    dynamicChangeKey,
    transactions = [],
    dataPoints = [],
    dataScripts = [];

connect = function (callback) {
    dynamicKey = bitcoin.ECKey.makeRandom();
    dynamicChangeKey = bitcoin.ECKey.makeRandom();

    buildTransaction(dynamicKey, dynamicChangeKey, 
            "Connecting to blockchain...", callback);
};

message = function (callback) {
    dynamicKey = dynamicChangeKey; //bitcoin.ECKey.makeRandom();
    dynamicChangeKey = bitcoin.ECKey.makeRandom();

    buildTransaction(dynamicKey, dynamicChangeKey, "Posted to blockchain.", 
            callback);
};

buildTransaction = function (key, changeKey, arbitraryData, callback) {
    var address = key.pub.getAddress(bitcoin.networks.testnet).
            toString();

    blockchain.addresses.__faucetWithdraw(address, 2e4, function (err) {

        if (err) return callback(err);

        blockchain.addresses.unspents(address, function (err, unspents) {
            var tx,
                data,
                dataScript,
                unspent,
                txBuilt;

            if (err) return callback(err);

            tx = new bitcoin.TransactionBuilder();
            data = new Buffer(arbitraryData.toString());
            dataScript = bitcoin.scripts.nullDataOutput(data);
            unspent = unspents.pop();

            tx.addInput(unspent.txId, unspent.vout);
            tx.addOutput(dataScript, 1000);
            tx.addOutput(changeKey.pub.getAddress(bitcoin.networks.
                    testnet).toString(), 1000);
            tx.sign(0, key);
            txBuilt = tx.build();

            dataPoints.push(data);
            dataScripts.push(dataScript);
            transactions.push(txBuilt);

            callback();
        });
    });
};

propogateTransaction = function (data, dataScript, txBuilt, callback) {

    blockchain.transactions.propagate(txBuilt.toHex(), function (err) {

        if (err) return callback(err);

        // check that the message was propagated
        blockchain.transactions.get(txBuilt.getId(), function (err, 
               transaction) {
            var actual,
                dataScript2,
                data2;

            if (err) return callback(err);

            actual = bitcoin.Transaction.fromHex(transaction.txHex);
            dataScript2 = actual.outs[0].script;
            data2 = dataScript2.chunks[1];

            assert.deepEqual(dataScript, dataScript2);
            assert.deepEqual(data, data2);

            callback();
        });
    });
};

propogateBothTransactions = function () {
    propogateTransaction(dataPoints[0], dataScripts[0], transactions[0], 
            propogateTransaction.bind(undefined, dataPoints[1], dataScripts[1], 
            transactions[1], log));
};


log = function (value) {
    console.log(value);
};

connect(message.bind(undefined, propogateBothTransactions));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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