AMO protocol version 6 (DRAFT).
현재 구현된 AMO 블록체인은 텐더민트에 크게 의존적이지만 AMO 블록체인의 프로토콜 자체는 텐더민트에 독립적입니다. 이는 각 블록체인 노드의 추상화 된 내부 데이터베이스에서 여러 프로토콜 메시지와 해당 상태 전환으로 설명이 됩니다. 프로토콜 메시지는 구체적으로 정의되어 있지만(의미와 형식) 블록체인 노드의 추상 내부 데이터베이스는 구현에 따라 다릅니다. 그러나 모든 AMO 블록체인 노드는 Blockchain Data 섹션에 설명된 모든 종류의 데이터 항목을 저장하는 데이터베이스를 포함해야 합니다.
텐더민트와 관련된 일부 내용에는 TM 태그가 지정됩니다.
AMO 블록체인은 다양한 트랜잭션과 메시지에 대한 서명 및 검증을 위해 ECDSA 키 쌍을 사용하고, ECDSA 기본 도메인 파라미터로 NIST P256 curve(secp256r1) and SHA256을 사용합니다. AMO 블록체인의 키 쌍은 개인 키와 공개 키의 쌍입니다. 개인 키는 32바이트 시퀀스이고 공개 키는 65바이트의 시퀀스로 0x04 접두어로 시작하는 압축되지 않은 형식입니다. 이러한 바이트 시퀀스는 통신 채널을 통해 전송되거나 마샬링된 형식으로 저장될 때 HEX 인코딩으로 표시되지만 프로그램의 내부 메모리 공간에서 다른 형식으로 상주할 수 있습니다.
개인 키는 네트워크 통신 채널을 통해 절대 전송되지 않아야 합니다. 공개 키는 프로토콜 메시지에 16진수로 인코딩되어 포함되어야 합니다.
이 문서에서는 Account Key와 관련된 형식을 다음과 같이 표현합니다.
_HEX_encoded_public_key_bytes_(16진수로 인코딩 된 공개키 바이트)
이 문서에서는 서명과 관련된 형식을 다음과 같이 사용합니다.
{
"pubkey": "_HEX_encoded_public_key_bytes_(16진수로 인코딩된 공개키 바이트)",
"sig_bytes": "_HEX_encoded_signature_bytes_(16진수로 인코딩된 서명 바이트)"
}pubkey는 서명자(서명하는 사람)의 공개키이고 sig_bytes는 r과 s로 연결된 16진수로 인코딩된 ECDSA 방식의 서명 바이트입니다. (r, s) = ECDSA(privkey, sig_bytes)는 ECDSA 서명 알고리즘의 출력이며, 여기서 privkey는 서명자의 개인 키입니다.
검증자의 키 쌍은 ed25519 키 쌍으로 텐더민트에서 처리하지만, 검증자의 공개키는 AMO 코인을 스테이킹하여 지분을 획득할 때 AMO 블록체인 프로토콜 메시지로 전달됩니다. 이 경우 검증자의 공개키는 16진수로 인코딩되어야 합니다.
이 문서에서는 검증자 키와 관련된 형식을 다음과 같이 사용합니다.
_HEX_encoded_ed25519_pubkey_
키 커스터디는 특별한 형태의 키 전송 매체입니다. 데이터 암호화 키는 공개 키 암호화를 권장합니다. PKEnc(PK, DEK), 여기서 PKEnc는 일종의 하이브리드 암호화(공개 키 암호화와 대칭 키 암호화의 조합)입니다. PKEnc의 경우 ECDH 임시 모드와 AES-256의 조합을 사용합니다. ECDH 임시 키 생성을 위해 ECDSA 키 생성 알고리즘을 재사용합니다. PK는 수신자의 공개 키이고 DEK는 암호화된 데이터 패키지를 암호화 하기 위한 키입니다.
이 문서에서는 키 커스터디와 관련된 형식을 다음과 같이 사용합니다.
_HEX_encoded_key_custody_(16진수로 인코딩된 키 커스터디)
주소는 길이가 20바이트(=160비트)인 바이트 시퀀스의 16진수로 인코딩된 human-readable한 문자열입니다. 따라서 주소의 형태는 [0-9]와 [A-F]로만 구성된 40바이트 문자열입니다.
계정 주소는 계정의 공개 키에서 유도됩니다. 먼저 공개 키 바이트에 SHA256을 적용하여 32바이트를 가져옵니다. 다음으로 32바이트 SHA256 출력에서 처음 20바이트를 잘라 20바이트를 가져옵니다: addr_bin = trunc_20(SHA256(PK)). 마지막 단계에서 이 addr_bin을 16진수 인코딩 문자열로 변환합니다. AMO 호환 프로그램은 내부 목적으로 이 addr_bin을 사용할 수 있지만 다른 프로토콜 당사자에게 전송하거나 프로그램 외부의 다른 매체에 저장하기 전에 16진수 인코딩을 적용해야 합니다.
주의사항: 비트코인에서는 addr_bin = RIPEMD160(SHA256(PK))를 사용하지만 RIPEMD160은 사용할 수 없습니다. 자세한 내용은 Notes on Cryptography을 참조하십시오.
이 문서에서는 계정 주소와 관련된 형식을 다음과 같이 사용합니다.
_account_address_=addr_bin(계정 주소)_HEX_encoded_account_address_= HEX encoding of_account_address_(16진수로 인코딩된 계정 주소)"_HEX_encoded_account_address_"as a JSON string(JSON 문자열로 사용시)
다른 대중적인 블록체인 시스템과 마찬가지로 AMO 코인은 전송 가능한 최소 단위의 배수인 정수 값으로 표시됩니다. AMO 블록체인에서는 단위를 *모트(mote)*라고 합니다. 그리고 1 AMO는 1018입니다. 모든 프로토콜 메시지에서 AMO 코인 금액은 mote(모트) 단위로 표시됩니다.
AMO 코인 또는 사용자 정의 코인의 금액은 JSON 형식의 메시지, 즉 모든 프로토콜 메시지에 포함될 때 큰따옴표로 묶인 십진수로 표현되어야 합니다.그러나 블록체인 노드의 내부 메모리에는 다른 형식으로 표현될 수 있습니다.
이 문서에서는 통화와 관련된 형식을 다음과 같이 사용합니다.
_currency_(통화)"_currency_"as a JSON string(JSON 문자열로 사용시)
Draft ID는 32비트 부호 없는 정수입니다. Draft ID는 JSON(e.g. 프로토콜 메시지)에서 사용될 때 숫자 앞에 0이 없는 큰따옴표로 묶인 십진수로 표시됩니다. 그러나 다른 식별자를 합성하는 데 사용되는 경우 숫자 앞에 0을 포함하는 4바이트 빅엔디안 정수로 표시됩니다.
이 문서에서는 Draft ID와 관련된 형식을 다음과 같이 사용합니다.
_draft_id_= alias of_decimal_number_(10진수)_draft_id_as a JSON number, e.g.1234not"1234"("1234"가 아닌 1234)
storage ID는 32비트 부호 없는 정수입니다. Storage ID는 JSON(e.g. 프로토콜 메시지)에서 사용될 때 숫자 앞에 0이 없는 큰따옴표로 묶인 십진수로 표시됩니다. 그러나 다른 식별자를 합성하는 데 사용되는 경우 4바이트 빅엔디안 정수로 표시됩니다.
이 문서에서는 Storage ID와 관련된 형식을 다음과 같이 사용합니다.
_storage_id_= alias of_decimal_number_(10진수)_storage_id_as a JSON number, e.g.1234not"1234"("1234"가 아닌 1234)
Parcel ID는 storage ID와 in-storage ID를 연결한 것입니다. In-storage ID는 32 바이트(256 비트) 바이너리 시퀀스입니다. 자세한 내용은 AMO Storage Specification를 참고하시기 바랍니다. 저장소 ID 자체는 32비트 부호 없는 정수입니다. 그러나 parcel ID를 구성할 때 storage ID는 4바이트 바이너리 시퀀스(빅엔디안 정수)로 변환되어 parcel ID에 포함되어야 합니다. 예를 들어, storage ID가 123456789이고, in-storage ID가 12ABEF23...이라고 가정 했을 때 이 Parcel은 16진수로 인코딩 될 경우 075BCD1512ABEF23...을 갖습니다. 여기서 075BCD15는 정수인 storage ID 123456789의 16진수 인코딩(빅엔디안 바이트 순서를 사용)입니다.
이 문서에서는 Parcel ID와 관련된 형식을 다음과 같이 사용합니다.
_parcel_id_(parcel ID)_HEX_encoded_parcel_id_= HEX encoding of_parcel_id_(16진수로 인코딩된 parcel ID)"_HEX_encoded_parcel_id_"as a JSON string(JSON 문자열로 사용 시)
register, request, grant tx는 추가 정보를 전달할 수 있습니다. 이러한 추가 정보는 JSON 객체여야하지만 JSON 객체의 내부 구조는 애플리케이션에 따라 다릅니다. 블록체인 노드의 내부 DB는 이전 단계의 추가 정보도 저장해야합니다. 즉, parcel은 register tx에 추가 저장하고, request는 register tx와 request tx 추가 정보를 저장, usage는 register tx, request tx 및 grant tx 추가 정보를 저장합니다.
extra in parcel store
{
"register": {} // application-specific JSON object, optional
}extra in request store
{
"register": {}, // application-specific JSON object, optional
"request": {} // application-specific JSON object, optional
}extra in usage store
{
"register": {}, // application-specific JSON object, optional
"request": {}, // application-specific JSON object, optional
"grant": {} // application-specific JSON object, optional
}JSON 객체는 중괄호({ 및 })로 묶어야 하므로 단일 JSON 값이 될 수 없습니다. 각 추가 정보는 빈 객체({})이거나 프로퍼티가 있는 적절한 JSON 객체여야 합니다.
{
"register": "boo", // wrong
"request": {"some":"value"}, // ok
"grant": {} // ok
}각 프로퍼티는 선택 사항으로 표시되므로 빈 객체({})는 세 가지 상태 저장소 모두에 유효한 추가 정보입니다.
UDC ID는 32비트 부호 없는 정수이며 JSON에서 사용될 때 숫자 앞에 0이 없는 10진수로써 큰따옴표로 표시됩니다(예: 프로토콜 메시지). 그러나 다른 식별자를 합성 하는데 사용되는 경우에는 4바이트 빅엔디안 정수로 표현한다.
이 문서에서는 UDC ID와 관련된 형식을 다음과 같이 사용합니다.
_udc_id_= alias of_decimal_number__udc_id_as a JSON number, e.g.1234not"1234"
AMO 메인넷에서 사용할 때 DID는 "did:amo:" 문자열과 계정 주소를 대문자 16진수 문자열로 연결한 것입니다. AMO 메인넷이 아닌 다른 네트워크에서 사용되는 경우 접두사는 "did:" + 메서드 이름 + ":"과 같으며 여기서 메서드 이름은 "amo"가 아닙니다.
did.claim tx와 did.dismiss tx는 AMO DID Method에서 사용하는 DID document에서 동작합니다. DID document는 JSON-LD 표현이라고 하는 추가적으로 최상위 @context 프로퍼티가 있는 JSON document입니다. AMO 블록체인 노드는 @context 프로퍼티 값을 신경 쓰지 않지만 이 프로퍼티는 반드시 존재해야 하며 그 값은 문자열 타입이어야 합니다.
AMO 메인넷에서 사용될 때 VC ID는 문자열 "amo:cred:"와 최대 길이가 64자인 대문자 16진수 문자열을 연결한 것입니다. 즉, 길이가 32 이하인 바이트 문자열의 표현입니다. AMO 메인넷이 아닌 다른 네트워크에서 사용되는 경우 접두사는 network_name + ":cred:"와 같으며 여기서 network_name은 "amo"가 아닙니다.
did.issue tx 및 did.revoke tx는 AMO Verifiable Credential Registry에서 사용되는 VC(Verifiable Credential)에서 동작합니다AMO Verifiable Credential Registry. VC는 JSON-LD 표현이라고 하는 추가 최상위 @context 속성이 있는 JSON document입니다. AMO 블록체인 노드는 @context 속성 값을 신경 쓰지 않지만 이 속성은 반드시 존재해야 하며 그 값은 문자열 타입이어야 합니다.
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 trigger the change in a 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 context:
{
"type": "_tx_type_",
"sender": "_HEX_encoded_account_address_",
"fee": "_currency_",
"last_height": "_decimal_number",
"payload": {}, // tx-specific JSON object
"signature": {
"pubkey": "_HEX_encoded_public_key_bytes_",
"sig_bytes": "_HEX_encoded_signature_bytes_"
}
}It is irrelevant whether it is in compact or pretty form.
type identifies a transaction type. The value _tx_type_ is one of the
following:
- coins and stakes
transferstakewithdrawdelegateretract
- governance
proposevote
- storage
setupclose
- parcels
registerrequestgrantdiscardcancelrevoke
- did
did.claimdid.dismissdid.issuedid.revoke
- user-defined coin
issueburnlock
sender identifies the sender or originator of this transaction. fee is
amount of AMO coin expected to get transferred to a block proposer after the
transaction is committed to a block. last_height is the last height of AMO
blockchain at the time creating the transaction. payload is a JSON object,
which is specific for each transaction type.
signature is an ECDSA signature of the sender on the compact JSON
representation of a transaction with all the HEX-encoded string in upper
case as the following:
{"type":"transfer","sender":"662E3DD1C6470CFE12C8EDBCE5F44C08E2763753","fee":"0","last_height":"4052","payload":{"to":"614A9F2FC4E6B119D7612C35BC150E33CB38BB40","amount":"100"}}A signed transaction is as the following:
{"type":"transfer","sender":"662E3DD1C6470CFE12C8EDBCE5F44C08E2763753","fee":"0","last_height":"4052","payload":{"to":"614A9F2FC4E6B119D7612C35BC150E33CB38BB40","amount":"100"},"signature":{"pubkey":"04DBCEC2C0F52018606F588713305E1DA49367037281B960F51C46BE64E3144977009A811A865B3CB3331B788147C03853C7920C4C8FB6FFB5B0D435DAEB3F59A4","sig_bytes":"50A8307AAFF6611AE67ADD09EA813F37668072A214230DF375CFA25FB368B0EBD861943661EC690AE0E5D789E738B3C4518F78D768E5E006C9EB53E81821671D"}}TM: Tendermint receives transactions via tendermint-specific RPC channel. For the exact RPC message format, see AMO Client RPC Specification.
A payload format for each transaction type is as the following.
-
transferpayload:{ "to": "_HEX_encoded_account_address_", "udc": _udc_id_, // optional "amount": "_currency_", // optional "parcel": "_HEX_encoded_parcel_id_" // optional }where
tois the recipient of the transfer and mandatory,amountis amount of AMO coin or user-defined coin, andparcelis ID of a parcel to be transferred. Either one ofamountorparcelmust be present, but not both. Ifamountis present there can be another optional fieldudc, which is an identifier of a user-defined coin. If both ofamountandudcare present, the transfer tx is for transferring user-defined coin. Ifamountis present butudcis not, the transfer tx is for transferring AMO coin._udc_id_must be one of registered user-defined coin ID._currency_is a string representation of a decimal number. -
stakepayload:{ "validator": "_HEX_encoded_ed25519_pubkey_", "amount": "_currency_" }where
validatoris the only public key type other than P256 public key used in AMO blockchain protocol. It must be obtained from underlying Tendermint node, but in HEX encoding, not Base64 encoding.amountis amount of AMO coin to be locked as stake. -
withdrawpayload:{ "amount": "_currency_" }where
amountis amount of AMO coin to be withdrawn from stake. -
delegatepayload:{ "to": "_HEX_encoded_account_address_", "amount": "_currency_" }where
tois an address of an account which has stakes already andamountis amount of AMO coin to be delegated. -
retractpayload:{ "amount": "_currency_" }where
amountis amount of AMO coin to be retracted from delegated stake. -
proposepayload{ "draft_id": "_draft_id_", "config": {}, // application-specific JSON object "desc": "human-readable string describing this draft" }where
configis an optional field which is necessary for a proposal of applying of new configuration on-chain. -
votepayload{ "draft_id": "_draft_id_", "approve": true // boolean }where
approveindicatessender's opinion ondraft_id;truefor approval orfalsefor rejection. -
setuppayload{ "storage": _storage_id_, // integer "url": "_url_", "registration_fee": "_currency_", "hosting_fee": "_currency_" } -
closepayload{ "storage": _storage_id_ // integer } -
registerpayload:{ "target": "_HEX_encoded_parcel_id_", "custody": "_HEX_encoded_key_custody_", "proxy_account": "_HEX_encoded_account_address_", // optional "extra": {} // application-specific JSON object, optional }where
targetis the id of a parcel currently being registered,custodyis a encrypted key material used to encrypt the data parcel body, and the key material is encrypted by the owner(seller)'s public key. -
requestpayload:{ "target": "_HEX_encoded_parcel_id_", "payment": "_currency_", "recipient": "_HEX_encoded_account_address_", // optional "dealer": "_HEX_encoded_account_address_", // optional "dealer_fee": "_currency_", // optional "extra": {} // application-specific JSON object, optional }where
targetis the id of a parcel for which the sender wants usage grant,paymentis amount of AMO coin to be collected by the seller,recipientis the address of a recipient explictly designated to get granted a usage on the parcel. In order fordealer_feeto work, both ofdealeranddealer_feemust be valid. -
grantpayload{ "target": "_HEX_encoded_parcel_id_", "recipient": "_HEX_encoded_account_address_", "custody": "_HEX_encoded_key_custody_", "extra": {} // application-specific JSON object, optional }where
targetis the id of a parcel currently being granted,recipientis the address of a recipient,custodyis a encrypted key material used to encrypt the data parcel body, and the key material is encrypted by the buyer's public key. -
discardpayload{ "target": "_HEX_encoded_parcel_id_" }where
targetis the id of a parcel currently being discarded. -
cancelpayload{ "target": "_HEX_encoded_parcel_id_", "recipient": "_HEX_encoded_account_address_" // optional }where
targetis the id of a parcel which the sender requested previously,recipientis the address of a recipient designated to get granted a usage on the parcel. -
revokepayload{ "target": "_HEX_encoded_parcel_id_", "recipient": "_HEX_encoded_account_address_" }where
targetis the id of a parcel currently being revoked,recipientis the address of a buyer which is previously granted a usage on the parcel. -
did.claimpayload{ "target": "_string_", "document": {} // DID document in the form of JSON object }where
targetis a JSON string conforming toidcharin DID syntax.documentvalue will be stored as a compact representation. Whendid.claimtx is received on a previously claimedtarget,documentwill be replaced with a new one. -
did.dismisspayload{ "target": "_string_", }effectively removes the DID document from the DID registry.
-
did.issuepayload{ "target": "_string_", "credential": {} // verifiable credential in the form of JSON object }where
targetis a JSON string representing a VC id.credentialvalue will be stored as a compact representation. Whendid.issuetx is received on a previously issuedtarget,credentialwill replace the old one. -
did.revokepayload{ "target": "_string_", }effectively removes the VC from the VC registry.
-
issuepayload{ "udc": _udc_id_, "desc": "human-readable string describing this user-defined coin", "operators": [ "_HEX_encoded_account_address_", ... ], "amount": "_currency_" }where
operatorsis an optional list of operator addresses, andamountis the amount of UDC balance to be created. -
lockpayload{ "udc": _udc_id_, "holder": "_HEX_encoded_account_address_", "amount": "_currency_" }where
udcis an identifier of a user-defined coin, andamountis the amount of UDC coin to be locked. -
burnpayload{ "udc": _udc_id_, "amount": "_currency_" }where
udcis an identifier of a user-defined coin, andamountis the amount of UDC balance to burn.
AMO blockchain state is an exact snapshot of all the active data items.
Typically, this state is stored as a key-value database, but the exact method
for managing this database may be different for each implementation. One
obligation is that every blockchain node must be able to calculate the same
app_hash for a given block height, and all kinds of implementation must have
the same semantic meaning. This section describes the data format suitable for
calculating app_hash.
The state database stores data items in separate logical state stores according to the data type. To distinguish between logical state stores, each state store has unique prefix for the database key. A prefix is a human-readable ASCII string, but it is treated as a byte array when concatenating with the in-store data item key.
There is a top-level data item not associated with any logical state store. This
item has the key as config and the value is a JSON marshaled blockchain
configuration.
{
"max_validators": 100,
"weight_validator": 2,
"weight_delegator": 1,
"min_staking_unit": "_currency_",
"blk_reward": "_currency_",
"tx_reward": "_currency_",
"penalty_ratio_m": 0.1,
"penalty_ratio_l": 0.01,
"laziness_window": 100,
"laziness_threshold": 90,
"hibernate_threshold": 10,
"hibernate_period": 1000,
"block_binding_window": 100,
"lockup_period": 3600,
"draft_open_count": 500000,
"draft_close_count": 100000,
"draft_apply_count": 500000,
"draft_deposit": "_currency_",
"draft_quorum_rate": 0.1,
"draft_pass_rate": 0.7,
"draft_refund_rate": 0.2,
"upgrade_protocol_height": 1,
"upgrade_protocol_version": 1
}| key | value type | value constraint |
|---|---|---|
max_validators |
uint64 | > 0 |
weight_validator |
float64 | > 0 |
weight_delegator |
float64 | > 0 |
min_staking_unit |
currency | > 0 |
blk_reward |
currency | >= 0 |
tx_reward |
currency | >= 0 |
penalty_ratio_m |
float64 | > 0 |
penalty_ratio_l |
float64 | > 0 |
laziness_window |
int64 | >= 10000 |
laziness_threshold |
int64 | > 0 |
hibernate_threshold |
int64 | >= 10000 |
hibernate_period |
int64 | > 0 |
block_binding_window |
int64 | >= 10000 |
lockup_period |
int64 | >= 10000 |
draft_open_count |
int64 | >= 10000 |
draft_close_count |
int64 | >= 10000 |
draft_apply_count |
int64 | >= 10000 |
draft_deposit |
currency | >= 0 |
draft_quorum_rate |
float64 | > 0 |
draft_pass_rate |
float64 | > 0 |
draft_refund_rate |
float64 | > 0 |
upgrade_protocol_height |
int64 | > app.state.Height + draft_open_count + draft_close_count + draft_apply_count |
upgrade_protocol_version |
uint64 | == app.state.ProtocolVersion + 1 |
It is mandatory to restrict proper type and value of configurations in order to
make AMO blockchain protocol keep operating as it has to, even after modifying
their values since genesis block. The currency-related configurations' type is
restricted to string as it can store values without limit. Even though it is
highly recommended to use uint64 on configurations for its better space
availability than int64, laziness_window, block_binding_window,
lockup_period, draft_*_count, and upgrade_protocol_height have to use
int64 as it is an tendermint-dependant configuration.
There are 12 default state stores and optional UDC(user-defined coin) balance and balance lock stores.
| tier | category | store | prefix |
|---|---|---|---|
| 0 | fungible asset | AMO coin balance | balance: |
| 0 | fungible asset | stake | stake: |
| 0 | fungible asset | delegate | delegate: |
| 0 | maintenance | hibernate | hibernate: |
| 1 | governance | draft | draft: |
| 1 | governance | vote | vote: |
| 2 | maintenance | storage | storage: |
| 2 | non-fungible asset | parcel | parcel: |
| 2 | non-fungible asset | request | request: |
| 2 | non-fungible asset | usage | usage: |
| 2 | non-fungible asset | did | did: |
| 3 | maintenance | UDC | udc: |
| 3 | maintenance | UDC balance lock | udclock:<udc_id>: |
| 3 | fungible asset | UDC balance | balance:<udc_id>: |
Tier 0 items are essential for the operations of a DPoS-based blockchain. Tier 1 items are important as much as the tier 0 items, but the chain may be still called a functional blockchain without them. Tier 2 items defines the core business data items, while tier 3 items are pretty much optional.
- default coin balance
- key:
_account_address_ - value: JSON string
"_currency_" - key is the owner of a coin balance
- key:
- stake
- key:
_account_address_ - value: compact representation of a JSON object
{ "validator": "_HEX_encoded_ed25519_pubkey_", "amount": "_currency" } - key is the sender of a stake tx
- key:
- delegate
- key:
_account_address_ - value: compact representation of a JSON object
{ "delegatee": "_HEX_encoded_accont_address_", "amount": "_currency_" } - key is the sender of a delegate tx
- NOTE: For delegate store, a key to the database is just
_account_address_, instead of a concatenation of holder address and delegatee address. This means that a user can have only one delegated stake. In other words, a user cannot delegate his/her stakes to multiple delegatees. 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_account_address_as a unique key, or use more loose database implementation with an application code or a wrapper layer to keep this constraint on top of it.
- key:
- hibernate
- key:
_validator_address_ - value: compact representation of a JSON object
{ "start": _block_height_, "end": _block_height_ }
- key:
- draft
- key:
_draft_id_(big-endian) - value: compact representation of a JSON object
{ "proposer": "_HEX_encoded_account_address_", "config": {}, "desc": "_human_readable_string_describing_this_draft_", "open_count": "_decimal_number_", "close_count": "_decimal_number_", "apply_count": "_decimal_number_", "deposit": "_currency_", "tally_approve": "_currency_", "tally_reject": "_currency_" } configkeys should be a subset of the top-levelconfigitem. The values may be omitted if they should remain the same. There should be no multiple live drafts having config change items conflicting with each other.*_countcontrol overall voting process until the draft being passed and applied to the blockchain configuration. They are initialized according to the configuration at the time of being proposed.open_countis decremented at each block progress, and when it reaches zeroclose_countis decremented afterwards. Whenclose_countreaches zero and the vote summary is approval, thenapply_countis decremented until the new configuration is applied.tally_*fields count votes cast upon this draft.tally_approveandtally_rejectare as the names imply.
- key:
- vote
- key:
_draft_id_(big-endian) +_account_address_ - value: compact representation of a JSON object
{ "approve": true // boolean }
- key:
- storage
- key:
_storage_id_(big-endian) - value: compact representation of a JSON object
{ "owner": "_HEX_encoded_account_address_", "url": "_url_", "registration_fee": "_currency_", "hosting_fee": "_currency_", "active": _bool_ }
- key:
- parcel
- key:
_parcel_id_ - value: compact representation of a JSON object
{ "owner": "_HEX_encoded_account_address_", "custody": "_HEX_encoded_key_custody_", "proxy_account": "_HEX_encoded_account_address_", "extra": {} // application-specific JSON object } - key is the
targetof a register tx owneris the sender of a register tx
- key:
- request
- key:
_account_address_+_parcel_id_ - value: compact representation of a JSON object
{ "payment": "_currency_", "agency": "_HEX_encoded_account_address_", // optional "dealer": "_HEX_encoded_account_address_", // optional "dealer_fee": "_currency_", // optional "extra": {} // application-specific JSON object } - key is a concatenation of (sender or
recipient) andtargetof a request tx
- key:
- usage
- key:
_account_address_+_parcel_id_ - value: compact representation of a JSON object
{ "custody": "_HEX_encoded_key_custody_", "extra": {} // application-specific JSON object } - key is a concatenation of
recipientandtargetof a grant tx
- key:
- did
- key:
_did_address_ - value: compact representation of a JSON object
{ "owner": "_HEX_encoded_account_address_", "document": {} // DID documentn in the form of JSON object } - key:
- udc(user-defined coin)
- key:
_udc_id_ - value: compact representation of a JSON object
{ "owner": "_HEX_encoded_account_address_", "desc": "human-readable string describing this user-defined coin", "operators": [ "_HEX_encoded_account_address_", ... ], "total": "_currency_" } - key is
idof an issue tx owneris the sender of an initial issue tx
- key:
- udc balance
- key:
_account_address_ - value: JSON string
"_currency_"
- key:
- udc balance lock
- key:
_account_address_ - value: JSON string
"_currency_" - UDC balance of an account cannot be lowered under this value via transfer tx. This lock value may be higher than the UDC balance of an account at the time of processing lock tx
- key:
Although the internal state DB is composed of top-level data items and several logical state stores, its actual form is a linear key-value database. In viewpoint of state management, it suffices to manage this database in any form as long as the contents are equivalent. However, in order to interact with the underlying Tendermint consensus engine, we need to calculate app hash from the state DB contents. Every blockchain node must be able to calculate the same app hash from the equivalent state DB contents. To calculate this app hash, we assume that the database is stored as a Merkle tree.
Every data item in the database is stored as a leaf node in a Merkle tree with
the key as the concatenation of the prefix and in-store key. The leaf nodes are
sorted by the key and they are labeled with a hash derived from hash(key + value). A pair of leaf nodes generates a one-level higher inner node labeled
with hash(ln1_hash + ln2_hash). In the similar way, another one-level higher
inner node is added to the merkle tree with the label of hash(in1_hash + in2_hash). The above process is repeated until only one single root node
appears at the top of the merkle tree. The resulting app hash is the hash label
of the root node.
TM: This app hash is calculated every time a new block is committed and stored as app hash in the next block. App hash is to provide an evidence that every blockchain node hash the same state DB contents for a given block height.
This section describes how the AMO blockchain state is changed when a transaction is included in a block or a block is completed. There shall be no other state change than described in this section.
In following subsections, blk.incentive is accumulated from the beginning of
a block until the end of a block. When completing a block, this incentive is
distributed among the validator who produced a block and the users who
delegated stakes to the validator.
TM: These operations are implemented by DeliverTx and EndBlock method
in the ABCI application.
Upon receiving a transfer transaction from an account with an asset type of
AMO coin or UDC coin, an AMO blockchain node performs a validity check and
transfers coins from sender's balance to recipient's balance when the
transaction is valid.
NOTE: When the asset type is parcel, see
Transferring data ownership.
- validity check
tx.amount>0sender.balance≥tx.amount+tx.fee
- state change
sender.balance←sender.balance-tx.amount-tx.feeto.balance←to.balance+tx.amountblk.incentive←blk.incentive+tx.fee
When optional parameter udc is given, the operation is changed as follows.
- validity check
- UDC id
<udc>is registered tx.amount>0<udc>.sender.balance≥<udc>.sender.lock+tx.amountsender.balance≥tx.fee
- UDC id
- state change
<udc>.sender.balance←<udc>.sender.balance-tx.amount<udc>.to.balance←<udc>.to.balance+tx.amountsender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
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 sender's balance when the transaction is valid.
- validity check
tx.amount>0tx.amount % config.minimum_staking_unit==0(check staking unit restriction)sender.balance≥tx.amount+tx.fee- There is no other account
holderhavingholder.stake.validator==tx.validator sender.stake.validator==tx.validatorifsender.stakeexists
- state change
sender.balance←sender.balance-tx.amount-tx.feesender.stake.amount←sender.stake.amount+tx.amountsender.stake.locked_height←config.lockup_periodblk.incentive←blk.incentive+tx.fee
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
tx.amount>0sender.balance≥tx.feesender.stake.unlocked==truesender.stake.amount≥tx.amountsender.stake.amount>tx.amountif this account is a delegatee for any of delegated stakessender.stake.delegateis empty
- state change
sender.stake.amount←sender.stake.amount-tx.amountsender.balance←sender.balance-tx.fee+tx.amountblk.incentive←blk.incentive+tx.fee
TODO: need rounding? or currency to stake ratio?
Stake Lock-up
This feature locks a newly added stake for a certain period of time. The time
is measured in terms of number of blocks. If a stake is set at the block height
h, the stake can be withdrawn after the block height reaches h + l, where
l is the pre-configured lock-up period.
Upon receiving a stake transaction from an account, an AMO blockchain node
records the stake in LockedStake with l. Then, the stake's l decreases by
1 block height per block creation. When l becomes 0, the stake gets removed
from LockedStake and put into UnlockedStake.
Block Progress(Creation) Condition
As tendermint's create_empty_blocks config is set to false on an AMO
blockchain node, the block is progressed only if there is a change of
appHash, the root hash value of State merkle tree. The conditions in which
the appHash can change are as follows:
- Successfully delivered(processed) transactions
- Stakes in
LockedStake
Even though there is no transaction to process on an AMO blockchain node, the
appHash can change. The lock-period l of locked stakes decrease by 1 block
height and it is written in State, resulting in the change of appHash.
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
tx.amount>0tx.amount%config.minimum_staking_unit==0(check staking unit restriction)sender.balance≥tx.fee+tx.amounttx.toaddress already has a positive stake instakestore- the
senderhas no previous delegatee ortx.tois the same as the previous delegatee
- state change
sender.balance←sender.balance-tx.fee-tx.amountblk.incentive←blk.incentive+tx.feesender.delegate.amount←sender.delegate.amount+tx.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
tx.amount>0sender.balance≥tx.feesender.delegate.amount≥tx.amount
- state change
sender.delegate.amount←sender.delegate.amount-tx.amountsender.balance←sender.balance-tx.fee+tx.amountblk.incentive←blk.incentive+tx.fee
NOTE: sender.delegate is a stake value in the delegate store where
the address is the sender account.
When it is necessary to modify the configuration of AMO blockchain without hard-forking the chain, one of the validators can propose a draft containing the configuration to get applied with its description and deposit. Then, the validators vote for or against it. For the draft to get processed further after the vote is closed, the draft must have a quorum for voting. If not, the votes for draft are ignored no matter what the final result of votes is. On the other hand, if quorum is met, the draft would get applied or not, according to its final result. Also, if turnout of voters is below refund rate, the draft deposit is distributed among the validators who participate in voting. Otherwise, it is returned to the proposer.
Upon receiving a propose transaction from an account, an AMO blockchain node
performs a validity check and add a record in draft store.
- validity check
- there is no other draft in progress
- there is no record having
tx.draft_idas a key indraftstore senderis one ofblk.validatorssender.balance≥config.draft_deposit+tx.feetx.draft_id==state.latest_draft_id+ 1
- state change
- add new record having
tx.draft_idas a key indraftstore sender.balance←sender.balance-config.draft_deposit-tx.feeblk.incentive←blk.incentive+tx.fee
- add new record having
Upon receiving a vote transaction from an account, an AMO blockchain node
performs a validity check and add a record in vote store.
- validity check
tx.draft_idis in progresssender!=draft.proposer- there is no record having
tx.draft_id+senderas a key invotestore senderis one ofblk.validatorssender.balance≥tx.fee
- state change
- add new record having
tx.draft_id+senderas a key invotestore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- add new record having
Upon close_count reaches zero and the vote gets closed, an AMO blockchain
node calculates and updates draft.tally_* values.
- state change
- for
validatorinvalidators:draft.tally_quorum←draft.tally_quorum+validator.effective_stake draft.tally_quorum←draft.tally_quorum*config.draft_quorum_ratedraft.tally_approve←draft.tally_approve+draft.proposer.effective_stake- for
voteindraft.votes:draft.tally_approve←draft.tally_approve+vote.voter.effective_stake, ifvote.approveistrue - for
voteindraft.votes:draft.tally_reject←draft.tally_reject+vote.voter.effective_stake, ifvote.approveisfalse
- for
In order to register a data parcel in AMO blockchain, there must be an already registered data storage in the blockchain.
Upon receiving a setup transaction from an account, an AMO blockchain node
performs a validity check and add or update an item in storage store.
- validity check
sender.balance≥tx.feeprev.owner==tx.senderifprevwithprev.id==tx.storageexists instoragestore
- state change
- add new record or update existing record having
tx.storageas a key instoragestore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- add new record or update existing record having
Upon receiving a close transaction from an account, an AMO blockchain node
performs a validity check and remove a record from store store.
- validity check
sender.balance≥tx.feeprevwithprev.id==tx.storageexists instoragestoreprev.owner==tx.sender
- state change
- remove record having
tx.storageas a key fromstoragestore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- remove record having
Upon receiving a register transaction from an account, an AMO blockchain node
performs a validity check and add a new record with its extra information in
parcel store.
- validity check
- extract storage ID
storagefromtx.target storageshould exist in storage store andstorage.activeshould be truetx.targetshould NOT exist inparcelstoresender.balance≥storage.registration_fee+tx.fee
- extract storage ID
- state change
- add new record having
tx.targetas a key inparcelstore sender.balance←sender.balance-storage.registration_fee-tx.feestorage.owner.balance←storage.owner.balance+storage.registration_feeblk.incentive←blk.incentive+tx.fee
- add new record having
Upon receiving a discard transaction from an account, an AMO blockchain node
performs a validity check and remove record in parcel store.
- validity check
tx.targetshould exist inparcelstoresender.balance≥tx.feesender==tx.target.ownerorsender==tx.target.proxy_account
- state change
- remove record having
tx.targetas a key inparcelstore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- remove record having
NOTE: proxy_account refers to an account which has a owner-equivalent
permission to control over tx.target record.
Upon receiving a request transaction from an account, an AMO blockchain node
performs a validity check and add a new record with its extra information in
request store.
- validity check
tx.targetshould exist inparcelstore- if
tx.recipientis valid, then
form request IDrequestfromtx.recipient+tx.target - else
form request IDrequestfromsender+tx.target requestshould NOT exist inrequeststorerequestshould NOT exist inusagestoretx.recipient≠tx.target.owner- if
tx.dealerandtx.dealer_feeis valid, then
sender.balance≥tx.fee+tx.payment+tx.dealer_fee - else
sender.balance≥tx.fee+tx.payment
- state change
- if
tx.recipientis valid, then
add new record havingtx.recipient+tx.targetas a key inrequeststore whererequest.agencyissender - else
add new record havingsender+tx.targetas a key inrequeststore whererequest.agencyis left empty - if
tx.dealerandtx.dealer_feeis valid, then
sender.balance←sender.balance-tx.fee-tx.payment-tx.dealer_fee - else
sender.balance←sender.balance-tx.fee-tx.payment blk.incentive←blk.incentive+tx.fee
- if
Upon receiving a cancel transaction from an account, an AMO blockchain node
performs a validity check and remove record in request store.
- validity check
tx.targetshould exist inparcelstore- if
tx.recipientis valid, then
form request IDrequestfromtx.recipient+tx.target - else
form request IDrequestfromsender+tx.target requestshould exist inrequeststore- if
tx.recipientis valid, then
sender==request.agency sender.balance≥tx.fee
- state change
- if
tx.recipientis valid, then
delete record having id astx.recipient+tx.targetinrequeststore - else
delete record having id assender+tx.targetinrequeststore - if
tx.dealerandtx.dealer_feeis valid, then
sender.balance←sender.balance-tx.fee+request.payment+request.dealer_fee - else
sender.balance←sender.balance-tx.fee+tx.target.payment blk.incentive←blk.incentive+tx.fee
- if
NOTE: payment refers to the amount of coins sender is willing to pay
for tx.target to tx.target.owner.
Upon receiving a grant transaction from an account, an AMO blockchain node
performs a validity check and add a new record with its extra information in
usage store.
- validity check
tx.targetshould exist inparcelstoretx.targetshould exist inrequeststoretx.targetshould NOT exist inusagestoresender==tx.target.ownerorsender==tx.target.proxy_account- extract storage ID
storagefromtx.target storageshould exist in storage store andstorage.activeshould be true- find
requesthaving id astx.recipient+tx.targetinrequeststore - if
sender==tx.target.owner, then
sender.balance+request.payment≥storage.hosting_fee+tx.fee - if
sender≠tx.target.owner, then
sender.balance≥tx.feeand
tx.target.owner.balance+request.payment≥storage.hosting_fee
- state change
- delete record having id as
tx.recipient+tx.targetinrequeststore - add new record having id as
tx.recipient+tx.targetinusagestore tx.target.owner.balance←tx.target.owner.balance+tx.target.payment-storage.hosting_feestorage.owner.balance←storage.owner.balance+storage.hosting_feerequest.dealer.balance←request.dealer.balance+request.dealer_feesender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- delete record having id as
Upon receiving a revoke transaction from an account, an AMO blockchain node
performs a validity check and remove record in usage store.
- validity check
tx.targetshould exist inparcelstoretx.targetshould exist inusagestoresender==tx.target.ownerorsender==tx.target.proxy_accountsender.balance≥tx.fee
- state change
- delete record having id as
tx.recipient+tx.targetinusagestore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- delete record having id as
Upon receiving a transfer transaction from an account with an asset type of parcel, an AMO blockchain node performs a validity check and transfers the ownership of the parcel to the recipient when the transaction is valid.
NOTE: When the asset type is AMO coin or UDC coin, see Transferring coin.
- validity check
tx.parcel.owner==tx.sendersender.balance≥tx.fee
- state change
tx.parcel.owner←tx.tosender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
NOTE: claim and dismiss tx in protocol v5 are deprecated.
Upon receiving a did.claim transaction from an account, an AMO blockchain
node performs a validity check and add a new record or update an existing
record in did store.
- validity check
- if
tx.targetalready exists indidstore asdoc, perform permission and validity check:tx.targetmust be the same asdoc.id.- concatenation of
did:amo:andtx.sendermust be eitherdoc.idordoc.controller. tx.documentanddocmust have the sameid,verificationMethodandauthenticationproperties. (assertionMethodmay change.)
- if
tx.targetdoes not exist indidstore, perform a validity check:tx.targetmust be the same astx.document.id.- concatenation of
did:amo:andtx.sendermust betx.document.id. tx.document.verificationMethod[0].idmust betx.document.id+#keys-1.tx.document.verificationMethod[0]must be of aJsonWebKey2020format, and a derived address fromtx.document.verificationMethod[0]must betx.document.id.
sender.balance≥tx.fee
- if
- state change
- add new record or replace the record with key
tx.target sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- add new record or replace the record with key
Upon receiving a did.dismiss transaction from an account, an AMO blockchain
node performs a validity check and remove record from did store.
- validity check
tx.targetshould exist indidstore asdoc.- concatenation of
did:amo:andtx.sendermust be eitherdoc.idordoc.controller. sender.balance≥tx.fee
- state change
- remove record with key
tx.targetfromdidstore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- remove record with key
NOTE: In the context of verifiable credential, an issuer is supposed to create, in other word, issue a verifiable credential.
Upon receiving a did.issue transaction from an account, an AMO blockchain
node performs a validity check and add a new record or update an existing
record in vc store.
- validity check
- if
tx.targetalready exists invcstore ascred, perform permission and validity check:tx.targetmust be the same ascred.id.- concatenation of
did:amo:andtx.sendermust becred.issuer. tx.credentialandcredmust have the sameidandissuerproperties.tx.credential.issuedmust be more recent thancred.issued.
- if
tx.targetdoes not exist invcstore, perform a validity check:tx.targetmust be the same astx.credential.id.- concatenation of
did:amo:andtx.sendermust betx.credential.issuer.
sender.balance≥tx.fee
- if
- state change
- add new record or replace the record with the key
tx.garget. sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- add new record or replace the record with the key
Upon receiving a did.revoke transaction from an account, an AMO blockchain node performs a validity check and remove a record from vc store.
- validity check
tx.targetshould exist invcstore as `cred.- concatenation of
did:amo:andtx.sendermust becred.issuer. sender.balance≥tx.fee
- state change
- remove record with key
tx.targetfromvcstore sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- remove record with key
In order to transfer user-defined coin balance in AMO blockchain, there must be an user-defined coin registry in the blockchain.
Upon receiving an issue transaction from an account, an AMO blockchain node
performs a validity check and add a new record in udc store.
- validity check
- if
udcexists havingtx.udcas a key in udc store, thensender==udc.ownerorsendershould be one ofudc.operators
- else
senderis one ofblk.validators
sender.balance≥tx.fee
- if
- state change
- if
udcexists in udc store, thenudc.operators←tx.operatorsudc.desc←tx.descudc.total←udc.total+tx.amount
- else add a new record
udcwith the followingudc.owner←senderudc.operators←tx.operatorsudc.desc←tx.descudc.total←tx.amount
<udc>.sender.balance←<udc>.sender.balance+tx.amountsender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- if
Upon receiving a lock transaction from an account, an AMO blockchain node
performs a validity check and add or update a record in udc balance lock store.
- validity check
udcexists havingtx.udcas a key in udc storesender==udc.ownerorsendershould be one ofudc.operatorstx.amount> 0sender.balance≥tx.fee
- state change
- if
tx.amount> 0, then
add new<udc>.holder.lockor update existing<udc>.holder.lockin udc balance lock store - else
delete existing<udc>.holder.lockfrom udc balance lock store sender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
- if
Upon receiving a burn transaction from an account, an AMO blockchain node
performs a validity check and reduce sender's designated UDC balance.
- validity check
udcexists havingtx.udcas a key in udc storetx.amount> 0<udc>.sender.balance≥<udc>.sender.lock+tx.amountsender.balance≥tx.fee
- state change
<udc>.sender.balance←<udc>.sender.balance-tx.amountsender.balance←sender.balance-tx.feeblk.incentive←blk.incentive+tx.fee
After processing state changes triggered by users' transactions in
DeliverTx(), the nodes complete a block in EndBlock() by applying
additional state changes described in this section.
tx.fee is collected while transactions are processed and it gets included in
blk.incentive. Then, blk.incentive is distributed among the stakers and the
delegators at the end of block creation. The process is explained in incentive
distribution section, in more detail.
A lazy validator is a validator who is in the validator set until a block, but
did not vote in the consensus process for the block. We say the validator
missed the block. If a validator missed consecutive blks_lazy blocks and
blks_lazy >= hibernate_threshold, it is removed from the validator set for
the next hibernate_period blocks. New record is created in hibernate store
with start to be the current height, and end current height plug
hibernate_period. We say the validator enters hibernation state. If a
validator missed consecutive blks_lazy blocks and blks_lazy < hibernate_threshold but wakes up and comes back to the consensus process,
blks_lazy resets to zero.
If a validator stayed hibernation state for blks_hib blocks and blks_hib >= hibernate_period, i.e. hibernate.address.end = current_height, then
hibernate.address is removed and the validator leaves the hibernation state
and may be included in the validator set again.
TM validator set may change along with the progress of the chain. There are two reasons for the change:
- Validator hibernation state change
- Stake or effective stake change
In order to select new set of validators, first top n_val accounts with
highest effective stakes are selected excluding accounts associated to the
validators in hibernation state. Extract validator public keys for the
accounts, and inform the list of validator public keys to Tendermint layer.
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 delegatee 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.
TM: According to the official documentation of tendermint and several experimental results, to maintain a blockchain network, it is mandatory for over 2/3 validator(MUST-ONLINE) nodes to be online. Also, the voting power of a validator node matters to the ratio of MUST-ONLINE nodes. That is, stopping validator nodes of which the sum of voting power is over 1/3 breaks the consensus algorithm of tendermint and results in the interruption of generating blocks on the chain.
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:
- For each validator
Val_i, set voting powervp_ito bestakeofVal_i. - Calculate
TotalVotingPower, which is the sum ofvp_is of all validators in the new validator set. adjFactor← 0 (use this as a persistent factor)- While
TotalVotingPower>MaxTotalVotingPoweradjFactor←adjFactor+ 1TotalVotingPower←TotalVotingPower/ 2
(implemented as right-shift)- For each validator
Val_i,vp_i←vp_i/ 2
(implemented as right-shift)
NOTE: When vp_i reaches to zero, then Val_i shall be removed from the
new validator set.
At the beginning of block creation BeginBlock(), AMO ABCI app receives a list
of convicts from tendermint. The convicts get penalized in EndBlock() for its
malicious attempts to harm the blockchain network. The detailed penalization
process is explained in penalty section.
TM: Tendermint provides a block information, in BeginBlock() method which
is called at the beginning of a block creation, including a block proposer
address. This address is derived from the validator pubkey who proposes the
block. In AMO ABCI app, we can look up the original stake holder in the stake
store having the same validator pubkey.
Incentive refers to the sum of a block reward and transaction fees. The fees of
transactions which are successfully verified(delivered) by the block proposer
are accumulated and then transferred to the stake holder at the end of a block
creation in EndBlock().
A stake holder who proposes a block receives an incentive. This is the only
step in which there is a state change in balance store without involving any
transaction:
R ← b_reward + n_delivered_txs * tx_reward
I ← R + acc_fee
where R is the final block reward, b_reward a block reward rate,
n_delivered_txs the number of delivered transactions in the block,
tx_reward a transaction reward rate, I the final incentive and acc_fee
the accumulated fee.
When the incentive is I, this incentive shall be distributed among the stake
holder and the delegated stake holders. The distribution mechanism is as the
following:
wStakes←w_val*stake_0(stake of the proposer)- For each delegated stake
stake_i,wStakes←wStakes+w_ds*stake_i - Calculate the incentive for the proposer
I_0←I*w_val*stake_0/wStakes. - For each delegated stake holder, calculate the incentive for
i-th delegated stake,I_i←I*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.
To maintain the DPoS blockchain as healthy as possible, it is essential to encourage block validators to participate in creating and verifying blocks with incentive, but also to impose responsibilities on their misbehavior with penalty. The penalty shall be distributed among the stake holder and the delegated stake holders according to the distribution mechanism presented in Incentive Distribution.
The types of abnormal behavior and parameters are defined as follows:
- Convict
- Malicious Validator:
PenaltyRatioM - Lazy Validator:
PenaltyRatioL
- Malicious Validator:
TM: The evidence of validators' misbehavior is provided by Tendermint in
BeginBlock() method which is called at the beginning of a block creation.
Tendermint supports currently only a single type of evidence, the
DuplicateVoteEvidence.
The relevant validators pay the price for misbehavior by burning the specific amount of coins staked and delegated to them, immediately at the moment when their misbehavior is caught. The penalty shall be distributed amount the stake holder and the delegated stake holders according to the distribution mechanism presented in Incentive Distribution.
PenaltyRatioM
If a validator missed n_blks blocks within last laziness_window
blocks and n_blks >= laziness_threshold, then this validator gets penalized
accordingly. The penalty is calculated by penalty_ratio_l * eff_stake.
To enhance the stability of AMO's overall system, it is required to upgrade its application protocol consistently. To apply a new protocol on alive blockchain, 'hard-fork' is an inevitable process necessary to be done externally. AMO provides a feature which helps 'hard-fork' get processed more smoothly.
AMO ABCI app's protocol version is recorded as ProtocolVersion in app's
state. ProtocolVersion gets initiated with the value of genesis.json. If
not specified, it is set with current app's hard-coded protocol version.
The specific time when a new protocol gets applied and its version is decided
among validators through proposing and voting draft.
The time is recorded as UpgradeProtocolHeight and the version as
UpgradeProtocolVersion in app's config.
At the beginning of block creation BeginBlock(), AMO ABCI app checks
conditions and processes operations as follows:
- if
blk.height==app.config.UpgradeProtocolHeightapp.state.ProtocolVersion←app.config.UpgradeProtocolVersion
- if
sw.ProtocolVersion!=app.state.ProtocolVersion- abort and exit current sw
- if
blk.height==app.config.UpgradeProtocolHeight- execute
app.MigrateToX()(Xrefers tosw.ProtocolVersion)
- execute
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:
amod tendermint unsafe_reset_allAn AMO-compliant blockchain node should have some mechanisms to modify internal database for this operation.
In order to prevent replay
attack (in some sense,
double-spending), every AMO transaction is checked for whether it is already
introduced or processed in previous blocks. Basic idea is that when a
blockchain node sees a transaction that is already presented in the blockchain
network, 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 to the transaction,
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 use ReplayPreventer, a module which
monitors every incoming transaction to prevent its replay attacks by checking
its existence in the blockchain network.
If a user wants to send the same amount of coin to the same recipient again,
then the user must put into the transaction a signature different from the one
used for the previous transaction. If so, the transaction would have a
different tx hash and be treated as a different one, passing the transaction
check process of ReplayPreventer successfully.