Skip to content
Timo Hanke edited this page Feb 8, 2013 · 13 revisions

Multi-Party

Example 3: Multiple parties

Intro

This example demonstrates a case where two parties A and B jointly issue a certificate that contains a multisignature base key which they split among themselves. Think of A and B as business partners who do not want either one of them to be able to control incoming payments alone (without the other partner). For simplicity we assume A and B have their machines running online. In practive, of course, the owner and receiving base keys should be generated on an offline machine. The joint project is called fooproject.

Certificate creation

A and B create their receiving base keys:

@onlineA: ./bitcoind -testnet getnewaddress fooproj_recvA true
0285b2eb2c0f2e4a12646dbcf38d08c29ef557b5616048575b133a2084a56bb84a
@onlineB: ./bitcoind -testnet getnewaddress "fooproj_recvB" true
03ba3137ddbee4e164390b7b67e0975d12969ef23ac1fd7b1f7e880319d072b323

A and B create their owner keys (revokation keys) for the certificate:

@rsonlineA: ./bitcoind -testnet getnewaddress "fooproj_ownerA" true
0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4
@onlineB: ./bitcoind -testnet getnewaddress "fooproj_ownerB" true
025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822

B sends both his base pubkeys to A, who drafts the certificate using his and B's receiving base pubkey in the file fooproject.yml:

data:
  version: 0.1
  subjectname: Foo Project
  contacts: 
   - type: EMAIL
     value: [email protected] 
   - type: EMAIL
     value: [email protected] 
   - type: URL  
     value: "http://www.fooproject.com"
  paymentkeys:
    - algorithm:
       type: P2CMULTI
       version: 0.1
      value: [ 2, 0285b2eb2c0f2e4a12646dbcf38d08c29ef557b5616048575b133a2084a56bb84a, 03ba3137ddbee4e164390b7b67e0975d12969ef23ac1fd7b1f7e880319d072b323 ]

Signing

A signs the certificate:

@onlineA: bcert/mkbcrt.py fooproject.yml
@onlineA: HEX=`xxd -p fooproject.bcrt | tr -d "\n"`
@onlineA: ./bitcoind -testnet bcsigncert '{"alias":"fooproject","owners":["0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4","025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822"]}' $HEX
{
    "bcsignresult" : {
        "nFee" : 0.00050000,
        "txid" : "efa92a4d7475f7c6ac22c5635a422e65a6d90035cd148b8c6cdaed806c0119ff",
        "fname" : "ad3ac8fe7136a922e2595417ddcebfdf5c764ca1"
    }
}

The result of mkbcrt.py is fooproject.bcrt.

Transfer and Verification by B

A sends the file ~/.bitcoin/testnet3/bcerts/ad3ac8fe7136a922e2595417ddcebfdf5c764ca1.bcrt to B (email). B verifies that his receiving base pubkey appears in the cert and the signature value is fooproject, and then imports it to his bitcoind:

@onlineB: bcert/dumpbcrt.py ad3ac8fe7136a922e2595417ddcebfdf5c764ca1.bcrt
data {
  version: "0.1"
  subjectname: "Foo Project"
  contacts {
    type: EMAIL
    value: "[email protected]"
  }
  contacts {
    type: EMAIL
    value: "[email protected]"
  }
  contacts {
    type: URL
    value: "http://www.fooproject.com"
  }
  paymentkeys {
    usage: PAYMENT
    algorithm {
      type: P2CMULTI
      version: "0.1"
    }
    value: "2"
    value: "\002\205\262\353,\017.J\022dm\274\363\215\010\302\236\365W\265a`HW[\023: \204\245k\270J"
    value: "\003\27217\335\276\344\341d9\013{g\340\227]\022\226\236\362:\301\375{\037~\210\003\031\320r\263#"
  }
}
signatures {
  algorithm {
    type: BCPKI
    version: "0.4"
  }
  value: "fooproject"
}
@onlineB: mv ad3ac8fe7136a922e2595417ddcebfdf5c764ca1.bcrt ~/.bitcoin/testnet3/bcerts/

B verifies the signature in the blockchain:

@onlineB: ./bitcoind -testnet bcverify fooproject
{
    "fSigned" : true,
    "txid" : "efa92a4d7475f7c6ac22c5635a422e65a6d90035cd148b8c6cdaed806c0119ff",
    "confirmations" : 1,
    "strTime" : "2013-02-07T15:28:05"
}

B also looks at the scriptPubKeys to verify that his owner pubkey 025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822 is indeed required to revoke the certificate:

@onlineB: ./bitcoind -testnet bclist fooproject
{
    "fRegistered" : true,
    "txid" : "efa92a4d7475f7c6ac22c5635a422e65a6d90035cd148b8c6cdaed806c0119ff",
    "confirmations" : 1,
    "strTime" : "2013-02-07T15:28:05",
    "values" : [
        {
            "vout" : 1,
            "id" : "ad3ac8fe7136a922e2595417ddcebfdf5c764ca1",
            "ownersReq" : 2,
            "amount" : 0.05000000,
            "owner" : "0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4",
            "owner" : "025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822"
        },
        {
            "vout" : 2,
            "id" : "2451b6d25d8217cc13b44b2f78ef01e0fcec6841",
            "ownersReq" : 2,
            "amount" : 0.05000000,
            "owner" : "0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4",
            "owner" : "025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822"
        }
    ]
}

Note that B needs to verify this for both outpoints. Now the project can start.

Receiving payments

A and B both add the receiving base address to their wallet:

@onlineA|B: ./bitcoind -testnet addmultisigaddress 2 '["0285b2eb2c0f2e4a12646dbcf38d08c29ef557b5616048575b133a2084a56bb84a","03ba3137ddbee4e164390b7b67e0975d12969ef23ac1fd7b1f7e880319d072b323"]' fooproj_recv
2NAM5kqrMNAVUFtwN9cQeTJyMJ5mSukorvH

They upload the certificate to a server:

@onlineB: HEX=`bcert/dumpbcrt.py -s -x fooproject`
@onlineB: wget -O log.html "http://btcrypt.org/cgi-bin/transfer.cgi?hexcert=$HEX"

A third party C imports the certificate from a server:

@onlineC: wget -q --content-disposition -P ~/.bitcoin/testnet3/bcerts "http://btcrypt.org/cgi-bin/get.cgi?alias=fooproject&format=b"

C pays directly to fooproject via the P2CMULTI method (pay-to-contract with multiple base keys). Say the chosen ticket is 4febe382ed164145bf248b448444b807.

@onlineC: ./bitcoind -testnet sendtoalias fooproject '[2,"4febe382ed164145bf248b448444b807"]' 0.5
{
    "dest" : "2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR",
    "txid" : "8320db2222fce16cadceea89ec59c16e6e972e9457046fbd9007b8ff073d1202",
    "vout" : 0
}

Here, the number 2 is the payment method and means P2CMULTI. The derived destination is the P2SH address that we see.

Look at the transaction:

@onlineC: ./bitcoind -testnet gettransaction 8320db2222fce16cadceea89ec59c16e6e972e9457046fbd9007b8ff073d1202
{
    [...]
    "p2cmulti" : "0285b2eb2c0f2e4a12646dbcf38d08c29ef557b5616048575b133a2084a56bb84a 03ba3137ddbee4e164390b7b67e0975d12969ef23ac1fd7b1f7e880319d072b323 ",
    "ticket" : "4febe382ed164145bf248b448444b807",
    "to" : "BCSIG_v0.4_F0PR0JECT<P2CMULTI:4febe382ed164145bf248b448444b807> = 2 038bf68f4f9eb98f38b5d2f4ded5707ff7a5e5b450a99ad75aa0e2de2022836570 0204a656ffeb67fe17d0e281ae182fd96dd5632f5469dc5295ca0a535dc672c1a8 2 OP_CHECKMULTISIG",
    [...]
            "vout" : [
            {
                "value" : 0.50000000,
                "n" : 0,
                "scriptPubKey" : {
                    "asm" : "OP_HASH160 06341acebd4d3248d447a201b7a912baed8c0e99 OP_EQUAL",
                    "hex" : "a91406341acebd4d3248d447a201b7a912baed8c0e9987",
                    "reqSigs" : 1,
                    "type" : "scripthash",
                    "addresses" : [
                        "2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR"
                    ]
                }
            },
	    [...]
}

We see A and B's base receiving pubkeys in the field p2cmulti and the derived pubkeys in the script under to. The output is the P2SH of that script.

Spending

A and B import the ticket

@onlineA|B: ./bitcoind -testnet importticket 2NAM5kqrMNAVUFtwN9cQeTJyMJ5mSukorvH 4febe382ed164145bf248b448444b807 
{
    "derived" : "2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR"
}

(This may be slow due to a rescan) This has created an account:

@onlineA|B: ./bitcoind -testnet getaccount 2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR
fooproj_recv<4febe382ed164145bf248b448444b807>

Unfortunately, bitcoind does not allow to watch arbitrary addresses without having the private key at hand, nor to watch non-wallet transactions. So A and B have to use a different client or an online service to watch for the incoming payment on 2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR and see the confirmations.

A creates a transaction that spends the coin received for 2Msp2TjXvUPr4qopYhQGujq7s4HukzfivAR, and partially signs it:

@onlineA: ./bitcoind -testnet getnewaddress
msFrMcXFB4R8vHQJAMXZa8H3RAD6BJkPD8
@onlineA: RAWTX=`./bitcoind -testnet createrawtransaction '[{"txid":"8320db2222fce16cadceea89ec59c16e6e972e9457046fbd9007b8ff073d1202","vout":0}]' '{"msFrMcXFB4R8vHQJAMXZa8H3RAD6BJkPD8":0.45}'`
@onlineA: ./bitcoind -testnet signrawtransaction $RAWTX
{
    "hex" : "010000000102123d07ffb80790bd6f0457942e976e6ec159ec89eacead6ce1fc22 ...",
    "complete" : false
}

B signs it too and commits it:

@onlineB: ./bitcoind -testnet signrawtransaction 010000000102123d07ffb80790bd6f0457942e976e6ec159ec89eacead6ce1fc22...
{
    "hex" : "010000000102123d07ffb80790bd6f0457942e976e6ec159ec89eacead6ce1fc2222db208300000000dc00493046022100d22d5e1ddbee47b6468ff6b63d7b76fe857c4db4f8e9b9c64a6771cd148f4ee3022100a4a20cd7647543978f0adbd05f369a3879b55c15cc8c459926d24afe821f17d701483045022100d2c1d00fb86a30cbc5c3d7860ebc296f273abfc4080c86bc6750396bb59d5f6c02200c13c7910d270b6f8b092f2958197b0a9e8d3fdd27a602e9f80d884b366555eb01475221038bf68f4f9eb98f38b5d2f4ded5707ff7a5e5b450a99ad75aa0e2de2022836570210204a656ffeb67fe17d0e281ae182fd96dd5632f5469dc5295ca0a535dc672c1a852aeffffffff0140a5ae02000000001976a91480c6500a42d6b7281f18ff544c339396aa60a11b88ac00000000",
    "complete" : true
}
@onlineB: ./bitcoind -testnet sendrawtransaction 010000000102123d07ffb80790bd6f0457942e976e6ec159ec89eacead6ce1fc2222db208300000000dc00493046022100d22d5e1ddbee47b6468ff6b63d7b76fe857c4db4f8e9b9c64a6771cd148f4ee3022100a4a20cd7647543978f0adbd05f369a3879b55c15cc8c459926d24afe821f17d701483045022100d2c1d00fb86a30cbc5c3d7860ebc296f273abfc4080c86bc6750396bb59d5f6c02200c13c7910d270b6f8b092f2958197b0a9e8d3fdd27a602e9f80d884b366555eb01475221038bf68f4f9eb98f38b5d2f4ded5707ff7a5e5b450a99ad75aa0e2de2022836570210204a656ffeb67fe17d0e281ae182fd96dd5632f5469dc5295ca0a535dc672c1a852aeffffffff0140a5ae02000000001976a91480c6500a42d6b7281f18ff544c339396aa60a11b88ac00000000
1299682d4d0ad6418535cf9fec09e6cb95ee312f9345255fa088647b4ac4a234

Revoking

A imports the private key corresponding to alias:

@onlineA: ./bitcoind -testnet aliasdump fooproject
{
    "normalized" : "BCSIG_v0.4_F0PR0JECT",
    "bcvalue" : "3c66a8afea592deb11d7d40ee07ec2b48d0e4256",
    "privkey" : "cMahea7zqjxrtgAbB7kjubh5TML4cbhdrrYHznStWYM1vj83F5qr",
    "pubkey" : "02b92c312b79b6c4910489997e640da4e749b4c9173acf10ea6c364fc918eb0d41",
    "id" : "ad3ac8fe7136a922e2595417ddcebfdf5c764ca1",
    "addr" : "mwJuchk8iqmFEXSVE7CpXUXRnEMFRqXqi6"
}
@onlineA: ./bitcoind -testnet importprivkey cMahea7zqjxrtgAbB7kjubh5TML4cbhdrrYHznStWYM1vj83F5qr

A looks at the relevant outpoint of the signing transaction, creates a revokation transaction, and partially signs it:

@onlineA: ./bitcoind -testnet bclist fooproject
{
    "fRegistered" : true,
    "txid" : "efa92a4d7475f7c6ac22c5635a422e65a6d90035cd148b8c6cdaed806c0119ff",
    "confirmations" : 1,
    "strTime" : "2013-02-07T15:28:05",
    "values" : [
        {
            "vout" : 1,
            "id" : "ad3ac8fe7136a922e2595417ddcebfdf5c764ca1",
            "ownersReq" : 2,
            "amount" : 0.05000000,
            "owner" : "0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4",
            "owner" : "025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822"
        },
        {
            "vout" : 2,
            "id" : "2451b6d25d8217cc13b44b2f78ef01e0fcec6841",
            "ownersReq" : 2,
            "amount" : 0.05000000,
            "owner" : "0397badc73e72ab8ddde12fa6512bc52e2de58870b682dffe9e08c31620f71b8e4",
            "owner" : "025c62affbfb6889603a204ccbde287d4d84c993b30e74a86e4ed51a56584ca822"
        }
    ]
}
@onlineA: RAWTX=`./bitcoind -testnet createrawtransaction '[{"txid":"efa92a4d7475f7c6ac22c5635a422e65a6d90035cd148b8c6cdaed806c0119ff","vout":1}]' '{"mgZL8xBMo2kpVeJHSSEi2x14pCJw5wndfp":0.0495}'`
@onlineA: ./bitcoind -testnet signrawtransaction $RAWTX
{
    "hex" : "0100000001ff19016c80edda6c8c8b14cd3500d9a6652e425a63c522acc6f775744d2aa9ef010000009300483045022100db90e45e03b712ec013fd49170446e1bcabff5928c4c42084dba75fe200e848f0220616032f9a964cab4b955dc92bf86ea9848718c679993eff7afb1e29e9f3fbe3b0147304402207da9924b61d8bb7c4599fd8421bae6f9e33f2ffb0ea58e5fdc673c89a12d7df3022005b377833ec46bb5a2758da83baea7d84add50c1240494ba8de4317854c33d540100ffffffff01f0874b00000000001976a9140b6b30fe9fc8b5023dbecd403a336434b87c1a9688ac00000000",
    "complete" : false
}

B also signs and commits:

@onlineB: ./bitcoind -testnet signrawtransaction 0100000001ff19016c80edda6c8c8b14cd3500d9a6652e425a63c522acc6f775744d2aa9ef010000009300483045022100db90e45e03b712ec013fd49170446e1bcabff5928c4c42084dba75fe200e848f0220616032f9a964cab4b955dc92bf86ea9848718c679993eff7afb1e29e9f3fbe3b0147304402207da9924b61d8bb7c4599fd8421bae6f9e33f2ffb0ea58e5fdc673c89a12d7df3022005b377833ec46bb5a2758da83baea7d84add50c1240494ba8de4317854c33d540100ffffffff01f0874b00000000001976a9140b6b30fe9fc8b5023dbecd403a336434b87c1a9688ac00000000
{
    "hex" : "0100000001...
    "complete" : true
}
@onlineB: ./bitcoind -testnet sendrawtransaction 0100000001...

After the revoking is confirmed we verify:

@onlineB: ./bitcoind -testnet bclist fooproject
{
    "fRegistered" : false
}

Previous: Pay-to-contract