Skip to content

Commit c24571a

Browse files
feat: committor service (#366)
## Summary Adding a full fledged committor service to support the following kind of commits: - commits up to 10Kb as long as the size increase per commit is <=1Kb - max accounts per bundle is 3 without lookup tables and 12 with lookup tables - commit results are persisted into a SQlite database in the ledger directory which makes it easy to manually retry them or do this automatically (next committor version will support this) ## Details The following crates were developed [here](https://github.com/magicblock-labs/committor/pulls?q=is%3Apr+is%3Aclosed) and integrated into the validator: ### magicblock-rpc-client - thin wrapper around the solana rpc client with added features like improved transaction signature status checking (including sane retries) - lookup table convenience methods - multi account fetch with known max limit - waiting for slots ### magicblock-table-mania - a lookup table manager that allows to create, extend and deactivate + close lookup tables - the API supports _reserving_ pubkeys which will be added to a table and when they need to be used in a transaction the needed table addresses are provided - once a pubkey is _released_ this is noted and a table is deactivated and closed on chain once all its pubkeys are released ### magicblock-committor-program - allows the committor service to upload account data to be committed in chunks - supports creation/resizing of buffers and filling them with transactions running in parallel (out of order is ok) ### magicblock-committor-service - commits a changeset as fast and efficiently as possible - first prefers transactions that don't require lookup tables since those take time to become available - then prefers args only commits to the use of buffers (which require preparation) ## Sample Flow 1. Validator starts up and reserves common pubkeys (used in all commits) with the committor service 2. Account are cloned and for each account the pubkeys for commits are reserved to prepare the lookup tables which we might need as fast as possible 3. Account(s) commit is scheduled and registered as we did before 4. The commits are processed via the committor service and the on chain transaction signature is provided to the user via logs in a transaction (as we did before, just now we have to wait for the commit to complete) For those commits the committor service ensures that accounts with the same commit (bundle) id are always processed atomically. The committor service also picks the best possible strategy to commit each changeset, preferring speed and then cost. It also inserts configurable (via code) compute budget instructions to each transaction. The outcome of each commit is persisted to a database which allows manual (and in the next version) automated retries of failed commits. Succeessful commits are also persisted to the database and can be used for diagnostics. In the future they should be removed since no retry is necessary. On chain signatures for process-commit/finaliz/undelegate transactions are also persisted to the database in a separate table. This table is queried by the validator to log those signatures as part of a transaction that the user waits for. ### Performance I made sure that there was no performance degradation and <summary> <h6>redline config using 1 account per transaction</h6> <details> ```toml # Example configuration file for Redline application # Inidicates how many concurrent benchmarks to run # # NOTE: each provided unit of parallelism will start its own thread of # execution for benchmark, dramatically increasing the load on target # validator. I.e. each benchmark will run in parallel with others on their own # OS thread, so for example providing 10 for parallelism, has 10x load in # comparison to 1. This might negatively affect validator performance running # on the same host as the REDLINE will compete for compute resources with # validator process parallelism = 1 # Connection settings for main chain and ephemeral URLs [connection] # URL of the chain node to connect to chain-url = "http://127.0.0.1:7799" # chain-url = "http://api.devnet.solana.com" # URL of the ephemeral node to connect to ephem-url = "http://127.0.0.1:8899" # Type of HTTP connection: "http1" or "{ http2 = { streams = <NUM> } }" # http-connection-type = { http2 = { streams = 128 } } http-connection-type = "http1" # Maximum number of HTTP connections http-connections-count = 16 # Maximum number of WebSocket connections ws-connections-count = 16 # RPS Benchmark settings (getX Requests) [rps-benchmark] # whether to perform RPS benchmark, either RPS or TPS benchmark should be enabled or both enabled = false # Number of iterations for the benchmark iterations = 1500 # The desired throughput for transaction submission to the target ER node, # expressed in transactions per second. # Note: This serves as a hint rather than a strict limit. For instance, # specifying a rate of 10,000 RPS while the ER node's request handling # rate is 3,000 RPS, will not increase the validator's capacity to handle the # specified throughput. Consequently, any value exceeding the validator's # saturation point is ineffective. rps = 50 # Number of concurrent executions concurrency = 50 # Number of accounts in the pool to be used getX requests accounts-count = 8 # Mode of rps benchmark, options: mode = "get-account-info" # mode = "get-multiple-accounts" # mode = "get-token-account-balance" # mode = "get-balance" # mode = { combined = [ "get-account-info", "get-balance" ] } # TPS Benchmark settings [tps-benchmark] enabled = true # Number of iterations for the benchmark iterations = 2000 # The desired throughput for transaction submission to the target ER node, # expressed in transactions per second. # Note: This serves as a hint rather than a strict limit. For instance, # specifying a rate of 10,000 TPS while the ER node's transaction ingestion # rate is 3,000 TPS, will not increase the validator's capacity to handle the # specified throughput. Consequently, any value exceeding the validator's # saturation point is ineffective. tps = 40 # Number of concurrent executions concurrency = 40 # Perform a preflight check for each transaction: true or false preflight-check = false # Mode of tps benchmark, options: # mode = "simple-byte-set" # Alternative modes are: #-------------------------------------------------------------------------------- #-------------------------------------------------------------------------------- # every clone-frequency-secs, and airdrop will be performed on of the readonly # accounts on chain, thus triggering clone of account on ER # mode = { trigger-clones = { clone-frequency-secs = 1, accounts-count = 16 } } #-------------------------------------------------------------------------------- # performs an expensive hash compute in loop (iters times), 28 iterations # consume almost the entirety of 200K CUs # mode = { high-cu-cost = { iters = 8 } } #-------------------------------------------------------------------------------- # performs data copy operation between accounts in the pool, each transaction # involves 2 accounts, one readable, one writable, thus high number of # transactions using intersecting set of accounts will create pressure on # scheduler due to account locking # mode = { read-write = { accounts-count = 16 } } #-------------------------------------------------------------------------------- # Uses provided accounts in the pool to read the length of the data field an log it, # all of the accounts are used in read only mode, so multiple transactions of this # kind can run in parallel on multi-threaded scheduler # mode = { read-only = { accounts-count = 32, accounts-per-transaction = 8 } } #-------------------------------### COMMIT ###------------------------------------ # Sends commit transactions to the ER choosinng accounts-per-transaction from # the pool of accounts-count mode = { commit = { accounts-count = 16, accounts-per-transaction = 1 } } #-------------------------------------------------------------------------------- # a combination of various benchmarking modes, to simulate more real world scenarios # mode = { mixed = [ { read-write = { accounts-count = 16 } }, "simple-byte-set", { commit = { accounts-count = 16, accounts-per-transaction = 2 } } ] } [confirmations] # Subscription settings # Whether to subscribe to account notifications: true or false subscribe-to-accounts = false # Whether to subscribe to signature notifications: true or false subscribe-to-signatures = true # Enforce total synchronization: true or false # this ensures that a transaction is considered completed only if account # update and signature update has been received, while preventing other # transactions from running, thus significantly decreasinng throughput. # If disabled, transaction will reserve concurrency slot only for as long as # it's required to receive HTTP response enforce-total-sync = true # TODO: unused get-signature-status = false [data] # Data settings for account encoding and size # Encoding type of the account data: "base58", "base64", "base64+zstd" account-encoding = "base64+zstd" # Size of the account data: bytes128, bytes512, bytes2048, bytes8192 account-size = "bytes512" ``` </details> </summary> #### master ``` +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Metric | Observations | Median | Min | Max | Avg | 95th Perc | Stddev | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | sendTransaction Response (μs) | 2000 | 2690 | 1116 | 12516 | 3340 | 7159 | 1801 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Account Update (μs) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Signature Confirmation (μs) | 2000 | 2756 | 1125 | 12559 | 3433 | 7494 | 1901 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Transactions Per Second (TPS) | 51 | 40 | 40 | 40 | 40 | 40 | 0 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ ``` #### new committor ``` +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Metric | Observations | Median | Min | Max | Avg | 95th Perc | Stddev | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | sendTransaction Response (μs) | 2000 | 2219 | 1104 | 5525915 | 39628 | 3647 | 329493 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Account Update (μs) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Signature Confirmation (μs) | 2000 | 2247 | 1116 | 5525967 | 39668 | 3730 | 329498 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Transactions Per Second (TPS) | 52 | 40 | 13 | 40 | 39 | 17 | 4 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ ``` We can see that there is a huge deviation in this branch due to the first clone taking a lot longer as it reserves the pubkeys needed to commit the cloned account in a lookup table. I improved this delay a bit by initiating the reserve transaction before running the clone transaction, thus performing both steps in parallel. However the reserve transaction takes a magnitude longer than the cloning into our validator and thus the overall time didn't improve much. #### Redline Configuration same as above with below change ```toml #-------------------------------### COMMIT ###------------------------------------ # Sends commit transactions to the ER choosinng accounts-per-transaction from # the pool of accounts-count mode = { commit = { accounts-count = 16, accounts-per-transaction = 8 } } ``` #### master Lots of committor transactions fail here since the created transactions are too large since we commit up to 8 accounts in each transaction. ``` +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Metric | Observations | Median | Min | Max | Avg | 95th Perc | Stddev | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | sendTransaction Response (μs) | 2000 | 1863 | 1010 | 17892 | 1959 | 2716 | 554 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Account Update (μs) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Signature Confirmation (μs) | 2000 | 1901 | 1239 | 37572 | 2048 | 2949 | 1052 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ | Transactions Per Second (TPS) | 51 | 40 | 40 | 40 | 40 | 40 | 0 | +-------------------------------+--------------+--------+------+-------+------+-----------+--------+ ``` #### new committor NOTE: that here lots of commit transactions fail since they are created with the same ephemeral blockhash and thus are identical to transactions that ran already. Others fail due to commits being requested without the finalize of the previous commit having completed. This is an issue that we won't see in real life since identical account bundles won't be committeed in such quick succession in practice. ``` +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Metric | Observations | Median | Min | Max | Avg | 95th Perc | Stddev | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | sendTransaction Response (μs) | 2000 | 1578 | 1110 | 7361215 | 55643 | 2932 | 592707 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Account Update (μs) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Signature Confirmation (μs) | 2000 | 1615 | 1145 | 7361283 | 55699 | 3010 | 592711 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ | Transactions Per Second (TPS) | 51 | 40 | 17 | 40 | 39 | 40 | 3 | +-------------------------------+--------------+--------+------+---------+-------+-----------+--------+ ``` --------- Co-authored-by: Gabriele Picco <piccogabriele@gmail.com>
1 parent 7d55f5b commit c24571a

File tree

125 files changed

+17521
-1348
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+17521
-1348
lines changed

Cargo.lock

Lines changed: 781 additions & 361 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ members = [
1515
"magicblock-accounts-db",
1616
"magicblock-api",
1717
"magicblock-bank",
18+
"magicblock-committor-program",
19+
"magicblock-committor-service",
1820
"magicblock-config",
1921
"magicblock-core",
2022
"magicblock-geyser-plugin",
@@ -25,6 +27,8 @@ members = [
2527
"magicblock-processor",
2628
"magicblock-pubsub",
2729
"magicblock-rpc",
30+
"magicblock-rpc-client",
31+
"magicblock-table-mania",
2832
"magicblock-tokens",
2933
"magicblock-transaction-status",
3034
"magicblock-version",
@@ -55,18 +59,18 @@ assert_matches = "1.5.0"
5559
async-trait = "0.1.77"
5660
base64 = "0.21.7"
5761
bincode = "1.3.3"
62+
borsh = { version = "1.5.1", features = ["derive", "unstable__schema"] }
63+
borsh-derive = "1.5.1"
5864
bs58 = "0.4.0"
5965
byteorder = "1.5.0"
6066
cargo-lock = "10.0.0"
61-
expiring-hashmap = { path = "./utils/expiring-hashmap" }
6267
conjunto-transwise = { git = "https://github.com/magicblock-labs/conjunto.git", rev = "bf82b45" }
6368
console-subscriber = "0.2.0"
64-
isocountry = "0.3.2"
6569
crossbeam-channel = "0.5.11"
70+
ed25519-dalek = "1.0.1"
6671
enum-iterator = "1.5.0"
6772
env_logger = "0.11.2"
68-
magic-domain-program = { git = "https://github.com/magicblock-labs/magic-domain-program.git", rev = "ea04d46", default-features = false }
69-
magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "4af7f1c" }
73+
expiring-hashmap = { path = "./utils/expiring-hashmap" }
7074
fd-lock = "4.0.2"
7175
flume = "0.11"
7276
fs_extra = "1.3.0"
@@ -77,6 +81,7 @@ hostname = "0.4.0"
7781
http-body-util = "0.1.2"
7882
hyper = "1.4.1"
7983
hyper-util = "0.1.9"
84+
isocountry = "0.3.2"
8085
itertools = "0.14"
8186
jsonrpc-core = "18.0.0"
8287
jsonrpc-core-client = "18.0.0"
@@ -88,21 +93,6 @@ lazy_static = "1.4.0"
8893
libc = "0.2.153"
8994
libloading = "0.7.4"
9095
log = "0.4.20"
91-
num_cpus = "1.16.0"
92-
num-derive = "0.4"
93-
num-format = "0.4.4"
94-
num-traits = "0.2"
95-
paste = "1.0"
96-
prometheus = "0.13.4"
97-
# Needs to match https://crates.io/crates/solana-storage-bigtable/2.1.13/dependencies
98-
prost = "0.11.9"
99-
rand = "0.8.5"
100-
rayon = "1.10.0"
101-
rustc_version = "0.4"
102-
semver = "1.0.22"
103-
serde = "1.0.217"
104-
serde_derive = "1.0"
105-
serde_json = "1.0"
10696
magicblock-account-cloner = { path = "./magicblock-account-cloner" }
10797
magicblock-account-dumper = { path = "./magicblock-account-dumper" }
10898
magicblock-account-fetcher = { path = "./magicblock-account-fetcher" }
@@ -112,8 +102,14 @@ magicblock-accounts-api = { path = "./magicblock-accounts-api" }
112102
magicblock-accounts-db = { path = "./magicblock-accounts-db" }
113103
magicblock-api = { path = "./magicblock-api" }
114104
magicblock-bank = { path = "./magicblock-bank" }
105+
magicblock-committor-program = { path = "./magicblock-committor-program", features = [
106+
"no-entrypoint",
107+
] }
108+
magicblock-committor-service = { path = "./magicblock-committor-service" }
115109
magicblock-config = { path = "./magicblock-config" }
116110
magicblock-core = { path = "./magicblock-core" }
111+
magicblock-delegation-program = { git = "https://github.com/magicblock-labs/delegation-program.git", rev = "4af7f1c" }
112+
magic-domain-program = { git = "https://github.com/magicblock-labs/magic-domain-program.git", rev = "ea04d46", default-features = false }
117113
magicblock-geyser-plugin = { path = "./magicblock-geyser-plugin" }
118114
magicblock-ledger = { path = "./magicblock-ledger" }
119115
magicblock-metrics = { path = "./magicblock-metrics" }
@@ -123,10 +119,29 @@ magicblock-processor = { path = "./magicblock-processor" }
123119
magicblock-program = { path = "./programs/magicblock" }
124120
magicblock-pubsub = { path = "./magicblock-pubsub" }
125121
magicblock-rpc = { path = "./magicblock-rpc" }
122+
magicblock-rpc-client = { path = "./magicblock-rpc-client" }
123+
magicblock-table-mania = { path = "./magicblock-table-mania" }
126124
magicblock-tokens = { path = "./magicblock-tokens" }
127125
magicblock-transaction-status = { path = "./magicblock-transaction-status" }
128126
magicblock-version = { path = "./magicblock-version" }
127+
num-derive = "0.4"
128+
num-format = "0.4.4"
129+
num-traits = "0.2"
130+
num_cpus = "1.16.0"
131+
paste = "1.0"
132+
prometheus = "0.13.4"
133+
# Needs to match https://crates.io/crates/solana-storage-bigtable/2.1.13/dependencies
134+
prost = "0.11.9"
129135
protobuf-src = "1.1"
136+
rand = "0.8.5"
137+
rayon = "1.10.0"
138+
rustc_version = "0.4"
139+
rusqlite = { version = "0.34.0", features = ["bundled"] } # bundled sqlite 3.44
140+
semver = "1.0.22"
141+
serde = "1.0.217"
142+
serde_derive = "1.0"
143+
serde_json = "1.0"
144+
sha3 = "0.10.8"
130145
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "7bdfefc" }
131146
solana-accounts-db = { version = "2.2" }
132147
solana-account-decoder = { version = "2.2" }
@@ -143,33 +158,39 @@ solana-log-collector = { version = "2.2" }
143158
solana-measure = { version = "2.2" }
144159
solana-metrics = { version = "2.2" }
145160
solana-perf = { version = "2.2" }
161+
solana-program = "2.2"
146162
solana-program-runtime = { version = "2.2" }
163+
solana-program-test = "2.2"
147164
solana-pubkey = { version = "2.2" }
148165
solana-rayon-threadlimit = { version = "2.2" }
149166
solana-pubsub-client = { version = "2.2" }
150167
solana-rpc = "2.2"
151168
solana-rpc-client = { version = "2.2" }
152169
solana-rpc-client-api = { version = "2.2" }
153170
solana-sdk = { version = "2.2" }
154-
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "e93eb57", features = [ "dev-context-only-utils" ] }
171+
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "e93eb57", features = [
172+
"dev-context-only-utils",
173+
] }
155174
solana-svm-transaction = { version = "2.2" }
156175
solana-storage-proto = { path = "storage-proto" }
157176
solana-system-program = { version = "2.2" }
158177
solana-timings = "2.2"
159178
solana-transaction-status = { version = "2.2" }
179+
solana-transaction-status-client-types = "2.2"
160180
spl-token = "=7.0"
161181
spl-token-2022 = "=6.0"
182+
static_assertions = "1.1.0"
162183
strum = "0.24"
163184
strum_macros = "0.24"
164185
tempfile = "3.10.1"
165186
test-tools = { path = "./test-tools" }
166187
test-tools-core = { path = "./test-tools-core" }
167188
thiserror = "1.0.57"
168-
toml = "0.8.13"
169189
# Update solana-tokio patch below when updating this version
170190
tokio = "1.0"
171191
tokio-stream = "0.1.15"
172192
tokio-util = "0.7.10"
193+
toml = "0.8.13"
173194
# Tonic version 11 conflicts with lower level deps of solana and 0.9.x is the last
174195
# version that allows prost 0.11.x to be used
175196
tonic = "0.9.2"
@@ -182,6 +203,6 @@ vergen = "8.3.1"
182203
# some solana dependencies have solana-storage-proto as dependency
183204
# we need to patch them with our version, because they use protobuf-src v1.1.0
184205
# and we use protobuf-src v2.1.1. Otherwise compilation fails
185-
solana-storage-proto = { path = "./storage-proto" }
186206
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "7bdfefc" }
187-
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", features = [ "dev-context-only-utils" ] }
207+
solana-storage-proto = { path = "./storage-proto" }
208+
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git" }

magicblock-account-cloner/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ magicblock-account-updates = { workspace = true }
1717
magicblock-account-dumper = { workspace = true }
1818
magicblock-accounts-api = { workspace = true }
1919
magicblock-core = { workspace = true }
20+
magicblock-committor-service = { workspace = true }
2021
magicblock-metrics = { workspace = true }
2122
magicblock-mutator = { workspace = true }
2223
solana-sdk = { workspace = true }
@@ -26,3 +27,6 @@ thiserror = { workspace = true }
2627
lru = "0.14"
2728

2829
[dev-dependencies]
30+
magicblock-committor-service = { workspace = true, features = [
31+
"dev-context-only-utils",
32+
] }

magicblock-account-cloner/src/account_cloner.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@ use futures_util::future::BoxFuture;
88
use magicblock_account_dumper::AccountDumperError;
99
use magicblock_account_fetcher::AccountFetcherError;
1010
use magicblock_account_updates::AccountUpdatesError;
11+
use magicblock_committor_service::{
12+
error::{CommittorServiceError, CommittorServiceResult},
13+
ChangesetCommittor,
14+
};
1115
use magicblock_core::magic_program;
1216
use solana_sdk::{clock::Slot, pubkey::Pubkey, signature::Signature};
1317
use thiserror::Error;
14-
use tokio::sync::oneshot::Sender;
18+
use tokio::sync::oneshot::{self, Sender};
1519

1620
#[derive(Debug, Clone, Error)]
1721
pub enum AccountClonerError {
@@ -21,6 +25,9 @@ pub enum AccountClonerError {
2125
#[error(transparent)]
2226
RecvError(#[from] tokio::sync::oneshot::error::RecvError),
2327

28+
#[error("JoinError ({0})")]
29+
JoinError(String),
30+
2431
#[error(transparent)]
2532
AccountFetcherError(#[from] AccountFetcherError),
2633

@@ -30,6 +37,9 @@ pub enum AccountClonerError {
3037
#[error(transparent)]
3138
AccountDumperError(#[from] AccountDumperError),
3239

40+
#[error("CommittorServiceError {0}")]
41+
CommittorServiceError(String),
42+
3343
#[error("ProgramDataDoesNotExist")]
3444
ProgramDataDoesNotExist,
3545

@@ -66,6 +76,50 @@ pub enum AccountClonerUnclonableReason {
6676
DelegatedAccountsNotClonedWhileHydrating,
6777
}
6878

79+
pub async fn map_committor_request_result<T, CC: ChangesetCommittor>(
80+
res: oneshot::Receiver<CommittorServiceResult<T>>,
81+
changeset_committor: Arc<CC>,
82+
) -> AccountClonerResult<T> {
83+
match res.await.map_err(|err| {
84+
// Send request error
85+
AccountClonerError::CommittorServiceError(format!(
86+
"error sending request {err:?}"
87+
))
88+
})? {
89+
Ok(val) => Ok(val),
90+
Err(err) => {
91+
// Commit error
92+
match err {
93+
CommittorServiceError::TableManiaError(table_mania_err) => {
94+
let Some(sig) = table_mania_err.signature() else {
95+
return Err(AccountClonerError::CommittorServiceError(
96+
format!("{:?}", table_mania_err),
97+
));
98+
};
99+
let cus =
100+
changeset_committor.get_transaction_cus(&sig).await;
101+
let logs =
102+
changeset_committor.get_transaction_logs(&sig).await;
103+
let cus_str = cus
104+
.map(|cus| format!("{:?}", cus))
105+
.unwrap_or("N/A".to_string());
106+
let logs_str = logs
107+
.map(|logs| format!("{:#?}", logs))
108+
.unwrap_or("N/A".to_string());
109+
Err(AccountClonerError::CommittorServiceError(format!(
110+
"{:?}\nCUs: {cus_str}\nLogs: {logs_str}",
111+
table_mania_err
112+
)))
113+
}
114+
_ => Err(AccountClonerError::CommittorServiceError(format!(
115+
"{:?}",
116+
err
117+
))),
118+
}
119+
}
120+
}
121+
}
122+
69123
#[derive(Debug, Clone)]
70124
pub struct AccountClonerPermissions {
71125
pub allow_cloning_refresh: bool,
@@ -75,6 +129,15 @@ pub struct AccountClonerPermissions {
75129
pub allow_cloning_program_accounts: bool,
76130
}
77131

132+
impl AccountClonerPermissions {
133+
pub fn can_clone(&self) -> bool {
134+
self.allow_cloning_feepayer_accounts
135+
|| self.allow_cloning_undelegated_accounts
136+
|| self.allow_cloning_delegated_accounts
137+
|| self.allow_cloning_program_accounts
138+
}
139+
}
140+
78141
#[derive(Debug, Clone)]
79142
pub enum AccountClonerOutput {
80143
Cloned {

magicblock-account-cloner/src/remote_account_cloner_client.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use magicblock_account_dumper::AccountDumper;
1111
use magicblock_account_fetcher::AccountFetcher;
1212
use magicblock_account_updates::AccountUpdates;
1313
use magicblock_accounts_api::InternalAccountProvider;
14+
use magicblock_committor_service::ChangesetCommittor;
1415
use solana_sdk::pubkey::Pubkey;
1516
use tokio::sync::oneshot::channel;
1617

@@ -25,14 +26,15 @@ pub struct RemoteAccountClonerClient {
2526
}
2627

2728
impl RemoteAccountClonerClient {
28-
pub fn new<IAP, AFE, AUP, ADU>(
29-
worker: &RemoteAccountClonerWorker<IAP, AFE, AUP, ADU>,
29+
pub fn new<IAP, AFE, AUP, ADU, CC>(
30+
worker: &RemoteAccountClonerWorker<IAP, AFE, AUP, ADU, CC>,
3031
) -> Self
3132
where
3233
IAP: InternalAccountProvider,
3334
AFE: AccountFetcher,
3435
AUP: AccountUpdates,
3536
ADU: AccountDumper,
37+
CC: ChangesetCommittor,
3638
{
3739
Self {
3840
clone_request_sender: worker.get_clone_request_sender(),

0 commit comments

Comments
 (0)