diff --git a/Cargo.lock b/Cargo.lock index 305a05017f..453b23d7c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.7.8" @@ -407,7 +418,7 @@ version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -469,9 +480,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -495,7 +506,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if", - "constant_time_eq", + "constant_time_eq 0.3.0", ] [[package]] @@ -616,6 +627,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" @@ -731,9 +752,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" dependencies = [ "jobserver", "libc", @@ -807,6 +828,16 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -937,6 +968,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1491,6 +1528,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1758,9 +1796,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-map" @@ -2195,7 +2233,7 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "grovedb" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" dependencies = [ "bincode 2.0.0-rc.3", "bitvec", @@ -2213,12 +2251,13 @@ dependencies = [ "nohash-hasher", "tempfile", "thiserror", + "zip-extensions", ] [[package]] name = "grovedb-costs" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" dependencies = [ "integer-encoding", "intmap", @@ -2228,7 +2267,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" dependencies = [ "blake3", "byteorder", @@ -2251,12 +2290,12 @@ dependencies = [ [[package]] name = "grovedb-path" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" [[package]] name = "grovedb-storage" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" dependencies = [ "blake3", "grovedb-costs", @@ -2275,7 +2314,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "1.0.0-rc.2" -source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#8664b723330cc3850edbebd5f336b982f29ed23c" +source = "git+https://github.com/dashpay/grovedb?branch=feat/QuerySumTree#1696c2ef47a674be5ec213b9ff723afe4c45fb90" dependencies = [ "hex", "itertools 0.12.1", @@ -2408,6 +2447,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -2645,6 +2693,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.13" @@ -2907,7 +2964,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -3421,7 +3478,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -3494,12 +3551,35 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -3955,7 +4035,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "memchr", "unicase", ] @@ -4051,7 +4131,7 @@ version = "11.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -4080,7 +4160,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -4318,7 +4398,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "itoa", "libc", @@ -4469,7 +4549,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4516,9 +4596,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] @@ -4546,9 +4626,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "indexmap 2.2.6", "itoa", @@ -4632,6 +4712,17 @@ dependencies = [ "syn 2.0.68", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -4809,6 +4900,8 @@ dependencies = [ "drive", "futures", "hex", + "platform-serialization", + "platform-serialization-derive", "platform-version", "rand", "rocksdb", @@ -4940,7 +5033,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0682e006dd35771e392a6623ac180999a9a854b1d4a6c12fb2e804941c2b1f58" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cap-fs-ext", "cap-std", "fd-lock", @@ -4982,8 +5075,8 @@ dependencies = [ [[package]] name = "tenderdash-abci" -version = "0.14.0-dev.12" -source = "git+https://github.com/dashpay/rs-tenderdash-abci#3048c2ecdf7a2bd023634082a85635c76db22bf0" +version = "1.0.0-dev.1" +source = "git+https://github.com/dashpay/rs-tenderdash-abci#cfb6ae41b4100c4b0af88ad1a201c5266a2ddc8c" dependencies = [ "bytes", "futures", @@ -5004,8 +5097,8 @@ dependencies = [ [[package]] name = "tenderdash-proto" -version = "0.14.0-dev.12" -source = "git+https://github.com/dashpay/rs-tenderdash-abci#3048c2ecdf7a2bd023634082a85635c76db22bf0" +version = "1.0.0-dev.1" +source = "git+https://github.com/dashpay/rs-tenderdash-abci#cfb6ae41b4100c4b0af88ad1a201c5266a2ddc8c" dependencies = [ "bytes", "chrono", @@ -5024,8 +5117,8 @@ dependencies = [ [[package]] name = "tenderdash-proto-compiler" -version = "0.14.0-dev.12" -source = "git+https://github.com/dashpay/rs-tenderdash-abci#3048c2ecdf7a2bd023634082a85635c76db22bf0" +version = "1.0.0-dev.1" +source = "git+https://github.com/dashpay/rs-tenderdash-abci#cfb6ae41b4100c4b0af88ad1a201c5266a2ddc8c" dependencies = [ "fs_extra", "prost-build 0.12.6", @@ -5156,9 +5249,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -5621,9 +5714,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea73390fe27785838dcbf75b91b1d84799e28f1ce71e6f372a5dc2200c80de5" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", "rand", @@ -5720,7 +5813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d888b611fee7d273dd057dc009d2dd3132736f36710ffd65657ac83628d1e3b" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.6.0", "cap-rand", "cap-std", "io-extras", @@ -6108,7 +6201,7 @@ checksum = "0afb26cd3269289bb314a361ff0a6685e5ce793b62181a9fe3f81ace15051697" dependencies = [ "anyhow", "async-trait", - "bitflags 2.5.0", + "bitflags 2.6.0", "thiserror", "tracing", "wasmtime", @@ -6345,7 +6438,7 @@ version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "windows-sys 0.52.0", ] @@ -6421,10 +6514,46 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ + "aes", "byteorder", + "bzip2", + "constant_time_eq 0.1.5", "crc32fast", "crossbeam-utils", "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zip-extensions" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecf62554c4ff96bce01a7ef123d160c3ffe9180638820f8b4d545c65b221b8c" +dependencies = [ + "zip", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", ] [[package]] diff --git a/packages/dapi-grpc/Cargo.toml b/packages/dapi-grpc/Cargo.toml index 21e63ea3e3..085300789d 100644 --- a/packages/dapi-grpc/Cargo.toml +++ b/packages/dapi-grpc/Cargo.toml @@ -42,7 +42,7 @@ tonic = { version = "0.11", features = [ serde = { version = "1.0.197", optional = true, features = ["derive"] } serde_bytes = { version = "0.11.12", optional = true } serde_json = { version = "1.0", optional = true } -tenderdash-proto = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "0.14.0-dev.12", default-features = false } +tenderdash-proto = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "1.0.0-dev.1", default-features = false } dapi-grpc-macros = { path = "../rs-dapi-grpc-macros" } platform-version = { path = "../rs-platform-version" } diff --git a/packages/rs-dpp/src/voting/contender_structs/mod.rs b/packages/rs-dpp/src/voting/contender_structs/mod.rs index 61e5ed2d58..3d7756a5ad 100644 --- a/packages/rs-dpp/src/voting/contender_structs/mod.rs +++ b/packages/rs-dpp/src/voting/contender_structs/mod.rs @@ -10,6 +10,7 @@ use platform_value::string_encoding::Encoding; use platform_value::Identifier; use platform_version::version::PlatformVersion; use std::fmt; +use std::fmt::format; pub use contender::v0::{ContenderV0, ContenderWithSerializedDocumentV0}; pub use contender::{Contender, ContenderWithSerializedDocument}; @@ -38,15 +39,15 @@ pub struct FinalizedContenderWithSerializedDocument { pub struct FinalizedResourceVoteChoicesWithVoterInfo { /// The resource vote choice. pub resource_vote_choice: ResourceVoteChoice, - /// The pro_tx_hashes of the voters for this contender. - pub voters: Vec, + /// The pro_tx_hashes of the voters for this contender along with their strength + pub voters: Vec<(Identifier, u8)>, } impl fmt::Display for FinalizedResourceVoteChoicesWithVoterInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let voters_str: Vec = self .voters .iter() - .map(|v| v.to_string(Encoding::Base58)) + .map(|(id, strength)| format!("{}:{}", id, strength)) .collect(); write!( f, diff --git a/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/mod.rs b/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/mod.rs index e50b5c5477..12eb2b400c 100644 --- a/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/mod.rs +++ b/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/mod.rs @@ -166,7 +166,7 @@ impl ContestedDocumentVotePollStoredInfoV0Getters for ContestedDocumentVotePollS } } - fn last_locked_voters(&self) -> Option> { + fn last_locked_voters(&self) -> Option> { match self { ContestedDocumentVotePollStoredInfo::V0(v0) => v0.last_locked_voters(), } @@ -178,7 +178,7 @@ impl ContestedDocumentVotePollStoredInfoV0Getters for ContestedDocumentVotePollS } } - fn last_abstain_voters(&self) -> Option> { + fn last_abstain_voters(&self) -> Option> { match self { ContestedDocumentVotePollStoredInfo::V0(v0) => v0.last_abstain_voters(), } diff --git a/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/v0/mod.rs b/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/v0/mod.rs index dc1d425daa..b7fe690994 100644 --- a/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/v0/mod.rs +++ b/packages/rs-dpp/src/voting/vote_info_storage/contested_document_vote_poll_stored_info/v0/mod.rs @@ -147,11 +147,11 @@ pub trait ContestedDocumentVotePollStoredInfoV0Getters { fn last_locked_votes(&self) -> Option; - fn last_locked_voters(&self) -> Option>; + fn last_locked_voters(&self) -> Option>; fn last_abstain_votes(&self) -> Option; - fn last_abstain_voters(&self) -> Option>; + fn last_abstain_voters(&self) -> Option>; fn contender_votes_in_vec_of_contender_with_serialized_document( &self, ) -> Option>; @@ -218,12 +218,19 @@ impl ContestedDocumentVotePollStoredInfoV0Getters for ContestedDocumentVotePollS .filter(|choice| { matches!(choice.resource_vote_choice, ResourceVoteChoice::Lock) }) - .map(|choice| choice.voters.len() as u32) + .map(|choice| { + let sum: u32 = choice + .voters + .iter() + .map(|(_, strength)| *strength as u32) + .sum(); + sum + }) .sum() }) } - fn last_locked_voters(&self) -> Option> { + fn last_locked_voters(&self) -> Option> { self.last_resource_vote_choices() .map(|resource_vote_choices| { resource_vote_choices @@ -244,12 +251,19 @@ impl ContestedDocumentVotePollStoredInfoV0Getters for ContestedDocumentVotePollS .filter(|choice| { matches!(choice.resource_vote_choice, ResourceVoteChoice::Abstain) }) - .map(|choice| choice.voters.len() as u32) + .map(|choice| { + let sum: u32 = choice + .voters + .iter() + .map(|(_, strength)| *strength as u32) + .sum(); + sum + }) .sum() }) } - fn last_abstain_voters(&self) -> Option> { + fn last_abstain_voters(&self) -> Option> { self.last_resource_vote_choices() .map(|resource_vote_choices| { resource_vote_choices @@ -273,11 +287,16 @@ impl ContestedDocumentVotePollStoredInfoV0Getters for ContestedDocumentVotePollS if let ResourceVoteChoice::TowardsIdentity(identity_id) = &choice.resource_vote_choice { + let vote_tally: u32 = choice + .voters + .iter() + .map(|(_, strength)| *strength as u32) + .sum(); Some( ContenderWithSerializedDocumentV0 { identity_id: *identity_id, serialized_document: None, - vote_tally: Some(choice.voters.len() as u32), + vote_tally: Some(vote_tally), } .into(), ) diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 40b131f212..5d75959961 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -55,7 +55,7 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features = "tracing-log", ], optional = false } atty = { version = "0.2.14", optional = false } -tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "0.14.0-dev.12", features = [ +tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "1.0.0-dev.1", features = [ "grpc", ] } lazy_static = "1.4.0" diff --git a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs index 4b6d62ef4a..f77c5ea16a 100644 --- a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs @@ -294,6 +294,17 @@ where platform_version, )?; + // Run all dao platform events, such as vote tallying and distribution of contested documents + // This must be done before state transition processing + // Otherwise we would expect a proof after a successful vote that has since been cleaned up. + self.run_dao_platform_events( + &block_info, + last_committed_platform_state, + &block_platform_state, + Some(transaction), + platform_version, + )?; + // Process transactions let state_transitions_result = self.process_raw_state_transitions( raw_state_transitions, @@ -313,16 +324,6 @@ where platform_version, )?; - // Run all dao platform events, such as vote tallying and distribution of contested documents - - self.run_dao_platform_events( - &block_info, - last_committed_platform_state, - &block_platform_state, - Some(transaction), - platform_version, - )?; - // Create a new block execution context let mut block_execution_context: BlockExecutionContext = diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v0/mod.rs index e7f03575d9..af1a185e84 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/execute_event/v0/mod.rs @@ -59,6 +59,7 @@ where )?) } ExecutionEvent::PaidFromAssetLockWithoutIdentity { .. } + | ExecutionEvent::PaidFixedCost { .. } | ExecutionEvent::Free { .. } => None, }; @@ -154,6 +155,29 @@ where )) } } + ExecutionEvent::PaidFixedCost { + operations, + fees_to_add_to_pool, + } => { + if consensus_errors.is_empty() { + self.drive + .apply_drive_operations( + operations, + true, + block_info, + Some(transaction), + platform_version, + ) + .map_err(Error::Drive)?; + + Ok(SuccessfulPaidExecution( + None, + FeeResult::default_with_fees(0, fees_to_add_to_pool), + )) + } else { + Ok(UnpaidConsensusExecutionError(consensus_errors)) + } + } ExecutionEvent::Free { operations } => { self.drive .apply_drive_operations( diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v0/mod.rs index 27e56dd02f..5f9000326f 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v0/mod.rs @@ -146,7 +146,8 @@ where )) } } - ExecutionEvent::Free { .. } + ExecutionEvent::PaidFixedCost { .. } + | ExecutionEvent::Free { .. } | ExecutionEvent::PaidFromAssetLockWithoutIdentity { .. } => Ok( ConsensusValidationResult::new_with_data(FeeResult::default()), ), diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/mod.rs index 2b27616301..6bb08f7fac 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/mod.rs @@ -1,6 +1,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; use dpp::block::block_info::BlockInfo; use dpp::version::PlatformVersion; @@ -15,6 +16,7 @@ where /// Checks for ended vote polls pub(in crate::execution) fn check_for_ended_vote_polls( &self, + block_platform_state: &PlatformState, block_info: &BlockInfo, transaction: TransactionArg, platform_version: &PlatformVersion, @@ -25,7 +27,12 @@ where .voting .check_for_ended_vote_polls { - 0 => self.check_for_ended_vote_polls_v0(block_info, transaction, platform_version), + 0 => self.check_for_ended_vote_polls_v0( + block_platform_state, + block_info, + transaction, + platform_version, + ), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "check_for_ended_vote_polls".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs index 761d113fba..ca84b63729 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/check_for_ended_vote_polls/v0/mod.rs @@ -1,5 +1,6 @@ use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; use dpp::block::block_info::BlockInfo; use dpp::document::DocumentV0Getters; @@ -23,6 +24,7 @@ where #[inline(always)] pub(super) fn check_for_ended_vote_polls_v0( &self, + block_platform_state: &PlatformState, block_info: &BlockInfo, transaction: TransactionArg, platform_version: &PlatformVersion, @@ -69,27 +71,23 @@ where let (contenders_with_votes, contenders_with_no_votes) : (Vec<_>, Vec<_>) = sorted_contenders.iter().partition(|a| a.final_vote_tally > 0); - let (restrict_to_only_fetch_contenders, mut other_contenders) = if contenders_with_no_votes.is_empty() + let fetch_contenders = contenders_with_votes + .iter() + .map(|contender| contender.identity_id) + .collect::>(); + let mut other_contenders = if contenders_with_no_votes.is_empty() { - (None, BTreeMap::new()) + BTreeMap::new() } else { - // Collect all identity_ids from contenders_with_votes - let restrict_to_only_fetch_contenders = Some( - contenders_with_votes - .iter() - .map(|contender| contender.identity_id) - .collect::>(), - ); - // Other contenders are only those with no votes - (restrict_to_only_fetch_contenders, contenders_with_no_votes.into_iter().map(|contender| (TowardsIdentity(contender.identity_id), vec![])).collect()) + contenders_with_no_votes.into_iter().map(|contender| (TowardsIdentity(contender.identity_id), vec![])).collect() }; // We need to get the votes of the sorted contenders let mut identifiers_voting_for_contenders = self.drive.fetch_identities_voting_for_contenders( &resolved_contested_document_resource_vote_poll, - restrict_to_only_fetch_contenders, + fetch_contenders, true, transaction, platform_version, @@ -156,6 +154,7 @@ where }; // We want to keep a record of how everyone voted self.keep_record_of_finished_contested_resource_vote_poll( + block_platform_state, block_info, &resolved_contested_document_resource_vote_poll, &identifiers_voting_for_contenders, diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/mod.rs index 9e8bb69e0e..b5743cbf50 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/mod.rs @@ -1,6 +1,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; use dpp::block::block_info::BlockInfo; use dpp::identifier::Identifier; @@ -20,6 +21,7 @@ where /// Keeps a record of the vote poll after it has finished pub(in crate::execution) fn keep_record_of_finished_contested_resource_vote_poll( &self, + block_platform_state: &PlatformState, block_info: &BlockInfo, vote_poll: &ContestedDocumentResourceVotePollWithContractInfo, contender_votes: &BTreeMap>, @@ -34,6 +36,7 @@ where .keep_record_of_finished_contested_resource_vote_poll { 0 => self.keep_record_of_finished_contested_resource_vote_poll_v0( + block_platform_state, block_info, vote_poll, contender_votes, diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/v0/mod.rs index 2e1cbd0eb9..1b3214a5fe 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/keep_record_of_vote_poll/v0/mod.rs @@ -1,8 +1,15 @@ use crate::error::Error; use crate::platform_types::platform::Platform; +use crate::platform_types::platform_state::v0::PlatformStateV0Methods; +use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; +use dashcore_rpc::dashcore_rpc_json::MasternodeType; use dpp::block::block_info::BlockInfo; +use dpp::consensus::state::voting::masternode_not_found_error::MasternodeNotFoundError; +use dpp::dashcore::hashes::Hash; +use dpp::dashcore::ProTxHash; use dpp::identifier::Identifier; +use dpp::prelude::ConsensusValidationResult; use dpp::version::PlatformVersion; use dpp::voting::contender_structs::FinalizedResourceVoteChoicesWithVoterInfo; use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; @@ -20,6 +27,7 @@ where #[inline(always)] pub(super) fn keep_record_of_finished_contested_resource_vote_poll_v0( &self, + block_platform_state: &PlatformState, block_info: &BlockInfo, vote_poll: &ContestedDocumentResourceVotePollWithContractInfo, contender_votes: &BTreeMap>, @@ -29,12 +37,30 @@ where ) -> Result<(), Error> { let finalized_resource_vote_choices_with_voter_infos = contender_votes .iter() - .map( - |(resource_vote_choice, voters)| FinalizedResourceVoteChoicesWithVoterInfo { - resource_vote_choice: resource_vote_choice.clone(), - voters: voters.clone(), - }, - ) + .map(|(resource_vote_choice, voters)| { + let full_masternode_list = block_platform_state.full_masternode_list(); + let voters = voters + .iter() + .map(|pro_tx_hash_identifier| { + let strength = if let Some(masternode) = full_masternode_list.get( + &ProTxHash::from_byte_array(pro_tx_hash_identifier.to_buffer()), + ) { + match masternode.node_type { + MasternodeType::Regular => 1, + MasternodeType::Evo => 4, + } + } else { + 0 + }; + (*pro_tx_hash_identifier, strength) + }) + .collect(); + + FinalizedResourceVoteChoicesWithVoterInfo { + resource_vote_choice: *resource_vote_choice, + voters, + } + }) .collect(); let stored_info_from_disk = self .drive diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs index e8375654df..817067f58c 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs @@ -29,7 +29,12 @@ where // Check for any vote polls that might have ended - self.check_for_ended_vote_polls(block_info, transaction, platform_version)?; + self.check_for_ended_vote_polls( + block_platform_state, + block_info, + transaction, + platform_version, + )?; Ok(()) } diff --git a/packages/rs-drive-abci/src/execution/types/execution_event/mod.rs b/packages/rs-drive-abci/src/execution/types/execution_event/mod.rs index 133aafb3a5..f3d3da8aaa 100644 --- a/packages/rs-drive-abci/src/execution/types/execution_event/mod.rs +++ b/packages/rs-drive-abci/src/execution/types/execution_event/mod.rs @@ -36,6 +36,13 @@ pub(in crate::execution) enum ExecutionEvent<'a> { /// the fee multiplier that the user agreed to, 0 means 100% of the base fee, 1 means 101% user_fee_increase: UserFeeIncrease, }, + /// A drive event that has a fixed cost that will be taken out in the operations + PaidFixedCost { + /// the operations that should be performed + operations: Vec>, + /// fees to add + fees_to_add_to_pool: Credits, + }, /// A drive event that is paid from an asset lock PaidFromAssetLock { /// The identity requesting the event @@ -177,7 +184,14 @@ impl<'a> ExecutionEvent<'a> { StateTransitionAction::MasternodeVoteAction(_) => { let operations = action.into_high_level_drive_operations(epoch, platform_version)?; - Ok(ExecutionEvent::Free { operations }) + + Ok(ExecutionEvent::PaidFixedCost { + operations, + fees_to_add_to_pool: platform_version + .fee_version + .vote_resolution_fund_fees + .contested_document_single_vote_cost, + }) } _ => { let user_fee_increase = action.user_fee_increase(); diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs index 7db6b4eadb..abc066b643 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/documents_batch/mod.rs @@ -929,10 +929,13 @@ mod tests { fast_forward_to_block(&platform, 2_000_000_000, 900); //more than two weeks + let platform_state = platform.state.load(); + let transaction = platform.drive.grove.start_transaction(); platform .check_for_ended_vote_polls( + &platform_state, &BlockInfo { time_ms: 2_000_000_000, height: 900, diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index f1dacfd141..77173b24cf 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -30,7 +30,7 @@ impl StateTransitionActionTransformerV0 for MasternodeVoteTransition { &self, platform: &PlatformRef, _block_info: &BlockInfo, - _validation_mode: ValidationMode, + validation_mode: ValidationMode, _execution_context: &mut StateTransitionExecutionContext, tx: TransactionArg, ) -> Result, Error> { @@ -44,7 +44,7 @@ impl StateTransitionActionTransformerV0 for MasternodeVoteTransition { .transform_into_action { 0 => self - .transform_into_action_v0(platform, tx, platform_version) + .transform_into_action_v0(platform, validation_mode, tx, platform_version) .map(|result| result.map(|action| action.into())), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "masternode votes state transition: transform_into_action".to_string(), @@ -920,6 +920,104 @@ mod tests { } } + #[test] + fn test_non_existing_end_index_value_many_values() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (_contender_1, _contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + let domain = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type"); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let encoded_non_existing_value_1 = + bincode::encode_to_vec(Value::Text("cashcash".to_string()), config) + .expect("expected to encode value"); + + let encoded_non_existing_value_2 = + bincode::encode_to_vec(Value::Text("cennnn".to_string()), config) + .expect("expected to encode value"); + + let index_name = "parentNameAndLabel".to_string(); + + { + let query_validation_error = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![], + end_index_values: vec![ + encoded_non_existing_value_1.clone(), + encoded_non_existing_value_2.clone(), + ], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .first_error() + .map(|e| e.to_string()); + + assert_eq!(query_validation_error, Some("query syntax error: incorrect index values error: too many end index values were provided".to_string())); + } + + { + let query_validation_error = platform + .query_contested_resources( + GetContestedResourcesRequest { + version: Some(get_contested_resources_request::Version::V0( + GetContestedResourcesRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: domain.name().clone(), + index_name: index_name.clone(), + start_index_values: vec![], + end_index_values: vec![ + encoded_non_existing_value_1.clone(), + encoded_non_existing_value_2.clone(), + ], + start_at_value_info: None, + count: None, + order_ascending: true, + prove: true, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .first_error() + .map(|e| e.to_string()); + + assert_eq!(query_validation_error, Some("query syntax error: incorrect index values error: too many end index values were provided".to_string())); + } + } + #[test] #[ignore] // Currently will have an issue due to a grovedb bug fn test_limit() { @@ -5333,7 +5431,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -5535,7 +5638,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -5737,7 +5845,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -5934,7 +6047,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -6122,7 +6240,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -6324,7 +6447,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform @@ -6426,6 +6554,372 @@ mod tests { assert_eq!(locking, Some(60)); } } + + #[test] + fn test_new_vote_after_document_distribution() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (contender_1, contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + perform_votes_multi( + &mut platform, + dpns_contract.as_ref(), + vec![ + (TowardsIdentity(contender_1.id()), 50), + (TowardsIdentity(contender_2.id()), 5), + (ResourceVoteChoice::Abstain, 10), + (ResourceVoteChoice::Lock, 3), + ], + "quantum", + 10, + platform_version, + ); + + let platform_state = platform.state.load(); + + let (contenders, abstaining, locking, finished_vote_info) = get_vote_states( + &platform, + &platform_state, + &dpns_contract, + "quantum", + None, + true, + None, + ResultType::DocumentsAndVoteTally, + platform_version, + ); + + assert_eq!(finished_vote_info, None); + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_ne!(first_contender.document(), second_contender.document()); + + assert_eq!(first_contender.identity_id(), contender_1.id()); + + assert_eq!(second_contender.identity_id(), contender_2.id()); + + assert_eq!(first_contender.vote_tally(), Some(50)); + + assert_eq!(second_contender.vote_tally(), Some(5)); + + assert_eq!(abstaining, Some(10)); + + assert_eq!(locking, Some(3)); + + let mut platform_state = (**platform_state).clone(); + + let block_info = BlockInfo { + time_ms: 1_209_900_000, //2 weeks and 300s + height: 10000, + core_height: 42, + epoch: Default::default(), + }; + + platform_state.set_last_committed_block_info(Some( + ExtendedBlockInfoV0 { + basic_info: block_info, + app_hash: platform.drive.grove.root_hash(None).unwrap().unwrap(), + quorum_hash: [0u8; 32], + block_id_hash: [0u8; 32], + signature: [0u8; 96], + round: 0, + } + .into(), + )); + + platform.state.store(Arc::new(platform_state)); + + let platform_state = platform.state.load(); + + let transaction = platform.drive.grove.start_transaction(); + + platform + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) + .expect("expected to check for ended vote polls"); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // At this point the document should have been awarded to contender 1. + + // now let's try to do another vote + + let (pro_tx_hash, _masternode, signer, voting_key) = + setup_masternode_identity(&mut platform, 5000, platform_version); + + let platform_state = platform.state.load(); + + perform_vote( + &mut platform, + &platform_state, + dpns_contract.as_ref(), + TowardsIdentity(contender_1.id()), + "quantum", + &signer, + pro_tx_hash, + &voting_key, + 2, + Some("VotePoll ContestedDocumentResourceVotePoll(ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }) not available for voting: Awarded(BjNejy4r9QAvLHpQ9Yq6yRMgNymeGZ46d48fJxJbMrfW)"), + platform_version, + ); + + { + let (contenders, abstaining, locking, finished_vote_info) = get_vote_states( + &platform, + &platform_state, + &dpns_contract, + "quantum", + None, + true, + None, + ResultType::DocumentsAndVoteTally, + platform_version, + ); + + assert_eq!( + finished_vote_info, + Some(FinishedVoteInfo { + finished_vote_outcome: + finished_vote_info::FinishedVoteOutcome::TowardsIdentity as i32, + won_by_identity_id: Some(contender_1.id().to_vec()), + finished_at_block_height: 10000, + finished_at_core_block_height: 42, + finished_at_block_time_ms: 1209900000, + finished_at_epoch: 0 + }) + ); + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_eq!(first_contender.document(), &None); + + assert_eq!(second_contender.document(), &None); + + assert_eq!(first_contender.identity_id(), contender_1.id()); + + assert_eq!(second_contender.identity_id(), contender_2.id()); + + assert_eq!(first_contender.vote_tally(), Some(50)); + + assert_eq!(second_contender.vote_tally(), Some(5)); + + assert_eq!(abstaining, Some(10)); + + assert_eq!(locking, Some(3)); + } + } + + #[test] + fn test_new_vote_after_lock() { + let platform_version = PlatformVersion::latest(); + let mut platform = TestPlatformBuilder::new() + .build_with_mock_rpc() + .set_genesis_state(); + + let platform_state = platform.state.load(); + + let (contender_1, contender_2, dpns_contract) = create_dpns_name_contest( + &mut platform, + &platform_state, + 7, + "quantum", + platform_version, + ); + + perform_votes_multi( + &mut platform, + dpns_contract.as_ref(), + vec![ + (TowardsIdentity(contender_1.id()), 2), + (TowardsIdentity(contender_2.id()), 5), + (ResourceVoteChoice::Abstain, 10), + (ResourceVoteChoice::Lock, 50), + ], + "quantum", + 10, + platform_version, + ); + + let platform_state = platform.state.load(); + + let (contenders, abstaining, locking, finished_vote_info) = get_vote_states( + &platform, + &platform_state, + &dpns_contract, + "quantum", + None, + true, + None, + ResultType::DocumentsAndVoteTally, + platform_version, + ); + + assert_eq!(finished_vote_info, None); + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_ne!(first_contender.document(), second_contender.document()); + + assert_eq!(first_contender.identity_id(), contender_1.id()); + + assert_eq!(second_contender.identity_id(), contender_2.id()); + + assert_eq!(first_contender.vote_tally(), Some(2)); + + assert_eq!(second_contender.vote_tally(), Some(5)); + + assert_eq!(abstaining, Some(10)); + + assert_eq!(locking, Some(50)); + + let mut platform_state = (**platform_state).clone(); + + let block_info = BlockInfo { + time_ms: 1_209_900_000, //2 weeks and 300s + height: 10000, + core_height: 42, + epoch: Default::default(), + }; + + platform_state.set_last_committed_block_info(Some( + ExtendedBlockInfoV0 { + basic_info: block_info, + app_hash: platform.drive.grove.root_hash(None).unwrap().unwrap(), + quorum_hash: [0u8; 32], + block_id_hash: [0u8; 32], + signature: [0u8; 96], + round: 0, + } + .into(), + )); + + platform.state.store(Arc::new(platform_state)); + + let platform_state = platform.state.load(); + + let transaction = platform.drive.grove.start_transaction(); + + platform + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) + .expect("expected to check for ended vote polls"); + + platform + .drive + .grove + .commit_transaction(transaction) + .unwrap() + .expect("expected to commit transaction"); + + // At this point the document should have been awarded to contender 1. + + // now let's try to do another vote + + let (pro_tx_hash, _masternode, signer, voting_key) = + setup_masternode_identity(&mut platform, 5000, platform_version); + + let platform_state = platform.state.load(); + + perform_vote( + &mut platform, + &platform_state, + dpns_contract.as_ref(), + TowardsIdentity(contender_1.id()), + "quantum", + &signer, + pro_tx_hash, + &voting_key, + 2, + Some("VotePoll ContestedDocumentResourceVotePoll(ContestedDocumentResourceVotePoll { contract_id: GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec, document_type_name: domain, index_name: parentNameAndLabel, index_values: [string dash, string quantum] }) not available for voting: Locked"), + platform_version, + ); + + { + let (contenders, abstaining, locking, finished_vote_info) = get_vote_states( + &platform, + &platform_state, + &dpns_contract, + "quantum", + None, + true, + None, + ResultType::DocumentsAndVoteTally, + platform_version, + ); + + assert_eq!( + finished_vote_info, + Some(FinishedVoteInfo { + finished_vote_outcome: finished_vote_info::FinishedVoteOutcome::Locked + as i32, + won_by_identity_id: None, + finished_at_block_height: 10000, + finished_at_core_block_height: 42, + finished_at_block_time_ms: 1209900000, + finished_at_epoch: 0 + }) + ); + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_eq!(first_contender.document(), &None); + + assert_eq!(second_contender.document(), &None); + + assert_eq!(first_contender.identity_id(), contender_1.id()); + + assert_eq!(second_contender.identity_id(), contender_2.id()); + + assert_eq!(first_contender.vote_tally(), Some(2)); + + assert_eq!(second_contender.vote_tally(), Some(5)); + + assert_eq!(abstaining, Some(10)); + + assert_eq!(locking, Some(50)); + } + } } mod changing_vote { use super::*; @@ -6805,7 +7299,12 @@ mod tests { let transaction = platform.drive.grove.start_transaction(); platform - .check_for_ended_vote_polls(&block_info, Some(&transaction), platform_version) + .check_for_ended_vote_polls( + &platform_state, + &block_info, + Some(&transaction), + platform_version, + ) .expect("expected to check for ended vote polls"); platform diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/transform_into_action/v0/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/transform_into_action/v0/mod.rs index 6e17495d3c..b49fbe56ea 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/transform_into_action/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/transform_into_action/v0/mod.rs @@ -14,6 +14,7 @@ use dpp::state_transition::masternode_vote_transition::accessors::MasternodeVote use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition; use drive::state_transition_action::identity::masternode_vote::MasternodeVoteTransitionAction; +use crate::execution::validation::state_transition::ValidationMode; use crate::platform_types::platform_state::v0::PlatformStateV0Methods; use dpp::version::PlatformVersion; use dpp::voting::vote_polls::VotePoll; @@ -26,6 +27,7 @@ pub(in crate::execution::validation::state_transition::state_transitions::master fn transform_into_action_v0( &self, platform: &PlatformRef, + validation_mode: ValidationMode, tx: TransactionArg, platform_version: &PlatformVersion, ) -> Result, Error>; @@ -35,66 +37,70 @@ impl MasternodeVoteStateTransitionTransformIntoActionValidationV0 for Masternode fn transform_into_action_v0( &self, platform: &PlatformRef, + validation_mode: ValidationMode, tx: TransactionArg, platform_version: &PlatformVersion, ) -> Result, Error> { let mut previous_resource_vote_choice_to_remove = None; - // Before we transform into action we want to make sure that we have not yet voted - match self.vote() { - Vote::ResourceVote(resource_vote) => { - match resource_vote.vote_poll() { - VotePoll::ContestedDocumentResourceVotePoll(vote_poll) => { - let vote_id = vote_poll.unique_id()?; - let maybe_existing_resource_vote_choice = - platform.drive.fetch_identity_contested_resource_vote( - self.pro_tx_hash(), - vote_id, - tx, - &mut vec![], - platform_version, - )?; - if let Some((existing_resource_vote_choice, previous_vote_count)) = - maybe_existing_resource_vote_choice - { - if existing_resource_vote_choice == resource_vote.resource_vote_choice() + if validation_mode != ValidationMode::NoValidation { + // Before we transform into action we want to make sure that we have not yet voted + match self.vote() { + Vote::ResourceVote(resource_vote) => { + match resource_vote.vote_poll() { + VotePoll::ContestedDocumentResourceVotePoll(vote_poll) => { + let vote_id = vote_poll.unique_id()?; + let maybe_existing_resource_vote_choice = + platform.drive.fetch_identity_contested_resource_vote( + self.pro_tx_hash(), + vote_id, + tx, + &mut vec![], + platform_version, + )?; + if let Some((existing_resource_vote_choice, previous_vote_count)) = + maybe_existing_resource_vote_choice { - // We are submitting a vote for something we already have - return Ok(ConsensusValidationResult::new_with_error( - ConsensusError::StateError( - StateError::MasternodeVoteAlreadyPresentError( - MasternodeVoteAlreadyPresentError::new( - self.pro_tx_hash(), - resource_vote.vote_poll().clone(), + if existing_resource_vote_choice + == resource_vote.resource_vote_choice() + { + // We are submitting a vote for something we already have + return Ok(ConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::MasternodeVoteAlreadyPresentError( + MasternodeVoteAlreadyPresentError::new( + self.pro_tx_hash(), + resource_vote.vote_poll().clone(), + ), ), ), - ), - )); - } else if previous_vote_count - >= platform_version - .dpp - .validation - .voting - .votes_allowed_per_masternode - { - // We are submitting a vote for something we already have - return Ok(ConsensusValidationResult::new_with_error( - ConsensusError::StateError( - StateError::MasternodeVotedTooManyTimesError( - MasternodeVotedTooManyTimesError::new( - self.pro_tx_hash(), - previous_vote_count, - platform_version - .dpp - .validation - .voting - .votes_allowed_per_masternode, + )); + } else if previous_vote_count + >= platform_version + .dpp + .validation + .voting + .votes_allowed_per_masternode + { + // We are submitting a vote for something we already have + return Ok(ConsensusValidationResult::new_with_error( + ConsensusError::StateError( + StateError::MasternodeVotedTooManyTimesError( + MasternodeVotedTooManyTimesError::new( + self.pro_tx_hash(), + previous_vote_count, + platform_version + .dpp + .validation + .voting + .votes_allowed_per_masternode, + ), ), ), - ), - )); - } else { - previous_resource_vote_choice_to_remove = - Some((existing_resource_vote_choice, previous_vote_count)); + )); + } else { + previous_resource_vote_choice_to_remove = + Some((existing_resource_vote_choice, previous_vote_count)); + } } } } diff --git a/packages/rs-drive-abci/src/query/proofs/v0/mod.rs b/packages/rs-drive-abci/src/query/proofs/v0/mod.rs index 4bd5bdf9c4..e82980bdf3 100644 --- a/packages/rs-drive-abci/src/query/proofs/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/proofs/v0/mod.rs @@ -96,7 +96,7 @@ impl Platform { contested_status, }) }) - .collect::, QueryError>>()); + .collect::, QueryError>>()); let vote_queries = check_validation_result_with_data!(votes .into_iter() @@ -169,9 +169,13 @@ impl Platform { mod tests { use super::*; use crate::query::tests::{assert_invalid_identifier, setup_platform}; + use dapi_grpc::platform::v0::get_proofs_request::get_proofs_request_v0::vote_status_request::ContestedResourceVoteStatusRequest; use dapi_grpc::platform::v0::get_proofs_request::get_proofs_request_v0::{ - ContractRequest, DocumentRequest, IdentityRequest, + ContractRequest, DocumentRequest, IdentityRequest, VoteStatusRequest, }; + use dpp::data_contract::accessors::v0::DataContractV0Getters; + use dpp::platform_value::Value; + use dpp::util::strings::convert_to_homograph_safe_chars; #[test] fn test_invalid_identity_ids() { @@ -318,6 +322,58 @@ mod tests { }) if !proof.grovedb_proof.is_empty())); } + #[test] + fn test_proof_of_absence_of_vote() { + let (platform, state, version) = setup_platform(false); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + let serialized_index_values = [ + Value::Text("dash".to_string()), + Value::Text(convert_to_homograph_safe_chars("quantum")), + ] + .iter() + .map(|value| { + bincode::encode_to_vec(value, config).expect("expected to encode value in path") + }) + .collect(); + + let request = GetProofsRequestV0 { + identities: vec![], + contracts: vec![], + documents: vec![], + votes: vec![VoteStatusRequest { + request_type: Some(RequestType::ContestedResourceVoteStatusRequest( + ContestedResourceVoteStatusRequest { + contract_id: dpns_contract.id().to_vec(), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: serialized_index_values, + voter_identifier: [0u8; 32].to_vec(), + }, + )), + }], + }; + + let validation_result = platform + .query_proofs_v0(request, &state, version) + .expect("expected query to succeed"); + + assert!(matches!(validation_result.data, Some(GetProofsResponseV0 { + result: Some(get_proofs_response_v0::Result::Proof(proof)), + metadata: Some(_), + }) if !proof.grovedb_proof.is_empty())); + } + #[test] fn test_prove_all() { let (platform, state, version) = setup_platform(false); diff --git a/packages/rs-drive-abci/tests/strategy_tests/chain_lock_update.rs b/packages/rs-drive-abci/tests/strategy_tests/chain_lock_update.rs index f80ec3645d..a210cb8bef 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/chain_lock_update.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/chain_lock_update.rs @@ -77,6 +77,6 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 50, strategy, config, 13); + run_chain_for_strategy(&mut platform, 50, strategy, config, 13, &mut None); } } diff --git a/packages/rs-drive-abci/tests/strategy_tests/core_update_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/core_update_tests.rs index 9b5e08ad33..44ac67b3d7 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/core_update_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/core_update_tests.rs @@ -77,7 +77,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 50, strategy, config, 13); + let outcome = run_chain_for_strategy(&mut platform, 50, strategy, config, 13, &mut None); // we expect to see quorums with banned members @@ -175,7 +175,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 50, strategy, config, 13); + let outcome = run_chain_for_strategy(&mut platform, 50, strategy, config, 13, &mut None); // we expect to see quorums with banned members @@ -264,7 +264,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 26, strategy, config, 13); + let outcome = run_chain_for_strategy(&mut platform, 26, strategy, config, 13, &mut None); // We should also see validator sets with less than the quorum size diff --git a/packages/rs-drive-abci/tests/strategy_tests/execution.rs b/packages/rs-drive-abci/tests/strategy_tests/execution.rs index 3db5a245fa..427703b02d 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/execution.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/execution.rs @@ -37,6 +37,7 @@ use drive_abci::test::fixture::abci::static_init_chain_request; use platform_version::version::PlatformVersion; use rand::prelude::{SliceRandom, StdRng}; use rand::{Rng, SeedableRng}; +use simple_signer::signer::SimpleSigner; use std::collections::{BTreeMap, HashMap}; use tenderdash_abci::proto::abci::{ResponseInitChain, ValidatorSetUpdate}; use tenderdash_abci::proto::crypto::public_key::Sum::Bls12381; @@ -46,13 +47,14 @@ use tenderdash_abci::Application; pub const GENESIS_TIME_MS: u64 = 1681094380000; -pub(crate) fn run_chain_for_strategy( - platform: &mut Platform, +pub(crate) fn run_chain_for_strategy<'a>( + platform: &'a mut Platform, block_count: u64, strategy: NetworkStrategy, config: PlatformConfig, seed: u64, -) -> ChainExecutionOutcome { + add_voting_keys_to_signer: &mut Option, +) -> ChainExecutionOutcome<'a> { let validator_quorum_count = strategy.validator_quorum_count; // In most tests 24 quorums let chain_lock_quorum_count = strategy.chain_lock_quorum_count; // In most tests 4 quorums when not the same as validator let validator_set_quorum_size = config.validator_set_quorum_size; @@ -115,6 +117,7 @@ pub(crate) fn run_chain_for_strategy( strategy.total_hpmns, generate_updates, &mut rng, + add_voting_keys_to_signer, ); let mut all_masternodes = initial_masternodes.clone(); @@ -165,6 +168,7 @@ pub(crate) fn run_chain_for_strategy( new_hpmns, generate_updates, &mut rng, + add_voting_keys_to_signer, ); if strategy.proposer_strategy.removed_masternodes.is_set() { @@ -207,6 +211,7 @@ pub(crate) fn run_chain_for_strategy( strategy.total_hpmns, None, &mut rng, + add_voting_keys_to_signer, ); ( initial_masternodes, @@ -772,6 +777,7 @@ pub(crate) fn start_chain_for_strategy( current_proposer_versions: None, current_identity_nonce_counter: Default::default(), current_identity_contract_nonce_counter: Default::default(), + current_votes: Default::default(), start_time_ms: GENESIS_TIME_MS, current_time_ms: GENESIS_TIME_MS, }, @@ -799,6 +805,7 @@ pub(crate) fn continue_chain_for_strategy( current_proposer_versions, mut current_identity_nonce_counter, mut current_identity_contract_nonce_counter, + mut current_votes, start_time_ms, mut current_time_ms, } = chain_execution_parameters; @@ -875,6 +882,7 @@ pub(crate) fn continue_chain_for_strategy( &mut current_identities, &mut current_identity_nonce_counter, &mut current_identity_contract_nonce_counter, + &mut current_votes, &mut signer, &mut rng, ); diff --git a/packages/rs-drive-abci/tests/strategy_tests/failures.rs b/packages/rs-drive-abci/tests/strategy_tests/failures.rs index c1fe815095..a02599128d 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/failures.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/failures.rs @@ -91,7 +91,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15, &mut None); outcome .abci_app @@ -183,13 +183,20 @@ mod tests { signature: [2; 96].into(), }) }); - run_chain_for_strategy(&mut platform, 1, strategy.clone(), config.clone(), 15); + run_chain_for_strategy( + &mut platform, + 1, + strategy.clone(), + config.clone(), + 15, + &mut None, + ); //platform block didn't complete, so it should get another init chain strategy.failure_testing = None; - run_chain_for_strategy(&mut platform, 15, strategy, config, 15); + run_chain_for_strategy(&mut platform, 15, strategy, config, 15, &mut None); } // #[test] diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index b7976a10ca..80d6999b35 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -130,7 +130,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 100, strategy, config, 15); + run_chain_for_strategy(&mut platform, 100, strategy, config, 15, &mut None); } #[test] @@ -175,7 +175,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 50, strategy, config, 13); + run_chain_for_strategy(&mut platform, 50, strategy, config, 13, &mut None); } #[test] @@ -233,7 +233,14 @@ mod tests { identity_nonce_counter, identity_contract_nonce_counter, .. - } = run_chain_for_strategy(&mut platform, 15, strategy.clone(), config.clone(), 40); + } = run_chain_for_strategy( + &mut platform, + 15, + strategy.clone(), + config.clone(), + 40, + &mut None, + ); let known_root_hash = abci_app .platform @@ -295,6 +302,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -365,7 +373,14 @@ mod tests { identity_nonce_counter, identity_contract_nonce_counter, .. - } = run_chain_for_strategy(&mut platform, 15, strategy.clone(), config.clone(), 40); + } = run_chain_for_strategy( + &mut platform, + 15, + strategy.clone(), + config.clone(), + 40, + &mut None, + ); let known_root_hash = abci_app .platform @@ -427,6 +442,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -485,7 +501,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 100, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 100, strategy, config, 15, &mut None); let balance = outcome .abci_app @@ -547,7 +563,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); } #[test] @@ -597,7 +613,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome .masternode_identity_balances @@ -655,7 +671,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome .masternode_identity_balances @@ -712,7 +728,7 @@ mod tests { .build_with_mock_rpc(); let ChainExecutionOutcome { abci_app, .. } = - run_chain_for_strategy(&mut platform, 2000, strategy, config, 40); + run_chain_for_strategy(&mut platform, 2000, strategy, config, 40, &mut None); // With these params if we didn't rotate we would have at most 240 // of the 500 hpmns that could get paid, however we are expecting that most @@ -792,7 +808,7 @@ mod tests { .build_with_mock_rpc(); let ChainExecutionOutcome { abci_app, .. } = - run_chain_for_strategy(&mut platform, 300, strategy, config, 43); + run_chain_for_strategy(&mut platform, 300, strategy, config, 43, &mut None); // With these params if we add new mns the hpmn masternode list would be 100, but we // can expect it to be much higher. @@ -859,7 +875,7 @@ mod tests { .build_with_mock_rpc(); let ChainExecutionOutcome { abci_app, .. } = - run_chain_for_strategy(&mut platform, 300, strategy, config, 43); + run_chain_for_strategy(&mut platform, 300, strategy, config, 43, &mut None); // With these params if we add new mns the hpmn masternode list would be randomly different than 100. @@ -924,7 +940,7 @@ mod tests { abci_app, proposers, .. - } = run_chain_for_strategy(&mut platform, 300, strategy, config, 43); + } = run_chain_for_strategy(&mut platform, 300, strategy, config, 43, &mut None); // With these params if we add new mns the hpmn masternode list would be randomly different than 100. @@ -1015,7 +1031,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 100, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 100, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len(), 100); } @@ -1070,7 +1086,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 150, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 150, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len(), 150); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome @@ -1150,7 +1166,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1, strategy, config, 15, &mut None); outcome .abci_app @@ -1269,7 +1285,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 30, strategy, config, 15); + run_chain_for_strategy(&mut platform, 30, strategy, config, 15, &mut None); } #[test] @@ -1356,7 +1372,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15, &mut None); outcome .abci_app @@ -1460,7 +1476,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - run_chain_for_strategy(&mut platform, 100, strategy, config, 15); + run_chain_for_strategy(&mut platform, 100, strategy, config, 15, &mut None); } #[test] @@ -1542,7 +1558,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, block_count); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome @@ -1650,7 +1667,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, block_count); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome @@ -1759,7 +1777,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, block_count); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome @@ -1885,7 +1904,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, block_count); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome @@ -2010,7 +2030,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); for tx_results_per_block in outcome.state_transition_results_per_block.values() { for (state_transition, result) in tx_results_per_block { assert_eq!( @@ -2124,7 +2145,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); for tx_results_per_block in outcome.state_transition_results_per_block.values() { for (state_transition, _unused_result) in tx_results_per_block { // We can't ever get a documents batch transition, because the proposer will remove it from a block @@ -2238,7 +2260,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, 470); assert_eq!(outcome.masternode_identity_balances.len(), 100); let balance_count = outcome @@ -2367,7 +2390,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, 90); assert_eq!(outcome.masternode_identity_balances.len(), 100); let balance_count = outcome @@ -2512,7 +2536,8 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, block_count, strategy, config, 15); + let outcome = + run_chain_for_strategy(&mut platform, block_count, strategy, config, 15, &mut None); assert_eq!(outcome.identities.len() as u64, 97); assert_eq!(outcome.masternode_identity_balances.len(), 100); let balance_count = outcome @@ -2579,7 +2604,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15, &mut None); let max_initial_balance = 100000000000u64; // TODO: some centralized way for random test data (`arbitrary` maybe?) let balances = outcome @@ -2659,7 +2684,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15, &mut None); let state = outcome.abci_app.platform.state.load(); let protocol_version = state.current_protocol_version_in_consensus(); let platform_version = PlatformVersion::get(protocol_version).unwrap(); @@ -2744,7 +2769,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 10, strategy, config, 15, &mut None); let identities = outcome .abci_app @@ -2912,8 +2937,14 @@ mod tests { }, last_block_pooled_withdrawals_amount, ) = { - let outcome = - run_chain_for_strategy(&mut platform, 2, strategy.clone(), config.clone(), 1); + let outcome = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 1, + &mut None, + ); // Withdrawal transactions are not populated to block execution context yet assert_eq!(outcome.withdrawals.len(), 0); @@ -2962,6 +2993,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: GENESIS_TIME_MS, current_time_ms: end_time_ms, }, @@ -3043,6 +3075,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: GENESIS_TIME_MS, current_time_ms: end_time_ms + 1000, }, @@ -3155,6 +3188,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: GENESIS_TIME_MS, current_time_ms: end_time_ms + 1000, }, @@ -3262,6 +3296,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: GENESIS_TIME_MS, current_time_ms: end_time_ms + 1000, }, @@ -3423,7 +3458,8 @@ mod tests { }) }); - let outcome = run_chain_for_strategy(platform, 1, strategy.clone(), config.clone(), 7); + let outcome = + run_chain_for_strategy(platform, 1, strategy.clone(), config.clone(), 7, &mut None); outcomes.push(outcome); } @@ -3593,9 +3629,15 @@ mod tests { }) }); - let outcome_a = - run_chain_for_strategy(&mut platform_a, 18, strategy.clone(), config.clone(), 7); - let outcome_b = run_chain_for_strategy(&mut platform_b, 18, strategy, config, 7); + let outcome_a = run_chain_for_strategy( + &mut platform_a, + 18, + strategy.clone(), + config.clone(), + 7, + &mut None, + ); + let outcome_b = run_chain_for_strategy(&mut platform_b, 18, strategy, config, 7, &mut None); assert_eq!(outcome_a.end_epoch_index, outcome_b.end_epoch_index); // 100/18 assert_eq!(outcome_a.masternode_identity_balances.len(), 500); // 500 nodes assert_eq!(outcome_b.masternode_identity_balances.len(), 500); // 500 nodes @@ -3728,9 +3770,16 @@ mod tests { }) }); - let outcome_a = - run_chain_for_strategy(&mut platform_a, 100, strategy.clone(), config.clone(), 7); - let outcome_b = run_chain_for_strategy(&mut platform_b, 100, strategy, config, 7); + let outcome_a = run_chain_for_strategy( + &mut platform_a, + 100, + strategy.clone(), + config.clone(), + 7, + &mut None, + ); + let outcome_b = + run_chain_for_strategy(&mut platform_b, 100, strategy, config, 7, &mut None); assert_eq!(outcome_a.end_epoch_index, outcome_b.end_epoch_index); // 100/18 assert_eq!(outcome_a.masternode_identity_balances.len(), 500); // 500 nodes assert_eq!(outcome_b.masternode_identity_balances.len(), 500); // 500 nodes @@ -3845,7 +3894,14 @@ mod tests { identity_nonce_counter, identity_contract_nonce_counter, .. - } = run_chain_for_strategy(&mut platform, 100, strategy.clone(), config.clone(), 89); + } = run_chain_for_strategy( + &mut platform, + 100, + strategy.clone(), + config.clone(), + 89, + &mut None, + ); let state = abci_app.platform.state.load(); let protocol_version = state.current_protocol_version_in_consensus(); @@ -3905,6 +3961,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -3976,7 +4033,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 15, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 15, strategy, config, 15, &mut None); let _balances = &outcome .abci_app @@ -4031,7 +4088,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1, strategy, config, 15, &mut None); let state_transitions = outcome .state_transition_results_per_block .get(&1) diff --git a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs index d240752a26..f10c37b5d0 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/masternodes.rs @@ -3,9 +3,14 @@ use dashcore_rpc::dashcore::hashes::Hash; use dashcore_rpc::dashcore::{ProTxHash, QuorumHash, Txid}; use dashcore_rpc::dashcore_rpc_json::{DMNState, MasternodeListItem, MasternodeType}; use dpp::bls_signatures::PrivateKey as BlsPrivateKey; +use dpp::identity::hash::IdentityPublicKeyHashMethodsV0; +use dpp::identity::IdentityPublicKey; +use dpp::identity::KeyType::{ECDSA_HASH160, ECDSA_SECP256K1}; use drive_abci::mimic::test_quorum::TestQuorumInfo; +use platform_version::version::PlatformVersion; use rand::prelude::{IteratorRandom, StdRng}; use rand::Rng; +use simple_signer::signer::SimpleSigner; use std::collections::{BTreeMap, BTreeSet}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::str::FromStr; @@ -33,6 +38,7 @@ pub fn generate_test_masternodes( hpmn_count: u16, updates: Option, rng: &mut StdRng, + add_voting_keys_to_signer: &mut Option, ) -> ( Vec, Vec, @@ -188,6 +194,21 @@ pub fn generate_test_masternodes( let hpmn_number_to_heights_http_port_changes = invert_btreemap(block_height_to_list_hpmn_http_port_changes); + fn generate_voting_address( + rng: &mut StdRng, + add_voting_keys_to_signer: &mut Option, + ) -> [u8; 20] { + if let Some(simple_signer) = add_voting_keys_to_signer { + let (identity_public_key, private_key) = + IdentityPublicKey::random_voting_key_with_rng(0, rng, PlatformVersion::latest()) + .expect("expected a random voting key"); + simple_signer.add_key(identity_public_key.clone(), private_key); + identity_public_key.public_key_hash().unwrap() + } else { + rng.gen() + } + } + for i in 0..masternode_count { let private_key_operator = BlsPrivateKey::generate_dash(rng).expect("expected to generate a private key"); @@ -212,7 +233,7 @@ pub fn generate_test_masternodes( pose_ban_height: None, revocation_reason: 0, owner_address: rng.gen::<[u8; 20]>(), - voting_address: rng.gen::<[u8; 20]>(), + voting_address: generate_voting_address(rng, add_voting_keys_to_signer), payout_address: rng.gen::<[u8; 20]>(), pub_key_operator, operator_payout_address: None, @@ -347,7 +368,7 @@ pub fn generate_test_masternodes( pose_ban_height: None, revocation_reason: 0, owner_address: rng.gen::<[u8; 20]>(), - voting_address: rng.gen::<[u8; 20]>(), + voting_address: generate_voting_address(rng, add_voting_keys_to_signer), payout_address: rng.gen::<[u8; 20]>(), pub_key_operator, operator_payout_address: None, @@ -581,9 +602,9 @@ mod tests { let mut rng2 = StdRng::seed_from_u64(12345); let (masternodes1, hpmn1) = - generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng1); + generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng1, &mut None); let (masternodes2, hpmn2) = - generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng2); + generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng2, &mut None); assert_eq!(masternodes1, masternodes2); assert_eq!(hpmn1, hpmn2); @@ -640,10 +661,20 @@ mod tests { let mut rng1 = StdRng::seed_from_u64(12345); let mut rng2 = StdRng::seed_from_u64(12345); - let (masternodes1, hpmn1) = - generate_test_masternodes(masternode_count, hpmn_count, updates.clone(), &mut rng1); - let (masternodes2, hpmn2) = - generate_test_masternodes(masternode_count, hpmn_count, updates.clone(), &mut rng2); + let (masternodes1, hpmn1) = generate_test_masternodes( + masternode_count, + hpmn_count, + updates.clone(), + &mut rng1, + &mut None, + ); + let (masternodes2, hpmn2) = generate_test_masternodes( + masternode_count, + hpmn_count, + updates.clone(), + &mut rng2, + &mut None, + ); assert_eq!(masternodes1, masternodes2); assert_eq!(hpmn1, hpmn2); @@ -666,9 +697,9 @@ mod tests { let hpmn_count = rng.gen_range(50..=150); let (masternodes1, hpmn1) = - generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng1); + generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng1, &mut None); let (masternodes2, hpmn2) = - generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng2); + generate_test_masternodes(masternode_count, hpmn_count, None, &mut rng2, &mut None); assert_eq!(masternodes1, masternodes2); assert_eq!(hpmn1, hpmn2); @@ -736,10 +767,20 @@ mod tests { }, }); - let (masternodes1, hpmn1) = - generate_test_masternodes(masternode_count, hpmn_count, updates.clone(), &mut rng1); - let (masternodes2, hpmn2) = - generate_test_masternodes(masternode_count, hpmn_count, updates.clone(), &mut rng2); + let (masternodes1, hpmn1) = generate_test_masternodes( + masternode_count, + hpmn_count, + updates.clone(), + &mut rng1, + &mut None, + ); + let (masternodes2, hpmn2) = generate_test_masternodes( + masternode_count, + hpmn_count, + updates.clone(), + &mut rng2, + &mut None, + ); assert_eq!(masternodes1, masternodes2); assert_eq!(hpmn1, hpmn2); diff --git a/packages/rs-drive-abci/tests/strategy_tests/query.rs b/packages/rs-drive-abci/tests/strategy_tests/query.rs index 899d3f2b85..7b885ead61 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/query.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/query.rs @@ -386,7 +386,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome .masternode_identity_balances @@ -484,7 +484,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome .masternode_identity_balances @@ -583,7 +583,7 @@ mod tests { .with_config(config.clone()) .build_with_mock_rpc(); - let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15); + let outcome = run_chain_for_strategy(&mut platform, 1000, strategy, config, 15, &mut None); assert_eq!(outcome.masternode_identity_balances.len(), 100); let all_have_balances = outcome .masternode_identity_balances diff --git a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs index 2c7fb68f25..94159aec1e 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs @@ -45,9 +45,12 @@ use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::str::FromStr; use tenderdash_abci::proto::abci::{ExecTxResult, ValidatorSetUpdate}; +use dpp::dashcore::hashes::Hash; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; use dpp::data_contract::document_type::v0::DocumentTypeV0; +use dpp::identifier::MasternodeIdentifiers; use dpp::identity::accessors::IdentityGettersV0; +use dpp::identity::identity_public_key::v0::IdentityPublicKeyV0; use dpp::platform_value::{BinaryData, Value}; use dpp::prelude::{Identifier, IdentityNonce}; use dpp::state_transition::documents_batch_transition::document_base_transition::v0::DocumentBaseTransitionV0; @@ -59,7 +62,15 @@ use dpp::state_transition::documents_batch_transition::document_transition::{Doc use drive::drive::document::query::QueryDocumentsOutcomeV0Methods; use dpp::state_transition::data_contract_create_transition::methods::v0::DataContractCreateTransitionMethodsV0; use dpp::state_transition::documents_batch_transition::document_transition::document_transfer_transition::DocumentTransferTransitionV0; +use dpp::state_transition::masternode_vote_transition::MasternodeVoteTransition; +use dpp::state_transition::masternode_vote_transition::methods::MasternodeVoteTransitionMethodsV0; +use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; +use dpp::voting::vote_polls::VotePoll; +use dpp::voting::votes::resource_vote::ResourceVote; +use dpp::voting::votes::resource_vote::v0::ResourceVoteV0; +use dpp::voting::votes::Vote; use drive_abci::abci::app::FullAbciApplication; +use drive_abci::platform_types::platform_state::v0::PlatformStateV0Methods; use drive_abci::platform_types::withdrawal::unsigned_withdrawal_txs::v0::UnsignedWithdrawalTxs; use crate::strategy::CoreHeightIncrease::NoCoreHeightIncrease; @@ -527,9 +538,13 @@ impl NetworkStrategy { signer: &mut SimpleSigner, identity_nonce_counter: &mut BTreeMap, contract_nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, + // first identifier is the vote poll id + // second identifier is the identifier + current_votes: &mut BTreeMap>, rng: &mut StdRng, platform_version: &PlatformVersion, ) -> (Vec, Vec) { + let mut maybe_state = None; let mut operations = vec![]; let mut finalize_block_operations = vec![]; let mut replaced = vec![]; @@ -1261,6 +1276,76 @@ impl NetworkStrategy { operations.push(state_transition); } } + OperationType::ResourceVote(resource_vote_op) => { + let state = maybe_state.get_or_insert(platform.state.load()); + let full_masternode_list = state.full_masternode_list(); + let vote_poll_id = resource_vote_op + .resolved_vote_poll + .unique_id() + .expect("expected a vote poll unique id"); + let vote_poll_votes = current_votes.entry(vote_poll_id).or_default(); + for _ in 0..count { + let rand_index = rng.gen_range(0..full_masternode_list.len()); + let (pro_tx_hash, masternode_list_item) = + full_masternode_list.iter().nth(rand_index).unwrap(); + + let pro_tx_hash_bytes: [u8; 32] = pro_tx_hash.to_raw_hash().into(); + let voting_address = masternode_list_item.state.voting_address; + + let voting_identifier = Identifier::create_voter_identifier( + pro_tx_hash.as_byte_array(), + &voting_address, + ); + + // Choose the resource vote choice based on weights + let resource_vote_choice = + resource_vote_op.action.choose_weighted_choice(rng); + + if vote_poll_votes.get(&voting_identifier) + == Some(&resource_vote_choice) + { + continue; + } + + let identity_public_key = IdentityPublicKey::V0(IdentityPublicKeyV0 { + id: 0, + purpose: Purpose::VOTING, + security_level: SecurityLevel::MEDIUM, + contract_bounds: None, + key_type: KeyType::ECDSA_HASH160, + read_only: false, + data: voting_address.to_vec().into(), + disabled_at: None, + }); + + let vote = Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 { + vote_poll: VotePoll::ContestedDocumentResourceVotePoll( + resource_vote_op.resolved_vote_poll.clone().into(), + ), + resource_vote_choice, + })); + + let identity_nonce = + identity_nonce_counter.entry(voting_identifier).or_default(); + *identity_nonce += 1; + + let state_transition = + MasternodeVoteTransition::try_from_vote_with_signer( + vote, + signer, + Identifier::from(pro_tx_hash_bytes), + &identity_public_key, + *identity_nonce, + platform_version, + None, + ) + .expect("expected to make a masternode vote transition"); + + vote_poll_votes.insert(voting_identifier, resource_vote_choice); + + operations.push(state_transition); + } + } _ => {} } } @@ -1275,6 +1360,7 @@ impl NetworkStrategy { current_identities: &mut Vec, identity_nonce_counter: &mut BTreeMap, contract_nonce_counter: &mut BTreeMap<(Identifier, Identifier), u64>, + current_votes: &mut BTreeMap>, signer: &mut SimpleSigner, rng: &mut StdRng, ) -> (Vec, Vec) { @@ -1318,6 +1404,7 @@ impl NetworkStrategy { signer, identity_nonce_counter, contract_nonce_counter, + current_votes, rng, platform_version, ); @@ -1460,6 +1547,7 @@ pub struct ChainExecutionParameters { pub current_proposer_versions: Option>>, pub current_identity_nonce_counter: BTreeMap, pub current_identity_contract_nonce_counter: BTreeMap<(Identifier, Identifier), IdentityNonce>, + pub current_votes: BTreeMap>, pub start_time_ms: u64, pub current_time_ms: u64, } diff --git a/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs index 2d4dac2e7e..5822bceb1d 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/upgrade_fork_tests.rs @@ -7,6 +7,7 @@ mod tests { use dpp::dashcore::{BlockHash, ChainLock}; use dpp::version::PlatformVersion; use drive::drive::config::DriveConfig; + use std::collections::BTreeMap; use crate::execution::{continue_chain_for_strategy, run_chain_for_strategy}; use crate::strategy::{ @@ -111,6 +112,7 @@ mod tests { strategy.clone(), config.clone(), 13, + &mut None, ); let platform = abci_app.platform; @@ -180,6 +182,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions.clone()), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -230,6 +233,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -350,6 +354,7 @@ mod tests { strategy.clone(), config.clone(), 13, + &mut None, ); let platform = abci_app.platform; @@ -416,6 +421,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions.clone()), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -466,6 +472,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -566,7 +573,14 @@ mod tests { current_quorum_hash, end_time_ms, .. - } = run_chain_for_strategy(&mut platform, 1, strategy.clone(), config.clone(), 13); + } = run_chain_for_strategy( + &mut platform, + 1, + strategy.clone(), + config.clone(), + 13, + &mut None, + ); let platform = abci_app.platform; @@ -749,6 +763,7 @@ mod tests { strategy.clone(), config.clone(), 16, + &mut None, ); let platform = abci_app.platform; let state = platform.state.load(); @@ -808,6 +823,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions.clone()), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -861,6 +877,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -981,6 +998,7 @@ mod tests { strategy.clone(), config.clone(), 15, + &mut None, ); let platform = abci_app.platform; @@ -1032,6 +1050,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -1128,6 +1147,7 @@ mod tests { current_proposer_versions: None, //restart the proposer versions current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -1183,6 +1203,7 @@ mod tests { current_proposer_versions: Some(current_proposer_versions), current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, @@ -1303,7 +1324,14 @@ mod tests { identity_nonce_counter, identity_contract_nonce_counter, .. - } = run_chain_for_strategy(&mut platform, 1400, strategy, config.clone(), 15); + } = run_chain_for_strategy( + &mut platform, + 1400, + strategy, + config.clone(), + 15, + &mut None, + ); let state = abci_app.platform.state.load(); { let platform = abci_app.platform; @@ -1400,6 +1428,7 @@ mod tests { current_proposer_versions: None, current_identity_nonce_counter: identity_nonce_counter, current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), start_time_ms: 1681094380000, current_time_ms: end_time_ms, }, diff --git a/packages/rs-drive-abci/tests/strategy_tests/verify_state_transitions.rs b/packages/rs-drive-abci/tests/strategy_tests/verify_state_transitions.rs index f97721c463..a2ed5728da 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/verify_state_transitions.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/verify_state_transitions.rs @@ -23,7 +23,9 @@ use drive_abci::rpc::core::MockCoreRPCLike; use tenderdash_abci::proto::abci::ExecTxResult; use dpp::state_transition::documents_batch_transition::accessors::DocumentsBatchTransitionAccessorsV0; +use dpp::voting::votes::resource_vote::accessors::v0::ResourceVoteGettersV0; use dpp::voting::votes::Vote; +use drive::drive::verify::RootHash; use drive::drive::votes::resolved::vote_polls::ResolvedVotePoll; use drive::drive::votes::resolved::votes::resolved_resource_vote::accessors::v0::ResolvedResourceVoteGettersV0; use drive::drive::votes::resolved::votes::ResolvedVote; @@ -554,7 +556,7 @@ pub(crate) fn verify_state_transitions_were_or_were_not_executed( false, platform_version, ) - .expect("expected to verify balance identity"); + .expect("expected to verify balance identity for top up"); let balance = balance.expect("expected a balance"); assert_eq!( &root_hash, @@ -616,7 +618,7 @@ pub(crate) fn verify_state_transitions_were_or_were_not_executed( false, platform_version, ) - .expect("expected to verify balance identity"); + .expect("expected to verify balance identity for withdrawal"); let _balance = balance.expect("expected a balance"); assert_eq!( &root_hash, @@ -717,7 +719,7 @@ pub(crate) fn verify_state_transitions_were_or_were_not_executed( true, platform_version, ) - .expect("expected to verify balance identity"); + .expect("expected to verify balance identity for credit transfer"); assert_eq!( &root_hash_identity, @@ -806,7 +808,7 @@ pub(crate) fn verify_state_transitions_were_or_were_not_executed( masternode_vote_action.pro_tx_hash().into_buffer(), &vote, data_contract, - true, + false, // we are not in a subset, we have just one vote platform_version, ) .expect("expected to verify balance identity"); diff --git a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs index 80c40e66fd..83eddeea27 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { - use crate::execution::run_chain_for_strategy; - use crate::strategy::NetworkStrategy; + use crate::execution::{continue_chain_for_strategy, run_chain_for_strategy}; + use crate::strategy::{ChainExecutionOutcome, ChainExecutionParameters, NetworkStrategy, StrategyRandomness}; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::document_type::random_document::{ DocumentFieldFillSize, DocumentFieldFillType, @@ -20,9 +20,17 @@ mod tests { use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::get_contested_resource_vote_state_request_v0::ResultType; use dapi_grpc::platform::v0::get_contested_resource_vote_state_request::GetContestedResourceVoteStateRequestV0; use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::{get_contested_resource_vote_state_response_v0, GetContestedResourceVoteStateResponseV0}; + use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::{finished_vote_info, FinishedVoteInfo}; + use dapi_grpc::platform::v0::get_contested_resource_vote_state_response::get_contested_resource_vote_state_response_v0::finished_vote_info::FinishedVoteOutcome; + use dpp::block::extended_block_info::v0::ExtendedBlockInfoV0Getters; use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters; + use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; + use drive::drive::config::DriveConfig; + use drive::drive::object_size_info::DataContractOwnedResolvedInfo; + use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo; + use drive_abci::platform_types::platform_state::v0::PlatformStateV0Methods; use strategy_tests::frequency::Frequency; - use strategy_tests::operations::{DocumentAction, DocumentOp, Operation, OperationType}; + use strategy_tests::operations::{DocumentAction, DocumentOp, Operation, OperationType, ResourceVoteOp, VoteAction}; use strategy_tests::transitions::create_state_transitions_for_identities; use strategy_tests::{StartIdentities, Strategy}; @@ -182,8 +190,14 @@ mod tests { }; // On the first block we only have identities and contracts - let outcome = - run_chain_for_strategy(&mut platform, 2, strategy.clone(), config.clone(), 15); + let outcome = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut None, + ); let platform = outcome.abci_app.platform; @@ -314,4 +328,1100 @@ mod tests { assert_eq!(second_contender.vote_count, Some(0)); } + + #[test] + fn run_chain_with_voting_on_conflicting_index_just_abstain_votes() { + // In this test we try to insert two state transitions with the same unique index + // We use the DPNS contract, and we insert two documents both with the same "name" + // This is a common scenario we should see quite often + let config = PlatformConfig { + validator_set_quorum_size: 100, + validator_set_quorum_type: "llmq_100_67".to_string(), + chain_lock_quorum_type: "llmq_100_67".to_string(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + validator_set_rotation_block_count: 25, + ..Default::default() + }, + block_spacing_ms: 3000, + ..Default::default() + }; + let mut platform = TestPlatformBuilder::new() + .with_config(config.clone()) + .build_with_mock_rpc(); + + let platform_version = PlatformVersion::latest(); + + let mut rng = StdRng::seed_from_u64(567); + + let mut simple_signer = SimpleSigner::default(); + + let (identity1, keys1) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys1); + + let (identity2, keys2) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys2); + + let start_identities = create_state_transitions_for_identities( + vec![identity1, identity2], + &simple_signer, + &mut rng, + platform_version, + ); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let document_type = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type") + .to_owned_document_type(); + + let identity1_id = start_identities.first().unwrap().0.id(); + let identity2_id = start_identities.last().unwrap().0.id(); + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("dashUniqueIdentityId", Value::from(identity1_id))]) + .into(), + ), + ]), + Some(start_identities.first().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([( + "dashUniqueIdentityId", + Value::from(start_identities.last().unwrap().0.id()), + )]) + .into(), + ), + ]), + Some(start_identities.last().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let strategy = NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities { + hard_coded: start_identities, + ..Default::default() + }, + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer), + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }; + + let mut voting_signer = Some(SimpleSigner::default()); + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + state_transition_results_per_block, + .. + } = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut voting_signer, + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let state_transitions_block_2 = state_transition_results_per_block + .get(&2) + .expect("expected to get block 2"); + + let first_document_insert_result = &state_transitions_block_2 + .first() + .as_ref() + .expect("expected a document insert") + .1; + assert_eq!(first_document_insert_result.code, 0); + + let second_document_insert_result = &state_transitions_block_2 + .get(1) + .as_ref() + .expect("expected a document insert") + .1; + + assert_eq!(second_document_insert_result.code, 0); // we expect the second to also be insertable as they are both contested + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + let outcome = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 30, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions: Some(current_proposer_versions.clone()), + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![Operation { + op_type: OperationType::ResourceVote(ResourceVoteOp { + resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + dpns_contract.clone(), + ), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec!["dash".into(), "quantum".into()], + }, + action: VoteAction { + vote_choices_with_weights: vec![(ResourceVoteChoice::Abstain, 1)], + }, + }), + frequency: Frequency { + times_per_block_range: 1..3, + chance_per_block: None, + }, + }], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: voting_signer, + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(7), + ); + + let platform = outcome.abci_app.platform; + + // Now let's run a query for the vote totals + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode the word dash"); + + let quantum_encoded = bincode::encode_to_vec(Value::Text("quantum".to_string()), config) + .expect("expected to encode the word quantum"); + + let index_name = "parentNameAndLabel".to_string(); + + let query_validation_result = platform + .query_contested_resource_vote_state( + GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: document_type.name().clone(), + index_name: index_name.clone(), + index_values: vec![dash_encoded.clone(), quantum_encoded.clone()], + result_type: ResultType::DocumentsAndVoteTally as i32, + allow_include_locked_and_abstaining_vote_tally: true, + start_at_identifier_info: None, + count: None, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resource_vote_state_response::Version::V0( + GetContestedResourceVoteStateResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some( + get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders( + get_contested_resource_vote_state_response_v0::ContestedResourceContenders { + contenders, + abstain_vote_tally, + lock_vote_tally: _, + finished_vote_info: _, + }, + ), + ) = result + else { + panic!("expected contenders") + }; + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert!(first_contender.document.is_some()); + + assert!(second_contender.document.is_some()); + + assert_eq!(first_contender.identifier, identity2_id.to_vec()); + + assert_eq!(second_contender.identifier, identity1_id.to_vec()); + + assert_eq!(first_contender.vote_count, Some(0)); + + assert_eq!(second_contender.vote_count, Some(0)); + + assert_eq!(abstain_vote_tally, Some(124)); + } + + #[test] + fn run_chain_with_voting_on_conflicting_index_various_votes() { + // In this test we try to insert two state transitions with the same unique index + // We use the DPNS contract, and we insert two documents both with the same "name" + // This is a common scenario we should see quite often + let config = PlatformConfig { + validator_set_quorum_size: 100, + validator_set_quorum_type: "llmq_100_67".to_string(), + chain_lock_quorum_type: "llmq_100_67".to_string(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + validator_set_rotation_block_count: 25, + ..Default::default() + }, + block_spacing_ms: 3000, + ..Default::default() + }; + let mut platform = TestPlatformBuilder::new() + .with_config(config.clone()) + .build_with_mock_rpc(); + + let platform_version = PlatformVersion::latest(); + + let mut rng = StdRng::seed_from_u64(567); + + let mut simple_signer = SimpleSigner::default(); + + let (identity1, keys1) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys1); + + let (identity2, keys2) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys2); + + let start_identities = create_state_transitions_for_identities( + vec![identity1, identity2], + &simple_signer, + &mut rng, + platform_version, + ); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let document_type = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type") + .to_owned_document_type(); + + let identity1_id = start_identities.first().unwrap().0.id(); + let identity2_id = start_identities.last().unwrap().0.id(); + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("dashUniqueIdentityId", Value::from(identity1_id))]) + .into(), + ), + ]), + Some(start_identities.first().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([( + "dashUniqueIdentityId", + Value::from(start_identities.last().unwrap().0.id()), + )]) + .into(), + ), + ]), + Some(start_identities.last().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let strategy = NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities { + hard_coded: start_identities, + ..Default::default() + }, + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer), + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }; + + let mut voting_signer = Some(SimpleSigner::default()); + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + state_transition_results_per_block, + .. + } = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut voting_signer, + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let state_transitions_block_2 = state_transition_results_per_block + .get(&2) + .expect("expected to get block 2"); + + let first_document_insert_result = &state_transitions_block_2 + .first() + .as_ref() + .expect("expected a document insert") + .1; + assert_eq!(first_document_insert_result.code, 0); + + let second_document_insert_result = &state_transitions_block_2 + .get(1) + .as_ref() + .expect("expected a document insert") + .1; + + assert_eq!(second_document_insert_result.code, 0); // we expect the second to also be insertable as they are both contested + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + let outcome = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 30, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions: Some(current_proposer_versions.clone()), + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![Operation { + op_type: OperationType::ResourceVote(ResourceVoteOp { + resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + dpns_contract.clone(), + ), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec!["dash".into(), "quantum".into()], + }, + action: VoteAction { + vote_choices_with_weights: vec![ + (ResourceVoteChoice::Abstain, 1), + (ResourceVoteChoice::Lock, 1), + (ResourceVoteChoice::TowardsIdentity(identity1_id), 5), + (ResourceVoteChoice::TowardsIdentity(identity2_id), 10), + ], + }, + }), + frequency: Frequency { + times_per_block_range: 1..3, + chance_per_block: None, + }, + }], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: voting_signer, + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(7), + ); + + let platform = outcome.abci_app.platform; + + // Now let's run a query for the vote totals + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode the word dash"); + + let quantum_encoded = bincode::encode_to_vec(Value::Text("quantum".to_string()), config) + .expect("expected to encode the word quantum"); + + let index_name = "parentNameAndLabel".to_string(); + + let query_validation_result = platform + .query_contested_resource_vote_state( + GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: document_type.name().clone(), + index_name: index_name.clone(), + index_values: vec![dash_encoded.clone(), quantum_encoded.clone()], + result_type: ResultType::DocumentsAndVoteTally as i32, + allow_include_locked_and_abstaining_vote_tally: true, + start_at_identifier_info: None, + count: None, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resource_vote_state_response::Version::V0( + GetContestedResourceVoteStateResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some( + get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders( + get_contested_resource_vote_state_response_v0::ContestedResourceContenders { + contenders, + abstain_vote_tally, + lock_vote_tally, + finished_vote_info, + }, + ), + ) = result + else { + panic!("expected contenders") + }; + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert!(first_contender.document.is_some()); + + assert!(second_contender.document.is_some()); + + assert_eq!(first_contender.identifier, identity2_id.to_vec()); + + assert_eq!(second_contender.identifier, identity1_id.to_vec()); + + // All vote counts are weighted, so for evonodes, these are in multiples of 4 + + assert_eq!(first_contender.vote_count, Some(52)); + + assert_eq!(second_contender.vote_count, Some(56)); + + assert_eq!(lock_vote_tally, Some(16)); + + assert_eq!(abstain_vote_tally, Some(8)); + + assert_eq!(finished_vote_info, None); + } + + #[test] + fn run_chain_with_voting_on_conflicting_index_distribution_after_won_by_identity() { + // In this test we try to insert two state transitions with the same unique index + // We use the DPNS contract, and we insert two documents both with the same "name" + // This is a common scenario we should see quite often + let config = PlatformConfig { + validator_set_quorum_size: 100, + validator_set_quorum_type: "llmq_100_67".to_string(), + chain_lock_quorum_type: "llmq_100_67".to_string(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + validator_set_rotation_block_count: 25, + ..Default::default() + }, + block_spacing_ms: 3000, + ..Default::default() + }; + let mut platform = TestPlatformBuilder::new() + .with_config(config.clone()) + .build_with_mock_rpc(); + + let platform_version = PlatformVersion::latest(); + + let mut rng = StdRng::seed_from_u64(567); + + let mut simple_signer = SimpleSigner::default(); + + let (identity1, keys1) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys1); + + let (identity2, keys2) = + Identity::random_identity_with_main_keys_with_private_key::>( + 2, + &mut rng, + platform_version, + ) + .unwrap(); + + simple_signer.add_keys(keys2); + + let start_identities = create_state_transitions_for_identities( + vec![identity1, identity2], + &simple_signer, + &mut rng, + platform_version, + ); + + let dpns_contract = platform + .drive + .cache + .system_data_contracts + .load_dpns() + .as_ref() + .clone(); + + let document_type = dpns_contract + .document_type_for_name("domain") + .expect("expected a profile document type") + .to_owned_document_type(); + + let identity1_id = start_identities.first().unwrap().0.id(); + let identity2_id = start_identities.last().unwrap().0.id(); + let document_op_1 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([("dashUniqueIdentityId", Value::from(identity1_id))]) + .into(), + ), + ]), + Some(start_identities.first().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let document_op_2 = DocumentOp { + contract: dpns_contract.clone(), + action: DocumentAction::DocumentActionInsertSpecific( + BTreeMap::from([ + ("label".into(), "quantum".into()), + ("normalizedLabel".into(), "quantum".into()), + ("normalizedParentDomainName".into(), "dash".into()), + ( + "records".into(), + BTreeMap::from([( + "dashUniqueIdentityId", + Value::from(start_identities.last().unwrap().0.id()), + )]) + .into(), + ), + ]), + Some(start_identities.last().unwrap().0.id()), + DocumentFieldFillType::FillIfNotRequired, + DocumentFieldFillSize::AnyDocumentFillSize, + ), + document_type: document_type.clone(), + }; + + let strategy = NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![ + Operation { + op_type: OperationType::Document(document_op_1), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + Operation { + op_type: OperationType::Document(document_op_2), + frequency: Frequency { + times_per_block_range: 1..2, + chance_per_block: None, + }, + }, + ], + start_identities: StartIdentities { + hard_coded: start_identities, + ..Default::default() + }, + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: Some(simple_signer), + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }; + + let mut voting_signer = Some(SimpleSigner::default()); + + // On the first block we only have identities and contracts + let ChainExecutionOutcome { + abci_app, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions, + end_time_ms, + identity_nonce_counter, + identity_contract_nonce_counter, + state_transition_results_per_block, + .. + } = run_chain_for_strategy( + &mut platform, + 2, + strategy.clone(), + config.clone(), + 15, + &mut voting_signer, + ); + + let platform = abci_app.platform; + + let platform_state = platform.state.load(); + + let state_transitions_block_2 = state_transition_results_per_block + .get(&2) + .expect("expected to get block 2"); + + let first_document_insert_result = &state_transitions_block_2 + .first() + .as_ref() + .expect("expected a document insert") + .1; + assert_eq!(first_document_insert_result.code, 0); + + let second_document_insert_result = &state_transitions_block_2 + .get(1) + .as_ref() + .expect("expected a document insert") + .1; + + assert_eq!(second_document_insert_result.code, 0); // we expect the second to also be insertable as they are both contested + + let block_start = platform_state + .last_committed_block_info() + .as_ref() + .unwrap() + .basic_info() + .height + + 1; + let day_in_ms = 1000 * 60 * 60 * 24; + let config = PlatformConfig { + validator_set_quorum_size: 100, + validator_set_quorum_type: "llmq_100_67".to_string(), + chain_lock_quorum_type: "llmq_100_67".to_string(), + execution: ExecutionConfig { + //we disable document triggers because we are using dpns and dpns needs a preorder + use_document_triggers: false, + validator_set_rotation_block_count: 25, + ..Default::default() + }, + block_spacing_ms: day_in_ms, + ..Default::default() + }; + + let outcome = continue_chain_for_strategy( + abci_app, + ChainExecutionParameters { + block_start, + core_height_start: 1, + block_count: 16, + proposers, + quorums, + current_quorum_hash, + current_proposer_versions: Some(current_proposer_versions.clone()), + current_identity_nonce_counter: identity_nonce_counter, + current_identity_contract_nonce_counter: identity_contract_nonce_counter, + current_votes: BTreeMap::default(), + start_time_ms: 1681094380000, + current_time_ms: end_time_ms, + }, + NetworkStrategy { + strategy: Strategy { + start_contracts: vec![], + operations: vec![Operation { + op_type: OperationType::ResourceVote(ResourceVoteOp { + resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + dpns_contract.clone(), + ), + document_type_name: "domain".to_string(), + index_name: "parentNameAndLabel".to_string(), + index_values: vec!["dash".into(), "quantum".into()], + }, + action: VoteAction { + vote_choices_with_weights: vec![ + (ResourceVoteChoice::Abstain, 1), + (ResourceVoteChoice::Lock, 1), + (ResourceVoteChoice::TowardsIdentity(identity1_id), 2), + (ResourceVoteChoice::TowardsIdentity(identity2_id), 10), + ], + }, + }), + frequency: Frequency { + times_per_block_range: 1..3, + chance_per_block: None, + }, + }], + start_identities: StartIdentities::default(), + identity_inserts: Default::default(), + + identity_contract_nonce_gaps: None, + signer: voting_signer, + }, + total_hpmns: 100, + extra_normal_mns: 0, + validator_quorum_count: 24, + chain_lock_quorum_count: 24, + upgrading_info: None, + + proposer_strategy: Default::default(), + rotate_quorums: false, + failure_testing: None, + query_testing: None, + verify_state_transition_results: true, + ..Default::default() + }, + config.clone(), + StrategyRandomness::SeedEntropy(9), + ); + + let platform = outcome.abci_app.platform; + + // Now let's run a query for the vote totals + + let config = bincode::config::standard() + .with_big_endian() + .with_no_limit(); + + let dash_encoded = bincode::encode_to_vec(Value::Text("dash".to_string()), config) + .expect("expected to encode the word dash"); + + let quantum_encoded = bincode::encode_to_vec(Value::Text("quantum".to_string()), config) + .expect("expected to encode the word quantum"); + + let index_name = "parentNameAndLabel".to_string(); + + let query_validation_result = platform + .query_contested_resource_vote_state( + GetContestedResourceVoteStateRequest { + version: Some(get_contested_resource_vote_state_request::Version::V0( + GetContestedResourceVoteStateRequestV0 { + contract_id: dpns_contract.id().to_vec(), + document_type_name: document_type.name().clone(), + index_name: index_name.clone(), + index_values: vec![dash_encoded.clone(), quantum_encoded.clone()], + result_type: ResultType::DocumentsAndVoteTally as i32, + allow_include_locked_and_abstaining_vote_tally: true, + start_at_identifier_info: None, + count: None, + prove: false, + }, + )), + }, + &platform_state, + platform_version, + ) + .expect("expected to execute query") + .into_data() + .expect("expected query to be valid"); + + let get_contested_resource_vote_state_response::Version::V0( + GetContestedResourceVoteStateResponseV0 { + metadata: _, + result, + }, + ) = query_validation_result.version.expect("expected a version"); + + let Some( + get_contested_resource_vote_state_response_v0::Result::ContestedResourceContenders( + get_contested_resource_vote_state_response_v0::ContestedResourceContenders { + contenders, + abstain_vote_tally, + lock_vote_tally, + finished_vote_info, + }, + ), + ) = result + else { + panic!("expected contenders") + }; + + assert_eq!(contenders.len(), 2); + + let first_contender = contenders.first().unwrap(); + + let second_contender = contenders.last().unwrap(); + + assert_eq!(first_contender.identifier, identity2_id.to_vec()); + + assert_eq!(second_contender.identifier, identity1_id.to_vec()); + + // All vote counts are weighted, so for evonodes, these are in multiples of 4 + + assert_eq!(first_contender.vote_count, Some(60)); + + assert_eq!(second_contender.vote_count, Some(4)); + + assert_eq!(lock_vote_tally, Some(4)); + + assert_eq!(abstain_vote_tally, Some(8)); + + assert_eq!( + finished_vote_info, + Some(FinishedVoteInfo { + finished_vote_outcome: FinishedVoteOutcome::TowardsIdentity.into(), + won_by_identity_id: Some(identity2_id.to_vec()), + finished_at_block_height: 17, + finished_at_core_block_height: 1, + finished_at_block_time_ms: 1682303986000, + finished_at_epoch: 1 + }) + ); + } } diff --git a/packages/rs-drive-proof-verifier/Cargo.toml b/packages/rs-drive-proof-verifier/Cargo.toml index 9ff302579f..d8647821b8 100644 --- a/packages/rs-drive-proof-verifier/Cargo.toml +++ b/packages/rs-drive-proof-verifier/Cargo.toml @@ -32,7 +32,7 @@ dpp = { path = "../rs-dpp", features = [ bincode = { version = "2.0.0-rc.3", features = ["serde"], optional = true } platform-serialization-derive = { path = "../rs-platform-serialization-derive", optional = true } platform-serialization = { path = "../rs-platform-serialization", optional = true } -tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "0.14.0-dev.12", features = [ +tenderdash-abci = { git = "https://github.com/dashpay/rs-tenderdash-abci", version = "1.0.0-dev.1", features = [ "crypto", ], default-features = false } tracing = { version = "0.1.37" } diff --git a/packages/rs-drive/src/drive/object_size_info/contract_info.rs b/packages/rs-drive/src/drive/object_size_info/contract_info.rs index 70be1a8db2..148706687d 100644 --- a/packages/rs-drive/src/drive/object_size_info/contract_info.rs +++ b/packages/rs-drive/src/drive/object_size_info/contract_info.rs @@ -122,6 +122,19 @@ impl AsRef for DataContractOwnedResolvedInfo { } } +impl DataContractOwnedResolvedInfo { + /// Get the contract as owned + pub fn into_owned(self) -> DataContract { + match self { + #[cfg(feature = "server")] + DataContractOwnedResolvedInfo::DataContractFetchInfo(fetch_info) => { + fetch_info.contract.clone() + } + DataContractOwnedResolvedInfo::OwnedDataContract(owned) => owned, + } + } +} + /// Contains resolved data contract information, typically used after initial /// fetching or retrieval steps have been completed. This enum simplifies handling /// of data contract states post-retrieval. diff --git a/packages/rs-drive/src/drive/prove/prove_multiple_state_transition_results/v0/mod.rs b/packages/rs-drive/src/drive/prove/prove_multiple_state_transition_results/v0/mod.rs index 2497218325..05ec3e2c67 100644 --- a/packages/rs-drive/src/drive/prove/prove_multiple_state_transition_results/v0/mod.rs +++ b/packages/rs-drive/src/drive/prove/prove_multiple_state_transition_results/v0/mod.rs @@ -120,6 +120,7 @@ impl Drive { _ => true, }; let path_query = PathQuery::merge(path_queries.iter().collect()).map_err(Error::GroveDB)?; + self.grove_get_proved_path_query( &path_query, verbose, diff --git a/packages/rs-drive/src/drive/verify/voting/verify_masternode_vote/v0/mod.rs b/packages/rs-drive/src/drive/verify/voting/verify_masternode_vote/v0/mod.rs index d98691171b..54034508d5 100644 --- a/packages/rs-drive/src/drive/verify/voting/verify_masternode_vote/v0/mod.rs +++ b/packages/rs-drive/src/drive/verify/voting/verify_masternode_vote/v0/mod.rs @@ -1,6 +1,7 @@ use crate::drive::Drive; use dpp::data_contract::DataContract; use grovedb::{GroveDb, PathQuery, SizedQuery}; +use tracing::debug; use crate::error::Error; diff --git a/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/mod.rs b/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/mod.rs index 734a66c8fa..6337f21cf8 100644 --- a/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/mod.rs +++ b/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/mod.rs @@ -17,7 +17,7 @@ impl Drive { pub fn fetch_identities_voting_for_contenders( &self, contested_document_resource_vote_poll_with_contract_info: &ContestedDocumentResourceVotePollWithContractInfo, - restrict_to_only_fetch_contenders: Option>, + fetch_contenders: Vec, also_fetch_abstaining_and_locked_votes: bool, transaction: TransactionArg, platform_version: &PlatformVersion, @@ -31,7 +31,7 @@ impl Drive { { 0 => self.fetch_identities_voting_for_contenders_v0( contested_document_resource_vote_poll_with_contract_info, - restrict_to_only_fetch_contenders, + fetch_contenders, also_fetch_abstaining_and_locked_votes, transaction, platform_version, diff --git a/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/v0/mod.rs b/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/v0/mod.rs index cfccd890c1..4dcb5a98f1 100644 --- a/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/v0/mod.rs +++ b/packages/rs-drive/src/drive/votes/fetch/fetch_identities_voting_for_contenders/v0/mod.rs @@ -18,7 +18,7 @@ impl Drive { pub fn fetch_identities_voting_for_contenders_v0( &self, contested_document_resource_vote_poll_with_contract_info: &ContestedDocumentResourceVotePollWithContractInfo, - restrict_to_only_fetch_contenders: Option>, + fetch_contenders: Vec, also_fetch_abstaining_and_locked_votes: bool, transaction: TransactionArg, platform_version: &PlatformVersion, @@ -28,23 +28,12 @@ impl Drive { let mut query = Query::new_with_direction(true); - if let Some(restrict_to_only_fetch_contenders) = restrict_to_only_fetch_contenders { - query.insert_keys( - restrict_to_only_fetch_contenders - .into_iter() - .map(|id| id.to_vec()) - .collect(), - ); - if also_fetch_abstaining_and_locked_votes { - query.insert_keys(vec![ - vec![RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8], - vec![RESOURCE_LOCK_VOTE_TREE_KEY_U8], - ]); - } - } else if also_fetch_abstaining_and_locked_votes { - query.insert_range_after(vec![RESOURCE_STORED_INFO_KEY_U8]..) - } else { - query.insert_range_after(vec![RESOURCE_LOCK_VOTE_TREE_KEY_U8]..) + query.insert_keys(fetch_contenders.into_iter().map(|id| id.to_vec()).collect()); + if also_fetch_abstaining_and_locked_votes { + query.insert_keys(vec![ + vec![RESOURCE_ABSTAIN_VOTE_TREE_KEY_U8], + vec![RESOURCE_LOCK_VOTE_TREE_KEY_U8], + ]); } query.set_subquery_path(vec![vec![VOTING_STORAGE_TREE_KEY]]); diff --git a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs index 1f7c672d57..743bbe26ba 100644 --- a/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs +++ b/packages/rs-drive/src/query/vote_polls_by_document_type_query.rs @@ -325,11 +325,16 @@ impl<'a> ResolvedVotePollsByDocumentTypeQuery<'a> { break; } } + if end_values_iter.next().is_some() { + return Err(Error::Query(QuerySyntaxError::IndexValuesError( + "too many end index values were provided".to_string(), + ))); + } let middle_index_property = middle_index_property.ok_or_else(|| { let error_msg = if has_end_index_values { "since end index values were provided, the start index values and the end index values must be equal to the amount of properties in the contested index minus one, we could not find a middle property".to_string() } else { - "too many start values were provided, since no end index values were provided, the start index values must be less than the amount of properties in the contested index".to_string() + "too many start index values were provided, since no end index values were provided, the start index values must be less than the amount of properties in the contested index".to_string() }; Error::Query(QuerySyntaxError::IndexValuesError(error_msg)) })?; diff --git a/packages/strategy-tests/Cargo.toml b/packages/strategy-tests/Cargo.toml index 7487c04c8b..f3e466a83b 100644 --- a/packages/strategy-tests/Cargo.toml +++ b/packages/strategy-tests/Cargo.toml @@ -37,6 +37,8 @@ dpp = { path = "../rs-dpp", features = [ ] } simple-signer = { path = "../simple-signer" } platform-version = { path = "../rs-platform-version" } +platform-serialization = { path = "../rs-platform-serialization" } +platform-serialization-derive = { path = "../rs-platform-serialization-derive" } [dev-dependencies] platform-version = { path = "../rs-platform-version", features = [ diff --git a/packages/strategy-tests/src/operations.rs b/packages/strategy-tests/src/operations.rs index 18c7dd0f11..3a02424ebe 100644 --- a/packages/strategy-tests/src/operations.rs +++ b/packages/strategy-tests/src/operations.rs @@ -16,10 +16,15 @@ use dpp::serialization::{ PlatformDeserializableWithPotentialValidationFromVersionedStructure, PlatformSerializableWithPlatformVersion, }; +use dpp::voting::vote_choices::resource_vote_choice::ResourceVoteChoice; use dpp::ProtocolError; use dpp::ProtocolError::{PlatformDeserializationError, PlatformSerializationError}; +use drive::drive::object_size_info::DataContractOwnedResolvedInfo; +use drive::drive::votes::resolved::vote_polls::contested_document_resource_vote_poll::ContestedDocumentResourceVotePollWithContractInfo; use platform_version::version::PlatformVersion; -use platform_version::TryIntoPlatformVersioned; +use platform_version::{TryFromPlatformVersioned, TryIntoPlatformVersioned}; +use rand::distributions::{Distribution, WeightedIndex}; +use rand::prelude::StdRng; use std::collections::BTreeMap; use std::ops::Range; @@ -347,6 +352,148 @@ impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for Dat } } +#[derive(Debug, PartialEq, Clone, Encode, Decode)] +pub struct ContestedDocumentResourceVotePollWithSerializableContract { + /// The contract information associated with the document. + pub contract: DataContractInSerializationFormat, + /// The name of the document type. + pub document_type_name: String, + /// The name of the index. + pub index_name: String, + /// The values used in the index for the poll. + pub index_values: Vec, +} + +impl TryFromPlatformVersioned + for ContestedDocumentResourceVotePollWithSerializableContract +{ + type Error = ProtocolError; + fn try_from_platform_versioned( + value: ContestedDocumentResourceVotePollWithContractInfo, + platform_version: &PlatformVersion, + ) -> Result { + let ContestedDocumentResourceVotePollWithContractInfo { + contract, + document_type_name, + index_name, + index_values, + } = value; + Ok(ContestedDocumentResourceVotePollWithSerializableContract { + contract: contract + .into_owned() + .try_into_platform_versioned(platform_version)?, + document_type_name, + index_name, + index_values, + }) + } +} + +impl TryFromPlatformVersioned + for ContestedDocumentResourceVotePollWithContractInfo +{ + type Error = ProtocolError; + fn try_from_platform_versioned( + value: ContestedDocumentResourceVotePollWithSerializableContract, + platform_version: &PlatformVersion, + ) -> Result { + let ContestedDocumentResourceVotePollWithSerializableContract { + contract, + document_type_name, + index_name, + index_values, + } = value; + Ok(ContestedDocumentResourceVotePollWithContractInfo { + contract: DataContractOwnedResolvedInfo::OwnedDataContract( + DataContract::try_from_platform_versioned( + contract, + false, + &mut vec![], + platform_version, + )?, + ), + document_type_name, + index_name, + index_values, + }) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ResourceVoteOp { + pub resolved_vote_poll: ContestedDocumentResourceVotePollWithContractInfo, + pub action: VoteAction, +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct ResourceVoteOpSerializable { + pub resolved_vote_poll: ContestedDocumentResourceVotePollWithSerializableContract, + pub action: VoteAction, +} + +impl TryFromPlatformVersioned for ResourceVoteOp { + type Error = ProtocolError; + + fn try_from_platform_versioned( + value: ResourceVoteOpSerializable, + platform_version: &PlatformVersion, + ) -> Result { + let ResourceVoteOpSerializable { + resolved_vote_poll, + action, + } = value; + + Ok(ResourceVoteOp { + resolved_vote_poll: resolved_vote_poll.try_into_platform_versioned(platform_version)?, + action, + }) + } +} + +impl TryFromPlatformVersioned for ResourceVoteOpSerializable { + type Error = ProtocolError; + + fn try_from_platform_versioned( + value: ResourceVoteOp, + platform_version: &PlatformVersion, + ) -> Result { + let ResourceVoteOp { + resolved_vote_poll, + action, + } = value; + + Ok(ResourceVoteOpSerializable { + resolved_vote_poll: resolved_vote_poll.try_into_platform_versioned(platform_version)?, + action, + }) + } +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct VoteAction { + pub vote_choices_with_weights: Vec<(ResourceVoteChoice, u8)>, +} + +impl VoteAction { + // Function to choose a ResourceVoteChoice based on weights + pub fn choose_weighted_choice(&self, rng: &mut StdRng) -> ResourceVoteChoice { + if self.vote_choices_with_weights.is_empty() { + ResourceVoteChoice::Abstain + } else if self.vote_choices_with_weights.len() == 1 { + self.vote_choices_with_weights[0].0 + } else { + let weights: Vec = self + .vote_choices_with_weights + .iter() + .map(|(_, weight)| *weight) + .collect(); + let dist = WeightedIndex::new(weights).unwrap(); + let index = dist.sample(rng); + self.vote_choices_with_weights[index].0 + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum OperationType { Document(DocumentOp), @@ -356,6 +503,7 @@ pub enum OperationType { ContractCreate(RandomDocumentTypeParameters, DocumentTypeCount), ContractUpdate(DataContractUpdateOp), IdentityTransfer, + ResourceVote(ResourceVoteOp), } #[derive(Clone, Debug, Encode, Decode)] @@ -367,6 +515,7 @@ enum OperationTypeInSerializationFormat { ContractCreate(RandomDocumentTypeParameters, DocumentTypeCount), ContractUpdate(Vec), IdentityTransfer, + ResourceVote(ResourceVoteOpSerializable), } impl PlatformSerializableWithPlatformVersion for OperationType { @@ -410,6 +559,11 @@ impl PlatformSerializableWithPlatformVersion for OperationType { ) } OperationType::IdentityTransfer => OperationTypeInSerializationFormat::IdentityTransfer, + OperationType::ResourceVote(resource_vote_op) => { + let vote_op_in_serialization_format = + resource_vote_op.try_into_platform_versioned(platform_version)?; + OperationTypeInSerializationFormat::ResourceVote(vote_op_in_serialization_format) + } }; let config = bincode::config::standard() .with_big_endian() @@ -466,6 +620,10 @@ impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for Ope OperationType::ContractUpdate(update_op) } OperationTypeInSerializationFormat::IdentityTransfer => OperationType::IdentityTransfer, + OperationTypeInSerializationFormat::ResourceVote(resource_vote_op) => { + let vote_op = resource_vote_op.try_into_platform_versioned(platform_version)?; + OperationType::ResourceVote(vote_op) + } }) } }