title | keywords | last_updated | sidebar | permalink | folder |
---|---|---|---|---|---|
Blockchain Overview |
blockchain |
mydoc_sidebar |
/in-depth/blockchain-overview |
in-depth |
This brief description of the structure of the structure of the Bitmark blockchain moves from the transactions up to the final structure of the chain itself.
The following variable types are used in most Bitmark records:
Account : An encoded Ed25519 public key. Contains a type code to allow for other key types. It is preceded by a VarInt byte count that is currently set to 33.
Binary : A sequence of bytes preceded by a VarInt byte count. The count is validated by various routines to prevent excessive storage use.
Byte : A single binary byte.
Map
: A sequence of NULL-separated UTF-8 byte sequences preceded by a VarInt
byte count. The count is
validated by various routines to prevent excessive storage use.
There must be an even number of sequences, as they form
key-value pairs. There must not be a trailing NULL byte. In JSON
representation, the NULL byte will show as \u0000
.
Signature : An Ed25519 signature over all previous bytes including VarInt fields. It is preceded by a VarInt with a count of 64. This value will change once other signature algorithms are used and will depend on the type code inside the Account field.
String : A sequence of UTF-8 bytes preceded by a VarInt byte count. The count is validated by various routines to prevent excessive storage use.
VarInt : A sequence of 1 to 9 bytes that represents a 64-bit unsigned integer in Little-Endian form. The high bit of each byte is used as a continuation flag, so 0 … 127 can be represented by each byte. Each successive byte adds 7 more bits. The 9th byte does not have a continuation flag, so it adds eight bits to the integer, bringing the total for the nine bytes to 64 bits.
The following variable types are used only in Bitmark block headers, because they are fixed length fields:
Unn
: An unsigned integer with a length of "nn"
bits. Normally this will be
multiples of 8 bits such as: U8, U16, U32, U64.
The main types of transactions on the Bitmark blockchain are asset, issue, and transfer records. Together, they create chains of provenance for individual copies of specific assets.
Asset records have an identifier that is the SHA3-512 hash of the bytes making up the fingerprint. This allows the fingerprint field to contain any data, binary, or text and to be represented by a fixed length 64-byte identifier in issue records. Only using the fingerprint for the hash prevents duplicate assets that have the same fingerprint but different names or metadata.
Other transactions have an identifier that is the SHA3-256 hash of the entire record (including all signatures). This shorter identifier of 256 bits is used both to save space in records and to easily distinguish transaction IDs from asset IDs.
An asset record contains the metadata describing an asset that will be issued on the blockchain. All assets will have at least one issue.
The Record structure:
Item | Type | Description |
---|---|---|
Type code | Byte | Asset Record type code |
Name | String | Short descriptive name |
Fingerprint | String | Unique identifier for the Asset |
Metadata | Map | Key-Value to describe Asset |
Registrant | Account | Public key of the signer |
Signature | Signature | Ed25519 signature of signer |
An issue record actually issues an asset (or an additional copy of an asset) to an owner on the Bitmark blockchain.
Each issue must have a unique nonce value to distinguish individual copies of that asset.
Item | Type | Description |
---|---|---|
Type code | Byte | Issue Record type code |
AssetId | Binary | Link to asset record (SHA3-512 of asset fingerprint) |
Owner | Account | The public key of the signer |
Nonce | VarInt | Allow for multiple issues of the same asset |
Signature | Signature | Ed25519 signature of signer |
A transfer record transfers ownership of an issued asset in a provenance chain on the Bitmark blockchain.
Item | Type | Description |
---|---|---|
Type code | Byte | Transfer Record type code |
Link | Binary | Link to previous Issue or Transfer (SHA3-256 of previous transaction) |
Escrow | Payment | Optional escrow payment and address |
Owner | Account | The public key of the new owner |
Signature | Signature | Ed25519 signature of linked previous owner |
CounterSignature | Signature | Ed25519 signature of this record owner |
The Bitmark blockchain also supports transactions that deal with fractional bitmarks as a kind of fungible token.
A share balance record marks the end of a provenance chain. It splits the issued asset into an initial quantity of shares. From this point on, the shares can be granted or swapped with other Bitmark accounts.
Item | Type | Description |
---|---|---|
Type code | Byte | Bitmark Balance type code |
Link | Binary | Link to previous Issue or Transfer (SHA3-256 of previous transaction) |
Quantity | VarInt | Initial balance quantity |
Signature | Signature | Ed25519 signature of linked previous owner |
A share grant record moves a quantity of shares from one owner to another.
Item | Type | Description |
---|---|---|
Type code | Byte | Share Grant type code |
ShareId | Binary | Link to previous Issue (SHA3-256 of issue transaction) |
Quantity | VarInt | Number of shares to transfer to recipient |
Owner | Account | Public key of the current owner |
Recipient | Account | Public key of the new owner |
BeforeBlock | VarInt | Expiry block height for transaction |
Signature | Signature | Ed25519 signature of current owner |
CounterSignature | Signature | Ed25519 signature of recipient |
A share swap record performs an atomic swap between two owners with two different share types. The action is indivisible, and there is no possibility of a half transaction being executed.
Item | Type | Description |
---|---|---|
Type code | Byte | Share Swap type code |
ShareIdOne | Binary | Link to previous Issue (SHA3-256 of issue transaction) |
QuantityOne | VarInt | Number of shares one to transfer to owner two |
OwnerOne | Account | The public key of the share one owner |
ShareIdTwo | Binary | Link to previous Issue (SHA3-256 of issue transaction) |
QuantityTwo | VarInt | Number of shares two to transfer to owner one |
OwnerTwo | Account | The public key of the share two owner |
BeforeBlock | VarInt | Expiry block height for transaction |
Signature | Signature | Ed25519 signature of owner one |
CounterSignature | Signature | Ed25519 signature of owner two |
Finally, the Bitmark blockchain also supports a few transactions for defining block ownership.
A block foundation record is the first transaction in a block and defines the ownership of that block. It is only set by the block mining process. It is also used to set the miner's payment addresses, which are used when transactions reference those in the block.
Item | Type | Description |
---|---|---|
Type code | Byte | Block Foundation type code |
Version | VarInt | Sets the combination of supported currencies |
Payments | Map | Map of Currency and Address |
Owner | Account | Public key of the owner |
Nonce | VarInt | Additional NONCE for mining |
Signature | Signature | Ed25519 signature of owner |
A block owner transfer record allows the current owner of a block to transfer any future earnings from that block to another owner, with new currency addresses.
Item | Type | Description |
---|---|---|
Type code | Byte | Block Foundation type code |
Link | Binary | Link to previous Foundation or Block Transfer (SHA3-256 of previous transaction) |
Escrow | Payment | Optional escrow payment and address |
Version | VarInt | Sets the combination of supported currencies |
Payments | Map | Map of Currency and Address |
Owner | Account | Public key of the new owner |
Signature | Signature | Ed25519 signature of linked previous owner |
CounterSignature | Signature | Ed25519 signature of this record owner |
Transactions are gathered together into blocks, which start with a block header, followed by a single foundation record and then the rest of the transactions.
A block is secured by having a nonce and a difficulty
value embedded in the header. The difficulty can only change at
specific points and in specific ways defined in the bitmarkd
consensus
algorithm; any block with a difficulty that does not comply is
immediately rejected. It is possible to determine if a header
is valid by checking if its hash value is less than or equal to the
value indicated by the difficulty.
The chain itself is formed by storing the hash of the previous block in the block header, which links the current block to the previous block.
The block header is used both to provide metadata (like timestamp) for a block and to verify that the list of transactions attached to the block are valid.
Item | Type | Description |
---|---|---|
Version | U16 | Version of block rules in action |
TransactionCount | U16 | Number of transactions in the block |
Number | U64 | The number of this block |
PreviousBlock | Argon2 | Thirty-two byte hash of the previous block |
MerkleRoot | SHA3-256 | Thirty-two byte hash of the transaction Merkle tree |
Timestamp | U64 | UNIX timestamp (seconds after start of 1970-01-01) |
Difficulty | U64 | Difficulty fraction as 57 bit mantissa + 8 bit exponent |
Nonce | U64 | Nonce created by hashing to meet the difficulty |
A Merkle Tree combines the hashes of individual records, such that only the same records hashed together in the same order will result in the same Merkle Tree root. It does so by hashing individual transactions using the SHA3-256 algorithm to create the leaves of a binary tree. Each pair of hashes in the tree is then combined to form a new hash. Whenever a hash in the tree does not have a paired hash to combine with, it is instead hashed with itself. This process is repeated until only a single hash is left: the Merkle Tree root.
The SHA3 algorithm is the current recommended hashing algorithm to use and fixes some vulnerability problems that were found in SHA2. The SHA2 algorithm would have to be used twice to protect against this, which would cost more CPU resources. The SHA3 is faster, so it reduces the time to build the Merkle Tree. SHA-1 has been broken, so it was not considered.
The SHA3 algorithm is also used for transaction IDs and is similarly the hash of the packed binary transaction including all signatures.
To prevent hardware attack of the Proof of Work system using GPUs or ASICs, a memory hard algorithm is used: Argon2. Its parameters are set such that a GPU will be some orders of magnitude slower that a typical x86 CPU core. The current setting for the hashing power of CPUs is still very low, on the order of a few hashes per second; this means that the difficulty scale is much lower than other blockchain systems.
The difficulty is represented as a floating point value in the range:
1 ≥ d > 0
The difficulty value is encoded as a 57-bit mantissa, normalized so that the most significant bit is one and can be dropped, leaving 56 bits to store:
[8 bits exponent][56 bits mantissa] = 64 bit unsigned value
mantissa is right shifted by exponent+8 from the most significant bit
Examples:
the "One" value: 00 ff ff ff ff ff ff ff
represents the 256 bit value: 00ff ffff ffff ffff 8000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
value: 01 ff ff ff ff ff ff ff
represents the 256 bit value: 007f ffff ffff ffff c000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
Notes:
1. the values here are shown as big endian, but the header is stored little endian
2. the "one" value represents exactly a difficulty of 1.0 in the packed 64 bits
The hash is considered as a 256-bit fixed point value with 8 bits before the decimal point:
[8 bits] . [248 bits]
A hash meets the difficulty if its value is less than or equal to difficulty value.