diff --git a/Cargo.lock b/Cargo.lock index 7c15336c5..5c0c6785d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2277,9 +2277,9 @@ dependencies = [ [[package]] name = "deadpool" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3c3845002cd8dec1e045bc3fc076d1e467a123794cf56326d804489df1896" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" dependencies = [ "deadpool-runtime", "num_cpus", @@ -2288,11 +2288,12 @@ dependencies = [ [[package]] name = "deadpool-postgres" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10566d25f606cc5f2dee63311af0eb2dc861cba4a5a7dee2daa4c2b181a42862" +checksum = "19be9da496d60d03ec3ab45d960d80a3afb285b787394b83614a79942f467e7f" dependencies = [ "deadpool", + "getrandom 0.2.12", "tokio", "tokio-postgres", "tracing", @@ -3516,8 +3517,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] diff --git a/crates/sovereign-sdk/fuzz/Cargo.toml b/crates/sovereign-sdk/fuzz/Cargo.toml index bd282476c..1c85c8ed1 100644 --- a/crates/sovereign-sdk/fuzz/Cargo.toml +++ b/crates/sovereign-sdk/fuzz/Cargo.toml @@ -15,11 +15,21 @@ rand = "0.8" # Sovereign-maintained dependencies. sov-celestia-adapter = { path = "../adapters/celestia", features = ["native"] } -sov-modules-api = { path = "../module-system/sov-modules-api", features = ["arbitrary", "native"] } -sov-accounts = { path = "../module-system/module-implementations/sov-accounts", features = ["arbitrary", "native"] } -sov-bank = { path = "../module-system/module-implementations/sov-bank", features = ["native"] } +sov-modules-api = { path = "../module-system/sov-modules-api", features = [ + "arbitrary", + "native", +] } +sov-accounts = { path = "../module-system/module-implementations/sov-accounts", features = [ + "arbitrary", + "native", +] } +sov-bank = { path = "../module-system/module-implementations/sov-bank", features = [ + "native", +] } sov-state = { path = "../module-system/sov-state" } -sov-prover-storage-manager = { path = "../full-node/sov-prover-storage-manager", features = ["test-utils"] } +sov-prover-storage-manager = { path = "../full-node/sov-prover-storage-manager", features = [ + "test-utils", +] } # Prevent this from interfering with workspaces. [workspace] @@ -55,32 +65,8 @@ path = "fuzz_targets/bank_call.rs" test = false doc = false -[[bin]] -name = "accounts_call" -path = "fuzz_targets/accounts_call.rs" -test = false -doc = false - -[[bin]] -name = "accounts_call_random" -path = "fuzz_targets/accounts_call_random.rs" -test = false -doc = false - [[bin]] name = "bank_parse_call_message" path = "fuzz_targets/bank_parse_call_message.rs" test = false doc = false - -[[bin]] -name = "accounts_parse_call_message" -path = "fuzz_targets/accounts_parse_call_message.rs" -test = false -doc = false - -[[bin]] -name = "accounts_parse_call_message_random" -path = "fuzz_targets/accounts_parse_call_message_random.rs" -test = false -doc = false diff --git a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call.rs b/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call.rs deleted file mode 100644 index 0d912de7c..000000000 --- a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call.rs +++ /dev/null @@ -1,87 +0,0 @@ -#![no_main] - -use std::collections::{HashMap, HashSet}; - -use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; -use libfuzzer_sys::{fuzz_target, Corpus}; -use rand::rngs::StdRng; -use rand::seq::SliceRandom; -use rand::{RngCore, SeedableRng}; -use sov_accounts::{AccountConfig, Accounts, CallMessage, UPDATE_ACCOUNT_MSG}; -use sov_modules_api::default_context::DefaultContext; -use sov_modules_api::default_signature::private_key::DefaultPrivateKey; -use sov_modules_api::{Context, Module, PrivateKey, Spec, WorkingSet}; -use sov_prover_storage_manager::new_orphan_storage; - -type C = DefaultContext; - -// Check well-formed calls -fuzz_target!( - |input: (u16, [u8; 32], [u8; 32], Vec)| -> Corpus { - let (iterations, seed, sequencer, keys) = input; - if iterations < 1024 { - // pointless to setup & run a small iterations count - return Corpus::Reject; - } - - // this is a workaround to the restriction where `ed25519_dalek::Keypair` doesn't implement - // `Eq` or `Sort`; reduce the set to a unique collection of keys so duplicated accounts are not - // used. - let keys = keys - .into_iter() - .map(|k| (k.as_hex(), k)) - .collect::>() - .into_values() - .collect::>(); - - if keys.is_empty() { - return Corpus::Reject; - } - - let rng = &mut StdRng::from_seed(seed); - let mut seed = [0u8; 32]; - let tmpdir = tempfile::tempdir().unwrap(); - let storage = new_orphan_storage(tmpdir.path()).unwrap(); - let working_set = &mut WorkingSet::new(storage); - - let sequencer = ::Address::from(sequencer); - let config: AccountConfig = keys.iter().map(|k| k.pub_key()).collect(); - let accounts: Accounts = Accounts::default(); - accounts.genesis(&config, working_set).unwrap(); - - // address list is constant for this test - let mut used = keys.iter().map(|k| k.as_hex()).collect::>(); - let mut state: HashMap<_, _> = keys.into_iter().map(|k| (k.default_address(), k)).collect(); - let addresses: Vec<_> = state.keys().copied().collect(); - - for i in 0..iterations { - // we use slices for better select performance - let sender = addresses.choose(rng).unwrap(); - let context = C::new(*sender, sequencer, i as u64); - - // clear previous state - let previous = state.get(sender).unwrap().as_hex(); - used.remove(&previous); - - // generate an unused key - rng.fill_bytes(&mut seed); - let u = &mut Unstructured::new(&seed); - let mut secret = DefaultPrivateKey::arbitrary(u).unwrap(); - while used.contains(&secret.as_hex()) { - rng.fill_bytes(&mut seed); - let u = &mut Unstructured::new(&seed); - secret = DefaultPrivateKey::arbitrary(u).unwrap(); - } - used.insert(secret.as_hex()); - - let public = secret.pub_key(); - let sig = secret.sign(&UPDATE_ACCOUNT_MSG); - state.insert(*sender, secret); - - let msg = CallMessage::::UpdatePublicKey(public.clone(), sig); - accounts.call(msg, &context, working_set).unwrap(); - } - - Corpus::Keep - } -); diff --git a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call_random.rs b/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call_random.rs deleted file mode 100644 index c88bebe2f..000000000 --- a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_call_random.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_main] - -use libfuzzer_sys::arbitrary::Unstructured; -use libfuzzer_sys::fuzz_target; -use sov_accounts::{Accounts, CallMessage}; -use sov_modules_api::default_context::DefaultContext; -use sov_modules_api::{Module, WorkingSet}; -use sov_prover_storage_manager::new_orphan_storage; - -type C = DefaultContext; - -// Check arbitrary, random calls -fuzz_target!(|input: (&[u8], Vec<(C, CallMessage)>)| { - let tmpdir = tempfile::tempdir().unwrap(); - let storage = new_orphan_storage(tmpdir.path()).unwrap(); - let working_set = &mut WorkingSet::new(storage); - - let (seed, msgs) = input; - let u = &mut Unstructured::new(seed); - let accounts: Accounts = Accounts::arbitrary_workset(u, working_set).unwrap(); - - for (ctx, msg) in msgs { - // assert malformed calls won't panic - accounts.call(msg, &ctx, working_set).ok(); - } -}); diff --git a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message.rs b/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message.rs deleted file mode 100644 index 0467112e1..000000000 --- a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use sov_accounts::CallMessage; -use sov_modules_api::default_context::DefaultContext; - -type C = DefaultContext; - -fuzz_target!(|input: CallMessage| { - let json = serde_json::to_vec(&input).unwrap(); - let msg = serde_json::from_slice::>(&json).unwrap(); - assert_eq!(input, msg); -}); diff --git a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message_random.rs b/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message_random.rs deleted file mode 100644 index a928f4c7d..000000000 --- a/crates/sovereign-sdk/fuzz/fuzz_targets/accounts_parse_call_message_random.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use sov_accounts::CallMessage; -use sov_modules_api::default_context::DefaultContext; - -type C = DefaultContext; - -fuzz_target!(|input: &[u8]| { - serde_json::from_slice::>(input).ok(); -}); diff --git a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/call.rs b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/call.rs deleted file mode 100644 index e0c6f5bc7..000000000 --- a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/call.rs +++ /dev/null @@ -1,76 +0,0 @@ -use anyhow::{ensure, Result}; -use sov_modules_api::{CallResponse, Context, Signature, StateMapAccessor, WorkingSet}; - -use crate::Accounts; - -/// To update the account's public key, the sender must sign this message as proof of possession of the new key. -pub const UPDATE_ACCOUNT_MSG: [u8; 32] = [1; 32]; - -/// Represents the available call messages for interacting with the sov-accounts module. -#[cfg_attr( - feature = "native", - derive(schemars::JsonSchema), - derive(sov_modules_api::macros::CliWalletArg), - schemars( - bound = "C::PublicKey: ::schemars::JsonSchema, C::Signature: ::schemars::JsonSchema", - rename = "CallMessage" - ) -)] -#[cfg_attr( - feature = "serde", - derive(serde::Serialize), - derive(serde::Deserialize) -)] -#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)] -pub enum CallMessage { - /// Updates a public key for the corresponding Account. - /// The sender must be in possession of the new key. - UpdatePublicKey( - /// The new public key - C::PublicKey, - /// A valid signature from the new public key - C::Signature, - ), -} - -impl Accounts { - pub(crate) fn update_public_key( - &self, - new_pub_key: C::PublicKey, - signature: C::Signature, - context: &C, - working_set: &mut WorkingSet, - ) -> Result { - self.exit_if_account_exists(&new_pub_key, working_set)?; - - let pub_key = self.public_keys.get_or_err(context.sender(), working_set)?; - - let account = self.accounts.remove_or_err(&pub_key, working_set)?; - // Sanity check - ensure!( - context.sender() == &account.addr, - "Inconsistent account data" - ); - - // Proof that the sender is in possession of the `new_pub_key`. - signature.verify(&new_pub_key, &UPDATE_ACCOUNT_MSG)?; - - // Update the public key (account data remains the same). - self.accounts.set(&new_pub_key, &account, working_set); - self.public_keys - .set(context.sender(), &new_pub_key, working_set); - Ok(CallResponse::default()) - } - - fn exit_if_account_exists( - &self, - new_pub_key: &C::PublicKey, - working_set: &mut WorkingSet, - ) -> Result<()> { - anyhow::ensure!( - self.accounts.get(new_pub_key, working_set).is_none(), - "New PublicKey already exists" - ); - Ok(()) - } -} diff --git a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/fuzz.rs b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/fuzz.rs index 87a9b1234..96b858329 100644 --- a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/fuzz.rs +++ b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/fuzz.rs @@ -3,43 +3,7 @@ use proptest::arbitrary::any; use proptest::strategy::{BoxedStrategy, Strategy}; use sov_modules_api::{Context, Module, PrivateKey, WorkingSet}; -use crate::{Account, AccountConfig, Accounts, CallMessage}; - -impl<'a, C> Arbitrary<'a> for CallMessage -where - C: Context, - C::PrivateKey: Arbitrary<'a>, -{ - fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { - let secret = C::PrivateKey::arbitrary(u)?; - let public = secret.pub_key(); - - let payload_len = u.arbitrary_len::()?; - let payload = u.bytes(payload_len)?; - let signature = secret.sign(payload); - - Ok(Self::UpdatePublicKey(public, signature)) - } -} - -impl proptest::arbitrary::Arbitrary for CallMessage -where - C: Context, - C::PrivateKey: proptest::arbitrary::Arbitrary, -{ - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), any::>()) - .prop_map(|(secret, payload)| { - let public = secret.pub_key(); - let signature = secret.sign(&payload); - Self::UpdatePublicKey(public, signature) - }) - .boxed() - } -} +use crate::{Account, AccountConfig, Accounts}; impl<'a, C> Arbitrary<'a> for Account where diff --git a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/lib.rs b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/lib.rs index f0d129309..2b3260f5c 100644 --- a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/lib.rs +++ b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/lib.rs @@ -1,6 +1,3 @@ -#![deny(missing_docs)] -#![doc = include_str!("../README.md")] -mod call; #[cfg(all(feature = "arbitrary", feature = "native"))] mod fuzz; mod genesis; @@ -13,7 +10,6 @@ pub use query::*; #[cfg(test)] mod tests; -pub use call::{CallMessage, UPDATE_ACCOUNT_MSG}; pub use hooks::AccountsTxHook; use sov_modules_api::{Context, Error, ModuleInfo, WorkingSet}; @@ -57,7 +53,7 @@ impl sov_modules_api::Module for Accounts { type Config = AccountConfig; - type CallMessage = call::CallMessage; + type CallMessage = (); type Event = (); @@ -67,14 +63,10 @@ impl sov_modules_api::Module for Accounts { fn call( &self, - msg: Self::CallMessage, - context: &Self::Context, - working_set: &mut WorkingSet, + _msg: Self::CallMessage, + _context: &Self::Context, + _working_set: &mut WorkingSet, ) -> Result { - match msg { - call::CallMessage::UpdatePublicKey(new_pub_key, sig) => { - Ok(self.update_public_key(new_pub_key, sig, context, working_set)?) - } - } + Ok(sov_modules_api::CallResponse::default()) } } diff --git a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/tests.rs b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/tests.rs index 956f2345e..84a2ac09a 100644 --- a/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/tests.rs +++ b/crates/sovereign-sdk/module-system/module-implementations/sov-accounts/src/tests.rs @@ -1,12 +1,10 @@ use sov_modules_api::default_context::DefaultContext; use sov_modules_api::default_signature::private_key::DefaultPrivateKey; -use sov_modules_api::{ - AddressBech32, Context, Module, PrivateKey, PublicKey, Spec, StateMapAccessor, WorkingSet, -}; +use sov_modules_api::{AddressBech32, PrivateKey, PublicKey, Spec, WorkingSet}; use sov_prover_storage_manager::new_orphan_storage; use crate::query::{self, Response}; -use crate::{call, AccountConfig, Accounts}; +use crate::{AccountConfig, Accounts}; type C = DefaultContext; @@ -37,133 +35,6 @@ fn test_config_account() { ) } -#[test] -fn test_update_account() { - let tmpdir = tempfile::tempdir().unwrap(); - let working_set = &mut WorkingSet::new(new_orphan_storage(tmpdir.path()).unwrap()); - let accounts = &mut Accounts::::default(); - - let priv_key = DefaultPrivateKey::generate(); - let sequencer_priv_key = DefaultPrivateKey::generate(); - - let sender = priv_key.pub_key(); - let sequencer = sequencer_priv_key.pub_key(); - let sender_addr = sender.to_address::<::Address>(); - let sequencer_addr = sequencer.to_address::<::Address>(); - let sender_context = C::new(sender_addr, sequencer_addr, 1); - - // Test new account creation - { - accounts - .create_default_account(&sender, working_set) - .unwrap(); - - let query_response = accounts.get_account(sender.clone(), working_set).unwrap(); - - assert_eq!( - query_response, - query::Response::AccountExists { - addr: AddressBech32::try_from(sender_addr.as_ref()).unwrap(), - nonce: 0 - } - ) - } - - // Test public key update - { - let priv_key = DefaultPrivateKey::generate(); - let new_pub_key = priv_key.pub_key(); - let sig = priv_key.sign(&call::UPDATE_ACCOUNT_MSG); - accounts - .call( - call::CallMessage::::UpdatePublicKey(new_pub_key.clone(), sig), - &sender_context, - working_set, - ) - .unwrap(); - - // Account corresponding to the old public key does not exist - let query_response = accounts.get_account(sender, working_set).unwrap(); - - assert_eq!(query_response, query::Response::AccountEmpty); - - // New account with the new public key and an old address is created. - let query_response = accounts.get_account(new_pub_key, working_set).unwrap(); - - assert_eq!( - query_response, - query::Response::AccountExists { - addr: AddressBech32::try_from(sender_addr.as_ref()).unwrap(), - nonce: 0 - } - ) - } -} - -#[test] -fn test_update_account_fails() { - let tmpdir = tempfile::tempdir().unwrap(); - let working_set = &mut WorkingSet::new(new_orphan_storage(tmpdir.path()).unwrap()); - let accounts = &mut Accounts::::default(); - - let sender_1 = DefaultPrivateKey::generate().pub_key(); - let sequencer = DefaultPrivateKey::generate().pub_key(); - let sender_context_1 = C::new(sender_1.to_address(), sequencer.to_address(), 1); - - accounts - .create_default_account(&sender_1, working_set) - .unwrap(); - - let priv_key = DefaultPrivateKey::generate(); - let sender_2 = priv_key.pub_key(); - let sig_2 = priv_key.sign(&call::UPDATE_ACCOUNT_MSG); - - accounts - .create_default_account(&sender_2, working_set) - .unwrap(); - - // The new public key already exists and the call fails. - assert!(accounts - .call( - call::CallMessage::::UpdatePublicKey(sender_2, sig_2), - &sender_context_1, - working_set - ) - .is_err()) -} - -#[test] -fn test_get_account_after_pub_key_update() { - let tmpdir = tempfile::tempdir().unwrap(); - let working_set = &mut WorkingSet::new(new_orphan_storage(tmpdir.path()).unwrap()); - let accounts = &mut Accounts::::default(); - - let sender_1 = DefaultPrivateKey::generate().pub_key(); - let sequencer = DefaultPrivateKey::generate().pub_key(); - let sender_1_addr = sender_1.to_address::<::Address>(); - let sequencer_addr = sequencer.to_address::<::Address>(); - let sender_context_1 = C::new(sender_1_addr, sequencer_addr, 1); - - accounts - .create_default_account(&sender_1, working_set) - .unwrap(); - - let priv_key = DefaultPrivateKey::generate(); - let new_pub_key = priv_key.pub_key(); - let sig = priv_key.sign(&call::UPDATE_ACCOUNT_MSG); - accounts - .call( - call::CallMessage::::UpdatePublicKey(new_pub_key.clone(), sig), - &sender_context_1, - working_set, - ) - .unwrap(); - - let acc = accounts.accounts.get(&new_pub_key, working_set).unwrap(); - - assert_eq!(acc.addr, sender_1_addr) -} - #[test] fn test_response_serialization() { let addr: Vec = (1..=32).collect(); diff --git a/crates/sovereign-sdk/module-system/module-schemas/schemas/sov-accounts.json b/crates/sovereign-sdk/module-system/module-schemas/schemas/sov-accounts.json index 1d3da7809..49a8df219 100644 --- a/crates/sovereign-sdk/module-system/module-schemas/schemas/sov-accounts.json +++ b/crates/sovereign-sdk/module-system/module-schemas/schemas/sov-accounts.json @@ -1,68 +1,5 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CallMessage", - "description": "Represents the available call messages for interacting with the sov-accounts module.", - "oneOf": [ - { - "description": "Updates a public key for the corresponding Account. The sender must be in possession of the new key.", - "type": "object", - "required": [ - "UpdatePublicKey" - ], - "properties": { - "UpdatePublicKey": { - "type": "array", - "items": [ - { - "$ref": "#/definitions/DefaultPublicKey" - }, - { - "$ref": "#/definitions/DefaultSignature" - } - ], - "maxItems": 2, - "minItems": 2 - } - }, - "additionalProperties": false - } - ], - "definitions": { - "DefaultPublicKey": { - "type": "object", - "required": [ - "pub_key" - ], - "properties": { - "pub_key": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - }, - "maxItems": 32, - "minItems": 32 - } - } - }, - "DefaultSignature": { - "type": "object", - "required": [ - "msg_sig" - ], - "properties": { - "msg_sig": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - }, - "maxItems": 64, - "minItems": 64 - } - } - } - } + "title": "Null", + "type": "null" }