the XPS is back up again
make -j6 is
definitely worth the pain.
Righty-ho, now that I have
finally managed to get the beecoinv2 daemon synced, it's time to think about the process of transferring an amended public ledger.
The technical side of the process is straightforward. The client code does all the processing:
Store the input/output type and value of all of the transactions in block 1, 2, 3, ...
n, deleting any outputs that later appear as inputs.
The result is a database of transactions that have not yet been spent and which, when summed by address, provides an overall balance for each address.
Preliminary working-but-not-yet-proven Python code to create such a beast is here:
https://gist.github.com/gjhiggins/44e8b84787db4ba188623c17097b15f3The address/balance pairs are hard-coded as (essentially) coinbase-generated transactions which are included in the “genesis block”, block 0, the root of the chain and whose block hash and Merkle tree root hash have to be hand-inserted into the program code before it can be successfully compiled to perform as a peer-to-peer networked cryptocurrency as designed.
It's a birthing process. And it's probably about time someone had a word with you about “the birds and the bees” ...
The code contains two protocol assertions: 1) the hash of block 0 is
X and 2) the hash of block 0's Merkle tree root is
Y. These assertions constrain
everyone who joins the network. You
can change the hashes but you're then on your own fork, a network of 1 node - unless you run the same code on another connected computer, now you have a network of two nodes and a new blockchain all of your very own.
The hashes are obtained by processing the genesis block transactions into a candidate block, then successfully mining the block, printing out the computed hash values for the block and the Merkle root, then copying those values back into the code so that the assertions are satisfied when the code is recompiled and the client restarted.
The actual code is included below, it's not completely unreadable. The first line binds a string to the variable
pszTimestamp. By
convention, it is intended to carry date-specific information as a (very weak) guard against pre-mining - no block should have a timestamp earlier than the date referenced in the psztimestamp. The semantics are purely a social convention, it is not enforced in the code. The next three lines are housekeeping for the creation of the transaction object we need. The next three lines fill in the details of the transaction (note the embedding of the pszTimestamp string into block 0 via the genesis transaction) and then the transaction object is hooked into the genesis block object, ready for the next step, the computation of the Merkle root hash and the hooking of the result into the genesis block object.
hashPrevBlock is how the chain is formed, the genesis block is unique in having 0 as the value, block 1 will have the genesis block's hash as its
prevBlockHash value and block 2 will have block 1's and so on. A chain of blocks.
nVersion establishes the versioning protocol, a means of creating distinct separations in the ledger, i.e. hard forks.
nTime should be the current time by the developer's watch,
nBits is how the starting difficulty is set (expressed in a somewhat oblique idiom of C) and
nNonce is used to seed the block hash guesses. All of these values are cryptographically secured in the genesis block by the mining process. They affect the hash computation: change the diff, the hash won't match, same goes for the entire content of the genesis block, ya can't change
anything without causing the hash match to fail. That's how the genesis block and the genesis transaction form the secured root of the blockchain. The separate group of lines at the end are the protocol assertions I mentioned earlier.
const char* pszTimestamp = "U.S. News & World Report Jan 28 2016 With His Absence, Trump Dominates Another Debate";
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].nValue = 250 * COIN;
txNew.vout[0].scriptPubKey = CScript() << ParseHex("04c10e83b2703ccf322f7dbd62dd5855ac7c10bd055814ce121ba32607d573b8810c02c0582aed0 5b4deb9c4b77b26d92428c61256cd42774babea0a073b2ed0c9") << OP_CHECKSIG;
genesis.vtx.push_back(txNew);
genesis.hashPrevBlock = 0;
genesis.hashMerkleRoot = genesis.BuildMerkleTree();
genesis.nVersion = 1;
genesis.nTime = 1454124731;
genesis.nBits = 0x1e0ffff0;
genesis.nNonce = 2402015;
hashGenesisBlock = genesis.GetHash();
assert(hashGenesisBlock == uint256("0x0000041e482b9b9691d98eefb48473405c0b8ec31b76df3797c74a78680ef818"));
assert(genesis.hashMerkleRoot == uint256("0x1b2ef6e2f28be914103a277377ae7729dcd125dfeb8bf97bd5964ba72b6dc39b"));
Why all this eye-watering detail? Because the actual transfer of the Bee public ledger will be implemented by the programmatic creation of balance->account transactions written to the (nascent) new blockchain's genesis block, following the actual code in
sfultong's convenient demonstration.
I include the code here and have styled it to emphasise just the key notions. Ignore the near-invisible beige; the stuff in blue we've already seen (above); the two red variables are the collections of pay-to-pubkey-hash and pay-to-script-hash transactions in the form of paired
hash (the address) and
amount, as created by the “snapshot” process.
The code works through each
entry (orange) in the collections of P2PKH and P2SH entries, creating a P2PKH or P2SH transaction as appropriate with the
entry.hash and the
entry.amount, adding each one to the collection of genesis block transactions as it goes.
static bool generateGenesis(CBlock& genesis) {
/**
* Build the genesis block. Note that the output of the genesis coinbase cannot
* be spent as it did not originally exist in the database.
*
* CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
* CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
* CTxIn(COutPoint(000000, -1), coinbase 04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f7 2206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73)
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
* vMerkleTree: 4a5e1e
*/
bst::snapshot_reader snapshot_reader;
ifstream stream;
if (! bst::openSnapshot(stream, snapshot_reader)) return false;
bst::SnapshotEntryCollection p2pkhEntries = bst::getP2PKHCollection(snapshot_reader);
bst::SnapshotEntryCollection p2shEntries = bst::getP2SHCollection(snapshot_reader);
const char* pszTimestamp = "NY Times 05/Oct/2011 Steve Jobs, Apple’s Visionary, Dies at 56";
CMutableTransaction txNew;
txNew.vin.resize(1);
txNew.vout.resize(1);
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(4) << vector((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
txNew.vout[0].nValue = 50 * COIN;
txNew.vout[0].scriptPubKey = CScript() << ParseHex("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08 dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9") << OP_CHECKSIG;
genesis.vtx.push_back(txNew);
CScript fakeSign = CScript() << 486604799;
BOOST_FOREACH(bst::snapshot_entry& entry, p2pkhEntries) {
txNew.vin[0].scriptSig = fakeSign;
txNew.vout[0].nValue = entry.amount;
txNew.vout[0].scriptPubKey = CScript() << OP_DUP << OP_HASH160 << entry.hash << OP_EQUALVERIFY << OP_CHECKSIG;
genesis.vtx.push_back(txNew);
}
BOOST_FOREACH(bst::snapshot_entry& entry, p2shEntries) {
txNew.vin[0].scriptSig = fakeSign;
txNew.vout[0].nValue = entry.amount;
txNew.vout[0].scriptPubKey = CScript() << OP_HASH160 << entry.hash << OP_EQUAL;
genesis.vtx.push_back(txNew);
}
genesis.hashPrevBlock = 0;
genesis.hashMerkleRoot = genesis.BuildMerkleTree();
genesis.nVersion = 1;
genesis.nTime = 1317972665;
genesis.nBits = 0x8e0ffff0;
genesis.nNonce = 2084524493;
return true;
}
This is, in essence, the complete approach - balances are transferred by programmatically creating transactions and embedding them in the genesis block. Then, when owners import their privkeys, the wallet (which is a UTXO browser) shows the UTXO balance. Ledger transferred.
I've done a couple of preliminary runs using sqlite for immediacy, one part way, the other complete (except that I inadvertently hit Ctl-C during the final
commit, sigh). I've configured it to commit every 10000 records, then if it's interrupted again, it'll just pick up where it left off. I hope to have something for public discussion by the end of this week. By “something for public discussion”, I mean a hefty SQL dump that can be loaded into an RDBMS (PostgreSQL is recommended) and the ledger edited to the community's satifaction.
Sorry for the tedious detail, it's an inevitable consequence of total transparency.
Cheers
Graham