Skip to content
Branch: master
Find file Copy path
Find file Copy path
3 contributors

Users who have contributed to this file

@Lbird @FelixSeol @Crepepepe
313 lines (253 sloc) 15.7 KB

AMO Blockchain Protocol Specification


Although the current implementation of AMO blockchain depends heavily on Tendermint, AMO blockchain protocol itself is independent of Tendermint. It is described by several protocol messages and corresponding state transition in abstract internal database of each blockchain node. While the protocol messages are concretely defined(meaning and format), abstract internal database of a blockchain node is implementation-specific. But, note that every AMO blockchain node MUST incorporate a kind of database storing all kinds of data items described in Internal Data section.

Some notes related to Tendermint will be tagged with TM.

Data Format


AMO blockchain uses ECDSA key pair to sign and verify various transactions and messages. AMO blockchain uses NIST P256 curve as its default ECDSA domain parameter.

A key pair in AMO blockchain is a pair of a private key and a public key. A private key is a sequence 32 bytes, and a public key is a sequence of 33 bytes(compressed form. TODO: give a reference). These byte sequences are represented by HEX encoding when transmitted over a communication channel or stored as a marshaled form, while they reside as is in a program's internal memory space.

Key custody

A key custody is a special form of key transfer medium. It is a public-key encryption of a data encryption key: PKEnc(PK, DEK), where PKEnc is a sort of a hybrid encryption (combination of public key encryption and symmetric key encryption). For PKEnc, we use a combination of ECDH ephemeral mode and AES. For ECDH ephemeral key generation, we use ECDSA key generation algorithm.PK is a public key of a recipient and DEK is a data encryption key of an encrypted data parcel.

Account address

An address is a human-readable character string which is a hex-encoding of a byte sequence with the length of 20 bytes (=160-bit). Hence, the opaque form of an address is a 40-byte character string which consists of [0-9] and [a-f] only.

An account address is derived from the public key of an account. First, take 32 bytes by applying SHA256 on the public key bytes. Next, take 20 bytes by truncating the first 20 bytes from the 32-byte SHA256 output: addr_bin = trunc_20(SHA256(PK)). For the last step, convert this addr_bin by hex-encoding. An AMO-compliant program may utilize this addr_bin for its internal purpose, but it should apply hex-encoding before sending to other protocol party or storing to other medium outside the program.

NOTE: In Bitcoin, they use addr_bin = RIPEMD160(SHA256(PK)), but we cannot use RIPEMD160. See Notes on Cryptography for more details and reasons.

Message Format


A transaction is a description of the state change in a blockchain node's internal database(i.e. blockchain state). In other words, sending a transaction to a blockchain node is the only way to change blockchain state. When a transaction is received by a node and eventually included in a block, a blockchain node shall modify the internal database according to each transaction type.

A transaction is represented by a JSON document which has the following format:

    "type": "_tx_type_",
    "sender": "_sender_address_",
    "nonce": "_HEX-encoded_nonce_bytes_",
    "params": "_HEX-encoded_JSON_object_",
    "signature": {
        "pubkey": "_HEX-encoded_public_key_bytes_",
        "sig_bytes": "_HEX-encoded_signature_bytes_"

"_tx_type_" is one of :

  • coins and stakes
    • "transfer"
    • "stake"
    • "withdraw"
    • "delegate"
    • "retract"
  • parcels
    • "register"
    • "request"
    • "grant"
    • "discard"
    • "cancel"
    • "revoke"

_sender_address_ identifies the sender or originator of this transaction. "nonce" is a random byte sequence with the length of 20 bytes represented by HEX-encoding(See Replay Attack). "params" is a HEX-encoded JSON object, which is different for each transaction type.

  • transfer body:
    "to": "_recipient_address_",
    "amount": "_currency_"
  • stake body:
    "validator": "_validator_pubkey_",
    "amount": "_currency_"
  • withdraw body:
    "amount": "_currency_"
  • delegate body:
    "to": "_delegator_address_",
    "amount": "_currency_"
  • retract body:
    "from": "_delegator_address_",
    "amount": "_currency_"
  • register body:
    "target": "_parcel_id_",
    "key_custody": "_owner_custody_",
    "extra": "_extra_info_"
  • request body:
    "target": "_parcel_id_",
    "payment": "_currency_",
    "extra": "_extra_info_"
  • cancel body
    "target": "_parcel_id_"
  • grant body
    "target": "_parcel_id_",
    "grantee": "_buyer_address_",
    "key_custody": "_buyer_custody_"
  • revoke body
    "target": "_parcel_id_",
    "grantee": "_buyer_address_"
  • discard body
    "target": "_parcel_id_"

"pubkey" is of type P256, neither of ed25519 or secp256k1. It is a HEX-encoding of a compressed elliptic curve point with the length of 33 bytes.

"sig_bytes" is HEX-encoded ECDSA signature, which is a concatenation of r and s. This is (r, s) = ECDSA(privkey, sb), where privkey is the corresponding private key, and sb is a concatenation of values of "type", "sender", "nonce" and "params".

Transaction Result



TM: Tendermint core sends a Transaction to AMO app, and the app replies with the response ResponseDeliverTx. This ResponseDeliverTx is defined in Tendermint, but is not part of AMO blockchain protocol. However, this is an important reply to the users indicating whether the transmitted transaction was successfully processed or not. This reply is described in RPC document. Moreover, CLI may process this reply and prompt the user with a more human-friendly output.

Internal Data

  • coins and stakes
    • balance store:
      • key: address
      • value: balance
    • stake store:
      • key: address
      • value: {validator pubkey, stake}
    • delegate store:
      • key: address *
      • value: {delegator address, stake}
  • parcels
    • parcel store:
      • key: parcel id
      • value: {owner address, key custody, extra info}
    • request store:
      • key: {buyer address, parcel id}
      • value: {payment, exp cond}
    • usage store:
      • key: {buyer address, parcel id}
      • value: {key custody, exp cond}

NOTE: In delegate store, a key to the database is just address, not {holder address, delegator address}. This means that a user cannot delegate his/her stakes to multiple delegators. While an AMO-compliant node can freely choose the actual database implementation, this constraint must be enforced in any way. An implementor may choose to keep this address as a unique key, or use more generous database implementation with an application code or a wrapper layer to keep this constraint on top of it.


There shall be no other state change than described in this section.

TM: These operations are implemented by DeliverTx method in the ABCI application.

Transferring coin

Upon receiving a transfer transaction from a sender, an AMO blockchain node performs a validity check and transfers coins from sender's balance to recipient's balance when the transaction is valid.

Validity check:

  1. sender_balanceamount.

State change:

  1. sender_balancesender_balance - amount
  2. recipient_balancerecipient_balance + amount

Staking coin

Upon receiving a stake transaction from an account, an AMO blockchain node performs a validity check and locks requested coins to stake store and decreases the account's balance when the transaction is valid.

Validity check:

  1. balanceamount
  2. There is no other stake holder with the same validator key as this transaction.

State change:

  1. balancebalance - amount
  2. stakestake + amount
  3. If the previous validator key is different from the key in the current stake transaction, then the stake holder's validator key is replaced with the new one.

Upon receiving a withdraw transaction from an account, an AMO blockchain node performs a validity check and relieves requested coins from stake store and increases the account's balance when the transaction is valid.

Validity check:

  1. stakeamount
  2. stake > amount if this account is a delegator for any of delegated stakes

TODO: minimum required stake to be a delegator

State change:

  1. balancebalance + amount
  2. stakestake - amount

TODO: need rounding? or currency to stake ratio?

Delegating stake

There may be users who have the intention to participate in the block production but don't have enough stake value or computing power to competent in the validator selection race. In this case, a user can delegate his/her stake to a more competent validator.

Upon receiving a delegate transaction from an account, an AMO blockchain node performs a validity check and locks requested coins to delegate store and decreases the account's balance when the transaction is valid.

Validity check:

  1. balanceamount
  2. to address already has a positive stake in stake store
  3. the account has no previous delegator or to is the same as the previous delegator

State change:

  1. balancebalance - amount
  2. delegated_stakedelegated_stake + amount

Upon receiving a retract transaction from an account, an AMO blockchain node performs a validity check and relieves requested coins from delegate store and increases the account's balance when the transaction is valid.

Validity check:

  1. delegated_stakeamount

State change:

  1. balancebalance + amount
  2. delegated_stakedelegated_stake - amount

NOTE: delegated_stake is a stake value in the delegate store where the address is the sender account.

Updating validator set

If there is at least one of stake, withdraw, delegate or retract transaction in the last block, the top n_val accounts with the highest effective stake value shall be selected again. These accounts shall be new validators for the upcoming blocks.

NOTE: Effective stake value is the sum of his/her own stake in the stake store and all items in the delegate store having the same delegator field as the account address in question

NOTE: n_val is a global parameter fixed across nodes and blocks (and so the time). So, it shall be set at the genesis time.

TM: New list of validator pubkeys shall be transferred to the Tendermint daemon via EndBlock response. Each validator has the voting power in proportion to the effective stake value.

TODO: more accurate validator update time. consider non-tendermint implementations.

Voting power calculation

TM: In tendermint, a voting power has a similar role as a stake in PoS or DPoS consensus mechanism. One limitation is that sum of voting powers of all validators must not exceed the value MaxTotalVotingPower, which is 2^60 - 1. When we use one-to-one relation between stake value and voting power, exceeding this max limit is not very likely, but possible anyway. So, the validator set update mechanism must adjust voting power of each validator, so that total sum of voting power does not exceed MaxTotalVotingPower:

  1. For each validator Val_i, set voting power vp_i to be stake of Val_i.
  2. Calculate TotalVotingPower, which is the sum of vp_is of all validators in the new validator set.
  3. adjFactor ← 0 (use this as a persistent factor)
  4. While TotalVotingPower > MaxTotalVotingPower
    1. adjFactoradjFactor + 1
    2. TotalVotingPowerTotalVotingPower / 2
      (implemented as right-shift)
    3. For each validator Val_i, vp_ivp_i / 2
      (implemented as right-shift)

NOTE: When vp_i reaches to zero, then Val_i shall be removed from the new validator set.

Registering data

Requesting data

Granting data

Block Reward

TM: Tendermint provides a block information via BeginBlock() ABCI method, which includes a block proposer address. This address is derived from the validator pubkey who proposed the block. In AMO ABCI app, we can look up the original stake holder in the stake store having the same validator pubkey.

A stake holder who proposed a block receives a block reward. This is the only step in which there is a state change in balance store without involving any transaction:
Rb_reward + n_tr * tx_reward
where R is the final block reward, b_reward a block reward rate, n_tr the number of transactions in the block, and tx_reward a transaction reward rate.

When the block reward is R, this reward shall be distributed among the stake holder and the delegated stake holders. The distribution mechanism is as the following:

  1. wStakesw_val * stake_0 (stake of the proposer)
  2. For each delegated stake stake_i, wStakeswStakes + w_ds * stake_i
  3. Calculate the reward for the proposer R_0R * w_val * stake_0 / wStakes.
  4. For each delegated stake holder, calculate the reward for i-th delegated stake, R_iR * w_ds * stake_i / wStakes.

where w_val is the validator stake weight, and w_ds is the delegated stake weight.

TODO: Eliminate ambiguity in float number arithmetic.

TODO: Take care of overflow situation.

Genesis App State

Initial state of the app (genesis app state) is defined by genesis document (genesis.json file in tendermint config directory, typically $HOME/.tendermint/config/genesis.json). Initial app state is described in app_state field in a genesis document. For example:

"app_state": {
    "balances": [
            "owner": "7CECB223B976F27D77B0E03E95602DABCC28D876",
            "amount": "100"

TM: In order to reset and apply new genesis state, run the following command in command line:

tendermint unsafe_reset_all

An AMO-compliant blockchain node should have some mechanisms to modify internal database for this operation.

Further Notes

Replay Attack

In order to prevent replay attack (in some sense, double-spending), every AMO transaction includes a nonce byte-sequence. Basic idea is that when a blockchain node sees a transaction that is already included in the blockchain, it immediately discards the transaction. Here, every transaction has a tx hash in Tendermint context. This tx hash is a hash of whole byte sequence representing the transaction. Since we incorporated ECDSA signature to authenticate the sender's identity, this gives randomness already, and it can prevent replay attacks. However, AMO blockchain protocol itself is independent of Tendermint. Moreover a future version AMO blockchain may not use Tendermint as a base platform. So, in order to provide some generic countermeasure against replay attacks, we introduced this nonce bytes.

However, if every transaction has different meaning and context from other transactions, users may choose nonce to be a fixed bytes. But, if a user wants to send the same amount of coin to the same recipient again, then the user must choose a different nonce than the previous transaction. This nonce need not be cryptographically random. The only requirement is that it must be different if the transaction context is the same as a previously processed one. An AMO-compliant client may introduce any method to do this.

You can’t perform that action at this time.