diff --git a/rust/psibase/src/services/transact.rs b/rust/psibase/src/services/transact.rs index e7ee2d4c0..2f6380fe3 100644 --- a/rust/psibase/src/services/transact.rs +++ b/rust/psibase/src/services/transact.rs @@ -153,29 +153,31 @@ pub mod auth_interface { unimplemented!() } - /// Handle notification related to the acceptance of a staged transaction + /// Check whether a specified set of authorizer accounts are sufficient to authorize sending a + /// transaction from a specified sender. /// - /// An auth service is notified when the sender of the staged transaction - /// is an account that uses the auth service. + /// * `sender`: The sender account for the transaction potentially being authorized. + /// * `authorizers`: The set of accounts that have already authorized the execution of the transaction. /// - /// * `staged_tx_id`: The ID of the staged transaction, used to identify the - /// staged-tx record. - /// * `actor`: The account that accepts the specified staged transaction + /// Returns: + /// * `true`: The authorizers are sufficient to authorize a transaction from the sender. + /// * `false`: The authorizers are not sufficient to authorize a transaction from the sender. #[action] - fn stagedAccept(staged_tx_id: u32, actor: crate::AccountNumber) { + fn isAuthSys(sender: crate::AccountNumber, authorizers: Vec) -> bool { unimplemented!() } - /// Handle notification related to the rejection of a staged transaction + /// Check whether a specified set of rejecter accounts are sufficient to reject (cancel) a + /// transaction from a specified sender. /// - /// An auth service is notified when the sender of the staged transaction - /// is an account that uses the auth service. + /// * `sender`: The sender account for the transaction potentially being rejected. + /// * `rejecters`: The set of accounts that have already authorized the rejection of the transaction. /// - /// * `staged_tx_id`: The ID of the staged transaction, used to identify the - /// staged-tx record. - /// * `actor`: The account that rejects the specified staged transaction + /// Returns: + /// * `true`: The rejecters are sufficient to reject a transaction from the sender. + /// * `false`: The rejecters are not sufficient to reject a transaction from the sender. #[action] - fn stagedReject(staged_tx_id: u32, actor: crate::AccountNumber) { + fn isRejectSys(sender: crate::AccountNumber, rejecters: Vec) -> bool { unimplemented!() } } diff --git a/services/system/AuthAny/include/services/system/AuthAny.hpp b/services/system/AuthAny/include/services/system/AuthAny.hpp index 4bb5f36f5..18010f383 100644 --- a/services/system/AuthAny/include/services/system/AuthAny.hpp +++ b/services/system/AuthAny/include/services/system/AuthAny.hpp @@ -17,15 +17,17 @@ namespace SystemService void canAuthUserSys(psibase::AccountNumber user); - void stagedAccept(uint32_t staged_tx_id, psibase::AccountNumber actor); + bool isAuthSys(psibase::AccountNumber sender, + std::vector authorizers); - void stagedReject(uint32_t staged_tx_id, psibase::AccountNumber actor); + bool isRejectSys(psibase::AccountNumber sender, + std::vector rejecters); }; PSIO_REFLECT(AuthAny, // method(checkAuthSys, flags, requester, sender, action, allowedActions, claims), method(canAuthUserSys, user), - method(stagedAccept, staged_tx_id, actor), - method(stagedReject, staged_tx_id, actor) + method(isAuthSys, sender, authorizers), + method(isRejectSys, sender, rejecters) // ) } // namespace SystemService diff --git a/services/system/AuthAny/src/AuthAny.cpp b/services/system/AuthAny/src/AuthAny.cpp index d62d1d87c..619111902 100644 --- a/services/system/AuthAny/src/AuthAny.cpp +++ b/services/system/AuthAny/src/AuthAny.cpp @@ -20,26 +20,29 @@ namespace SystemService std::vector claims) { if (enable_print) - std::printf("auth_check\n"); + std::printf("checkAuthSys\n"); } void AuthAny::canAuthUserSys(AccountNumber user) { if (enable_print) - std::printf("user_check\n"); + std::printf("canAuthUserSys\n"); } - void AuthAny::stagedAccept(uint32_t stagedTxId, AccountNumber actor) + bool AuthAny::isAuthSys(AccountNumber sender, std::vector authorizers) { - check(getSender() == StagedTxService::service, "can only be called by staged-tx"); + if (enable_print) + std::printf("isAuthSys\n"); - auto [execute, allowedActions] = to().get_exec_info(stagedTxId); - recurse().to().runAs(std::move(execute), allowedActions); + return true; } - void AuthAny::stagedReject(uint32_t stagedTxId, AccountNumber actor) + bool AuthAny::isRejectSys(AccountNumber sender, std::vector rejecters) { - check(false, "not supported"); + if (enable_print) + std::printf("isRejectSys\n"); + + return false; } } // namespace SystemService diff --git a/services/system/AuthSig/include/services/system/AuthSig.hpp b/services/system/AuthSig/include/services/system/AuthSig.hpp index 4ed91ee95..12bd1c57e 100644 --- a/services/system/AuthSig/include/services/system/AuthSig.hpp +++ b/services/system/AuthSig/include/services/system/AuthSig.hpp @@ -70,17 +70,29 @@ namespace SystemService /// submits a transaction. void setKey(SubjectPublicKeyInfo key); - /// Handle notification related to the acceptance of a staged transaction + /// Check whether a specified set of authorizer accounts are sufficient to authorize sending a + /// transaction from a specified sender. /// - /// Auth-sig will execute the staged transaction if the sender of the call to `accept` - /// is the same as the sender of the staged transaction. - void stagedAccept(uint32_t staged_tx_id, psibase::AccountNumber actor); + /// * `sender`: The sender account for the transaction potentially being authorized. + /// * `authorizers`: The set of accounts that have already authorized the execution of the transaction. + /// + /// Returns: + /// * `true`: If the sender is among the authorizers + /// * `false`: If the sender is not among the authorizers + bool isAuthSys(psibase::AccountNumber sender, + std::vector authorizers); - /// Handle notification related to the rejection of a staged transaction + /// Check whether a specified set of rejecter accounts are sufficient to reject (cancel) a + /// transaction from a specified sender. + /// + /// * `sender`: The sender account for the transaction potentially being rejected. + /// * `rejecters`: The set of accounts that have already authorized the rejection of the transaction. /// - /// Auth-sig will reject the staged transaction if the sender of the call to `reject` is - /// the same as the sender of the staged transaction. - void stagedReject(uint32_t staged_tx_id, psibase::AccountNumber actor); + /// Returns: + /// * `true`: If the sender is among the rejecters + /// * `false`: If the sender is not among the rejecters + bool isRejectSys(psibase::AccountNumber sender, + std::vector rejecters); private: Tables db{psibase::getReceiver()}; @@ -89,8 +101,8 @@ namespace SystemService method(checkAuthSys, flags, requester, sender, action, allowedActions, claims), method(canAuthUserSys, user), method(setKey, key), - method(stagedAccept, staged_tx_id, actor), - method(stagedReject, staged_tx_id, actor) + method(isAuthSys, sender, authorizers), + method(isRejectSys, sender, rejecters) // ) } // namespace AuthSig diff --git a/services/system/AuthSig/src/AuthSig.cpp b/services/system/AuthSig/src/AuthSig.cpp index b6762c408..f27cd2923 100644 --- a/services/system/AuthSig/src/AuthSig.cpp +++ b/services/system/AuthSig/src/AuthSig.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -83,32 +84,16 @@ namespace SystemService authTable.put(AuthRecord{.account = getSender(), .pubkey = std::move(key)}); } - void AuthSig::stagedAccept(uint32_t staged_tx_id, psibase::AccountNumber actor) + bool AuthSig::isAuthSys(psibase::AccountNumber sender, + std::vector authorizers) { - check(getSender() == StagedTxService::service, "can only be called by staged-tx"); - - auto staged_tx = to().get_staged_tx(staged_tx_id); - auto actions = staged_tx.action_list.actions; - - check(actions.size() > 0, "staged tx has no actions"); - if (actor == actions[0].sender) - { - auto [execute, allowedActions] = to().get_exec_info(staged_tx_id); - recurse().to().runAs(std::move(execute), allowedActions); - } + return std::ranges::contains(authorizers, sender); } - void AuthSig::stagedReject(uint32_t staged_tx_id, psibase::AccountNumber actor) + bool AuthSig::isRejectSys(psibase::AccountNumber sender, + std::vector rejecters) { - check(getSender() == StagedTxService::service, "can only be called by staged-tx"); - - auto staged_tx = to().get_staged_tx(staged_tx_id); - auto actions = staged_tx.action_list.actions; - check(actions.size() > 0, "staged tx has no actions"); - if (actor == actions[0].sender) - { - to().remove(staged_tx.id, staged_tx.txid); - } + return std::ranges::contains(rejecters, sender); } } // namespace AuthSig } // namespace SystemService diff --git a/services/system/StagedTx/service/Cargo.toml b/services/system/StagedTx/service/Cargo.toml index 575dec864..1f8082571 100644 --- a/services/system/StagedTx/service/Cargo.toml +++ b/services/system/StagedTx/service/Cargo.toml @@ -8,6 +8,7 @@ edition.workspace = true publish = false [package.metadata.psibase] +flags = ["allowSudo"] server = "r-staged-tx" postinstall = [ { sender = "staged-tx", service = "staged-tx", method = "init", rawData = "0000" }, diff --git a/services/system/StagedTx/service/cpp/include/services/system/StagedTx.hpp b/services/system/StagedTx/service/cpp/include/services/system/StagedTx.hpp index 5ccb49a76..f8ccb48b7 100644 --- a/services/system/StagedTx/service/cpp/include/services/system/StagedTx.hpp +++ b/services/system/StagedTx/service/cpp/include/services/system/StagedTx.hpp @@ -57,6 +57,14 @@ namespace SystemService /// * `actions` - The actions to be staged uint32_t propose(const std::vector& actions); + /// Removes (deletes) a staged transaction + /// + /// A staged transaction can only be removed by its proposer. + /// + /// * `id`: The ID of the database record containing the staged transaction + /// * `txid`: The unique txid of the staged transaction + void remove(uint32_t id, psibase::Checksum256 txid); + /// Indicates that the caller accepts the specified staged transaction /// /// Depending on the staging rules enforced by the auth service of the sender @@ -72,44 +80,20 @@ namespace SystemService /// * `txid`: The unique txid of the staged transaction void reject(uint32_t id, psibase::Checksum256 txid); - /// Removes (deletes) a staged transaction - /// - /// A staged transaction can only be removed by the proposer, the staged tx - /// first sender, or the first sender's auth service. - /// - /// * `id`: The ID of the database record containing the staged transaction - /// * `txid`: The unique txid of the staged transaction - void remove(uint32_t id, psibase::Checksum256 txid); - - /// Notifies the staged-tx service that a staged transaction should be executed. - /// Typically this call is facilitated by the staged transaction's first sender's auth - /// service on behalf of the staged transaction's first sender. - /// - /// * `id`: The ID of the database record containing the staged transaction - /// * `txid`: The unique txid of the staged transaction - void execute(uint32_t id, psibase::Checksum256 txid); - - /// Gets a staged transaction by id. Typically used inline by auth services. + /// Gets a staged transaction by id. /// /// * `id`: The ID of the database record containing the staged transaction StagedTx get_staged_tx(uint32_t id); - - /// Gets info needed for staged tx execution. Typically used inline by auth services. - /// - /// * `id`: The ID of the database record containing the staged transaction - std::tuple> get_exec_info(uint32_t id); }; // clang-format off PSIO_REFLECT(StagedTxService, method(init), method(propose, actions), + method(remove, id, txid), method(accept, id, txid), method(reject, id, txid), - method(remove, id, txid), - method(execute, id, txid), method(get_staged_tx, id), - method(get_exec_info, id) ); // clang-format on diff --git a/services/system/StagedTx/service/src/lib.rs b/services/system/StagedTx/service/src/lib.rs index 904de5e14..052359640 100644 --- a/services/system/StagedTx/service/src/lib.rs +++ b/services/system/StagedTx/service/src/lib.rs @@ -11,7 +11,6 @@ pub fn sha256(data: &[u8]) -> [u8; 32] { pub fn get_auth_service(sender: AccountNumber) -> Option { Accounts::call().getAccount(sender).map(|a| a.authService) } - /// A service for staged transaction proposal and execution /// /// A staged transaction allows an account (the proposer) to propose a set of @@ -31,7 +30,7 @@ pub mod service { use crate::sha256; use async_graphql::SimpleObject; use psibase::fracpack::Pack; - use psibase::services::transact::{auth_interface::auth_action_structs, ServiceMethod}; + use psibase::services::transact::auth_interface::auth_action_structs; use psibase::services::{ accounts::Wrapper as Accounts, events::Wrapper as Events, transact::Wrapper as Transact, }; @@ -39,34 +38,36 @@ pub mod service { use serde::{Deserialize, Serialize}; struct StagedTxPolicy { + user: AccountNumber, service_caller: ServiceCaller, } impl StagedTxPolicy { - pub fn new(auth_service: AccountNumber) -> Self { + pub fn new(user: AccountNumber) -> Self { StagedTxPolicy { + user, service_caller: ServiceCaller { sender: Wrapper::SERVICE, - service: auth_service, + service: get_auth_service(user).unwrap(), }, } } - pub fn accept(&self, id: u32, actor: psibase::AccountNumber) { - self.service_caller.call_returns_nothing( - MethodNumber::from(auth_action_structs::stagedAccept::ACTION_NAME), - auth_action_structs::stagedAccept { - staged_tx_id: id, - actor, + pub fn does_auth(&self, accepters: Vec) -> bool { + self.service_caller.call( + MethodNumber::from(auth_action_structs::isAuthSys::ACTION_NAME), + auth_action_structs::isAuthSys { + sender: self.user, + authorizers: accepters, }, ) } - pub fn reject(&self, id: u32, actor: psibase::AccountNumber) { - self.service_caller.call_returns_nothing( - MethodNumber::from(auth_action_structs::stagedReject::ACTION_NAME), - auth_action_structs::stagedReject { - staged_tx_id: id, - actor, + pub fn does_reject(&self, rejecters: Vec) -> bool { + self.service_caller.call( + MethodNumber::from(auth_action_structs::isRejectSys::ACTION_NAME), + auth_action_structs::isRejectSys { + sender: self.user, + rejecters, }, ) } @@ -101,43 +102,17 @@ pub mod service { self.id } - #[secondary_key(1)] - fn by_act_sender(&self) -> (AccountNumber, u32) { - check( - self.action_list.actions.len() > 0, - "Staged transaction contains no actions", - ); - (self.action_list.actions[0].sender, self.id) - } - - #[secondary_key(2)] - fn by_staging_policy(&self) -> (AccountNumber, u32) { - check( - self.action_list.actions.len() > 0, - "Staged transaction contains no actions", - ); - let sender = self.action_list.actions[0].sender; - let auth_service = - get_auth_service(sender).expect("Action sender account in staged tx is invalid"); - (auth_service, self.id) - } - fn new(actions: Vec) -> Self { check( actions.len() > 0, "Staged transaction must contain at least one action", ); - let sender = actions[0].sender; - check( - Accounts::call().getAccount(sender).is_some(), - "Sender account in staged tx is invalid", - ); - for action in &actions { + let sender = action.sender; check( - action.sender == sender, - "All actions in staged tx must have the same sender", + Accounts::call().getAccount(sender).is_some(), + "Sender account in staged tx is invalid", ); } @@ -168,22 +143,43 @@ pub mod service { staged_tx } - fn first_sender(&self) -> AccountNumber { - self.action_list.actions[0].sender + fn accept(&self) { + Response::upsert(self.id, true); } - fn staged_tx_policy(&self) -> StagedTxPolicy { - StagedTxPolicy::new(get_auth_service(self.first_sender()).unwrap()) + fn reject(&self) { + Response::upsert(self.id, false); } - fn emit(&self, event_type: u8) { - Wrapper::emit().history().updated( - self.txid, - self.first_sender(), - get_sender(), - Transact::call().currentBlock().time, - event_type.into(), - ); + fn delete(&self) { + // Delete all responses for this staged tx + let id = self.id; + let responses = ResponseTable::new(); + responses + .get_index_pk() + .range((id, AccountNumber::new(0))..=(id, AccountNumber::new(u64::MAX))) + .for_each(|r| responses.erase(&(r.id, r.account))); + + // Delete the staged tx itself + StagedTxTable::new().erase(&id); + } + + fn accepters(&self) -> Vec { + ResponseTable::new() + .get_index_pk() + .range((self.id, AccountNumber::new(0))..=(self.id, AccountNumber::new(u64::MAX))) + .filter(|response| response.accepted) + .map(|response| response.account) + .collect() + } + + fn rejecters(&self) -> Vec { + ResponseTable::new() + .get_index_pk() + .range((self.id, AccountNumber::new(0))..=(self.id, AccountNumber::new(u64::MAX))) + .filter(|response| !response.accepted) + .map(|response| response.account) + .collect() } } @@ -252,8 +248,7 @@ pub mod service { let updated = MethodNumber::from("updated"); Events::call().setSchema(create_schema::()); Events::call().addIndex(DbId::HistoryEvent, SERVICE, updated, 0); // Index events related to specific txid - Events::call().addIndex(DbId::HistoryEvent, SERVICE, updated, 1); // Index events related to specific txid sender - Events::call().addIndex(DbId::HistoryEvent, SERVICE, updated, 2); // Index events related to specific proposer/accepter/rejecter + Events::call().addIndex(DbId::HistoryEvent, SERVICE, updated, 1); // Index events related to specific proposer/accepter/rejecter } #[pre_action(exclude(init))] @@ -277,7 +272,7 @@ pub mod service { StagedTxTable::new().put(&new_tx).unwrap(); - new_tx.emit(StagedTxEvent::PROPOSED); + emit_update(new_tx.txid, StagedTxEvent::PROPOSED); // A proposal is also an implicit accept accept(new_tx.id, new_tx.txid); @@ -296,13 +291,19 @@ pub mod service { fn accept(id: u32, txid: [u8; 32]) { let staged_tx = StagedTx::get(id, txid); - Response::upsert(id, true); + staged_tx.accept(); - staged_tx.emit(StagedTxEvent::ACCEPTED); + emit_update(staged_tx.txid, StagedTxEvent::ACCEPTED); - staged_tx - .staged_tx_policy() - .accept(staged_tx.id, get_sender()); + let authorized = staged_tx + .action_list + .actions + .iter() + .all(|action| StagedTxPolicy::new(action.sender).does_auth(staged_tx.accepters())); + + if authorized { + execute(staged_tx); + } } /// Indicates that the caller rejects the staged transaction @@ -313,81 +314,57 @@ pub mod service { fn reject(id: u32, txid: [u8; 32]) { let staged_tx = StagedTx::get(id, txid); - Response::upsert(id, false); + staged_tx.reject(); - staged_tx.emit(StagedTxEvent::REJECTED); + emit_update(staged_tx.txid, StagedTxEvent::REJECTED); - staged_tx - .staged_tx_policy() - .reject(staged_tx.id, get_sender()); + let rejected = + staged_tx.action_list.actions.iter().any(|action| { + StagedTxPolicy::new(action.sender).does_reject(staged_tx.rejecters()) + }); + + if rejected { + staged_tx.delete(); + emit_update(staged_tx.txid, StagedTxEvent::DELETED); + } } /// Removes (deletes) a staged transaction /// - /// A staged transaction can only be removed by the proposer, the staged tx - /// first sender, or the first sender's auth service. + /// A staged transaction can only be removed by its proposer. /// /// * `id`: The ID of the database record containing the staged transaction /// * `txid`: The unique txid of the staged transaction #[action] fn remove(id: u32, txid: [u8; 32]) { let staged_tx = StagedTx::get(id, txid); - remove_impl(&staged_tx); - staged_tx.emit(StagedTxEvent::DELETED); - } - // Needed to separate the event emission from the removal logic - fn remove_impl(staged_tx: &StagedTx) { - let sender = get_sender(); - let first_sender = staged_tx.first_sender(); check( - sender == staged_tx.proposer - || sender == first_sender - || sender == get_auth_service(first_sender).unwrap(), - "Only the proposer or the staged tx first sender can remove the staged tx", + staged_tx.proposer == get_sender(), + "only the proposer can explicitly remove a staged transaction", ); - // Delete all responses for this staged tx - let id = staged_tx.id; - let responses = ResponseTable::new(); - let deletable = responses - .get_index_pk() - .range((id, AccountNumber::new(0))..=(id, AccountNumber::new(u64::MAX))) - .collect::>(); - for d in deletable { - responses.erase(&(d.id, d.account)); - } - - // Delete the staged tx itself - StagedTxTable::new().erase(&id); + staged_tx.delete(); + emit_update(staged_tx.txid, StagedTxEvent::DELETED); } - /// Notifies the staged-tx service that a staged transaction should be executed. - /// Typically this call is facilitated by the staged transaction's first sender's auth - /// service on behalf of the staged transaction's first sender. - /// - /// * `id`: The ID of the database record containing the staged transaction - /// * `txid`: The unique txid of the staged transaction - #[action] - fn execute(id: u32, txid: [u8; 32]) { - let staged_tx = StagedTx::get(id, txid); - - check( - staged_tx.first_sender() == get_sender(), - "Only the first sender of the staged tx can execute it", - ); - - remove_impl(&staged_tx); - - staged_tx.action_list.actions.iter().for_each(|action| { - let _ = Transact::call().runAs(action.clone(), Vec::new()); - }); + fn execute(staged_tx: StagedTx) { + staged_tx.delete(); - staged_tx.emit(StagedTxEvent::EXECUTED); - staged_tx.emit(StagedTxEvent::DELETED); + staged_tx + .action_list + .actions + .into_iter() + .for_each(|action| { + let act = action.packed(); + unsafe { native_raw::call(act.as_ptr(), act.len() as u32) }; + }); + + emit_update(staged_tx.txid, StagedTxEvent::EXECUTED); + emit_update(staged_tx.txid, StagedTxEvent::DELETED); } - /// Gets a staged transaction by id. Typically used inline by auth services. + /// Gets a staged transaction by id. /// /// * `id`: The ID of the database record containing the staged transaction #[action] @@ -397,37 +374,6 @@ pub mod service { staged_tx.unwrap() } - /// Gets info needed for staged tx execution. Typically used inline by auth services. - /// - /// * `id`: The ID of the database record containing the staged transaction - #[action] - fn get_exec_info(id: u32) -> (Action, Vec) { - let staged_tx = get_staged_tx(id); - - let make_service_method = |action: &Action| ServiceMethod { - service: action.service, - method: action.method, - }; - - let allowed_actions: Vec = staged_tx - .action_list - .actions - .iter() - .map(make_service_method) - .collect(); - - let staged_tx_sender = staged_tx.action_list.actions[0].sender; - - let execute = Action { - sender: staged_tx_sender, - service: Wrapper::SERVICE, - method: MethodNumber::from(action_structs::execute::ACTION_NAME), - rawData: (id, staged_tx.txid).packed().into(), - }; - - (execute, allowed_actions) - } - #[derive(Debug, Fracpack, Serialize, Deserialize, ToSchema, SimpleObject, Clone)] struct StagedTxEvent { ty: u8, @@ -448,10 +394,18 @@ pub mod service { } } + fn emit_update(txid: [u8; 32], event_type: u8) { + Wrapper::emit().history().updated( + txid, + get_sender(), + Transact::call().currentBlock().time, + event_type.into(), + ); + } + #[event(history)] pub fn updated( txid: [u8; 32], // The txid of the staged transaction - sender: AccountNumber, // The sender of the staged transaction actor: AccountNumber, // The sender of the action causing the event datetime: TimePointUSec, // The time of the event emission event_type: StagedTxEvent, // The type of event diff --git a/services/system/Transact/include/services/system/Transact.hpp b/services/system/Transact/include/services/system/Transact.hpp index a758c909a..ef43b811c 100644 --- a/services/system/Transact/include/services/system/Transact.hpp +++ b/services/system/Transact/include/services/system/Transact.hpp @@ -140,31 +140,35 @@ namespace SystemService // TODO: Return error message instead? void canAuthUserSys(psibase::AccountNumber user); - /// Handle notification related to the acceptance of a staged transaction + /// Check whether a specified set of authorizer accounts are sufficient to authorize sending a + /// transaction from a specified sender. /// - /// An auth service is notified when the sender of the staged transaction - /// is an account that uses the auth service. + /// * `sender`: The sender account for the transaction potentially being authorized. + /// * `authorizers`: The set of accounts that have already authorized the execution of the transaction. /// - /// * `staged_tx_id`: The ID of the staged transaction, used to identify the - /// staged-tx record. - /// * `actor`: The account that accepts the specified staged transaction - void stagedAccept(uint32_t staged_tx_id, psibase::AccountNumber actor); + /// Returns: + /// * `true`: The authorizers are sufficient to authorize a transaction from the sender. + /// * `false`: The authorizers are not sufficient to authorize a transaction from the sender. + bool isAuthSys(psibase::AccountNumber sender, + std::vector authorizers); - /// Handle notification related to the rejection of a staged transaction + /// Check whether a specified set of rejecter accounts are sufficient to reject (cancel) a + /// transaction from a specified sender. /// - /// An auth service is notified when the sender of the staged transaction - /// is an account that uses the auth service. + /// * `sender`: The sender account for the transaction potentially being rejected. + /// * `rejecters`: The set of accounts that have already authorized the rejection of the transaction. /// - /// * `staged_tx_id`: The ID of the staged transaction, used to identify the - /// staged-tx record. - /// * `actor`: The account that rejects the specified staged transaction - void stagedReject(uint32_t staged_tx_id, psibase::AccountNumber actor); + /// Returns: + /// * `true`: The rejecters are sufficient to reject a transaction from the sender. + /// * `false`: The rejecters are not sufficient to reject a transaction from the sender. + bool isRejectSys(psibase::AccountNumber sender, + std::vector rejecters); }; PSIO_REFLECT(AuthInterface, method(checkAuthSys, flags, requester, sender, action, allowedActions, claims), method(canAuthUserSys, user), - method(stagedAccept, txid, actor), - method(stagedReject, txid, actor) + method(isAuthSys, sender, authorizers), + method(isRejectSys, sender, rejecters) // )