Skip to content
Timo Hanke edited this page Feb 7, 2013 · 21 revisions

First Steps

Example 1: only one machine, running online.

Certificate creation

Suppose we have compiled the BCPKI-extended bitcoind and changed into the src directory.

To check if alias foo1 already has its signatures in the blockchain we use the RPC bclist (blockchainlist):

@onlinemachine: ./bitcoind -testnet bclist foo1
{
    "fRegistered" : false
}

This means that foo1 is not yet registered in the blockchain. We now build a BCPKI certificate (blockchain-PKI) that can later be signed by the alias foo1 within the blockchain. We first generate a payment address to be written into the cert:

@onlinemachine: ./bitcoind -testnet getnewaddress
mrMyF68x19kAc2byGKqR9MLfdAe1t5MPzh

We then create the cert in a text editor by filling out the YAML-template cert_template.yml. The result looks like this:

  data:
    version: 0.1
    subjectname: Foo Inc.
    contacts: 
     - type: EMAIL
       value: contact@foo.com 
     - type: URL  
       value: "http://foo.com"
    paymentkeys:
      - algorithm:
         type: STATIC_BTCADDR
        value: [ mrMyF68x19kAc2byGKqR9MLfdAe1t5MPzh ]

We save it as foo1_static.yml and translate it to a binary protobuf message according to the specification bcert.proto with the python script mkbcrt.py:

@onlinemachine: bcert/mkbcrt.py foo1_static
data {
  version: "0.1"
  subjectname: "Foo Inc."
  contacts {
    type: EMAIL
    value: "foo@fooinc.com"
  }
  contacts {
    type: URL
    value: "http://www.fooinc.com"
  }
  paymentkeys {
    usage: PAYMENT
    algorithm {
      type: STATIC_BTCADDR
    }
    value: "mrMyF68x19kAc2byGKqR9MLfdAe1t5MPzh"
  }
}

The result is foo1_static.bcrt Conversely, we can parse the binary protobuf message with dumpbcrt.py:

@onlinemachine: bcert/dumpbcrt.py foo1_static.bcrt
[same output as above]

Signing

We double-check the pretty-printed version of the certificate at the beginning of the output. If correct, we proceed signing it in the blockchain under alias foo1 with the RPC bcsign (blockchainsign):

@onlinemachine: HEX=`xxd -p foo1_static.bcrt | tr -d "\n"`
@onlinemachine: ./bitcoind -testnet bcsigncert foo1 $HEX
{
    "bcsignresult" : {
        "nFee" : 0.00050000,
        "txid" : "0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc",
        "fname" : "ecff20c2e787f20913522799cd8e216d4c69a355"
    }
}

After the transaction gets mined we can verify this signature within the blockchain.

Interna

While we wait for a confirmation, lets take the time to look what this signature has done. First look at a few values that are associated to the alias foo1:

@onlinemachine: ./bitcoind -testnet aliasdump foo1
{
    "normalized" : "BCSIG_v0.4_F01",
    "bcvalue" : "d378cf5f150d277d9412e8dfbc07390b6e10e157",
    "privkey" : "cMahea7zqjxrtgAbB8oXbesknbteMmCW7Dz2VeZAmV7A3kvufGcy",
    "pubkey" : "0327c383d206cc901c5d8c00d8d47cb32b29fe18220f5f3c03cb1ff3d44f453822",
    "id" : "ecff20c2e787f20913522799cd8e216d4c69a355",
    "addr" : "n385PvoQ4komgBxYNu4FegVnb5iPHH3wsJ"
}

Explanation (also see Technical):

  • normalized is the prefix BCSIG_v0.4 plus the alias name after a normalization, which (in this order) removes underscores and dashes, capitalizes all letters, replaces O by 0, replaces L and I by 1, replaces repeated characters by single characters
  • bcvalue is the Hash160 of normalized
  • pubkey is the curve point bcvalue*G where G is the base point and bcvalue is interpreted as a little-endian number
  • id is the Hash160 of pubkey
  • addr is the base58-check-encoding (i.e. bitcoin address) of id

Now compare this to the transaction 0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc that was created (only partial output shown here):

@onlinemachine: ./bitcoind -testnet gettransaction 0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc
{
    "amount" : -0.05000000,
    "fee" : -0.00050000,
    "confirmations" : 0,
    "txid" : "0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc",
    "bcsignv" : "0.4",
    "bcvalues" : "(cMahea7zqjxrtgAbB8oXbesknbteMmCW7Dz2VeZAmV7A3kvufGcy:d378cf5f150d277d9412e8dfbc07390b6e10e157)(cMahea7zqjxrtgAbB7mW7hrBAvKRVhEWhmrdd2qKymq5gNQSAtxG:3e4b985a4a3922de7667b407cc603252b9b7de51)",
    "owners" : "(03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b:mtjWtZZojT5YoeSezCZZseAv99yzP1hDFR)",
    [...]
        "vout" : [{
                "value" : 0.05000000,
                "n" : 0,
                "scriptPubKey" : {
                    "asm" : "2 0327c383d206cc901c5d8c00d8d47cb32b29fe18220f5f3c03cb1ff3d44f453822 03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b 2 OP_CHECKMULTISIG",
                    "hex" : "52210327c383d206cc901c5d8c00d8d47cb32b29fe18220f5f3c03cb1ff3d44f4538222103113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b52ae",
                    "reqSigs" : 2,
                    "type" : "multisig",
                    "addresses" : [
                        "n385PvoQ4komgBxYNu4FegVnb5iPHH3wsJ",
                        "mtjWtZZojT5YoeSezCZZseAv99yzP1hDFR"
                    ]
                }},
            {
                "value" : 0.24900000,
                "n" : 1,
                "scriptPubKey" : {
                    "asm" : "OP_DUP OP_HASH160 7657583644111b0bafd7ce75d20ca229d12f8e34 OP_EQUALVERIFY OP_CHECKSIG",
                    "hex" : "76a9147657583644111b0bafd7ce75d20ca229d12f8e3488ac",
                    "reqSigs" : 1,
                    "type" : "pubkeyhash",
                    "addresses" : [
                        "mrJgfVpU5dKsxByqioEocJDTtJuUrvQnuG"
                    ]
                }},
            {
                "value" : 0.05000000,
                "n" : 2,
                "scriptPubKey" : {
                    "asm" : "2 03e593e5ddc4cd5da07e877e61d9408e607c9455c620b2339f1806e6c4889dd384 03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b 2 OP_CHECKMULTISIG",
                    "hex" : "522103e593e5ddc4cd5da07e877e61d9408e607c9455c620b2339f1806e6c4889dd3842103113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b52ae",
                    "reqSigs" : 2,
                    "type" : "multisig",
                    "addresses" : [
                        "mokupqSvHrDYYpc8dnMEHcTM64BXKkLnzw",
                        "mtjWtZZojT5YoeSezCZZseAv99yzP1hDFR"
                    ]
                }}
        ]
    }
}

We see that the scriptPubKeys of output 0 and 2 are explicit 2-to-redeem multisignature outputs (not P2SH). Output 1 is change.

Output 0: We recognize the pubkey 0327c383d206cc901c5d8c00d8d47cb32b29fe18220f5f3c03cb1ff3d44f453822 from the output of aliasdump above as the pubkey derived from alias foo1. The second pubkey (03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b) was taken fresh from our wallet. The private key for the pubkey derived from alias foo1 is publicly known, it can be found in the output of aliasdump above as well. Therefore, the second pubkey is the only one that protects this output, hence we call it the owner of this outputs.

Output 2: Output 2 has the same second pubkey (i.e. owner) as output 0. But the first pubkey is derived from the hash160 of the data part of our certificate. To compare, we can compute the hash160 data part of our certificate like this:

@onlinemachine: bcert/dumpbcrt.py -d foo1_static.bcrt
3e4b985a4a3922de7667b407cc603252b9b7de51

The -d flag outputs the hash of the data part instead of pretty-printing the whole cert. Now, aliasdump shows the values associated with this hash and we recognize the first pubkey of output 2 among them:

@onlinemachine: ./bitcoind -testnet aliasdump 3e4b985a4a3922de7667b407cc603252b9b7de51
{
    "bcvalue" : "3e4b985a4a3922de7667b407cc603252b9b7de51",
    "privkey" : "cMahea7zqjxrtgAbB7mW7hrBAvKRVhEWhmrdd2qKymq5gNQSAtxG",
    "pubkey" : "03e593e5ddc4cd5da07e877e61d9408e607c9455c620b2339f1806e6c4889dd384",
    "id" : "5a64c4bbaf1cd3e110dbbc51f49dc073cb98f653",
    "addr" : "mokupqSvHrDYYpc8dnMEHcTM64BXKkLnzw"
}

Also note that the private keys that were derived from alias foo1 and from the hash of the data part of the certificate, respectively, are both stored inside the wallet transaction in the field bcvalues. They we will be needed later to revoke the certificate (i.e. to spend outputs 0 and/or 2). Note that the private keys are not added to the keystore at this point in order to avoid spending these outputs accidentally.

Besides storing this information in the wallet transaction, bitcoind has also created a file ecff20c2e787f20913522799cd8e216d4c69a355.bcrt in .bitcoin/testnet3/bcerts/. Note that the filename is the id-output of aliasdump for foo1. Pretty-printing this file with dumpbcrt.py we see that the data part is unchanged but that bitcoind has added a signature to our certificate:

@onlinemachine: ./dumpbcrt.py -s foo1
data {
  version: "0.1"
  subjectname: "Foo Inc."
  contacts {
    type: EMAIL
    value: "foo@fooinc.com"
  }
  contacts {
    type: URL
    value: "http://www.fooinc.com"
  }
  paymentkeys {
    usage: PAYMENT
    algorithm {
      type: STATIC_BTCADDR
    }
    value: "mrMyF68x19kAc2byGKqR9MLfdAe1t5MPzh"
  }
}
signatures {
  algorithm {
    type: BCPKI
    version: "0.4"
  }
  value: "foo1"
}

The flag -s tells dumpbcrt.py to take the certificate from the local store under .bitcoin/testnet3/bcerts/. The filename is automatically derived from the given alias argument. As we see, the bcsigncert RPC has added a signature to the certificate. The signature is of type BCPKI and consists merely of a reference to the alias foo1. This is actually a reference to the blockchain: the signature is to be found inside the blockchain under the alias foo1.

Certificate Server

We upload the signed certificate to a certificate server:

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

The -x flag make dumpbcrt return a hexdump of the certificate. The server bcrypt.org stores it under the same filename as we did locally. So it can be retrieved and imported on another machine like this:

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

Verifying

We now use bitcoind (on this or the other machine) to verify the signature in the blockchain:

@onlinemachine: ./bitcoind -testnet bcverify foo1
{
    "fSigned" : true,
    "txid" : "0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc",
    "confirmations" : 2,
    "strTime" : "2013-02-07T16:10:28"
}

We recognize the transaction id and see that the signature has 6 confirmations. The RPC bcverify has done the following:

  • derive a filename from alias foo1
  • look up the certificate in ~/.bitcoin/testnet3/bcert/
  • compute datahash, i.e. the hash160 of the data part of the certificate
  • search the UTXO set for the the first transaction that has an unspent output meeting these criteria:
  • sigPubKey is an explicit (i.e. non-P2SH) n-to-redeem multisig with n>=2
  • the first pubkey is the one derived from alias foo1
  • the amount is >= 0.05
  • inside this transaction, look for another output meeting the same criteria except that the pubkey is the one derived from datahash
  • if successful, output this transaction

Paying

Now we wish to send funds directly to a certificate which is stored under .bitcoin/testnet3/bcerts. The RPC sendtoalias looks up that cert, verifies if the signature has more than a given number of confirmations, extracts the address, and sends the funds there.

@onlinemachine: ./bitcoind -testnet sendtoalias foo1 '[4]' 1 2
{
    "dest" : "mrMyF68x19kAc2byGKqR9MLfdAe1t5MPzh",
    "txid" : "7a5fcdff97eaa16bc7899fe68d596b95d98f0bea52a3cdf5835852997a27c4ba",
    "vout" : 0
}

This sent 1 BTC to the address stored in foo1's certificate, but only after successful verification of this certificate with at least 2 confirmations (as specified on the command line). The argument '[4]' chooses the payment method (4 means a static bitcoin address). This requires that foo1's certificate acutally contains a payment key for this method.

Revoking

Finally we wish to revoke the certificate. Remember the signing transaction had two relevant outputs. If we spend output 2 then transaction will still count as the (only) signing transaction of alias foo1, but with the signed value of datahash removed from it. If (instead or additionally) we spend output 0 then the transaction will no longer count as the signing transaction of alias foo1, and can be superseded (i.e. updated) by a later signing transaction for foo1. In both cases the certificate will no longer verify. We choose to spend output 2 first and look at the output of bclist before and after we do this.

@onlinemachine: ./bitcoind -testnet bclist foo1
{
    "fRegistered" : true,
    "txid" : "0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc",
    "confirmations" : 3,
    "strTime" : "2013-02-07T16:10:28",
    "values" : [
        {
            "vout" : 0,
            "id" : "ecff20c2e787f20913522799cd8e216d4c69a355",
            "ownersReq" : 1,
            "amount" : 0.05000000,
            "owner" : "03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b"
        },
        {
            "vout" : 2,
            "id" : "5a64c4bbaf1cd3e110dbbc51f49dc073cb98f653",
            "ownersReq" : 1,
            "amount" : 0.05000000,
            "owner" : "03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b"
        }
    ]
}

This shows two values in the signature of foo1: the id derived from alias foo1 and the id derived from datahash. They appeared in the output of two calls of aliasdump above.

In order to spend output 2 we need to import the private key for the pubkey derived from datahash, which is simply datahash itself interpreted as a private key. We paste the privkey from the output of aliasdump 3e4b985a4a3922de7667b407cc603252b9b7de51 above into this command line:

@onlinemachine: ./bitcoind -testnet importprivkey cMahea7zqjxrtgAbB7mW7hrBAvKRVhEWhmrdd2qKymq5gNQSAtxG

Now we can spend output 2 to a fresh change address:

@onlinemachine: ./bitcoind -testnet spendoutpoint 0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc 2
{
    "nFee" : 0.00050000,
    "txid" : "ed02512349c8641eb4581e2ab3f92ac04c0e2d56b4dc507d8c289210a57e56f2"
}

Looking at the wallet transaction we see that bitcoind has specially marked it (only partial output shown):

@onlinemachine: ./bitcoind -testnet gettransaction ed02512349c8641eb4581e2ab3f92ac04c0e2d56b4dc507d8c289210a57e56f2
{
    "amount" : 0.00000000,
    "fee" : -0.00050000,
    "confirmations" : 0,
    "txid" : "ed02512349c8641eb4581e2ab3f92ac04c0e2d56b4dc507d8c289210a57e56f2",
    "comment" : "spending outpoint (0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc,2)",
    "to" : "our change address",
    [...]
}

After this we look at the output of bclist again:

@onlinemachine: ./bitcoind -testnet bclist foo1
{
    "fRegistered" : true,
    "txid" : "0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc",
    "confirmations" : 3,
    "strTime" : "2013-02-07T16:10:28",
    "values" : [
        {
            "vout" : 0,
            "id" : "ecff20c2e787f20913522799cd8e216d4c69a355",
            "ownersReq" : 1,
            "amount" : 0.05000000,
            "owner" : "03113697196e30a6f3089103482d88711f867a2cc59aca90300978676ab17a0c0b"
        }
    ]
}

As expected the second value is gone. This means the certificate should not verify anymore. Indeed:

@onlinemachine: ./bitcoind -testnet bcverify foo1
{
    "fSigned" : false
}

Now we spend output 0 as well, using the privkey from the output of aliasdump foo1:

@onlinemachine: ./bitcoind -testnet importprivkey cMahea7zqjxrtgAbB8oXbesknbteMmCW7Dz2VeZAmV7A3kvufGcy
@onlinemachine: ./bitcoind -testnet spendoutpoint 0dea9bbe01252ab6af1e505baa91a87cb771feebcb3e0d8a36b34db49c65c5bc 0
{
    "nFee" : 0.00050000,
    "txid" : "2171e3e7f40f7f1066f6f31724389b2fc05ada7911e07cbdde5062c3fad080b1"
}

After this transaction gets confirmed, we see:

@onlinemachine: ./bitcoind -testnet  bclist foo1
{
    "fRegistered" : false
}

Now we could start from the beginning and sign a different certificate with the alias foo1.

Next: Pay-to-contract

Clone this wiki locally