diff --git a/Cargo.lock b/Cargo.lock index bd584c53..0ded6306 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -776,7 +776,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure", ] @@ -788,7 +788,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -933,7 +933,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -950,7 +950,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1161,7 +1161,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6cbbb8f56245b5a479b30a62cdc86d26e2f35c2b9f594bc4671654b03851380" dependencies = [ "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1259,7 +1259,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1413,9 +1413,9 @@ dependencies = [ [[package]] name = "caryatid_process" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490593ed41db447fff9c0bed61e8e792894ebe8d3d00908ac59bccbf7ceaf3bd" +checksum = "295eb5e868e9f1a9d2eebfd1d883b5cb7d9f857d56a3b2542f33f15dfd44563e" dependencies = [ "anyhow", "async-trait", @@ -1463,9 +1463,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.41" +version = "1.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +checksum = "739eb0f94557554b3ca9a86d2d37bebd49c5e6d0c1d2bda35ba5bdac830befc2" dependencies = [ "find-msvc-tools", "jobserver", @@ -1576,7 +1576,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1897,7 +1897,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1931,7 +1931,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1945,7 +1945,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1956,7 +1956,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -1967,7 +1967,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2025,14 +2025,14 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", "serde_core", @@ -2075,7 +2075,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2089,9 +2089,9 @@ dependencies = [ [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "double-ended-peekable" @@ -2164,7 +2164,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -2321,9 +2321,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -2487,7 +2487,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -3478,7 +3478,7 @@ checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -3489,7 +3489,7 @@ checksum = "a9882ef5c56df184b8ffc107fc6c61e33ee3a654b021961d790a78571bb9d67a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -3838,7 +3838,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4555,7 +4555,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4595,7 +4595,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4740,7 +4740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -4754,9 +4754,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4787,7 +4787,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.107", + "syn 2.0.108", "tempfile", ] @@ -4801,7 +4801,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5068,7 +5068,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5403,9 +5403,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -5608,7 +5608,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5710,7 +5710,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5722,7 +5722,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5932,7 +5932,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -5954,9 +5954,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.107" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -5986,7 +5986,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6099,7 +6099,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6110,7 +6110,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6252,7 +6252,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6475,7 +6475,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6574,7 +6574,7 @@ checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6777,7 +6777,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "wasm-bindgen-shared", ] @@ -6812,7 +6812,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6929,7 +6929,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -6940,7 +6940,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -7339,7 +7339,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure", ] @@ -7360,7 +7360,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -7380,7 +7380,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", "synstructure", ] @@ -7401,7 +7401,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] @@ -7434,7 +7434,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.107", + "syn 2.0.108", ] [[package]] diff --git a/codec/src/map_parameters.rs b/codec/src/map_parameters.rs index 4de54aff..208ab36b 100644 --- a/codec/src/map_parameters.rs +++ b/codec/src/map_parameters.rs @@ -674,7 +674,7 @@ fn map_governance_action(action: &conway::GovAction) -> Result conway::GovAction::HardForkInitiation(id, version) => Ok( GovernanceAction::HardForkInitiation(HardForkInitiationAction { previous_action_id: map_nullable_gov_action_id(id)?, - protocol_version: *version, + protocol_version: protocol_params::ProtocolVersion::new(version.0, version.1), }), ), diff --git a/common/src/protocol_params.rs b/common/src/protocol_params.rs index a1dc56a5..8839036e 100644 --- a/common/src/protocol_params.rs +++ b/common/src/protocol_params.rs @@ -5,7 +5,7 @@ use crate::{ ExUnitPrices, ExUnits, GenesisDelegate, HeavyDelegate, NetworkId, PoolVotingThresholds, ProtocolConsts, }; -use anyhow::Result; +use anyhow::{bail, Result}; use blake2::{digest::consts::U32, Blake2b, Digest}; use chrono::{DateTime, Utc}; use serde_with::{hex::Hex, serde_as}; @@ -227,11 +227,27 @@ pub struct ConwayParams { pub committee: Committee, } -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct ProtocolVersion { - pub minor: u64, pub major: u64, + pub minor: u64, +} + +impl ProtocolVersion { + pub fn new(major: u64, minor: u64) -> Self { + Self { major, minor } + } + + pub fn is_chang(&self) -> Result { + if self.major == 9 { + if self.minor != 0 { + bail!("Chang version 9.xx with nonzero xx is not supported") + } + return Ok(true); + } + Ok(false) + } } #[derive( @@ -370,3 +386,43 @@ impl Nonces { slot + window < next_epoch_first_slot } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_protocol_version_order() { + let v9_0 = ProtocolVersion::new(9, 0); + let v9_1 = ProtocolVersion::new(9, 1); + let v9_10 = ProtocolVersion::new(9, 10); + let v10_0 = ProtocolVersion::new(10, 0); + let v10_9 = ProtocolVersion::new(10, 9); + let v10_10 = ProtocolVersion::new(10, 10); + let v10_11 = ProtocolVersion::new(10, 11); + + assert!(v10_9 > v9_10); + + let from = vec![v9_0, v9_1, v9_10, v10_0, v10_9, v10_10, v10_11]; + let mut upd = from.clone(); + upd.sort(); + + assert_eq!(from, upd); + } + + #[test] + fn test_protocol_version_parsing() -> Result<()> { + let v9_0 = serde_json::from_slice::(b"{\"minor\": 0, \"major\": 9}")?; + let v9_0a = serde_json::from_slice::(b"{\"major\": 9, \"minor\": 0}")?; + let v0_9 = serde_json::from_slice::(b"{\"minor\": 9, \"major\": 0}")?; + let v0_9a = serde_json::from_slice::(b"{\"major\": 0, \"minor\": 9}")?; + + assert_eq!(v9_0, v9_0a); + assert_eq!(v0_9, v0_9a); + assert_eq!(v9_0, ProtocolVersion::new(9, 0)); + assert_eq!(v9_0.major, 9); + assert_eq!(v0_9, ProtocolVersion::new(0, 9)); + + Ok(()) + } +} diff --git a/common/src/types.rs b/common/src/types.rs index fc9985ea..65ee5545 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -1590,7 +1590,7 @@ pub struct ParameterChangeAction { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct HardForkInitiationAction { pub previous_action_id: Option, - pub protocol_version: (u64, u64), + pub protocol_version: protocol_params::ProtocolVersion, } #[serde_as] @@ -1633,6 +1633,32 @@ pub enum GovernanceAction { Information, } +impl GovernanceAction { + pub fn get_previous_action_id(&self) -> Option { + match &self { + Self::ParameterChange(ParameterChangeAction { + previous_action_id: prev, + .. + }) => prev.clone(), + Self::HardForkInitiation(HardForkInitiationAction { + previous_action_id: prev, + .. + }) => prev.clone(), + Self::TreasuryWithdrawals(_) => None, + Self::NoConfidence(prev) => prev.clone(), + Self::UpdateCommittee(UpdateCommitteeAction { + previous_action_id: prev, + .. + }) => prev.clone(), + Self::NewConstitution(NewConstitutionAction { + previous_action_id: prev, + .. + }) => prev.clone(), + Self::Information => None, + } + } +} + #[derive( serde::Serialize, serde::Deserialize, Debug, PartialEq, PartialOrd, Eq, Ord, Clone, Hash, )] @@ -1710,6 +1736,14 @@ impl VotesCount { } } + pub fn infinity() -> Self { + Self { + committee: u64::MAX, + drep: u64::MAX, + pool: u64::MAX, + } + } + pub fn majorizes(&self, v: &VotesCount) -> bool { self.committee >= v.committee && self.drep >= v.drep && self.pool >= v.pool } @@ -1751,6 +1785,7 @@ pub enum EnactStateElem { Params(Box), Constitution(Constitution), Committee(CommitteeChange), + ProtVer(protocol_params::ProtocolVersion), NoConfidence, } diff --git a/modules/governance_state/NOTES.md b/modules/governance_state/NOTES.md index c8b2ac2c..9751ff5f 100644 --- a/modules/governance_state/NOTES.md +++ b/modules/governance_state/NOTES.md @@ -16,18 +16,46 @@ This is ok, mainnet Cardano scanners detect this and show latest vote. Other names: 'Gov Action Validity', 'govActionLifetime', updated in governance_action_validity_period parameter (measured in epochs). That is, if proposal is published in epoch E, then voting is finished at -the end of epoch E+governance_action_validity_period. +the end of epoch E+governance_action_validity_period+1. + +In another words, each action is granted 'govActionLifetime' full +epochs for voting (P is proposal, E is expiration, govActionLifetime is 6): + +'---P--|-----|-----|-----|-----|-----|-----|E' + +For +1 origin see e.g. `cardano-node/cardano-testnet/src/Testnet/Components/Query.hs`: + +``` +-- | Obtains the @govActionLifetime@ from the protocol parameters. +-- The @govActionLifetime@ or governance action maximum lifetime in epochs is +-- the number of epochs such that a governance action submitted during an epoch @e@ +-- expires if it is still not ratified as of the end of epoch: @e + govActionLifetime + 1@. +``` Default value (6) is taken from Conway Genesis, which is (in turn) taken from Cardano book: https://book.world.dev.cardano.org/environments/mainnet/conway-genesis.json +So, if voting starts at 529, gov_action_lifetime is 6, then the last epoch +when votes have meaning is 535. At the 535/536 transition all votes expire. + ### Ratification process. Ratification checked at epoch boundary. -If ratified, deposits returned immediately, actions take place at E+1/E+2 -boundary. +If ratified at E/E+1 transition, actions take place at E+1/E+2 boundary. +Rewards are paid at E+1/E+2 transition (?) Deposits transferred to reward account. +### Bootstrap period (Chang sub-era of Conway) + +Conway era is split into two parts: Chang (9.0 protocol version) and +Plomin (10.0 protocol version). The first ("Chang") epoch has limited +governance ("bootstrap governance"): + +* DReps may vote only for Info actions, they don't count for other actions. +* Only Info, ParameterChange and HardFork actions are allowed. + +https://docs.cardano.org/about-cardano/evolution/upgrades/chang + ### Genesis blocks * Conway genesis: committee key hashes have prefix 'scriptHash-' (I believe, 'keyHash-'), followed by hex hash. To be researched.... @@ -74,6 +102,7 @@ epoch. Special message? total. Need info about voting registration. * DRep::epoch -- it's written that it's epoch, which has ended. But I receive messages with this epoch in its beginning. Need to sort out. +* Implement bootstrap period (resolved). * What if voting length changes during voting process? E.g.: (a) epoch 100 voting starts, voting length is 10 epochs (b) epoch 107 voting length change to 3 enacts (e.g., because some earlier successful vote) diff --git a/modules/governance_state/src/ab_sancho_voting.json b/modules/governance_state/data/ab_sancho_voting.json similarity index 100% rename from modules/governance_state/src/ab_sancho_voting.json rename to modules/governance_state/data/ab_sancho_voting.json diff --git a/modules/governance_state/src/alonzo_babbage_voting.json b/modules/governance_state/data/alonzo_babbage_voting.json similarity index 100% rename from modules/governance_state/src/alonzo_babbage_voting.json rename to modules/governance_state/data/alonzo_babbage_voting.json diff --git a/modules/governance_state/data/check_conway_syncdb.py b/modules/governance_state/data/check_conway_syncdb.py new file mode 100644 index 00000000..94f38535 --- /dev/null +++ b/modules/governance_state/data/check_conway_syncdb.py @@ -0,0 +1,89 @@ +# Standalone test for Conway governance correctness checking +# Use: check_conway_syncdb.py + +import sys + +def screen_strings(header): + inside = False + prev = " " + out = "" + for ptr in range(0,len(header)): + if prev == '\"' and header[ptr] != '\"': + inside = not inside + + if header[ptr] == ',' and inside: + out += ';' + else: + out += header[ptr] + prev = header[ptr] + + return out + +def strip_some(s): + return s.removeprefix('Some(').removesuffix(')') + +if len(sys.argv) <= 2: + print("\nUsage: %s \n" % sys.argv[0]) + exit(1) + +dbsync = open(sys.argv[1],'rt') +acropolis = open(sys.argv[2],'rt') + +dbsync_dict = {} + +# 0 7 13 23 +header = """id,tx_id,index,prev_gov_action_proposal,deposit,return_address,expiration, + voting_anchor_id,type,description,param_proposal,ratified_epoch,enacted_epoch, + dropped_epoch,expired_epoch,id,hash,block_id,block_index,out_sum,fee,deposit,size, + invalid_before,invalid_hereafter,valid_contract,script_size,treasury_donation""" +header = screen_strings(header) +hl = header.split(',') + +for s in dbsync: + sh = screen_strings(s) + sl = sh.strip().split(',') + if len(sl) != len(hl): + continue + if sl == hl: + continue + + hhash = sl[16].strip("\\x") + + idx = (hhash,sl[2]) + if idx not in dbsync_dict: + dbsync_dict[idx] = [] + dbsync_dict[idx].append((sl[8],sl[11],sl[12],sl[13],sl[14])) + +header_a = """id,start,tx_id,index,prev_gov_action_proposal,deposit,return_address,expiration, + voting_anchor_id,type,description,param_proposal,ratified_epoch,enacted_epoch, + dropped_epoch,expired_epoch""" +al = header_a.split(',') + +found = 0 +for s in acropolis: + sh = screen_strings(s) + sl = sh.strip().split(',') + if len(sl) < 14: + print("Rejecting:",s) + continue + if sl == al: + continue + + idx = (sl[1],sl[2]) + if idx not in dbsync_dict: + print(idx," not found\n") + else: + rec = dbsync_dict[idx] + if len(rec) > 1: + print(idx, "too many records: ",rec,"\n") + + found += 1 + (ty,re,en,de,ex) = rec[0] + if ty == "InfoAction": + ty = "Information" + if ty != sl[8]: print (idx, "types do not match: `",ty,"` `",sl[8],"`\n") + if re != strip_some(sl[11]): print (idx, "re do not match: `",re,"` `",sl[11],"`\n") + if en != strip_some(sl[12]): print (idx, "en do not match: `",en,"` `",sl[12],"`\n") + if ex != strip_some(sl[14]): print (idx, "ex do not match: `",ex,"` `",sl[14],"`\n") + +print("Total found records: ",found,"\n") diff --git a/modules/governance_state/data/mysql_db_voting.csv b/modules/governance_state/data/mysql_db_voting.csv new file mode 100644 index 00000000..a5238b5f --- /dev/null +++ b/modules/governance_state/data/mysql_db_voting.csv @@ -0,0 +1,71 @@ +id,tx_id,index,prev_gov_action_proposal,deposit,return_address,expiration,voting_anchor_id,type,description,param_proposal,ratified_epoch,enacted_epoch,dropped_epoch,expired_epoch,id,hash,block_id,block_index,out_sum,fee,deposit,size,invalid_before,invalid_hereafter,valid_contract,script_size,treasury_donation +5,98477975,0,,100000000000,4563516,526,695,ParameterChange,"{""tag"": ""ParameterChange"", ""contents"": [null, {""costModels"": {""PlutusV3"": [100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, 36, 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, 719, 0, 1, 107878, 680, 0, 1, 95336, 1, 281145, 18848, 0, 1, 180194, 159, 1, 1, 158519, 8942, 0, 1, 159378, 8813, 0, 1, 107490, 3298, 1, 106057, 655, 1, 1964219, 24520, 3]}}, ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",401,525,526,,,98477975,\xb2a591ac219ce6dcca5847e0248015209c7cb0436aa6bd6863d0c1f152a60bc5,11059483,12,1277744,366804,100000000000,3594,,,t,2132,0 +1,95815162,0,,100000000000,4870428,514,90,InfoAction,"{""tag"": ""InfoAction""}",,,,515,514,95815162,\x15f82a365bdee483a4b03873a40d3829cc88c048ff3703e11bd01dd9e035c916,10789475,12,65650430,175929,100000000000,362,,,t,0,0 +8,105650982,0,,100000000000,4563516,544,1150,NewConstitution,"{""tag"": ""NewConstitution"", ""contents"": [null, {""anchor"": {""url"": ""ipfs://bafkreiazhhawe7sjwuthcfgl3mmv2swec7sukvclu3oli7qdyz4uhhuvmy"", ""dataHash"": ""2a61e2f4b63442978140c77a70daab3961b22b12b63b13949a390c097214d1c5""}, ""script"": ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""}]}",,541,542,,,105650982,\x8c653ee5c9800e6d31e79b5a7f7d4400c81d44717ad4db633dc18d4c07e4a4fd,11428973,21,1708465,187237,100000000000,518,,,t,0,0 +2,96653159,0,,100000000000,4896630,517,443,InfoAction,"{""tag"": ""InfoAction""}",,,,518,517,96653159,\x59fd353253eb177e2104e8f23ea4c63e3d32ef95c7865d03e90d3884424dc1db,10852595,12,9823015,176985,100000000000,386,,,t,0,0 +3,97475494,0,,100000000000,4918992,521,569,InfoAction,"{""tag"": ""InfoAction""}",,,,522,521,97475494,\x7fd6429add8f2611ad8d48c0cc49101463093aec285faea402e8cfde78ea58d7,10951864,7,999827107,172893,100000000000,393,,,t,0,0 +4,98263157,0,,100000000000,4563516,525,671,ParameterChange,"{""tag"": ""ParameterChange"", ""contents"": [null, {""costModels"": {""PlutusV3"": [100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, 36, 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, 0, 1]}}, ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",400,,,526,525,98263157,\x51f495aa23f4b3b3aa90afde4a0e67823bb7ac4ac65f5ffbb138373b863f2f74,11038356,0,1644548,355452,100000000000,3441,,,t,2132,0 +6,99700949,0,,100000000000,4563516,529,754,InfoAction,"{""tag"": ""InfoAction""}",,,,530,529,99700949,\xfff0df644d328a5367212f45bab59060bde3c4091dc96c723062896fd6197314,11122159,76,1079595,198149,100000000000,560,,,t,0,0 +7,103039629,0,,100000000000,4563516,536,936,HardForkInitiation,"{""tag"": ""HardForkInitiation"", ""contents"": [null, {""major"": 10, ""minor"": 0}]}",,536,537,,,103039629,\x0b19476e40bbbb5e1e8ce153523762e2b6859e7ecacbaf06eae0ee6a447e79b9,11254748,38,895702,183893,100000000000,442,,,t,0,0 +9,106371891,0,5,100000000000,5186648,546,1302,ParameterChange,"{""tag"": ""ParameterChange"", ""contents"": [{""txId"": ""b2a591ac219ce6dcca5847e0248015209c7cb0436aa6bd6863d0c1f152a60bc5"", ""govActionIx"": 0}, {""treasuryCut"": 0.1}, ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",402,,,547,546,106371891,\x941502b0aa104c850d197923259444d2b57cab7af18b63143775465aaacc84f5,11488715,31,2992488895,323076,100000000000,2855,,147912156,t,2132,0 +10,107048539,0,,100000000000,4563516,549,1458,InfoAction,"{""tag"": ""InfoAction""}",,,,550,549,107048539,\x56f39054758f1a3cedc1de9225d66bf270b62dfdbfbc5399f1d6d43aceffc636,11554076,2,1527168,181297,100000000000,383,,,t,0,0 +11,107777670,0,,100000000000,5253767,553,1566,InfoAction,"{""tag"": ""InfoAction""}",,,,554,553,107777670,\x7f320409d9998712ff3a3cdf0c9439e1543f236a3d746766f78f1fdbe1e06bf8,11622438,9,9987445153,181913,100000000000,601,,,t,0,0 +12,108041910,0,,100000000000,4563516,554,1630,InfoAction,"{""tag"": ""InfoAction""}",,,,555,554,108041910,\x9b62b3c632f329016a968ac25211825bb4f84b12461121c7da3aa11df92370f9,11661886,14,1345871,181297,100000000000,383,,,t,0,0 +13,108980410,0,,100000000000,3391711,561,1892,InfoAction,"{""tag"": ""InfoAction""}",,,,562,561,108980410,\x7d9fc9fe4cee64fb34e57783378ac869a85c78d6fbcd4078ed131ab6fa3c7db6,11794543,12,11990791828,295653,100000000000,3187,,,t,0,0 +15,109382083,0,,100000000000,4180416,563,1969,InfoAction,"{""tag"": ""InfoAction""}",,,,564,563,109382083,\x4840e305563327358cf70dae5015b2df8f8c35cef03f74521d4f117ac17bc384,11845257,6,234865010,270001,100000000000,2600,,,t,0,0 +16,109387915,0,,100000000000,5321718,563,1973,InfoAction,"{""tag"": ""InfoAction""}",,,,564,563,109387915,\x637e3c256da9c1350847ca3211e8c44e3660a4471e8b6f68a8c537746b4aeb73,11846463,2,9993345457,177645,100000000000,501,,,t,0,0 +14,109365119,0,,100000000000,5321055,563,1961,InfoAction,"{""tag"": ""InfoAction""}",,,,564,563,109365119,\xbd488931f792651fefa9c6fda185a2c6cec83245b51d994e33090ce36e29cc26,11843663,2,2750000,250000,100002000000,1071,,,t,0,0 +17,109397627,0,,100000000000,3391711,563,1983,InfoAction,"{""tag"": ""InfoAction""}",,,,564,563,109397627,\xdefbf15b06092718adf4befeab982e03d2966b9caeef93c19e470549ef75ea49,11847866,12,12352487425,287073,100000000000,2992,,,t,0,0 +67,113657169,0,,100000000000,5473879,587,6058,InfoAction,"{""tag"": ""InfoAction""}",,,,,,113657169,\x90cf51975f9c19c291bac3e57364a89507e64daa0a2d35e356f87e6684821556,12363296,1,149823499,176501,100000000000,375,,,t,0,0 +18,109605083,0,,100000000000,5322199,564,2037,InfoAction,"{""tag"": ""InfoAction""}",,,,565,564,109605083,\xe14de8d9dc4f4ddf3fe9250a8a926e20f10e99b86bd0610b77d7a054981591ee,11870630,19,2641190,181297,100000000000,383,,,t,0,0 +66,113591364,0,,100000000000,3391711,586,6022,InfoAction,"{""tag"": ""InfoAction""}",,,,,586,113591364,\x89e3a4f09122d669d6f6c82dd894e02826e8497be7eb036233bafc0ee4fc6665,12354745,2,6609016485,334329,100000000000,4066,,,t,0,0 +22,111497194,0,,100000000000,3391711,576,2536,InfoAction,"{""tag"": ""InfoAction""}",,,,577,576,111497194,\x495bd3cd44ea6bc0b4c2dbb1e4cdc7e214874c2c07490a4e2476a627ef911280,12123162,5,133600048012,305201,100000000000,3404,,,t,0,0 +20,111300899,0,,100000000000,5423582,574,2502,InfoAction,"{""tag"": ""InfoAction""}",,,,575,574,111300899,\x9d213a57684d7ddf6f3350c80d042639ecbed5ccccc4a05bf54959a086593e7b,12093342,12,369725480,267229,100000000000,1027,,,t,0,0 +24,111863657,0,,100000000000,5443227,577,2620,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 2162096000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,574,575,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +25,111863657,1,,100000000000,5443227,577,2621,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 96817080000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,574,575,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +43,111863657,19,,100000000000,5443227,577,2639,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 605000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,577,578,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +46,111863657,22,,100000000000,5443227,577,2642,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 592780000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,577,578,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +62,112292472,0,,100000000000,5443227,580,4387,NewCommittee,"{""tag"": ""UpdateCommittee"", ""contents"": [null, [{""scriptHash"": ""b6012034ba0a7e4afbbf2c7a1432f8824aee5299a48e38e41a952686""}, {""scriptHash"": ""ce8b37a72b178a37bbd3236daa7b2c158c9d3604e7aa667e6c6004b7""}, {""scriptHash"": ""df0e83bde65416dade5b1f97e7f115cc1ff999550ad968850783fe50""}, {""scriptHash"": ""e8165b3328027ee0d74b1f07298cb092fd99aa7697a1436f5997f625""}, {""scriptHash"": ""f0dc2c00d92a45521267be2d5de1c485f6f9d14466d7e16062897cf7""}], {""keyHash-13493790d9b03483a1e1e684ea4faf1ee48a58f402574e7f2246f4d4"": 653, ""keyHash-dc0d6ef49590eb6880a50a00adde17596e6d76f7159572fa1ff85f2a"": 653, ""scriptHash-1980dbf1ad624b0cb5410359b5ab14d008561994a6c2b6c53fabec00"": 726, ""scriptHash-349e55f83e9af24813e6cb368df6a80d38951b2a334dfcdf26815558"": 653, ""scriptHash-84aebcfd3e00d0f87af918fc4b5e00135f407e379893df7e7d392c6a"": 726, ""scriptHash-9752e4306e5ae864441d21064f791174c8b626199b8e7a45f9e03b45"": 726, ""scriptHash-9cc3f387623f45dae6a68b7096b0c2e403d8601a82dc40221ead41e2"": 653}, {""numerator"": 2, ""denominator"": 3}]}",,580,581,,,112292472,\x47a0e7a4f9383b1afc2192b23b41824d65ac978d7741aca61fc1fa16833d1111,12207873,26,4805195,194805,100000000000,791,,,t,0,0 +68,114141147,0,,100000000000,4180416,589,6205,InfoAction,"{""tag"": ""InfoAction""}",,,,,,114141147,\xe5643c33f608642e329228a968770e5b19ef5f48ff1f698712e2ce864a49e3f0,12419898,0,99823499,176501,100000000000,375,,,t,0,0 +69,114195603,0,,100000000000,5556256,590,6229,InfoAction,"{""tag"": ""InfoAction""}",,,,,,114195603,\x996edfe370b8e51be73541e75499a818461305e37c7fa0c1b193f2c587167cc7,12427264,0,46833006,178437,100000000000,519,,,t,0,0 +70,114354580,0,,100000000000,5443227,591,6278,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 1150000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,,,,,114354580,\xd2db60c5307cb517c735e2d0138d2b6f10fc5b221d610fa187719bdc82af9a03,12448520,3,4487863,317332,100000000000,2788,,,t,2132,0 +71,114668785,0,8,100000000000,5321718,593,6411,NewConstitution,"{""tag"": ""NewConstitution"", ""contents"": [{""txId"": ""8c653ee5c9800e6d31e79b5a7f7d4400c81d44717ad4db633dc18d4c07e4a4fd"", ""govActionIx"": 0}, {""anchor"": {""url"": ""ipfs://bafkreibyaftvpujgjernbcj2rgrwg3fntsg37cxx5bdgpy4hyt4eki5r2e"", ""dataHash"": ""9fc568bb8ae398ff2e9b1cf7ca459c3648f865338a70248dd753aea175b0d4d4""}, ""script"": ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""}]}",,,,,,114668785,\xc882f194684d672316212f01efc6d28177e8965b7cd6956981fe37cc6715961e,12492094,6,99816195,183805,100000000000,646,,,t,0,0 +27,111863657,3,,100000000000,5443227,577,2623,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 424800000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +51,111863657,27,,100000000000,5443227,577,2647,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 1500000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,,,578,577,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +54,111863657,30,,100000000000,5443227,577,2650,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 3000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,,,578,577,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +52,111863657,28,,100000000000,5443227,577,2648,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 3126000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,577,578,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +53,111863657,29,,100000000000,5443227,577,2649,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 889500000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,577,578,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +59,111863657,35,,100000000000,5443227,577,2655,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 750000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,577,578,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +63,112437920,0,,100000000000,5473879,580,4832,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""keyHash"": ""28e35801c889b56600b218e29080235e3172f6da9fa06b0d6e0cb049""}}, 5000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,,,581,580,112437920,\x4ff43f1eab5252ef00ba6f1429dc23715e3899834f10c067e8065c8364d5559a,12223934,17,22944868,305132,100000000000,2724,,,t,2132,0 +19,111039533,0,,100000000000,5321055,573,2466,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""860ee0f6bd8235842a908afd86dc949c4c9dabd74b0965c364b8d315""}}, 100000000000], [{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""877f57e0bdcc8167e984dee052d8dd7346effc08ff80a29b045e1a10""}}, 500000000000], [{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""9beae6fc7feada6ccf824554b5e3a498eab15205ac9248c3fb993b5f""}}, 300000000000], [{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""a7966878550ce11ec8a83b93fc1a6e105d544f78494401066c4fe0cf""}}, 300000000000], [{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""e425984e24fb2c06ad676882178ddee6e47b994a589ae3d70c817c79""}}, 300000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,570,571,,,111039533,\x60ed6ab43c840ff888a8af30a1ed27b41e9f4a91a89822b2b63d1bfc52aeec45,12057260,5,49656796,343204,100000000000,3533,159148801,,t,2132,0 +49,111863657,25,,100000000000,5443227,577,2645,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 6000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,576,577,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +50,111863657,26,,100000000000,5443227,577,2646,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 12000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,576,577,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +47,111863657,23,,100000000000,5443227,577,2643,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 69459000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,574,575,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +64,112439116,0,,100000000000,5473879,580,4834,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""keyHash"": ""28e35801c889b56600b218e29080235e3172f6da9fa06b0d6e0cb049""}}, 5000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,,,581,580,112439116,\x1d6aea56e6a523b13e03ae30ff24ebf9852543b4f7d415c7e50f55f6baaa4e3d,12224102,1,39261128,308740,100000000000,2806,,,t,2132,0 +65,112509795,0,8,100000000000,5321718,581,5001,NewConstitution,"{""tag"": ""NewConstitution"", ""contents"": [{""txId"": ""8c653ee5c9800e6d31e79b5a7f7d4400c81d44717ad4db633dc18d4c07e4a4fd"", ""govActionIx"": 0}, {""anchor"": {""url"": ""ipfs://bafkreiat3w662d6jnsuz2kpra2h5ve4xxij4jkl7kylf7jk6gw43q5b5ny"", ""dataHash"": ""170379f4c41a1833743837770e5bdb034c089a8edc835174e73a9c5b692676cc""}, ""script"": ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""}]}",,,,582,581,112509795,\x207b226e110e13bb18b119fcd313520e0fcd060b2bc9fb9a5e5bc6e94ab10f3b,12233245,2,10016517356,183981,100000000000,650,,,t,0,0 +23,111783541,0,,100000000000,5443227,577,2597,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 45217000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111783541,\x3cf29192a0ee1a77985054072edcdb566ac14707730637c4635d8fb6813cb4c9,12152955,0,9688696,311304,100000000000,2756,,,t,2132,0 +61,111864094,0,,100000000000,5443227,577,2657,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 99600000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111864094,\x2c7f900b7ff68f317a7b0e42231d4aed36227660baf2ee9a4be7e880eb977313,12159572,8,7768942,317332,100000000000,2788,,,t,2132,0 +26,111863657,2,,100000000000,5443227,577,2622,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 1300000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +28,111863657,4,,100000000000,5443227,577,2624,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 220914000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +29,111863657,5,,100000000000,5443227,577,2625,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 220914000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +30,111863657,6,,100000000000,5443227,577,2626,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 130903000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +31,111863657,7,,100000000000,5443227,577,2627,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 1161000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +32,111863657,8,,100000000000,5443227,577,2628,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 220914000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +33,111863657,9,,100000000000,5443227,577,2629,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 300000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +34,111863657,10,,100000000000,5443227,577,2630,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 600000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +35,111863657,11,,100000000000,5443227,577,2631,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 5885000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +36,111863657,12,,100000000000,5443227,577,2632,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 578571000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +37,111863657,13,,100000000000,5443227,577,2633,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 243478000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +38,111863657,14,,100000000000,5443227,577,2634,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 11070323000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +39,111863657,15,,100000000000,5443227,577,2635,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 700000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +40,111863657,16,,100000000000,5443227,577,2636,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 583000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +41,111863657,17,,100000000000,5443227,577,2637,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 657692000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +42,111863657,18,,100000000000,5443227,577,2638,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 266667000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +44,111863657,20,,100000000000,5443227,577,2640,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 212000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +45,111863657,21,,100000000000,5443227,577,2641,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 15750000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +48,111863657,24,,100000000000,5443227,577,2644,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 6000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +55,111863657,31,,100000000000,5443227,577,2651,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 4000000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +56,111863657,32,,100000000000,5443227,577,2652,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 26840000000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +57,111863657,33,,100000000000,5443227,577,2653,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 199911000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +58,111863657,34,,100000000000,5443227,577,2654,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 314800000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 +60,111863657,36,,100000000000,5443227,577,2656,TreasuryWithdrawals,"{""tag"": ""TreasuryWithdrawals"", ""contents"": [[[{""network"": ""Mainnet"", ""credential"": {""scriptHash"": ""8583857e4a12ffe1e6f641a1785a0f2f036c565cfbe6ff9db8e5a469""}}, 104347000000]], ""fa24fb305126805cf2164c161d852a0e7330cf988f1fe558cf7d4a64""]}",,575,576,,,111863657,\x8ad3d454f3496a35cb0d07b0fd32f687f66338b7d60e787fc0a22939e5d8833e,12159549,11,99998397578,1602422,3700000000000,11159,,,t,2132,0 diff --git a/modules/governance_state/data/retrieve.sql b/modules/governance_state/data/retrieve.sql new file mode 100644 index 00000000..e5112952 --- /dev/null +++ b/modules/governance_state/data/retrieve.sql @@ -0,0 +1 @@ +\copy (select * from gov_action_proposal left join tx on gov_action_proposal.tx_id = tx.id) to 'mysql_db_voting.csv' csv header diff --git a/modules/governance_state/src/alonzo_babbage_voting.rs b/modules/governance_state/src/alonzo_babbage_voting.rs index bc90a45b..46c9a4f7 100644 --- a/modules/governance_state/src/alonzo_babbage_voting.rs +++ b/modules/governance_state/src/alonzo_babbage_voting.rs @@ -106,6 +106,10 @@ impl AlonzoBabbageVoting { pub fn advance_epoch(&mut self, epoch_blk: &BlockInfo) { self.proposals.retain(|enact_epoch, _| *enact_epoch >= epoch_blk.epoch); } + + pub fn get_stats(&self) -> String { + format!("alonzo proposal epochs: {:?}", self.proposals.keys()) + } } #[cfg(test)] @@ -211,7 +215,7 @@ mod tests { // Mainnet Tests // - const MAINNET_PROPOSALS_JSON: &[u8] = include_bytes!("./alonzo_babbage_voting.json"); + const MAINNET_PROPOSALS_JSON: &[u8] = include_bytes!("../data/alonzo_babbage_voting.json"); fn extract_mainnet_parameter( f: impl Fn(&ProtocolParamUpdate) -> Option, @@ -310,7 +314,7 @@ mod tests { // SanchoNet Tests // - const SANCHONET_PROPOSALS_JSON: &[u8] = include_bytes!("./ab_sancho_voting.json"); + const SANCHONET_PROPOSALS_JSON: &[u8] = include_bytes!("../data/ab_sancho_voting.json"); fn extract_sanchonet_parameter( f: impl Fn(&ProtocolParamUpdate) -> Option, diff --git a/modules/governance_state/src/conway_voting.rs b/modules/governance_state/src/conway_voting.rs new file mode 100644 index 00000000..f78d4950 --- /dev/null +++ b/modules/governance_state/src/conway_voting.rs @@ -0,0 +1,611 @@ +use crate::voting_state::VotingRegistrationState; +use acropolis_common::protocol_params::ConwayParams; +use acropolis_common::{ + BlockInfo, DRepCredential, DelegatedStake, EnactStateElem, GovActionId, GovernanceAction, + GovernanceOutcome, GovernanceOutcomeVariant, KeyHash, Lovelace, ProposalProcedure, + SingleVoterVotes, TreasuryWithdrawalsAction, TxHash, Vote, Voter, VotesCount, VotingOutcome, + VotingProcedure, +}; +use anyhow::{anyhow, bail, Result}; +use hex::ToHex; +use std::collections::{HashMap, HashSet}; +use std::fs::OpenOptions; +use std::io::Write; +use std::ops::Range; +use tracing::{debug, error, info}; + +#[derive(Debug)] +pub struct ActionStatus { + voting_epochs: Range, + ratification_epoch: Option, + enactment_epoch: Option, + expiration_epoch: Option, +} + +impl ActionStatus { + pub fn new(current_epoch: u64, voting_length: u64) -> Self { + Self { + voting_epochs: current_epoch..current_epoch + voting_length + 1, + ratification_epoch: None, + enactment_epoch: None, + expiration_epoch: None, + } + } + + pub fn is_active(&self, current_epoch: u64) -> bool { + self.voting_epochs.contains(¤t_epoch) + } + + pub fn is_accepted(&self) -> bool { + self.ratification_epoch.is_some() + } +} + +pub struct ConwayVoting { + conway: Option, + bootstrap: Option, + + pub proposals: HashMap, + pub votes: HashMap>, + action_status: HashMap, + + verification_output_file: Option, + action_proposal_count: usize, + votes_count: usize, +} + +impl ConwayVoting { + pub fn new(verification_output_file: Option) -> Self { + Self { + conway: None, + bootstrap: None, + proposals: Default::default(), + votes: Default::default(), + action_status: Default::default(), + action_proposal_count: 0, + votes_count: 0, + verification_output_file, + } + } + + pub fn get_conway_params(&self) -> Result<&ConwayParams> { + self.conway.as_ref().ok_or_else(|| anyhow!("Conway parameters not available")) + } + + /// Update Conway governance parameters. + /// `bootstrap` parameter: Conway era is split into Chang era (protocol version 9.0) + /// and Plomin era (10.0). During Chang era governance procedures are working in + /// bootstrap (limited) mode. + /// Pass true at Chang era, and false at Plomin era. + /// https://docs.cardano.org/about-cardano/evolution/upgrades/chang + pub fn update_parameters(&mut self, conway: &Option, bootstrap: bool) { + self.conway = conway.clone(); + self.bootstrap = Some(bootstrap); + } + + pub fn insert_proposal_procedure( + &mut self, + epoch: u64, + proc: &ProposalProcedure, + ) -> Result<()> { + self.action_proposal_count += 1; + let prev = self.proposals.insert(proc.gov_action_id.clone(), (epoch, proc.clone())); + if let Some(prev) = prev { + return Err(anyhow!( + "Governance procedure {} already exists! New: {:?}, old: {:?}", + proc.gov_action_id, + (epoch, proc), + prev + )); + } + + let prev = self.action_status.insert( + proc.gov_action_id.clone(), + ActionStatus::new(epoch, self.get_conway_params()?.gov_action_lifetime as u64), + ); + if let Some(prev) = prev { + return Err(anyhow!( + "Governance procedure {} action status already exists! Old: {:?}", + proc.gov_action_id, + prev + )); + } + + Ok(()) + } + + /// Update votes memory cache + pub fn insert_voting_procedure( + &mut self, + current_epoch: u64, + voter: &Voter, + transaction: &TxHash, + voter_votes: &SingleVoterVotes, + ) -> Result<()> { + self.votes_count += voter_votes.voting_procedures.len(); + for (action_id, procedure) in voter_votes.voting_procedures.iter() { + let votes = self.votes.entry(action_id.clone()).or_insert_with(|| HashMap::new()); + + match self.action_status.get(action_id) { + None => { + error!( + "Governance vote by {} for non-registered {}. Ignored.", + voter, action_id + ); + continue; + } + Some(vs) if !vs.is_active(current_epoch) => { + error!( + "Governance vote by {} for inactive {}. Active at {:?}. Ignored.", + voter, action_id, vs.voting_epochs + ); + continue; + } + Some(_) => (), + } + + if let Some((prev_trans, prev_vote)) = + votes.insert(voter.clone(), (transaction.clone(), procedure.clone())) + { + // Re-voting is allowed; new vote must be treated as the proper one, + // older is to be discarded. + if tracing::enabled!(tracing::Level::DEBUG) { + debug!("Governance vote by {} for {} already registered! New: {:?}, old: {:?} from {}", + voter, action_id, procedure, prev_vote, prev_trans.encode_hex::() + ); + } + } + } + Ok(()) + } + + /// Checks whether action_id can be considered finally accepted + fn is_finally_accepted( + &self, + voting_state: &VotingRegistrationState, + action_id: &GovActionId, + drep_stake: &HashMap, + spo_stake: &HashMap, + ) -> Result { + let (_epoch, proposal) = self + .proposals + .get(action_id) + .ok_or_else(|| anyhow!("action {} not found", action_id))?; + let conway_params = self.get_conway_params()?; + let bootstrap = self.bootstrap.ok_or_else(|| anyhow!("'bootstrap' param not set"))?; + let threshold = voting_state.get_action_thresholds(proposal, conway_params, bootstrap)?; + + let votes = self.get_actual_votes(action_id, drep_stake, spo_stake); + let voted = votes.majorizes(&threshold); + let previous_ok = match proposal.gov_action.get_previous_action_id() { + Some(act) => self.action_status.get(&act).map(|x| x.is_accepted()).unwrap_or(false), + None => true, + }; + let accepted = previous_ok && voted; + info!( + "Proposal {action_id}: votes {votes}, thresholds {threshold}, prevous_ok {previous_ok}, \ + voted {voted}, result {accepted}" + ); + + Ok(VotingOutcome { + procedure: proposal.clone(), + votes_cast: votes, + votes_threshold: threshold, + accepted, + }) + } + + /// Should be called when voting is over + fn end_voting(&mut self, action_id: &GovActionId) -> Result<()> { + self.votes.remove(&action_id); + self.proposals.remove(&action_id); + + Ok(()) + } + + /// Returns actual votes: (Pool votes, DRep votes, committee votes) + fn get_actual_votes( + &self, + action_id: &GovActionId, + drep_stake: &HashMap, + spo_stake: &HashMap, + ) -> VotesCount { + let mut votes = VotesCount::zero(); + if let Some(all_votes) = self.votes.get(&action_id) { + for (voter, (_hash, voting_proc)) in all_votes.iter() { + if voting_proc.vote != Vote::Yes { + // TODO: correctly count abstain votes + count vote pools + continue; + } + + match voter { + Voter::ConstitutionalCommitteeKey(_) => votes.committee += 1, + Voter::ConstitutionalCommitteeScript(_) => votes.committee += 1, + Voter::DRepKey(key) => { + drep_stake + .get(&DRepCredential::AddrKeyHash(key.clone())) + .inspect(|v| votes.drep += *v); + } + Voter::DRepScript(script) => { + drep_stake + .get(&DRepCredential::ScriptHash(script.clone())) + .inspect(|v| votes.drep += *v); + } + Voter::StakePoolKey(pool) => { + spo_stake.get(pool).inspect(|ds| votes.pool += ds.live); + } + } + } + } + votes + } + + /// Checks whether action is expired at the beginning of new_epoch + fn is_expired(&self, new_epoch: u64, action_id: &GovActionId) -> Result { + info!( + "Checking whether {} is expired at new epoch {}", + action_id, new_epoch + ); + + let action_status = self + .action_status + .get(action_id) + .ok_or_else(|| anyhow!("Action status {action_id} not found"))?; + + Ok(!action_status.is_active(new_epoch)) + } + + fn pack_as_enact_state_elem(p: &ProposalProcedure) -> Option { + match &p.gov_action { + GovernanceAction::Information => None, + GovernanceAction::TreasuryWithdrawals(_wt) => None, + GovernanceAction::HardForkInitiation(hf) => { + Some(EnactStateElem::ProtVer(hf.protocol_version.clone())) + } + GovernanceAction::ParameterChange(pc) => { + Some(EnactStateElem::Params(pc.protocol_param_update.clone())) + } + GovernanceAction::NewConstitution(nc) => { + Some(EnactStateElem::Constitution(nc.new_constitution.clone())) + } + GovernanceAction::UpdateCommittee(uc) => { + Some(EnactStateElem::Committee(uc.data.clone())) + } + GovernanceAction::NoConfidence(_) => Some(EnactStateElem::NoConfidence), + } + } + + fn retrieve_withdrawal(p: &ProposalProcedure) -> Option { + if let GovernanceAction::TreasuryWithdrawals(ref action) = p.gov_action { + Some(action.clone()) + } else { + None + } + } + + /// Checks and updates action_id state at the start of new_epoch + /// If the action is accepted, returns accepted ProposalProcedure. + fn process_one_proposal( + &mut self, + new_epoch: u64, + voting_state: &VotingRegistrationState, + action_id: &GovActionId, + drep_stake: &HashMap, + spo_stake: &HashMap, + ) -> Result> { + let outcome = self.is_finally_accepted(voting_state, &action_id, drep_stake, spo_stake)?; + let expired = self.is_expired(new_epoch, &action_id)?; + if outcome.accepted || expired { + self.end_voting(&action_id)?; + info!( + "New epoch {new_epoch}: voting for {action_id} outcome: {}, expired: {expired}", + outcome.accepted + ); + return Ok(Some(outcome)); + } + + Ok(None) + } + + fn gov_action_id_to_string(action_id: &GovActionId) -> String { + format!( + "\"transaction: {}, action_index: {}\"", + hex::encode(action_id.transaction_id), + action_id.action_index + ) + } + + fn get_action_name(action: &GovernanceAction) -> &str { + match action { + GovernanceAction::ParameterChange(_) => "ParameterChange", + GovernanceAction::HardForkInitiation(_) => "HardForkInitiation", + GovernanceAction::TreasuryWithdrawals(_) => "TreasuryWithdrawals", + GovernanceAction::NoConfidence(_) => "NoConfidence", + GovernanceAction::UpdateCommittee(_) => "UpdateCommittee", + GovernanceAction::NewConstitution(_) => "NewConstitution", + GovernanceAction::Information => "Information", + } + } + + fn prepare_quotes(input: &str) -> String { + input.replace("\"", "\"\"") + } + + /// Function dumps information about completed (expired, ratified, enacted) governance + /// actions in format, close to that of `gov_action_proposal` from `sqldb`. + pub fn print_outcome_to_verify(&self, outcome: &Vec) -> Result<()> { + let out_file_name = match &self.verification_output_file { + Some(o) => o, + None => return Ok(()), + }; + + let mut out_file = match OpenOptions::new().append(true).open(out_file_name.clone()) { + Ok(res) => res, + Err(e) => bail!("Cannot open verification output {out_file_name} for writing: {e}"), + }; + + // If there is no outcome, the file will be created (appended), but not changed. + // This is intentional for ease of debugging. + for elem in outcome.iter() { + let prev_action = match &elem.voting.procedure.gov_action.get_previous_action_id() { + Some(act) => Self::gov_action_id_to_string(act), + None => "".to_owned(), + }; + + let action_status = + self.action_status.get(&elem.voting.procedure.gov_action_id).ok_or_else(|| { + anyhow!( + "Cannot get action status for {}", + &elem.voting.procedure.gov_action_id + ) + })?; + + let deposit = &elem.voting.procedure.deposit; + let reward = hex::encode(elem.voting.procedure.reward_account.get_hash()); + let expire = action_status.voting_epochs.end; + let ratification_info = if elem.voting.accepted { + format!( + "{:?},{:?},,", + action_status.ratification_epoch, action_status.enactment_epoch + ) + } else { + format!(",,,{:?}", action_status.expiration_epoch) + }; + let txid: String = elem.voting.procedure.gov_action_id.transaction_id.encode_hex(); + let idx = elem.voting.procedure.gov_action_id.action_index; + let ptype = Self::get_action_name(&elem.voting.procedure.gov_action); + let proc = Self::prepare_quotes(&format!("{:?}", &elem.voting.procedure.gov_action)); + let cast = &elem.voting.votes_cast; + let threshold = &elem.voting.votes_threshold; + + // id,tx_id,index,prev_gov_action_proposal,deposit,return_address,expiration, + // voting_anchor_id,type,description,param_proposal,ratified_epoch,enacted_epoch, + // dropped_epoch,expired_epoch,votes_cast,votes_threshold + let res = format!( + "{},{txid},{idx},{prev_action},{deposit},{reward},{expire},,{ptype},\"{proc}\",,\ + {ratification_info},{cast},{threshold}\n", + elem.voting.procedure.gov_action_id + ); + if let Err(e) = out_file.write(&res.as_bytes()) { + error!( + "Cannot write 'res' to verification output {out_file_name} for writing: {e}" + ); + } + } + + Ok(()) + } + + pub fn finalize_conway_voting( + &mut self, + new_block: &BlockInfo, + voting_state: &VotingRegistrationState, + drep_stake: &HashMap, + spo_stake: &HashMap, + ) -> Result> { + let mut outcome = Vec::::new(); + let actions = self.proposals.keys().map(|a| a.clone()).collect::>(); + + for action_id in actions.iter() { + info!( + "Epoch {} started: processing action {}", + new_block.epoch, action_id + ); + let one_outcome = match self.process_one_proposal( + new_block.epoch, + &voting_state, + &action_id, + drep_stake, + spo_stake, + ) { + Err(e) => { + error!("Error processing governance {action_id}: {e}"); + continue; + } + Ok(None) => continue, + Ok(Some(out)) if out.accepted => { + let mut action_to_perform = GovernanceOutcomeVariant::NoAction; + + if let Some(elem) = Self::pack_as_enact_state_elem(&out.procedure) { + action_to_perform = GovernanceOutcomeVariant::EnactStateElem(elem); + } else if let Some(wt) = Self::retrieve_withdrawal(&out.procedure) { + action_to_perform = GovernanceOutcomeVariant::TreasuryWithdrawal(wt); + } + + GovernanceOutcome { + voting: out, + action_to_perform, + } + } + Ok(Some(out)) => GovernanceOutcome { + voting: out, + action_to_perform: GovernanceOutcomeVariant::NoAction, + }, + }; + + outcome.push(one_outcome); + } + + Ok(outcome) + } + + pub fn log_conway_voting_stats(&self) { + let mut proposal_procedures = + self.proposals.keys().cloned().collect::>(); + + for (action_id, voting_procedure) in self.votes.iter() { + let proposal = match self.proposals.get(action_id) { + None => " (absent) ".to_string(), + Some(p) => { + proposal_procedures.remove(action_id); + format!(" {p:?} ") + } + }; + info!("{action_id}: {proposal} => {voting_procedure:?}",) + } + + if !proposal_procedures.is_empty() { + let pp = proposal_procedures.into_iter().map(|x| format!("{x},")).collect::(); + info!("Proposal procedures without 'votes' records: [{}]", pp); + } + } + + /// Processes final `outcomes`, checks ratification/enaction epochs, + /// updates `action_status` data structrure. + pub fn update_action_status_with_outcomes( + &mut self, + epoch: u64, + outcomes: &Vec, + ) -> Result<()> { + for one_outcome in outcomes.iter() { + let action_id = &one_outcome.voting.procedure.gov_action_id; + let action = self + .action_status + .get_mut(action_id) + .ok_or_else(|| anyhow!("Cannot get action status for {action_id}"))?; + + if one_outcome.voting.accepted { + action.ratification_epoch = Some(epoch); + action.enactment_epoch = Some(epoch + 1); + } else { + if action.is_active(epoch) { + bail!( + "Impossible outcome: {action_id} votes {:?}, not ended at {epoch}", + action.voting_epochs + ); + } + action.expiration_epoch = Some(epoch); + } + } + Ok(()) + } + + pub fn get_stats(&self) -> String { + format!( + "conway proposals: {}, conway votes: {}", + self.proposals.len(), + self.votes.len() + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use acropolis_common::Anchor; + + fn create_governance_outcome(id: u8) -> GovernanceOutcome { + let votes = VotesCount { + committee: 1, + drep: 1, + pool: 1, + }; + + let v = VotingOutcome { + procedure: ProposalProcedure { + deposit: 0, + reward_account: vec![], + gov_action_id: GovActionId { + transaction_id: [0; 32], + action_index: id, + }, + gov_action: GovernanceAction::Information, + anchor: Anchor { + url: "".to_owned(), + data_hash: Vec::new(), + }, + }, + votes_cast: votes.clone(), + votes_threshold: votes.clone(), + accepted: true, + }; + + GovernanceOutcome { + voting: v, + action_to_perform: GovernanceOutcomeVariant::NoAction, + } + } + + #[test] + fn test_outcomes_queue() -> Result<()> { + let mut voting = ConwayVoting::new(None); + let oc1 = create_governance_outcome(1); + voting.action_status.insert( + oc1.voting.procedure.gov_action_id.clone(), + ActionStatus { + voting_epochs: 0..4, + ratification_epoch: None, + enactment_epoch: None, + expiration_epoch: None, + }, + ); + + voting.update_action_status_with_outcomes(0, &vec![])?; + voting.update_action_status_with_outcomes(1, &vec![oc1.clone()])?; + assert_eq!( + voting + .action_status + .get(&oc1.voting.procedure.gov_action_id) + .unwrap() + .ratification_epoch, + Some(1) + ); + assert_eq!( + voting.action_status.get(&oc1.voting.procedure.gov_action_id).unwrap().enactment_epoch, + Some(2) + ); + + let oc2 = create_governance_outcome(2); + voting.action_status.insert( + oc2.voting.procedure.gov_action_id.clone(), + ActionStatus { + voting_epochs: 0..5, + ratification_epoch: None, + enactment_epoch: None, + expiration_epoch: None, + }, + ); + voting.update_action_status_with_outcomes(2, &vec![oc2.clone()])?; + assert_eq!( + voting + .action_status + .get(&oc2.voting.procedure.gov_action_id) + .unwrap() + .ratification_epoch, + Some(2) + ); + assert_eq!( + voting.action_status.get(&oc2.voting.procedure.gov_action_id).unwrap().enactment_epoch, + Some(3) + ); + Ok(()) + } + + #[test] + fn test_prepare_quotes() -> Result<()> { + let x = "\"A\"\" lot (\"of\") quotes\""; + let xx = ConwayVoting::prepare_quotes(x); + assert_eq!(xx, "\"\"A\"\"\"\" lot (\"\"of\"\") quotes\"\""); + Ok(()) + } +} diff --git a/modules/governance_state/src/governance_state.rs b/modules/governance_state/src/governance_state.rs index 3e38c524..eae19f48 100644 --- a/modules/governance_state/src/governance_state.rs +++ b/modules/governance_state/src/governance_state.rs @@ -20,8 +20,10 @@ use tokio::sync::Mutex; use tracing::{error, info, info_span, Instrument}; mod alonzo_babbage_voting; +mod conway_voting; mod state; mod voting_state; + use state::State; use voting_state::VotingRegistrationState; @@ -34,6 +36,8 @@ const DEFAULT_PROTOCOL_PARAMETERS_TOPIC: (&str, &str) = ("protocol-parameters-topic", "cardano.protocol.parameters"); const DEFAULT_ENACT_STATE_TOPIC: (&str, &str) = ("enact-state-topic", "cardano.enact.state"); +const VERIFICATION_OUTPUT_FILE: &str = "verification-output-file"; + /// Governance State module #[module( message_type(Message), @@ -49,6 +53,7 @@ pub struct GovernanceStateConfig { protocol_parameters_topic: String, enact_state_topic: String, governance_query_topic: String, + verification_output_file: Option, } impl GovernanceStateConfig { @@ -66,6 +71,10 @@ impl GovernanceStateConfig { protocol_parameters_topic: Self::conf(config, DEFAULT_PROTOCOL_PARAMETERS_TOPIC), enact_state_topic: Self::conf(config, DEFAULT_ENACT_STATE_TOPIC), governance_query_topic: Self::conf(config, DEFAULT_GOVERNANCE_QUERY_TOPIC), + verification_output_file: config + .get_string(VERIFICATION_OUTPUT_FILE) + .map(|x| Some(x)) + .unwrap_or(None), }) } } @@ -134,6 +143,7 @@ impl GovernanceState { let state = Arc::new(Mutex::new(State::new( context.clone(), config.enact_state_topic.clone(), + config.verification_output_file.clone(), ))); // Ticker to log stats diff --git a/modules/governance_state/src/state.rs b/modules/governance_state/src/state.rs index dcb0dc27..fc80f649 100644 --- a/modules/governance_state/src/state.rs +++ b/modules/governance_state/src/state.rs @@ -1,61 +1,54 @@ //! Acropolis Governance State: State storage -use crate::alonzo_babbage_voting::AlonzoBabbageVoting; -use crate::VotingRegistrationState; use acropolis_common::{ messages::{ CardanoMessage, DRepStakeDistributionMessage, GovernanceOutcomesMessage, GovernanceProceduresMessage, Message, ProtocolParamsMessage, SPOStakeDistributionMessage, }, - protocol_params::ConwayParams, - BlockInfo, DRepCredential, DelegatedStake, EnactStateElem, Era, GovActionId, GovernanceAction, - GovernanceOutcome, GovernanceOutcomeVariant, KeyHash, Lovelace, ProposalProcedure, - SingleVoterVotes, TreasuryWithdrawalsAction, TxHash, Voter, VotesCount, VotingOutcome, - VotingProcedure, + BlockInfo, DRepCredential, DelegatedStake, Era, GovActionId, KeyHash, Lovelace, + ProposalProcedure, TxHash, Voter, VotingProcedure, }; use anyhow::{anyhow, bail, Result}; use caryatid_sdk::Context; use hex::ToHex; use std::{collections::HashMap, sync::Arc}; -use tracing::{debug, error, info}; +use tracing::{error, info}; + +use crate::{ + alonzo_babbage_voting::AlonzoBabbageVoting, conway_voting::ConwayVoting, + VotingRegistrationState, +}; pub struct State { pub enact_state_topic: String, pub context: Arc>, - proposal_count: usize, - - pub action_proposal_count: usize, - pub votes_count: usize, pub drep_stake_messages_count: usize, current_era: Era, - conway: Option, drep_stake: HashMap, spo_stake: HashMap, alonzo_babbage_voting: AlonzoBabbageVoting, - proposals: HashMap, - votes: HashMap>, + conway_voting: ConwayVoting, } impl State { - pub fn new(context: Arc>, enact_state_topic: String) -> Self { + pub fn new( + context: Arc>, + enact_state_topic: String, + verification_output_file: Option, + ) -> Self { Self { context, enact_state_topic, - proposal_count: 0, - action_proposal_count: 0, - votes_count: 0, drep_stake_messages_count: 0, - conway: None, current_era: Era::default(), alonzo_babbage_voting: AlonzoBabbageVoting::default(), - proposals: HashMap::new(), - votes: HashMap::new(), + conway_voting: ConwayVoting::new(verification_output_file), drep_stake: HashMap::new(), spo_stake: HashMap::new(), @@ -77,11 +70,15 @@ impl State { &mut self, message: &ProtocolParamsMessage, ) -> Result<()> { - if let Some(p) = &message.params.shelley { - self.alonzo_babbage_voting.update_parameters(p.epoch_length, p.update_quorum); - } - if message.params.conway.is_some() { - self.conway = message.params.conway.clone(); + if let Some(ps) = &message.params.shelley { + self.alonzo_babbage_voting.update_parameters(ps.epoch_length, ps.update_quorum); + + if message.params.conway.is_some() { + let bootstrap = ps.protocol_params.protocol_version.is_chang()?; + self.conway_voting.update_parameters(&message.params.conway, bootstrap) + } + } else if message.params.conway.is_some() { + bail!("Impossible parameters combination: Shelley is missing, but Conway is present."); } Ok(()) @@ -124,15 +121,19 @@ impl State { } else { // Conway governance for pproc in &governance_message.proposal_procedures { - self.proposal_count += 1; - if let Err(e) = self.insert_proposal_procedure(block.epoch, pproc) { + if let Err(e) = self.conway_voting.insert_proposal_procedure(block.epoch, pproc) { error!("Error handling governance_message: '{}'", e); } } for (trans, vproc) in &governance_message.voting_procedures { for (voter, voter_votes) in vproc.votes.iter() { - if let Err(e) = self.insert_voting_procedure(voter, trans, voter_votes) { + if let Err(e) = self.conway_voting.insert_voting_procedure( + block.epoch, + voter, + trans, + voter_votes, + ) { error!( "Error handling governance voting block {}, trans {}: '{}'", block.number, @@ -140,223 +141,17 @@ impl State { e ); } - self.votes_count += voter_votes.voting_procedures.len(); - } - } - } - - Ok(()) - } - - pub fn get_conway_params(&self) -> Result<&ConwayParams> { - self.conway.as_ref().ok_or_else(|| anyhow!("Conway parameters not available")) - } - - #[allow(dead_code)] - fn have_committee(&self) -> bool { - !self.conway.iter().any(|c| c.committee.is_empty()) - } - - /// Update proposals memory cache - fn insert_proposal_procedure(&mut self, epoch: u64, proc: &ProposalProcedure) -> Result<()> { - self.action_proposal_count += 1; - let prev = self.proposals.insert(proc.gov_action_id.clone(), (epoch, proc.clone())); - if let Some(prev) = prev { - return Err(anyhow!( - "Governance procedure {} already exists! New: {:?}, old: {:?}", - proc.gov_action_id, - (epoch, proc), - prev - )); - } - Ok(()) - } - - /// Update votes memory cache - fn insert_voting_procedure( - &mut self, - voter: &Voter, - transaction: &TxHash, - voter_votes: &SingleVoterVotes, - ) -> Result<()> { - for (action_id, procedure) in voter_votes.voting_procedures.iter() { - let votes = self.votes.entry(action_id.clone()).or_insert_with(|| HashMap::new()); - if let Some((prev_trans, prev_vote)) = - votes.insert(voter.clone(), (transaction.clone(), procedure.clone())) - { - // Re-voting is allowed; new vote must be treated as the proper one, - // older is to be discarded. - if tracing::enabled!(tracing::Level::DEBUG) { - debug!("Governance vote by {} for {} already registered! New: {:?}, old: {:?} from {}", - voter, action_id, procedure, prev_vote, prev_trans.encode_hex::() - ); } } } - Ok(()) - } - - /// Checks whether action_id can be considered finally accepted - fn is_finally_accepted( - &self, - voting_state: &VotingRegistrationState, - action_id: &GovActionId, - ) -> Result { - let (_epoch, proposal) = self - .proposals - .get(action_id) - .ok_or_else(|| anyhow!("action {} not found", action_id))?; - let conway_params = self.get_conway_params()?; - let threshold = voting_state.get_action_thresholds(proposal, conway_params)?; - - let votes = self.get_actual_votes(action_id); - let accepted = votes.majorizes(&threshold); - info!("Proposal {action_id}: votes {votes}, thresholds {threshold}, result {accepted}"); - - Ok(VotingOutcome { - procedure: proposal.clone(), - votes_cast: votes, - votes_threshold: threshold, - accepted, - }) - } - - /// Should be called when voting is over - fn end_voting(&mut self, action_id: &GovActionId) -> Result<()> { - self.votes.remove(&action_id); - self.proposals.remove(&action_id); Ok(()) } - /// Returns actual votes: (Pool votes, DRep votes, committee votes) - fn get_actual_votes(&self, action_id: &GovActionId) -> VotesCount { - let mut votes = VotesCount::zero(); - if let Some(all_votes) = self.votes.get(&action_id) { - for voter in all_votes.keys() { - match voter { - Voter::ConstitutionalCommitteeKey(_) => votes.committee += 1, - Voter::ConstitutionalCommitteeScript(_) => votes.committee += 1, - Voter::DRepKey(key) => { - self.drep_stake - .get(&DRepCredential::AddrKeyHash(key.clone())) - .inspect(|v| votes.drep += *v); - } - Voter::DRepScript(script) => { - self.drep_stake - .get(&DRepCredential::ScriptHash(script.clone())) - .inspect(|v| votes.drep += *v); - } - Voter::StakePoolKey(pool) => { - self.spo_stake.get(pool).inspect(|ds| votes.pool += ds.live); - } - } - } - } - votes - } - - /// Checks whether `action_id` is expired at the beginning of `new_epoch`. - fn is_expired(&self, new_epoch: u64, action_id: &GovActionId) -> Result { - info!("Checking whether {action_id} is expired at new epoch {new_epoch}"); - let (proposal_epoch, _proposal) = self - .proposals - .get(action_id) - .ok_or_else(|| anyhow!("action {} not found", action_id))?; - - Ok(proposal_epoch + self.get_conway_params()?.gov_action_lifetime as u64 <= new_epoch) - } - - fn pack_as_enact_state_elem(p: &ProposalProcedure) -> Option { - match &p.gov_action { - GovernanceAction::HardForkInitiation(_hf) => None, - GovernanceAction::TreasuryWithdrawals(_wt) => None, - GovernanceAction::Information => None, - - GovernanceAction::ParameterChange(pc) => { - Some(EnactStateElem::Params(pc.protocol_param_update.clone())) - } - GovernanceAction::NewConstitution(nc) => { - Some(EnactStateElem::Constitution(nc.new_constitution.clone())) - } - GovernanceAction::UpdateCommittee(uc) => { - Some(EnactStateElem::Committee(uc.data.clone())) - } - GovernanceAction::NoConfidence(_) => Some(EnactStateElem::NoConfidence), - } - } - - fn retrieve_withdrawal(p: &ProposalProcedure) -> Option { - if let GovernanceAction::TreasuryWithdrawals(ref action) = p.gov_action { - Some(action.clone()) - } else { - None - } - } - - /// Checks and updates action_id state at the start of new_epoch - /// If the action is accepted, returns accepted ProposalProcedure. - fn process_one_proposal( - &mut self, - new_epoch: u64, - voting_state: &VotingRegistrationState, - action_id: &GovActionId, - ) -> Result> { - let outcome = self.is_finally_accepted(voting_state, &action_id)?; - let expired = self.is_expired(new_epoch, &action_id)?; - if outcome.accepted || expired { - self.end_voting(&action_id)?; - info!( - "New epoch {new_epoch}: voting for {action_id} outcome: {}, expired: {expired}", - outcome.accepted - ); - return Ok(Some(outcome)); - } - - Ok(None) - } - - fn finalize_conway_voting( - &mut self, - new_block: &BlockInfo, - voting_state: &VotingRegistrationState, - ) -> Result> { - let mut outcome = Vec::::new(); - let actions = self.proposals.keys().map(|a| a.clone()).collect::>(); - - for action_id in actions.iter() { - info!("Epoch {}: processing action {}", new_block.epoch, action_id); - match self.process_one_proposal(new_block.epoch, &voting_state, &action_id) { - Err(e) => error!("Error processing governance {action_id}: {e}"), - Ok(None) => (), - Ok(Some(out)) if out.accepted => { - let mut action_to_perform = GovernanceOutcomeVariant::NoAction; - - if let Some(elem) = Self::pack_as_enact_state_elem(&out.procedure) { - action_to_perform = GovernanceOutcomeVariant::EnactStateElem(elem); - } else if let Some(wt) = Self::retrieve_withdrawal(&out.procedure) { - action_to_perform = GovernanceOutcomeVariant::TreasuryWithdrawal(wt); - } - - outcome.push(GovernanceOutcome { - voting: out, - action_to_perform, - }) - } - Ok(Some(out)) => outcome.push(GovernanceOutcome { - voting: out, - action_to_perform: GovernanceOutcomeVariant::NoAction, - }), - } - } - - Ok(outcome) - } - fn recalculate_voting_state(&self) -> Result { let drep_stake = self.drep_stake.iter().map(|(_dr, lov)| lov).sum(); - let committee_usize = self.get_conway_params()?.committee.members.len(); + let committee_usize = self.conway_voting.get_conway_params()?.committee.members.len(); let committee = committee_usize.try_into().or_else(|e| { Err(anyhow!( "Commitee size: conversion usize -> u64 failed, {e}" @@ -382,34 +177,42 @@ impl State { if self.current_era >= Era::Conway { let voting_state = self.recalculate_voting_state()?; - let outcome = self.finalize_conway_voting(&new_block, &voting_state)?; - let acc = outcome.iter().filter(|oc| oc.voting.accepted).count(); + let ratified = self.conway_voting.finalize_conway_voting( + &new_block, + &voting_state, + &self.drep_stake, + &self.spo_stake, + )?; + self.conway_voting.update_action_status_with_outcomes(new_block.epoch, &ratified)?; + let acc = ratified.iter().filter(|oc| oc.voting.accepted).count(); info!( "Conway voting, epoch {} ({}): {voting_state}, total {} actions, {acc} accepted", new_block.epoch, new_block.era, - outcome.len() + ratified.len() + ); + + self.conway_voting.log_conway_voting_stats(); + info!( + "Conway voting: new epoch {}, outcomes: {ratified:?}", + new_block.epoch ); - output.conway_outcomes = outcome; + output.conway_outcomes = ratified; } - return Ok(output); + + self.conway_voting.print_outcome_to_verify(&output.conway_outcomes)?; + Ok(output) } - async fn log_stats(&self) { - info!("props: {}, props_with_id: {}, votes: {}, stored proposal procedures: {}, drep stake msgs (size): {} ({})", - self.proposal_count, self.action_proposal_count, self.votes_count, self.proposals.len(), - self.drep_stake_messages_count, self.drep_stake.len(), + fn log_stats(&self) { + info!( + "{}, {}, drep stake msgs (size): {} ({})", + self.alonzo_babbage_voting.get_stats(), + self.conway_voting.get_stats(), + self.drep_stake_messages_count, + self.drep_stake.len(), ); - - for (action_id, procedure) in self.votes.iter() { - info!( - "{}{} => {}", - action_id, - [" (absent)", ""][self.proposals.contains_key(action_id) as usize], - procedure.len() - ) - } } pub async fn send(&self, block: &BlockInfo, message: GovernanceOutcomesMessage) -> Result<()> { @@ -432,12 +235,12 @@ impl State { /// Get list of actual voting proposals pub fn list_proposals(&self) -> Vec { - self.proposals.keys().cloned().collect() + self.conway_voting.proposals.keys().cloned().collect() } /// Get details for a specific proposal pub fn get_proposal(&self, id: &GovActionId) -> Option { - self.proposals.get(id).map(|(_epoch, prop)| prop.clone()) + self.conway_voting.proposals.get(id).map(|(_epoch, prop)| prop.clone()) } /// Get list of votes for a specific proposal @@ -445,7 +248,7 @@ impl State { &self, proposal_id: &GovActionId, ) -> Result> { - match self.votes.get(proposal_id) { + match self.conway_voting.votes.get(proposal_id) { Some(all_votes) => Ok(all_votes.clone()), None => Err(anyhow::anyhow!( "Governance action: {:?} not found", @@ -455,7 +258,7 @@ impl State { } pub async fn tick(&self) -> Result<()> { - self.log_stats().await; + self.log_stats(); Ok(()) } } diff --git a/modules/governance_state/src/voting_state.rs b/modules/governance_state/src/voting_state.rs index eb511243..1619b2f4 100644 --- a/modules/governance_state/src/voting_state.rs +++ b/modules/governance_state/src/voting_state.rs @@ -166,8 +166,9 @@ impl VotingRegistrationState { /// Computes necessary votes count to accept proposal `pp`, according to /// actual parameters. The result is triple of votes' thresholds (as fraction of the - /// total corresponding votes count): (Pool, DRep, Committee) - pub fn get_action_thresholds( + /// total corresponding votes count): (Pool, DRep, Committee). + /// This function computes results in full governance implementation ('plomin' sub-era). + fn get_plomin_action_thresholds( &self, pp: &ProposalProcedure, thresholds: &ConwayParams, @@ -233,4 +234,35 @@ impl VotingRegistrationState { GovernanceAction::Information => self.proportional_count(one, one, zero), } } + + /// Computes necessary votes count to accept proposal `pp`, according to + /// actual parameters. The result is triple of votes' thresholds (as fraction of the + /// total corresponding votes count): (Pool, DRep, Committee). + /// This function variant works both for bootstrap governance era (Chang sub-era), + /// and full governance (Plomin sub-era) --- this is controlled by `bootstrap` parameter. + pub fn get_action_thresholds( + &self, + pp: &ProposalProcedure, + thresholds: &ConwayParams, + bootstrap: bool, + ) -> Result { + // In case of governance bootstrap (Chang sub-era for Conway) Info, ParamChange and + // HardFork actions are allowed, with dreps voting only for Info. + if bootstrap { + match &pp.gov_action { + GovernanceAction::Information => self.get_plomin_action_thresholds(pp, thresholds), + + GovernanceAction::ParameterChange(_) | GovernanceAction::HardForkInitiation(_) => { + let mut votes_count = self.get_plomin_action_thresholds(pp, thresholds)?; + votes_count.drep = 0; + Ok(votes_count) + } + + // All other actions can never be approved + _ => Ok(VotesCount::infinity()), + } + } else { + self.get_plomin_action_thresholds(pp, thresholds) + } + } } diff --git a/modules/parameters_state/src/parameters_updater.rs b/modules/parameters_state/src/parameters_updater.rs index 2854f10d..36244d0b 100644 --- a/modules/parameters_state/src/parameters_updater.rs +++ b/modules/parameters_state/src/parameters_updater.rs @@ -246,13 +246,16 @@ impl ParametersUpdater { .params .conway .as_mut() - .ok_or_else(|| anyhow!("Shelley must present for enact state"))?; + .ok_or_else(|| anyhow!("Conway must present for enact state"))?; match &u { EnactStateElem::Params(pu) => self.update_params(&pu)?, EnactStateElem::Constitution(cu) => c.constitution = cu.clone(), EnactStateElem::Committee(cu) => Self::update_committee(&mut c.committee, cu), EnactStateElem::NoConfidence => c.committee.members.clear(), + EnactStateElem::ProtVer(pv) => { + self.sh_upd(|sp| &mut sp.protocol_version, &Some(pv.clone()))? + } } Ok(()) diff --git a/processes/replayer/src/playback.rs b/processes/replayer/src/playback.rs index 6ae07e9f..82521bf0 100644 --- a/processes/replayer/src/playback.rs +++ b/processes/replayer/src/playback.rs @@ -188,7 +188,7 @@ impl PlaybackRunner { info!("Current replay state: {stats}"); } - async fn run(&mut self) -> Result<()> { + async fn run_loop(&mut self) -> Result<()> { // Initializing message status for (topic, prefix, _epoch_bound, _skip_zero) in self.topics.clone().iter() { let msg = self @@ -228,4 +228,17 @@ impl PlaybackRunner { info!("All messages replayed, stopping"); Ok(()) } + + async fn run(&mut self) -> Result<()> { + match self.run_loop().await { + Err(e) => { + tracing::error!("Error running playback: {e}"); + Err(e) + } + Ok(()) => { + info!("Playback is over"); + Ok(()) + } + } + } }