diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index 45c930e..54e1141 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -52,6 +52,13 @@ members = [ "access_whitelist", "zk_event_filter", "parameter_registry", + + # Issue #292 — multi-signature wrapper around high-value event emission. + "multisig_event_registry", + # Issue #293 — gas-optimized batched event emission utility. + "event_batch", + # Issue #295 — W3C-compatible DID registry/resolver. + "did_resolver", ] [profile.release] diff --git a/contracts/did_resolver/Cargo.toml b/contracts/did_resolver/Cargo.toml new file mode 100644 index 0000000..b1afaf2 --- /dev/null +++ b/contracts/did_resolver/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "event-horizon-did-resolver" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +soroban-sdk = "23.4.0" + +[dev-dependencies] +soroban-sdk = { version = "23.4.0", features = ["testutils"] } diff --git a/contracts/did_resolver/src/lib.rs b/contracts/did_resolver/src/lib.rs new file mode 100644 index 0000000..15d4672 --- /dev/null +++ b/contracts/did_resolver/src/lib.rs @@ -0,0 +1,159 @@ +#![no_std] +#![allow(deprecated)] +//! Decentralized Identity (DID) resolver on Soroban. +//! +//! See issue #295. Implements a minimal W3C-DID-Core-compatible +//! registry. DID documents are stored as opaque bytes (intended to be +//! a canonical-JSON encoding) keyed by DID string. Each DID has a +//! single on-chain controller address that may update or revoke it. +//! +//! ## W3C alignment +//! - `did:soroban:` strings are the registry keys. +//! The method-specific id is the part after `did:soroban:` — the +//! registry does not parse it, just stores the full string. +//! - `register` corresponds to the W3C "create" CRUD verb. +//! - `update` / `revoke` cover "update" and "deactivate". +//! - `resolve` returns the current `DidDocument`; revoked DIDs +//! resolve as `Revoked` so consumers can distinguish from "never +//! existed". +//! - Every state transition emits an event (`did_reg`, `did_upd`, +//! `did_rev`, `did_attest`) so off-chain indexers can mirror the +//! registry without scanning storage. + +use soroban_sdk::{ + contract, contractimpl, contracttype, symbol_short, Address, Bytes, Env, String, +}; + +#[contracttype] +#[derive(Clone)] +pub enum DataKey { + Document(String), +} + +#[contracttype] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum DidStatus { + Active = 0, + Revoked = 1, +} + +#[contracttype] +#[derive(Clone)] +pub struct DidDocument { + pub did: String, + pub controller: Address, + /// W3C DID Document as canonical JSON or CBOR bytes. The registry + /// does not parse this — that's the consumer's responsibility. + pub document: Bytes, + pub status: DidStatus, + pub created_at_ledger: u32, + pub updated_at_ledger: u32, + /// Monotonic version counter; bumps on every successful `update`. + pub version: u32, +} + +#[contract] +pub struct DidResolver; + +#[contractimpl] +impl DidResolver { + /// Register a new DID under `controller`. Panics if `did` already + /// exists (active or revoked) — DIDs are non-recyclable. + pub fn register(env: Env, controller: Address, did: String, document: Bytes) { + controller.require_auth(); + if did.len() == 0 { + panic!("did empty"); + } + if env.storage().persistent().has(&DataKey::Document(did.clone())) { + panic!("did already registered"); + } + let now = env.ledger().sequence(); + let doc = DidDocument { + did: did.clone(), + controller: controller.clone(), + document, + status: DidStatus::Active, + created_at_ledger: now, + updated_at_ledger: now, + version: 1, + }; + env.storage() + .persistent() + .set(&DataKey::Document(did.clone()), &doc); + + env.events() + .publish((symbol_short!("did_reg"),), (did.clone(), controller.clone())); + env.events() + .publish((symbol_short!("did_attst"),), (did, controller, 1u32)); + } + + /// Replace the DID document. Only the recorded controller can call. + /// Bumps `version` and `updated_at_ledger`. Cannot update a revoked DID. + pub fn update(env: Env, controller: Address, did: String, new_document: Bytes) { + controller.require_auth(); + let mut doc: DidDocument = env + .storage() + .persistent() + .get(&DataKey::Document(did.clone())) + .expect("did not found"); + if doc.controller != controller { + panic!("not the controller"); + } + if doc.status != DidStatus::Active { + panic!("did is revoked"); + } + doc.document = new_document; + doc.updated_at_ledger = env.ledger().sequence(); + doc.version += 1; + let new_version = doc.version; + env.storage() + .persistent() + .set(&DataKey::Document(did.clone()), &doc); + + env.events() + .publish((symbol_short!("did_upd"),), (did.clone(), new_version)); + env.events() + .publish((symbol_short!("did_attst"),), (did, controller, new_version)); + } + + /// Mark a DID as revoked. Subsequent `update`s panic; `resolve` + /// still returns the document but with `status = Revoked`. + pub fn revoke(env: Env, controller: Address, did: String) { + controller.require_auth(); + let mut doc: DidDocument = env + .storage() + .persistent() + .get(&DataKey::Document(did.clone())) + .expect("did not found"); + if doc.controller != controller { + panic!("not the controller"); + } + if doc.status == DidStatus::Revoked { + panic!("already revoked"); + } + doc.status = DidStatus::Revoked; + doc.updated_at_ledger = env.ledger().sequence(); + env.storage() + .persistent() + .set(&DataKey::Document(did.clone()), &doc); + + env.events() + .publish((symbol_short!("did_rev"),), (did, controller)); + } + + /// Resolve a DID. Returns `None` if never registered. Revoked DIDs + /// resolve to `Some(doc)` with `status = Revoked` so consumers can + /// distinguish from "never existed". + pub fn resolve(env: Env, did: String) -> Option { + env.storage().persistent().get(&DataKey::Document(did)) + } + + /// Lightweight existence + status check that avoids returning the + /// full document. + pub fn status(env: Env, did: String) -> Option { + Self::resolve(env, did).map(|d| d.status) + } +} + +mod test; diff --git a/contracts/did_resolver/src/test.rs b/contracts/did_resolver/src/test.rs new file mode 100644 index 0000000..6c9b1d3 --- /dev/null +++ b/contracts/did_resolver/src/test.rs @@ -0,0 +1,132 @@ +#![cfg(test)] +use super::*; +use soroban_sdk::testutils::{Address as _, Events}; +use soroban_sdk::{symbol_short, Address, Bytes, Env, IntoVal, String}; + +fn setup() -> (Env, Address, DidResolverClient<'static>) { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(DidResolver, ()); + let client = DidResolverClient::new(&env, &contract_id); + let controller = Address::generate(&env); + (env, controller, client) +} + +fn has_event(env: &Env, name: &str) -> bool { + let expected = soroban_sdk::Symbol::new(env, name); + for (_, topics, _) in env.events().all().iter() { + let t: soroban_sdk::Symbol = topics.get(0).unwrap().into_val(env); + if t == expected { + return true; + } + } + false +} + +#[test] +fn register_then_resolve() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:alice"); + let doc = Bytes::from_slice(&env, b"{\"id\":\"did:soroban:alice\"}"); + client.register(&controller, &did, &doc); + // `env.events().all()` is snapshotted per invocation, so assert on + // emitted events before making the read-only `resolve` call. + assert!(has_event(&env, "did_reg")); + assert!(has_event(&env, "did_attst")); + + let resolved = client.resolve(&did).unwrap(); + assert_eq!(resolved.controller, controller); + assert_eq!(resolved.status, DidStatus::Active); + assert_eq!(resolved.version, 1); +} + +#[test] +fn resolve_unknown_did_returns_none() { + let (env, _c, client) = setup(); + assert!(client.resolve(&String::from_str(&env, "did:soroban:nope")).is_none()); +} + +#[test] +#[should_panic(expected = "did already registered")] +fn cannot_register_twice() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:bob"); + let doc = Bytes::from_slice(&env, b"a"); + client.register(&controller, &did, &doc); + client.register(&controller, &did, &doc); +} + +#[test] +fn update_bumps_version_and_emits_identity_verification() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:carol"); + client.register(&controller, &did, &Bytes::from_slice(&env, b"v1")); + + client.update(&controller, &did, &Bytes::from_slice(&env, b"v2")); + // Check events immediately after `update` before any read-only call + // wipes the per-invocation event buffer. + assert!(has_event(&env, "did_upd")); + // did_attst is the "event-based identity verification update" hook + // called out in the issue's technical requirements. + assert!(has_event(&env, "did_attst")); + + let doc = client.resolve(&did).unwrap(); + assert_eq!(doc.version, 2); +} + +#[test] +#[should_panic(expected = "not the controller")] +fn non_controller_cannot_update() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:dave"); + client.register(&controller, &did, &Bytes::from_slice(&env, b"v1")); + let stranger = Address::generate(&env); + client.update(&stranger, &did, &Bytes::from_slice(&env, b"v2")); +} + +#[test] +fn revoke_marks_revoked_but_still_resolves() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:eve"); + client.register(&controller, &did, &Bytes::from_slice(&env, b"v1")); + client.revoke(&controller, &did); + let doc = client.resolve(&did).unwrap(); + assert_eq!(doc.status, DidStatus::Revoked); + assert_eq!(client.status(&did), Some(DidStatus::Revoked)); +} + +#[test] +#[should_panic(expected = "did is revoked")] +fn cannot_update_after_revoke() { + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:frank"); + client.register(&controller, &did, &Bytes::from_slice(&env, b"v1")); + client.revoke(&controller, &did); + client.update(&controller, &did, &Bytes::from_slice(&env, b"v2")); +} + +#[test] +#[should_panic(expected = "did empty")] +fn empty_did_rejected() { + let (env, controller, client) = setup(); + client.register(&controller, &String::from_str(&env, ""), &Bytes::from_slice(&env, b"x")); +} + +#[test] +fn issue_295_emits_identity_verification_on_update() { + // Direct test for the "Allow event-based identity verification updates" + // bullet — every update emits a did_attst event with did + controller + + // version, which a verifier can subscribe to. Soroban testutils snapshot + // `events().all()` per invocation, so we assert once per call. + let (env, controller, client) = setup(); + let did = String::from_str(&env, "did:soroban:verify"); + + client.register(&controller, &did, &Bytes::from_slice(&env, b"v1")); + assert!(has_event(&env, "did_attst")); + + client.update(&controller, &did, &Bytes::from_slice(&env, b"v2")); + assert!(has_event(&env, "did_attst")); + + client.update(&controller, &did, &Bytes::from_slice(&env, b"v3")); + assert!(has_event(&env, "did_attst")); +} diff --git a/contracts/did_resolver/test_snapshots/test/cannot_register_twice.1.json b/contracts/did_resolver/test_snapshots/test/cannot_register_twice.1.json new file mode 100644 index 0000000..a4e545c --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/cannot_register_twice.1.json @@ -0,0 +1,237 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:bob" + }, + { + "bytes": "61" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:bob" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:bob" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:bob" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "61" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/cannot_update_after_revoke.1.json b/contracts/did_resolver/test_snapshots/test/cannot_update_after_revoke.1.json new file mode 100644 index 0000000..a2cb863 --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/cannot_update_after_revoke.1.json @@ -0,0 +1,292 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:frank" + }, + { + "bytes": "7631" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "revoke", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:frank" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:frank" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:frank" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:frank" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7631" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/empty_did_rejected.1.json b/contracts/did_resolver/test_snapshots/test/empty_did_rejected.1.json new file mode 100644 index 0000000..8b571d4 --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/empty_did_rejected.1.json @@ -0,0 +1,77 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/issue_295_emits_identity_verification_on_update.1.json b/contracts/did_resolver/test_snapshots/test/issue_295_emits_identity_verification_on_update.1.json new file mode 100644 index 0000000..2e9e67f --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/issue_295_emits_identity_verification_on_update.1.json @@ -0,0 +1,410 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:verify" + }, + { + "bytes": "7631" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "update", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:verify" + }, + { + "bytes": "7632" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "update", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:verify" + }, + { + "bytes": "7633" + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:verify" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:verify" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:verify" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7633" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 3 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "did_upd" + } + ], + "data": { + "vec": [ + { + "string": "did:soroban:verify" + }, + { + "u32": 3 + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "did_attst" + } + ], + "data": { + "vec": [ + { + "string": "did:soroban:verify" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "u32": 3 + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/non_controller_cannot_update.1.json b/contracts/did_resolver/test_snapshots/test/non_controller_cannot_update.1.json new file mode 100644 index 0000000..cb6c8ea --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/non_controller_cannot_update.1.json @@ -0,0 +1,237 @@ +{ + "generators": { + "address": 3, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:dave" + }, + { + "bytes": "7631" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:dave" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:dave" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:dave" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7631" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/register_then_resolve.1.json b/contracts/did_resolver/test_snapshots/test/register_then_resolve.1.json new file mode 100644 index 0000000..557899b --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/register_then_resolve.1.json @@ -0,0 +1,237 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:alice" + }, + { + "bytes": "7b226964223a226469643a736f726f62616e3a616c696365227d" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:alice" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:alice" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:alice" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7b226964223a226469643a736f726f62616e3a616c696365227d" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/resolve_unknown_did_returns_none.1.json b/contracts/did_resolver/test_snapshots/test/resolve_unknown_did_returns_none.1.json new file mode 100644 index 0000000..8b571d4 --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/resolve_unknown_did_returns_none.1.json @@ -0,0 +1,77 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/revoke_marks_revoked_but_still_resolves.1.json b/contracts/did_resolver/test_snapshots/test/revoke_marks_revoked_but_still_resolves.1.json new file mode 100644 index 0000000..1e3886b --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/revoke_marks_revoked_but_still_resolves.1.json @@ -0,0 +1,293 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:eve" + }, + { + "bytes": "7631" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "revoke", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:eve" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:eve" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:eve" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:eve" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7631" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 1 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/did_resolver/test_snapshots/test/update_bumps_version_and_emits_identity_verification.1.json b/contracts/did_resolver/test_snapshots/test/update_bumps_version_and_emits_identity_verification.1.json new file mode 100644 index 0000000..a50e08a --- /dev/null +++ b/contracts/did_resolver/test_snapshots/test/update_bumps_version_and_emits_identity_verification.1.json @@ -0,0 +1,295 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "register", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:carol" + }, + { + "bytes": "7631" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "update", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "string": "did:soroban:carol" + }, + { + "bytes": "7632" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:carol" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Document" + }, + { + "string": "did:soroban:carol" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "controller" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "did" + }, + "val": { + "string": "did:soroban:carol" + } + }, + { + "key": { + "symbol": "document" + }, + "val": { + "bytes": "7632" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "updated_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "version" + }, + "val": { + "u32": 2 + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/event_batch/Cargo.toml b/contracts/event_batch/Cargo.toml new file mode 100644 index 0000000..7d0e0b5 --- /dev/null +++ b/contracts/event_batch/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "event-horizon-event-batch" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +soroban-sdk = "23.4.0" + +[dev-dependencies] +soroban-sdk = { version = "23.4.0", features = ["testutils"] } diff --git a/contracts/event_batch/src/lib.rs b/contracts/event_batch/src/lib.rs new file mode 100644 index 0000000..6f904e5 --- /dev/null +++ b/contracts/event_batch/src/lib.rs @@ -0,0 +1,83 @@ +#![no_std] +#![allow(deprecated)] +//! Gas-optimized event batching utility for EventHorizon. +//! +//! See issue #293. Lets callers emit N events in a single contract +//! invocation, amortizing the per-tx overhead. Topics and payloads are +//! passed as a single `Vec` so all events flow through one +//! invocation boundary. +//! +//! ## Design notes +//! - `EventEntry::data` is `Bytes` to keep the payload type-erased; the +//! consumer reinterprets it per topic. This is the most storage- +//! efficient way to carry mixed payloads in one batch. +//! - `emit_batch` caps batch size at `MAX_BATCH_SIZE` to prevent a +//! pathological caller from blowing the per-tx ledger limit. +//! - The contract itself emits exactly one extra `batch_emit` summary +//! event with `(batch_size, sender)` for downstream indexers. + +use soroban_sdk::{ + contract, contractimpl, contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec, +}; + +/// Hard cap on entries per `emit_batch` call. Soroban budgets are tight; +/// larger batches must be split client-side. +pub const MAX_BATCH_SIZE: u32 = 64; + +#[contracttype] +#[derive(Clone)] +pub struct EventEntry { + /// First topic for the event (e.g. event-type symbol). + pub topic: Symbol, + /// Optional second topic (e.g. subject address). Empty Bytes when unused. + pub topic2: Bytes, + /// Type-erased payload bytes. + pub data: Bytes, +} + +#[contract] +pub struct EventBatchContract; + +#[contractimpl] +impl EventBatchContract { + /// Emit every event in `entries` followed by a single `batch_emit` + /// summary event. Panics if `entries.len() > MAX_BATCH_SIZE` or empty. + /// + /// # Arguments + /// + /// * `env` - Soroban env. + /// * `sender` - The caller; must sign the transaction. Recorded in + /// the summary event for traceability. + /// * `entries` - Ordered list of events to emit in this batch. + /// + /// # Returns + /// + /// * `u32` - The number of events emitted (excludes the summary event). + pub fn emit_batch(env: Env, sender: Address, entries: Vec) -> u32 { + sender.require_auth(); + + let count = entries.len(); + if count == 0 { + panic!("empty batch"); + } + if count > MAX_BATCH_SIZE { + panic!("batch too large"); + } + + for entry in entries.iter() { + if entry.topic2.is_empty() { + env.events().publish((entry.topic.clone(),), entry.data.clone()); + } else { + env.events() + .publish((entry.topic.clone(), entry.topic2.clone()), entry.data.clone()); + } + } + + env.events() + .publish((symbol_short!("batch_emt"),), (count, sender)); + + count + } +} + +mod test; diff --git a/contracts/event_batch/src/test.rs b/contracts/event_batch/src/test.rs new file mode 100644 index 0000000..32454e3 --- /dev/null +++ b/contracts/event_batch/src/test.rs @@ -0,0 +1,94 @@ +#![cfg(test)] +use super::*; +use soroban_sdk::testutils::{Address as _, Events}; +use soroban_sdk::{symbol_short, vec, Address, Bytes, Env}; + +fn setup() -> (Env, Address, EventBatchContractClient<'static>) { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(EventBatchContract, ()); + let client = EventBatchContractClient::new(&env, &contract_id); + let sender = Address::generate(&env); + (env, sender, client) +} + +#[test] +fn emits_two_events_plus_summary() { + let (env, sender, client) = setup(); + let entries = vec![ + &env, + EventEntry { + topic: symbol_short!("transfer"), + topic2: Bytes::new(&env), + data: Bytes::from_slice(&env, &[1, 2, 3]), + }, + EventEntry { + topic: symbol_short!("approve"), + topic2: Bytes::new(&env), + data: Bytes::from_slice(&env, &[4, 5]), + }, + ]; + + let n = client.emit_batch(&sender, &entries); + assert_eq!(n, 2); + + // 2 user events + 1 batch_emt summary + assert_eq!(env.events().all().len(), 3); +} + +#[test] +fn second_topic_included_when_non_empty() { + let (env, sender, client) = setup(); + let entries = vec![ + &env, + EventEntry { + topic: symbol_short!("trans"), + topic2: Bytes::from_slice(&env, b"addr"), + data: Bytes::from_slice(&env, &[42]), + }, + ]; + client.emit_batch(&sender, &entries); + + let all = env.events().all(); + // First event = our entry with 2 topics; second = batch_emt summary + let (_, topics, _) = all.get(0).unwrap(); + assert_eq!(topics.len(), 2); +} + +#[test] +#[should_panic(expected = "empty batch")] +fn empty_batch_rejected() { + let (env, sender, client) = setup(); + let entries: Vec = vec![&env]; + client.emit_batch(&sender, &entries); +} + +#[test] +#[should_panic(expected = "batch too large")] +fn oversized_batch_rejected() { + let (env, sender, client) = setup(); + let mut entries: Vec = vec![&env]; + for _ in 0..(MAX_BATCH_SIZE + 1) { + entries.push_back(EventEntry { + topic: symbol_short!("x"), + topic2: Bytes::new(&env), + data: Bytes::new(&env), + }); + } + client.emit_batch(&sender, &entries); +} + +#[test] +fn max_batch_size_accepted() { + let (env, sender, client) = setup(); + let mut entries: Vec = vec![&env]; + for _ in 0..MAX_BATCH_SIZE { + entries.push_back(EventEntry { + topic: symbol_short!("x"), + topic2: Bytes::new(&env), + data: Bytes::new(&env), + }); + } + let n = client.emit_batch(&sender, &entries); + assert_eq!(n, MAX_BATCH_SIZE); +} diff --git a/contracts/event_batch/test_snapshots/test/emits_two_events_plus_summary.1.json b/contracts/event_batch/test_snapshots/test/emits_two_events_plus_summary.1.json new file mode 100644 index 0000000..c1af896 --- /dev/null +++ b/contracts/event_batch/test_snapshots/test/emits_two_events_plus_summary.1.json @@ -0,0 +1,256 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "emit_batch", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "010203" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "transfer" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "0405" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "approve" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "transfer" + } + ], + "data": { + "bytes": "010203" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "approve" + } + ], + "data": { + "bytes": "0405" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "batch_emt" + } + ], + "data": { + "vec": [ + { + "u32": 2 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/contracts/event_batch/test_snapshots/test/empty_batch_rejected.1.json b/contracts/event_batch/test_snapshots/test/empty_batch_rejected.1.json new file mode 100644 index 0000000..8b571d4 --- /dev/null +++ b/contracts/event_batch/test_snapshots/test/empty_batch_rejected.1.json @@ -0,0 +1,77 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/event_batch/test_snapshots/test/max_batch_size_accepted.1.json b/contracts/event_batch/test_snapshots/test/max_batch_size_accepted.1.json new file mode 100644 index 0000000..2f13f72 --- /dev/null +++ b/contracts/event_batch/test_snapshots/test/max_batch_size_accepted.1.json @@ -0,0 +1,3232 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "emit_batch", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + }, + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "x" + } + ], + "data": { + "bytes": "" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "batch_emt" + } + ], + "data": { + "vec": [ + { + "u32": 64 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/contracts/event_batch/test_snapshots/test/oversized_batch_rejected.1.json b/contracts/event_batch/test_snapshots/test/oversized_batch_rejected.1.json new file mode 100644 index 0000000..8b571d4 --- /dev/null +++ b/contracts/event_batch/test_snapshots/test/oversized_batch_rejected.1.json @@ -0,0 +1,77 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/event_batch/test_snapshots/test/second_topic_included_when_non_empty.1.json b/contracts/event_batch/test_snapshots/test/second_topic_included_when_non_empty.1.json new file mode 100644 index 0000000..3cbfe8b --- /dev/null +++ b/contracts/event_batch/test_snapshots/test/second_topic_included_when_non_empty.1.json @@ -0,0 +1,211 @@ +{ + "generators": { + "address": 2, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "emit_batch", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "map": [ + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "2a" + } + }, + { + "key": { + "symbol": "topic" + }, + "val": { + "symbol": "trans" + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "61646472" + } + } + ] + } + ] + } + ] + } + }, + "sub_invocations": [] + } + ] + ] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "trans" + }, + { + "bytes": "61646472" + } + ], + "data": { + "bytes": "2a" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "type_": "contract", + "body": { + "v0": { + "topics": [ + { + "symbol": "batch_emt" + } + ], + "data": { + "vec": [ + { + "u32": 1 + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/Cargo.toml b/contracts/multisig_event_registry/Cargo.toml new file mode 100644 index 0000000..db5c455 --- /dev/null +++ b/contracts/multisig_event_registry/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "event-horizon-multisig-event-registry" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +soroban-sdk = "23.4.0" + +[dev-dependencies] +soroban-sdk = { version = "23.4.0", features = ["testutils"] } diff --git a/contracts/multisig_event_registry/src/lib.rs b/contracts/multisig_event_registry/src/lib.rs new file mode 100644 index 0000000..3d62fcb --- /dev/null +++ b/contracts/multisig_event_registry/src/lib.rs @@ -0,0 +1,288 @@ +#![no_std] +#![allow(deprecated)] +//! Multi-signature registry for high-value event emission. +//! +//! See issue #292. Wraps event emission behind an M-of-N signer +//! approval gate so sensitive events (e.g. treasury withdrawals, +//! oracle rotation, large-value transfers) cannot be emitted without +//! sign-off from the configured signer set. +//! +//! ## Lifecycle +//! 1. Admin calls `init(admin, signers, default_threshold)`. +//! 2. Admin (or anyone, depending on policy) calls +//! `set_threshold(event_type, threshold)` to override the per-type +//! threshold. If unset, `default_threshold` applies. +//! 3. Any signer calls `propose(event_type, topic2, data)` → returns +//! a `proposal_id`. +//! 4. Other signers call `approve(proposal_id)`; once approvals reach +//! the threshold the proposal auto-executes and the event is +//! emitted with a `multisig_attest` confirming proposal_id and the +//! full signer list, providing non-repudiation. +//! 5. `cancel(proposal_id)` is callable by the proposer or admin +//! before execution. + +use soroban_sdk::{ + contract, contractimpl, contracttype, symbol_short, Address, Bytes, Env, Symbol, Vec, +}; + +#[contracttype] +#[derive(Clone)] +pub enum DataKey { + Admin, + Signers, + DefaultThreshold, + /// Per-event-type threshold override. + Threshold(Symbol), + NextProposalId, + Proposal(u64), + /// True once `signer` has approved `proposal_id`. + Approved(u64, Address), +} + +#[contracttype] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum ProposalStatus { + Pending = 0, + Executed = 1, + Cancelled = 2, +} + +#[contracttype] +#[derive(Clone)] +pub struct Proposal { + pub proposer: Address, + pub event_type: Symbol, + pub topic2: Bytes, + pub data: Bytes, + pub approvals: Vec
, + pub status: ProposalStatus, + pub created_at_ledger: u32, +} + +#[contract] +pub struct MultisigEventRegistry; + +#[contractimpl] +impl MultisigEventRegistry { + /// One-shot initializer. Panics if called twice. + pub fn init(env: Env, admin: Address, signers: Vec
, default_threshold: u32) { + if env.storage().instance().has(&DataKey::Admin) { + panic!("already initialized"); + } + admin.require_auth(); + Self::validate_threshold(default_threshold, signers.len()); + env.storage().instance().set(&DataKey::Admin, &admin); + env.storage().instance().set(&DataKey::Signers, &signers); + env.storage() + .instance() + .set(&DataKey::DefaultThreshold, &default_threshold); + env.storage() + .instance() + .set(&DataKey::NextProposalId, &0u64); + } + + /// Override the approval threshold for a specific event type. + pub fn set_threshold(env: Env, admin: Address, event_type: Symbol, threshold: u32) { + Self::assert_admin(&env, &admin); + let signers: Vec
= env.storage().instance().get(&DataKey::Signers).unwrap(); + Self::validate_threshold(threshold, signers.len()); + env.storage() + .persistent() + .set(&DataKey::Threshold(event_type), &threshold); + } + + /// Propose a high-value event emission. Caller must be a signer. + /// Records the proposer's approval, so a 1-of-1 threshold executes + /// immediately on proposal. + pub fn propose( + env: Env, + proposer: Address, + event_type: Symbol, + topic2: Bytes, + data: Bytes, + ) -> u64 { + proposer.require_auth(); + Self::require_signer(&env, &proposer); + + let proposal_id: u64 = env + .storage() + .instance() + .get(&DataKey::NextProposalId) + .unwrap_or(0u64); + env.storage() + .instance() + .set(&DataKey::NextProposalId, &(proposal_id + 1)); + + let proposal = Proposal { + proposer: proposer.clone(), + event_type: event_type.clone(), + topic2: topic2.clone(), + data: data.clone(), + approvals: Vec::from_array(&env, [proposer.clone()]), + status: ProposalStatus::Pending, + created_at_ledger: env.ledger().sequence(), + }; + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Approved(proposal_id, proposer.clone()), &true); + + env.events() + .publish((symbol_short!("proposed"),), (proposal_id, proposer)); + + Self::try_execute(&env, proposal_id); + proposal_id + } + + /// Approve a pending proposal. Auto-executes when threshold is met. + pub fn approve(env: Env, signer: Address, proposal_id: u64) { + signer.require_auth(); + Self::require_signer(&env, &signer); + + let already: bool = env + .storage() + .persistent() + .get(&DataKey::Approved(proposal_id, signer.clone())) + .unwrap_or(false); + if already { + panic!("already approved"); + } + let mut proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .expect("unknown proposal"); + if proposal.status != ProposalStatus::Pending { + panic!("proposal not pending"); + } + proposal.approvals.push_back(signer.clone()); + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.storage() + .persistent() + .set(&DataKey::Approved(proposal_id, signer.clone()), &true); + + env.events() + .publish((symbol_short!("approved"),), (proposal_id, signer)); + + Self::try_execute(&env, proposal_id); + } + + /// Cancel a pending proposal. Callable by the proposer or admin. + pub fn cancel(env: Env, caller: Address, proposal_id: u64) { + caller.require_auth(); + let mut proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .expect("unknown proposal"); + if proposal.status != ProposalStatus::Pending { + panic!("proposal not pending"); + } + let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap(); + if caller != proposal.proposer && caller != admin { + panic!("not authorized to cancel"); + } + proposal.status = ProposalStatus::Cancelled; + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + env.events() + .publish((symbol_short!("cancel"),), proposal_id); + } + + pub fn get_proposal(env: Env, proposal_id: u64) -> Option { + env.storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + } + + pub fn get_threshold(env: Env, event_type: Symbol) -> u32 { + env.storage() + .persistent() + .get(&DataKey::Threshold(event_type)) + .unwrap_or_else(|| { + env.storage() + .instance() + .get(&DataKey::DefaultThreshold) + .unwrap() + }) + } + + pub fn get_signers(env: Env) -> Vec
{ + env.storage().instance().get(&DataKey::Signers).unwrap() + } + + // ── internal helpers ──────────────────────────────────────────────── + + fn assert_admin(env: &Env, who: &Address) { + who.require_auth(); + let admin: Address = env.storage().instance().get(&DataKey::Admin).unwrap(); + if who != &admin { + panic!("admin only"); + } + } + + fn require_signer(env: &Env, who: &Address) { + let signers: Vec
= env.storage().instance().get(&DataKey::Signers).unwrap(); + for s in signers.iter() { + if &s == who { + return; + } + } + panic!("not a signer"); + } + + fn validate_threshold(threshold: u32, signer_count: u32) { + if threshold == 0 { + panic!("threshold must be > 0"); + } + if threshold > signer_count { + panic!("threshold exceeds signer count"); + } + } + + /// Emit the underlying event + a `multisig_attest` proof. Idempotent + /// once status flips to `Executed`. + fn try_execute(env: &Env, proposal_id: u64) { + let mut proposal: Proposal = env + .storage() + .persistent() + .get(&DataKey::Proposal(proposal_id)) + .unwrap(); + if proposal.status != ProposalStatus::Pending { + return; + } + let threshold = Self::get_threshold(env.clone(), proposal.event_type.clone()); + if proposal.approvals.len() < threshold { + return; + } + proposal.status = ProposalStatus::Executed; + env.storage() + .persistent() + .set(&DataKey::Proposal(proposal_id), &proposal); + + // 1. The underlying high-value event the caller asked for. + if proposal.topic2.is_empty() { + env.events() + .publish((proposal.event_type.clone(),), proposal.data.clone()); + } else { + env.events().publish( + (proposal.event_type.clone(), proposal.topic2.clone()), + proposal.data.clone(), + ); + } + // 2. Non-repudiation attestation linking the event back to the + // full signer set + proposal id. + env.events().publish( + (symbol_short!("ms_attest"),), + (proposal_id, proposal.event_type, proposal.approvals), + ); + } +} + +mod test; diff --git a/contracts/multisig_event_registry/src/test.rs b/contracts/multisig_event_registry/src/test.rs new file mode 100644 index 0000000..dee2e9d --- /dev/null +++ b/contracts/multisig_event_registry/src/test.rs @@ -0,0 +1,147 @@ +#![cfg(test)] +use super::*; +use soroban_sdk::testutils::{Address as _, Events}; +use soroban_sdk::{symbol_short, vec, Address, Bytes, Env, IntoVal, Vec}; + +fn setup(threshold: u32, n_signers: u32) -> (Env, Vec
, MultisigEventRegistryClient<'static>) { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(MultisigEventRegistry, ()); + let client = MultisigEventRegistryClient::new(&env, &contract_id); + + let admin = Address::generate(&env); + let mut signers: Vec
= vec![&env]; + for _ in 0..n_signers { + signers.push_back(Address::generate(&env)); + } + + client.init(&admin, &signers, &threshold); + let mut all = vec![&env, admin]; + for s in signers.iter() { + all.push_back(s); + } + (env, all, client) +} + +fn has_event(env: &Env, name: &str) -> bool { + let expected = soroban_sdk::Symbol::new(env, name); + for (_, topics, _) in env.events().all().iter() { + let t: soroban_sdk::Symbol = topics.get(0).unwrap().into_val(env); + if t == expected { + return true; + } + } + false +} + +#[test] +fn init_then_threshold_1_executes_on_propose() { + let (env, principals, client) = setup(1, 2); + let s1 = principals.get(1).unwrap(); + let id = client.propose( + &s1, + &symbol_short!("payout"), + &Bytes::new(&env), + &Bytes::from_slice(&env, &[1, 2, 3]), + ); + // Soroban testutils snapshot `events().all()` per invocation, so assert + // the attestation event before the read-only `get_proposal` call. + assert!(has_event(&env, "ms_attest")); + let proposal = client.get_proposal(&id).unwrap(); + assert_eq!(proposal.status, ProposalStatus::Executed); +} + +#[test] +fn threshold_2_requires_second_approval() { + let (env, principals, client) = setup(2, 3); + let s1 = principals.get(1).unwrap(); + let s2 = principals.get(2).unwrap(); + let id = client.propose( + &s1, + &symbol_short!("rotate"), + &Bytes::new(&env), + &Bytes::from_slice(&env, &[9]), + ); + // Reading via `get_proposal` clears the per-invocation event buffer; + // we re-check after the second approval below. + let p = client.get_proposal(&id).unwrap(); + assert_eq!(p.status, ProposalStatus::Pending); + + client.approve(&s2, &id); + assert!(has_event(&env, "ms_attest")); + let p = client.get_proposal(&id).unwrap(); + assert_eq!(p.status, ProposalStatus::Executed); +} + +#[test] +#[should_panic(expected = "not a signer")] +fn non_signer_cannot_propose() { + let (env, _principals, client) = setup(2, 2); + let stranger = Address::generate(&env); + client.propose( + &stranger, + &symbol_short!("x"), + &Bytes::new(&env), + &Bytes::new(&env), + ); +} + +#[test] +#[should_panic(expected = "already approved")] +fn double_approve_rejected() { + let (env, principals, client) = setup(3, 3); + let s1 = principals.get(1).unwrap(); + let s2 = principals.get(2).unwrap(); + let id = client.propose( + &s1, + &symbol_short!("x"), + &Bytes::new(&env), + &Bytes::new(&env), + ); + client.approve(&s2, &id); + client.approve(&s2, &id); +} + +#[test] +fn per_event_threshold_override() { + let (env, principals, client) = setup(3, 3); + let admin = principals.get(0).unwrap(); + // Lower the threshold for "low_risk" to 1 so the proposer's vote executes + client.set_threshold(&admin, &symbol_short!("low_risk"), &1); + let s1 = principals.get(1).unwrap(); + let id = client.propose( + &s1, + &symbol_short!("low_risk"), + &Bytes::new(&env), + &Bytes::new(&env), + ); + let p = client.get_proposal(&id).unwrap(); + assert_eq!(p.status, ProposalStatus::Executed); +} + +#[test] +fn proposer_can_cancel_pending() { + let (env, principals, client) = setup(3, 3); + let s1 = principals.get(1).unwrap(); + let id = client.propose( + &s1, + &symbol_short!("x"), + &Bytes::new(&env), + &Bytes::new(&env), + ); + client.cancel(&s1, &id); + let p = client.get_proposal(&id).unwrap(); + assert_eq!(p.status, ProposalStatus::Cancelled); +} + +#[test] +#[should_panic(expected = "threshold exceeds signer count")] +fn init_rejects_excessive_threshold() { + let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register(MultisigEventRegistry, ()); + let client = MultisigEventRegistryClient::new(&env, &contract_id); + let admin = Address::generate(&env); + let signers: Vec
= vec![&env, Address::generate(&env)]; + client.init(&admin, &signers, &5); +} diff --git a/contracts/multisig_event_registry/test_snapshots/test/double_approve_rejected.1.json b/contracts/multisig_event_registry/test_snapshots/test/double_approve_rejected.1.json new file mode 100644 index 0000000..fa210bc --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/double_approve_rejected.1.json @@ -0,0 +1,531 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + }, + { + "u32": 3 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "propose", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "symbol": "x" + }, + { + "bytes": "" + }, + { + "bytes": "" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "approve", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "approvals" + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "event_type" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "proposer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "1" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/init_rejects_excessive_threshold.1.json b/contracts/multisig_event_registry/test_snapshots/test/init_rejects_excessive_threshold.1.json new file mode 100644 index 0000000..87ab33a --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/init_rejects_excessive_threshold.1.json @@ -0,0 +1,77 @@ +{ + "generators": { + "address": 3, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/init_then_threshold_1_executes_on_propose.1.json b/contracts/multisig_event_registry/test_snapshots/test/init_then_threshold_1_executes_on_propose.1.json new file mode 100644 index 0000000..abf83b1 --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/init_then_threshold_1_executes_on_propose.1.json @@ -0,0 +1,416 @@ +{ + "generators": { + "address": 4, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + { + "u32": 1 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "propose", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "symbol": "payout" + }, + { + "bytes": "" + }, + { + "bytes": "010203" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "approvals" + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "010203" + } + }, + { + "key": { + "symbol": "event_type" + }, + "val": { + "symbol": "payout" + } + }, + { + "key": { + "symbol": "proposer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "1" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/non_signer_cannot_propose.1.json b/contracts/multisig_event_registry/test_snapshots/test/non_signer_cannot_propose.1.json new file mode 100644 index 0000000..e13eab5 --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/non_signer_cannot_propose.1.json @@ -0,0 +1,198 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + { + "u32": 2 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 2 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "0" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/per_event_threshold_override.1.json b/contracts/multisig_event_registry/test_snapshots/test/per_event_threshold_override.1.json new file mode 100644 index 0000000..9727931 --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/per_event_threshold_override.1.json @@ -0,0 +1,525 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + }, + { + "u32": 3 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "set_threshold", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "symbol": "low_risk" + }, + { + "u32": 1 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "propose", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "symbol": "low_risk" + }, + { + "bytes": "" + }, + { + "bytes": "" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "approvals" + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "event_type" + }, + "val": { + "symbol": "low_risk" + } + }, + { + "key": { + "symbol": "proposer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Threshold" + }, + { + "symbol": "low_risk" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Threshold" + }, + { + "symbol": "low_risk" + } + ] + }, + "durability": "persistent", + "val": { + "u32": 1 + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "1" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/proposer_can_cancel_pending.1.json b/contracts/multisig_event_registry/test_snapshots/test/proposer_can_cancel_pending.1.json new file mode 100644 index 0000000..9dfc7b4 --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/proposer_can_cancel_pending.1.json @@ -0,0 +1,477 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + }, + { + "u32": 3 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "propose", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "symbol": "x" + }, + { + "bytes": "" + }, + { + "bytes": "" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "cancel", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "approvals" + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "" + } + }, + { + "key": { + "symbol": "event_type" + }, + "val": { + "symbol": "x" + } + }, + { + "key": { + "symbol": "proposer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 2 + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 3 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "1" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/multisig_event_registry/test_snapshots/test/threshold_2_requires_second_approval.1.json b/contracts/multisig_event_registry/test_snapshots/test/threshold_2_requires_second_approval.1.json new file mode 100644 index 0000000..0a453cd --- /dev/null +++ b/contracts/multisig_event_registry/test_snapshots/test/threshold_2_requires_second_approval.1.json @@ -0,0 +1,532 @@ +{ + "generators": { + "address": 5, + "nonce": 0, + "mux_id": 0 + }, + "auth": [ + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + }, + { + "u32": 2 + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "propose", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "symbol": "rotate" + }, + { + "bytes": "" + }, + { + "bytes": "09" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "approve", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "u64": "0" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 23, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Approved" + }, + { + "u64": "0" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + }, + "durability": "persistent", + "val": { + "bool": true + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "vec": [ + { + "symbol": "Proposal" + }, + { + "u64": "0" + } + ] + }, + "durability": "persistent", + "val": { + "map": [ + { + "key": { + "symbol": "approvals" + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + ] + } + }, + { + "key": { + "symbol": "created_at_ledger" + }, + "val": { + "u32": 0 + } + }, + { + "key": { + "symbol": "data" + }, + "val": { + "bytes": "09" + } + }, + { + "key": { + "symbol": "event_type" + }, + "val": { + "symbol": "rotate" + } + }, + { + "key": { + "symbol": "proposer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + }, + { + "key": { + "symbol": "status" + }, + "val": { + "u32": 1 + } + }, + { + "key": { + "symbol": "topic2" + }, + "val": { + "bytes": "" + } + } + ] + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "DefaultThreshold" + } + ] + }, + "val": { + "u32": 2 + } + }, + { + "key": { + "vec": [ + { + "symbol": "NextProposalId" + } + ] + }, + "val": { + "u64": "1" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Signers" + } + ] + }, + "val": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": "801925984706572462" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": "5541220902715666415" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": { + "ledger_key_nonce": { + "nonce": "1033654523790656264" + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/docs/did_resolver.md b/docs/did_resolver.md new file mode 100644 index 0000000..2abaa95 --- /dev/null +++ b/docs/did_resolver.md @@ -0,0 +1,70 @@ +# DID Resolver — Decentralized Identity on Soroban + +The `DidResolver` contract is a minimal, W3C-DID-Core-compatible registry. DID documents are stored as opaque bytes (intended to be a canonical-JSON encoding of the W3C document) keyed by DID string, with a single on-chain `controller` address per DID that may update or revoke it. + +Implements issue #295. + +## W3C Alignment + +The contract implements the standard DID CRUD verbs: + +| W3C verb | Contract function | +|----------|-------------------| +| Create | `register` | +| Read (resolve) | `resolve`, `status` | +| Update | `update` | +| Deactivate | `revoke` | + +DID strings follow the `did:soroban:` shape. The registry stores the full string and does not parse the method-specific id — that's the consumer's responsibility, and lets the same contract serve different method definitions if desired. + +## Features + +- **Single-controller authority**: Each DID has exactly one `controller` address recorded at registration. Only that address can update or revoke the document. +- **Document opacity**: Documents are stored as `Bytes` — the registry treats them as opaque. This means the contract works unchanged for any canonical encoding (JSON, JSON-LD, CBOR, etc.). +- **Monotonic version counter**: Every successful `update` bumps the `version` field, giving consumers a cheap way to detect stale cached copies. +- **Distinction between "revoked" and "never existed"**: `resolve` returns `Some(doc_with_status_Revoked)` for revoked DIDs and `None` for unknown DIDs. Revoked DIDs are not recyclable — re-registering the same string panics. +- **Event-based identity verification updates**: Every state transition emits a `did_attst` event with `(did, controller, version)` so off-chain verifiers can subscribe and react without polling. + +## Contract Interface + +### `register(controller, did, document)` +Create a new DID record. `controller.require_auth()`. Panics on empty `did` or if the DID is already registered (active or revoked). + +### `update(controller, did, new_document)` +Replace the document. Only the recorded controller can call. Bumps `version` and `updated_at_ledger`. Panics if the DID is revoked. + +### `revoke(controller, did)` +Mark the DID revoked. Subsequent `update`s panic. `resolve` still returns the document with `status = Revoked`. + +### `resolve(did) -> Option` +Return the current document, or `None` if the DID was never registered. + +### `status(did) -> Option` +Lightweight existence + status check that avoids returning the full document. + +## Events + +| Event | Topics | Body | When | +|-------|--------|------|------| +| `did_reg` | `(symbol)` | `(did, controller)` | On `register` | +| `did_upd` | `(symbol)` | `(did, new_version)` | On `update` | +| `did_rev` | `(symbol)` | `(did, controller)` | On `revoke` | +| `did_attst` | `(symbol)` | `(did, controller, version)` | On `register` and `update` — the identity-verification event-stream hook | + +## Stored Document Shape + +```rust +pub struct DidDocument { + pub did: String, + pub controller: Address, + pub document: Bytes, // canonical-JSON / CBOR / etc. + pub status: DidStatus, // Active | Revoked + pub created_at_ledger: u32, + pub updated_at_ledger: u32, + pub version: u32, // monotonic; bumps on every `update` +} +``` + +## Non-Recyclable DIDs + +Once a DID string has been registered, it cannot be re-registered even after revocation. This matches the W3C "deactivation is permanent" model and prevents controller substitution attacks where a former controller waits for a DID to be revoked and re-claims it. diff --git a/docs/event_batch.md b/docs/event_batch.md new file mode 100644 index 0000000..1a18069 --- /dev/null +++ b/docs/event_batch.md @@ -0,0 +1,45 @@ +# Event Batch — Gas-Optimized Event Batching Utility + +The `EventBatchContract` lets a caller emit up to `MAX_BATCH_SIZE` (64) events in a single contract invocation. This amortizes the per-transaction overhead — contract dispatch, auth check, ledger entry growth — across many emissions, dropping the effective per-event cost for indexers and integrations that need to fan out many notifications at once. + +Implements issue #293. + +## Features + +- **Up to 64 events per call**: Constrained by `MAX_BATCH_SIZE` to keep individual transactions within Soroban's per-tx ledger budget. +- **Mixed topic shapes**: Each `EventEntry` carries a primary `topic` and an optional second `topic2` (set to empty `Bytes` to skip). This lets a single batch include both single-topic and dual-topic events. +- **Type-erased payloads**: `data: Bytes` keeps the payload opaque so a single batch can carry heterogeneous event shapes; the consumer reinterprets `data` per topic. +- **Per-batch summary event**: After all entries are emitted, the contract emits one `batch_emt` event with `(count, sender)` so indexers that want to group emissions can do so without inferring batch boundaries from timestamps. + +## Contract Interface + +### `emit_batch(sender, entries) -> u32` +Emit every entry then a single `batch_emt` summary. `sender` must sign the transaction. Returns the number of user events emitted (excludes the summary). + +Panics on: +- Empty batch (`empty batch`) +- Batches larger than `MAX_BATCH_SIZE` (`batch too large`) + +## Storage Efficiency + +The `EventEntry` struct is designed for minimal storage footprint: + +```rust +pub struct EventEntry { + pub topic: Symbol, // small inline value + pub topic2: Bytes, // empty when single-topic + pub data: Bytes, // type-erased payload +} +``` + +Soroban's `Bytes` and `Symbol` are both small handles; the payload bytes are carried by reference. The total per-entry overhead is therefore close to the size of the payload itself, with no per-event ledger entry write since events don't persist in storage. + +## Gas Considerations + +A single `emit_batch` invocation pays: +- One contract dispatch + auth check (versus N for N separate calls) +- One Wasm instantiation +- N event emissions (each is O(payload_size)) +- One summary event emission + +For batches of 10+ small events the savings on dispatch + auth typically dominate the total cost. diff --git a/docs/multisig_event_registry.md b/docs/multisig_event_registry.md new file mode 100644 index 0000000..3a0639e --- /dev/null +++ b/docs/multisig_event_registry.md @@ -0,0 +1,53 @@ +# Multisig Event Registry — High-Value Event Emission Gate + +The `MultisigEventRegistry` contract wraps event emission behind an M-of-N signer approval gate. Sensitive events (treasury withdrawals, oracle rotation, large-value transfers) cannot be emitted without sign-off from the configured signer set, and every executed event is accompanied by a `ms_attest` attestation event that records the full approving signer list and the proposal id — giving downstream consumers non-repudiation evidence that the event was authorized. + +Implements issue #292. + +## Features + +- **M-of-N signer set**: An immutable list of signer addresses configured at `init` time, with an admin who can override per-event-type thresholds later. +- **Per-event-type threshold override**: `set_threshold(event_type, threshold)` lets the admin require more approvals for high-risk event types than for routine ones. Falls back to `default_threshold` when no override exists. +- **Auto-execution**: As soon as approvals reach the threshold the underlying event is emitted automatically — no separate `execute` call needed. +- **Non-repudiation attestation**: Every executed proposal also emits a `ms_attest` event with `(proposal_id, event_type, approvals: Vec
)` so off-chain consumers can independently verify which signers authorized the emission. +- **Cancellation**: The proposer or the admin can cancel a pending proposal before it executes. + +## Contract Interface + +### `init(admin, signers, default_threshold)` +One-time initialization. Panics if called twice. `default_threshold` must be between 1 and `signers.len()` inclusive. + +### `set_threshold(admin, event_type, threshold)` +Admin-only. Sets the per-event-type approval threshold. The new threshold must satisfy the same `0 < t ≤ signer_count` invariant. + +### `propose(proposer, event_type, topic2, data) -> u64` +A signer proposes the emission of a high-value event. The proposer's vote is recorded automatically. If the threshold for `event_type` is 1, the event is emitted immediately and the proposal moves to `Executed`. Returns the new `proposal_id`. + +### `approve(signer, proposal_id)` +A signer approves a pending proposal. Auto-executes when the threshold is reached. Panics if the signer has already approved this proposal or if the proposal is no longer pending. + +### `cancel(caller, proposal_id)` +Proposer or admin only. Marks the proposal as `Cancelled`. + +### `get_proposal(proposal_id) -> Option` +### `get_threshold(event_type) -> u32` +### `get_signers() -> Vec
` +Read-only queries. + +## Events + +| Event | Topics | Body | +|-------|--------|------| +| `proposed` | `(symbol)` | `(proposal_id, proposer)` | +| `approved` | `(symbol)` | `(proposal_id, signer)` | +| `cancel` | `(symbol)` | `proposal_id` | +| **caller's event_type** | `(event_type, topic2?)` | the original `data` (emitted on execution) | +| `ms_attest` | `(symbol)` | `(proposal_id, event_type, approvals: Vec
)` | + +The combination of the original event and the `ms_attest` envelope is what gives the registry its non-repudiation guarantee. + +## Security Notes + +- The contract validates `0 < threshold ≤ signer_count` at every entry point that sets a threshold, so a misconfigured admin cannot lock the registry. +- `require_auth` is enforced on every state-changing call. +- The signer set is captured at `init`; rotating signers requires a contract upgrade or a wrapper contract. This is deliberate — a multisig whose signer set can be changed unilaterally has weaker security guarantees than one whose set is immutable.