Prove two parties signed a copy of a document (Multi Sig, Hash, OP_RETURN)

In my app, Blockusign, I am trying to prove two parties signed a copy of a document (hash).

I am a noobie to bitcoin programming and read you can accomplish this using a multi-sig and pass data into a transaction using the OP_RETURN.

I would prefer to use the bitcoin public keys that have the user’s blockstack id and attestations associated to it for proof that a “real” user signed the transaction.

Is there an easy way to do this with blockstack.js? Or do you suggest a better/different architecture?

@jude @aaron


You can do this with bitcoinjs-lib :slight_smile: All you need to do is create a transaction where each party signs an input, and sends themselves back their change. The OP_RETURN would encode the document hash. The nice thing about this approach over multisig is that you don’t have to first create a multisig output to spend.

That makes total sense! Thanks @jude

@jude @aaron ,

Is it possible in blockstack.js to sign the transaction with the users bitcoin private key, similar to in the Blockstack Browser Wallet but with the ability to add the OP_RETURN hash data? I noticed it was sort of answered here but i could not find a concrete code example in the docs: Add blockstack transaction generation support to blockstack.js #382

Also, I was doing research on alternative approaches and noticed a forum post on immutable data storage using atlas. Would this approach make sense for my use-case? If so, does anybody have a some sample javascript code? Immutable data


blockstack.js will help you a little bit – the function blockstack.hexStringToECPair() will convert a private key string (like the app-private key) into an ECPair object, which can be used to sign bitcoinjs-lib transactions. For actually creating the transaction with the OP_RETURN, etc., I recommend looking at the test cases for the TransactionBuilder object in bitcoinjs-lib, but the general idea would be something like this:

const tx = new TransactionBuilder()
tx.addInput(user1UTXO, 0)
tx.addInput(user2UTXO, 2) // 2 is the vout of the UTXO
tx.addInput(user3UTXO, 1)
// store the data hash in a buffer `opReturnBuffer`
const nullOutput = bitcoin.payments.embed({ data: [opReturnBuffer] }).output
tx.addOutput(nullOutput, 0)
// add any change outputs -- you will have to compute the change amount yourself.
tx.addOutput(change1, changeAmount)
// sign
tx.sign(0, user1ECPair)
tx.sign(1, user2ECPair)
tx.sign(2, user3ECPair)

// return the built transaction as a hex string
1 Like


Per our discussion at the meetup, do you have the code to map the app key with the user’s blockstack ID, so I can prove the user signed the transaction with his associated attestations?


Hey @nicktee – I wrote a post up about how to do this here: