diff --git a/docs/README.md b/docs/README.md index 2b878e792..7a56f2c16 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,6 +33,14 @@ To be able to confirm checkpoints we need to know how deeply embedded they are i ![BTC Light Client](diagrams/btc_light_client.png) +## Database Schema + +Even though we use a Key-Value store instead of a Relational Database, the following Entity Relationship Diagram is useful to get a sense of the conceptual data model, including the cardinalities. The grouping shows which module the collections belong to in the design. + +Note that some boxes are actually _messages_ and aren't part of the storage schema, they are just there to illustrate where some of the entities are coming from, or to establish a relationship between entities that come to life as a result of a common message, but then live separately without explicit foreign keys between them. + +![Database Schema](diagrams/database_schema.png) + ## Automation Adding the following to `.git/hooks/pre-commit` automatically renders and checks in the images when we commit changes to the diagrams. CI should also check that there are no uncommitted changes. diff --git a/docs/diagrams/database_schema.png b/docs/diagrams/database_schema.png new file mode 100644 index 000000000..992c3d6a3 Binary files /dev/null and b/docs/diagrams/database_schema.png differ diff --git a/docs/diagrams/database_schema.puml b/docs/diagrams/database_schema.puml new file mode 100644 index 000000000..43ef181db --- /dev/null +++ b/docs/diagrams/database_schema.puml @@ -0,0 +1,237 @@ + +@startuml Database Schema + +entity "Tendermint Block" as block { + * hash + -- + * height + * last_commit + ... +} + +package "staking" #79ADDC { + enum "Message" as staking_msg { + <> + -- + * oneof + | CreateValidator + | EditValidator + | Delegate + | BeginRedelegate + | Undelegate + } + + entity "Validator" as validator { + * public_key: Ed25519 public key + -- + * power: delegated staking power + } + + entity "Unbonding Queue" as unbonding_queue { + * public_key <>: validator key + -- + * height: set to epoch end block height + * time: left at default 21 days + * tokens: amount to refund + } +} + +package "epoching" #FFC09F { + entity "Epoch" as epoch { + * epoch_number + -- + * start_block_height <> + * epoch_length + } + entity "Queued Message" as queued_msg { + * tx_id <>: hash of tx bytes + * msg_id: hash of msg bytes + -- + * msg : wrapped staking msg + } + entity "Delayed Result" as delayed_result { + * tx_id <>: hash of original tx + * msg_id: hash of message + -- + * outcome: success or failure + * events: emitted by staking execution + * logs: emitted by staking execution + } + note bottom + Returned to and stored by Tendermint. + end note + + enum MsgWrappedStaking { + <> + -- + * wrapped staking msg\n except MsgCreateValidator + } + + entity "Validator Snapshot" as validator_snapshot { + * epoch_number <> + * address <> + -- + * power + } + note left + At beginning of epoch + end note + + entity "Slashed Validators" as validator_slashed { + * epoch_number <> + * address <> + } +} + + +package "checkpointing" #FFEE93 { + entity "Raw Checkpoint" as raw_ckpt { + * epoch_number <> + -- + * last_commit_hash: Quorum Certificate + * aggr_bls_sig: aggregated BLS signature + * aggr_bls_bitmap: which validators signed + } + note top + Goes in the checkpoint in serialized form. + end note + + entity "Checkpoint Status" as ckpt_status { + * epoch_number <> + -- + * status: + | ACCUMULATING: await sigs + | SIGNED: has +1/3 sigs, await submit + | SUBMITTED: included on BTC + | CONFIRMED: k-deep on BTC + | FINALIZED: w-deep on BTC + * aggr_power: sum of accumulated validator power + } + + entity "BLS Key" as bls_key { + * bls_public_key: validator BLS public key + -- + * public_key <>: validator Ed25519 public key + } + + entity "BLS Signature" as bls_sig { + * bls_public_key <> + * last_commit_hash + -- + * bls_sig + } + + enum MsgWrappedCreateValidator { + <> + -- + * BLS key + * PoP: Proof-of-Possession + * wrapped staking msg: \n MsgCreateValidator + } +} + +package "btccheckpoint" #FCF5C7 { + entity "Registered Submitter" as ckpt_submitter { + * btc_public_key + -- + * public_key <>: User account to reward + } + + entity "Checkpoint Submission" as ckpt_submission { + * submission ID <> + -- + * ckpt_hash <> <>: from OP_RETURN + * btc_public_key <> <>: submitter from inclusions + -- + * prev_epoch_ckpt_submission <>: + at least one submission for previous epoch in an + ancestor Bitcoin block has to exist + } + + entity "Checkpoint Inclusion Proof" as ckpt_inclusion { + * btc_block_hash <> + * tx_index: position of transaction in BTC block + -- + * btc_transaction: raw BTC transaction with OP_RETURN + * proof: that this transaction is part of the merkle_root + } +} + +package "btclightclient" #ADF7B6 { + entity "Bitcoin Header" as btc_header { + * hash: block hash <> + -- + * version + * parent_hash <> + * merkle_root + * timestamp + * difficulty_target + * nonce + -- + * height <> + * total_pow <> + } + + entity "Trusted Header" as btc_trusted { + <> + -- + * raw_header: bytes of a deeply embedded block + * height: from block explorer + -- + * hash: Block Hash <> + } + note bottom + total_pow will be considered to start from + this block, as if it was genesis. + end note + + entity "Tip" as btc_tip { + <> + -- + * hash <>: block hash + } +} + +block }o--|{ validator : current validator set +block }o--|{ validator : next validator set +block }o--|| validator : proposer + +epoch ||..o{ queued_msg : delay to end of epoch +epoch ||--|| block : start height + +validator_snapshot }|--|| epoch +validator_snapshot }|--|| validator + +validator_slashed }o--|| epoch +validator_slashed }o--|| validator + +queued_msg .> staking_msg +queued_msg ||--o| delayed_result : inform user \nvia events + +unbonding_queue }o--|| block : unbonding height +unbonding_queue }o--|| validator : unbonding validator + +raw_ckpt |o--|| epoch : checkpointed epoch +raw_ckpt |o..|| block : commit hash from +raw_ckpt ||--|| ckpt_status : current status +raw_ckpt ||--o{ bls_sig : submitted sigs + +bls_key ||--o{ bls_sig : signed with +bls_key ||--|| validator : registered by + +ckpt_submission }o--|| raw_ckpt : submitted ckpt +ckpt_submission }o--|| ckpt_submitter : to reward +ckpt_submission }o--|{ ckpt_submission : previous epoch\n submission + +ckpt_inclusion }|--|| ckpt_submission : multiple UTxO needed +ckpt_inclusion }o--|| btc_header : included in + +btc_header }o--o| btc_header : parent +btc_trusted |o--|| btc_header : start from +btc_tip |o--|| btc_header: longest chain + +MsgWrappedCreateValidator .> bls_key : creates +MsgWrappedCreateValidator .> queued_msg : creates +MsgWrappedStaking .> queued_msg : creates + +@enduml diff --git a/docs/diagrams/submit_checkpoint.png b/docs/diagrams/submit_checkpoint.png index 977dcd3c3..8cf7d8969 100644 Binary files a/docs/diagrams/submit_checkpoint.png and b/docs/diagrams/submit_checkpoint.png differ diff --git a/docs/diagrams/submit_checkpoint.puml b/docs/diagrams/submit_checkpoint.puml index 78f4ff67f..fadc9b02c 100644 --- a/docs/diagrams/submit_checkpoint.puml +++ b/docs/diagrams/submit_checkpoint.puml @@ -108,8 +108,8 @@ alt raw checkpoint exists alt previous epoch BTC checkpoint found btccheckpoint -> btccheckpoint_db : Add BTC checkpoint transaction btccheckpoint -> checkpointing ++ : Callback on checkpoint registered - checkpointing -> checkpointing : Change checkpoint status to \n CHECKPOINTED_NOT_CONFIRMED - checkpointing -> Events -- : Emit epoch checkpoint included + checkpointing -> checkpointing : Change checkpoint status to \n SUBMITTED + checkpointing -> Events -- : Emit epoch checkpoint SUBMITTED Events --> submitter : Observe submitted checkpoints else out-of-sequence checkpoint btccheckpoint -> bank : Penalty for out-of-sequence submission @@ -159,26 +159,42 @@ alt if tip changed btccheckpoint -> btccheckpoint_db : Get unstable checkpoints btccheckpoint -> btccheckpoint : Sort unstable checkpoints \n by BTC height and tx index - loop foreach unstable checkpoint tx + loop foreach SUBMITTED checkpoint tx btccheckpoint -> btclightclient ++: Check including block embedding depth return block depth if on main chain - alt if checkpoint tx became stable - btccheckpoint -> checkpointing ++ : Callback on checkpoint stable + alt if checkpoint tx became k-deep + btccheckpoint -> checkpointing ++ : Callback on checkpoint CONFIRMED checkpointing -> checkpointing : Change checkpoint status to \n CONFIRMED - checkpointing -> Events : Emit epoch checkpoint stable + checkpointing -> Events : Emit epoch checkpoint CONFIRMED return true if just became CONFIRMED alt checkpoint/epoch just became CONFIRMED btccheckpoint -> btccheckpoint_db : Get BTC-to-Cosmos key of tx submitter btccheckpoint -> bank : Mint reward for submitter + end + end + end + + loop foreach CONFIRMED checkpoint tx + btccheckpoint -> btclightclient ++: Check including block embedding depth + return block depth if on main chain + + alt if checkpoint tx became w-deep + btccheckpoint -> checkpointing ++ : Callback on checkpoint FINALIZED + checkpointing -> checkpointing : Change checkpoint status to \n FINALIZED + checkpointing -> Events : Emit epoch checkpoint FINALIZED + return true if just became FINALIZED + + alt checkpoint/epoch just became FINALIZED btccheckpoint -> staking : Release unbonding tokens for epoch end end end - alt if checkpoint status was CHECKPOINTED_NOT_CONFIRMED\n but currently no checkpoint tx was on main chain - checkpointing -> checkpointing : Change checkpoint status to \n UNCONFIRMED - checkpointing -> Events : Emit epoch checkpoint UNCONFIRMED + + alt if a checkpoint status was SUBMITTED\n but currently no checkpoint tx was on main chain + checkpointing -> checkpointing : Change checkpoint status to \n SIGNED + checkpointing -> Events : Emit epoch checkpoint SIGNED end deactivate btccheckpoint end