1use std::path::PathBuf;
6
7use anyhow::{bail, ensure};
8use clap::{self, Args, Parser};
9use iota_types::{
10 base_types::{IotaAddress, SequenceNumber},
11 move_package::UpgradePolicy,
12 object::{Object, Owner},
13 programmable_transaction_builder::ProgrammableTransactionBuilder,
14 transaction::{Argument, CallArg, ObjectArg},
15};
16use move_compiler::editions::Flavor;
17use move_core_types::{
18 parsing::{
19 parser::{Parser as MoveCLParser, parse_u64, parse_u256},
20 values::{ParsableValue, ParsedValue, ValueToken},
21 },
22 runtime_value::{MoveStruct, MoveValue},
23 u256::U256,
24};
25use move_symbol_pool::Symbol;
26use move_transactional_test_runner::tasks::{RunCommand, SyntaxChoice};
27
28use crate::test_adapter::{FakeID, IotaTestAdapter};
29
30pub const IOTA_ARGS_LONG: &str = "iota-args";
31
32#[derive(Clone, Debug, clap::Parser)]
33pub struct IotaRunArgs {
34 #[arg(long)]
35 pub sender: Option<String>,
36 #[arg(long)]
37 pub gas_price: Option<u64>,
38 #[arg(long)]
39 pub summarize: bool,
40}
41
42#[derive(Debug, clap::Parser, Default)]
43pub struct IotaPublishArgs {
44 #[arg(long)]
45 pub sender: Option<String>,
46 #[arg(long, action = clap::ArgAction::SetTrue)]
47 pub upgradeable: bool,
48 #[arg(long, num_args(1..))]
49 pub dependencies: Vec<String>,
50 #[arg(long)]
51 pub gas_price: Option<u64>,
52}
53
54#[derive(Debug, clap::Parser)]
55pub struct IotaInitArgs {
56 #[arg(long, num_args(1..))]
57 pub accounts: Option<Vec<String>>,
58 #[arg(long)]
59 pub protocol_version: Option<u64>,
60 #[arg(long)]
61 pub max_gas: Option<u64>,
62 #[arg(long)]
63 pub move_binary_format_version: Option<u32>,
64 #[arg(long)]
65 pub simulator: bool,
66 #[arg(long)]
67 pub custom_validator_account: bool,
68 #[arg(long)]
69 pub reference_gas_price: Option<u64>,
70 #[arg(long)]
71 pub default_gas_price: Option<u64>,
72 #[arg(long)]
73 pub objects_snapshot_min_checkpoint_lag: Option<usize>,
74 #[arg(long)]
75 pub flavor: Option<Flavor>,
76 #[arg(long)]
79 pub epochs_to_keep: Option<u64>,
80 #[clap(long)]
83 pub data_ingestion_path: Option<PathBuf>,
84 #[clap(long)]
87 pub rest_api_url: Option<String>,
88}
89
90#[derive(Debug, clap::Parser)]
91pub struct ViewObjectCommand {
92 #[arg(value_parser = parse_fake_id)]
93 pub id: FakeID,
94}
95
96#[derive(Debug, clap::Parser)]
97pub struct TransferObjectCommand {
98 #[arg(value_parser = parse_fake_id)]
99 pub id: FakeID,
100 #[arg(long)]
101 pub recipient: String,
102 #[arg(long)]
103 pub sender: Option<String>,
104 #[arg(long)]
105 pub gas_budget: Option<u64>,
106 #[arg(long)]
107 pub gas_price: Option<u64>,
108}
109
110#[derive(Debug, clap::Parser)]
111pub struct ConsensusCommitPrologueCommand {
112 #[arg(long)]
113 pub timestamp_ms: u64,
114}
115
116#[derive(Debug, clap::Parser)]
117pub struct ProgrammableTransactionCommand {
118 #[arg(long)]
119 pub sender: Option<String>,
120 #[clap(long = "sponsor")]
121 pub sponsor: Option<String>,
122 #[arg(long = "gas-budget")]
123 pub gas_budget: Option<u64>,
124 #[arg(long)]
125 pub gas_price: Option<u64>,
126 #[clap(long = "gas-payment", value_parser = parse_fake_id)]
127 pub gas_payment: Option<FakeID>,
128 #[arg(long = "dev-inspect")]
129 pub dev_inspect: bool,
130 #[clap(long = "dry-run")]
131 pub dry_run: bool,
132 #[arg(
133 long,
134 value_parser = ParsedValue::<IotaExtraValueArgs>::parse,
135 num_args(1..),
136 action = clap::ArgAction::Append,
137 )]
138 pub inputs: Vec<ParsedValue<IotaExtraValueArgs>>,
139}
140
141#[derive(Debug, clap::Parser)]
142pub struct UpgradePackageCommand {
143 #[arg(long)]
144 pub package: String,
145 #[arg(long, value_parser = parse_fake_id)]
146 pub upgrade_capability: FakeID,
147 #[arg(long, num_args(1..))]
148 pub dependencies: Vec<String>,
149 #[arg(long)]
150 pub sender: String,
151 #[arg(long)]
152 pub gas_budget: Option<u64>,
153 #[arg(long)]
154 pub syntax: Option<SyntaxChoice>,
155 #[arg(long, default_value="compatible", value_parser = parse_policy)]
156 pub policy: u8,
157 #[arg(long)]
158 pub gas_price: Option<u64>,
159}
160
161#[derive(Debug, clap::Parser)]
162pub struct StagePackageCommand {
163 #[arg(long)]
164 pub syntax: Option<SyntaxChoice>,
165 #[arg(long, num_args(1..))]
166 pub dependencies: Vec<String>,
167}
168
169#[derive(Debug, clap::Parser)]
170pub struct SetAddressCommand {
171 pub address: String,
172 #[arg(value_parser = ParsedValue::<IotaExtraValueArgs>::parse)]
173 pub input: ParsedValue<IotaExtraValueArgs>,
174}
175
176#[derive(Debug, clap::Parser)]
177pub struct AdvanceClockCommand {
178 #[arg(long)]
179 pub duration_ns: u64,
180}
181
182#[derive(Debug, clap::Parser)]
183pub struct RunGraphqlCommand {
184 #[arg(long)]
185 pub show_usage: bool,
186 #[arg(long)]
187 pub show_headers: bool,
188 #[arg(long)]
189 pub show_service_version: bool,
190 #[arg(long, num_args(1..))]
191 pub cursors: Vec<String>,
192 #[arg(long)]
193 pub wait_for_checkpoint_pruned: Option<u64>,
194}
195
196#[derive(Debug, clap::Parser)]
197pub struct CreateCheckpointCommand {
198 pub count: Option<u64>,
199}
200
201#[derive(Debug, clap::Parser)]
202pub struct AdvanceEpochCommand {
203 pub count: Option<u64>,
204}
205
206#[derive(Debug, clap::Parser)]
207pub struct SetRandomStateCommand {
208 #[arg(long)]
209 pub randomness_round: u64,
210 #[arg(long)]
211 pub random_bytes: String,
212 #[arg(long)]
213 pub randomness_initial_version: u64,
214}
215
216#[derive(Debug)]
217pub enum IotaSubcommand<ExtraValueArgs: ParsableValue, ExtraRunArgs: Parser> {
218 ViewObject(ViewObjectCommand),
219 TransferObject(TransferObjectCommand),
220 ConsensusCommitPrologue(ConsensusCommitPrologueCommand),
221 ProgrammableTransaction(ProgrammableTransactionCommand),
222 UpgradePackage(UpgradePackageCommand),
223 StagePackage(StagePackageCommand),
224 SetAddress(SetAddressCommand),
225 CreateCheckpoint(CreateCheckpointCommand),
226 AdvanceEpoch(AdvanceEpochCommand),
227 AdvanceClock(AdvanceClockCommand),
228 SetRandomState(SetRandomStateCommand),
229 ViewCheckpoint,
230 RunGraphql(RunGraphqlCommand),
231 Bench(RunCommand<ExtraValueArgs>, ExtraRunArgs),
232}
233
234impl<ExtraValueArgs: ParsableValue, ExtraRunArgs: Parser> clap::FromArgMatches
235 for IotaSubcommand<ExtraValueArgs, ExtraRunArgs>
236{
237 fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
238 Ok(match matches.subcommand() {
239 Some(("view-object", matches)) => {
240 IotaSubcommand::ViewObject(ViewObjectCommand::from_arg_matches(matches)?)
241 }
242 Some(("transfer-object", matches)) => {
243 IotaSubcommand::TransferObject(TransferObjectCommand::from_arg_matches(matches)?)
244 }
245 Some(("consensus-commit-prologue", matches)) => {
246 IotaSubcommand::ConsensusCommitPrologue(
247 ConsensusCommitPrologueCommand::from_arg_matches(matches)?,
248 )
249 }
250 Some(("programmable", matches)) => IotaSubcommand::ProgrammableTransaction(
251 ProgrammableTransactionCommand::from_arg_matches(matches)?,
252 ),
253 Some(("upgrade", matches)) => {
254 IotaSubcommand::UpgradePackage(UpgradePackageCommand::from_arg_matches(matches)?)
255 }
256 Some(("stage-package", matches)) => {
257 IotaSubcommand::StagePackage(StagePackageCommand::from_arg_matches(matches)?)
258 }
259 Some(("set-address", matches)) => {
260 IotaSubcommand::SetAddress(SetAddressCommand::from_arg_matches(matches)?)
261 }
262 Some(("create-checkpoint", matches)) => IotaSubcommand::CreateCheckpoint(
263 CreateCheckpointCommand::from_arg_matches(matches)?,
264 ),
265 Some(("advance-epoch", matches)) => {
266 IotaSubcommand::AdvanceEpoch(AdvanceEpochCommand::from_arg_matches(matches)?)
267 }
268 Some(("advance-clock", matches)) => {
269 IotaSubcommand::AdvanceClock(AdvanceClockCommand::from_arg_matches(matches)?)
270 }
271 Some(("set-random-state", matches)) => {
272 IotaSubcommand::SetRandomState(SetRandomStateCommand::from_arg_matches(matches)?)
273 }
274 Some(("view-checkpoint", _)) => IotaSubcommand::ViewCheckpoint,
275 Some(("run-graphql", matches)) => {
276 IotaSubcommand::RunGraphql(RunGraphqlCommand::from_arg_matches(matches)?)
277 }
278 Some(("bench", matches)) => IotaSubcommand::Bench(
279 RunCommand::from_arg_matches(matches)?,
280 ExtraRunArgs::from_arg_matches(matches)?,
281 ),
282 _ => {
283 return Err(clap::Error::raw(
284 clap::error::ErrorKind::InvalidSubcommand,
285 "Invalid submcommand",
286 ));
287 }
288 })
289 }
290
291 fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> {
292 *self = Self::from_arg_matches(matches)?;
293 Ok(())
294 }
295}
296
297impl<ExtraValueArgs: ParsableValue, ExtraRunArgs: Parser> clap::CommandFactory
298 for IotaSubcommand<ExtraValueArgs, ExtraRunArgs>
299{
300 fn command() -> clap::Command {
301 clap::Command::new("iota_sub_command")
302 .subcommand(ViewObjectCommand::command().name("view-object"))
303 .subcommand(TransferObjectCommand::command().name("transfer-object"))
304 .subcommand(ConsensusCommitPrologueCommand::command().name("consensus-commit-prologue"))
305 .subcommand(ProgrammableTransactionCommand::command().name("programmable"))
306 .subcommand(UpgradePackageCommand::command().name("upgrade"))
307 .subcommand(StagePackageCommand::command().name("stage-package"))
308 .subcommand(SetAddressCommand::command().name("set-address"))
309 .subcommand(CreateCheckpointCommand::command().name("create-checkpoint"))
310 .subcommand(AdvanceEpochCommand::command().name("advance-epoch"))
311 .subcommand(AdvanceClockCommand::command().name("advance-clock"))
312 .subcommand(SetRandomStateCommand::command().name("set-random-state"))
313 .subcommand(clap::Command::new("view-checkpoint"))
314 .subcommand(RunGraphqlCommand::command().name("run-graphql"))
315 .subcommand(
316 RunCommand::<ExtraValueArgs>::augment_args(ExtraRunArgs::command()).name("bench"),
317 )
318 }
319
320 fn command_for_update() -> clap::Command {
321 todo!()
322 }
323}
324
325impl<ExtraValueArgs: ParsableValue, ExtraRunArgs: Parser> clap::Parser
326 for IotaSubcommand<ExtraValueArgs, ExtraRunArgs>
327{
328}
329
330#[derive(Clone, Debug)]
331pub enum IotaExtraValueArgs {
332 Object(FakeID, Option<SequenceNumber>),
333 Digest(String),
334 Receiving(FakeID, Option<SequenceNumber>),
335 ImmShared(FakeID, Option<SequenceNumber>),
336}
337
338#[derive(Clone)]
339pub enum IotaValue {
340 MoveValue(MoveValue),
341 Object(FakeID, Option<SequenceNumber>),
342 ObjVec(Vec<(FakeID, Option<SequenceNumber>)>),
343 Digest(String),
344 Receiving(FakeID, Option<SequenceNumber>),
345 ImmShared(FakeID, Option<SequenceNumber>),
346}
347
348impl IotaExtraValueArgs {
349 fn parse_object_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
350 parser: &mut MoveCLParser<'a, ValueToken, I>,
351 ) -> anyhow::Result<Self> {
352 let (fake_id, version) = Self::parse_receiving_or_object_value(parser, "object")?;
353 Ok(IotaExtraValueArgs::Object(fake_id, version))
354 }
355
356 fn parse_receiving_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
357 parser: &mut MoveCLParser<'a, ValueToken, I>,
358 ) -> anyhow::Result<Self> {
359 let (fake_id, version) = Self::parse_receiving_or_object_value(parser, "receiving")?;
360 Ok(IotaExtraValueArgs::Receiving(fake_id, version))
361 }
362
363 fn parse_read_shared_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
364 parser: &mut MoveCLParser<'a, ValueToken, I>,
365 ) -> anyhow::Result<Self> {
366 let (fake_id, version) = Self::parse_receiving_or_object_value(parser, "immshared")?;
367 Ok(IotaExtraValueArgs::ImmShared(fake_id, version))
368 }
369
370 fn parse_digest_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
371 parser: &mut MoveCLParser<'a, ValueToken, I>,
372 ) -> anyhow::Result<Self> {
373 let contents = parser.advance(ValueToken::Ident)?;
374 ensure!(contents == "digest");
375 parser.advance(ValueToken::LParen)?;
376 let package = parser.advance(ValueToken::Ident)?;
377 parser.advance(ValueToken::RParen)?;
378 Ok(IotaExtraValueArgs::Digest(package.to_owned()))
379 }
380
381 fn parse_receiving_or_object_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
382 parser: &mut MoveCLParser<'a, ValueToken, I>,
383 ident_name: &str,
384 ) -> anyhow::Result<(FakeID, Option<SequenceNumber>)> {
385 let contents = parser.advance(ValueToken::Ident)?;
386 ensure!(contents == ident_name);
387 parser.advance(ValueToken::LParen)?;
388 let i_str = parser.advance(ValueToken::Number)?;
389 let (i, _) = parse_u256(i_str)?;
390 let fake_id = if let Some(ValueToken::Comma) = parser.peek_tok() {
391 parser.advance(ValueToken::Comma)?;
392 let j_str = parser.advance(ValueToken::Number)?;
393 let (j, _) = parse_u64(j_str)?;
394 if i > U256::from(u64::MAX) {
395 bail!("Object ID too large")
396 }
397 FakeID::Enumerated(i.unchecked_as_u64(), j)
398 } else {
399 let mut u256_bytes = i.to_le_bytes().to_vec();
400 u256_bytes.reverse();
401 let address: IotaAddress = IotaAddress::from_bytes(&u256_bytes).unwrap();
402 FakeID::Known(address.into())
403 };
404 parser.advance(ValueToken::RParen)?;
405 let version = if let Some(ValueToken::AtSign) = parser.peek_tok() {
406 parser.advance(ValueToken::AtSign)?;
407 let v_str = parser.advance(ValueToken::Number)?;
408 let (v, _) = parse_u64(v_str)?;
409 Some(SequenceNumber::from_u64(v))
410 } else {
411 None
412 };
413 Ok((fake_id, version))
414 }
415}
416
417impl IotaValue {
418 fn assert_move_value(self) -> MoveValue {
419 match self {
420 IotaValue::MoveValue(v) => v,
421 IotaValue::Object(_, _) => panic!("unexpected nested IOTA object in args"),
422 IotaValue::ObjVec(_) => panic!("unexpected nested IOTA object vector in args"),
423 IotaValue::Digest(_) => panic!("unexpected nested IOTA package digest in args"),
424 IotaValue::Receiving(_, _) => panic!("unexpected nested IOTA receiving object in args"),
425 IotaValue::ImmShared(_, _) => panic!("unexpected nested IOTA shared object in args"),
426 }
427 }
428
429 fn assert_object(self) -> (FakeID, Option<SequenceNumber>) {
430 match self {
431 IotaValue::MoveValue(_) => panic!("unexpected nested non-object value in args"),
432 IotaValue::Object(id, version) => (id, version),
433 IotaValue::ObjVec(_) => panic!("unexpected nested IOTA object vector in args"),
434 IotaValue::Digest(_) => panic!("unexpected nested IOTA package digest in args"),
435 IotaValue::Receiving(_, _) => panic!("unexpected nested IOTA receiving object in args"),
436 IotaValue::ImmShared(_, _) => panic!("unexpected nested IOTA shared object in args"),
437 }
438 }
439
440 fn resolve_object(
441 fake_id: FakeID,
442 version: Option<SequenceNumber>,
443 test_adapter: &IotaTestAdapter,
444 ) -> anyhow::Result<Object> {
445 let id = match test_adapter.fake_to_real_object_id(fake_id) {
446 Some(id) => id,
447 None => bail!("INVALID TEST. Unknown object, object({})", fake_id),
448 };
449 let obj_res = if let Some(v) = version {
450 iota_types::storage::ObjectStore::get_object_by_key(&*test_adapter.executor, &id, v)
451 } else {
452 iota_types::storage::ObjectStore::get_object(&*test_adapter.executor, &id)
453 };
454 let obj = match obj_res {
455 Ok(Some(obj)) => obj,
456 Err(_) | Ok(None) => bail!("INVALID TEST. Could not load object argument {}", id),
457 };
458 Ok(obj)
459 }
460
461 fn receiving_arg(
462 fake_id: FakeID,
463 version: Option<SequenceNumber>,
464 test_adapter: &IotaTestAdapter,
465 ) -> anyhow::Result<ObjectArg> {
466 let obj = Self::resolve_object(fake_id, version, test_adapter)?;
467 Ok(ObjectArg::Receiving(obj.compute_object_reference()))
468 }
469
470 fn read_shared_arg(
471 fake_id: FakeID,
472 version: Option<SequenceNumber>,
473 test_adapter: &IotaTestAdapter,
474 ) -> anyhow::Result<ObjectArg> {
475 let obj = Self::resolve_object(fake_id, version, test_adapter)?;
476 let id = obj.id();
477 if let Owner::Shared {
478 initial_shared_version,
479 } = obj.owner
480 {
481 Ok(ObjectArg::SharedObject {
482 id,
483 initial_shared_version,
484 mutable: false,
485 })
486 } else {
487 bail!("{fake_id} is not a shared object.")
488 }
489 }
490
491 fn object_arg(
492 fake_id: FakeID,
493 version: Option<SequenceNumber>,
494 test_adapter: &IotaTestAdapter,
495 ) -> anyhow::Result<ObjectArg> {
496 let obj = Self::resolve_object(fake_id, version, test_adapter)?;
497 let id = obj.id();
498 match obj.owner {
499 Owner::Shared {
500 initial_shared_version,
501 } => Ok(ObjectArg::SharedObject {
502 id,
503 initial_shared_version,
504 mutable: true,
505 }),
506 Owner::AddressOwner(_) | Owner::ObjectOwner(_) | Owner::Immutable => {
507 let obj_ref = obj.compute_object_reference();
508 Ok(ObjectArg::ImmOrOwnedObject(obj_ref))
509 }
510 }
511 }
512
513 pub(crate) fn into_call_arg(self, test_adapter: &IotaTestAdapter) -> anyhow::Result<CallArg> {
514 Ok(match self {
515 IotaValue::Object(fake_id, version) => {
516 CallArg::Object(Self::object_arg(fake_id, version, test_adapter)?)
517 }
518 IotaValue::MoveValue(v) => CallArg::Pure(v.simple_serialize().unwrap()),
519 IotaValue::Receiving(fake_id, version) => {
520 CallArg::Object(Self::receiving_arg(fake_id, version, test_adapter)?)
521 }
522 IotaValue::ImmShared(fake_id, version) => {
523 CallArg::Object(Self::read_shared_arg(fake_id, version, test_adapter)?)
524 }
525 IotaValue::ObjVec(_) => bail!("obj vec is not supported as an input"),
526 IotaValue::Digest(pkg) => {
527 let pkg = Symbol::from(pkg);
528 let Some(staged) = test_adapter.staged_modules.get(&pkg) else {
529 bail!("Unbound staged package '{pkg}'")
530 };
531 CallArg::Pure(bcs::to_bytes(&staged.digest).unwrap())
532 }
533 })
534 }
535
536 pub(crate) fn into_argument(
537 self,
538 builder: &mut ProgrammableTransactionBuilder,
539 test_adapter: &IotaTestAdapter,
540 ) -> anyhow::Result<Argument> {
541 match self {
542 IotaValue::ObjVec(vec) => builder.make_obj_vec(
543 vec.iter()
544 .map(|(fake_id, version)| Self::object_arg(*fake_id, *version, test_adapter))
545 .collect::<Result<Vec<ObjectArg>, _>>()?,
546 ),
547 value => {
548 let call_arg = value.into_call_arg(test_adapter)?;
549 builder.input(call_arg)
550 }
551 }
552 }
553}
554
555impl ParsableValue for IotaExtraValueArgs {
556 type ConcreteValue = IotaValue;
557
558 fn parse_value<'a, I: Iterator<Item = (ValueToken, &'a str)>>(
559 parser: &mut MoveCLParser<'a, ValueToken, I>,
560 ) -> Option<anyhow::Result<Self>> {
561 match parser.peek()? {
562 (ValueToken::Ident, "object") => Some(Self::parse_object_value(parser)),
563 (ValueToken::Ident, "digest") => Some(Self::parse_digest_value(parser)),
564 (ValueToken::Ident, "receiving") => Some(Self::parse_receiving_value(parser)),
565 (ValueToken::Ident, "immshared") => Some(Self::parse_read_shared_value(parser)),
566 _ => None,
567 }
568 }
569
570 fn move_value_into_concrete(v: MoveValue) -> anyhow::Result<Self::ConcreteValue> {
571 Ok(IotaValue::MoveValue(v))
572 }
573
574 fn concrete_vector(elems: Vec<Self::ConcreteValue>) -> anyhow::Result<Self::ConcreteValue> {
575 if !elems.is_empty() && matches!(elems[0], IotaValue::Object(_, _)) {
576 Ok(IotaValue::ObjVec(
577 elems.into_iter().map(IotaValue::assert_object).collect(),
578 ))
579 } else {
580 Ok(IotaValue::MoveValue(MoveValue::Vector(
581 elems
582 .into_iter()
583 .map(IotaValue::assert_move_value)
584 .collect(),
585 )))
586 }
587 }
588
589 fn concrete_struct(values: Vec<Self::ConcreteValue>) -> anyhow::Result<Self::ConcreteValue> {
590 Ok(IotaValue::MoveValue(MoveValue::Struct(MoveStruct(
591 values.into_iter().map(|v| v.assert_move_value()).collect(),
592 ))))
593 }
594
595 fn into_concrete_value(
596 self,
597 _mapping: &impl Fn(&str) -> Option<move_core_types::account_address::AccountAddress>,
598 ) -> anyhow::Result<Self::ConcreteValue> {
599 match self {
600 IotaExtraValueArgs::Object(id, version) => Ok(IotaValue::Object(id, version)),
601 IotaExtraValueArgs::Digest(pkg) => Ok(IotaValue::Digest(pkg)),
602 IotaExtraValueArgs::Receiving(id, version) => Ok(IotaValue::Receiving(id, version)),
603 IotaExtraValueArgs::ImmShared(id, version) => Ok(IotaValue::ImmShared(id, version)),
604 }
605 }
606}
607
608fn parse_fake_id(s: &str) -> anyhow::Result<FakeID> {
609 Ok(if let Some((s1, s2)) = s.split_once(',') {
610 let (i, _) = parse_u64(s1)?;
611 let (j, _) = parse_u64(s2)?;
612 FakeID::Enumerated(i, j)
613 } else {
614 let (i, _) = parse_u256(s)?;
615 let mut u256_bytes = i.to_le_bytes().to_vec();
616 u256_bytes.reverse();
617 let address: IotaAddress = IotaAddress::from_bytes(&u256_bytes).unwrap();
618 FakeID::Known(address.into())
619 })
620}
621
622fn parse_policy(x: &str) -> anyhow::Result<u8> {
623 Ok(match x {
624 "compatible" => UpgradePolicy::COMPATIBLE,
625 "additive" => UpgradePolicy::ADDITIVE,
626 "dep_only" => UpgradePolicy::DEP_ONLY,
627 _ => bail!(
628 "Invalid upgrade policy {x}. Policy must be one of 'compatible', 'additive', or 'dep_only'"
629 ),
630 })
631}