diff --git a/packages/dashpay-contract/schema/v3/dashpay.schema.json b/packages/dashpay-contract/schema/v3/dashpay.schema.json index 433cd3756e5..a7c3f794778 100644 --- a/packages/dashpay-contract/schema/v3/dashpay.schema.json +++ b/packages/dashpay-contract/schema/v3/dashpay.schema.json @@ -297,5 +297,55 @@ "accountReference" ], "additionalProperties": false + }, + "privateContactRequest": { + "type": "object", + "description": "A contact request nobody else can see: an entry encrypted to the recipient's DashPay decryption key and appended, without an identity, by a transition paid from the shielded pool. These properties describe the plaintext the recipient decrypts; the chain never reads it.", + "private": { + "entrySize": 589 + }, + "documentsMutable": false, + "canBeDeleted": false, + "properties": { + "accountReference": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295, + "position": 0 + }, + "extendedPublicKey": { + "type": "array", + "byteArray": true, + "minItems": 69, + "maxItems": 69, + "description": "The DIP-15 compact extended public key the recipient pays to: the parent fingerprint (4 bytes), the chain code (32) and the public key (33).", + "position": 1 + }, + "accountLabel": { + "type": "string", + "maxBytes": 63, + "position": 2 + }, + "autoAcceptProof": { + "type": "array", + "byteArray": true, + "minItems": 38, + "maxItems": 102, + "position": 3 + }, + "shieldedAddress": { + "type": "array", + "byteArray": true, + "minItems": 43, + "maxItems": 43, + "description": "Raw Orchard receiving address for this contact: 11-byte diversifier followed by 32-byte diversified transmission key.", + "position": 4 + } + }, + "required": [ + "accountReference", + "extendedPublicKey" + ], + "additionalProperties": false } } diff --git a/packages/dashpay-contract/src/v3/mod.rs b/packages/dashpay-contract/src/v3/mod.rs index 628e3bac60e..3e6cdf5ea1b 100644 --- a/packages/dashpay-contract/src/v3/mod.rs +++ b/packages/dashpay-contract/src/v3/mod.rs @@ -18,6 +18,14 @@ use serde_json::Value; // hold the first byte of each DIP-33 payment address to the address type, the // check the profile data trigger made up to protocol version 14. Their descriptions say // so in place of v2's mention of the data trigger. +// +// And it adds one type: +// +// - `privateContactRequest`, a private type of 589-byte entries: a contact request +// encrypted to the recipient and appended by `PrivateDocumentCreateFromShieldedPool` +// without an identity. Its properties describe the plaintext the recipient decrypts. +// At their largest they take 290 bytes (`accountReference` is an i64, as DashPay +// stores every integer), which with the envelope's 299 bytes fill an entry. pub fn load_documents_schemas() -> Result { serde_json::from_str(include_str!("../../schema/v3/dashpay.schema.json")) diff --git a/packages/rs-dpp/src/system_data_contracts.rs b/packages/rs-dpp/src/system_data_contracts.rs index 3c5a3d7ce94..4c8ac1cf02e 100644 --- a/packages/rs-dpp/src/system_data_contracts.rs +++ b/packages/rs-dpp/src/system_data_contracts.rs @@ -1484,6 +1484,10 @@ mod dashpay_v3_tests { DistinctFrom, DocumentPropertyReferenceTarget, DocumentPropertyType, DocumentTypeRef, EncryptedFor, EncryptedForRecipient, EncryptionScheme, }; + use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0; + use crate::document::DocumentV0; + use platform_value::{Identifier, Value}; + use std::collections::BTreeMap; /// DashPay v3, registered as the chain registers it, under full validation. fn dashpay() -> DataContract { @@ -1503,6 +1507,94 @@ mod dashpay_v3_tests { /// from then on, so every property v1 or v2 declares must be stored exactly as /// it was. DashPay keeps sized integer types off: an integer is an i64 whatever /// its bounds, so the key indexes' new `maximum` changes nothing stored. + /// The bytes of an envelope around a private entry's payload, fixed by the private document + /// envelope (version 1): the commitment (32), the ephemeral key (33), the body's tag (16) and + /// the sender-recovery ciphertext (81) around the body, and the body's own header before the + /// payload (version, auth mode, sender id, sender key id, auth, recipient id, payload length: + /// 1 + 1 + 32 + 4 + 65 + 32 + 2). + const PRIVATE_ENTRY_WRAPPER_BYTES: usize = 162; + const PRIVATE_ENTRY_BODY_HEADER_BYTES: usize = 137; + + /// The type's properties in the document property encoding, as a private entry's payload + /// carries them: each in position order, an optional one led by a presence byte. + fn payload(document_type: DocumentTypeRef, properties: &BTreeMap) -> Vec { + let mut bytes = vec![]; + for (name, property) in document_type.properties() { + match properties.get(name) { + Some(value) => { + if !property.required { + bytes.push(1); + } + bytes.extend( + property + .property_type + .encode_value_ref_with_size(value, property.required) + .expect("the value encodes"), + ); + } + None => bytes.push(0), + } + } + bytes + } + + /// `privateContactRequest` is private, and its entries take the smallest size that holds its + /// largest payload in the envelope: every property at its largest, `accountReference` an + /// 8-byte integer as DashPay stores every integer, is 290 bytes, so 137 + 290 + 162 = 589. + /// The entry size is frozen with the contract, so this pins it to the properties. + #[test] + fn should_size_private_contact_requests_to_hold_their_largest_payload() { + let contract = dashpay(); + let private_contact_request = document_type(&contract, "privateContactRequest"); + let entry_size = private_contact_request + .private_document() + .expect("privateContactRequest is private") + .entry_size() as usize; + assert!(!private_contact_request.documents_mutable()); + assert!(!private_contact_request.documents_can_be_deleted()); + + let largest = BTreeMap::from([ + ("accountReference".to_string(), Value::U32(u32::MAX)), + ("extendedPublicKey".to_string(), Value::Bytes(vec![1; 69])), + ("accountLabel".to_string(), Value::Text("a".repeat(63))), + ("autoAcceptProof".to_string(), Value::Bytes(vec![2; 102])), + ("shieldedAddress".to_string(), Value::Bytes(vec![3; 43])), + ]); + let largest_payload = payload(private_contact_request, &largest); + assert_eq!(largest_payload.len(), 290); + assert_eq!( + PRIVATE_ENTRY_BODY_HEADER_BYTES + largest_payload.len() + PRIVATE_ENTRY_WRAPPER_BYTES, + entry_size + ); + + // The payload is the document's own property encoding: a document of the type + // serializes to its system fields followed by exactly these bytes. + let document = DocumentV0 { + id: Identifier::from([4; 32]), + owner_id: Identifier::from([5; 32]), + properties: largest, + ..Default::default() + }; + let serialized = document + .serialize( + private_contact_request, + &contract, + PlatformVersion::latest(), + ) + .expect("the document serializes"); + assert!(serialized.ends_with(&largest_payload)); + + // The smallest payload: the two required properties and three absent ones. + let smallest = BTreeMap::from([ + ("accountReference".to_string(), Value::U32(0)), + ("extendedPublicKey".to_string(), Value::Bytes(vec![1; 69])), + ]); + assert_eq!( + payload(private_contact_request, &smallest).len(), + 8 + 69 + 3 + ); + } + #[test] fn should_store_every_dashpay_v1_and_v2_property_as_it_was_stored() { let v3 = dashpay(); diff --git a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs index 6a153311fe0..f0aaa21b279 100644 --- a/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs @@ -657,7 +657,7 @@ mod tests { 166, 203, 222, 4, 64, 31, 215, 199, 149, 151, 190, 246, 251, 44, ]), public_keys: BTreeMap::from([(1, key.clone())]), - balance: 25_000_000_000, // 0.25 Dash + balance: 27_000_000_000, // 0.27 Dash revision: 0, } .into(); @@ -737,8 +737,9 @@ mod tests { // 1.8 times its bytes, which are byte-billed, and checking its compiled size is // charged per compiled byte // and a document type's schema size is billed at least the values the - // repeated-key walk read; the DashPay v3 schema adds its declarations - 24004855740 + // repeated-key walk read; the DashPay v3 schema adds its declarations and the + // privateContactRequest type (one more document type registration fee) + 26005728080 ); let check_result = platform @@ -967,7 +968,7 @@ mod tests { 166, 203, 222, 4, 64, 31, 215, 199, 149, 151, 190, 246, 251, 44, ]), public_keys: BTreeMap::from([(1, key.clone())]), - balance: 27_000_000_000, + balance: 29_000_000_000, revision: 0, } .into(); @@ -1329,7 +1330,7 @@ mod tests { 166, 203, 222, 4, 64, 31, 215, 199, 149, 151, 190, 246, 251, 44, ]), public_keys: BTreeMap::from([(1, key.clone())]), - balance: 25_000_000_000, // 0.25 Dash + balance: 27_000_000_000, // 0.27 Dash revision: 0, } .into(); @@ -1412,8 +1413,9 @@ mod tests { // 1.8 times its bytes, which are byte-billed, and checking its compiled size is // charged per compiled byte // and a document type's schema size is billed at least the values the - // repeated-key walk read; the DashPay v3 schema adds its declarations - 24009711480 + // repeated-key walk read; the DashPay v3 schema adds its declarations and the + // privateContactRequest type (one more document type registration fee) + 26011456160 ); let check_result = platform @@ -1497,8 +1499,9 @@ mod tests { public_keys: BTreeMap::from([(1, key.clone())]), // we have enough balance only for 1 insertion (this is where this test is different); // from protocol version 15 one insertion stores the contract compiled, about 1.8 - // times its bytes, and DashPay v3 adds its declarations, so it needs more - balance: 24_400_000_000, + // times its bytes, and DashPay v3 adds its declarations and a document type, so it + // needs more + balance: 26_400_000_000, revision: 0, } .into(); @@ -1898,8 +1901,9 @@ mod tests { // 1.8 times its bytes, which are byte-billed, and checking its compiled size is // charged per compiled byte // and a document type's schema size is billed at least the values the - // repeated-key walk read; the DashPay v3 schema adds its declarations - 24004855740 + // repeated-key walk read; the DashPay v3 schema adds its declarations and the + // privateContactRequest type (one more document type registration fee) + 26005728080 ); platform @@ -1997,8 +2001,9 @@ mod tests { // about 1.8 times its bytes, which are byte-billed, and checking its compiled size // is charged per compiled byte // and a document type's schema size is billed at least the values the - // repeated-key walk read; the DashPay v3 schema adds its declarations - 29005647760 + // repeated-key walk read; the DashPay v3 schema adds its declarations and the + // privateContactRequest type (one more document type registration fee) + 31006514040 ); let check_result = platform @@ -2593,8 +2598,9 @@ mod tests { // 1.8 times its bytes, which are byte-billed, and checking its compiled size is // charged per compiled byte // and a document type's schema size is billed at least the values the - // repeated-key walk read; the DashPay v3 schema adds its declarations - 24004855740 + // repeated-key walk read; the DashPay v3 schema adds its declarations and the + // privateContactRequest type (one more document type registration fee) + 26005728080 ); platform @@ -4395,7 +4401,7 @@ mod tests { 166, 203, 222, 4, 64, 31, 215, 199, 149, 151, 190, 246, 251, 44, ]), public_keys: BTreeMap::from([(1, key.clone())]), - balance: 25_000_000_000, // 0.25 Dash + balance: 27_000_000_000, // 0.27 Dash revision: 0, } .into(); @@ -4663,7 +4669,7 @@ mod tests { let identity: Identity = IdentityV0 { id: Identifier::new([7; 32]), public_keys: BTreeMap::from([(1, key.clone())]), - balance: 25_000_000_000, // 0.25 Dash + balance: 27_000_000_000, // 0.27 Dash revision: 0, } .into(); @@ -4791,7 +4797,7 @@ mod tests { BasicError::SerializedObjectParsingError(_) )) ); - assert_eq!(balance, 25_000_000_000, "nothing is charged"); + assert_eq!(balance, 27_000_000_000, "nothing is charged"); } } @@ -4813,7 +4819,7 @@ mod tests { execution_result, StateTransitionExecutionResult::SuccessfulExecution { .. } ); - assert!(balance < 25_000_000_000, "the create is paid"); + assert!(balance < 27_000_000_000, "the create is paid"); } } } diff --git a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs index d7dfd378053..d3b8ac9458b 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs @@ -2709,6 +2709,23 @@ mod tests { "v3 must read the v2 {document_type_name} back unchanged" ); } + + // v3's new private type gets its store with the upgrade, as a contract update adding a + // private type creates one: 589-byte entries, 512 to a chunk. + assert_eq!( + platform + .drive + .private_document_store_chunk_power_taking_entries( + SystemDataContract::Dashpay.id().as_bytes(), + "privateContactRequest", + 589, + None, + platform_version_15, + ) + .expect("expected to read the store"), + Some(9), + "the upgrade creates the privateContactRequest store" + ); } // test_transition_to_version_9 removed: requires prior state from versions 4-8 diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs index 15648ab7bf9..430f7dfc475 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/deletion.rs @@ -19,7 +19,8 @@ mod deletion_tests { // genesis contracts tree, increasing the bytes billed when reading that tree // (protocol 14 only). The decision proposals and token market contracts add two more // at protocol 15, which reshape the tree so the contract read is billed fewer bytes (-47_280). - 1770160, + // DashPay v3's privateContactRequest type makes its sibling node larger (+2_860). + 1773020, ) .await; } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs index a19c1477808..df4a91b4dcb 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/replacement.rs @@ -527,7 +527,8 @@ mod replacement_tests { // genesis contracts tree, increasing the bytes billed when reading that tree // (protocol 14 only). The decision proposals and token market contracts add two more // at protocol 15, which reshape the tree so the contract read is billed fewer bytes (-47_280). - 1498860, + // DashPay v3's privateContactRequest type makes its sibling node larger (+2_860). + 1501720, ) .await; } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/shielded/nullifier_batch_guard.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/shielded/nullifier_batch_guard.rs index 7f8707cfeef..55f724b2fd7 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/shielded/nullifier_batch_guard.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/token/shielded/nullifier_batch_guard.rs @@ -734,7 +734,7 @@ async fn should_preserve_single_entry_full_processor_fees_and_result() { .value .unwrap() ), - "01b1aa765b58c73b580de019d98fdb10106003b0941b738769a96dc0feba8741" + "c08eb5f5971bb07143a63bc92d6fad5c593e77623ea7a7f178c857eb1ae48cea" ); assert_eq!( pool.platform @@ -2185,39 +2185,39 @@ async fn should_preserve_single_entry_actions_errors_operations_and_settled_stat let (expected_validation, expected_settled) = match case { "shield" => ( "6a823c2db94612652926e6b9c1ae7feec9c2547dfd4cfc4e474318da1c16d59c", - "761e92d28f8cef61e908c2ae3b24938b405868e468f44fd22a68f61da64b682c", + "c9577d238a283729bfce503563e22542356460aef2d4680eddf8c91a6981189b", ), "valid-payment" => ( "9388d5cf63c4a571b9fb009679e2e34d0b681acc88f0f5195e7287c7efbd92f7", - "5a8fa6e02357c351ba35b210eaaadb09cb9786d7b12d06b1483e75411c478d3f", + "22fc25da7ded9ec8ef0c63b691f3c5411c063072458379ff0bc449f4c61f4e33", ), "failed-payment" => ( "d9b5fe191841bea952494f2a45e3833f925f4d367a1937b2ef1235a1aa01f51e", - "1dee6b00d4f6ab7be85238448840e8a124b75260a445a3ec38b0c2283633544b", + "e106a9e7ec95fa9ae56956cb8d2eb55f92b2003c63e52b30b0e2a4015aa4ffea", ), "within-bundle-repeat" => ( "8023e03a697a20f6a78396a05503a4bc583ca8f69e19bd7ee07a6ae0ce7be478", - "5557745a60262645a038803fffb5c3e7f5a85b0822d0d81f515b30cfca8a5316", + "e5168d60f643ae3a98d325fbe4f61b179d85fc281e27784456d05fd2de17bc0b", ), "stored-repeat" => ( "05652ac85c972902e8c09d20a7d8586e17e9434a478b335da136ad85f73def01", - "a8fa01705072cdaddf01155bc21a707da71765cca36e829ed8689bc010721571", + "52f715a3ee906f0e79f474734f1441e61d8a68d4a293b7ea80196bc27c16416a", ), "open-mint-group" => ( "5c68381297c9bf68c5e85ebe6bb9e0f1289e2c07718eecfb5e6773d6009455bb", - "2cc8ac005b08d6bdba5713b81653f43d8db6d62f1f0c64f2e11dc987dd6587be", + "9b8a20a60c362b7cc16224aea3f79ff699c53643b2ecedbd10bec753edac0e5b", ), "closing-mint-group" => ( "2cedc16732c76e0605b5f46085db3d69c12936a5af55a2a82f52ec9e9ca67e38", - "cc45783c058f65155e440957bde48a991854eda553fe868586c4688e7e608963", + "d104393779df056d6444dfa4a69aaf4582913b6f00b61f2f53e062fdef250d47", ), "open-burn-group" => ( "e664a045f020ff84de1ca98f364fbabccfc141ad46aa82c79491a340bb0db5f8", - "a15f8c988bf4aac48480c32842c9e0b1d04b6304fbfd427b289dc7dc9fb54b8a", + "224d1a01832e9faf0f4c9d8af8f8c318855f74f5549976d1b1d1e48896d0ba93", ), "closing-burn-group" => ( "93e1732286f168c4a91d2765931e181c5721bfca1bfd5f93b8baacca03e00b2b", - "d799aab48f5ce1eb60b3076e576b663c982e59ac003b42210565753f68848591", + "5d8c544ca25dc3d9850bae5b5a2eaa17fd8ac36a486bdd48010c6f2e3c929d59", ), _ => unreachable!("enumerated characterization case"), }; diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/private_document_create_from_shielded_pool/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/private_document_create_from_shielded_pool/tests.rs index 445c81ccc77..471b5422c39 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/private_document_create_from_shielded_pool/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/private_document_create_from_shielded_pool/tests.rs @@ -16,7 +16,9 @@ use dpp::consensus::state::state_error::StateError; use dpp::consensus::ConsensusError; use dpp::data_contract::accessors::v0::DataContractV0Getters; use dpp::data_contract::conversion::value::v0::DataContractValueConversionMethodsV0; +use dpp::data_contract::document_type::accessors::DocumentTypeV2Getters; use dpp::data_contract::DataContract; +use dpp::data_contracts::SystemDataContract; use dpp::platform_value::string_encoding::Encoding; use dpp::platform_value::{BinaryData, Identifier}; use dpp::serialization::PlatformSerializable; @@ -31,6 +33,7 @@ use dpp::state_transition::private_document_create_from_shielded_pool_transition use dpp::state_transition::private_document_create_from_shielded_pool_transition::PrivateDocumentCreateFromShieldedPoolTransition; use dpp::state_transition::proof_result::StateTransitionProofResult; use dpp::state_transition::StateTransition; +use dpp::system_data_contracts::load_system_data_contract; use drive::drive::Drive; use grovedb_commitment_tree::{ Builder, BundleType, ClientMemoryCommitmentTree, DashMemo, ExtractedNoteCommitment, @@ -324,6 +327,63 @@ fn should_append_the_entries_behind_the_block_header_and_pay_exactly_the_fee() { } } +/// DashPay's `privateContactRequest` (DashPay v3, which a chain born at protocol version 15 +/// registers at genesis) takes entries: its store exists, and a transition appending two +/// 589-byte entries to it executes and leaves them in the store. +#[test] +fn should_append_private_contact_requests_to_dashpay() { + let platform_version = PlatformVersion::latest(); + let platform = setup_platform(); + let dashpay = load_system_data_contract(SystemDataContract::Dashpay, platform_version) + .expect("DashPay v3"); + let private_contact_request = dashpay + .document_type_for_name("privateContactRequest") + .expect("the privateContactRequest type"); + let entry_size = private_contact_request + .private_document() + .expect("privateContactRequest is private") + .entry_size(); + assert_eq!(entry_size, 589); + + let entries = entries(2, entry_size); + let (st, anchor) = proven_transition(dashpay.id(), "privateContactRequest", entries.clone()); + seed_pool(&platform, &anchor); + let platform_state = platform.state.load(); + let (fee_results, _) = process_state_transitions( + &platform, + &[st], + BlockInfo { + height: 3, + time_ms: 1_700_000_000_000, + ..Default::default() + }, + &platform_state, + ); + assert_eq!(fee_results[0].total_base_fee(), fee(&entries)); + + let page = platform + .drive + .fetch_private_document_entries( + dashpay.id().to_buffer(), + private_contact_request, + 0, + None, + platform_version, + ) + .expect("expected to read DashPay's store"); + assert_eq!(page.total_count, 2); + assert_eq!( + page.entries + .iter() + .map(|stored| stored.data.clone()) + .collect::>(), + entries + .iter() + .map(|entry| entry.to_vec()) + .collect::>() + ); +} + #[test] fn should_prove_the_spent_nullifiers_as_the_affected_state() { let platform_version = PlatformVersion::latest(); diff --git a/packages/rs-drive/tests/deterministic_root_hash.rs b/packages/rs-drive/tests/deterministic_root_hash.rs index 80e3c346ffe..41b041b42f6 100644 --- a/packages/rs-drive/tests/deterministic_root_hash.rs +++ b/packages/rs-drive/tests/deterministic_root_hash.rs @@ -313,8 +313,9 @@ mod tests { 14 => "c25bc863d30a4185311546d51bacfdc030ee57671dbae774775d0efd4474220c", // From protocol version 15 the contract is stored compiled (format version 2), its // document types beside its schemas, and is DashPay v3, which declares the checks of - // its data triggers. - _ => "ba8d5ff45dad759997166e63772ff7c582faf9b28c0a5d4d51b970b90d4ca9bd", + // its data triggers and adds the private `privateContactRequest` type, whose store + // the insert creates. + _ => "66306426bce1beaa7319e7e6d822bcf8a7c93f14425c2f82841a3222a7502912", }; assert_eq!( diff --git a/packages/rs-platform-version/src/version/v15.rs b/packages/rs-platform-version/src/version/v15.rs index 255733a8cc3..1e9f324468b 100644 --- a/packages/rs-platform-version/src/version/v15.rs +++ b/packages/rs-platform-version/src/version/v15.rs @@ -483,7 +483,8 @@ pub const PROTOCOL_VERSION_15: ProtocolVersion = 15; /// is refused unpaid either way. Protocol v14 and earlier keep `basic_structure` 0 and /// `advanced_structure` 0 or 1. /// -/// 24. **DashPay v3 states what its data triggers checked, and its ciphertexts' shape**: +/// 24. **DashPay v3 states what its data triggers checked and its ciphertexts' shape, and adds +/// private contact requests**: /// `SYSTEM_DATA_CONTRACT_VERSIONS_V4` selects DashPay contract v3, loaded at /// genesis and re-stored by `transition_to_version_15` (`apply_contract`, as the /// upgrade to v2 did). A `contactRequest`'s `toUserId` refers to an identity @@ -505,6 +506,14 @@ pub const PROTOCOL_VERSION_15: ProtocolVersion = 15; /// profile create and replace triggers, which made the identity, writer and type byte /// checks and refused with `DataTriggerConditionError` (40500). Protocol v14 keeps /// DashPay v2 and bindings 2, and earlier versions the contract and bindings they had. +/// DashPay v3 also declares `privateContactRequest`, a private type (note 13) of 589-byte +/// entries whose properties describe a contact request only its recipient can read: +/// `accountReference`, the DIP-15 compact `extendedPublicKey` (69 bytes), and the optional +/// `accountLabel` (at most 63 bytes), `autoAcceptProof` (38 to 102) and `shieldedAddress` +/// (43). At their largest they encode to 290 bytes, which with the private document +/// envelope's 299 fill an entry. Genesis creates its store as a contract insert does, and +/// `transition_to_version_15` as a contract update adding a private type does (note 13); +/// only note 25's transition appends to it. /// /// 25. **Private document entries**: a new state transition, /// `PrivateDocumentCreateFromShieldedPool` (type 29), appends entries to the store of a