From c3ff5aa9574f9308668b969db44948a27ae85e10 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 21 Aug 2024 08:29:22 -0500 Subject: [PATCH 001/107] type_tag: include source string in parse error --- crates/iota-rust-sdk/src/types/type_tag/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/iota-rust-sdk/src/types/type_tag/mod.rs b/crates/iota-rust-sdk/src/types/type_tag/mod.rs index 83d0ac351..97d244da9 100644 --- a/crates/iota-rust-sdk/src/types/type_tag/mod.rs +++ b/crates/iota-rust-sdk/src/types/type_tag/mod.rs @@ -47,13 +47,14 @@ impl std::str::FromStr for TypeTag { type Err = TypeParseError; fn from_str(s: &str) -> Result { - parse::parse_type_tag(s).map_err(|_| TypeParseError) + parse::parse_type_tag(s).map_err(|_| TypeParseError { source: s.into() }) } } -// TODO flesh out this error type #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeParseError; +pub struct TypeParseError { + source: String, +} impl std::fmt::Display for TypeParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -80,7 +81,9 @@ impl Identifier { pub fn new(identifier: impl AsRef) -> Result { parse::parse_identifier(identifier.as_ref()) .map(|ident| Self(ident.into())) - .map_err(|_| TypeParseError) + .map_err(|_| TypeParseError { + source: identifier.as_ref().into(), + }) } pub fn into_inner(self) -> Box { @@ -104,7 +107,7 @@ impl std::str::FromStr for Identifier { fn from_str(s: &str) -> Result { parse::parse_identifier(s) .map(|ident| Self(ident.into())) - .map_err(|_| TypeParseError) + .map_err(|_| TypeParseError { source: s.into() }) } } @@ -192,6 +195,6 @@ impl std::str::FromStr for StructTag { type Err = TypeParseError; fn from_str(s: &str) -> Result { - parse::parse_struct_tag(s).map_err(|_| TypeParseError) + parse::parse_struct_tag(s).map_err(|_| TypeParseError { source: s.into() }) } } From 1368bcacebe117d35357cc00e867be3ca2be9ae9 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:31:03 -0700 Subject: [PATCH 002/107] sui-graphql-client: introduce initial implementation a sui GraphQL client (#4) --- crates/sui-graphql-client/Cargo.toml | 23 + crates/sui-graphql-client/README.md | 164 + crates/sui-graphql-client/build.rs | 6 + .../examples/custom_query.rs | 74 + .../queries/coin_metadata.graphql | 11 + .../queries/custom_query.graphql | 9 + .../queries/epoch_total_checkpoints.graphql | 5 + .../sui-graphql-client/queries/object.graphql | 6 + .../queries/objects.graphql | 20 + .../schema/graphql_rpc.graphql | 4410 +++++++++++++++++ crates/sui-graphql-client/src/lib.rs | 569 +++ .../src/query_types/chain.rs | 13 + .../src/query_types/checkpoint.rs | 133 + .../src/query_types/coin.rs | 40 + .../src/query_types/epoch.rs | 73 + .../src/query_types/events.rs | 54 + .../sui-graphql-client/src/query_types/mod.rs | 106 + .../src/query_types/object.rs | 75 + .../src/query_types/protocol_config.rs | 56 + .../src/query_types/service_config.rs | 83 + .../src/query_types/transaction.rs | 89 + 21 files changed, 6019 insertions(+) create mode 100644 crates/sui-graphql-client/Cargo.toml create mode 100644 crates/sui-graphql-client/README.md create mode 100644 crates/sui-graphql-client/build.rs create mode 100644 crates/sui-graphql-client/examples/custom_query.rs create mode 100644 crates/sui-graphql-client/queries/coin_metadata.graphql create mode 100644 crates/sui-graphql-client/queries/custom_query.graphql create mode 100644 crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql create mode 100644 crates/sui-graphql-client/queries/object.graphql create mode 100644 crates/sui-graphql-client/queries/objects.graphql create mode 100644 crates/sui-graphql-client/schema/graphql_rpc.graphql create mode 100644 crates/sui-graphql-client/src/lib.rs create mode 100644 crates/sui-graphql-client/src/query_types/chain.rs create mode 100644 crates/sui-graphql-client/src/query_types/checkpoint.rs create mode 100644 crates/sui-graphql-client/src/query_types/coin.rs create mode 100644 crates/sui-graphql-client/src/query_types/epoch.rs create mode 100644 crates/sui-graphql-client/src/query_types/events.rs create mode 100644 crates/sui-graphql-client/src/query_types/mod.rs create mode 100644 crates/sui-graphql-client/src/query_types/object.rs create mode 100644 crates/sui-graphql-client/src/query_types/protocol_config.rs create mode 100644 crates/sui-graphql-client/src/query_types/service_config.rs create mode 100644 crates/sui-graphql-client/src/query_types/transaction.rs diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml new file mode 100644 index 000000000..9d6d5b081 --- /dev/null +++ b/crates/sui-graphql-client/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "sui-graphql-client" +version = "0.1.0" +authors = ["Stefan Stanciulescu ", "Brandon Williams "] +license = "Apache-2.0" +edition = "2021" +publish = false +readme = "README.md" +description = "Sui GraphQL RPC Client for the Sui Blockchain" + +[dependencies] +anyhow = "1.0.86" +async-trait = "0.1.81" +base64ct = { version = "1.6.0", features = ["alloc"] } +bcs = "0.1.6" +chrono = { version = "0.4.38" } +cynic = { version = "3.7.3" } +reqwest = { version = "0.12", features = ["json"] } +sui-types = { package= "sui-sdk", path = "../sui-sdk", features = ["serde"] } +tokio = { version = "1.39.2", features = ["full"] } + +[build-dependencies] +cynic-codegen = { version = "3.7.3" } diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md new file mode 100644 index 000000000..316b7ae7a --- /dev/null +++ b/crates/sui-graphql-client/README.md @@ -0,0 +1,164 @@ +The Sui GraphQL client is a client for interacting with the Sui blockchain via GraphQL. +It provides a set of APIs for querying the blockchain for information such as chain identifier, +reference gas price, protocol configuration, service configuration, checkpoint, epoch, +executing transactions and more. + +# Design Principles + +1. **Type Safety**: The client uses the `cynic` library to generate types from the schema. This ensures that the queries are type-safe. +1. **Convenience**: The client provides a set of APIs for common queries such as chain identifier, reference gas price, protocol configuration, service configuration, checkpoint, epoch, executing transactions and more. +1. **Custom Queries**: The client provides a way to run custom queries using the `cynic` library. + +# Usage + +## Connecting to a GraphQL server +Instantiate a client with [`Client::new(server: &str)`] or use one of the predefined functions for different networks [`Client`]. + +```rust +use sui_graphql_client::Client; +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + + // Connect to the mainnet GraphQL server + let client = Client::new_mainnet(); + let chain_id = client.chain_id().await?; + println!("{:?}", chain_id); + + Ok(()) +} +``` + +## Custom Queries +There are several options for running custom queries. +1) Use a GraphQL client library of your choosing. +2) Use the [cynic's web generator](https://generator.cynic-rs.dev/) that accepts as input the schema and generates the query types. +3) Use the [cynic's CLI](https://github.com/obmarg/cynic/tree/main/cynic-cli) and use the `cynic querygen` command to generate the query types. + +Below is an example that uses the `cynic querygen` CLI to generate the query types from the schema and the following query: +```bash +cynic querygen --schema rpc.graphql --query custom_query.graphql +``` +where `custom_query.graphql` contains the following query: + +```graphql +query CustomQuery($id: UInt53) { + epoch(id: $id) { + referenceGasPrice + totalGasFees + totalCheckpoints + totalTransactions + } +} +``` + +The generated query types are defined below. Note that the `id` variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the `id` variable is not provided, the query will return the data for the last known epoch. + + +```rust,ignore +#[derive(cynic::QueryVariables, Debug)] +pub struct CustomQueryVariables { + pub id: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(graphql_type = "Query", variables = "CustomQueryVariables")] +pub struct CustomQuery { + #[arguments(id: $id)] + pub epoch: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +pub struct Epoch { + pub epoch_id: Uint53, + pub reference_gas_price: Option, + pub total_gas_fees: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, +} + +#[derive(cynic::Scalar, Debug, Clone)] +pub struct BigInt(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "UInt53")] +pub struct Uint53(pub u64); +``` + +The complete example is shown below: +```rust +use anyhow::Result; +use cynic::QueryBuilder; + +use sui_graphql_client::{ + query_types::{schema, BigInt, Uint53}, + Client, +}; +use sui_types::types::Address; + +// The data returned by the custom query. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Epoch")] +pub struct EpochData { + pub epoch_id: Uint53, + pub reference_gas_price: Option, + pub total_gas_fees: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, +} + +// The variables to pass to the custom query. +// If an epoch id is passed, then the query will return the data for that epoch. +// Otherwise, the query will return the data for the last known epoch. +#[derive(cynic::QueryVariables, Debug)] +pub struct CustomVariables { + pub id: Option, +} + +// The custom query. Note that the variables need to be explicitly declared. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CustomVariables")] +pub struct CustomQuery { + #[arguments(id: $id)] + pub epoch: Option, +} + +// Custom query with no variables. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query")] +pub struct ChainIdQuery { + chain_identifier: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + let mut client = Client::new_devnet(); + + // Query the data for the last known epoch. Note that id variable is None, so last epoch data + // will be returned. + let operation = CustomQuery::build(CustomVariables { id: None }); + let response = client + .run_query::(&operation) + .await; + println!("{:?}", response); + + // Query the data for epoch 1. + let epoch_id = Uint53(1); + let operation = CustomQuery::build(CustomVariables { id: Some(epoch_id) }); + let response = client + .run_query::(&operation) + .await; + println!("{:?}", response); + + // When the query has no variables, just pass () as the type argument + let operation = ChainIdQuery::build(()); + let response = client.run_query::(&operation).await?; + if let Some(chain_id) = response.data { + println!("Chain ID: {}", chain_id.chain_identifier); + } + + Ok(()) +} +``` + diff --git a/crates/sui-graphql-client/build.rs b/crates/sui-graphql-client/build.rs new file mode 100644 index 000000000..293b6dd38 --- /dev/null +++ b/crates/sui-graphql-client/build.rs @@ -0,0 +1,6 @@ +/// Register Sui RPC schema for creating structs for queries +fn main() { + cynic_codegen::register_schema("rpc") + .from_sdl_file("schema/graphql_rpc.graphql") + .expect("Failed to find GraphQL Schema"); +} diff --git a/crates/sui-graphql-client/examples/custom_query.rs b/crates/sui-graphql-client/examples/custom_query.rs new file mode 100644 index 000000000..06b7644a7 --- /dev/null +++ b/crates/sui-graphql-client/examples/custom_query.rs @@ -0,0 +1,74 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use cynic::QueryBuilder; + +use sui_graphql_client::{ + query_types::{schema, BigInt, Uint53}, + Client, +}; + +// The data returned by the custom query. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Epoch")] +pub struct EpochData { + pub epoch_id: Uint53, + pub reference_gas_price: Option, + pub total_gas_fees: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, +} + +// The variables to pass to the custom query. +// If an epoch id is passed, then the query will return the data for that epoch. +// Otherwise, the query will return the data for the last known epoch. +#[derive(cynic::QueryVariables, Debug)] +pub struct CustomVariables { + pub id: Option, +} + +// The custom query. Note that the variables need to be explicitly declared. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CustomVariables")] +pub struct CustomQuery { + #[arguments(id: $id)] + pub epoch: Option, +} + +// Custom query with no variables. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query")] +pub struct ChainIdQuery { + chain_identifier: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + let client = Client::new_devnet(); + + // Query the data for the last known epoch. Note that id variable is None, so last epoch data + // will be returned. + let operation = CustomQuery::build(CustomVariables { id: None }); + let response = client + .run_query::(&operation) + .await; + println!("{:?}", response); + + // Query the data for epoch 1. + let epoch_id = Uint53(1); + let operation = CustomQuery::build(CustomVariables { id: Some(epoch_id) }); + let response = client + .run_query::(&operation) + .await; + println!("{:?}", response); + + // When the query has no variables, just pass () as the type argument + let operation = ChainIdQuery::build(()); + let response = client.run_query::(&operation).await?; + if let Some(chain_id) = response.data { + println!("Chain ID: {}", chain_id.chain_identifier); + } + + Ok(()) +} diff --git a/crates/sui-graphql-client/queries/coin_metadata.graphql b/crates/sui-graphql-client/queries/coin_metadata.graphql new file mode 100644 index 000000000..40bfc1f69 --- /dev/null +++ b/crates/sui-graphql-client/queries/coin_metadata.graphql @@ -0,0 +1,11 @@ +query CoinMetadataQuery($coinType: String!) { + coinMetadata(coinType: $coinType) { + decimals + description + iconUrl + name + symbol + supply + version + } +} diff --git a/crates/sui-graphql-client/queries/custom_query.graphql b/crates/sui-graphql-client/queries/custom_query.graphql new file mode 100644 index 000000000..e6016c046 --- /dev/null +++ b/crates/sui-graphql-client/queries/custom_query.graphql @@ -0,0 +1,9 @@ +query CustomQuery($id: UInt53) { + epoch(id: $id) { + epochId + referenceGasPrice + totalGasFees + totalCheckpoints + totalTransactions + } +} diff --git a/crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql b/crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql new file mode 100644 index 000000000..a60c1cb90 --- /dev/null +++ b/crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql @@ -0,0 +1,5 @@ +query EpochTotalCheckpoints($id: UInt53){ + epoch(id: $id) { + totalCheckpoints + } +} diff --git a/crates/sui-graphql-client/queries/object.graphql b/crates/sui-graphql-client/queries/object.graphql new file mode 100644 index 000000000..8daef0638 --- /dev/null +++ b/crates/sui-graphql-client/queries/object.graphql @@ -0,0 +1,6 @@ +query ObjectQuery($address: SuiAddress!, $version: UInt53) { + object(address: $address, version: $version) { + bcs + } +} + diff --git a/crates/sui-graphql-client/queries/objects.graphql b/crates/sui-graphql-client/queries/objects.graphql new file mode 100644 index 000000000..c287294c8 --- /dev/null +++ b/crates/sui-graphql-client/queries/objects.graphql @@ -0,0 +1,20 @@ +query ObjectsQuery($after: String, $before: String, $filter: ObjectFilter, $first: Int, $last: Int) { + objects( + after: $after, + before: $before, + filter: $filter, + first: $first, + last: $last + ) { + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + nodes { + bcs + } + } +} + diff --git a/crates/sui-graphql-client/schema/graphql_rpc.graphql b/crates/sui-graphql-client/schema/graphql_rpc.graphql new file mode 100644 index 000000000..cbde89c2c --- /dev/null +++ b/crates/sui-graphql-client/schema/graphql_rpc.graphql @@ -0,0 +1,4410 @@ +type ActiveJwk { + """ + The string (Issuing Authority) that identifies the OIDC provider. + """ + iss: String! + """ + The string (Key ID) that identifies the JWK among a set of JWKs, (RFC 7517, Section 4.5). + """ + kid: String! + """ + The JWK key type parameter, (RFC 7517, Section 4.1). + """ + kty: String! + """ + The JWK RSA public exponent, (RFC 7517, Section 9.3). + """ + e: String! + """ + The JWK RSA modulus, (RFC 7517, Section 9.3). + """ + n: String! + """ + The JWK algorithm parameter, (RFC 7517, Section 4.4). + """ + alg: String! + """ + The most recent epoch in which the JWK was validated. + """ + epoch: Epoch +} + +type ActiveJwkConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [ActiveJwkEdge!]! + """ + A list of nodes. + """ + nodes: [ActiveJwk!]! +} + +""" +An edge in a connection. +""" +type ActiveJwkEdge { + """ + The item at the end of the edge + """ + node: ActiveJwk! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The 32-byte address that is an account address (corresponding to a public key). +""" +type Address implements IOwner { + address: SuiAddress! + """ + Objects owned by this address, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this address. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this address. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this address. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this address. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this address. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this address. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + """ + Similar behavior to the `transactionBlocks` in Query but supporting the additional + `AddressTransactionBlockRelationship` filter, which defaults to `SIGN`. + """ + transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter): TransactionBlockConnection! +} + +type AddressConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [AddressEdge!]! + """ + A list of nodes. + """ + nodes: [Address!]! +} + +""" +An edge in a connection. +""" +type AddressEdge { + """ + The item at the end of the edge + """ + node: Address! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +An address-owned object is owned by a specific 32-byte address that is +either an account address (derived from a particular signature scheme) or +an object ID. An address-owned object is accessible only to its owner and no others. +""" +type AddressOwner { + owner: Owner +} + +""" +The possible relationship types for a transaction block: sign, sent, received, or paid. +""" +enum AddressTransactionBlockRelationship { + """ + Transactions this address has signed either as a sender or as a sponsor. + """ + SIGN + """ + Transactions that sent objects to this address. + """ + RECV +} + +""" +System transaction for creating the on-chain state used by zkLogin. +""" +type AuthenticatorStateCreateTransaction { + """ + A workaround to define an empty variant of a GraphQL union. + """ + _: Boolean +} + +type AuthenticatorStateExpireTransaction { + """ + Expire JWKs that have a lower epoch than this. + """ + minEpoch: Epoch + """ + The initial version that the AuthenticatorStateUpdate was shared at. + """ + authenticatorObjInitialSharedVersion: UInt53! +} + +""" +System transaction for updating the on-chain state used by zkLogin. +""" +type AuthenticatorStateUpdateTransaction { + """ + Epoch of the authenticator state update transaction. + """ + epoch: Epoch + """ + Consensus round of the authenticator state update. + """ + round: UInt53! + """ + Newly active JWKs (JSON Web Keys). + """ + newActiveJwks(first: Int, after: String, last: Int, before: String): ActiveJwkConnection! + """ + The initial version of the authenticator object that it was shared at. + """ + authenticatorObjInitialSharedVersion: UInt53! +} + +""" +Range of checkpoints that the RPC is guaranteed to produce a consistent response for. +""" +type AvailableRange { + first: Checkpoint + last: Checkpoint +} + +""" +The total balance for a particular coin type. +""" +type Balance { + """ + Coin type for the balance, such as 0x2::sui::SUI + """ + coinType: MoveType! + """ + How many coins of this type constitute the balance + """ + coinObjectCount: UInt53 + """ + Total balance across all coin objects of the coin type + """ + totalBalance: BigInt +} + +""" +Effects to the balance (sum of coin values per coin type) owned by an address or object. +""" +type BalanceChange { + """ + The address or object whose balance has changed. + """ + owner: Owner + """ + The inner type of the coin whose balance has changed (e.g. `0x2::sui::SUI`). + """ + coinType: MoveType + """ + The signed balance change. + """ + amount: BigInt +} + +type BalanceChangeConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [BalanceChangeEdge!]! + """ + A list of nodes. + """ + nodes: [BalanceChange!]! +} + +""" +An edge in a connection. +""" +type BalanceChangeEdge { + """ + The item at the end of the edge + """ + node: BalanceChange! + """ + A cursor for use in pagination + """ + cursor: String! +} + +type BalanceConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [BalanceEdge!]! + """ + A list of nodes. + """ + nodes: [Balance!]! +} + +""" +An edge in a connection. +""" +type BalanceEdge { + """ + The item at the end of the edge + """ + node: Balance! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +String containing Base64-encoded binary data. +""" +scalar Base64 + +""" +String representation of an arbitrary width, possibly signed integer. +""" +scalar BigInt + + +type BridgeCommitteeInitTransaction { + bridgeObjInitialSharedVersion: UInt53! +} + +type BridgeStateCreateTransaction { + chainId: String! +} + +""" +A system transaction that updates epoch information on-chain (increments the current epoch). +Executed by the system once per epoch, without using gas. Epoch change transactions cannot be +submitted by users, because validators will refuse to sign them. + +This transaction kind is deprecated in favour of `EndOfEpochTransaction`. +""" +type ChangeEpochTransaction { + """ + The next (to become) epoch. + """ + epoch: Epoch + """ + The protocol version in effect in the new epoch. + """ + protocolVersion: UInt53! + """ + The total amount of gas charged for storage during the previous epoch (in MIST). + """ + storageCharge: BigInt! + """ + The total amount of gas charged for computation during the previous epoch (in MIST). + """ + computationCharge: BigInt! + """ + The SUI returned to transaction senders for cleaning up objects (in MIST). + """ + storageRebate: BigInt! + """ + The total gas retained from storage fees, that will not be returned by storage rebates when + the relevant objects are cleaned up (in MIST). + """ + nonRefundableStorageFee: BigInt! + """ + Time at which the next epoch will start. + """ + startTimestamp: DateTime! + """ + System packages (specifically framework and move stdlib) that are written before the new + epoch starts, to upgrade them on-chain. Validators write these packages out when running the + transaction. + """ + systemPackages(first: Int, after: String, last: Int, before: String): MovePackageConnection! +} + +""" +Checkpoints contain finalized transactions and are used for node synchronization +and global transaction ordering. +""" +type Checkpoint { + """ + A 32-byte hash that uniquely identifies the checkpoint contents, encoded in Base58. This + hash can be used to verify checkpoint contents by checking signatures against the committee, + Hashing contents to match digest, and checking that the previous checkpoint digest matches. + """ + digest: String! + """ + This checkpoint's position in the total order of finalized checkpoints, agreed upon by + consensus. + """ + sequenceNumber: UInt53! + """ + The timestamp at which the checkpoint is agreed to have happened according to consensus. + Transactions that access time in this checkpoint will observe this timestamp. + """ + timestamp: DateTime! + """ + This is an aggregation of signatures from a quorum of validators for the checkpoint + proposal. + """ + validatorSignatures: Base64! + """ + The digest of the checkpoint at the previous sequence number. + """ + previousCheckpointDigest: String + """ + The total number of transaction blocks in the network by the end of this checkpoint. + """ + networkTotalTransactions: UInt53 + """ + The computation cost, storage cost, storage rebate, and non-refundable storage fee + accumulated during this epoch, up to and including this checkpoint. These values increase + monotonically across checkpoints in the same epoch, and reset on epoch boundaries. + """ + rollingGasSummary: GasCostSummary + """ + The epoch this checkpoint is part of. + """ + epoch: Epoch + """ + Transactions in this checkpoint. + """ + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! +} + +type CheckpointConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [CheckpointEdge!]! + """ + A list of nodes. + """ + nodes: [Checkpoint!]! +} + +""" +An edge in a connection. +""" +type CheckpointEdge { + """ + The item at the end of the edge + """ + node: Checkpoint! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Filter either by the digest, or the sequence number, or neither, to get the latest checkpoint. +""" +input CheckpointId { + digest: String + sequenceNumber: UInt53 +} + +""" +Some 0x2::coin::Coin Move object. +""" +type Coin implements IMoveObject & IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also + provides the flat representation of the type signature, and the BCS of the corresponding + data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects + transaction command or `sui::transfer::public_transfer`, both of which require the object to + have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + Balance of this coin object. + """ + coinBalance: BigInt +} + +type CoinConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [CoinEdge!]! + """ + A list of nodes. + """ + nodes: [Coin!]! +} + +type CoinDenyListStateCreateTransaction { + """ + A workaround to define an empty variant of a GraphQL union. + """ + _: Boolean +} + +""" +An edge in a connection. +""" +type CoinEdge { + """ + The item at the end of the edge + """ + node: Coin! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The metadata for a coin type. +""" +type CoinMetadata implements IMoveObject & IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also + provides the flat representation of the type signature, and the BCS of the corresponding + data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects + transaction command or `sui::transfer::public_transfer`, both of which require the object to + have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + The number of decimal places used to represent the token. + """ + decimals: Int + """ + Full, official name of the token. + """ + name: String + """ + The token's identifying abbreviation. + """ + symbol: String + """ + Optional description of the token, provided by the creator of the token. + """ + description: String + iconUrl: String + """ + The overall quantity of tokens that will be issued. + """ + supply: BigInt +} + +""" +System transaction that runs at the beginning of a checkpoint, and is responsible for setting +the current value of the clock, based on the timestamp from consensus. +""" +type ConsensusCommitPrologueTransaction { + """ + Epoch of the commit prologue transaction. + """ + epoch: Epoch + """ + Consensus round of the commit. + """ + round: UInt53! + """ + Unix timestamp from consensus. + """ + commitTimestamp: DateTime! + """ + Digest of consensus output, encoded as a Base58 string (only available from V2 of the + transaction). + """ + consensusCommitDigest: String +} + +""" +ISO-8601 Date and Time: RFC3339 in UTC with format: YYYY-MM-DDTHH:MM:SS.mmmZ. Note that the milliseconds part is optional, and it may be omitted if its value is 0. +""" +scalar DateTime + +type DependencyConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [DependencyEdge!]! + """ + A list of nodes. + """ + nodes: [TransactionBlock!]! +} + +""" +An edge in a connection. +""" +type DependencyEdge { + """ + The item at the end of the edge + """ + node: TransactionBlock + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The set of named templates defined on-chain for the type of this object, +to be handled off-chain. The server substitutes data from the object +into these templates to generate a display string per template. +""" +type DisplayEntry { + """ + The identifier for a particular template string of the Display object. + """ + key: String! + """ + The template string for the key with placeholder values substituted. + """ + value: String + """ + An error string describing why the template could not be rendered. + """ + error: String +} + +enum DomainFormat { + AT + DOT +} + +type DryRunEffect { + """ + Changes made to arguments that were mutably borrowed by each command in this transaction. + """ + mutatedReferences: [DryRunMutation!] + """ + Return results of each command in this transaction. + """ + returnValues: [DryRunReturn!] +} + +type DryRunMutation { + input: TransactionArgument! + type: MoveType! + bcs: Base64! +} + +type DryRunResult { + """ + The error that occurred during dry run execution, if any. + """ + error: String + """ + The intermediate results for each command of the dry run execution, including + contents of mutated references and return values. + """ + results: [DryRunEffect!] + """ + The transaction block representing the dry run execution. + """ + transaction: TransactionBlock +} + +type DryRunReturn { + type: MoveType! + bcs: Base64! +} + +""" +Dynamic fields are heterogeneous fields that can be added or removed at runtime, +and can have arbitrary user-assigned names. There are two sub-types of dynamic +fields: + +1) Dynamic Fields can store any value that has the `store` ability, however an object +stored in this kind of field will be considered wrapped and will not be accessible +directly via its ID by external tools (explorers, wallets, etc) accessing storage. +2) Dynamic Object Fields values must be Sui objects (have the `key` and `store` +abilities, and id: UID as the first field), but will still be directly accessible off-chain +via their object ID after being attached. +""" +type DynamicField { + """ + The string type, data, and serialized value of the DynamicField's 'name' field. + This field is used to uniquely identify a child of the parent object. + """ + name: MoveValue + """ + The returned dynamic field is an object if its return type is `MoveObject`, + in which case it is also accessible off-chain via its address. Its contents + will be from the latest version that is at most equal to its parent object's + version + """ + value: DynamicFieldValue +} + +type DynamicFieldConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [DynamicFieldEdge!]! + """ + A list of nodes. + """ + nodes: [DynamicField!]! +} + +""" +An edge in a connection. +""" +type DynamicFieldEdge { + """ + The item at the end of the edge + """ + node: DynamicField! + """ + A cursor for use in pagination + """ + cursor: String! +} + +input DynamicFieldName { + """ + The string type of the DynamicField's 'name' field. + A string representation of a Move primitive like 'u64', or a struct type like '0x2::kiosk::Listing' + """ + type: String! + """ + The Base64 encoded bcs serialization of the DynamicField's 'name' field. + """ + bcs: Base64! +} + +union DynamicFieldValue = MoveObject | MoveValue + +""" +System transaction that supersedes `ChangeEpochTransaction` as the new way to run transactions +at the end of an epoch. Behaves similarly to `ChangeEpochTransaction` but can accommodate other +optional transactions to run at the end of the epoch. +""" +type EndOfEpochTransaction { + """ + The list of system transactions that are allowed to run at the end of the epoch. + """ + transactions(first: Int, before: String, last: Int, after: String): EndOfEpochTransactionKindConnection! +} + +union EndOfEpochTransactionKind = ChangeEpochTransaction | AuthenticatorStateCreateTransaction | AuthenticatorStateExpireTransaction | RandomnessStateCreateTransaction | CoinDenyListStateCreateTransaction | BridgeStateCreateTransaction | BridgeCommitteeInitTransaction + +type EndOfEpochTransactionKindConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EndOfEpochTransactionKindEdge!]! + """ + A list of nodes. + """ + nodes: [EndOfEpochTransactionKind!]! +} + +""" +An edge in a connection. +""" +type EndOfEpochTransactionKindEdge { + """ + The item at the end of the edge + """ + node: EndOfEpochTransactionKind! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Operation of the Sui network is temporally partitioned into non-overlapping epochs, +and the network aims to keep epochs roughly the same duration as each other. +During a particular epoch the following data is fixed: + +- the protocol version +- the reference gas price +- the set of participating validators +""" +type Epoch { + """ + The epoch's id as a sequence number that starts at 0 and is incremented by one at every epoch change. + """ + epochId: UInt53! + """ + The minimum gas price that a quorum of validators are guaranteed to sign a transaction for. + """ + referenceGasPrice: BigInt + """ + Validator related properties, including the active validators. + """ + validatorSet: ValidatorSet + """ + The epoch's starting timestamp. + """ + startTimestamp: DateTime! + """ + The epoch's ending timestamp. + """ + endTimestamp: DateTime + """ + The total number of checkpoints in this epoch. + """ + totalCheckpoints: UInt53 + """ + The total number of transaction blocks in this epoch. + """ + totalTransactions: UInt53 + """ + The total amount of gas fees (in MIST) that were paid in this epoch. + """ + totalGasFees: BigInt + """ + The total MIST rewarded as stake. + """ + totalStakeRewards: BigInt + """ + The amount added to total gas fees to make up the total stake rewards. + """ + totalStakeSubsidies: BigInt + """ + The storage fund available in this epoch. + This fund is used to redistribute storage fees from past transactions + to future validators. + """ + fundSize: BigInt + """ + The difference between the fund inflow and outflow, representing + the net amount of storage fees accumulated in this epoch. + """ + netInflow: BigInt + """ + The storage fees paid for transactions executed during the epoch. + """ + fundInflow: BigInt + """ + The storage fee rebates paid to users who deleted the data associated with past + transactions. + """ + fundOutflow: BigInt + """ + The epoch's corresponding protocol configuration, including the feature flags and the + configuration options. + """ + protocolConfigs: ProtocolConfigs! + """ + SUI set aside to account for objects stored on-chain, at the start of the epoch. + This is also used for storage rebates. + """ + storageFund: StorageFund + """ + Information about whether this epoch was started in safe mode, which happens if the full epoch + change logic fails for some reason. + """ + safeMode: SafeMode + """ + The value of the `version` field of `0x5`, the `0x3::sui::SuiSystemState` object. This + version changes whenever the fields contained in the system state object (held in a dynamic + field attached to `0x5`) change. + """ + systemStateVersion: UInt53 + """ + Details of the system that are decided during genesis. + """ + systemParameters: SystemParameters + """ + Parameters related to the subsidy that supplements staking rewards + """ + systemStakeSubsidy: StakeSubsidy + """ + A commitment by the committee at the end of epoch on the contents of the live object set at + that time. This can be used to verify state snapshots. + """ + liveObjectSetDigest: String + """ + The epoch's corresponding checkpoints. + """ + checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! + """ + The epoch's corresponding transaction blocks. + """ + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! +} + +type Event { + """ + The Move module containing some function that when called by + a programmable transaction block (PTB) emitted this event. + For example, if a PTB invokes A::m1::foo, which internally + calls A::m2::emit_event to emit an event, + the sending module would be A::m1. + """ + sendingModule: MoveModule + """ + Address of the sender of the event + """ + sender: Address + """ + UTC timestamp in milliseconds since epoch (1/1/1970) + """ + timestamp: DateTime + """ + The value's Move type. + """ + type: MoveType! + """ + The BCS representation of this value, Base64 encoded. + """ + bcs: Base64! + """ + Structured contents of a Move value. + """ + data: MoveData! + """ + Representation of a Move value in JSON, where: + + - Addresses, IDs, and UIDs are represented in canonical form, as JSON strings. + - Bools are represented by JSON boolean literals. + - u8, u16, and u32 are represented as JSON numbers. + - u64, u128, and u256 are represented as JSON strings. + - Vectors are represented by JSON arrays. + - Structs are represented by JSON objects. + - Empty optional values are represented by `null`. + + This form is offered as a less verbose convenience in cases where the layout of the type is + known by the client. + """ + json: JSON! +} + +type EventConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EventEdge!]! + """ + A list of nodes. + """ + nodes: [Event!]! +} + +""" +An edge in a connection. +""" +type EventEdge { + """ + The item at the end of the edge + """ + node: Event! + """ + A cursor for use in pagination + """ + cursor: String! +} + +input EventFilter { + sender: SuiAddress + transactionDigest: String + """ + Events emitted by a particular module. An event is emitted by a + particular module if some function in the module is called by a + PTB and emits an event. + + Modules can be filtered by their package, or package::module. + """ + emittingModule: String + """ + This field is used to specify the type of event emitted. + + Events can be filtered by their type's package, package::module, + or their fully qualified type name. + + Generic types can be queried by either the generic type name, e.g. + `0x2::coin::Coin`, or by the full type name, such as + `0x2::coin::Coin<0x2::sui::SUI>`. + """ + eventType: String +} + +""" +The result of an execution, including errors that occurred during said execution. +""" +type ExecutionResult { + """ + The errors field captures any errors that occurred during execution + """ + errors: [String!] + """ + The effects of the executed transaction. Since the transaction was just executed + and not indexed yet, fields including `balance_changes`, `timestamp` and `checkpoint` + are not available. + """ + effects: TransactionBlockEffects! +} + +""" +The execution status of this transaction block: success or failure. +""" +enum ExecutionStatus { + """ + The transaction block was successfully executed + """ + SUCCESS + """ + The transaction block could not be executed + """ + FAILURE +} + +""" +Groups of features served by the RPC service. The GraphQL Service can be configured to enable +or disable these features. +""" +enum Feature { + """ + Statistics about how the network was running (TPS, top packages, APY, etc) + """ + ANALYTICS + """ + Coin metadata, per-address coin and balance information. + """ + COINS + """ + Querying an object's dynamic fields. + """ + DYNAMIC_FIELDS + """ + SuiNS name and reverse name look-up. + """ + NAME_SERVICE + """ + Transaction and Event subscriptions. + """ + SUBSCRIPTIONS + """ + Aspects that affect the running of the system that are managed by the + validators either directly, or through system transactions. + """ + SYSTEM_STATE +} + + +""" +Access to the gas inputs, after they have been smashed into one coin. The gas coin can only be +used by reference, except for with `TransferObjectsTransaction` that can accept it by value. +""" +type GasCoin { + """ + A workaround to define an empty variant of a GraphQL union. + """ + _: Boolean +} + +""" +Breakdown of gas costs in effects. +""" +type GasCostSummary { + """ + Gas paid for executing this transaction (in MIST). + """ + computationCost: BigInt + """ + Gas paid for the data stored on-chain by this transaction (in MIST). + """ + storageCost: BigInt + """ + Part of storage cost that can be reclaimed by cleaning up data created by this transaction + (when objects are deleted or an object is modified, which is treated as a deletion followed + by a creation) (in MIST). + """ + storageRebate: BigInt + """ + Part of storage cost that is not reclaimed when data created by this transaction is cleaned + up (in MIST). + """ + nonRefundableStorageFee: BigInt +} + +""" +Effects related to gas (costs incurred and the identity of the smashed gas object returned). +""" +type GasEffects { + gasObject: Object + gasSummary: GasCostSummary +} + +""" +Configuration for this transaction's gas price and the coins used to pay for gas. +""" +type GasInput { + """ + Address of the owner of the gas object(s) used + """ + gasSponsor: Address + """ + Objects used to pay for a transaction's execution and storage + """ + gasPayment(first: Int, after: String, last: Int, before: String): ObjectConnection! + """ + An unsigned integer specifying the number of native tokens per gas unit this transaction + will pay (in MIST). + """ + gasPrice: BigInt + """ + The maximum number of gas units that can be expended by executing this transaction + """ + gasBudget: BigInt +} + +""" +System transaction that initializes the network and writes the initial set of objects on-chain. +""" +type GenesisTransaction { + """ + Objects to be created during genesis. + """ + objects(first: Int, after: String, last: Int, before: String): ObjectConnection! +} + + +""" +Interface implemented by all GraphQL types that represent a Move datatype (either structs or +enums). This interface is used to provide a way to access fields that are shared by both +structs and enums, e.g., the module that the datatype belongs to, the name of the datatype, +type parameters etc. +""" +interface IMoveDatatype { + """ + The module that the datatype belongs to. + """ + module: MoveModule! + """ + The name of the datatype. + """ + name: String! + """ + The abilities of the datatype. + """ + abilities: [MoveAbility!] + """ + The type parameters of the datatype. + """ + typeParameters: [MoveStructTypeParameter!] +} + +""" +This interface is implemented by types that represent a Move object on-chain (A Move value whose +type has `key`). +""" +interface IMoveObject { + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also provides the flat representation of the type signature, and the BCS of the corresponding data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects transaction command or `sui::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled off-chain. The server substitutes data from the object into these templates to generate a display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Ownertype. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values whose type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! +} + +""" +Interface implemented by on-chain values that are addressable by an ID (also referred to as its +address). This includes Move objects and packages. +""" +interface IObject { + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or system package upgrade transaction. LIVE, the version returned is the most recent for the object, and it is not deleted or wrapped at that version. HISTORICAL, the object was referenced at a specific version or checkpoint, so is fetched from historical tables and may not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or wrapped and only partial information can be loaded. + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's current contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + Immutable and Shared Objects do not have owners. + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 +} + +""" +Interface implemented by GraphQL types representing entities that can own objects. Object owners +are identified by an address which can represent either the public key of an account or another +object. The same address can only refer to an account or an object, never both, but it is not +possible to know which up-front. +""" +interface IOwner { + address: SuiAddress! + """ + Objects owned by this object or address, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object or address. If type is not supplied, it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object or address. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object or address. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object or address. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object or address. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object or address. These grant the owner the capability to manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! +} + +""" +An immutable object is an object that can't be mutated, transferred, or deleted. +Immutable objects have no owner, so anyone can use them. +""" +type Immutable { + _: Boolean +} + +""" +One of the input objects or primitive values to the programmable transaction block. +""" +type Input { + """ + Index of the programmable transaction block input (0-indexed). + """ + ix: Int! +} + + +""" +Arbitrary JSON data. +""" +scalar JSON + +""" +Information used by a package to link to a specific version of its dependency. +""" +type Linkage { + """ + The ID on-chain of the first version of the dependency. + """ + originalId: SuiAddress! + """ + The ID on-chain of the version of the dependency that this package depends on. + """ + upgradedId: SuiAddress! + """ + The version of the dependency that this package depends on. + """ + version: UInt53! +} + +""" +Create a vector (possibly empty). +""" +type MakeMoveVecTransaction { + """ + If the elements are not objects, or the vector is empty, a type must be supplied. + """ + type: MoveType + """ + The values to pack into the vector, all of the same type. + """ + elements: [TransactionArgument!]! +} + +""" +Merges `coins` into the first `coin` (produces no results). +""" +type MergeCoinsTransaction { + """ + The coin to merge into. + """ + coin: TransactionArgument! + """ + The coins to be merged. + """ + coins: [TransactionArgument!]! +} + +""" +Abilities are keywords in Sui Move that define how types behave at the compiler level. +""" +enum MoveAbility { + """ + Enables values to be copied. + """ + COPY + """ + Enables values to be popped/dropped. + """ + DROP + """ + Enables values to be held directly in global storage. + """ + KEY + """ + Enables values to be held inside a struct in global storage. + """ + STORE +} + +""" +A call to either an entry or a public Move function. +""" +type MoveCallTransaction { + """ + The storage ID of the package the function being called is defined in. + """ + package: SuiAddress! + """ + The name of the module the function being called is defined in. + """ + module: String! + """ + The name of the function being called. + """ + functionName: String! + """ + The function being called, resolved. + """ + function: MoveFunction + """ + The actual type parameters passed in for this move call. + """ + typeArguments: [MoveType!]! + """ + The actual function parameters passed in for this move call. + """ + arguments: [TransactionArgument!]! +} + +""" +The contents of a Move Value, corresponding to the following recursive type: + +type MoveData = + { Address: SuiAddress } + | { UID: SuiAddress } + | { ID: SuiAddress } + | { Bool: bool } + | { Number: BigInt } + | { String: string } + | { Vector: [MoveData] } + | { Option: MoveData? } + | { Struct: [{ name: string , value: MoveData }] } + | { Variant: { + name: string, + fields: [{ name: string, value: MoveData }], + } +""" +scalar MoveData + +""" +The generic representation of a Move datatype (either a struct or an enum) which exposes common +fields and information (module, name, abilities, type parameters etc.) that is shared across +them. +""" +type MoveDatatype implements IMoveDatatype { + module: MoveModule! + name: String! + abilities: [MoveAbility!] + typeParameters: [MoveStructTypeParameter!] + asMoveEnum: MoveEnum + asMoveStruct: MoveStruct +} + +type MoveDatatypeConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveDatatypeEdge!]! + """ + A list of nodes. + """ + nodes: [MoveDatatype!]! +} + +""" +An edge in a connection. +""" +type MoveDatatypeEdge { + """ + The item at the end of the edge + """ + node: MoveDatatype! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Description of an enum type, defined in a Move module. +""" +type MoveEnum implements IMoveDatatype { + """ + The module this enum was originally defined in. + """ + module: MoveModule! + """ + The enum's (unqualified) type name. + """ + name: String! + """ + The enum's abilities. + """ + abilities: [MoveAbility!] + """ + Constraints on the enum's formal type parameters. Move bytecode does not name type + parameters, so when they are referenced (e.g. in field types) they are identified by their + index in this list. + """ + typeParameters: [MoveStructTypeParameter!] + """ + The names and types of the enum's fields. Field types reference type parameters, by their + index in the defining enum's `typeParameters` list. + """ + variants: [MoveEnumVariant!] +} + +type MoveEnumConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveEnumEdge!]! + """ + A list of nodes. + """ + nodes: [MoveEnum!]! +} + +""" +An edge in a connection. +""" +type MoveEnumEdge { + """ + The item at the end of the edge + """ + node: MoveEnum! + """ + A cursor for use in pagination + """ + cursor: String! +} + +type MoveEnumVariant { + """ + The name of the variant + """ + name: String! + """ + The names and types of the variant's fields. Field types reference type parameters, by their + index in the defining enum's `typeParameters` list. + """ + fields: [MoveField!] +} + +""" +Information for a particular field on a Move struct. +""" +type MoveField { + name: String! + type: OpenMoveType +} + +""" +Signature of a function, defined in a Move module. +""" +type MoveFunction { + """ + The module this function was defined in. + """ + module: MoveModule! + """ + The function's (unqualified) name. + """ + name: String! + """ + The function's visibility: `public`, `public(friend)`, or `private`. + """ + visibility: MoveVisibility + """ + Whether the function has the `entry` modifier or not. + """ + isEntry: Boolean + """ + Constraints on the function's formal type parameters. Move bytecode does not name type + parameters, so when they are referenced (e.g. in parameter and return types) they are + identified by their index in this list. + """ + typeParameters: [MoveFunctionTypeParameter!] + """ + The function's parameter types. These types can reference type parameters introduce by this + function (see `typeParameters`). + """ + parameters: [OpenMoveType!] + """ + The function's return types. There can be multiple because functions in Move can return + multiple values. These types can reference type parameters introduced by this function (see + `typeParameters`). + """ + return: [OpenMoveType!] +} + +type MoveFunctionConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveFunctionEdge!]! + """ + A list of nodes. + """ + nodes: [MoveFunction!]! +} + +""" +An edge in a connection. +""" +type MoveFunctionEdge { + """ + The item at the end of the edge + """ + node: MoveFunction! + """ + A cursor for use in pagination + """ + cursor: String! +} + +type MoveFunctionTypeParameter { + constraints: [MoveAbility!]! +} + +""" +Represents a module in Move, a library that defines struct types +and functions that operate on these types. +""" +type MoveModule { + """ + The package that this Move module was defined in + """ + package: MovePackage! + """ + The module's (unqualified) name. + """ + name: String! + """ + Format version of this module's bytecode. + """ + fileFormatVersion: Int! + """ + Modules that this module considers friends (these modules can access `public(friend)` + functions from this module). + """ + friends(first: Int, after: String, last: Int, before: String): MoveModuleConnection! + """ + Look-up the definition of a struct defined in this module, by its name. + """ + struct(name: String!): MoveStruct + """ + Iterate through the structs defined in this module. + """ + structs(first: Int, after: String, last: Int, before: String): MoveStructConnection + """ + Look-up the definition of a enum defined in this module, by its name. + """ + enum(name: String!): MoveEnum + """ + Iterate through the enums defined in this module. + """ + enums(first: Int, after: String, last: Int, before: String): MoveEnumConnection + """ + Look-up the definition of a datatype (struct or enum) defined in this module, by its name. + """ + datatype(name: String!): MoveDatatype + """ + Iterate through the datatypes (enmums and structs) defined in this module. + """ + datatypes(first: Int, after: String, last: Int, before: String): MoveDatatypeConnection + """ + Look-up the signature of a function defined in this module, by its name. + """ + function(name: String!): MoveFunction + """ + Iterate through the signatures of functions defined in this module. + """ + functions(first: Int, after: String, last: Int, before: String): MoveFunctionConnection + """ + The Base64 encoded bcs serialization of the module. + """ + bytes: Base64 + """ + Textual representation of the module's bytecode. + """ + disassembly: String +} + +type MoveModuleConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveModuleEdge!]! + """ + A list of nodes. + """ + nodes: [MoveModule!]! +} + +""" +An edge in a connection. +""" +type MoveModuleEdge { + """ + The item at the end of the edge + """ + node: MoveModule! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The representation of an object as a Move Object, which exposes additional information +(content, module that governs it, version, is transferrable, etc.) about this object. +""" +type MoveObject implements IMoveObject & IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also + provides the flat representation of the type signature, and the BCS of the corresponding + data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects + transaction command or `sui::transfer::public_transfer`, both of which require the object to + have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + Attempts to convert the Move object into a `0x2::coin::Coin`. + """ + asCoin: Coin + """ + Attempts to convert the Move object into a `0x3::staking_pool::StakedSui`. + """ + asStakedSui: StakedSui + """ + Attempts to convert the Move object into a `0x2::coin::CoinMetadata`. + """ + asCoinMetadata: CoinMetadata + """ + Attempts to convert the Move object into a `SuinsRegistration` object. + """ + asSuinsRegistration: SuinsRegistration +} + +type MoveObjectConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveObjectEdge!]! + """ + A list of nodes. + """ + nodes: [MoveObject!]! +} + +""" +An edge in a connection. +""" +type MoveObjectEdge { + """ + The item at the end of the edge + """ + node: MoveObject! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +A MovePackage is a kind of Move object that represents code that has been published on chain. +It exposes information about its modules, type definitions, functions, and dependencies. +""" +type MovePackage implements IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this package, optionally `filter`-ed. + + Note that objects owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this package. If type is not supplied, + it defaults to `0x2::sui::SUI`. + + Note that coins owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this package. + + Note that coins owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects owned by this package. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + + Note that coins owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this package. + + Note that objects owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this package. These grant the owner the capability to + manage the associated domain. + + Note that objects owned by a package are inaccessible, because packages are immutable and + cannot be owned by an address. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the package's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + Packages are always Immutable. + """ + owner: ObjectOwner + """ + The transaction block that published or upgraded this package. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + + Note that packages cannot be deleted or mutated, so this number is provided purely for + reference. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this package. + + Note that objects that have been sent to a package become inaccessible. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the package's content. + """ + bcs: Base64 + """ + A representation of the module called `name` in this package, including the + structs and functions it defines. + """ + module(name: String!): MoveModule + """ + Paginate through the MoveModules defined in this package. + """ + modules(first: Int, after: String, last: Int, before: String): MoveModuleConnection + """ + The transitive dependencies of this package. + """ + linkage: [Linkage!] + """ + The (previous) versions of this package that introduced its types. + """ + typeOrigins: [TypeOrigin!] + """ + BCS representation of the package's modules. Modules appear as a sequence of pairs (module + name, followed by module bytes), in alphabetic order by module name. + """ + moduleBcs: Base64 +} + +type MovePackageConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MovePackageEdge!]! + """ + A list of nodes. + """ + nodes: [MovePackage!]! +} + +""" +An edge in a connection. +""" +type MovePackageEdge { + """ + The item at the end of the edge + """ + node: MovePackage! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Description of a struct type, defined in a Move module. +""" +type MoveStruct implements IMoveDatatype { + """ + The module this struct was originally defined in. + """ + module: MoveModule! + """ + The struct's (unqualified) type name. + """ + name: String! + """ + Abilities this struct has. + """ + abilities: [MoveAbility!] + """ + Constraints on the struct's formal type parameters. Move bytecode does not name type + parameters, so when they are referenced (e.g. in field types) they are identified by their + index in this list. + """ + typeParameters: [MoveStructTypeParameter!] + """ + The names and types of the struct's fields. Field types reference type parameters, by their + index in the defining struct's `typeParameters` list. + """ + fields: [MoveField!] +} + +type MoveStructConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [MoveStructEdge!]! + """ + A list of nodes. + """ + nodes: [MoveStruct!]! +} + +""" +An edge in a connection. +""" +type MoveStructEdge { + """ + The item at the end of the edge + """ + node: MoveStruct! + """ + A cursor for use in pagination + """ + cursor: String! +} + +type MoveStructTypeParameter { + constraints: [MoveAbility!]! + isPhantom: Boolean! +} + +""" +Represents concrete types (no type parameters, no references). +""" +type MoveType { + """ + Flat representation of the type signature, as a displayable string. + """ + repr: String! + """ + Structured representation of the type signature. + """ + signature: MoveTypeSignature! + """ + Structured representation of the "shape" of values that match this type. + """ + layout: MoveTypeLayout! + """ + The abilities this concrete type has. + """ + abilities: [MoveAbility!]! +} + +""" +The shape of a concrete Move Type (a type with all its type parameters instantiated with concrete types), corresponding to the following recursive type: + +type MoveTypeLayout = + "address" + | "bool" + | "u8" | "u16" | ... | "u256" + | { vector: MoveTypeLayout } + | { + struct: { + type: string, + fields: [{ name: string, layout: MoveTypeLayout }], + } + } + | { enum: [{ + type: string, + variants: [{ + name: string, + fields: [{ name: string, layout: MoveTypeLayout }], + }] + }] + } +""" +scalar MoveTypeLayout + +""" +The signature of a concrete Move Type (a type with all its type parameters instantiated with concrete types, that contains no references), corresponding to the following recursive type: + +type MoveTypeSignature = + "address" + | "bool" + | "u8" | "u16" | ... | "u256" + | { vector: MoveTypeSignature } + | { + datatype: { + package: string, + module: string, + type: string, + typeParameters: [MoveTypeSignature], + } + } +""" +scalar MoveTypeSignature + +type MoveValue { + """ + The value's Move type. + """ + type: MoveType! + """ + The BCS representation of this value, Base64 encoded. + """ + bcs: Base64! + """ + Structured contents of a Move value. + """ + data: MoveData! + """ + Representation of a Move value in JSON, where: + + - Addresses, IDs, and UIDs are represented in canonical form, as JSON strings. + - Bools are represented by JSON boolean literals. + - u8, u16, and u32 are represented as JSON numbers. + - u64, u128, and u256 are represented as JSON strings. + - Vectors are represented by JSON arrays. + - Structs are represented by JSON objects. + - Empty optional values are represented by `null`. + + This form is offered as a less verbose convenience in cases where the layout of the type is + known by the client. + """ + json: JSON! +} + +""" +The visibility modifier describes which modules can access this module member. +By default, a module member can be called only within the same module. +""" +enum MoveVisibility { + """ + A public member can be accessed by any module. + """ + PUBLIC + """ + A private member can be accessed in the module it is defined in. + """ + PRIVATE + """ + A friend member can be accessed in the module it is defined in and any other module in + its package that is explicitly specified in its friend list. + """ + FRIEND +} + +""" +Mutations are used to write to the Sui network. +""" +type Mutation { + """ + Execute a transaction, committing its effects on chain. + + - `txBytes` is a `TransactionData` struct that has been BCS-encoded and then Base64-encoded. + - `signatures` are a list of `flag || signature || pubkey` bytes, Base64-encoded. + + Waits until the transaction has reached finality on chain to return its transaction digest, + or returns the error that prevented finality if that was not possible. A transaction is + final when its effects are guaranteed on chain (it cannot be revoked). + + There may be a delay between transaction finality and when GraphQL requests (including the + request that issued the transaction) reflect its effects. As a result, queries that depend + on indexing the state of the chain (e.g. contents of output objects, address-level balance + information at the time of the transaction), must wait for indexing to catch up by polling + for the transaction digest using `Query.transactionBlock`. + """ + executeTransactionBlock(txBytes: String!, signatures: [String!]!): ExecutionResult! +} + +""" +An object in Sui is a package (set of Move bytecode modules) or object (typed data structure +with fields) with additional metadata detailing its id, version, transaction digest, owner +field indicating how this object can be accessed. +""" +type Object implements IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's current contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + Immutable and Shared Objects do not have owners. + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + Attempts to convert the object into a MoveObject + """ + asMoveObject: MoveObject + """ + Attempts to convert the object into a MovePackage + """ + asMovePackage: MovePackage +} + +""" +Effect on an individual Object (keyed by its ID). +""" +type ObjectChange { + """ + The address of the object that has changed. + """ + address: SuiAddress! + """ + The contents of the object immediately before the transaction. + """ + inputState: Object + """ + The contents of the object immediately after the transaction. + """ + outputState: Object + """ + Whether the ID was created in this transaction. + """ + idCreated: Boolean + """ + Whether the ID was deleted in this transaction. + """ + idDeleted: Boolean +} + +type ObjectChangeConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [ObjectChangeEdge!]! + """ + A list of nodes. + """ + nodes: [ObjectChange!]! +} + +""" +An edge in a connection. +""" +type ObjectChangeEdge { + """ + The item at the end of the edge + """ + node: ObjectChange! + """ + A cursor for use in pagination + """ + cursor: String! +} + +type ObjectConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [ObjectEdge!]! + """ + A list of nodes. + """ + nodes: [Object!]! +} + +""" +An edge in a connection. +""" +type ObjectEdge { + """ + The item at the end of the edge + """ + node: Object! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Constrains the set of objects returned. All filters are optional, and the resulting set of +objects are ones whose + +- Type matches the `type` filter, +- AND, whose owner matches the `owner` filter, +- AND, whose ID is in `objectIds` OR whose ID and version is in `objectKeys`. +""" +input ObjectFilter { + """ + This field is used to specify the type of objects that should be included in the query + results. + + Objects can be filtered by their type's package, package::module, or their fully qualified + type name. + + Generic types can be queried by either the generic type name, e.g. `0x2::coin::Coin`, or by + the full type name, such as `0x2::coin::Coin<0x2::sui::SUI>`. + """ + type: String + """ + Filter for live objects by their current owners. + """ + owner: SuiAddress + """ + Filter for live objects by their IDs. + """ + objectIds: [SuiAddress!] + """ + Filter for live or potentially historical objects by their ID and version. + """ + objectKeys: [ObjectKey!] +} + +input ObjectKey { + objectId: SuiAddress! + version: UInt53! +} + +enum ObjectKind { + """ + The object is loaded from serialized data, such as the contents of a transaction that hasn't + been indexed yet. + """ + NOT_INDEXED + """ + The object is fetched from the index. + """ + INDEXED + """ + The object is deleted or wrapped and only partial information can be loaded from the + indexer. + """ + WRAPPED_OR_DELETED +} + +""" +The object's owner type: Immutable, Shared, Parent, or Address. +""" +union ObjectOwner = Immutable | Shared | Parent | AddressOwner + +input ObjectRef { + """ + ID of the object. + """ + address: SuiAddress! + """ + Version or sequence number of the object. + """ + version: UInt53! + """ + Digest of the object. + """ + digest: String! +} + +""" +Represents types that could contain references or free type parameters. Such types can appear +as function parameters, in fields of structs, or as actual type parameter. +""" +type OpenMoveType { + """ + Structured representation of the type signature. + """ + signature: OpenMoveTypeSignature! + """ + Flat representation of the type signature, as a displayable string. + """ + repr: String! +} + +""" +The shape of an abstract Move Type (a type that can contain free type parameters, and can optionally be taken by reference), corresponding to the following recursive type: + +type OpenMoveTypeSignature = { + ref: ("&" | "&mut")?, + body: OpenMoveTypeSignatureBody, +} + +type OpenMoveTypeSignatureBody = + "address" + | "bool" + | "u8" | "u16" | ... | "u256" + | { vector: OpenMoveTypeSignatureBody } + | { + datatype { + package: string, + module: string, + type: string, + typeParameters: [OpenMoveTypeSignatureBody] + } + } + | { typeParameter: number } +""" +scalar OpenMoveTypeSignature + +""" +A Move object, either immutable, or owned mutable. +""" +type OwnedOrImmutable { + """ + ID of the object being read. + """ + address: SuiAddress! + """ + Version of the object being read. + """ + version: UInt53! + """ + 32-byte hash that identifies the object's contents at this version, encoded as a Base58 + string. + """ + digest: String! + """ + The object at this version. May not be available due to pruning. + """ + object: Object +} + +""" +An Owner is an entity that can own an object. Each Owner is identified by a SuiAddress which +represents either an Address (corresponding to a public key of an account) or an Object, but +never both (it is not known up-front whether a given Owner is an Address or an Object). +""" +type Owner implements IOwner { + address: SuiAddress! + """ + Objects owned by this object or address, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object or address. If type is not + supplied, it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object or address. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object or address. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object or address. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object or address. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object or address. These grant the owner the + capability to manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + asAddress: Address + asObject: Object + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + This field exists as a convenience when accessing a dynamic field on a wrapped object. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + This field exists as a convenience when accessing a dynamic field on a wrapped object. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + This field exists as a convenience when accessing a dynamic field on a wrapped object. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! +} + +""" +Information about pagination in a connection +""" +type PageInfo { + """ + When paginating backwards, are there more items? + """ + hasPreviousPage: Boolean! + """ + When paginating forwards, are there more items? + """ + hasNextPage: Boolean! + """ + When paginating backwards, the cursor to continue. + """ + startCursor: String + """ + When paginating forwards, the cursor to continue. + """ + endCursor: String +} + +""" +If the object's owner is a Parent, this object is part of a dynamic field (it is the value of +the dynamic field, or the intermediate Field object itself). Also note that if the owner +is a parent, then it's guaranteed to be an object. +""" +type Parent { + parent: Object +} + +""" +A single transaction, or command, in the programmable transaction block. +""" +union ProgrammableTransaction = MoveCallTransaction | TransferObjectsTransaction | SplitCoinsTransaction | MergeCoinsTransaction | PublishTransaction | UpgradeTransaction | MakeMoveVecTransaction + +""" +A user transaction that allows the interleaving of native commands (like transfer, split coins, +merge coins, etc) and move calls, executed atomically. +""" +type ProgrammableTransactionBlock { + """ + Input objects or primitive values. + """ + inputs(first: Int, after: String, last: Int, before: String): TransactionInputConnection! + """ + The transaction commands, executed sequentially. + """ + transactions(first: Int, after: String, last: Int, before: String): ProgrammableTransactionConnection! +} + +type ProgrammableTransactionConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [ProgrammableTransactionEdge!]! + """ + A list of nodes. + """ + nodes: [ProgrammableTransaction!]! +} + +""" +An edge in a connection. +""" +type ProgrammableTransactionEdge { + """ + The item at the end of the edge + """ + node: ProgrammableTransaction! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +A single protocol configuration value. +""" +type ProtocolConfigAttr { + key: String! + value: String +} + +""" +Whether or not a single feature is enabled in the protocol config. +""" +type ProtocolConfigFeatureFlag { + key: String! + value: Boolean! +} + +""" +Constants that control how the chain operates. + +These can only change during protocol upgrades which happen on epoch boundaries. +""" +type ProtocolConfigs { + """ + The protocol is not required to change on every epoch boundary, so the protocol version + tracks which change to the protocol these configs are from. + """ + protocolVersion: UInt53! + """ + List all available feature flags and their values. Feature flags are a form of boolean + configuration that are usually used to gate features while they are in development. Once a + flag has been enabled, it is rare for it to be disabled. + """ + featureFlags: [ProtocolConfigFeatureFlag!]! + """ + List all available configurations and their values. These configurations can take any value + (but they will all be represented in string form), and do not include feature flags. + """ + configs: [ProtocolConfigAttr!]! + """ + Query for the value of the configuration with name `key`. + """ + config(key: String!): ProtocolConfigAttr + """ + Query for the state of the feature flag with name `key`. + """ + featureFlag(key: String!): ProtocolConfigFeatureFlag +} + +""" +Publishes a Move Package. +""" +type PublishTransaction { + """ + Bytecode for the modules to be published, BCS serialized and Base64 encoded. + """ + modules: [Base64!]! + """ + IDs of the transitive dependencies of the package to be published. + """ + dependencies: [SuiAddress!]! +} + +""" +BCS encoded primitive value (not an object or Move struct). +""" +type Pure { + """ + BCS serialized and Base64 encoded primitive value. + """ + bytes: Base64! +} + +type Query { + """ + First four bytes of the network's genesis checkpoint digest (uniquely identifies the + network). + """ + chainIdentifier: String! + """ + Range of checkpoints that the RPC has data available for (for data + that can be tied to a particular checkpoint). + """ + availableRange: AvailableRange! + """ + Configuration for this RPC service + """ + serviceConfig: ServiceConfig! + """ + Simulate running a transaction to inspect its effects without + committing to them on-chain. + + `txBytes` either a `TransactionData` struct or a `TransactionKind` + struct, BCS-encoded and then Base64-encoded. The expected + type is controlled by the presence or absence of `txMeta`: If + present, `txBytes` is assumed to be a `TransactionKind`, if + absent, then `TransactionData`. + + `txMeta` the data that is missing from a `TransactionKind` to make + a `TransactionData` (sender address and gas information). All + its fields are nullable. + + `skipChecks` optional flag to disable the usual verification + checks that prevent access to objects that are owned by + addresses other than the sender, and calling non-public, + non-entry functions, and some other checks. Defaults to false. + """ + dryRunTransactionBlock(txBytes: String!, txMeta: TransactionMetadata, skipChecks: Boolean): DryRunResult! + """ + Look up an Owner by its SuiAddress. + + `rootVersion` represents the version of the root object in some nested chain of dynamic + fields. It allows consistent historical queries for the case of wrapped objects, which don't + have a version. For example, if querying the dynamic field of a table wrapped in a parent + object, passing the parent object's version here will ensure we get the dynamic field's + state at the moment that parent's version was created. + + Also, if this Owner is an object itself, `rootVersion` will be used to bound its version + from above when querying `Owner.asObject`. This can be used, for example, to get the + contents of a dynamic object field when its parent was at `rootVersion`. + + If `rootVersion` is omitted, dynamic fields will be from a consistent snapshot of the Sui + state at the latest checkpoint known to the GraphQL RPC. Similarly, `Owner.asObject` will + return the object's version at the latest checkpoint. + """ + owner(address: SuiAddress!, rootVersion: UInt53): Owner + """ + The object corresponding to the given address at the (optionally) given version. + When no version is given, the latest version is returned. + """ + object(address: SuiAddress!, version: UInt53): Object + """ + Look-up an Account by its SuiAddress. + """ + address(address: SuiAddress!): Address + """ + Fetch a structured representation of a concrete type, including its layout information. + Fails if the type is malformed. + """ + type(type: String!): MoveType! + """ + Fetch epoch information by ID (defaults to the latest epoch). + """ + epoch(id: UInt53): Epoch + """ + Fetch checkpoint information by sequence number or digest (defaults to the latest available + checkpoint). + """ + checkpoint(id: CheckpointId): Checkpoint + """ + Fetch a transaction block by its transaction digest. + """ + transactionBlock(digest: String!): TransactionBlock + """ + The coin objects that exist in the network. + + The type field is a string of the inner type of the coin by which to filter (e.g. + `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The checkpoints that exist in the network. + """ + checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! + """ + The transaction blocks that exist in the network. + """ + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The events that exist in the network. + """ + events(first: Int, after: String, last: Int, before: String, filter: EventFilter): EventConnection! + """ + The objects that exist in the network. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): ObjectConnection! + """ + Fetch the protocol config by protocol version (defaults to the latest protocol + version known to the GraphQL service). + """ + protocolConfig(protocolVersion: UInt53): ProtocolConfigs! + """ + Resolves a SuiNS `domain` name to an address, if it has been bound. + """ + resolveSuinsAddress(domain: String!): Address + """ + The coin metadata associated with the given coin type. + """ + coinMetadata(coinType: String!): CoinMetadata + """ + Verify a zkLogin signature based on the provided transaction or personal message + based on current epoch, chain id, and latest JWKs fetched on-chain. If the + signature is valid, the function returns a `ZkLoginVerifyResult` with success as + true and an empty list of errors. If the signature is invalid, the function returns + a `ZkLoginVerifyResult` with success as false with a list of errors. + + - `bytes` is either the personal message in raw bytes or transaction data bytes in + BCS-encoded and then Base64-encoded. + - `signature` is a serialized zkLogin signature that is Base64-encoded. + - `intentScope` is an enum that specifies the intent scope to be used to parse bytes. + - `author` is the address of the signer of the transaction or personal msg. + """ + verifyZkloginSignature(bytes: Base64!, signature: Base64!, intentScope: ZkLoginIntentScope!, author: SuiAddress!): ZkLoginVerifyResult! +} + +type RandomnessStateCreateTransaction { + """ + A workaround to define an empty variant of a GraphQL union. + """ + _: Boolean +} + +""" +System transaction to update the source of on-chain randomness. +""" +type RandomnessStateUpdateTransaction { + """ + Epoch of the randomness state update transaction. + """ + epoch: Epoch + """ + Randomness round of the update. + """ + randomnessRound: UInt53! + """ + Updated random bytes, encoded as Base64. + """ + randomBytes: Base64! + """ + The initial version the randomness object was shared at. + """ + randomnessObjInitialSharedVersion: UInt53! +} + +""" +A Move object that can be received in this transaction. +""" +type Receiving { + """ + ID of the object being read. + """ + address: SuiAddress! + """ + Version of the object being read. + """ + version: UInt53! + """ + 32-byte hash that identifies the object's contents at this version, encoded as a Base58 + string. + """ + digest: String! + """ + The object at this version. May not be available due to pruning. + """ + object: Object +} + +""" +The result of another transaction command. +""" +type Result { + """ + The index of the previous command (0-indexed) that returned this result. + """ + cmd: Int! + """ + If the previous command returns multiple values, this is the index of the individual result + among the multiple results from that command (also 0-indexed). + """ + ix: Int +} + +""" +Information about whether epoch changes are using safe mode. +""" +type SafeMode { + """ + Whether safe mode was used for the last epoch change. The system will retry a full epoch + change on every epoch boundary and automatically reset this flag if so. + """ + enabled: Boolean + """ + Accumulated fees for computation and cost that have not been added to the various reward + pools, because the full epoch change did not happen. + """ + gasSummary: GasCostSummary +} + +""" +The enabled features and service limits configured by the server. +""" +type ServiceConfig { + """ + Check whether `feature` is enabled on this GraphQL service. + """ + isEnabled(feature: Feature!): Boolean! + """ + List the available versions for this GraphQL service. + """ + availableVersions: [String!]! + """ + List of all features that are enabled on this GraphQL service. + """ + enabledFeatures: [Feature!]! + """ + The maximum depth a GraphQL query can be to be accepted by this service. + """ + maxQueryDepth: Int! + """ + The maximum number of nodes (field names) the service will accept in a single query. + """ + maxQueryNodes: Int! + """ + The maximum number of output nodes in a GraphQL response. + + Non-connection nodes have a count of 1, while connection nodes are counted as + the specified 'first' or 'last' number of items, or the default_page_size + as set by the server if those arguments are not set. + + Counts accumulate multiplicatively down the query tree. For example, if a query starts + with a connection of first: 10 and has a field to a connection with last: 20, the count + at the second level would be 200 nodes. This is then summed to the count of 10 nodes + at the first level, for a total of 210 nodes. + """ + maxOutputNodes: Int! + """ + Maximum estimated cost of a database query used to serve a GraphQL request. This is + measured in the same units that the database uses in EXPLAIN queries. + """ + maxDbQueryCost: Int! + """ + Default number of elements allowed on a single page of a connection. + """ + defaultPageSize: Int! + """ + Maximum number of elements allowed on a single page of a connection. + """ + maxPageSize: Int! + """ + Maximum time in milliseconds spent waiting for a response from fullnode after issuing a + a transaction to execute. Note that the transaction may still succeed even in the case of a + timeout. Transactions are idempotent, so a transaction that times out should be resubmitted + until the network returns a definite response (success or failure, not timeout). + """ + mutationTimeoutMs: Int! + """ + Maximum time in milliseconds that will be spent to serve one query request. + """ + requestTimeoutMs: Int! + """ + Maximum length of a query payload string. + """ + maxQueryPayloadSize: Int! + """ + Maximum nesting allowed in type arguments in Move Types resolved by this service. + """ + maxTypeArgumentDepth: Int! + """ + Maximum number of type arguments passed into a generic instantiation of a Move Type resolved + by this service. + """ + maxTypeArgumentWidth: Int! + """ + Maximum number of structs that need to be processed when calculating the layout of a single + Move Type. + """ + maxTypeNodes: Int! + """ + Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. + """ + maxMoveValueDepth: Int! +} + +""" +A shared object is an object that is shared using the 0x2::transfer::share_object function. +Unlike owned objects, once an object is shared, it stays mutable and is accessible by anyone. +""" +type Shared { + initialSharedVersion: UInt53! +} + +""" +A Move object that's shared. +""" +type SharedInput { + address: SuiAddress! + """ + The version that this this object was shared at. + """ + initialSharedVersion: UInt53! + """ + Controls whether the transaction block can reference the shared object as a mutable + reference or by value. This has implications for scheduling: Transactions that just read + shared objects at a certain version (mutable = false) can be executed concurrently, while + transactions that write shared objects (mutable = true) must be executed serially with + respect to each other. + """ + mutable: Boolean! +} + +""" +The transaction accpeted a shared object as input, but its execution was cancelled. +""" +type SharedObjectCancelled { + """ + ID of the shared object. + """ + address: SuiAddress! + """ + The assigned shared object version. It is a special version indicating transaction cancellation reason. + """ + version: UInt53! +} + +""" +The transaction accepted a shared object as input, but it was deleted before the transaction +executed. +""" +type SharedObjectDelete { + """ + ID of the shared object. + """ + address: SuiAddress! + """ + The version of the shared object that was assigned to this transaction during by consensus, + during sequencing. + """ + version: UInt53! + """ + Whether this transaction intended to use this shared object mutably or not. See + `SharedInput.mutable` for further details. + """ + mutable: Boolean! +} + +""" +The transaction accepted a shared object as input, but only to read it. +""" +type SharedObjectRead { + """ + ID of the object being read. + """ + address: SuiAddress! + """ + Version of the object being read. + """ + version: UInt53! + """ + 32-byte hash that identifies the object's contents at this version, encoded as a Base58 + string. + """ + digest: String! + """ + The object at this version. May not be available due to pruning. + """ + object: Object +} + +""" +Splits off coins with denominations in `amounts` from `coin`, returning multiple results (as +many as there are amounts.) +""" +type SplitCoinsTransaction { + """ + The coin to split. + """ + coin: TransactionArgument! + """ + The denominations to split off from the coin. + """ + amounts: [TransactionArgument!]! +} + +""" +The stake's possible status: active, pending, or unstaked. +""" +enum StakeStatus { + """ + The stake object is active in a staking pool and it is generating rewards. + """ + ACTIVE + """ + The stake awaits to join a staking pool in the next epoch. + """ + PENDING + """ + The stake is no longer active in any staking pool. + """ + UNSTAKED +} + +""" +Parameters that control the distribution of the stake subsidy. +""" +type StakeSubsidy { + """ + SUI set aside for stake subsidies -- reduces over time as stake subsidies are paid out over + time. + """ + balance: BigInt + """ + Number of times stake subsidies have been distributed subsidies are distributed with other + staking rewards, at the end of the epoch. + """ + distributionCounter: Int + """ + Amount of stake subsidy deducted from the balance per distribution -- decays over time. + """ + currentDistributionAmount: BigInt + """ + Maximum number of stake subsidy distributions that occur with the same distribution amount + (before the amount is reduced). + """ + periodLength: Int + """ + Percentage of the current distribution amount to deduct at the end of the current subsidy + period, expressed in basis points. + """ + decreaseRate: Int +} + +""" +Represents a `0x3::staking_pool::StakedSui` Move object on-chain. +""" +type StakedSui implements IMoveObject & IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also + provides the flat representation of the type signature, and the BCS of the corresponding + data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects + transaction command or `sui::transfer::public_transfer`, both of which require the object to + have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + A stake can be pending, active, or unstaked + """ + stakeStatus: StakeStatus! + """ + The epoch at which this stake became active. + """ + activatedEpoch: Epoch + """ + The epoch at which this object was requested to join a stake pool. + """ + requestedEpoch: Epoch + """ + The object id of the validator staking pool this stake belongs to. + """ + poolId: SuiAddress + """ + The SUI that was initially staked. + """ + principal: BigInt + """ + The estimated reward for this stake object, calculated as: + + principal * (initial_stake_rate / current_stake_rate - 1.0) + + Or 0, if this value is negative, where: + + - `initial_stake_rate` is the stake rate at the epoch this stake was activated at. + - `current_stake_rate` is the stake rate in the current epoch. + + This value is only available if the stake is active. + """ + estimatedReward: BigInt +} + +type StakedSuiConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [StakedSuiEdge!]! + """ + A list of nodes. + """ + nodes: [StakedSui!]! +} + +""" +An edge in a connection. +""" +type StakedSuiEdge { + """ + The item at the end of the edge + """ + node: StakedSui! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +SUI set aside to account for objects stored on-chain. +""" +type StorageFund { + """ + Sum of storage rebates of live objects on chain. + """ + totalObjectStorageRebates: BigInt + """ + The portion of the storage fund that will never be refunded through storage rebates. + + The system maintains an invariant that the sum of all storage fees into the storage fund is + equal to the sum of of all storage rebates out, the total storage rebates remaining, and the + non-refundable balance. + """ + nonRefundableBalance: BigInt +} + + +""" +String containing 32B hex-encoded address, with a leading "0x". Leading zeroes can be omitted on input but will always appear in outputs (SuiAddress in output is guaranteed to be 66 characters long). +""" +scalar SuiAddress + +type SuinsRegistration implements IMoveObject & IObject & IOwner { + address: SuiAddress! + """ + Objects owned by this object, optionally `filter`-ed. + """ + objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! + """ + Total balance of all coins with marker type owned by this object. If type is not supplied, + it defaults to `0x2::sui::SUI`. + """ + balance(type: String): Balance + """ + The balances of all coin types owned by this object. + """ + balances(first: Int, after: String, last: Int, before: String): BalanceConnection! + """ + The coin objects for this object. + + `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + """ + coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + """ + The `0x3::staking_pool::StakedSui` objects owned by this object. + """ + stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + """ + The domain explicitly configured as the default domain pointing to this object. + """ + defaultSuinsName(format: DomainFormat): String + """ + The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + manage the associated domain. + """ + suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + version: UInt53! + """ + The current status of the object as read from the off-chain store. The possible states are: + NOT_INDEXED, the object is loaded from serialized data, such as the contents of a genesis or + system package upgrade transaction. LIVE, the version returned is the most recent for the + object, and it is not deleted or wrapped at that version. HISTORICAL, the object was + referenced at a specific version or checkpoint, so is fetched from historical tables and may + not be the latest version of the object. WRAPPED_OR_DELETED, the object is deleted or + wrapped and only partial information can be loaded." + """ + status: ObjectKind! + """ + 32-byte hash that identifies the object's contents, encoded as a Base58 string. + """ + digest: String + """ + The owner type of this object: Immutable, Shared, Parent, Address + """ + owner: ObjectOwner + """ + The transaction block that created this version of the object. + """ + previousTransactionBlock: TransactionBlock + """ + The amount of SUI we would rebate if this object gets deleted or mutated. This number is + recalculated based on the present storage gas price. + """ + storageRebate: BigInt + """ + The transaction blocks that sent objects to this object. + """ + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + """ + The Base64-encoded BCS serialization of the object's content. + """ + bcs: Base64 + """ + Displays the contents of the Move object in a JSON string and through GraphQL types. Also + provides the flat representation of the type signature, and the BCS of the corresponding + data. + """ + contents: MoveValue + """ + Determines whether a transaction can transfer this object, using the TransferObjects + transaction command or `sui::transfer::public_transfer`, both of which require the object to + have the `key` and `store` abilities. + """ + hasPublicTransfer: Boolean! + """ + The set of named templates defined on-chain for the type of this object, to be handled + off-chain. The server substitutes data from the object into these templates to generate a + display string per template. + """ + display: [DisplayEntry!] + """ + Access a dynamic field on an object using its name. Names are arbitrary Move values whose + type have `copy`, `drop`, and `store`, and are specified using their type, and their BCS + contents, Base64 encoded. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicField(name: DynamicFieldName!): DynamicField + """ + Access a dynamic object field on an object using its name. Names are arbitrary Move values + whose type have `copy`, `drop`, and `store`, and are specified using their type, and their + BCS contents, Base64 encoded. The value of a dynamic object field can also be accessed + off-chain directly via its address (e.g. using `Query.object`). + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicObjectField(name: DynamicFieldName!): DynamicField + """ + The dynamic fields and dynamic object fields on an object. + + Dynamic fields on wrapped objects can be accessed by using the same API under the Owner + type. + """ + dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! + """ + Domain name of the SuinsRegistration object + """ + domain: String! +} + +type SuinsRegistrationConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [SuinsRegistrationEdge!]! + """ + A list of nodes. + """ + nodes: [SuinsRegistration!]! +} + +""" +An edge in a connection. +""" +type SuinsRegistrationEdge { + """ + The item at the end of the edge + """ + node: SuinsRegistration! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Details of the system that are decided during genesis. +""" +type SystemParameters { + """ + Target duration of an epoch, in milliseconds. + """ + durationMs: BigInt + """ + The epoch at which stake subsidies start being paid out. + """ + stakeSubsidyStartEpoch: UInt53 + """ + The minimum number of active validators that the system supports. + """ + minValidatorCount: Int + """ + The maximum number of active validators that the system supports. + """ + maxValidatorCount: Int + """ + Minimum stake needed to become a new validator. + """ + minValidatorJoiningStake: BigInt + """ + Validators with stake below this threshold will enter the grace period (see + `validatorLowStakeGracePeriod`), after which they are removed from the active validator set. + """ + validatorLowStakeThreshold: BigInt + """ + Validators with stake below this threshold will be removed from the active validator set + at the next epoch boundary, without a grace period. + """ + validatorVeryLowStakeThreshold: BigInt + """ + The number of epochs that a validator has to recover from having less than + `validatorLowStakeThreshold` stake. + """ + validatorLowStakeGracePeriod: BigInt +} + +""" +An argument to a programmable transaction command. +""" +union TransactionArgument = GasCoin | Input | Result + +type TransactionBlock { + """ + A 32-byte hash that uniquely identifies the transaction block contents, encoded in Base58. + This serves as a unique id for the block on chain. + """ + digest: String + """ + The address corresponding to the public key that signed this transaction. System + transactions do not have senders. + """ + sender: Address + """ + The gas input field provides information on what objects were used as gas as well as the + owner of the gas object(s) and information on the gas price and budget. + + If the owner of the gas object(s) is not the same as the sender, the transaction block is a + sponsored transaction block. + """ + gasInput: GasInput + """ + The type of this transaction as well as the commands and/or parameters comprising the + transaction of this kind. + """ + kind: TransactionBlockKind + """ + A list of all signatures, Base64-encoded, from senders, and potentially the gas owner if + this is a sponsored transaction. + """ + signatures: [Base64!] + """ + The effects field captures the results to the chain of executing this transaction. + """ + effects: TransactionBlockEffects + """ + This field is set by senders of a transaction block. It is an epoch reference that sets a + deadline after which validators will no longer consider the transaction valid. By default, + there is no deadline for when a transaction must execute. + """ + expiration: Epoch + """ + Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + """ + bcs: Base64 +} + +type TransactionBlockConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [TransactionBlockEdge!]! + """ + A list of nodes. + """ + nodes: [TransactionBlock!]! +} + +""" +An edge in a connection. +""" +type TransactionBlockEdge { + """ + The item at the end of the edge + """ + node: TransactionBlock! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The effects representing the result of executing a transaction block. +""" +type TransactionBlockEffects { + """ + The transaction that ran to produce these effects. + """ + transactionBlock: TransactionBlock + """ + Whether the transaction executed successfully or not. + """ + status: ExecutionStatus + """ + The latest version of all objects (apart from packages) that have been created or modified + by this transaction, immediately following this transaction. + """ + lamportVersion: UInt53! + """ + The reason for a transaction failure, if it did fail. + If the error is a Move abort, the error message will be resolved to a human-readable form if + possible, otherwise it will fall back to displaying the abort code and location. + """ + errors: String + """ + Transactions whose outputs this transaction depends upon. + """ + dependencies(first: Int, after: String, last: Int, before: String): DependencyConnection! + """ + Effects to the gas object. + """ + gasEffects: GasEffects + """ + Shared objects that are referenced by but not changed by this transaction. + """ + unchangedSharedObjects(first: Int, after: String, last: Int, before: String): UnchangedSharedObjectConnection! + """ + The effect this transaction had on objects on-chain. + """ + objectChanges(first: Int, after: String, last: Int, before: String): ObjectChangeConnection! + """ + The effect this transaction had on the balances (sum of coin values per coin type) of + addresses and objects. + """ + balanceChanges(first: Int, after: String, last: Int, before: String): BalanceChangeConnection! + """ + Events emitted by this transaction block. + """ + events(first: Int, after: String, last: Int, before: String): EventConnection! + """ + Timestamp corresponding to the checkpoint this transaction was finalized in. + """ + timestamp: DateTime + """ + The epoch this transaction was finalized in. + """ + epoch: Epoch + """ + The checkpoint this transaction was finalized in. + """ + checkpoint: Checkpoint + """ + Base64 encoded bcs serialization of the on-chain transaction effects. + """ + bcs: Base64! +} + +input TransactionBlockFilter { + function: String + """ + An input filter selecting for either system or programmable transactions. + """ + kind: TransactionBlockKindInput + afterCheckpoint: UInt53 + atCheckpoint: UInt53 + beforeCheckpoint: UInt53 + signAddress: SuiAddress + recvAddress: SuiAddress + inputObject: SuiAddress + changedObject: SuiAddress + transactionIds: [String!] +} + +""" +The kind of transaction block, either a programmable transaction or a system transaction. +""" +union TransactionBlockKind = ConsensusCommitPrologueTransaction | GenesisTransaction | ChangeEpochTransaction | ProgrammableTransactionBlock | AuthenticatorStateUpdateTransaction | RandomnessStateUpdateTransaction | EndOfEpochTransaction + +""" +An input filter selecting for either system or programmable transactions. +""" +enum TransactionBlockKindInput { + """ + A system transaction can be one of several types of transactions. + See [unions/transaction-block-kind] for more details. + """ + SYSTEM_TX + """ + A user submitted transaction block. + """ + PROGRAMMABLE_TX +} + +union TransactionInput = OwnedOrImmutable | SharedInput | Receiving | Pure + +type TransactionInputConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [TransactionInputEdge!]! + """ + A list of nodes. + """ + nodes: [TransactionInput!]! +} + +""" +An edge in a connection. +""" +type TransactionInputEdge { + """ + The item at the end of the edge + """ + node: TransactionInput! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +The optional extra data a user can provide to a transaction dry run. +`sender` defaults to `0x0`. If gasObjects` is not present, or is an empty list, +it is substituted with a mock Coin object, `gasPrice` defaults to the reference +gas price, `gasBudget` defaults to the max gas budget and `gasSponsor` defaults +to the sender. +""" +input TransactionMetadata { + sender: SuiAddress + gasPrice: UInt53 + gasObjects: [ObjectRef!] + gasBudget: UInt53 + gasSponsor: SuiAddress +} + +""" +Transfers `inputs` to `address`. All inputs must have the `store` ability (allows public +transfer) and must not be previously immutable or shared. +""" +type TransferObjectsTransaction { + """ + The objects to transfer. + """ + inputs: [TransactionArgument!]! + """ + The address to transfer to. + """ + address: TransactionArgument! +} + +""" +Information about which previous versions of a package introduced its types. +""" +type TypeOrigin { + """ + Module defining the type. + """ + module: String! + """ + Name of the struct. + """ + struct: String! + """ + The storage ID of the package that first defined this type. + """ + definingId: SuiAddress! +} + +""" +An unsigned integer that can hold values up to 2^53 - 1. This can be treated similarly to `Int`, +but it is guaranteed to be non-negative, and it may be larger than 2^32 - 1. +""" +scalar UInt53 + +""" +Details pertaining to shared objects that are referenced by but not changed by a transaction. +This information is considered part of the effects, because although the transaction specifies +the shared object as input, consensus must schedule it and pick the version that is actually +used. +""" +union UnchangedSharedObject = SharedObjectRead | SharedObjectDelete | SharedObjectCancelled + +type UnchangedSharedObjectConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [UnchangedSharedObjectEdge!]! + """ + A list of nodes. + """ + nodes: [UnchangedSharedObject!]! +} + +""" +An edge in a connection. +""" +type UnchangedSharedObjectEdge { + """ + The item at the end of the edge + """ + node: UnchangedSharedObject! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Upgrades a Move Package. +""" +type UpgradeTransaction { + """ + Bytecode for the modules to be published, BCS serialized and Base64 encoded. + """ + modules: [Base64!]! + """ + IDs of the transitive dependencies of the package to be published. + """ + dependencies: [SuiAddress!]! + """ + ID of the package being upgraded. + """ + currentPackage: SuiAddress! + """ + The `UpgradeTicket` authorizing the upgrade. + """ + upgradeTicket: TransactionArgument! +} + +type Validator { + """ + The validator's address. + """ + address: Address! + """ + Validator's set of credentials such as public keys, network addresses and others. + """ + credentials: ValidatorCredentials + """ + Validator's set of credentials for the next epoch. + """ + nextEpochCredentials: ValidatorCredentials + """ + Validator's name. + """ + name: String + """ + Validator's description. + """ + description: String + """ + Validator's url containing their custom image. + """ + imageUrl: String + """ + Validator's homepage URL. + """ + projectUrl: String + """ + The validator's current valid `Cap` object. Validators can delegate + the operation ability to another address. The address holding this `Cap` object + can then update the reference gas price and tallying rule on behalf of the validator. + """ + operationCap: MoveObject + """ + The validator's current staking pool object, used to track the amount of stake + and to compound staking rewards. + """ + stakingPool: MoveObject @deprecated(reason: "The staking pool is a wrapped object. Access its fields directly on the `Validator` type.") + """ + The ID of this validator's `0x3::staking_pool::StakingPool`. + """ + stakingPoolId: SuiAddress! + """ + The validator's current exchange object. The exchange rate is used to determine + the amount of SUI tokens that each past SUI staker can withdraw in the future. + """ + exchangeRates: MoveObject @deprecated(reason: "The exchange object is a wrapped object. Access its dynamic fields through the `exchangeRatesTable` query.") + """ + A wrapped object containing the validator's exchange rates. This is a table from epoch + number to `PoolTokenExchangeRate` value. The exchange rate is used to determine the amount + of SUI tokens that each past SUI staker can withdraw in the future. + """ + exchangeRatesTable: Owner + """ + Number of exchange rates in the table. + """ + exchangeRatesSize: UInt53 + """ + The epoch at which this pool became active. + """ + stakingPoolActivationEpoch: UInt53 + """ + The total number of SUI tokens in this pool. + """ + stakingPoolSuiBalance: BigInt + """ + The epoch stake rewards will be added here at the end of each epoch. + """ + rewardsPool: BigInt + """ + Total number of pool tokens issued by the pool. + """ + poolTokenBalance: BigInt + """ + Pending stake amount for this epoch. + """ + pendingStake: BigInt + """ + Pending stake withdrawn during the current epoch, emptied at epoch boundaries. + """ + pendingTotalSuiWithdraw: BigInt + """ + Pending pool token withdrawn during the current epoch, emptied at epoch boundaries. + """ + pendingPoolTokenWithdraw: BigInt + """ + The voting power of this validator in basis points (e.g., 100 = 1% voting power). + """ + votingPower: Int + """ + The reference gas price for this epoch. + """ + gasPrice: BigInt + """ + The fee charged by the validator for staking services. + """ + commissionRate: Int + """ + The total number of SUI tokens in this pool plus + the pending stake amount for this epoch. + """ + nextEpochStake: BigInt + """ + The validator's gas price quote for the next epoch. + """ + nextEpochGasPrice: BigInt + """ + The proposed next epoch fee for the validator's staking services. + """ + nextEpochCommissionRate: Int + """ + The number of epochs for which this validator has been below the + low stake threshold. + """ + atRisk: UInt53 + """ + The addresses of other validators this validator has reported. + """ + reportRecords(first: Int, before: String, last: Int, after: String): AddressConnection! + """ + The APY of this validator in basis points. + To get the APY in percentage, divide by 100. + """ + apy: Int +} + +type ValidatorConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [ValidatorEdge!]! + """ + A list of nodes. + """ + nodes: [Validator!]! +} + +""" +The credentials related fields associated with a validator. +""" +type ValidatorCredentials { + protocolPubKey: Base64 + networkPubKey: Base64 + workerPubKey: Base64 + proofOfPossession: Base64 + netAddress: String + p2PAddress: String + primaryAddress: String + workerAddress: String +} + +""" +An edge in a connection. +""" +type ValidatorEdge { + """ + The item at the end of the edge + """ + node: Validator! + """ + A cursor for use in pagination + """ + cursor: String! +} + +""" +Representation of `0x3::validator_set::ValidatorSet`. +""" +type ValidatorSet { + """ + Total amount of stake for all active validators at the beginning of the epoch. + """ + totalStake: BigInt + """ + Validators that are pending removal from the active validator set, expressed as indices in + to `activeValidators`. + """ + pendingRemovals: [Int!] + """ + Object ID of the wrapped object `TableVec` storing the pending active validators. + """ + pendingActiveValidatorsId: SuiAddress + """ + Size of the pending active validators table. + """ + pendingActiveValidatorsSize: Int + """ + Object ID of the `Table` storing the mapping from staking pool ids to the addresses + of the corresponding validators. This is needed because a validator's address + can potentially change but the object ID of its pool will not. + """ + stakingPoolMappingsId: SuiAddress + """ + Size of the stake pool mappings `Table`. + """ + stakingPoolMappingsSize: Int + """ + Object ID of the `Table` storing the inactive staking pools. + """ + inactivePoolsId: SuiAddress + """ + Size of the inactive pools `Table`. + """ + inactivePoolsSize: Int + """ + Object ID of the `Table` storing the validator candidates. + """ + validatorCandidatesId: SuiAddress + """ + Size of the validator candidates `Table`. + """ + validatorCandidatesSize: Int + """ + The current set of active validators. + """ + activeValidators(first: Int, before: String, last: Int, after: String): ValidatorConnection! +} + +""" +An enum that specifies the intent scope to be used to parse the bytes for signature +verification. +""" +enum ZkLoginIntentScope { + """ + Indicates that the bytes are to be parsed as transaction data bytes. + """ + TRANSACTION_DATA + """ + Indicates that the bytes are to be parsed as a personal message. + """ + PERSONAL_MESSAGE +} + +""" +The result of the zkLogin signature verification. +""" +type ZkLoginVerifyResult { + """ + The boolean result of the verification. If true, errors should be empty. + """ + success: Boolean! + """ + The errors field captures any verification error + """ + errors: [String!]! +} + +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT +schema { + query: Query + mutation: Mutation +} diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs new file mode 100644 index 000000000..54b3d2807 --- /dev/null +++ b/crates/sui-graphql-client/src/lib.rs @@ -0,0 +1,569 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![doc = include_str!("../README.md")] + +pub mod query_types; + +use base64ct::Encoding; +use query_types::{ + ChainIdentifierQuery, CheckpointArgs, CheckpointId, CheckpointQuery, CoinMetadata, + CoinMetadataArgs, CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, EventFilter, + EventsQuery, EventsQueryArgs, ObjectFilter, ObjectQuery, ObjectQueryArgs, ObjectsQuery, + ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs, + ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, TransactionBlockQuery, + TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, Uint53, +}; +use reqwest::Url; +use sui_types::types::{ + Address, CheckpointSequenceNumber, CheckpointSummary, Event, Object, SignedTransaction, +}; + +use anyhow::{anyhow, ensure, Error, Result}; +use cynic::{serde, GraphQlResponse, Operation, QueryBuilder}; + +const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; +const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; +const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; +const DEFAULT_LOCAL_HOST: &str = "http://localhost:9125/graphql"; +static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); + +#[derive(Debug)] +/// A page of items returned by the GraphQL server. +pub struct Page { + /// Information about the page, such as the cursor and whether there are more pages. + page_info: PageInfo, + /// The data returned by the server. + data: Vec, +} + +impl Page { + pub fn page_info(&self) -> &PageInfo { + &self.page_info + } + + pub fn data(&self) -> &[T] { + &self.data + } + + fn new(page_info: PageInfo, data: Vec) -> Self { + Self { page_info, data } + } +} + +/// The GraphQL client for interacting with the Sui blockchain. +/// By default, it uses the `reqwest` crate as the HTTP client. +pub struct Client { + /// The URL of the GraphQL server. + rpc: Url, + /// The reqwest client. + inner: reqwest::Client, +} + +impl Client { + // =========================================================================== + // Client Misc API + // =========================================================================== + + /// Create a new GraphQL client with the provided server address. + pub fn new(server: &str) -> Result { + let rpc = reqwest::Url::parse(server).map_err(|_| anyhow!("Invalid URL: {}", server))?; + + let client = Client { + rpc, + inner: reqwest::Client::builder().user_agent(USER_AGENT).build()?, + }; + Ok(client) + } + + /// Create a new GraphQL client connected to the `mainnet` GraphQL server: {MAINNET_HOST}. + pub fn new_mainnet() -> Self { + Self::new(MAINNET_HOST).expect("Invalid mainnet URL") + } + + /// Create a new GraphQL client connected to the `testnet` GraphQL server: {TESTNET_HOST}. + pub fn new_testnet() -> Self { + Self::new(TESTNET_HOST).expect("Invalid testnet URL") + } + + /// Create a new GraphQL client connected to the `devnet` GraphQL server: {DEVNET_HOST}. + pub fn new_devnet() -> Self { + Self::new(DEVNET_HOST).expect("Invalid devnet URL") + } + + /// Create a new GraphQL client connected to the `localhost` GraphQL server: + /// {DEFAULT_LOCAL_HOST}. + pub fn new_localhost() -> Self { + Self::new(DEFAULT_LOCAL_HOST).expect("Invalid localhost URL") + } + + /// Set the server address for the GraphQL GraphQL client. It should be a valid URL with a host and + /// optionally a port number. + pub fn set_rpc_server(&mut self, server: &str) -> Result<(), Error> { + let rpc = reqwest::Url::parse(server)?; + self.rpc = rpc; + Ok(()) + } + + /// Return the URL for the GraphQL server. + fn rpc_server(&self) -> &str { + self.rpc.as_str() + } + + /// Run a query on the GraphQL server and return the response. + /// This method returns [`cynic::GraphQlResponse`] over the query type `T`, and it is + /// intended to be used with custom queries. + pub async fn run_query(&self, operation: &Operation) -> Result> + where + T: serde::de::DeserializeOwned, + V: serde::Serialize, + { + let res = self + .inner + .post(self.rpc_server()) + .json(&operation) + .send() + .await? + .json::>() + .await?; + Ok(res) + } + + // =========================================================================== + // Network info API + // =========================================================================== + + /// Get the chain identifier. + pub async fn chain_id(&self) -> Result { + let operation = ChainIdentifierQuery::build(()); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + response + .data + .map(|e| e.chain_identifier) + .ok_or_else(|| Error::msg("No data in response")) + } + + /// Get the reference gas price for the provided epoch or the last known one if no epoch is + /// provided. + /// + /// This will return `Ok(None)` if the epoch requested is not available in the GraphQL service + /// (e.g., due to prunning). + pub async fn reference_gas_price(&self, epoch: Option) -> Result, Error> { + let operation = EpochSummaryQuery::build(EpochSummaryArgs { + id: epoch.map(Uint53), + }); + let response = self.run_query(&operation).await?; + + if let Some(data) = response.data { + data.epoch + .and_then(|e| e.reference_gas_price.map(|x| x.try_into())) + .transpose() + } else if let Some(errors) = response.errors { + Err(Error::msg(format!("{:?}", errors))) + } else { + Err(Error::msg("No data in response")) + } + } + + /// Get the protocol configuration. + pub async fn protocol_config( + &self, + version: Option, + ) -> Result, Error> { + let operation = ProtocolConfigQuery::build(ProtocolVersionArgs { + id: version.map(Uint53), + }); + let response = self.run_query(&operation).await?; + Ok(response.data.map(|p| p.protocol_config)) + } + + /// Get the GraphQL service configuration, including complexity limits, read and mutation limits, + /// supported versions, and others. + pub async fn service_config(&self) -> Result { + let operation = ServiceConfigQuery::build(()); + let response = self.run_query(&operation).await?; + + response + .data + .map(|s| s.service_config) + .ok_or_else(|| Error::msg("No data in response")) + } + + // =========================================================================== + // Coin API + // =========================================================================== + + pub async fn coin_metadata(&self, coin_type: &str) -> Result, Error> { + let operation = CoinMetadataQuery::build(CoinMetadataArgs { coin_type }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response.data.and_then(|x| x.coin_metadata)) + } + + // =========================================================================== + // Checkpoints API + // =========================================================================== + + /// Get the `CheckpointSummary` for a given checkpoint digest or checkpoint id. If none is + /// provided, it will use the last known checkpoint id. + pub async fn checkpoint( + &self, + digest: Option, + seq_num: Option, + ) -> Result, Error> { + ensure!( + digest.is_some() != seq_num.is_some(), + "Either digest or seq_num must be provided" + ); + + let operation = CheckpointQuery::build(CheckpointArgs { + id: CheckpointId { + digest, + sequence_number: seq_num.map(Uint53), + }, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + response + .data + .map(|c| c.checkpoint.map(|c| c.try_into()).transpose()) + .ok_or_else(|| Error::msg("No data in response"))? + } + + /// Return the sequence number of the latest checkpoint that has been executed. + pub async fn latest_checkpoint_sequence_number( + &self, + ) -> Result, Error> { + Ok(self + .checkpoint(None, None) + .await? + .map(|c| c.sequence_number)) + } + + // =========================================================================== + // Epoch API + // =========================================================================== + + /// Return the number of checkpoints in this epoch. This will return `Ok(None)` if the epoch + /// requested is not available in the GraphQL service (e.g., due to prunning). + pub async fn epoch_total_checkpoints(&self, epoch: Option) -> Result, Error> { + let response = self.epoch_summary(epoch).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response + .data + .and_then(|d| d.epoch) + .and_then(|e| e.total_checkpoints) + .map(|x| x.into())) + } + + /// Return the number of transaction blocks in this epoch. This will return `Ok(None)` if the + /// epoch requested is not available in the GraphQL service (e.g., due to prunning). + pub async fn epoch_total_transaction_blocks( + &self, + epoch: Option, + ) -> Result, Error> { + let response = self.epoch_summary(epoch).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response + .data + .and_then(|d| d.epoch) + .and_then(|e| e.total_transactions) + .map(|x| x.into())) + } + + /// Internal method for getting the epoch summary that is called in a few other APIs for + /// convenience. + async fn epoch_summary( + &self, + epoch: Option, + ) -> Result, Error> { + let operation = EpochSummaryQuery::build(EpochSummaryArgs { + id: epoch.map(Uint53), + }); + self.run_query(&operation).await + } + + // =========================================================================== + // Events API + // =========================================================================== + + pub async fn events( + &self, + after: Option, + before: Option, + filter: Option, + first: Option, + last: Option, + ) -> Result>, Error> { + let operation = EventsQuery::build(EventsQueryArgs { + after, + before, + filter, + first, + last, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + // TODO bcs from bytes into Event fails due to custom type parser error + // called `Result::unwrap()` on an `Err` value: Custom("TypeParseError") + if let Some(events) = response.data { + let ec = events.events; + let page_info = ec.page_info; + let nodes = ec + .nodes + .iter() + .map(|e| base64ct::Base64::decode_vec(e.bcs.0.as_str())) + .collect::, base64ct::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode Base64 event bcs bytes: {e}",)))? + .iter() + .map(|b| bcs::from_bytes::(b)) + .collect::, bcs::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Event: {e}",)))?; + + Ok(Some(Page::new(page_info, nodes))) + } else { + Ok(None) + } + } + + // =========================================================================== + // Objects API + // =========================================================================== + + /// Return an object based on the provided [`Address`]. + /// + /// If the object does not exist (e.g., due to prunning), this will return `Ok(None)`. + /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + pub async fn object( + &self, + address: Address, + version: Option, + ) -> Result, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { + address: address.into(), + version: version.map(Uint53), + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data { + let obj = object.object; + let bcs = obj + .and_then(|o| o.bcs) + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}",)))?; + let object = bcs + .map(|b| bcs::from_bytes::(&b)) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}",)))?; + + Ok(object) + } else { + Ok(None) + } + } + + /// Return a page of objects based on the provided parameters. + /// + /// Use this function together with the [`ObjectFilter::owner`] to get the objects owned by an + /// address. + /// + /// # Example + /// + /// ```rust,ignore + /// let filter = ObjectFilter { + /// type_: None, + /// owner: Some(Address::from_str("test").unwrap().into()), + /// object_ids: None, + /// object_keys: None, + /// }; + /// + /// let owned_objects = client.objects(None, None, Some(filter), None, None).await; + /// ``` + pub async fn objects( + &self, + after: Option<&str>, + before: Option<&str>, + filter: Option>, + first: Option, + last: Option, + ) -> Result>, Error> { + let operation = ObjectsQuery::build(ObjectsQueryArgs { + after, + before, + filter, + first, + last, + }); + + let response = self.run_query(&operation).await?; + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(objects) = response.data { + let oc = objects.objects; + let page_info = oc.page_info; + let bcs = oc + .nodes + .iter() + .map(|o| &o.bcs) + .filter_map(|b64| { + b64.as_ref() + .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) + }) + .collect::, base64ct::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}")))?; + let objects = bcs + .iter() + .map(|b| bcs::from_bytes::(b)) + .collect::, bcs::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}")))?; + + Ok(Some(Page::new(page_info, objects))) + } else { + Ok(None) + } + } + + /// Return the object's bcs content [`Vec`] based on the provided [`Address`]. + pub async fn object_bcs(&self, object_id: Address) -> Result>, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { + address: object_id.into(), + version: None, + }); + + let response = self.run_query(&operation).await.unwrap(); + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data.map(|d| d.object) { + object + .and_then(|o| o.bcs) + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}"))) + } else { + Ok(None) + } + } + + // =========================================================================== + // Transaction API + // =========================================================================== + + // TODO: From Brandon: this fails due to SignedTransaction in Sui core type being technically inaccurate but it is fixed in this SDK here. in particular core incorrectly appends the signing intent when it shouldn't so my guess is that's whats wrong + /// Get a transaction by its digest. + pub async fn transaction(&self, digest: &str) -> Result, Error> { + let operation = TransactionBlockQuery::build(TransactionBlockArgs { + digest: digest.to_string(), + }); + let response = self.run_query(&operation).await?; + + let signed_tx = response + .data + .and_then(|tbq| tbq.transaction_block) + .and_then(|tb| tb.bcs) + .map(|bcs| bcs::from_bytes::(bcs.0.as_bytes()).unwrap()); + Ok(signed_tx) + } + + /// Get a page of transactions based on the provided filters. + pub async fn transactions( + &self, + after: Option, + before: Option, + first: Option, + last: Option, + filter: Option, + ) -> Result>, Error> { + let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { + after, + before, + filter, + first, + last, + }); + + let response = self.run_query(&operation).await?; + + if let Some(txb) = response.data { + let txc = txb.transaction_blocks; + let page_info = txc.page_info; + let bcs = txc + .nodes + .iter() + .map(|tx| &tx.bcs) + .filter_map(|b64| { + b64.as_ref() + .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) + }) + .collect::, base64ct::Error>>() + .map_err(|e| { + Error::msg(format!("Cannot decode Base64 transaction bcs bytes: {e}")) + })?; + + let transactions = bcs + .iter() + .map(|tx| bcs::from_bytes::(tx)) + .collect::, bcs::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}")))?; + let page = Page::new(page_info, transactions); + Ok(Some(page)) + } else { + Ok(None) + } + } +} + +#[cfg(test)] +mod tests { + use crate::{Client, DEFAULT_LOCAL_HOST, DEVNET_HOST, MAINNET_HOST, TESTNET_HOST}; + + #[test] + fn test_rpc_server() { + let mut client = Client::new_mainnet(); + assert_eq!(client.rpc_server(), MAINNET_HOST); + client.set_rpc_server(TESTNET_HOST).unwrap(); + assert_eq!(client.rpc_server(), TESTNET_HOST); + client.set_rpc_server(DEVNET_HOST).unwrap(); + assert_eq!(client.rpc_server(), DEVNET_HOST); + client.set_rpc_server(DEFAULT_LOCAL_HOST).unwrap(); + assert_eq!(client.rpc_server(), DEFAULT_LOCAL_HOST); + + assert!(client.set_rpc_server("localhost:9125/graphql").is_ok()); + assert!(client.set_rpc_server("9125/graphql").is_err()); + } +} diff --git a/crates/sui-graphql-client/src/query_types/chain.rs b/crates/sui-graphql-client/src/query_types/chain.rs new file mode 100644 index 000000000..c1b4226b4 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/chain.rs @@ -0,0 +1,13 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +use crate::query_types::schema; + +// =========================================================================== +// Chain Identifier Query +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query")] +pub struct ChainIdentifierQuery { + pub chain_identifier: String, +} diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs new file mode 100644 index 000000000..380966ef2 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -0,0 +1,133 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + +use anyhow::Error; +use chrono::DateTime as ChronoDT; +use sui_types::types::GasCostSummary as NativeGasCostSummary; +use sui_types::types::{CheckpointContentsDigest, CheckpointDigest, CheckpointSummary}; + +use crate::query_types::{schema, Base64, BigInt, DateTime, Epoch, Uint53}; + +// =========================================================================== +// Checkpoint Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CheckpointArgs")] +pub struct CheckpointQuery { + #[arguments(id: $id)] + pub checkpoint: Option, +} + +// =========================================================================== +// Checkpoint Query Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct CheckpointArgs { + pub id: CheckpointId, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "CheckpointId")] +pub struct CheckpointId { + pub digest: Option, + pub sequence_number: Option, +} +// =========================================================================== +// Checkpoint Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Checkpoint")] +pub struct Checkpoint { + pub epoch: Option, + pub digest: String, + pub network_total_transactions: Option, + pub previous_checkpoint_digest: Option, + pub sequence_number: Uint53, + pub timestamp: DateTime, + pub validator_signatures: Base64, + pub rolling_gas_summary: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "GasCostSummary")] +pub struct GasCostSummary { + pub computation_cost: Option, + pub non_refundable_storage_fee: Option, + pub storage_cost: Option, + pub storage_rebate: Option, +} + +// TODO need bcs in GraphQL Checkpoint to avoid this conversion +impl TryInto for Checkpoint { + type Error = anyhow::Error; + + fn try_into(self) -> Result { + let epoch = self + .epoch + .ok_or_else(|| Error::msg("Epoch is missing"))? + .epoch_id + .into(); + let network_total_transactions = self + .network_total_transactions + .ok_or_else(|| Error::msg("Network total transactions is missing"))? + .into(); + let sequence_number = self.sequence_number.into(); + let timestamp_ms = ChronoDT::parse_from_rfc3339(&self.timestamp.0) + .map_err(|e| Error::msg(format!("Cannot parse DateTime: {e}")))? + .timestamp_millis() + .try_into()?; + let content_digest = CheckpointContentsDigest::from_str(&self.digest)?; + let previous_digest = self + .previous_checkpoint_digest + .map(|d| CheckpointDigest::from_str(&d)) + .transpose()?; + let epoch_rolling_gas_cost_summary = self + .rolling_gas_summary + .ok_or_else(|| Error::msg("Rolling gas summary is missing"))? + .try_into()?; + Ok(CheckpointSummary { + epoch, + sequence_number, + network_total_transactions, + timestamp_ms, + content_digest, + previous_digest, + epoch_rolling_gas_cost_summary, + checkpoint_commitments: vec![], + end_of_epoch_data: None, + version_specific_data: vec![], + }) + } +} + +impl TryInto for GasCostSummary { + type Error = anyhow::Error; + fn try_into(self) -> Result { + let computation_cost = self + .computation_cost + .ok_or_else(|| Error::msg("Computation cost is missing"))? + .try_into()?; + let non_refundable_storage_fee = self + .non_refundable_storage_fee + .ok_or_else(|| Error::msg("Non-refundable storage fee is missing"))? + .try_into()?; + let storage_cost = self + .storage_cost + .ok_or_else(|| Error::msg("Storage cost is missing"))? + .try_into()?; + let storage_rebate = self + .storage_rebate + .ok_or_else(|| Error::msg("Storage rebate is missing"))? + .try_into()?; + Ok(NativeGasCostSummary { + computation_cost, + non_refundable_storage_fee, + storage_cost, + storage_rebate, + }) + } +} diff --git a/crates/sui-graphql-client/src/query_types/coin.rs b/crates/sui-graphql-client/src/query_types/coin.rs new file mode 100644 index 000000000..c699afa3b --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/coin.rs @@ -0,0 +1,40 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// =========================================================================== +// Coin(s) Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CoinMetadataArgs")] +pub struct CoinMetadataQuery { + #[arguments(coinType: $coin_type)] + pub coin_metadata: Option, +} + +// =========================================================================== +// Coin(s) Query Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct CoinMetadataArgs<'a> { + pub coin_type: &'a str, +} + +// =========================================================================== +// Types +// =========================================================================== + +use crate::query_types::{schema, BigInt, Uint53}; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "CoinMetadata")] +pub struct CoinMetadata { + pub decimals: Option, + pub description: Option, + pub icon_url: Option, + pub name: Option, + pub symbol: Option, + pub supply: Option, + pub version: Uint53, +} diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/sui-graphql-client/src/query_types/epoch.rs new file mode 100644 index 000000000..f87574c78 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/epoch.rs @@ -0,0 +1,73 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +// +use crate::query_types::{schema, BigInt, DateTime, SuiAddress, Uint53}; + +// =========================================================================== +// Epoch Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "EpochSummaryArgs")] +pub struct EpochSummaryQuery { + #[arguments(id: $id)] + pub epoch: Option, +} + +// =========================================================================== +// Epoch Summary Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct EpochSummaryArgs { + pub id: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Epoch")] +pub struct EpochSummary { + pub epoch_id: Uint53, + pub reference_gas_price: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, +} + +// =========================================================================== +// Epoch Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Epoch")] +pub struct Epoch { + pub end_timestamp: Option, + pub epoch_id: Uint53, + pub fund_inflow: Option, + pub fund_outflow: Option, + pub fund_size: Option, + pub live_object_set_digest: Option, + pub net_inflow: Option, + pub reference_gas_price: Option, + pub start_timestamp: DateTime, + pub system_state_version: Option, + pub total_checkpoints: Option, + pub total_gas_fees: Option, + pub total_stake_rewards: Option, + pub total_stake_subsidies: Option, + pub total_transactions: Option, + pub validator_set: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ValidatorSet")] +pub struct ValidatorSet { + pub inactive_pools_id: Option, + pub inactive_pools_size: Option, + pub pending_active_validators_id: Option, + pub pending_active_validators_size: Option, + pub pending_removals: Option>, + pub staking_pool_mappings_id: Option, + pub staking_pool_mappings_size: Option, + pub total_stake: Option, + pub validator_candidates_size: Option, + pub validator_candidates_id: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs new file mode 100644 index 000000000..288cd27fc --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -0,0 +1,54 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::{schema, Base64, PageInfo, SuiAddress}; + +// =========================================================================== +// Events Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "EventsQueryArgs")] +pub struct EventsQuery { + #[arguments(after: $after, before: $before, filter: $filter, first: $first, last: $last)] + pub events: EventConnection, +} + +// =========================================================================== +// Events Query Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct EventsQueryArgs { + pub after: Option, + pub before: Option, + pub filter: Option, + pub first: Option, + pub last: Option, +} + +// =========================================================================== +// Events Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "EventConnection")] +pub struct EventConnection { + pub page_info: PageInfo, + pub nodes: Vec, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Event")] +pub struct Event { + pub bcs: Base64, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "EventFilter")] +pub struct EventFilter { + pub emitting_module: Option, + pub event_type: Option, + pub sender: Option, + pub transaction_digest: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs new file mode 100644 index 000000000..e45edfa28 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -0,0 +1,106 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + +mod chain; +mod checkpoint; +mod coin; +mod epoch; +mod events; +mod object; +mod protocol_config; +mod service_config; +mod transaction; + +use anyhow::{anyhow, Error}; +pub use chain::ChainIdentifierQuery; +pub use checkpoint::{CheckpointArgs, CheckpointId, CheckpointQuery}; +pub use coin::{CoinMetadata, CoinMetadataArgs, CoinMetadataQuery}; +pub use epoch::{Epoch, EpochSummaryArgs, EpochSummaryQuery}; +pub use events::{Event, EventConnection, EventFilter, EventsQuery, EventsQueryArgs}; +pub use object::{ + ObjectFilter, ObjectKey, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, +}; +pub use protocol_config::{ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs}; +pub use service_config::{Feature, ServiceConfig, ServiceConfigQuery}; +use sui_types::types::Address; +pub use transaction::{ + TransactionBlockArgs, TransactionBlockQuery, TransactionBlocksQuery, + TransactionBlocksQueryArgs, TransactionsFilter, +}; + +#[cynic::schema("rpc")] +pub mod schema {} + +// =========================================================================== +// Scalars +// =========================================================================== + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "Base64")] +pub struct Base64(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "BigInt")] +pub struct BigInt(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "DateTime")] +pub struct DateTime(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +pub struct SuiAddress(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "UInt53")] +pub struct Uint53(pub u64); + +// =========================================================================== +// Utility Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "PageInfo")] +/// Information about pagination in a connection. +pub struct PageInfo { + /// When paginating backwards, are there more items? + pub has_previous_page: bool, + /// Are there more items when paginating forwards? + pub has_next_page: bool, + /// When paginating backwards, the cursor to continue. + pub start_cursor: Option, + /// When paginating forwards, the cursor to continue. + pub end_cursor: Option, +} + +impl From for u64 { + fn from(value: Uint53) -> Self { + value.0 + } +} + +impl TryFrom for u64 { + type Error = anyhow::Error; + + fn try_from(value: BigInt) -> Result { + value + .0 + .parse::() + .map_err(|e| anyhow!("Cannot convert BigInt into u64: {e}")) + } +} + +impl From
for SuiAddress { + fn from(value: Address) -> Self { + SuiAddress(value.to_string()) + } +} + +impl TryFrom for Address { + type Error = anyhow::Error; + + fn try_from(value: SuiAddress) -> Result { + Address::from_str(&value.0) + .map_err(|e| Error::msg(format!("Cannot convert SuiAddress into Address: {e}"))) + } +} diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs new file mode 100644 index 000000000..267f2fd85 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -0,0 +1,75 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::{schema, Base64, PageInfo, SuiAddress, Uint53}; + +// =========================================================================== +// Object(s) Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "ObjectQueryArgs")] +pub struct ObjectQuery { + #[arguments(address: $address, version: $version)] + pub object: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "ObjectsQueryArgs")] +pub struct ObjectsQuery { + #[arguments(after: $after, before: $before, filter: $filter, first: $first, last: $last)] + pub objects: ObjectConnection, +} + +// =========================================================================== +// Object(s) Query Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct ObjectQueryArgs { + pub address: SuiAddress, + pub version: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct ObjectsQueryArgs<'a> { + pub after: Option<&'a str>, + pub before: Option<&'a str>, + pub filter: Option>, + pub first: Option, + pub last: Option, +} + +// =========================================================================== +// Object(s) Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Object")] +pub struct Object { + pub bcs: Option, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "ObjectFilter")] +pub struct ObjectFilter<'a> { + #[cynic(rename = "type")] + pub type_: Option<&'a str>, + pub owner: Option, + pub object_ids: Option>, + pub object_keys: Option>, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "ObjectKey")] +pub struct ObjectKey { + pub object_id: SuiAddress, + pub version: Uint53, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ObjectConnection")] +pub struct ObjectConnection { + pub page_info: PageInfo, + pub nodes: Vec, +} diff --git a/crates/sui-graphql-client/src/query_types/protocol_config.rs b/crates/sui-graphql-client/src/query_types/protocol_config.rs new file mode 100644 index 000000000..1299b4621 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/protocol_config.rs @@ -0,0 +1,56 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::schema; +use crate::query_types::Uint53; + +// =========================================================================== +// Protocol Config Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "ProtocolVersionArgs" +)] +pub struct ProtocolConfigQuery { + #[arguments(protocolVersion: $id)] + pub protocol_config: ProtocolConfigs, +} + +// =========================================================================== +// Protocol Version Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct ProtocolVersionArgs { + pub id: Option, +} + +// =========================================================================== +// Protocol Config Types +// =========================================================================== + +/// Information about the configuration of the protocol. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ProtocolConfigs")] +pub struct ProtocolConfigs { + pub protocol_version: Uint53, + pub feature_flags: Vec, + pub configs: Vec, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ProtocolConfigFeatureFlag")] +pub struct ProtocolConfigFeatureFlag { + pub key: String, + pub value: bool, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ProtocolConfigAttr")] +pub struct ProtocolConfigAttr { + pub key: String, + pub value: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/service_config.rs b/crates/sui-graphql-client/src/query_types/service_config.rs new file mode 100644 index 000000000..4265c27c1 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/service_config.rs @@ -0,0 +1,83 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::schema; + +// =========================================================================== +// Service Config Query +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query")] +pub struct ServiceConfigQuery { + pub service_config: ServiceConfig, +} + +// =========================================================================== +// Service Config Types +// =========================================================================== + +// Information about the configuration of the GraphQL service. +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ServiceConfig")] +pub struct ServiceConfig { + /// List the available versions for this GraphQL service. + pub available_versions: Vec, + /// Default number of elements allowed on a single page of a connection. + pub default_page_size: i32, + /// List of all features that are enabled on this RPC service. + pub enabled_features: Vec, + // TODO This field is retrieved as a string, instead of i32 + /// Maximum estimated cost of a database query used to serve a GraphQL request. This is + /// measured in the same units that the database uses in EXPLAIN queries. + // pub max_db_query_cost: i32, + /// Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. + pub max_move_value_depth: i32, + /// The maximum number of output nodes in a GraphQL response. + /// Non-connection nodes have a count of 1, while connection nodes are counted as + /// the specified 'first' or 'last' number of items, or the default_page_size + /// as set by the server if those arguments are not set. + /// Counts accumulate multiplicatively down the query tree. For example, if a query starts + /// with a connection of first: 10 and has a field to a connection with last: 20, the count + /// at the second level would be 200 nodes. This is then summed to the count of 10 nodes + /// at the first level, for a total of 210 nodes. + pub max_output_nodes: i32, + /// Maximum number of elements allowed on a single page of a connection. + pub max_page_size: i32, + /// The maximum depth a GraphQL query can be to be accepted by this service. + pub max_query_depth: i32, + /// The maximum number of nodes (field names) the service will accept in a single query. + pub max_query_nodes: i32, + /// Maximum length of a query payload string. + pub max_query_payload_size: i32, + /// Maximum nesting allowed in type arguments in Move Types resolved by this service. + pub max_type_argument_depth: i32, + /// Maximum number of type arguments passed into a generic instantiation of a Move Type resolved + /// by this service. + pub max_type_argument_width: i32, + /// Maximum number of structs that need to be processed when calculating the layout of a single + /// Move Type. + pub max_type_nodes: i32, + /// Maximum time in milliseconds spent waiting for a response from fullnode after issuing a + /// a transaction to execute. Note that the transaction may still succeed even in the case of a + /// timeout. Transactions are idempotent, so a transaction that times out should be resubmitted + /// until the network returns a definite response (success or failure, not timeout). + pub mutation_timeout_ms: i32, + /// Maximum time in milliseconds that will be spent to serve one query request. + pub request_timeout_ms: i32, +} + +#[derive(cynic::Enum, Clone, Copy, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Feature", + rename_all = "SCREAMING_SNAKE_CASE" +)] +pub enum Feature { + Analytics, + Coins, + DynamicFields, + NameService, + Subscriptions, + SystemState, +} diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs new file mode 100644 index 000000000..fef4c33f8 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -0,0 +1,89 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::Base64; +use crate::query_types::{schema, PageInfo, SuiAddress, Uint53}; + +// =========================================================================== +// Transaction Block(s) Queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "TransactionBlockArgs" +)] +pub struct TransactionBlockQuery { + #[arguments(digest: $digest)] + pub transaction_block: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "TransactionBlocksQueryArgs" +)] +pub struct TransactionBlocksQuery { + #[arguments(first: $first, after: $after, last: $last, before: $before, filter: $filter)] + pub transaction_blocks: TransactionBlockConnection, +} + +// =========================================================================== +// Transaction Block(s) Query Args +// =========================================================================== + +#[derive(cynic::QueryVariables, Debug)] +pub struct TransactionBlockArgs { + pub digest: String, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct TransactionBlocksQueryArgs { + pub first: Option, + pub after: Option, + pub last: Option, + pub before: Option, + pub filter: Option, +} + +// =========================================================================== +// Transaction Block(s) Types +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlock")] +pub struct TransactionBlock { + pub bcs: Option, +} + +#[derive(cynic::Enum, Clone, Copy, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "TransactionBlockKindInput", + rename_all = "SCREAMING_SNAKE_CASE" +)] +pub enum TransactionBlockKindInput { + SystemTx, + ProgrammableTx, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlockFilter")] +pub struct TransactionsFilter { + pub function: Option, + pub kind: Option, + pub at_checkpoint: Option, + pub before_checkpoint: Option, + pub changed_object: Option, + pub input_object: Option, + pub recv_address: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlockConnection")] +pub struct TransactionBlockConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} From 56f9983bc354d90eff8282171d752f1158d44289 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:33:27 -0700 Subject: [PATCH 003/107] graphql: update to latest graphql schema (#7) --- crates/sui-graphql-client/Cargo.toml | 1 + .../schema/graphql_rpc.graphql | 417 ++++++++++++++++-- .../src/query_types/service_config.rs | 3 +- 3 files changed, 393 insertions(+), 28 deletions(-) diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 9d6d5b081..b27c9e5d6 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -21,3 +21,4 @@ tokio = { version = "1.39.2", features = ["full"] } [build-dependencies] cynic-codegen = { version = "3.7.3" } + diff --git a/crates/sui-graphql-client/schema/graphql_rpc.graphql b/crates/sui-graphql-client/schema/graphql_rpc.graphql index cbde89c2c..177346bbe 100644 --- a/crates/sui-graphql-client/schema/graphql_rpc.graphql +++ b/crates/sui-graphql-client/schema/graphql_rpc.graphql @@ -97,9 +97,28 @@ type Address implements IOwner { suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! """ Similar behavior to the `transactionBlocks` in Query but supporting the additional - `AddressTransactionBlockRelationship` filter, which defaults to `SIGN`. + `AddressTransactionBlockRelationship` filter, which defaults to `SENT`. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, relation: AddressTransactionBlockRelationship, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type AddressConnection { @@ -141,14 +160,22 @@ type AddressOwner { } """ -The possible relationship types for a transaction block: sign, sent, received, or paid. +The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { """ - Transactions this address has signed either as a sender or as a sponsor. + Transactions this address has sent. NOTE: this input filter has been deprecated in favor of + `SENT` which behaves identically but is named more clearly. Both filters restrict + transactions by their sender, only, not signers in general. + + This filter will be removed after 1.36.0 (2024-10-14). """ SIGN """ + Transactions this address has sent. + """ + SENT + """ Transactions that sent objects to this address. """ RECV @@ -409,8 +436,25 @@ type Checkpoint { epoch: Epoch """ Transactions in this checkpoint. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this checkpoint. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type CheckpointConnection { @@ -517,8 +561,27 @@ type Coin implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -676,8 +739,27 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1093,8 +1175,25 @@ type Epoch { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The epoch's corresponding transaction blocks. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range consists of all transactions in this epoch. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } type Event { @@ -1173,7 +1272,13 @@ type EventEdge { } input EventFilter { + """ + Filter down to events from transactions sent by this address. + """ sender: SuiAddress + """ + Filter down to the events from this transaction (given by its transaction digest). + """ transactionDigest: String """ Events emitted by a particular module. An event is emitted by a @@ -1181,6 +1286,8 @@ input EventFilter { PTB and emits an event. Modules can be filtered by their package, or package::module. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ emittingModule: String """ @@ -1256,6 +1363,10 @@ enum Feature { validators either directly, or through system transactions. """ SYSTEM_STATE + """ + Named packages service (utilizing dotmove package registry). + """ + MOVE_REGISTRY } @@ -1429,7 +1540,7 @@ interface IObject { """ The transaction blocks that sent objects to this object. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -1972,8 +2083,27 @@ type MoveObject implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -2161,13 +2291,48 @@ type MovePackage implements IObject & IOwner { The transaction blocks that sent objects to this package. Note that objects that have been sent to a package become inaccessible. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the package's content. """ bcs: Base64 """ + Fetch another version of this package (the package that shares this package's original ID, + but has the specified `version`). + """ + packageAtVersion(version: Int!): MovePackage + """ + Fetch all versions of this package (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, filter: MovePackageVersionFilter): MovePackageConnection! + """ + Fetch the latest version of this package (the package with the highest `version` that shares + this packages's original ID) + """ + latestPackage: MovePackage! + """ A representation of the module called `name` in this package, including the structs and functions it defines. """ @@ -2191,6 +2356,22 @@ type MovePackage implements IObject & IOwner { moduleBcs: Base64 } +""" +Filter for paginating `MovePackage`s that were created within a range of checkpoints. +""" +input MovePackageCheckpointFilter { + """ + Fetch packages that were published strictly after this checkpoint. Omitting this fetches + packages published since genesis. + """ + afterCheckpoint: UInt53 + """ + Fetch packages that were published strictly before this checkpoint. Omitting this fetches + packages published up to the latest checkpoint (inclusive). + """ + beforeCheckpoint: UInt53 +} + type MovePackageConnection { """ Information to aid in pagination. @@ -2220,6 +2401,22 @@ type MovePackageEdge { cursor: String! } +""" +Filter for paginating versions of a given `MovePackage`. +""" +input MovePackageVersionFilter { + """ + Fetch versions of this package that are strictly newer than this version. Omitting this + fetches versions since the original version. + """ + afterVersion: UInt53 + """ + Fetch versions of this package that are strictly older than this version. Omitting this + fetches versions up to the latest version (inclusive). + """ + beforeVersion: UInt53 +} + """ Description of a struct type, defined in a Move module. """ @@ -2492,8 +2689,27 @@ type Object implements IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -2634,11 +2850,8 @@ objects are ones whose """ input ObjectFilter { """ - This field is used to specify the type of objects that should be included in the query - results. - - Objects can be filtered by their type's package, package::module, or their fully qualified - type name. + Filter objects by their type's `package`, `package::module`, or their fully qualified type + name. Generic types can be queried by either the generic type name, e.g. `0x2::coin::Coin`, or by the full type name, such as `0x2::coin::Coin<0x2::sui::SUI>`. @@ -3040,6 +3253,26 @@ type Query { """ object(address: SuiAddress!, version: UInt53): Object """ + The package corresponding to the given address (at the optionally given version). + + When no version is given, the package is loaded directly from the address given. Otherwise, + the address is translated before loading to point to the package whose original ID matches + the package at `address`, but whose version is `version`. For non-system packages, this + might result in a different address than `address` because different versions of a package, + introduced by upgrades, exist at distinct addresses. + + Note that this interpretation of `version` is different from a historical object read (the + interpretation of `version` for the `object` query). + """ + package(address: SuiAddress!, version: UInt53): MovePackage + """ + The latest version of the package at `address`. + + This corresponds to the package with the highest `version` that shares its original ID with + the package at `address`. + """ + latestPackage(address: SuiAddress!): MovePackage + """ Look-up an Account by its SuiAddress. """ address(address: SuiAddress!): Address @@ -3074,10 +3307,31 @@ type Query { checkpoints(first: Int, after: String, last: Int, before: String): CheckpointConnection! """ The transaction blocks that exist in the network. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ - The events that exist in the network. + Query events that are emitted in the network. + We currently do not support filtering by emitting module and event type + at the same time so if both are provided in one filter, the query will error. """ events(first: Int, after: String, last: Int, before: String, filter: EventFilter): EventConnection! """ @@ -3085,6 +3339,20 @@ type Query { """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): ObjectConnection! """ + The Move packages that exist in the network, optionally filtered to be strictly before + `beforeCheckpoint` and/or strictly after `afterCheckpoint`. + + This query returns all versions of a given user package that appear between the specified + checkpoints, but only records the latest versions of system packages. + """ + packages(first: Int, after: String, last: Int, before: String, filter: MovePackageCheckpointFilter): MovePackageConnection! + """ + Fetch all versions of package at `address` (packages that share this package's original ID), + optionally bounding the versions exclusively from below with `afterVersion`, or from above + with `beforeVersion`. + """ + packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ @@ -3094,6 +3362,14 @@ type Query { """ resolveSuinsAddress(domain: String!): Address """ + Fetch a package by its name (using dot move service) + """ + packageByName(name: String!): MovePackage + """ + Fetch a type that includes dot move service names in it. + """ + typeByName(name: String!): MoveType! + """ The coin metadata associated with the given coin type. """ coinMetadata(coinType: String!): CoinMetadata @@ -3205,10 +3481,6 @@ type ServiceConfig { """ isEnabled(feature: Feature!): Boolean! """ - List the available versions for this GraphQL service. - """ - availableVersions: [String!]! - """ List of all features that are enabled on this GraphQL service. """ enabledFeatures: [Feature!]! @@ -3258,7 +3530,18 @@ type ServiceConfig { """ requestTimeoutMs: Int! """ - Maximum length of a query payload string. + The maximum bytes allowed for the `txBytes` and `signatures` fields of the GraphQL mutation + `executeTransactionBlock` node, or for the `txBytes` of a `dryRunTransactionBlock`. + + It is the value of the maximum transaction bytes (including the signatures) allowed by the + protocol, plus the Base64 overhead (roughly 1/3 of the original string). + """ + maxTransactionPayloadSize: Int! + """ + The maximum bytes allowed for the JSON object in the request body of a GraphQL query, for + the read part of the query. + In case of mutations or dryRunTransactionBlocks the txBytes and signatures are not + included in this limit. """ maxQueryPayloadSize: Int! """ @@ -3279,6 +3562,14 @@ type ServiceConfig { Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. """ maxMoveValueDepth: Int! + """ + Maximum number of transaction ids that can be passed to a `TransactionBlockFilter`. + """ + maxTransactionIds: Int! + """ + Maximum number of candidates to scan when gathering a page of results. + """ + maxScanLimit: Int! } """ @@ -3496,8 +3787,27 @@ type StakedSui implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3698,8 +4008,27 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { storageRebate: BigInt """ The transaction blocks that sent objects to this object. + + `scanLimit` restricts the number of candidate transactions scanned when gathering a page of + results. It is required for queries that apply more than two complex filters (on function, + kind, sender, recipient, input object, changed object, or ids), and can be at most + `serviceConfig.maxScanLimit`. + + When the scan limit is reached the page will be returned even if it has fewer than `first` + results when paginating forward (`last` when paginating backwards). If there are more + transactions to scan, `pageInfo.hasNextPage` (or `pageInfo.hasPreviousPage`) will be set to + `true`, and `PageInfo.endCursor` (or `PageInfo.startCursor`) will be set to the last + transaction that was scanned as opposed to the last (or first) transaction in the page. + + Requesting the next (or previous) page after this cursor will resume the search, scanning + the next `scanLimit` many transactions in the direction of pagination, and so on until all + transactions in the scanning range have been visited. + + By default, the scanning range includes all transactions known to GraphQL, but it can be + restricted by the `after` and `before` cursors, and the `beforeCheckpoint`, + `afterCheckpoint` and `atCheckpoint` filters. """ - receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter): TransactionBlockConnection! + receivedTransactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! """ The Base64-encoded BCS serialization of the object's content. """ @@ -3970,18 +4299,54 @@ type TransactionBlockEffects { } input TransactionBlockFilter { + """ + Filter transactions by move function called. Calls can be filtered by the `package`, + `package::module`, or the `package::module::name` of their function. + """ function: String """ An input filter selecting for either system or programmable transactions. """ kind: TransactionBlockKindInput + """ + Limit to transactions that occured strictly after the given checkpoint. + """ afterCheckpoint: UInt53 + """ + Limit to transactions in the given checkpoint. + """ atCheckpoint: UInt53 + """ + Limit to transaction that occured strictly before the given checkpoint. + """ beforeCheckpoint: UInt53 + """ + Limit to transactions that were sent by the given address. NOTE: this input filter has been + deprecated in favor of `sentAddress` which behaves identically but is named more clearly. + Both filters restrict transactions by their sender, only, not signers in general. + + This filter will be removed after 1.36.0 (2024-10-14). + """ signAddress: SuiAddress + """ + Limit to transactions that were sent by the given address. + """ + sentAddress: SuiAddress + """ + Limit to transactions that sent an object to the given address. + """ recvAddress: SuiAddress + """ + Limit to transactions that accepted the given object as an input. + """ inputObject: SuiAddress + """ + Limit to transactions that output a versioon of this object. + """ changedObject: SuiAddress + """ + Select transactions by their digest. + """ transactionIds: [String!] } diff --git a/crates/sui-graphql-client/src/query_types/service_config.rs b/crates/sui-graphql-client/src/query_types/service_config.rs index 4265c27c1..facc09565 100644 --- a/crates/sui-graphql-client/src/query_types/service_config.rs +++ b/crates/sui-graphql-client/src/query_types/service_config.rs @@ -21,8 +21,6 @@ pub struct ServiceConfigQuery { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ServiceConfig")] pub struct ServiceConfig { - /// List the available versions for this GraphQL service. - pub available_versions: Vec, /// Default number of elements allowed on a single page of a connection. pub default_page_size: i32, /// List of all features that are enabled on this RPC service. @@ -80,4 +78,5 @@ pub enum Feature { NameService, Subscriptions, SystemState, + MoveRegistry, } From 91f2aa79e59e6356078e58866fddd92ab3e87760 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:22:26 -0700 Subject: [PATCH 004/107] graphql: add a few tests (#8) --- crates/sui-graphql-client/src/lib.rs | 180 ++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 3 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 54b3d2807..6f3120823 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -221,7 +221,7 @@ impl Client { seq_num: Option, ) -> Result, Error> { ensure!( - digest.is_some() != seq_num.is_some(), + !(digest.is_some() && seq_num.is_some()), "Either digest or seq_num must be provided" ); @@ -496,7 +496,11 @@ impl Client { .data .and_then(|tbq| tbq.transaction_block) .and_then(|tb| tb.bcs) - .map(|bcs| bcs::from_bytes::(bcs.0.as_bytes()).unwrap()); + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 transaction bcs bytes: {e}")))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose()?; Ok(signed_tx) } @@ -539,7 +543,11 @@ impl Client { .iter() .map(|tx| bcs::from_bytes::(tx)) .collect::, bcs::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}")))?; + .map_err(|e| { + Error::msg(format!( + "Cannot decode bcs bytes into SignedTransaction: {e}" + )) + })?; let page = Page::new(page_info, transactions); Ok(Some(page)) } else { @@ -551,6 +559,7 @@ impl Client { #[cfg(test)] mod tests { use crate::{Client, DEFAULT_LOCAL_HOST, DEVNET_HOST, MAINNET_HOST, TESTNET_HOST}; + const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; #[test] fn test_rpc_server() { @@ -566,4 +575,169 @@ mod tests { assert!(client.set_rpc_server("localhost:9125/graphql").is_ok()); assert!(client.set_rpc_server("9125/graphql").is_err()); } + + #[tokio::test] + async fn test_chain_id() { + for (n, id) in NETWORKS.iter() { + let client = Client::new(n).unwrap(); + let chain_id = client.chain_id().await; + assert!(chain_id.is_ok()); + assert_eq!(&chain_id.unwrap(), id); + } + } + + #[tokio::test] + async fn test_reference_gas_price_query() { + for (n, _) in NETWORKS.iter() { + let client = Client::new(n).unwrap(); + let rgp = client.reference_gas_price(None).await; + assert!( + rgp.is_ok(), + "Reference gas price query failed for network: {n}" + ); + } + } + + #[tokio::test] + async fn test_protocol_config_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let pc = client.protocol_config(None).await; + assert!(pc.is_ok()); + + // test specific version + let pc = client.protocol_config(Some(50)).await; + assert!(pc.is_ok()); + let pc = pc.unwrap(); + if let Some(pc) = pc { + assert_eq!( + pc.protocol_version.0, 50, + "Protocol version query mismatch for network: {n}. Expected: 50, received: {}", + pc.protocol_version.0 + ); + } + } + } + + #[tokio::test] + async fn test_service_config_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let sc = client.service_config().await; + assert!(sc.is_ok(), "Service config query failed for network: {n}"); + } + } + + #[tokio::test] + async fn test_coin_metadata_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let cm = client.coin_metadata("0x2::sui::SUI").await; + assert!(cm.is_ok(), "Coin metadata query failed for network: {n}"); + } + } + + #[tokio::test] + async fn test_checkpoint_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let c = client.checkpoint(None, None).await; + assert!( + c.is_ok(), + "Checkpoint query failed for network: {n}. Error: {}", + c.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_latest_checkpoint_sequence_number_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let last_checkpoint = client.latest_checkpoint_sequence_number().await; + assert!( + last_checkpoint.is_ok(), + "Latest checkpoint sequence number query failed for network: {n}. Error: {}", + last_checkpoint.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_epoch_total_checkpoints_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let e = client.epoch_total_checkpoints(None).await; + assert!( + e.is_ok(), + "Epoch total checkpoints query failed for network: {n}. Error: {}", + e.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_epoch_total_transaction_blocks_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let e = client.epoch_total_transaction_blocks(None).await; + assert!( + e.is_ok(), + "Epoch total transaction blocks query failed for network: {n}. Error: {}", + e.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_epoch_summary_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let e = client.epoch_summary(None).await; + assert!( + e.is_ok(), + "Epoch summary query failed for network: {n}. Error: {}", + e.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_objects_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let objects = client.objects(None, None, None, None, None).await; + assert!( + objects.is_ok(), + "Objects query failed for network: {n}. Error: {}", + objects.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_object_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let object = client.object("0x5".parse().unwrap(), None).await; + assert!( + object.is_ok(), + "Object query failed for network: {n}. Error: {}", + object.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_object_bcs_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let object_bcs = client.object_bcs("0x5".parse().unwrap()).await; + assert!( + object_bcs.is_ok(), + "Object bcs query failed for network: {n}. Error: {}", + object_bcs.unwrap_err() + ); + } + } } From e7c624a5ddb004f245f6a7812f41ceaacd6f49fe Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 24 Sep 2024 19:40:32 -0500 Subject: [PATCH 005/107] coin: use Cow for coin_type --- crates/iota-rust-sdk/src/types/framework.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/iota-rust-sdk/src/types/framework.rs b/crates/iota-rust-sdk/src/types/framework.rs index 0e9fc4dda..709830246 100644 --- a/crates/iota-rust-sdk/src/types/framework.rs +++ b/crates/iota-rust-sdk/src/types/framework.rs @@ -1,17 +1,19 @@ //! Rust definitions of move/iota framework types. +use std::borrow::Cow; + use super::{Object, ObjectId, TypeTag}; #[derive(Debug, Clone)] pub struct Coin<'a> { - coin_type: &'a TypeTag, + coin_type: Cow<'a, TypeTag>, id: ObjectId, balance: u64, } impl<'a> Coin<'a> { - pub fn coin_type(&self) -> &'a TypeTag { - self.coin_type + pub fn coin_type(&self) -> &TypeTag { + &self.coin_type } pub fn id(&self) -> &ObjectId { @@ -37,7 +39,7 @@ impl<'a> Coin<'a> { u64::from_le_bytes((&contents[ObjectId::LENGTH..]).try_into().unwrap()); Some(Self { - coin_type, + coin_type: Cow::Borrowed(coin_type), id, balance, }) @@ -45,4 +47,12 @@ impl<'a> Coin<'a> { _ => None, // package } } + + pub fn into_owned(self) -> Coin<'static> { + Coin { + coin_type: Cow::Owned(self.coin_type.into_owned()), + id: self.id, + balance: self.balance, + } + } } From 24937c8185492e34bdae80c28ade3b0a4d675224 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 08:22:22 -0500 Subject: [PATCH 006/107] signature: add bytes and base64 to/from methods for UserSignature --- .../src/types/crypto/multisig.rs | 38 ++++-- .../iota-rust-sdk/src/types/crypto/passkey.rs | 43 ++++-- .../src/types/crypto/signature.rs | 128 +++++++++++++----- .../iota-rust-sdk/src/types/crypto/zklogin.rs | 33 +++-- 4 files changed, 174 insertions(+), 68 deletions(-) diff --git a/crates/iota-rust-sdk/src/types/crypto/multisig.rs b/crates/iota-rust-sdk/src/types/crypto/multisig.rs index ea3838231..adf76bedd 100644 --- a/crates/iota-rust-sdk/src/types/crypto/multisig.rs +++ b/crates/iota-rust-sdk/src/types/crypto/multisig.rs @@ -178,16 +178,8 @@ mod serialization { }; readable.serialize(serializer) } else { - let mut buf = Vec::new(); - buf.push(SignatureScheme::Multisig as u8); - - let multisig = MultisigRef { - signatures: &self.signatures, - bitmap: self.bitmap, - committee: &self.committee, - }; - bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail"); - serializer.serialize_bytes(&buf) + let bytes = self.to_bytes(); + serializer.serialize_bytes(&bytes) } } } @@ -212,6 +204,32 @@ mod serialization { } impl MultisigAggregatedSignature { + pub(crate) fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + buf.push(SignatureScheme::Multisig as u8); + + if let Some(bitmap) = &self.legacy_bitmap { + let legacy = LegacyMultisigRef { + signatures: &self.signatures, + bitmap, + committee: LegacyMultisigCommitteeRef { + members: &self.committee.members, + threshold: self.committee.threshold, + }, + }; + + bcs::serialize_into(&mut buf, &legacy).expect("serialization cannot fail"); + } else { + let multisig = MultisigRef { + signatures: &self.signatures, + bitmap: self.bitmap, + committee: &self.committee, + }; + bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail"); + } + buf + } + pub fn from_serialized_bytes( bytes: impl AsRef<[u8]>, ) -> Result { diff --git a/crates/iota-rust-sdk/src/types/crypto/passkey.rs b/crates/iota-rust-sdk/src/types/crypto/passkey.rs index 749faf6f0..7df6b83a7 100644 --- a/crates/iota-rust-sdk/src/types/crypto/passkey.rs +++ b/crates/iota-rust-sdk/src/types/crypto/passkey.rs @@ -87,24 +87,20 @@ mod serialization { where S: Serializer, { - let authenticator_ref = AuthenticatorRef { - authenticator_data: &self.authenticator_data, - client_data_json: &self.client_data_json, - signature: SimpleSignature::Secp256r1 { - signature: self.signature, - public_key: self.public_key, - }, - }; - if serializer.is_human_readable() { + let authenticator_ref = AuthenticatorRef { + authenticator_data: &self.authenticator_data, + client_data_json: &self.client_data_json, + signature: SimpleSignature::Secp256r1 { + signature: self.signature, + public_key: self.public_key, + }, + }; + authenticator_ref.serialize(serializer) } else { - let mut buf = Vec::new(); - buf.push(SignatureScheme::Passkey as u8); - - bcs::serialize_into(&mut buf, &authenticator_ref) - .expect("serialization cannot fail"); - serializer.serialize_bytes(&buf) + let bytes = self.to_bytes(); + serializer.serialize_bytes(&bytes) } } } @@ -195,6 +191,23 @@ mod serialization { Self::try_from_raw(authenticator) } + + pub(crate) fn to_bytes(&self) -> Vec { + let authenticator_ref = AuthenticatorRef { + authenticator_data: &self.authenticator_data, + client_data_json: &self.client_data_json, + signature: SimpleSignature::Secp256r1 { + signature: self.signature, + public_key: self.public_key, + }, + }; + + let mut buf = Vec::new(); + buf.push(SignatureScheme::Passkey as u8); + + bcs::serialize_into(&mut buf, &authenticator_ref).expect("serialization cannot fail"); + buf + } } /// The client data represents the contextual bindings of both the Relying diff --git a/crates/iota-rust-sdk/src/types/crypto/signature.rs b/crates/iota-rust-sdk/src/types/crypto/signature.rs index dac9948cc..966328cfa 100644 --- a/crates/iota-rust-sdk/src/types/crypto/signature.rs +++ b/crates/iota-rust-sdk/src/types/crypto/signature.rs @@ -146,6 +146,38 @@ mod serialization { use crate::types::crypto::SignatureFromBytesError; impl SimpleSignature { + fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + match self { + SimpleSignature::Ed25519 { + signature, + public_key, + } => { + buf.push(SignatureScheme::Ed25519 as u8); + buf.extend_from_slice(signature.as_ref()); + buf.extend_from_slice(public_key.as_ref()); + } + SimpleSignature::Secp256k1 { + signature, + public_key, + } => { + buf.push(SignatureScheme::Secp256k1 as u8); + buf.extend_from_slice(signature.as_ref()); + buf.extend_from_slice(public_key.as_ref()); + } + SimpleSignature::Secp256r1 { + signature, + public_key, + } => { + buf.push(SignatureScheme::Secp256r1 as u8); + buf.extend_from_slice(signature.as_ref()); + buf.extend_from_slice(public_key.as_ref()); + } + } + + buf + } + pub fn from_serialized_bytes( bytes: impl AsRef<[u8]>, ) -> Result { @@ -373,6 +405,69 @@ mod serialization { } } + impl UserSignature { + pub fn to_bytes(&self) -> Vec { + match self { + UserSignature::Simple(s) => s.to_bytes(), + UserSignature::Multisig(m) => m.to_bytes(), + UserSignature::ZkLogin(z) => z.to_bytes(), + UserSignature::Passkey(p) => p.to_bytes(), + } + } + + pub fn to_base64(&self) -> String { + use base64ct::Encoding; + + base64ct::Base64::encode_string(&self.to_bytes()) + } + + fn from_serialized_bytes(bytes: impl AsRef<[u8]>) -> Result { + let bytes = bytes.as_ref(); + + let flag = SignatureScheme::from_byte( + *bytes + .first() + .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?, + ) + .map_err(serde::de::Error::custom)?; + match flag { + SignatureScheme::Ed25519 + | SignatureScheme::Secp256k1 + | SignatureScheme::Secp256r1 => { + let simple = SimpleSignature::from_serialized_bytes(bytes)?; + Ok(Self::Simple(simple)) + } + SignatureScheme::Multisig => { + let multisig = MultisigAggregatedSignature::from_serialized_bytes(bytes)?; + Ok(Self::Multisig(multisig)) + } + SignatureScheme::Bls12381 => Err(serde::de::Error::custom( + "bls not supported for user signatures", + )), + SignatureScheme::ZkLogin => { + let zklogin = ZkLoginAuthenticator::from_serialized_bytes(bytes)?; + Ok(Self::ZkLogin(Box::new(zklogin))) + } + SignatureScheme::Passkey => { + let passkey = PasskeyAuthenticator::from_serialized_bytes(bytes)?; + Ok(Self::Passkey(passkey)) + } + } + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + Self::from_serialized_bytes(bytes) + } + + pub fn from_base64(s: &str) -> Result { + use base64ct::Encoding; + use serde::de::Error; + + let bytes = base64ct::Base64::decode_vec(s).map_err(bcs::Error::custom)?; + Self::from_bytes(&bytes) + } + } + #[derive(serde_derive::Serialize)] #[serde(tag = "scheme", rename_all = "lowercase")] enum ReadableUserSignatureRef<'a> { @@ -510,38 +605,7 @@ mod serialization { let bytes: std::borrow::Cow<'de, [u8]> = serde_with::Bytes::deserialize_as(deserializer)?; - let flag = - SignatureScheme::from_byte(*bytes.first().ok_or_else(|| { - serde::de::Error::custom("missing signature scheme flag") - })?) - .map_err(serde::de::Error::custom)?; - match flag { - SignatureScheme::Ed25519 - | SignatureScheme::Secp256k1 - | SignatureScheme::Secp256r1 => { - let simple = SimpleSignature::from_serialized_bytes(bytes) - .map_err(serde::de::Error::custom)?; - Ok(Self::Simple(simple)) - } - SignatureScheme::Multisig => { - let multisig = MultisigAggregatedSignature::from_serialized_bytes(bytes) - .map_err(serde::de::Error::custom)?; - Ok(Self::Multisig(multisig)) - } - SignatureScheme::Bls12381 => Err(serde::de::Error::custom( - "bls not supported for user signatures", - )), - SignatureScheme::ZkLogin => { - let zklogin = ZkLoginAuthenticator::from_serialized_bytes(bytes) - .map_err(serde::de::Error::custom)?; - Ok(Self::ZkLogin(Box::new(zklogin))) - } - SignatureScheme::Passkey => { - let passkey = PasskeyAuthenticator::from_serialized_bytes(bytes) - .map_err(serde::de::Error::custom)?; - Ok(Self::Passkey(passkey)) - } - } + Self::from_serialized_bytes(bytes) } } } diff --git a/crates/iota-rust-sdk/src/types/crypto/zklogin.rs b/crates/iota-rust-sdk/src/types/crypto/zklogin.rs index 88820fbaf..aa5746dc8 100644 --- a/crates/iota-rust-sdk/src/types/crypto/zklogin.rs +++ b/crates/iota-rust-sdk/src/types/crypto/zklogin.rs @@ -346,20 +346,17 @@ mod serialization { where S: Serializer, { - let authenticator_ref = AuthenticatorRef { - inputs: &self.inputs, - max_epoch: self.max_epoch, - signature: &self.signature, - }; if serializer.is_human_readable() { + let authenticator_ref = AuthenticatorRef { + inputs: &self.inputs, + max_epoch: self.max_epoch, + signature: &self.signature, + }; + authenticator_ref.serialize(serializer) } else { - let mut buf = Vec::new(); - buf.push(SignatureScheme::ZkLogin as u8); - - bcs::serialize_into(&mut buf, &authenticator_ref) - .expect("serialization cannot fail"); - serializer.serialize_bytes(&buf) + let bytes = self.to_bytes(); + serializer.serialize_bytes(&bytes) } } } @@ -388,6 +385,20 @@ mod serialization { } impl ZkLoginAuthenticator { + pub(crate) fn to_bytes(&self) -> Vec { + let authenticator_ref = AuthenticatorRef { + inputs: &self.inputs, + max_epoch: self.max_epoch, + signature: &self.signature, + }; + + let mut buf = Vec::new(); + buf.push(SignatureScheme::ZkLogin as u8); + + bcs::serialize_into(&mut buf, &authenticator_ref).expect("serialization cannot fail"); + buf + } + pub fn from_serialized_bytes( bytes: impl AsRef<[u8]>, ) -> Result { From 19defd57a3cb17c0c2e9639a4d87978d8c24fc46 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 09:37:42 -0500 Subject: [PATCH 007/107] types: introduce Intent types --- .../iota-rust-sdk/src/types/crypto/intent.rs | 73 +++++++++++++++++++ crates/iota-rust-sdk/src/types/crypto/mod.rs | 2 + crates/iota-rust-sdk/src/types/mod.rs | 15 ++-- .../src/types/transaction/serialization.rs | 33 +-------- 4 files changed, 85 insertions(+), 38 deletions(-) create mode 100644 crates/iota-rust-sdk/src/types/crypto/intent.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/intent.rs b/crates/iota-rust-sdk/src/types/crypto/intent.rs new file mode 100644 index 000000000..110a31ba0 --- /dev/null +++ b/crates/iota-rust-sdk/src/types/crypto/intent.rs @@ -0,0 +1,73 @@ +/// A Signing Intent +/// +/// An intent is a compact struct serves as the domain separator for a message that a signature +/// commits to. It consists of three parts: +/// 1. [enum IntentScope] (what the type of the message is) +/// 2. [enum IntentVersion] +/// 3. [enum AppId] (what application that the signature refers to). +/// +/// The serialization of an Intent is a 3-byte array where each field is represented by a byte and +/// it is prepended onto a message before it is signed in Sui. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Intent { + pub scope: IntentScope, + pub version: IntentVersion, + pub app_id: IntentAppId, +} + +impl Intent { + pub fn new(scope: IntentScope, version: IntentVersion, app_id: IntentAppId) -> Self { + Self { + scope, + version, + app_id, + } + } + + pub fn to_bytes(self) -> [u8; 3] { + [self.scope as u8, self.version as u8, self.app_id as u8] + } + + pub fn scope(self) -> IntentScope { + self.scope + } + + pub fn version(self) -> IntentVersion { + self.version + } + + pub fn app_id(self) -> IntentAppId { + self.app_id + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +#[non_exhaustive] +pub enum IntentScope { + TransactionData = 0, // Used for a user signature on a transaction data. + TransactionEffects = 1, // Used for an authority signature on transaction effects. + CheckpointSummary = 2, // Used for an authority signature on a checkpoint summary. + PersonalMessage = 3, // Used for a user signature on a personal message. + SenderSignedTransaction = 4, // Used for an authority signature on a user signed transaction. + ProofOfPossession = 5, // Used as a signature representing an authority's proof of possession of its authority protocol key. + HeaderDigest = 6, // Used for narwhal authority signature on header digest. + BridgeEventUnused = 7, // for bridge purposes but it's currently not included in messages. + ConsensusBlock = 8, // Used for consensus authority signature on block's digest +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +#[non_exhaustive] +pub enum IntentVersion { + V0 = 0, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +#[non_exhaustive] +pub enum IntentAppId { + Sui = 0, + Narwhal = 1, + Consensus = 2, +} diff --git a/crates/iota-rust-sdk/src/types/crypto/mod.rs b/crates/iota-rust-sdk/src/types/crypto/mod.rs index 6485fd56b..af9a6dde1 100644 --- a/crates/iota-rust-sdk/src/types/crypto/mod.rs +++ b/crates/iota-rust-sdk/src/types/crypto/mod.rs @@ -1,5 +1,6 @@ mod bls12381; mod ed25519; +mod intent; mod multisig; mod passkey; mod secp256k1; @@ -10,6 +11,7 @@ mod zklogin; pub use bls12381::{Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature}; pub use ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}; +pub use intent::{Intent, IntentAppId, IntentScope, IntentVersion}; pub use multisig::{ MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, MultisigMemberSignature, diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/iota-rust-sdk/src/types/mod.rs index 07b9f7c09..a30fcb24d 100644 --- a/crates/iota-rust-sdk/src/types/mod.rs +++ b/crates/iota-rust-sdk/src/types/mod.rs @@ -21,13 +21,14 @@ pub use checkpoint::{ }; pub use crypto::{ Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, - CircomG2, Claim, Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, Jwk, JwkId, JwtDetails, - MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, - MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PrivateKey, - Secp256k1PublicKey, Secp256k1Signature, Secp256r1PrivateKey, Secp256r1PublicKey, - Secp256r1Signature, SignatureScheme, SimpleSignature, UserSignature, - ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, - ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, + CircomG2, Claim, Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, + IntentScope, IntentVersion, Jwk, JwkId, JwtDetails, MultisigAggregatedSignature, + MultisigCommittee, MultisigMember, MultisigMemberPublicKey, MultisigMemberSignature, + PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PrivateKey, Secp256k1PublicKey, + Secp256k1Signature, Secp256r1PrivateKey, Secp256r1PublicKey, Secp256r1Signature, + SignatureScheme, SimpleSignature, UserSignature, ValidatorAggregatedSignature, + ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, ZkLoginAuthenticator, + ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, }; pub use digest::{ CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, diff --git a/crates/iota-rust-sdk/src/types/transaction/serialization.rs b/crates/iota-rust-sdk/src/types/transaction/serialization.rs index 13c12f25f..34afa58c5 100644 --- a/crates/iota-rust-sdk/src/types/transaction/serialization.rs +++ b/crates/iota-rust-sdk/src/types/transaction/serialization.rs @@ -912,37 +912,8 @@ mod signed_transaction { transaction::{SignedTransaction, Transaction}, }; - /// Intents are defined as: - /// - /// ``` - /// struct Intent { - /// scope: IntentScope, - /// version: IntentVersion, - /// app_id: AppId, - /// } - /// - /// enum IntentVersion { - /// V0 = 0, - /// } - /// - /// enum AppId { - /// Iota = 0, - /// Narwhal = 1, - /// Consensus = 2, - /// } - /// - /// enum IntentScope { - /// TransactionData = 0, // Used for a user signature on a transaction data. - /// TransactionEffects = 1, // Used for an authority signature on transaction effects. - /// CheckpointSummary = 2, // Used for an authority signature on a checkpoint summary. - /// PersonalMessage = 3, // Used for a user signature on a personal message. - /// SenderSignedTransaction = 4, // Used for an authority signature on a user signed transaction. - /// ProofOfPossession = 5, // Used as a signature representing an authority's proof of possession of its authority protocol key. - /// HeaderDigest = 6, // Used for narwhal authority signature on header digest. - /// BridgeEventUnused = 7, // for bridge purposes but it's currently not included in messages. - /// ConsensusBlock = 8, // Used for consensus authority signature on block's digest - /// } - /// ``` + /// Serde implementation that serializes a transaction prefixed with the + /// signing intent. See [struct Intent] for more info. /// /// So we need to serialize Transaction as (0, 0, 0, Transaction) struct IntentMessageWrappedTransaction; From e7277fe6e69319894bbd0aad36da45599ffca6df Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 09:50:00 -0500 Subject: [PATCH 008/107] types: introduce PersonalMessage --- crates/iota-rust-sdk/src/types/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/iota-rust-sdk/src/types/mod.rs index a30fcb24d..0bec0cb99 100644 --- a/crates/iota-rust-sdk/src/types/mod.rs +++ b/crates/iota-rust-sdk/src/types/mod.rs @@ -67,3 +67,6 @@ pub use type_tag::{Identifier, StructTag, TypeParseError, TypeTag}; #[cfg(test)] mod serialization_proptests; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PersonalMessage<'a>(pub std::borrow::Cow<'a, [u8]>); From cf6a0d22bddb24d0e65a7959630762ca2125dcab Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 09:53:31 -0500 Subject: [PATCH 009/107] hash: add methods for getting the signing digest Add methods for Transaction and PersonalMessage for calculating the signing digest which is used as the message to sign against in Sui. --- crates/iota-rust-sdk/src/hash.rs | 43 ++++++++++++++++++++++++ crates/iota-rust-sdk/src/types/digest.rs | 3 ++ crates/iota-rust-sdk/src/types/mod.rs | 4 +-- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/crates/iota-rust-sdk/src/hash.rs b/crates/iota-rust-sdk/src/hash.rs index 2c3722b12..c8fb7412f 100644 --- a/crates/iota-rust-sdk/src/hash.rs +++ b/crates/iota-rust-sdk/src/hash.rs @@ -225,6 +225,49 @@ mod type_digest { } } +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +mod signing_message { + use crate::{ + hash::Hasher, + types::{ + Digest, Intent, IntentAppId, IntentScope, IntentVersion, PersonalMessage, + SigningDigest, Transaction, + }, + }; + + impl Transaction { + pub fn signing_digest(&self) -> SigningDigest { + const INTENT: Intent = Intent { + scope: IntentScope::TransactionData, + version: IntentVersion::V0, + app_id: IntentAppId::Sui, + }; + let digest = signing_digest(INTENT, self); + digest.into_inner() + } + } + + fn signing_digest(intent: Intent, ty: &T) -> Digest { + let mut hasher = Hasher::new(); + hasher.update(intent.to_bytes()); + bcs::serialize_into(&mut hasher, ty).unwrap(); + hasher.finalize() + } + + impl<'a> PersonalMessage<'a> { + pub fn signing_digest(&self) -> SigningDigest { + const INTENT: Intent = Intent { + scope: IntentScope::PersonalMessage, + version: IntentVersion::V0, + app_id: IntentAppId::Sui, + }; + let digest = signing_digest(INTENT, &self.0); + digest.into_inner() + } + } +} + /// A 1-byte domain separator for hashing Object ID in IOTA. It is starting from /// 0xf0 to ensure no hashing collision for any ObjectId vs Address which is /// derived as the hash of `flag || pubkey`. diff --git a/crates/iota-rust-sdk/src/types/digest.rs b/crates/iota-rust-sdk/src/types/digest.rs index 927f695c1..58383b5cb 100644 --- a/crates/iota-rust-sdk/src/types/digest.rs +++ b/crates/iota-rust-sdk/src/types/digest.rs @@ -315,6 +315,9 @@ impl_digest!(ObjectDigest); impl_digest!(ConsensusCommitDigest); impl_digest!(EffectsAuxiliaryDataDigest); +// Don't implement like the other digest types since this isn't intended to be serialized +pub type SigningDigest = [u8; Digest::LENGTH]; + #[cfg(test)] mod test { use test_strategy::proptest; diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/iota-rust-sdk/src/types/mod.rs index 0bec0cb99..823675071 100644 --- a/crates/iota-rust-sdk/src/types/mod.rs +++ b/crates/iota-rust-sdk/src/types/mod.rs @@ -32,8 +32,8 @@ pub use crypto::{ }; pub use digest::{ CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, - EffectsAuxiliaryDataDigest, ObjectDigest, TransactionDigest, TransactionEffectsDigest, - TransactionEventsDigest, + EffectsAuxiliaryDataDigest, ObjectDigest, SigningDigest, TransactionDigest, + TransactionEffectsDigest, TransactionEventsDigest, }; pub use effects::{ ChangedObject, EffectsObjectChange, IdOperation, ObjectIn, ObjectOut, TransactionEffects, From 2314407c3ddaf7f00a8bbf213c27c45db707e46e Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 10:02:41 -0500 Subject: [PATCH 010/107] crypto: remove unused private key types --- .../iota-rust-sdk/src/types/crypto/bls12381.rs | 18 ------------------ .../iota-rust-sdk/src/types/crypto/ed25519.rs | 18 ------------------ crates/iota-rust-sdk/src/types/crypto/mod.rs | 8 ++++---- .../src/types/crypto/secp256k1.rs | 18 ------------------ crates/iota-rust-sdk/src/types/mod.rs | 11 +++++------ 5 files changed, 9 insertions(+), 64 deletions(-) diff --git a/crates/iota-rust-sdk/src/types/crypto/bls12381.rs b/crates/iota-rust-sdk/src/types/crypto/bls12381.rs index 01a7acc45..5da099849 100644 --- a/crates/iota-rust-sdk/src/types/crypto/bls12381.rs +++ b/crates/iota-rust-sdk/src/types/crypto/bls12381.rs @@ -1,23 +1,5 @@ //! Implementation of bls12381 min-sig public-key cryptogrophy. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -pub struct Bls12381PrivateKey( - #[cfg_attr( - feature = "serde", - serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") - )] - [u8; Self::LENGTH], -); - -impl Bls12381PrivateKey { - /// The length of an bls12381 private key in bytes. - pub const LENGTH: usize = 32; -} - #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", diff --git a/crates/iota-rust-sdk/src/types/crypto/ed25519.rs b/crates/iota-rust-sdk/src/types/crypto/ed25519.rs index 2cc478e99..9d42f868a 100644 --- a/crates/iota-rust-sdk/src/types/crypto/ed25519.rs +++ b/crates/iota-rust-sdk/src/types/crypto/ed25519.rs @@ -1,23 +1,5 @@ //! Implementation of ed25519 public-key cryptogrophy. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -pub struct Ed25519PrivateKey( - #[cfg_attr( - feature = "serde", - serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") - )] - [u8; Self::LENGTH], -); - -impl Ed25519PrivateKey { - /// The length of an ed25519 private key in bytes. - pub const LENGTH: usize = 32; -} - #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", diff --git a/crates/iota-rust-sdk/src/types/crypto/mod.rs b/crates/iota-rust-sdk/src/types/crypto/mod.rs index af9a6dde1..52561bf17 100644 --- a/crates/iota-rust-sdk/src/types/crypto/mod.rs +++ b/crates/iota-rust-sdk/src/types/crypto/mod.rs @@ -9,16 +9,16 @@ mod signature; mod validator; mod zklogin; -pub use bls12381::{Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature}; -pub use ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}; +pub use bls12381::{Bls12381PublicKey, Bls12381Signature}; +pub use ed25519::{Ed25519PublicKey, Ed25519Signature}; pub use intent::{Intent, IntentAppId, IntentScope, IntentVersion}; pub use multisig::{ MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, MultisigMemberSignature, }; pub use passkey::{PasskeyAuthenticator, PasskeyPublicKey}; -pub use secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey, Secp256k1Signature}; -pub use secp256r1::{Secp256r1PrivateKey, Secp256r1PublicKey, Secp256r1Signature}; +pub use secp256k1::{Secp256k1PublicKey, Secp256k1Signature}; +pub use secp256r1::{Secp256r1PublicKey, Secp256r1Signature}; pub use signature::{SignatureScheme, SimpleSignature, UserSignature}; pub use validator::{ ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, diff --git a/crates/iota-rust-sdk/src/types/crypto/secp256k1.rs b/crates/iota-rust-sdk/src/types/crypto/secp256k1.rs index b01422b27..928ffff70 100644 --- a/crates/iota-rust-sdk/src/types/crypto/secp256k1.rs +++ b/crates/iota-rust-sdk/src/types/crypto/secp256k1.rs @@ -1,23 +1,5 @@ //! Implementation of secp256k1 public-key cryptogrophy. -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -pub struct Secp256k1PrivateKey( - #[cfg_attr( - feature = "serde", - serde(with = "::serde_with::As::<::serde_with::IfIsHumanReadable>") - )] - [u8; Self::LENGTH], -); - -impl Secp256k1PrivateKey { - /// The length of an secp256k1 private key in bytes. - pub const LENGTH: usize = 32; -} - #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/iota-rust-sdk/src/types/mod.rs index 823675071..ab33d7c84 100644 --- a/crates/iota-rust-sdk/src/types/mod.rs +++ b/crates/iota-rust-sdk/src/types/mod.rs @@ -20,12 +20,11 @@ pub use checkpoint::{ EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit, }; pub use crypto::{ - Bls12381PrivateKey, Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, - CircomG2, Claim, Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, - IntentScope, IntentVersion, Jwk, JwkId, JwtDetails, MultisigAggregatedSignature, - MultisigCommittee, MultisigMember, MultisigMemberPublicKey, MultisigMemberSignature, - PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PrivateKey, Secp256k1PublicKey, - Secp256k1Signature, Secp256r1PrivateKey, Secp256r1PublicKey, Secp256r1Signature, + Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, + Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, + JwkId, JwtDetails, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, + MultisigMemberPublicKey, MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, + Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, From 8919b3c8399eed9ba1c46be0c8e353e745a4a0a1 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 10:56:19 -0500 Subject: [PATCH 011/107] sui-sdk-crypto: introduce crate to handle signing and verifying --- Makefile | 6 +- crates/sui-sdk-crypto/Cargo.toml | 55 ++++++ crates/sui-sdk-crypto/Makefile | 23 +++ crates/sui-sdk-crypto/src/bls12381.rs | 7 + crates/sui-sdk-crypto/src/ed25519.rs | 236 +++++++++++++++++++++++++ crates/sui-sdk-crypto/src/lib.rs | 41 +++++ crates/sui-sdk-crypto/src/secp256k1.rs | 7 + crates/sui-sdk-crypto/src/secp256r1.rs | 7 + 8 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 crates/sui-sdk-crypto/Cargo.toml create mode 100644 crates/sui-sdk-crypto/Makefile create mode 100644 crates/sui-sdk-crypto/src/bls12381.rs create mode 100644 crates/sui-sdk-crypto/src/ed25519.rs create mode 100644 crates/sui-sdk-crypto/src/lib.rs create mode 100644 crates/sui-sdk-crypto/src/secp256k1.rs create mode 100644 crates/sui-sdk-crypto/src/secp256r1.rs diff --git a/Makefile b/Makefile index 778d3e444..88af67e49 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ all:: ci .PHONY: check-features check-features: - $(MAKE) -C crates/iota-rust-sdk check-features + $(MAKE) -C crates/iota-sdk check-features + $(MAKE) -C crates/iota-sdk-crypto check-features .PHONY: check-fmt check-fmt: @@ -21,7 +22,8 @@ test: .PHONY: wasm wasm: - $(MAKE) -C crates/iota-rust-sdk wasm + $(MAKE) -C crates/iota-sdk wasm + $(MAKE) -C crates/iota-sdk-crypto wasm .PHONY: doc doc: diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml new file mode 100644 index 000000000..1e2eefe2b --- /dev/null +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "sui-sdk-crypto" +version = "0.0.0" +authors = ["Brandon Williams "] +license = "Apache-2.0" +edition = "2021" +publish = false + +[package.metadata.docs.rs] +# To build locally: +# RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps --open +all-features = true +rustdoc-args = [ + # Enable doc_cfg showing the required features. + "--cfg=doc_cfg", + + # Generate links to definition in rustdoc source code pages + # https://github.com/rust-lang/rust/pull/84176 + "-Zunstable-options", "--generate-link-to-definition" +] + +[features] +default = [] +rand = ["dep:rand_core"] +ed25519 = ["dep:ed25519-dalek"] + +[dependencies] +base64ct = { version = "1.6.0", features = ["alloc"] } +bcs = { version = "0.1.6" } +hex = "0.4.3" +serde_json = { version = "1.0.114" } +signature = "2.2" +sui-sdk = { version = "0.0.0", path = "../sui-sdk", features = ["hash", "serde"] } + +# RNG support +rand_core = { version = "0.6.4", optional = true } + +# ed25519 support +ed25519-dalek = { version = "2.1.1", optional = true } + +[dev-dependencies] + +# proptest support in tests +# +# Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments +# see https://github.com/proptest-rs/proptest/pull/270 for more info +proptest = { git = "https://github.com/bmwill/proptest.git", rev = "bc36db126183bce18c8bc595f0c0cfeac48b870c", default-features = false, features = ["std"] } +test-strategy = "0.3.1" + +[target.wasm32-unknown-unknown.dev-dependencies] +wasm-bindgen-test = "0.3" +getrandom = { version = "0.2", features = ["js"] } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(doc_cfg)'] } diff --git a/crates/sui-sdk-crypto/Makefile b/crates/sui-sdk-crypto/Makefile new file mode 100644 index 000000000..acc0c2322 --- /dev/null +++ b/crates/sui-sdk-crypto/Makefile @@ -0,0 +1,23 @@ +# Set the default target of this Makefile +.PHONY: all +all:: check-features clippy test wasm + +.PHONY: check-features +check-features: + cargo hack check --feature-powerset --no-dev-deps + +.PHONY: clippy +clippy: + cargo clippy --all-features --all-targets + +.PHONY: test +test: + cargo nextest run --all-features + cargo test --doc + +.PHONY: wasm +wasm: + CC=clang wasm-pack test --node --all-features + +%: + $(MAKE) -C ../.. $@ diff --git a/crates/sui-sdk-crypto/src/bls12381.rs b/crates/sui-sdk-crypto/src/bls12381.rs new file mode 100644 index 000000000..4999432a2 --- /dev/null +++ b/crates/sui-sdk-crypto/src/bls12381.rs @@ -0,0 +1,7 @@ +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Bls12381PrivateKey([u8; Self::LENGTH]); + +impl Bls12381PrivateKey { + /// The length of an bls12381 private key in bytes. + pub const LENGTH: usize = 32; +} diff --git a/crates/sui-sdk-crypto/src/ed25519.rs b/crates/sui-sdk-crypto/src/ed25519.rs new file mode 100644 index 000000000..9f2e8fc07 --- /dev/null +++ b/crates/sui-sdk-crypto/src/ed25519.rs @@ -0,0 +1,236 @@ +use crate::SignatureError; +use crate::Signer; +use crate::SuiSigner; +use crate::SuiVerifier; +use crate::Verifier; +use sui_sdk::types::Ed25519PublicKey; +use sui_sdk::types::Ed25519Signature; +use sui_sdk::types::PersonalMessage; +use sui_sdk::types::SimpleSignature; +use sui_sdk::types::Transaction; +use sui_sdk::types::UserSignature; + +pub struct Ed25519PrivateKey(ed25519_dalek::SigningKey); + +impl std::fmt::Debug for Ed25519PrivateKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Ed25519PrivateKey") + .field(&"__elided__") + .finish() + } +} + +#[cfg(test)] +impl proptest::arbitrary::Arbitrary for Ed25519PrivateKey { + type Parameters = (); + type Strategy = proptest::strategy::BoxedStrategy; + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + use proptest::strategy::Strategy; + + proptest::arbitrary::any::<[u8; Self::LENGTH]>() + .prop_map(Self::new) + .boxed() + } +} + +impl Ed25519PrivateKey { + /// The length of an ed25519 private key in bytes. + pub const LENGTH: usize = 32; + + pub fn new(bytes: [u8; Self::LENGTH]) -> Self { + Self(bytes.into()) + } + + pub fn verifying_key(&self) -> Ed25519VerifyingKey { + let verifying_key = self.0.verifying_key(); + Ed25519VerifyingKey(verifying_key) + } + + pub fn public_key(&self) -> Ed25519PublicKey { + self.verifying_key().public_key() + } + + #[cfg(feature = "rand")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] + pub fn generate(mut rng: R) -> Self + where + R: rand_core::RngCore + rand_core::CryptoRng, + { + let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH]; + rng.fill_bytes(&mut buf); + Self(buf.into()) + } +} + +impl Signer for Ed25519PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + self.0 + .try_sign(msg) + .map(|signature| Ed25519Signature::new(signature.to_bytes())) + } +} + +impl Signer for Ed25519PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(|signature| { + let signature = SimpleSignature::Ed25519 { + signature, + public_key: self.public_key(), + }; + UserSignature::Simple(signature) + }) + } +} + +impl SuiSigner for Ed25519PrivateKey { + fn sign_transaction(&self, transaction: &Transaction) -> Result { + let msg = transaction.signing_digest(); + self.try_sign(&msg) + } + + fn sign_personal_message( + &self, + message: &PersonalMessage<'_>, + ) -> Result { + let msg = message.signing_digest(); + self.try_sign(&msg) + } +} + +pub struct Ed25519VerifyingKey(ed25519_dalek::VerifyingKey); + +impl Ed25519VerifyingKey { + pub fn new(public_key: &Ed25519PublicKey) -> Result { + ed25519_dalek::VerifyingKey::from_bytes(public_key.inner()).map(Self) + } + + pub fn public_key(&self) -> Ed25519PublicKey { + Ed25519PublicKey::new(self.0.to_bytes()) + } +} + +impl Verifier for Ed25519VerifyingKey { + fn verify(&self, message: &[u8], signature: &Ed25519Signature) -> Result<(), SignatureError> { + let signature = ed25519_dalek::Signature::from_bytes(signature.inner()); + self.0.verify_strict(message, &signature) + } +} + +impl Verifier for Ed25519VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Ed25519 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not an ed25519 signature")); + }; + + if public_key.inner() != self.0.as_bytes() { + return Err(SignatureError::from_source( + "public_key in signature does not match", + )); + } + + >::verify(self, message, signature) + } +} + +impl SuiVerifier for Ed25519VerifyingKey { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +#[derive(Default, Clone, Debug)] +pub struct Ed25519Verifier {} + +impl Ed25519Verifier { + pub fn new() -> Self { + Self {} + } +} + +impl Verifier for Ed25519Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Ed25519 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not an ed25519 signature")); + }; + + let verifying_key = Ed25519VerifyingKey::new(public_key)?; + + verifying_key.verify(message, signature) + } +} + +impl SuiVerifier for Ed25519Verifier { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +#[cfg(test)] +mod test { + use super::*; + use test_strategy::proptest; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + // TODO need to export proptest impl from core crate + // #[proptest] + // fn transaction_signing(signer: Ed25519PrivateKey, transaction: Transaction) { + // let signature = signer.sign_transaction(&transaction).unwrap(); + // let verifier = signer.public_key(); + // verifier + // .verify_transaction(&transaction, &signature) + // .unwrap(); + // } + + #[proptest] + fn personal_message_signing(signer: Ed25519PrivateKey, message: Vec) { + let message = PersonalMessage(message.into()); + let signature = signer.sign_personal_message(&message).unwrap(); + let verifying_key = signer.verifying_key(); + verifying_key + .verify_personal_message(&message, &signature) + .unwrap(); + + let verifier = Ed25519Verifier::default(); + verifier + .verify_personal_message(&message, &signature) + .unwrap(); + } +} diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-sdk-crypto/src/lib.rs new file mode 100644 index 000000000..16e2e6e0d --- /dev/null +++ b/crates/sui-sdk-crypto/src/lib.rs @@ -0,0 +1,41 @@ +#![cfg_attr(doc_cfg, feature(doc_cfg))] + +use sui_sdk::types::PersonalMessage; +use sui_sdk::types::Transaction; +use sui_sdk::types::UserSignature; + +pub use signature::Error as SignatureError; +pub use signature::Signer; +pub use signature::Verifier; + +#[cfg(feature = "ed25519")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))] +pub mod ed25519; + +#[allow(unused)] +mod bls12381; +#[allow(unused)] +mod secp256k1; +#[allow(unused)] +mod secp256r1; + +pub trait SuiSigner { + fn sign_transaction(&self, transaction: &Transaction) -> Result; + fn sign_personal_message( + &self, + message: &PersonalMessage<'_>, + ) -> Result; +} + +pub trait SuiVerifier { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError>; + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError>; +} diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-sdk-crypto/src/secp256k1.rs new file mode 100644 index 000000000..b7de40455 --- /dev/null +++ b/crates/sui-sdk-crypto/src/secp256k1.rs @@ -0,0 +1,7 @@ +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Secp256k1PrivateKey([u8; Self::LENGTH]); + +impl Secp256k1PrivateKey { + /// The length of an secp256k1 private key in bytes. + pub const LENGTH: usize = 32; +} diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-sdk-crypto/src/secp256r1.rs new file mode 100644 index 000000000..5f43c19eb --- /dev/null +++ b/crates/sui-sdk-crypto/src/secp256r1.rs @@ -0,0 +1,7 @@ +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Secp256r1PrivateKey([u8; Self::LENGTH]); + +impl Secp256r1PrivateKey { + /// The length of an secp256r1 private key in bytes. + pub const LENGTH: usize = 32; +} From 143e4253827edc116beb44388c0cd4fce195b82f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 14:38:39 -0500 Subject: [PATCH 012/107] crypto: introduce secp256r1 support --- crates/sui-sdk-crypto/Cargo.toml | 4 + crates/sui-sdk-crypto/src/lib.rs | 6 +- crates/sui-sdk-crypto/src/secp256r1.rs | 235 ++++++++++++++++++++++++- 3 files changed, 241 insertions(+), 4 deletions(-) diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index 1e2eefe2b..709606919 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -23,6 +23,7 @@ rustdoc-args = [ default = [] rand = ["dep:rand_core"] ed25519 = ["dep:ed25519-dalek"] +secp256r1 = ["dep:p256"] [dependencies] base64ct = { version = "1.6.0", features = ["alloc"] } @@ -38,6 +39,9 @@ rand_core = { version = "0.6.4", optional = true } # ed25519 support ed25519-dalek = { version = "2.1.1", optional = true } +# secp256r1 support +p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std"], optional = true } + [dev-dependencies] # proptest support in tests diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-sdk-crypto/src/lib.rs index 16e2e6e0d..d254a62de 100644 --- a/crates/sui-sdk-crypto/src/lib.rs +++ b/crates/sui-sdk-crypto/src/lib.rs @@ -16,8 +16,10 @@ pub mod ed25519; mod bls12381; #[allow(unused)] mod secp256k1; -#[allow(unused)] -mod secp256r1; + +#[cfg(feature = "secp256r1")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] +pub mod secp256r1; pub trait SuiSigner { fn sign_transaction(&self, transaction: &Transaction) -> Result; diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-sdk-crypto/src/secp256r1.rs index 5f43c19eb..96b0e1a14 100644 --- a/crates/sui-sdk-crypto/src/secp256r1.rs +++ b/crates/sui-sdk-crypto/src/secp256r1.rs @@ -1,7 +1,238 @@ -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Secp256r1PrivateKey([u8; Self::LENGTH]); +use crate::SignatureError; +use crate::SuiSigner; +use crate::SuiVerifier; +use p256::ecdsa::SigningKey; +use p256::ecdsa::VerifyingKey; +use p256::elliptic_curve::group::GroupEncoding; +use signature::Signer; +use signature::Verifier; +use sui_sdk::types::PersonalMessage; +use sui_sdk::types::Secp256r1PublicKey; +use sui_sdk::types::Secp256r1Signature; +use sui_sdk::types::SimpleSignature; +use sui_sdk::types::Transaction; +use sui_sdk::types::UserSignature; + +pub struct Secp256r1PrivateKey(SigningKey); + +impl std::fmt::Debug for Secp256r1PrivateKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256r1PrivateKey") + .field(&"__elided__") + .finish() + } +} + +#[cfg(test)] +impl proptest::arbitrary::Arbitrary for Secp256r1PrivateKey { + type Parameters = (); + type Strategy = proptest::strategy::BoxedStrategy; + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + use proptest::strategy::Strategy; + + proptest::arbitrary::any::<[u8; Self::LENGTH]>() + .prop_map(Self::new) + .boxed() + } +} impl Secp256r1PrivateKey { /// The length of an secp256r1 private key in bytes. pub const LENGTH: usize = 32; + + pub fn new(bytes: [u8; Self::LENGTH]) -> Self { + Self(SigningKey::from_bytes(&bytes.into()).unwrap()) + } + + pub fn verifying_key(&self) -> Secp256r1VerifyingKey { + let verifying_key = self.0.verifying_key(); + Secp256r1VerifyingKey(*verifying_key) + } + + pub fn public_key(&self) -> Secp256r1PublicKey { + Secp256r1PublicKey::new(self.0.verifying_key().as_ref().to_bytes().into()) + } + + #[cfg(feature = "rand")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] + pub fn generate(mut rng: R) -> Self + where + R: rand_core::RngCore + rand_core::CryptoRng, + { + let mut buf: [u8; Self::LENGTH] = [0; Self::LENGTH]; + rng.fill_bytes(&mut buf); + Self::new(buf) + } +} + +impl Signer for Secp256r1PrivateKey { + fn try_sign(&self, message: &[u8]) -> Result { + let signature: p256::ecdsa::Signature = self.0.try_sign(message)?; + Ok(Secp256r1Signature::new(signature.to_bytes().into())) + } +} + +impl Signer for Secp256r1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(|signature| { + let signature = SimpleSignature::Secp256r1 { + signature, + public_key: self.public_key(), + }; + UserSignature::Simple(signature) + }) + } +} + +impl SuiSigner for Secp256r1PrivateKey { + fn sign_transaction(&self, transaction: &Transaction) -> Result { + let msg = transaction.signing_digest(); + self.try_sign(&msg) + } + + fn sign_personal_message( + &self, + message: &PersonalMessage<'_>, + ) -> Result { + let msg = message.signing_digest(); + self.try_sign(&msg) + } +} + +pub struct Secp256r1VerifyingKey(VerifyingKey); + +impl Secp256r1VerifyingKey { + pub fn new(public_key: &Secp256r1PublicKey) -> Result { + VerifyingKey::try_from(public_key.inner().as_ref()).map(Self) + } + + pub fn public_key(&self) -> Secp256r1PublicKey { + Secp256r1PublicKey::new(self.0.as_ref().to_bytes().into()) + } +} + +impl Verifier for Secp256r1VerifyingKey { + fn verify(&self, message: &[u8], signature: &Secp256r1Signature) -> Result<(), SignatureError> { + let signature = p256::ecdsa::Signature::from_bytes(signature.inner().into())?; + self.0.verify(message, &signature) + } +} + +impl Verifier for Secp256r1VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Secp256r1 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not a secp256r1 signature")); + }; + + if public_key.inner() != self.public_key().inner() { + return Err(SignatureError::from_source( + "public_key in signature does not match", + )); + } + + >::verify(self, message, signature) + } +} + +impl SuiVerifier for Secp256r1VerifyingKey { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +#[derive(Default, Clone, Debug)] +pub struct Secp256r1Verifier {} + +impl Secp256r1Verifier { + pub fn new() -> Self { + Self {} + } +} + +impl Verifier for Secp256r1Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Secp256r1 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not a secp256r1 signature")); + }; + + let verifying_key = Secp256r1VerifyingKey::new(public_key)?; + + verifying_key.verify(message, signature) + } +} + +impl SuiVerifier for Secp256r1Verifier { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +#[cfg(test)] +mod test { + use super::*; + use test_strategy::proptest; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + // TODO need to export proptest impl from core crate + // #[proptest] + // fn transaction_signing(signer: Secp256r1PrivateKey, transaction: Transaction) { + // let signature = signer.sign_transaction(&transaction).unwrap(); + // let verifier = signer.public_key(); + // verifier + // .verify_transaction(&transaction, &signature) + // .unwrap(); + // } + + #[proptest] + fn personal_message_signing(signer: Secp256r1PrivateKey, message: Vec) { + let message = PersonalMessage(message.into()); + let signature = signer.sign_personal_message(&message).unwrap(); + let verifying_key = signer.verifying_key(); + verifying_key + .verify_personal_message(&message, &signature) + .unwrap(); + + let verifier = Secp256r1Verifier::default(); + verifier + .verify_personal_message(&message, &signature) + .unwrap(); + } } From 593229c2696fa48ac06c950092a59e1d91cdd1bb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 15:44:11 -0500 Subject: [PATCH 013/107] crypto: tweak dependencies and default features --- crates/sui-sdk-crypto/Cargo.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index 709606919..d17602494 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -22,16 +22,12 @@ rustdoc-args = [ [features] default = [] rand = ["dep:rand_core"] -ed25519 = ["dep:ed25519-dalek"] -secp256r1 = ["dep:p256"] +ed25519 = ["dep:ed25519-dalek", "sui-sdk/hash", "sui-sdk/serde"] +secp256r1 = ["dep:p256", "sui-sdk/hash", "sui-sdk/serde"] [dependencies] -base64ct = { version = "1.6.0", features = ["alloc"] } -bcs = { version = "0.1.6" } -hex = "0.4.3" -serde_json = { version = "1.0.114" } signature = "2.2" -sui-sdk = { version = "0.0.0", path = "../sui-sdk", features = ["hash", "serde"] } +sui-sdk = { version = "0.0.0", path = "../sui-sdk", default-features = false } # RNG support rand_core = { version = "0.6.4", optional = true } @@ -43,6 +39,10 @@ ed25519-dalek = { version = "2.1.1", optional = true } p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std"], optional = true } [dev-dependencies] +base64ct = { version = "1.6.0", features = ["alloc"] } +bcs = { version = "0.1.6" } +hex = "0.4.3" +serde_json = { version = "1.0.114" } # proptest support in tests # From bc23a3ce2816b3ec92d007b0cc606d2c5c869a49 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 15:45:20 -0500 Subject: [PATCH 014/107] crypto: introduce secp256k1 support --- crates/sui-sdk-crypto/Cargo.toml | 7 +- crates/sui-sdk-crypto/src/lib.rs | 6 +- crates/sui-sdk-crypto/src/secp256k1.rs | 253 ++++++++++++++++++++++++- 3 files changed, 261 insertions(+), 5 deletions(-) diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index d17602494..8f63bc189 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -21,9 +21,10 @@ rustdoc-args = [ [features] default = [] -rand = ["dep:rand_core"] +rand = ["dep:rand_core", "secp256k1?/rand"] ed25519 = ["dep:ed25519-dalek", "sui-sdk/hash", "sui-sdk/serde"] secp256r1 = ["dep:p256", "sui-sdk/hash", "sui-sdk/serde"] +secp256k1 = ["dep:secp256k1", "dep:sha2", "signature/std", "sui-sdk/hash", "sui-sdk/serde"] [dependencies] signature = "2.2" @@ -38,6 +39,10 @@ ed25519-dalek = { version = "2.1.1", optional = true } # secp256r1 support p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std"], optional = true } +# secp256k1 support +secp256k1 = { version = "0.29.0", default-features = false, features = ["std", "global-context"], optional = true } +sha2 = { version = "0.10.8", default-features = false, features = ["std"], optional = true } + [dev-dependencies] base64ct = { version = "1.6.0", features = ["alloc"] } bcs = { version = "0.1.6" } diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-sdk-crypto/src/lib.rs index d254a62de..1c8e141de 100644 --- a/crates/sui-sdk-crypto/src/lib.rs +++ b/crates/sui-sdk-crypto/src/lib.rs @@ -14,8 +14,10 @@ pub mod ed25519; #[allow(unused)] mod bls12381; -#[allow(unused)] -mod secp256k1; + +#[cfg(feature = "secp256k1")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))] +pub mod secp256k1; #[cfg(feature = "secp256r1")] #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-sdk-crypto/src/secp256k1.rs index b7de40455..f3c876eff 100644 --- a/crates/sui-sdk-crypto/src/secp256k1.rs +++ b/crates/sui-sdk-crypto/src/secp256k1.rs @@ -1,7 +1,256 @@ -#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Secp256k1PrivateKey([u8; Self::LENGTH]); +use crate::SignatureError; +use crate::SuiSigner; +use crate::SuiVerifier; +use secp256k1::ecdsa::Signature; +use secp256k1::Message; +use secp256k1::PublicKey; +use secp256k1::SecretKey; +use signature::Signer; +use signature::Verifier; +use sui_sdk::types::PersonalMessage; +use sui_sdk::types::Secp256k1PublicKey; +use sui_sdk::types::Secp256k1Signature; +use sui_sdk::types::SimpleSignature; +use sui_sdk::types::Transaction; +use sui_sdk::types::UserSignature; + +pub struct Secp256k1PrivateKey(SecretKey); + +impl std::fmt::Debug for Secp256k1PrivateKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Secp256k1PrivateKey") + .field(&"__elided__") + .finish() + } +} + +#[cfg(test)] +impl proptest::arbitrary::Arbitrary for Secp256k1PrivateKey { + type Parameters = (); + type Strategy = proptest::strategy::BoxedStrategy; + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + use proptest::strategy::Strategy; + + proptest::arbitrary::any::<[u8; Self::LENGTH]>() + .prop_filter_map("invalid secp256k1 private key", |bytes| { + Self::new(bytes).ok() + }) + .boxed() + } +} impl Secp256k1PrivateKey { /// The length of an secp256k1 private key in bytes. pub const LENGTH: usize = 32; + + pub fn new(bytes: [u8; Self::LENGTH]) -> Result { + SecretKey::from_slice(&bytes) + .map_err(SignatureError::from_source) + .map(Self) + } + + pub fn verifying_key(&self) -> Secp256k1VerifyingKey { + Secp256k1VerifyingKey(PublicKey::from_secret_key_global(&self.0)) + } + + pub fn public_key(&self) -> Secp256k1PublicKey { + self.verifying_key().public_key() + } + + #[cfg(feature = "rand")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] + pub fn generate(mut rng: R) -> Self + where + R: rand_core::RngCore + rand_core::CryptoRng, + { + Self(SecretKey::new(&mut rng)) + } +} + +impl Signer for Secp256k1PrivateKey { + fn try_sign(&self, message: &[u8]) -> Result { + let message = to_message(message); + let signature = self.0.sign_ecdsa(message); + Ok(Secp256k1Signature::new(signature.serialize_compact())) + } +} + +impl Signer for Secp256k1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(|signature| { + let signature = SimpleSignature::Secp256k1 { + signature, + public_key: self.public_key(), + }; + UserSignature::Simple(signature) + }) + } +} + +impl SuiSigner for Secp256k1PrivateKey { + fn sign_transaction(&self, transaction: &Transaction) -> Result { + let msg = transaction.signing_digest(); + self.try_sign(&msg) + } + + fn sign_personal_message( + &self, + message: &PersonalMessage<'_>, + ) -> Result { + let msg = message.signing_digest(); + self.try_sign(&msg) + } +} + +pub struct Secp256k1VerifyingKey(PublicKey); + +impl Secp256k1VerifyingKey { + pub fn new(public_key: &Secp256k1PublicKey) -> Result { + PublicKey::from_slice(public_key.inner().as_ref()) + .map_err(SignatureError::from_source) + .map(Self) + } + + pub fn public_key(&self) -> Secp256k1PublicKey { + Secp256k1PublicKey::new(self.0.serialize()) + } +} + +impl Verifier for Secp256k1VerifyingKey { + fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> Result<(), SignatureError> { + let signature = + Signature::from_compact(signature.inner()).map_err(SignatureError::from_source)?; + let message = to_message(message); + signature + .verify(&message, &self.0) + .map_err(SignatureError::from_source) + } +} + +impl Verifier for Secp256k1VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Secp256k1 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not a secp256k1 signature")); + }; + + if public_key.inner() != self.public_key().inner() { + return Err(SignatureError::from_source( + "public_key in signature does not match", + )); + } + + >::verify(self, message, signature) + } +} + +impl SuiVerifier for Secp256k1VerifyingKey { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +fn to_message(message: &[u8]) -> Message { + use sha2::Digest; + + let mut hasher = sha2::Sha256::new(); + hasher.update(message); + let digest = hasher.finalize(); + Message::from_digest(digest.into()) +} + +#[derive(Default, Clone, Debug)] +pub struct Secp256k1Verifier {} + +impl Secp256k1Verifier { + pub fn new() -> Self { + Self {} + } +} + +impl Verifier for Secp256k1Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(SimpleSignature::Secp256k1 { + signature, + public_key, + }) = signature + else { + return Err(SignatureError::from_source("not a secp256k1 signature")); + }; + + let verifying_key = Secp256k1VerifyingKey::new(public_key)?; + + verifying_key.verify(message, signature) + } +} + +impl SuiVerifier for Secp256k1Verifier { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +#[cfg(test)] +mod test { + use super::*; + use test_strategy::proptest; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + // TODO need to export proptest impl from core crate + // #[proptest] + // fn transaction_signing(signer: Secp256k1PrivateKey, transaction: Transaction) { + // let signature = signer.sign_transaction(&transaction).unwrap(); + // let verifier = signer.public_key(); + // verifier + // .verify_transaction(&transaction, &signature) + // .unwrap(); + // } + + #[proptest] + fn personal_message_signing(signer: Secp256k1PrivateKey, message: Vec) { + let message = PersonalMessage(message.into()); + let signature = signer.sign_personal_message(&message).unwrap(); + let verifying_key = signer.verifying_key(); + verifying_key + .verify_personal_message(&message, &signature) + .unwrap(); + + let verifier = Secp256k1Verifier::default(); + verifier + .verify_personal_message(&message, &signature) + .unwrap(); + } } From 5fe928611c06db11728b429499fe2b0096b5b88f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 20 Sep 2024 16:07:45 -0500 Subject: [PATCH 015/107] crypto: use wasm compatible k256 library for secp256k1 support --- crates/sui-sdk-crypto/Cargo.toml | 10 ++--- crates/sui-sdk-crypto/src/ed25519.rs | 2 - crates/sui-sdk-crypto/src/secp256k1.rs | 52 ++++++++------------------ crates/sui-sdk-crypto/src/secp256r1.rs | 2 - 4 files changed, 20 insertions(+), 46 deletions(-) diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index 8f63bc189..bea402cf5 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -21,10 +21,9 @@ rustdoc-args = [ [features] default = [] -rand = ["dep:rand_core", "secp256k1?/rand"] -ed25519 = ["dep:ed25519-dalek", "sui-sdk/hash", "sui-sdk/serde"] -secp256r1 = ["dep:p256", "sui-sdk/hash", "sui-sdk/serde"] -secp256k1 = ["dep:secp256k1", "dep:sha2", "signature/std", "sui-sdk/hash", "sui-sdk/serde"] +ed25519 = ["dep:ed25519-dalek", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] +secp256r1 = ["dep:p256", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] +secp256k1 = ["dep:k256", "dep:rand_core", "signature/std", "sui-sdk/hash", "sui-sdk/serde"] [dependencies] signature = "2.2" @@ -40,8 +39,7 @@ ed25519-dalek = { version = "2.1.1", optional = true } p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std"], optional = true } # secp256k1 support -secp256k1 = { version = "0.29.0", default-features = false, features = ["std", "global-context"], optional = true } -sha2 = { version = "0.10.8", default-features = false, features = ["std"], optional = true } +k256 = { version = "0.13.4", default-features = false, features = ["ecdsa"], optional = true } [dev-dependencies] base64ct = { version = "1.6.0", features = ["alloc"] } diff --git a/crates/sui-sdk-crypto/src/ed25519.rs b/crates/sui-sdk-crypto/src/ed25519.rs index 9f2e8fc07..9ee790f6d 100644 --- a/crates/sui-sdk-crypto/src/ed25519.rs +++ b/crates/sui-sdk-crypto/src/ed25519.rs @@ -50,8 +50,6 @@ impl Ed25519PrivateKey { self.verifying_key().public_key() } - #[cfg(feature = "rand")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] pub fn generate(mut rng: R) -> Self where R: rand_core::RngCore + rand_core::CryptoRng, diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-sdk-crypto/src/secp256k1.rs index f3c876eff..e24d1cd78 100644 --- a/crates/sui-sdk-crypto/src/secp256k1.rs +++ b/crates/sui-sdk-crypto/src/secp256k1.rs @@ -1,10 +1,9 @@ use crate::SignatureError; use crate::SuiSigner; use crate::SuiVerifier; -use secp256k1::ecdsa::Signature; -use secp256k1::Message; -use secp256k1::PublicKey; -use secp256k1::SecretKey; +use k256::ecdsa::SigningKey; +use k256::ecdsa::VerifyingKey; +use k256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; use sui_sdk::types::PersonalMessage; @@ -14,7 +13,7 @@ use sui_sdk::types::SimpleSignature; use sui_sdk::types::Transaction; use sui_sdk::types::UserSignature; -pub struct Secp256k1PrivateKey(SecretKey); +pub struct Secp256k1PrivateKey(SigningKey); impl std::fmt::Debug for Secp256k1PrivateKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -44,34 +43,30 @@ impl Secp256k1PrivateKey { pub const LENGTH: usize = 32; pub fn new(bytes: [u8; Self::LENGTH]) -> Result { - SecretKey::from_slice(&bytes) - .map_err(SignatureError::from_source) - .map(Self) + SigningKey::from_bytes(&bytes.into()).map(Self) } pub fn verifying_key(&self) -> Secp256k1VerifyingKey { - Secp256k1VerifyingKey(PublicKey::from_secret_key_global(&self.0)) + let verifying_key = self.0.verifying_key(); + Secp256k1VerifyingKey(*verifying_key) } pub fn public_key(&self) -> Secp256k1PublicKey { - self.verifying_key().public_key() + Secp256k1PublicKey::new(self.0.verifying_key().as_ref().to_bytes().into()) } - #[cfg(feature = "rand")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] pub fn generate(mut rng: R) -> Self where R: rand_core::RngCore + rand_core::CryptoRng, { - Self(SecretKey::new(&mut rng)) + Self(SigningKey::random(&mut rng)) } } impl Signer for Secp256k1PrivateKey { fn try_sign(&self, message: &[u8]) -> Result { - let message = to_message(message); - let signature = self.0.sign_ecdsa(message); - Ok(Secp256k1Signature::new(signature.serialize_compact())) + let signature: k256::ecdsa::Signature = self.0.try_sign(message)?; + Ok(Secp256k1Signature::new(signature.to_bytes().into())) } } @@ -102,28 +97,22 @@ impl SuiSigner for Secp256k1PrivateKey { } } -pub struct Secp256k1VerifyingKey(PublicKey); +pub struct Secp256k1VerifyingKey(VerifyingKey); impl Secp256k1VerifyingKey { pub fn new(public_key: &Secp256k1PublicKey) -> Result { - PublicKey::from_slice(public_key.inner().as_ref()) - .map_err(SignatureError::from_source) - .map(Self) + VerifyingKey::try_from(public_key.inner().as_ref()).map(Self) } pub fn public_key(&self) -> Secp256k1PublicKey { - Secp256k1PublicKey::new(self.0.serialize()) + Secp256k1PublicKey::new(self.0.as_ref().to_bytes().into()) } } impl Verifier for Secp256k1VerifyingKey { fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> Result<(), SignatureError> { - let signature = - Signature::from_compact(signature.inner()).map_err(SignatureError::from_source)?; - let message = to_message(message); - signature - .verify(&message, &self.0) - .map_err(SignatureError::from_source) + let signature = k256::ecdsa::Signature::from_bytes(signature.inner().into())?; + self.0.verify(message, &signature) } } @@ -167,15 +156,6 @@ impl SuiVerifier for Secp256k1VerifyingKey { } } -fn to_message(message: &[u8]) -> Message { - use sha2::Digest; - - let mut hasher = sha2::Sha256::new(); - hasher.update(message); - let digest = hasher.finalize(); - Message::from_digest(digest.into()) -} - #[derive(Default, Clone, Debug)] pub struct Secp256k1Verifier {} diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-sdk-crypto/src/secp256r1.rs index 96b0e1a14..c3ca83ebe 100644 --- a/crates/sui-sdk-crypto/src/secp256r1.rs +++ b/crates/sui-sdk-crypto/src/secp256r1.rs @@ -53,8 +53,6 @@ impl Secp256r1PrivateKey { Secp256r1PublicKey::new(self.0.verifying_key().as_ref().to_bytes().into()) } - #[cfg(feature = "rand")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "rand")))] pub fn generate(mut rng: R) -> Self where R: rand_core::RngCore + rand_core::CryptoRng, From 70f6b86d2afa52f3fd95b0ee563585dd9ff75aa7 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sat, 21 Sep 2024 21:40:37 -0500 Subject: [PATCH 016/107] crypto: introduce zklogin verifier support --- crates/iota-rust-sdk/src/types/crypto/mod.rs | 4 +- .../iota-rust-sdk/src/types/crypto/zklogin.rs | 57 +- crates/iota-rust-sdk/src/types/mod.rs | 11 +- .../src/types/serialization_proptests.rs | 1 - crates/sui-sdk-crypto/Cargo.toml | 30 +- crates/sui-sdk-crypto/Makefile | 2 +- crates/sui-sdk-crypto/src/ed25519.rs | 51 +- crates/sui-sdk-crypto/src/lib.rs | 21 + crates/sui-sdk-crypto/src/secp256k1.rs | 67 +- crates/sui-sdk-crypto/src/secp256r1.rs | 66 +- crates/sui-sdk-crypto/src/simple.rs | 81 + crates/sui-sdk-crypto/src/zklogin/mod.rs | 258 + .../src/zklogin/poseidon/constants.rs | 13015 ++++++++++++++++ .../src/zklogin/poseidon/mod.rs | 272 + crates/sui-sdk-crypto/src/zklogin/tests.rs | 80 + crates/sui-sdk-crypto/src/zklogin/verify.rs | 681 + 16 files changed, 14617 insertions(+), 80 deletions(-) create mode 100644 crates/sui-sdk-crypto/src/simple.rs create mode 100644 crates/sui-sdk-crypto/src/zklogin/mod.rs create mode 100644 crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs create mode 100644 crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs create mode 100644 crates/sui-sdk-crypto/src/zklogin/tests.rs create mode 100644 crates/sui-sdk-crypto/src/zklogin/verify.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/mod.rs b/crates/iota-rust-sdk/src/types/crypto/mod.rs index 52561bf17..65323093e 100644 --- a/crates/iota-rust-sdk/src/types/crypto/mod.rs +++ b/crates/iota-rust-sdk/src/types/crypto/mod.rs @@ -24,8 +24,8 @@ pub use validator::{ ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, }; pub use zklogin::{ - Bn254FieldElement, CircomG1, CircomG2, Claim, Jwk, JwkId, JwtDetails, ZkLoginAuthenticator, - ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, + Bn254FieldElement, CircomG1, CircomG2, Claim, Jwk, JwkId, ZkLoginAuthenticator, ZkLoginInputs, + ZkLoginProof, ZkLoginPublicIdentifier, }; #[cfg(feature = "serde")] diff --git a/crates/iota-rust-sdk/src/types/crypto/zklogin.rs b/crates/iota-rust-sdk/src/types/crypto/zklogin.rs index aa5746dc8..e1369dee1 100644 --- a/crates/iota-rust-sdk/src/types/crypto/zklogin.rs +++ b/crates/iota-rust-sdk/src/types/crypto/zklogin.rs @@ -6,10 +6,10 @@ use crate::types::{checkpoint::EpochId, u256::U256}; #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub struct ZkLoginAuthenticator { - inputs: ZkLoginInputs, + pub inputs: ZkLoginInputs, #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] - max_epoch: EpochId, - signature: SimpleSignature, + pub max_epoch: EpochId, + pub signature: SimpleSignature, } /// All inputs required for the zk login proof verification and other public @@ -22,12 +22,10 @@ pub struct ZkLoginAuthenticator { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub struct ZkLoginInputs { - proof_points: ZkLoginProof, - iss_base64_details: Claim, - header_base64: String, - address_seed: Bn254FieldElement, - // #[serde(skip)] - // jwt_details: JwtDetails, + pub proof_points: ZkLoginProof, + pub iss_base64_details: Claim, + pub header_base64: String, + pub address_seed: Bn254FieldElement, } /// A claim consists of value and index_mod_4. @@ -39,22 +37,8 @@ pub struct ZkLoginInputs { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub struct Claim { - value: String, - index_mod_4: u8, -} - -/// A structured of parsed JWT details, consists of kid, header, iss. -#[derive(Default, Debug, Clone, PartialEq, Eq)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] -pub struct JwtDetails { - kid: String, - header: String, - iss: String, + pub value: String, + pub index_mod_4: u8, } /// The struct for zk login proof. @@ -66,9 +50,9 @@ pub struct JwtDetails { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub struct ZkLoginProof { - a: CircomG1, - b: CircomG2, - c: CircomG1, + pub a: CircomG1, + pub b: CircomG2, + pub c: CircomG1, } /// A G1 point in BN254 serialized as a vector of three strings which is the @@ -76,7 +60,7 @@ pub struct ZkLoginProof { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] -pub struct CircomG1([Bn254FieldElement; 3]); +pub struct CircomG1(pub [Bn254FieldElement; 3]); /// A G2 point in BN254 serialized as a vector of three vectors each being a /// vector of two strings which are the canonical decimal representation of the @@ -84,7 +68,7 @@ pub struct CircomG1([Bn254FieldElement; 3]); #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] -pub struct CircomG2([[Bn254FieldElement; 2]; 3]); +pub struct CircomG2(pub [[Bn254FieldElement; 2]; 3]); /// A wrapper struct to retrofit in [enum PublicKey] for zkLogin. /// Useful to construct [struct MultiSigPublicKey]. @@ -151,6 +135,19 @@ pub struct Bn254FieldElement( ); impl Bn254FieldElement { + pub const fn new(bytes: [u8; 32]) -> Self { + Self(bytes) + } + + pub const fn from_str_radix_10(s: &str) -> Result { + let u256 = match U256::from_str_radix(s, 10) { + Ok(u256) => u256, + Err(e) => return Err(Bn254FieldElementParseError(e)), + }; + let be = u256.to_be(); + Ok(Self(*be.digits())) + } + pub fn unpadded(&self) -> &[u8] { let mut buf = self.0.as_slice(); diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/iota-rust-sdk/src/types/mod.rs index ab33d7c84..d17121bfc 100644 --- a/crates/iota-rust-sdk/src/types/mod.rs +++ b/crates/iota-rust-sdk/src/types/mod.rs @@ -22,12 +22,11 @@ pub use checkpoint::{ pub use crypto::{ Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, - JwkId, JwtDetails, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, - MultisigMemberPublicKey, MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, - Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, - SignatureScheme, SimpleSignature, UserSignature, ValidatorAggregatedSignature, - ValidatorCommittee, ValidatorCommitteeMember, ValidatorSignature, ZkLoginAuthenticator, - ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, + JwkId, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, + MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PublicKey, + Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, + UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, + ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, }; pub use digest::{ CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, diff --git a/crates/iota-rust-sdk/src/types/serialization_proptests.rs b/crates/iota-rust-sdk/src/types/serialization_proptests.rs index 76a122259..c6b50cefd 100644 --- a/crates/iota-rust-sdk/src/types/serialization_proptests.rs +++ b/crates/iota-rust-sdk/src/types/serialization_proptests.rs @@ -88,7 +88,6 @@ serialization_test!(Ed25519PublicKey); serialization_test!(Ed25519Signature); serialization_test!(Jwk); serialization_test!(JwkId); -serialization_test!(JwtDetails); serialization_test!(MultisigAggregatedSignature); serialization_test!(MultisigCommittee); serialization_test!(MultisigMember); diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index bea402cf5..35d05cdb4 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -24,6 +24,22 @@ default = [] ed25519 = ["dep:ed25519-dalek", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] secp256r1 = ["dep:p256", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] secp256k1 = ["dep:k256", "dep:rand_core", "signature/std", "sui-sdk/hash", "sui-sdk/serde"] +zklogin = [ + "dep:ark-bn254", + "dep:ark-ff", + "dep:ark-groth16", + "dep:ark-snark", + "dep:ark-std", + "dep:base64ct", + "dep:bnum", + "dep:itertools", + "dep:serde", + "dep:serde_derive", + "dep:serde_json", + "signature/std", + "sui-sdk/hash", + "sui-sdk/serde", +] [dependencies] signature = "2.2" @@ -41,8 +57,20 @@ p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std # secp256k1 support k256 = { version = "0.13.4", default-features = false, features = ["ecdsa"], optional = true } +# zklogin verification support +ark-bn254 = { version = "0.4.0", optional = true } +ark-ff = { version = "0.4.1", features = ["asm"], optional = true } +ark-groth16 = { version = "0.4.0", default-features = false, optional = true } +ark-snark = { version = "0.4.0", optional = true } +ark-std = { version = "0.4.0", optional = true } +base64ct = { version = "1.6.0", features = ["alloc"], optional = true } +bnum = { version = "0.11.0", optional = true } +itertools = { version = "0.10.5", optional = true } +serde = { version = "1.0.190", optional = true } +serde_derive = { version = "1.0.190", optional = true } +serde_json = { version = "1.0.114", optional = true } + [dev-dependencies] -base64ct = { version = "1.6.0", features = ["alloc"] } bcs = { version = "0.1.6" } hex = "0.4.3" serde_json = { version = "1.0.114" } diff --git a/crates/sui-sdk-crypto/Makefile b/crates/sui-sdk-crypto/Makefile index acc0c2322..8f7253573 100644 --- a/crates/sui-sdk-crypto/Makefile +++ b/crates/sui-sdk-crypto/Makefile @@ -17,7 +17,7 @@ test: .PHONY: wasm wasm: - CC=clang wasm-pack test --node --all-features + CC=clang wasm-pack test -r --node --all-features %: $(MAKE) -C ../.. $@ diff --git a/crates/sui-sdk-crypto/src/ed25519.rs b/crates/sui-sdk-crypto/src/ed25519.rs index 9ee790f6d..3bf22c5fb 100644 --- a/crates/sui-sdk-crypto/src/ed25519.rs +++ b/crates/sui-sdk-crypto/src/ed25519.rs @@ -68,18 +68,23 @@ impl Signer for Ed25519PrivateKey { } } -impl Signer for Ed25519PrivateKey { - fn try_sign(&self, msg: &[u8]) -> Result { +impl Signer for Ed25519PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { >::try_sign(self, msg).map(|signature| { - let signature = SimpleSignature::Ed25519 { + SimpleSignature::Ed25519 { signature, public_key: self.public_key(), - }; - UserSignature::Simple(signature) + } }) } } +impl Signer for Ed25519PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(UserSignature::Simple) + } +} + impl SuiSigner for Ed25519PrivateKey { fn sign_transaction(&self, transaction: &Transaction) -> Result { let msg = transaction.signing_digest(); @@ -114,12 +119,12 @@ impl Verifier for Ed25519VerifyingKey { } } -impl Verifier for Ed25519VerifyingKey { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Ed25519 { +impl Verifier for Ed25519VerifyingKey { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Ed25519 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not an ed25519 signature")); }; @@ -134,6 +139,16 @@ impl Verifier for Ed25519VerifyingKey { } } +impl Verifier for Ed25519VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not an ed25519 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Ed25519VerifyingKey { fn verify_transaction( &self, @@ -163,12 +178,12 @@ impl Ed25519Verifier { } } -impl Verifier for Ed25519Verifier { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Ed25519 { +impl Verifier for Ed25519Verifier { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Ed25519 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not an ed25519 signature")); }; @@ -179,6 +194,16 @@ impl Verifier for Ed25519Verifier { } } +impl Verifier for Ed25519Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not an ed25519 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Ed25519Verifier { fn verify_transaction( &self, diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-sdk-crypto/src/lib.rs index 1c8e141de..cc153be96 100644 --- a/crates/sui-sdk-crypto/src/lib.rs +++ b/crates/sui-sdk-crypto/src/lib.rs @@ -23,6 +23,27 @@ pub mod secp256k1; #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] pub mod secp256r1; +#[cfg(feature = "zklogin")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))] +pub mod zklogin; + +#[cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" +))] +#[cfg_attr( + doc_cfg, + doc(cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" + ))) +)] +pub mod simple; + pub trait SuiSigner { fn sign_transaction(&self, transaction: &Transaction) -> Result; fn sign_personal_message( diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-sdk-crypto/src/secp256k1.rs index e24d1cd78..d44ccb9f2 100644 --- a/crates/sui-sdk-crypto/src/secp256k1.rs +++ b/crates/sui-sdk-crypto/src/secp256k1.rs @@ -70,18 +70,23 @@ impl Signer for Secp256k1PrivateKey { } } -impl Signer for Secp256k1PrivateKey { - fn try_sign(&self, msg: &[u8]) -> Result { +impl Signer for Secp256k1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { >::try_sign(self, msg).map(|signature| { - let signature = SimpleSignature::Secp256k1 { + SimpleSignature::Secp256k1 { signature, public_key: self.public_key(), - }; - UserSignature::Simple(signature) + } }) } } +impl Signer for Secp256k1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(UserSignature::Simple) + } +} + impl SuiSigner for Secp256k1PrivateKey { fn sign_transaction(&self, transaction: &Transaction) -> Result { let msg = transaction.signing_digest(); @@ -116,12 +121,12 @@ impl Verifier for Secp256k1VerifyingKey { } } -impl Verifier for Secp256k1VerifyingKey { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Secp256k1 { +impl Verifier for Secp256k1VerifyingKey { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Secp256k1 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not a secp256k1 signature")); }; @@ -136,6 +141,16 @@ impl Verifier for Secp256k1VerifyingKey { } } +impl Verifier for Secp256k1VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a secp256k1 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Secp256k1VerifyingKey { fn verify_transaction( &self, @@ -165,12 +180,12 @@ impl Secp256k1Verifier { } } -impl Verifier for Secp256k1Verifier { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Secp256k1 { +impl Verifier for Secp256k1Verifier { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Secp256k1 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not a secp256k1 signature")); }; @@ -181,6 +196,16 @@ impl Verifier for Secp256k1Verifier { } } +impl Verifier for Secp256k1Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a secp256k1 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Secp256k1Verifier { fn verify_transaction( &self, @@ -233,4 +258,20 @@ mod test { .verify_personal_message(&message, &signature) .unwrap(); } + + #[test] + fn personal_message_signing_fixture() { + let key = [ + 172, 12, 96, 180, 207, 143, 111, 151, 81, 57, 242, 89, 74, 5, 150, 51, 56, 111, 245, + 150, 182, 30, 149, 178, 29, 255, 188, 27, 48, 241, 151, 193, + ]; + + let signer = Secp256k1PrivateKey::new(key).unwrap(); + + let message = PersonalMessage(b"hello".into()); + let sig = signer.sign_personal_message(&message).unwrap(); + let external_sig = "AVFAWGjuD8+xUoc6jMC0lKqMtT+4ukln7vz+8Nuv+EbYKl47jwzOWn39maDsqu81kezLPgLzz6o/AfSE0M9+jVwClcrtiuyUggEt/6CEZi8+JQ+NS9TmOhPBZV2X1KjhGCw="; + let b64 = sig.to_base64(); + assert_eq!(external_sig, b64); + } } diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-sdk-crypto/src/secp256r1.rs index c3ca83ebe..5219b8a73 100644 --- a/crates/sui-sdk-crypto/src/secp256r1.rs +++ b/crates/sui-sdk-crypto/src/secp256r1.rs @@ -70,18 +70,23 @@ impl Signer for Secp256r1PrivateKey { } } -impl Signer for Secp256r1PrivateKey { - fn try_sign(&self, msg: &[u8]) -> Result { +impl Signer for Secp256r1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { >::try_sign(self, msg).map(|signature| { - let signature = SimpleSignature::Secp256r1 { + SimpleSignature::Secp256r1 { signature, public_key: self.public_key(), - }; - UserSignature::Simple(signature) + } }) } } +impl Signer for Secp256r1PrivateKey { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(UserSignature::Simple) + } +} + impl SuiSigner for Secp256r1PrivateKey { fn sign_transaction(&self, transaction: &Transaction) -> Result { let msg = transaction.signing_digest(); @@ -116,12 +121,12 @@ impl Verifier for Secp256r1VerifyingKey { } } -impl Verifier for Secp256r1VerifyingKey { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Secp256r1 { +impl Verifier for Secp256r1VerifyingKey { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Secp256r1 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not a secp256r1 signature")); }; @@ -136,6 +141,16 @@ impl Verifier for Secp256r1VerifyingKey { } } +impl Verifier for Secp256r1VerifyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a secp256r1 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Secp256r1VerifyingKey { fn verify_transaction( &self, @@ -165,12 +180,12 @@ impl Secp256r1Verifier { } } -impl Verifier for Secp256r1Verifier { - fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { - let UserSignature::Simple(SimpleSignature::Secp256r1 { +impl Verifier for Secp256r1Verifier { + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + let SimpleSignature::Secp256r1 { signature, public_key, - }) = signature + } = signature else { return Err(SignatureError::from_source("not a secp256r1 signature")); }; @@ -181,6 +196,16 @@ impl Verifier for Secp256r1Verifier { } } +impl Verifier for Secp256r1Verifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a secp256r1 signature")); + }; + + >::verify(self, message, signature) + } +} + impl SuiVerifier for Secp256r1Verifier { fn verify_transaction( &self, @@ -233,4 +258,19 @@ mod test { .verify_personal_message(&message, &signature) .unwrap(); } + + #[test] + fn personal_message_signing_fixture() { + let key = [ + 167, 44, 116, 0, 51, 221, 254, 179, 210, 44, 93, 196, 125, 155, 85, 94, 29, 41, 13, 60, + 59, 132, 69, 84, 176, 217, 77, 49, 25, 113, 118, 125, + ]; + let signer = Secp256r1PrivateKey::new(key); + + let message = PersonalMessage(b"hello".into()); + let sig = signer.sign_personal_message(&message).unwrap(); + let external_sig = "AlqWPdkIE2bZAUquKv2Tdh9i+Ih+rVSQXH/YsgvwkmeOJR0YLjL/kadivoPtiQkvZBQ1ZI8eDZxe8SaLniwoT88Dh+/vAuGf1UrouFTdefpBEWn3apy8x3EexN5c5ESzGDc="; + let b64 = sig.to_base64(); + assert_eq!(external_sig, b64); + } } diff --git a/crates/sui-sdk-crypto/src/simple.rs b/crates/sui-sdk-crypto/src/simple.rs new file mode 100644 index 000000000..90f626498 --- /dev/null +++ b/crates/sui-sdk-crypto/src/simple.rs @@ -0,0 +1,81 @@ +use crate::{SignatureError, SuiVerifier}; +use signature::Verifier; +use sui_sdk::types::{SimpleSignature, UserSignature}; + +pub struct SimpleVerifier; + +impl Verifier for SimpleVerifier { + #[allow(unused_variables)] + fn verify(&self, message: &[u8], signature: &SimpleSignature) -> Result<(), SignatureError> { + match signature { + #[cfg(feature = "ed25519")] + SimpleSignature::Ed25519 { + signature, + public_key, + } => { + let verifying_key = crate::ed25519::Ed25519VerifyingKey::new(public_key)?; + verifying_key.verify(message, signature) + } + #[cfg(not(feature = "ed25519"))] + SimpleSignature::Ed25519 { .. } => Err(SignatureError::from_source( + "support for ed25519 is not enabled", + )), + + #[cfg(feature = "secp256k1")] + SimpleSignature::Secp256k1 { + signature, + public_key, + } => { + let verifying_key = crate::secp256k1::Secp256k1VerifyingKey::new(public_key)?; + verifying_key.verify(message, signature) + } + #[cfg(not(feature = "secp256k1"))] + SimpleSignature::Secp256k1 { .. } => Err(SignatureError::from_source( + "support for secp256k1 is not enabled", + )), + + #[cfg(feature = "secp256r1")] + SimpleSignature::Secp256r1 { + signature, + public_key, + } => { + let verifying_key = crate::secp256r1::Secp256r1VerifyingKey::new(public_key)?; + verifying_key.verify(message, signature) + } + #[cfg(not(feature = "secp256r1"))] + SimpleSignature::Secp256r1 { .. } => Err(SignatureError::from_source( + "support for secp256r1 is not enabled", + )), + } + } +} + +impl Verifier for SimpleVerifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a simple signature")); + }; + + >::verify(self, message, signature) + } +} + +impl SuiVerifier for SimpleVerifier { + fn verify_transaction( + &self, + transaction: &sui_sdk::types::Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &sui_sdk::types::PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} diff --git a/crates/sui-sdk-crypto/src/zklogin/mod.rs b/crates/sui-sdk-crypto/src/zklogin/mod.rs new file mode 100644 index 000000000..9d23065f3 --- /dev/null +++ b/crates/sui-sdk-crypto/src/zklogin/mod.rs @@ -0,0 +1,258 @@ +use std::collections::HashMap; + +use crate::{SignatureError, SuiVerifier}; +use poseidon::POSEIDON; +use signature::Verifier; +use sui_sdk::types::{Claim, Jwk, JwkId, UserSignature, ZkLoginAuthenticator, ZkLoginInputs}; + +mod poseidon; +mod verify; + +#[cfg(test)] +mod tests; + +pub struct ZkloginVerifier { + proof_verifying_key: verify::VerifyingKey, + jwks: HashMap, +} + +impl ZkloginVerifier { + fn new(proof_verifying_key: verify::VerifyingKey) -> Self { + Self { + proof_verifying_key, + jwks: Default::default(), + } + } + + pub fn new_mainnet() -> Self { + Self::new(verify::VerifyingKey::new_mainnet()) + } + + pub fn new_dev() -> Self { + Self::new(verify::VerifyingKey::new_dev()) + } + + pub fn jwks(&self) -> &HashMap { + &self.jwks + } + + pub fn jwks_mut(&mut self) -> &mut HashMap { + &mut self.jwks + } +} + +impl Verifier for ZkloginVerifier { + fn verify( + &self, + message: &[u8], + signature: &ZkLoginAuthenticator, + ) -> Result<(), SignatureError> { + // 1. check that we have a valid corrisponding Jwk + let jwt_details = JwtDetails::from_zklogin_inputs(&signature.inputs)?; + let jwk = self.jwks.get(&jwt_details.id).ok_or_else(|| { + SignatureError::from_source(format!( + "unable to find corrisponding jwk with id '{:?}' for provided authenticator", + jwt_details.id + )) + })?; + + // 2. verify that the provided SimpleSignature is valid + crate::simple::SimpleVerifier.verify(message, &signature.signature)?; + + // 3. verify groth16 proof + self.proof_verifying_key.verify_zklogin( + jwk, + &signature.inputs, + &signature.signature, + signature.max_epoch, + ) + } +} + +impl Verifier for ZkloginVerifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::ZkLogin(zklogin_authenticator) = signature else { + return Err(SignatureError::from_source("not a zklogin signature")); + }; + + self.verify(message, zklogin_authenticator.as_ref()) + } +} + +impl SuiVerifier for ZkloginVerifier { + fn verify_transaction( + &self, + transaction: &sui_sdk::types::Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &sui_sdk::types::PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +/// A structed of parsed JWT details, consists of kid, header, iss. +#[derive(Debug, Clone, PartialEq, Eq)] +struct JwtDetails { + header: JwtHeader, + id: JwkId, +} + +impl JwtDetails { + fn from_zklogin_inputs(inputs: &ZkLoginInputs) -> Result { + const ISS: &str = "iss"; + + let header = JwtHeader::from_base64(&inputs.header_base64)?; + let id = JwkId { + iss: verify_extended_claim(&inputs.iss_base64_details, ISS)?, + kid: header.kid.clone(), + }; + Ok(JwtDetails { header, id }) + } +} + +/// Struct that represents a standard JWT header according to +/// https://openid.net/specs/openid-connect-core-1_0.html +#[derive(Debug, Clone, PartialEq, Eq)] +struct JwtHeader { + alg: String, + kid: String, + typ: Option, +} + +impl JwtHeader { + fn from_base64(s: &str) -> Result { + use base64ct::{Base64UrlUnpadded, Encoding}; + + #[derive(serde_derive::Serialize, serde_derive::Deserialize)] + struct Header { + alg: String, + kid: String, + #[serde(skip_serializing_if = "Option::is_none")] + typ: Option, + } + + let header_bytes = Base64UrlUnpadded::decode_vec(s) + .map_err(|e| SignatureError::from_source(e.to_string()))?; + let Header { alg, kid, typ } = + serde_json::from_slice(&header_bytes).map_err(SignatureError::from_source)?; + if alg != "RS256" { + return Err(SignatureError::from_source("jwt alg must be RS256")); + } + Ok(Self { alg, kid, typ }) + } +} + +/// Parse the extended claim json value to its claim value, using the expected claim key. +fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result { + /// Map a base64 string to a bit array by taking each char's index and convert it to binary form with one bit per u8 + /// element in the output. Returns SignatureError if one of the characters is not in the base64 charset. + fn base64_to_bitarray(input: &str) -> Result, SignatureError> { + use itertools::Itertools; + + const BASE64_URL_CHARSET: &str = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + input + .chars() + .map(|c| { + BASE64_URL_CHARSET + .find(c) + .map(|index| index as u8) + .map(|index| (0..6).rev().map(move |i| index >> i & 1)) + .ok_or_else(|| SignatureError::from_source("base64_to_bitarry invalid input")) + }) + .flatten_ok() + .collect() + } + + /// Convert a bitarray (each bit is represented by a u8) to a byte array by taking each 8 bits as a + /// byte in big-endian format. + fn bitarray_to_bytearray(bits: &[u8]) -> Result, SignatureError> { + if bits.len() % 8 != 0 { + return Err(SignatureError::from_source( + "bitarray_to_bytearray invalid input", + )); + } + Ok(bits + .chunks(8) + .map(|chunk| { + let mut byte = 0u8; + for (i, bit) in chunk.iter().rev().enumerate() { + byte |= bit << i; + } + byte + }) + .collect()) + } + + /// Parse the base64 string, add paddings based on offset, and convert to a bytearray. + fn decode_base64_url(s: &str, index_mod_4: &u8) -> Result { + if s.len() < 2 { + return Err(SignatureError::from_source("Base64 string smaller than 2")); + } + let mut bits = base64_to_bitarray(s)?; + match index_mod_4 { + 0 => {} + 1 => { + bits.drain(..2); + } + 2 => { + bits.drain(..4); + } + _ => { + return Err(SignatureError::from_source("Invalid first_char_offset")); + } + } + + let last_char_offset = (index_mod_4 + s.len() as u8 - 1) % 4; + match last_char_offset { + 3 => {} + 2 => { + bits.drain(bits.len() - 2..); + } + 1 => { + bits.drain(bits.len() - 4..); + } + _ => { + return Err(SignatureError::from_source("Invalid last_char_offset")); + } + } + + if bits.len() % 8 != 0 { + return Err(SignatureError::from_source("Invalid bits length")); + } + + Ok(std::str::from_utf8(&bitarray_to_bytearray(&bits)?) + .map_err(|_| SignatureError::from_source("Invalid UTF8 string"))? + .to_owned()) + } + + let extended_claim = decode_base64_url(&claim.value, &claim.index_mod_4)?; + + // Last character of each extracted_claim must be '}' or ',' + if !(extended_claim.ends_with('}') || extended_claim.ends_with(',')) { + return Err(SignatureError::from_source("Invalid extended claim")); + } + + let json_str = format!("{{{}}}", &extended_claim[..extended_claim.len() - 1]); + + serde_json::from_str::(&json_str) + .map_err(SignatureError::from_source)? + .as_object_mut() + .and_then(|o| o.get_mut(expected_key)) + .map(serde_json::Value::take) + .and_then(|v| match v { + serde_json::Value::String(s) => Some(s), + _ => None, + }) + .ok_or_else(|| SignatureError::from_source("invalid extended claim")) +} diff --git a/crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs b/crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs new file mode 100644 index 000000000..34b4332ba --- /dev/null +++ b/crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs @@ -0,0 +1,13015 @@ +pub fn constants() -> (Vec>, Vec>>) { + let c_str: Vec> = vec![ + vec![ + "4417881134626180770308697923359573201005643519861877412381846989312604493735", + "5433650512959517612316327474713065966758808864213826738576266661723522780033", + "13641176377184356099764086973022553863760045607496549923679278773208775739952", + "17949713444224994136330421782109149544629237834775211751417461773584374506783", + "13765628375339178273710281891027109699578766420463125835325926111705201856003", + "19179513468172002314585757290678967643352171735526887944518845346318719730387", + "5157412437176756884543472904098424903141745259452875378101256928559722612176", + "535160875740282236955320458485730000677124519901643397458212725410971557409", + "1050793453380762984940163090920066886770841063557081906093018330633089036729", + "10665495010329663932664894101216428400933984666065399374198502106997623173873", + "19965634623406616956648724894636666805991993496469370618546874926025059150737", + "13007250030070838431593222885902415182312449212965120303174723305710127422213", + "16877538715074991604507979123743768693428157847423939051086744213162455276374", + "18211747749504876135588847560312685184956239426147543810126553367063157141465", + "18151553319826126919739798892854572062191241985315767086020821632812331245635", + "19957033149976712666746140949846950406660099037474791840946955175819555930825", + "3469514863538261843186854830917934449567467100548474599735384052339577040841", + "989698510043911779243192466312362856042600749099921773896924315611668507708", + "12568377015646290945235387813564567111330046038050864455358059568128000172201", + "20856104135605479600325529349246932565148587186338606236677138505306779314172", + "8206918720503535523121349917159924938835810381723474192155637697065780938424", + "1309058477013932989380617265069188723120054926187607548493110334522527703566", + "14076116939332667074621703729512195584105250395163383769419390236426287710606", + "10153498892749751942204288991871286290442690932856658983589258153608012428674", + "18202499207234128286137597834010475797175973146805180988367589376893530181575", + "12739388830157083522877690211447248168864006284243907142044329113461613743052", + "15123358710467780770838026754240340042441262572309759635224051333176022613949", + "19925004701844594370904593774447343836015483888496504201331110250494635362184", + "10352416606816998476681131583320899030072315953910679608943150613208329645891", + "10567371822366244361703342347428230537114808440249611395507235283708966113221", + "5635498582763880627392290206431559361272660937399944184533035305989295959602", + "11866432933224219174041051738704352719163271639958083608224676028593315904909", + "5795020705294401441272215064554385591292330721703923167136157291459784140431", + "9482202378699252817564375087302794636287866584767523335624368774856230692758", + "4245237636894546151746468406560945873445548423466753843402086544922216329298", + "12000500941313982757584712677991730019124834399479314697467598397927435905133", + "7596790274058425558167520209857956363736666939016807569082239187494363541787", + "2484867918246116343205467273440098378820186751202461278013576281097918148877", + "18312645949449997391810445935615409295369169383463185688973803378104013950190", + "15320686572748723004980855263301182130424010735782762814513954166519592552733", + "12618438900597948888520621062416758747872180395546164387827245287017031303859", + "17438141672027706116733201008397064011774368832458707512367404736905021019585", + "6374197807230665998865688675365359100400438034755781666913068586172586548950", + "2189398913433273865510950346186699930188746169476472274335177556702504595264", + "6268495580028970231803791523870131137294646402347399003576649137450213034606", + "17896250365994900261202920044129628104272791547990619503076839618914047059275", + "13692156312448722528008862371944543449350293305158722920787736248435893008873", + "15234446864368744483209945022439268713300180233589581910497691316744177619376", + "1572426502623310766593681563281600503979671244997798691029595521622402217227", + "80103447810215150918585162168214870083573048458555897999822831203653996617", + "8228820324013669567851850635126713973797711779951230446503353812192849106342", + "5375851433746509614045812476958526065449377558695752132494533666370449415873", + "12115998939203497346386774317892338270561208357481805380546938146796257365018", + "9764067909645821279940531410531154041386008396840887338272986634350423466622", + "8538708244538850542384936174629541085495830544298260335345008245230827876882", + "7140127896620013355910287215441004676619168261422440177712039790284719613114", + "14297402962228458726038826185823085337698917275385741292940049024977027409762", + "6667115556431351074165934212337261254608231545257434281887966406956835140819", + "20226761165244293291042617464655196752671169026542832236139342122602741090001", + "12038289506489256655759141386763477208196694421666339040483042079632134429119", + "19027757334170818571203982241812412991528769934917288000224335655934473717551", + "16272152964456553579565580463468069884359929612321610357528838696790370074720", + "2500392889689246014710135696485946334448570271481948765283016105301740284071", + "8595254970528530312401637448610398388203855633951264114100575485022581946023", + "11635945688914011450976408058407206367914559009113158286982919675551688078198", + "614739068603482619581328040478536306925147663946742687395148680260956671871", + "18692271780377861570175282183255720350972693125537599213951106550953176268753", + "4987059230784976306647166378298632695585915319042844495357753339378260807164", + "21851403978498723616722415377430107676258664746210815234490134600998983955497", + "9830635451186415300891533983087800047564037813328875992115573428596207326204", + "4842706106434537116860242620706030229206345167233200482994958847436425185478", + "6422235064906823218421386871122109085799298052314922856340127798647926126490", + "4564364104986856861943331689105797031330091877115997069096365671501473357846", + "1944043894089780613038197112872830569538541856657037469098448708685350671343", + "21179865974855950600518216085229498748425990426231530451599322283119880194955", + "14296697761894107574369608843560006996183955751502547883167824879840894933162", + "12274619649702218570450581712439138337725246879938860735460378251639845671898", + "16371396450276899401411886674029075408418848209575273031725505038938314070356", + "3702561221750983937578095019779188631407216522704543451228773892695044653565", + "19721616877735564664624984774636557499099875603996426215495516594530838681980", + "6383350109027696789969911008057747025018308755462287526819231672217685282429", + "20860583956177367265984596617324237471765572961978977333122281041544719622905", + "5766390934595026947545001478457407504285452477687752470140790011329357286275", + "4043175758319898049344746138515323336207420888499903387536875603879441092484", + "15579382179133608217098622223834161692266188678101563820988612253342538956534", + "1864640783252634743892105383926602930909039567065240010338908865509831749824", + "15943719865023133586707144161652035291705809358178262514871056013754142625673", + "2326415993032390211558498780803238091925402878871059708106213703504162832999", + "19995326402773833553207196590622808505547443523750970375738981396588337910289", + "5143583711361588952673350526320181330406047695593201009385718506918735286622", + "15436006486881920976813738625999473183944244531070780793506388892313517319583", + "16660446760173633166698660166238066533278664023818938868110282615200613695857", + "4966065365695755376133119391352131079892396024584848298231004326013366253934", + "20683781957411705574951987677641476019618457561419278856689645563561076926702", + "17280836839165902792086432296371645107551519324565649849400948918605456875699", + "17045635513701208892073056357048619435743564064921155892004135325530808465371", + "17055032967194400710390142791334572297458033582458169295920670679093585707295", + "15727174639569115300068198908071514334002742825679221638729902577962862163505", + "1001755657610446661315902885492677747789366510875120894840818704741370398633", + "18638547332826171619311285502376343504539399518545103511265465604926625041234", + "6751954224763196429755298529194402870632445298969935050224267844020826420799", + "3526747115904224771452549517614107688674036840088422555827581348280834879405", + "15705897908180497062880001271426561999724005008972544196300715293701537574122", + "574386695213920937259007343820417029802510752426579750428758189312416867750", + "15973040855000600860816974646787367136127946402908768408978806375685439868553", + "20934130413948796333037139460875996342810005558806621330680156931816867321122", + "6918585327145564636398173845411579411526758237572034236476079610890705810764", + "14158163500813182062258176233162498241310167509137716527054939926126453647182", + "4164602626597695668474100217150111342272610479949122406544277384862187287433", + "12146526846507496913615390662823936206892812880963914267275606265272996025304", + "10153527926900017763244212043512822363696541810586522108597162891799345289938", + "13564663485965299104296214940873270349072051793008946663855767889066202733588", + "5612449256997576125867742696783020582952387615430650198777254717398552960096", + "12151885480032032868507892738683067544172874895736290365318623681886999930120", + "380452237704664384810613424095477896605414037288009963200982915188629772177", + "9067557551252570188533509616805287919563636482030947363841198066124642069518", + "21280306817619711661335268484199763923870315733198162896599997188206277056900", + "5567165819557297006750252582140767993422097822227408837378089569369734876257", + "10411936321072105429908396649383171465939606386380071222095155850987201580137", + "21338390051413922944780864872652000187403217966653363270851298678606449622266", + "12156296560457833712186127325312904760045212412680904475497938949653569234473", + "4271647814574748734312113971565139132510281260328947438246615707172526380757", + "9061738206062369647211128232833114177054715885442782773131292534862178874950", + "10134551893627587797380445583959894183158393780166496661696555422178052339133", + "8932270237664043612366044102088319242789325050842783721780970129656616386103", + "3339412934966886386194449782756711637636784424032779155216609410591712750636", + "9704903972004596791086522314847373103670545861209569267884026709445485704400", + "17467570179597572575614276429760169990940929887711661192333523245667228809456", + ], + vec![ + "6745197990210204598374042828761989596302876299545964402857411729872131034734", + "426281677759936592021316809065178817848084678679510574715894138690250139748", + "4014188762916583598888942667424965430287497824629657219807941460227372577781", + "21328925083209914769191926116470334003273872494252651254811226518870906634704", + "19525217621804205041825319248827370085205895195618474548469181956339322154226", + "1402547928439424661186498190603111095981986484908825517071607587179649375482", + "18320863691943690091503704046057443633081959680694199244583676572077409194605", + "17709820605501892134371743295301255810542620360751268064484461849423726103416", + "15970119011175710804034336110979394557344217932580634635707518729185096681010", + "9818625905832534778628436765635714771300533913823445439412501514317783880744", + "6235167673500273618358172865171408902079591030551453531218774338170981503478", + "12575685815457815780909564540589853169226710664203625668068862277336357031324", + "7381963244739421891665696965695211188125933529845348367882277882370864309593", + "14214782117460029685087903971105962785460806586237411939435376993762368956406", + "13382692957873425730537487257409819532582973556007555550953772737680185788165", + "2203881792421502412097043743980777162333765109810562102330023625047867378813", + "2916799379096386059941979057020673941967403377243798575982519638429287573544", + "4341714036313630002881786446132415875360643644216758539961571543427269293497", + "2340590164268886572738332390117165591168622939528604352383836760095320678310", + "5222233506067684445011741833180208249846813936652202885155168684515636170204", + "7963328565263035669460582454204125526132426321764384712313576357234706922961", + "1394121618978136816716817287892553782094854454366447781505650417569234586889", + "20251767894547536128245030306810919879363877532719496013176573522769484883301", + "141695147295366035069589946372747683366709960920818122842195372849143476473", + "15919677773886738212551540894030218900525794162097204800782557234189587084981", + "2616624285043480955310772600732442182691089413248613225596630696960447611520", + "4740655602437503003625476760295930165628853341577914460831224100471301981787", + "19201590924623513311141753466125212569043677014481753075022686585593991810752", + "12116486795864712158501385780203500958268173542001460756053597574143933465696", + "8481222075475748672358154589993007112877289817336436741649507712124418867136", + "5181207870440376967537721398591028675236553829547043817076573656878024336014", + "1576305643467537308202593927724028147293702201461402534316403041563704263752", + "2555752030748925341265856133642532487884589978209403118872788051695546807407", + "18840924862590752659304250828416640310422888056457367520753407434927494649454", + "14593453114436356872569019099482380600010961031449147888385564231161572479535", + "20826991704411880672028799007667199259549645488279985687894219600551387252871", + "9159011389589751902277217485643457078922343616356921337993871236707687166408", + "5605846325255071220412087261490782205304876403716989785167758520729893194481", + "1148784255964739709393622058074925404369763692117037208398835319441214134867", + "20945896491956417459309978192328611958993484165135279604807006821513499894540", + "229312996389666104692157009189660162223783309871515463857687414818018508814", + "21184391300727296923488439338697060571987191396173649012875080956309403646776", + "21853424399738097885762888601689700621597911601971608617330124755808946442758", + "12776298811140222029408960445729157525018582422120161448937390282915768616621", + "7556638921712565671493830639474905252516049452878366640087648712509680826732", + "19042212131548710076857572964084011858520620377048961573689299061399932349935", + "12871359356889933725034558434803294882039795794349132643274844130484166679697", + "3313271555224009399457959221795880655466141771467177849716499564904543504032", + "15080780006046305940429266707255063673138269243146576829483541808378091931472", + "21300668809180077730195066774916591829321297484129506780637389508430384679582", + "20480395468049323836126447690964858840772494303543046543729776750771407319822", + "10034492246236387932307199011778078115444704411143703430822959320969550003883", + "19584962776865783763416938001503258436032522042569001300175637333222729790225", + "20155726818439649091211122042505326538030503429443841583127932647435472711802", + "13313554736139368941495919643765094930693458639277286513236143495391474916777", + "14606609055603079181113315307204024259649959674048912770003912154260692161833", + "5563317320536360357019805881367133322562055054443943486481491020841431450882", + "10535419877021741166931390532371024954143141727751832596925779759801808223060", + "12025323200952647772051708095132262602424463606315130667435888188024371598063", + "2906495834492762782415522961458044920178260121151056598901462871824771097354", + "19131970618309428864375891649512521128588657129006772405220584460225143887876", + "8896386073442729425831367074375892129571226824899294414632856215758860965449", + "7748212315898910829925509969895667732958278025359537472413515465768989125274", + "422974903473869924285294686399247660575841594104291551918957116218939002865", + "6398251826151191010634405259351528880538837895394722626439957170031528482771", + "18978082967849498068717608127246258727629855559346799025101476822814831852169", + "19150742296744826773994641927898928595714611370355487304294875666791554590142", + "12896891575271590393203506752066427004153880610948642373943666975402674068209", + "9546270356416926575977159110423162512143435321217584886616658624852959369669", + "2159256158967802519099187112783460402410585039950369442740637803310736339200", + "8911064487437952102278704807713767893452045491852457406400757953039127292263", + "745203718271072817124702263707270113474103371777640557877379939715613501668", + "19313999467876585876087962875809436559985619524211587308123441305315685710594", + "13254105126478921521101199309550428567648131468564858698707378705299481802310", + "1842081783060652110083740461228060164332599013503094142244413855982571335453", + "9630707582521938235113899367442877106957117302212260601089037887382200262598", + "5066637850921463603001689152130702510691309665971848984551789224031532240292", + "4222575506342961001052323857466868245596202202118237252286417317084494678062", + "2919565560395273474653456663643621058897649501626354982855207508310069954086", + "6828792324689892364977311977277548750189770865063718432946006481461319858171", + "2245543836264212411244499299744964607957732316191654500700776604707526766099", + "19602444885919216544870739287153239096493385668743835386720501338355679311704", + "8239538512351936341605373169291864076963368674911219628966947078336484944367", + "15053013456316196458870481299866861595818749671771356646798978105863499965417", + "7173615418515925804810790963571435428017065786053377450925733428353831789901", + "8239211677777829016346247446855147819062679124993100113886842075069166957042", + "15330855478780269194281285878526984092296288422420009233557393252489043181621", + "10014883178425964324400942419088813432808659204697623248101862794157084619079", + "14014440630268834826103915635277409547403899966106389064645466381170788813506", + "3580284508947993352601712737893796312152276667249521401778537893620670305946", + "2559754020964039399020874042785294258009596917335212876725104742182177996988", + "14898657953331064524657146359621913343900897440154577299309964768812788279359", + "2094037260225570753385567402013028115218264157081728958845544426054943497065", + "18051086536715129874440142649831636862614413764019212222493256578581754875930", + "21680659279808524976004872421382255670910633119979692059689680820959727969489", + "13950668739013333802529221454188102772764935019081479852094403697438884885176", + "9703845704528288130475698300068368924202959408694460208903346143576482802458", + "12064310080154762977097567536495874701200266107682637369509532768346427148165", + "16970760937630487134309762150133050221647250855182482010338640862111040175223", + "9790997389841527686594908620011261506072956332346095631818178387333642218087", + "16314772317774781682315680698375079500119933343877658265473913556101283387175", + "82044870826814863425230825851780076663078706675282523830353041968943811739", + "21696416499108261787701615667919260888528264686979598953977501999747075085778", + "327771579314982889069767086599893095509690747425186236545716715062234528958", + "4606746338794869835346679399457321301521448510419912225455957310754258695442", + "64499140292086295251085369317820027058256893294990556166497635237544139149", + "10455028514626281809317431738697215395754892241565963900707779591201786416553", + "10421411526406559029881814534127830959833724368842872558146891658647152404488", + "18848084335930758908929996602136129516563864917028006334090900573158639401697", + "13844582069112758573505569452838731733665881813247931940917033313637916625267", + "13488838454403536473492810836925746129625931018303120152441617863324950564617", + "15742141787658576773362201234656079648895020623294182888893044264221895077688", + "6756884846734501741323584200608866954194124526254904154220230538416015199997", + "7860026400080412708388991924996537435137213401947704476935669541906823414404", + "7871040688194276447149361970364037034145427598711982334898258974993423182255", + "20758972836260983284101736686981180669442461217558708348216227791678564394086", + "21723241881201839361054939276225528403036494340235482225557493179929400043949", + "19428469330241922173653014973246050805326196062205770999171646238586440011910", + "7969200143746252148180468265998213908636952110398450526104077406933642389443", + "10950417916542216146808986264475443189195561844878185034086477052349738113024", + "18149233917533571579549129116652755182249709970669448788972210488823719849654", + "3729796741814967444466779622727009306670204996071028061336690366291718751463", + "5172504399789702452458550583224415301790558941194337190035441508103183388987", + "6686473297578275808822003704722284278892335730899287687997898239052863590235", + "19426913098142877404613120616123695099909113097119499573837343516470853338513", + "5120337081764243150760446206763109494847464512045895114970710519826059751800", + "5055737465570446530938379301905385631528718027725177854815404507095601126720", + "14235578612970484492268974539959119923625505766550088220840324058885914976980", + "653592517890187950103239281291172267359747551606210609563961204572842639923", + "5507360526092411682502736946959369987101940689834541471605074817375175870579", + "7864202866011437199771472205361912625244234597659755013419363091895334445453", + "21294659996736305811805196472076519801392453844037698272479731199885739891648", + "13767183507040326119772335839274719411331242166231012705169069242737428254651", + "810181532076738148308457416289197585577119693706380535394811298325092337781", + "14232321930654703053193240133923161848171310212544136614525040874814292190478", + "16796904728299128263054838299534612533844352058851230375569421467352578781209", + "16256310366973209550759123431979563367001604350120872788217761535379268327259", + "19791658638819031543640174069980007021961272701723090073894685478509001321817", + "7046232469803978873754056165670086532908888046886780200907660308846356865119", + "16001732848952745747636754668380555263330934909183814105655567108556497219752", + "9737276123084413897604802930591512772593843242069849260396983774140735981896", + "11410895086919039954381533622971292904413121053792570364694836768885182251535", + "19098362474249267294548762387533474746422711206129028436248281690105483603471", + "11013788190750472643548844759298623898218957233582881400726340624764440203586", + "2206958256327295151076063922661677909471794458896944583339625762978736821035", + "7171889270225471948987523104033632910444398328090760036609063776968837717795", + "2510237900514902891152324520472140114359583819338640775472608119384714834368", + "8825275525296082671615660088137472022727508654813239986303576303490504107418", + "1481125575303576470988538039195271612778457110700618040436600537924912146613", + "16268684562967416784133317570130804847322980788316762518215429249893668424280", + "4681491452239189664806745521067158092729838954919425311759965958272644506354", + "3131438137839074317765338377823608627360421824842227925080193892542578675835", + "7930402370812046914611776451748034256998580373012248216998696754202474945793", + "8973151117361309058790078507956716669068786070949641445408234962176963060145", + "10223139291409280771165469989652431067575076252562753663259473331031932716923", + "2232089286698717316374057160056566551249777684520809735680538268209217819725", + "16930089744400890347392540468934821520000065594669279286854302439710657571308", + "21739597952486540111798430281275997558482064077591840966152905690279247146674", + "7508315029150148468008716674010060103310093296969466203204862163743615534994", + "11418894863682894988747041469969889669847284797234703818032750410328384432224", + "10895338268862022698088163806301557188640023613155321294365781481663489837917", + "18644184384117747990653304688839904082421784959872380449968500304556054962449", + "7414443845282852488299349772251184564170443662081877445177167932875038836497", + "5391299369598751507276083947272874512197023231529277107201098701900193273851", + "10329906873896253554985208009869159014028187242848161393978194008068001342262", + "4711719500416619550464783480084256452493890461073147512131129596065578741786", + "11943219201565014805519989716407790139241726526989183705078747065985453201504", + "4298705349772984837150885571712355513879480272326239023123910904259614053334", + "9999044003322463509208400801275356671266978396985433172455084837770460579627", + "4908416131442887573991189028182614782884545304889259793974797565686968097291", + "11963412684806827200577486696316210731159599844307091475104710684559519773777", + "20129916000261129180023520480843084814481184380399868943565043864970719708502", + "12884788430473747619080473633364244616344003003135883061507342348586143092592", + "20286808211545908191036106582330883564479538831989852602050135926112143921015", + "16282045180030846845043407450751207026423331632332114205316676731302016331498", + "4332932669439410887701725251009073017227450696965904037736403407953448682093", + "11105712698773407689561953778861118250080830258196150686012791790342360778288", + "21853934471586954540926699232107176721894655187276984175226220218852955976831", + "9807888223112768841912392164376763820266226276821186661925633831143729724792", + "13411808896854134882869416756427789378942943805153730705795307450368858622668", + "17906847067500673080192335286161014930416613104209700445088168479205894040011", + "14554387648466176616800733804942239711702169161888492380425023505790070369632", + "4264116751358967409634966292436919795665643055548061693088119780787376143967", + "2401104597023440271473786738539405349187326308074330930748109868990675625380", + "12251645483867233248963286274239998200789646392205783056343767189806123148785", + "15331181254680049984374210433775713530849624954688899814297733641575188164316", + "13108834590369183125338853868477110922788848506677889928217413952560148766472", + "6843160824078397950058285123048455551935389277899379615286104657075620692224", + "10151103286206275742153883485231683504642432930275602063393479013696349676320", + "7074320081443088514060123546121507442501369977071685257650287261047855962224", + "11413928794424774638606755585641504971720734248726394295158115188173278890938", + "7312756097842145322667451519888915975561412209738441762091369106604423801080", + "7181677521425162567568557182629489303281861794357882492140051324529826589361", + "15123155547166304758320442783720138372005699143801247333941013553002921430306", + "13409242754315411433193860530743374419854094495153957441316635981078068351329", + ], + vec![ + "11633431549750490989983886834189948010834808234699737327785600195936805266405", + "17353750182810071758476407404624088842693631054828301270920107619055744005334", + "11575173631114898451293296430061690731976535592475236587664058405912382527658", + "9724643380371653925020965751082872123058642683375812487991079305063678725624", + "20936725237749945635418633443468987188819556232926135747685274666391889856770", + "6427758822462294912934022562310355233516927282963039741999349770315205779230", + "16782979953202249973699352594809882974187694538612412531558950864304931387798", + "8979171037234948998646722737761679613767384188475887657669871981433930833742", + "5428827536651017352121626533783677797977876323745420084354839999137145767736", + "507241738797493565802569310165979445570507129759637903167193063764556368390", + "6711578168107599474498163409443059675558516582274824463959700553865920673097", + "2197359304646916921018958991647650011119043556688567376178243393652789311643", + "4634703622846121403803831560584049007806112989824652272428991253572845447400", + "17008376818199175111793852447685303011746023680921106348278379453039148937791", + "18430784755956196942937899353653692286521408688385681805132578732731487278753", + "4573768376486344895797915946239137669624900197544620153250805961657870918727", + "5624865188680173294191042415227598609140934495743721047183803859030618890703", + "8228252753786907198149068514193371173033070694924002912950645971088002709521", + "17586714789554691446538331362711502394998837215506284064347036653995353304693", + "12985198716830497423350597750558817467658937953000235442251074063454897365701", + "13480076116139680784838493959937969792577589073830107110893279354229821035984", + "480609231761423388761863647137314056373740727639536352979673303078459561332", + "19503345496799249258956440299354839375920540225688429628121751361906635419276", + "16837818502122887883669221005435922946567532037624537243846974433811447595173", + "5492108497278641078569490709794391352213168666744080628008171695469579703581", + "11365311159988448419785032079155356000691294261495515880484003277443744617083", + "13876891705632851072613751905778242936713392247975808888614530203269491723653", + "10660388389107698747692475159023710744797290186015856503629656779989214850043", + "18876318870401623474401728758498150977988613254023317877612912724282285739292", + "15543349138237018307536452195922365893694804703361435879256942490123776892424", + "2839988449157209999638903652853828318645773519300826410959678570041742458201", + "7566039810305694135184226097163626060317478635973510706368412858136696413063", + "6344830340705033582410486810600848473125256338903726340728639711688240744220", + "12475357769019880256619207099578191648078162511547701737481203260317463892731", + "13337401254840718303633782478677852514218549070508887338718446132574012311307", + "21161869193849404954234950798647336336709035097706159414187214758702055364571", + "20671052961616073313397254362345395594858011165315285344464242404604146448678", + "2772189387845778213446441819361180378678387127454165972767013098872140927416", + "3339032002224218054945450150550795352855387702520990006196627537441898997147", + "14919705931281848425960108279746818433850049439186607267862213649460469542157", + "17056699976793486403099510941807022658662936611123286147276760381688934087770", + "16144580075268719403964467603213740327573316872987042261854346306108421013323", + "15582343953927413680541644067712456296539774919658221087452235772880573393376", + "17528510080741946423534916423363640132610906812668323263058626230135522155749", + "3190600034239022251529646836642735752388641846393941612827022280601486805721", + "8463814172152682468446984305780323150741498069701538916468821815030498611418", + "16533435971270903741871235576178437313873873358463959658178441562520661055273", + "11845696835505436397913764735273748291716405946246049903478361223369666046634", + "18391057370973634202531308463652130631065370546571735004701144829951670507215", + "262537877325812689820791215463881982531707709719292538608229687240243203710", + "2187234489894387585309965540987639130975753519805550941279098789852422770021", + "19189656350920455659006418422409390013967064310525314160026356916172976152967", + "15839474183930359560478122372067744245080413846070743460407578046890458719219", + "1805019124769763805045852541831585930225376844141668951787801647576910524592", + "323592203814803486950280155834638828455175703393817797003361354810251742052", + "9780393509796825017346015868945480913627956475147371732521398519483580624282", + "14009429785059642386335012561867511048847749030947687313594053997432177705759", + "13749550162460745037234826077137388777330401847577727796245150843898019635981", + "19497187499283431845443758879472819384797584633472792651343926414232528405311", + "3708428802547661961864524194762556064568867603968214870300574294082023305587", + "1339414413482882567499652761996854155383863472782829777976929310155400981782", + "6396261245879814100794661157306877072718690153118140891315137894471052482309", + "2069661495404347929962833138824526893650803079024564477269192079629046031674", + "15793521554502133342917616035884588152451122589545915605459159078589855944361", + "17053424498357819626596285492499512504457128907932827007302385782133229252374", + "13658536470391360399708067455536748955260723760813498481671323619545320978896", + "21546095668130239633971575351786704948662094117932406102037724221634677838565", + "21411726238386979516934941789127061362496195649331822900487557574597304399109", + "1944776378988765673004063363506638781964264107780425928778257145151172817981", + "15590719714223718537172639598316570285163081746016049278954513732528516468773", + "1351266421179051765004709939353170430290500926943038391678843253157009556309", + "6772476224477167317130064764757502335545080109882028900432703947986275397548", + "10670120969725161535937685539136065944959698664551200616467222887025111751992", + "4731853626374224678749618809759140702342195350742653173378450474772131006181", + "14473527495914528513885847341981310373531349450901830749157165104135412062812", + "16937191362061486658876740597821783333355021670608822932942683228741190786143", + "5656559696428674390125424316117443507583679061659043998559560535270557939546", + "8897648276515725841133578021896617755369443750194849587616503841335248902806", + "14938684446722672719637788054570691068799510611164812175626676768545923371470", + "15284149043690546115252102390417391226617211133644099356880071475803043461465", + "2623479025068612775740107497276979457946709347831661908218182874823658838107", + "6809791961761836061129379546794905411734858375517368211894790874813684813988", + "2417620338751920563196799065781703780495622795713803712576790485412779971775", + "4445143310792944321746901285176579692343442786777464604312772017806735512661", + "1429019233589939118995503267516676481141938536269008901607126781291273208629", + "19874283200702583165110559932895904979843482162236139561356679724680604144459", + "13426632171723830006915194799390005513190035492503509233177687891041405113055", + "10582332261829184460912611488470654685922576576939233092337240630493625631748", + "21233753931561918964692715735079738969202507286592442257083521969358109931739", + "15570526832729960536088203016939646235070527502823725736220985057263010426410", + "9379993197409194016084018867205217180276068758980710078281820842068357746159", + "20771047769547788232530761122022227554484215799917531852224053856574439035591", + "20468066117407230615347036860121267564735050776924839007390915936603720868039", + "5488458379783632930817704196671117722181776789793038046303454621235628350505", + "1394272944960494549436156060041871735938329188644910029274839018389507786995", + "5147716541319265558364686380685869814344975511061045836883803841066664401308", + "14583556014436264794011679557180458872925270147116325433110111823036572987256", + "11881598145635709076820802010238799308467020773223027240974808290357539410246", + "1566675577370566803714158020143436746360531503329117352692311127363508063658", + "212097210828847555076368799807292486212366234848453077606919035866276438405", + "7447795983723838393344606913699113402588250391491430720006009618589586043349", + "7626475329478847982857743246276194948757851985510858890691733676098590062312", + "148936322117705719734052984176402258788283488576388928671173547788498414614", + "15456385653678559339152734484033356164266089951521103188900320352052358038156", + "18207029603568083031075933940507782729612798852390383193518574746240484434885", + "2783356767974552799246444090988849933848968900471538294757665724820698962027", + "2721136724873145834448711197875719736776242904173494370334510875996324906822", + "2101139679159828164567502977338446902934095964116292264803779234163802308621", + "8995221857405946029753863203034191016106353727035116779995228902499254557482", + "502050382895618998241481591846956281507455925731652006822624065608151015665", + "4998642074447347292230083981705092465562944918178587362047610976950173759150", + "9349925422548495396957991080641322437286312278286826683803695584372829655908", + "11780347248050333407713097022607360765169543706092266937432199545936788840710", + "17875657248128792902343900636176628524337469245418171053476833541334867949063", + "10366707960411170224546487410133378396211437543372531210718212258701730218585", + "16918708725327525329474486073529093971911689155838787615544405646587858805834", + "18845394288827839099791436411179859406694814287249240544635770075956540806104", + "9838806160073701591447223014625214979004281138811495046618998465898136914308", + "10285680425916086863571101560978592912547567902925573205991454216988033815759", + "1292119286233210185026381033809498665433650491423040630240164455269575958565", + "2665524343601461489082054230426835550060387413710679950970616347092017688857", + "13502286133892103192305476866434484921895765252706158317341618311553476426306", + "686854655578191041672292972738875170071982317195092845673566320025160026512", + "9315942923163981372372434957632152754092082859001311184186702151150554806508", + "17166793131238158480636170455452575971861309825745828685724097210995239015581", + "4443784618760852757287735236046535266034706880634443644576653970979377878608", + "21470445782021672615018345703580059646973568891521510437236903770708690160080", + "6932852445473908850835611723958058203645654625170962537129706393570586565567", + "17078326120157725640173982185667969009350208542843294226397809921509565607842", + "19251873001736801921864956728611772738233338338726553113352118847732921831266", + "13062907978694932362695258750558734366820802962383346229947907261606619788585", + "16576609187793673559170206379939616900133457644695219057683704871664434872406", + "17140499059660867342372156843620845644831519603574612796639429147195776838516", + "16226688173010504218547945848523900236290532501559570164276462499487632388445", + "2806068123803905806401128967330263340459046260107112845068533446899070326517", + "17788735370835052317224182711467216134690146479710634688273650370951230404901", + "9840665370904113434661468973557421114403401847108482949465899631150766783733", + "17357287363046228581837055771327121704742940914150998420465281177406182088510", + "8956082469997974864521346025916496675956939495318858500685756691488425559998", + "10583741436561099911914917245130852199607666337956354910388730829023746895549", + "15241902639811607164983030447109332729761435946009172128089506810551693978973", + "10889882303914055687481932975789161945462141459528413507160087442461090813788", + "19789561133254944544821898921133697408237804586549835559829396563401674817160", + "20741336668287037026472434608739333171202674306575625457456116338034432647230", + "17864073449995977742930566850933082711031717858550870842712972350665650521079", + "6017691253505466300212182439349954426085752315661098358839308909771637792741", + "5209125836207196173669497054522582922896061838702136844305036341250990710540", + "8138726312837322624537330169363664364899441867118983214176695868443641051381", + "15491983986041746833254372934846748393213690608865689646440909282144232382678", + "5054332867608171303802774230688792431028169804536607979111644888500809938980", + "15427030776591294577308915282298854681562344215287630895931797573417982096417", + "21754057982677295571284116502193272661309010996970316384923307174180521790164", + "16265286590463120486705206231835953324076688991892805307349612983237844034032", + "17679791107777049796013011282788633179411040182820636236163074053597517790779", + "4281652562868629887097957174897458165728741859103571825874408386197225591996", + "9168010397863299719604788533602757515513214141450093775967322808686129400625", + "17584182367226175071087689123358883902969885218985589531538416263709138156515", + "15671512310414658663135385639435845966109237059155734764323312289873534719186", + "10536294659491685326297777845632759824567028904726211134518740400643540109527", + "13431319759608247201135260841651365578663315527795431484765940626659812285319", + "9584697124715190200241839387725546204368618031045071660911490086723434692561", + "5180327104839158483066851400960171505063442195966219343315555549982472660055", + "18888217223053385111625483360538133292128748730565502371803782424772027937822", + "19535732913737027522540340630296365525208404217634392013266346283017745945894", + "8577759627886344995887423695190093296190181539234301534326157005220006624466", + "16793670928407147476673650839110019799844249677846432113010280456483595763987", + "13926032620965299897272071104154310460519723329016284975305942957859374938463", + "4794697578055472890255676575927616606591024075768967985031137397587590174501", + "3529566190782060578446859853852791941913086545101307988176595267965876143250", + "3975008029239568933166738482470827494289192118694622729549964538823092192163", + "17739094873244464728483944474780943281491793683051033330476367597242349886622", + "7367136451127531266518046223598095299278392589059366687082785080179161005418", + "11175297939460631138047404082172242706491354303440776362693987984031241399771", + "21687543815463985355165197827968086406938428974327951792877419032069230058777", + "21156136641989461785420005321350884477682466566148802533375726181416623358719", + "17347558768803521970212188258074365309929638984714303299899732035040892048478", + "16293716234695956076322008955071091921491953458541407305955104663269677475740", + "4206144021605871396668976569508168522675546062304959729829228403361714668567", + "19988050626299122864942213847548542155670073758974734015174045163059179151544", + "747972634423324369570795147739377097591383105262743308036321386836856106229", + "4612470951309047869982067912468200581649949743307592869671537990797895413707", + "9630852913694079049153027193127278569487291430069466630362958024525616303220", + "17941539917430916523930519432495442476511211427972760202450248798031711471474", + "20332911350443969653703295317915788278109458962706923653715140186132935894113", + "21764801803055897327474057344100833670291402543384934706514147201527191846513", + "18792043166429470991157980448329308661526906138700725174612608941551872082876", + "12308177224490762720061048892842527800271687977085172836705858261595655154325", + "6234555076867437297776538521925679658360922070165740193866337972293380196151", + "4651047048822067434403056477377459986292934655827821636179452835839127581305", + "4762047093602693619418269784972874862577325737690375448572644958129932507374", + "12373514879531674477721132062882065826558811149582829246378921774344318418269", + "452512704634345955634014968317367844987135264395068376894497483188243356523", + "21642936370936057063268550589361090955573362743817395689260298777690935495218", + "16170209200627740434842090607802586195654207376087117044989637541681675086276", + "11682826760471401430136435257946377996085824742031456481961511737883954750045", + "20628055165039718158878805520495324869838279647796500565701893698896698211929", + "16438375313036818694140277721632185529697783132872683043559674569424388375143", + "4855690425141732729622202649174026736476144238882856677953515240716341676853", + "11680269552161854836013784579325442981497075865007420427279871128110023581360", + "7052688838948398479718163301866620773458411881591190572311273079833122884040", + "10339199500986679207942447430230758709198802637648680544816596214595887890122", + "16310974164366557619327768780809157500356605306298690718711623172209302167675", + "4572051236178600578566286373491186377601851723137133424312445102215267283375", + "20933392620931420860078756859763708025350478446661033451436796955762857910093", + "10145870387395991071594748880090507240612313913083518483680901820696866812598", + "11173854866888110108878560284050142518686158431744851782991510385755602063727", + "3895357290105797542988795070918100785105415165483657264407967118738833241858", + "16358886674154007883356717944805100413481233709808000948036974385803613296849", + "10544067501284177518983466437755150442726536257903869254459488412549270232123", + "10495171258604974589451578238018388630585794890815982293891430761424812600427", + "13820724103604550843562070971473423552484851063169471886037640613650155173554", + "2334954333435579600152488915208745055087482119087065911968347050969338669409", + "15100284614446277058846085121308897497066957549089629374506920751044105723791", + "8493821960754696376711287628276980042183127459347650448500304251148421115590", + "18612435536889941393944858783110719304584209891406420832295898519317994950798", + "362101794940079733974215941991047456600874474038781578925062694203564740952", + "11020033081956343850903875701444955317664141075326494650405276926536449284939", + "9396289482656518627529185765935649373549564165735162258912975312413185691167", + "6879055176150676925438486069371149089824290576271090206945130252868108043422", + "12466610601804566637227883322591924115458766539177061670432424956205788935144", + "6570302110526154075173287644133038486970998888099669190857256824048085590052", + "20997862990590350605775941983360263378441519274215787225587679916056749626824", + "2642485040919927233352421501444361753154137311893617974318977215281720542724", + "18832940311494549247524002614969382413324906834787422940144532352384742506504", + "18751288968473015103659806087408412890105261892140397690496125593160830694164", + "13938622158186434739533995447553824444480420613323252752005511269934155122652", + "12878982657080117316101160964182202074759312554860119090514406868768962707099", + "13757859113119127982418426758782225628393556023865807897214601826218702003247", + "11817871682869491875135867072669251115204978941736982465520516648114811792373", + "11336448548896065624515261709306933490181794458266726453198857687608284871020", + "194970717714150352477887371297168267861902418496792228400198694925721020795", + "4999282817977533227652305360183045040853565298259070645110453061034932285549", + "17094174197873140035316532568922652294881600587639905417701074492648767414173", + "8484251464872873032022789624790167173458682056313339863651348894878144808746", + "10260366716129057466862964875306868898686918428814373470382979997177852668590", + "549263552864476084904464374701167884060947403076520259964592729731619317724", + "10052714818439832487575851829190658679562445501271745818931448693381812170889", + "1735373362835209096342827192021124337509188507323448903608623506589963950966", + "7998373949540733111485892137806629484517602009122941425332571732658301689428", + "9035170288660659483243066011612158174896974797912618405030929911180945246244", + "6458619567307414386633203375143968061892762498463026121155477954682976784731", + "12314261817227551876673777186352972884847144237148169773300066404053441924532", + "19869454329688183813243851218196625862680921049019496233616575272637276975230", + "20326917073492686652690019138603910654692396590122884746951129061818467704300", + "20403270805536666081472738304916561119325397964511536801752236086414818653063", + "2865941730880218719188224311916978807415673142487507504983320505748719154068", + "20614246027521726470902405957496110178017768563127335842405314212897493119848", + "12060194341463088508348622863463208827312128863463014006529428845777217660299", + "1128906798719793375274166820235650701301189774851381709919492584451845983197", + "19670876372911656158743764425809421400123168087389888660308456184201759209723", + "5647230694522866559497222129254930524469944430191328619422533907417776118543", + "318629082509194371490189248876734616088516535434806492900653650176451776632", + "13685970881538585172319228162662520285656571966985351768743970447782846353365", + "8283840607829148567836919316142994745766280854211662326632930274668867638198", + "8968895518159422029900464138741638511289476298837958524156654785428413265371", + "10061801991000917366002570579819627134666386452411986168205986791283562415829", + ], + vec![ + "6652655389322448471317061533546982911992554640679550674058582942754771150993", + "2411464732857349694082092299330329691469354396507353145272547491824343787723", + "21491443688002139478732659842894153142870918973450440713149176834049574486740", + "20196926676989483530222124573030747187074792043523478381149800153065505592963", + "12986278951352369831003505493892366673723882190521699331613883287145355738793", + "21126146258242782643168619000295062005037298340836817770565977031890883232034", + "15509665795506578582538177431401381655815033647735781734613703976071034655246", + "6989769181472743404364681671283889685042701491627165526899522083327752110839", + "7062179885254277466334896166987547257487047183881628199983668518000910197987", + "13842521112365108087725039904948872289730786568469683976372377853164252494752", + "3830559505943186272618534143266118508463381443414165428900505002474439179836", + "17704863473432653834041116667846189591617394753001613253930974854399793083900", + "875580502229441633079974792778818749112423694973231971690365132230865385439", + "1971134273535892826573832061354985059300866001765691176219451252512658771248", + "4865738840363990164915013008693722144676933915103280504727326977328013515878", + "1148603338028060679975883868174895825055359423662532941509525326937127571764", + "17506086433923270253695698017062834613463718526046463655503742220257039588796", + "21580033018107258179208198773211859664893072138803756118939260252922297665067", + "15411900706973212043830142913959920716501447427702082030760032355626616412240", + "12219699506725448409610279620972339448030565224304464695714944121760832152291", + "4525719544192047521328360848269156485222470829314314216955024799558286708479", + "19667371373588322336224317159113441765198420040800065314868656839300028747331", + "18916925604689704279265158984702141998345424765142129953154245912230835240445", + "12789343981741773931665143789673052782408749041041266509485929045869073416222", + "3094428508959717445577232225505810354980663487713729230015754183012845687401", + "18544590634480965569098056786078005630500574069468005220462377474861119476492", + "20990087440247450018723844204951613913840993427110495085701200965767234569705", + "17552251989761134508416634118845221324472178264364440017634233349418103869223", + "21000797802575507763447855752602183842956182733750968489641741136166640639409", + "19292751508591545849778577901067988044973302547209758604667395356943370737868", + "18314088316445539319869442180584299715533304874169767778761887632882728399870", + "15003745150856597539000559910957155642193629735521291045949652201905498569732", + "7839443900003691950104175747634267110464104444913379977500178134209666299140", + "13568305490393393394812598233983935295266242465548739772708079888867621061127", + "6453005227995051361096639028742707098785560656441339640433794156400437698140", + "1420171596348195609536167209221442141824294918625468780931400849866478645240", + "8347329128252205996443084339884155586061343024498283583400215109265013719709", + "7893774494551056447960817286805128884970061671041428326788899872964096959040", + "8970476243368194065341537088653900235777512204874037182428362347342487241690", + "239049405935404678508864874854718951364753739466303321590415544572014148257", + "15772878921699764223771017074289335629553777447709755479885293350677783703695", + "5416082112919155131434995906647355834510201879607888732259087164602171650389", + "4384524908062410354304345761652962203632712291085564157560146286207296352050", + "4210984612917608245844011498198864216639269565627982123611519493203177283139", + "18816442907032290878644773027005263628136050677095986565400687355912498966559", + "21443510232279945782338486087712914668515437675585863788610958361560172084515", + "3234314779308300525339049581669531363375743827111579883853941968586490182859", + "11029499234949696730080035941750777601416171837281021031653841244636590396063", + "11145210633226924132308292113124660576759662647204939721872338908644906571564", + "4583160563963432761409369246361117506465307518522062239686649163525543782173", + "9813992026757562966842771727657080117609486122615087352428596024939855084450", + "10084171857039480706430282187972782725948479260179367780776125786119489581409", + "3874212709197875589640151274548083098712939093643165182881681226579903752816", + "21595542491397091124739711708612983479307589335640792812157875295064235960610", + "2068530815441314105493629066002923150651375034543842424822712297257260726954", + "2673459852071215292298131389250564595426361004231758522146794940265552265806", + "8591046256746588406353455230465605224309754008961178558834659065898923355164", + "1020055192431352394776887540248098706183934464205704158014904833376067287118", + "11085709480582865378042656141271006552092494690130782253913953070642865919312", + "5673844083530503489429922596812992664928167369104420134641855283771127716005", + "10492199162275168254265892158402955076490959375050993042712629236807564461542", + "2280843393156259739329331366624245275580688891778782679394848304764573859886", + "6807797027131305026345508953353882265754363485246407959111359919046340709440", + "12692191384043938397944633973317584101723715998700063415107128429315536223446", + "19818676957110967644349139912613239435706480354664804036688552936554140369382", + "18055602608192644695569077694296748842203151828348990995792087204755925787339", + "20934555391215769430553078793246717148484784880715746179415906355043590089450", + "11420705181439111353998210442417752592951340005396931802449360401461783159557", + "19878854521263746227125001670931867821366047088989510542865511663910116386085", + "8568201846715449867087132677683368912214864824182424933182820310911278496552", + "19198701614488576617610339232794062430644024620523684127268879880793305460015", + "15262122764244854433806270478871594904740306012582364033343126589996733802868", + "6412758421155818207287638337822550233376667015263373809976157264137577776202", + "17371585001641430978766734501830788427263945848682170096055857509304472649262", + "20262970042379497707724791203314262108784948621691331141565359315001027736581", + "3859750447119748295302212198327542106766447958113540005985799287718502362717", + "1172269945800307665458943534144481495673510885455899148864236015097947176746", + "8164247467959680477306326470118519335673181279975551434197731340070491876250", + "4513977811114181395323888111232002391599397736872779927267726121435887238972", + "1075250595927474080680862736233039825365918646878264905022213616210377518447", + "18658420120424372681792175914064174056413842231969276203770574969914576681364", + "17769673440848360838244654765103041739044212539359630263894092078288342647801", + "4319086204044362848967484441065231939136453667264715596505827197873119273506", + "11221173270629292820060668122527062274557317856738971635698169204652845111606", + "8635411372759272135249379415383299350267629947167809163276219879514948820576", + "926977621651476360285369760355547766944001783780761167546467658394097283069", + "17702143780592866375901805387463459229828093905183622296234691441436877570082", + "629612289140842594504574984021125242351317893847688437087866691775821981724", + "19990548577495092294245865870717186004301934545721835081514347926537975465539", + "7124830628609719908679298707909792306162298058570958688501370177898647946696", + "14620227791860703231425817538142948793892390269806790476396226159679984968174", + "18495581997440241868332244230687799183899751339442721677540757155760745277888", + "16922065056093401385376103551657968760602009001905886435813054626317776258714", + "9969610601962874779035054685661667941954971427956866645694064022029705170229", + "15281641269114187762159685323068136816556739502211864119670902056596295644116", + "12114994625438879103001132949163961965524612903017200394727056658298824651596", + "4840986177718281128440833017205097196672382395936939379498412745183060615212", + "12847307562796769659308999092658905656250954898192781948610713494470441775991", + "20290096217351155282642224215178246911041509999959311313223857240001143893317", + "16151664509646153154405691138084115125600386733136285504828908979176781265710", + "13848845391482751436287906247470303487958950799995701248612703022979890932133", + "6335716166231441585596963683321661194889815181545222079376536449814718259931", + "1824302750039354704619545544386637317858342555634601563660279997221547953768", + "11327469654081586239268713126961534952233559223228327222485848924908493444712", + "10077703415170135154603829433031861799853903739210136452726077323833067256620", + "16368073884579385814331927334821006319227867093692644942500207970751483237405", + "10621580796499573269115131164341885791299038227955222944695715163010783205295", + "2099241376651019397894434242565225315652133572870234550073686122343103853816", + "17104632243449417396641550271977294699471083572885397875525767745512335891599", + "1935453754847256492223646005402770357836971113012418013930273797463411526183", + "7492761611332930896292052363224494314920390056637668407353957465667515477934", + "16836705924460095689555600825174696605443212968244843485187771119291716736958", + "16995495500678141665340056658079449793587669420913589967848082091551329904176", + "16097379973857697753436437302681608056543122759719328497348770844548177814262", + "17476569537128329379528694049566216604638194592812108658767104922628767500420", + "17997217989870184804787026924935938133194070033518938653831611194683423549591", + "17573343771046232580761295935281170028624495346579002725814597714902588657750", + "2450087639204541254902859018960918562514681200270997307467560465282168310665", + "17288084325555056222618040923753050382954155896826087372317882602328092535440", + "21837047676579063581498107773514419735425738753079336764356909012851439336687", + "370061273472837873736743292149368449614309676635341873070086681342317566380", + "420725183996224279379885018872359102189091670793820517618337092091910692771", + "4966571645678139143731798992823327185758562224229132271884647901363447388530", + "5039558223429273757296118284876763395391635773837549121798873235133698166026", + "14663152729953724779401067486012084029581847325524052152795817923033297673686", + "7201040456590575809960214033959496417566605177095808543357813677845263237276", + "16872945504528960415453618286121813996587432836152082188694652370255998768595", + "4914824783780909279212078186433590922437371437384817332713271291839616026466", + "17503018483514413315464207189113334433424965178631599286655188843769810245465", + "4087750571011463387872022799241315348852213278729592692674275176152296405923", + "4006961923780091252337105595934918049936238157468198971234322013673884171131", + "4481908842184366902145805444001507554481032302978790080019710161108326487967", + "13532316826436461968093937893872910736305115143550039673102602344678825540956", + "11602986656925867325907196773754426955346837006705269228226729102186031417465", + "15306992574062791537454541745213815567999895856471097922112648012979731636068", + "4497571735611504561173050536899411999551839050319538712220770383407135602945", + "2571242673174714867278075260451133687893879636121064640779554188161591611843", + "7070272070524747733177730083966686149849667613589868731851816020060781720851", + "1308310289745495626002351437755820460104812708071634598163946330870933261232", + "9483468192990391193401121929514821570714432121414330663623018046165053411090", + "7317568349845215930675847155716598288688799068821709820024570206796617676748", + "1918505733423704616434273602054555051755671749253598966287072464475922854850", + "15158168161084905689406532256983805923258003804476527617207287404280855731962", + "6855540174355511438343304861678411868002455139032857270673849263857877330771", + "5989863238360846166935911112885654223487221280254816980802479355446167746774", + "20283337058688740322296928691341300752003492063748410749625272920572074851396", + "18957132189629332408653055312790838576277703952267542471751593810468444454136", + "15764518568966520670995753676429154315765754748131847346608706222194564055358", + "7192524197002826721654253762628934164676539329903087107420445743247046038858", + "142950766663597487919643890566358241353679421113406309294925836697585309311", + "15012262168187689680572958978610204856600235635916074406168861726626292993057", + "20795666834671497603181209610179324236645779324677512349797033323222380300794", + "12650341271833683789775531792948185319868795529390391267833516836256688318306", + "5597700232877580665749288204589530549415282468176625525368428476461504532052", + "20949303924691159143653175365242293984396858344688574262804199947001630916385", + "10746523145835332938672833282581864816136388045771578294905302886974358762209", + "4998982766221590779170630035756820066555357949247521575936385387288356143784", + "6936999580131731861735955554005106460473097800566952971315565150681540640020", + "6670695360676548472482680016233507548657051302712214051977034166870814430578", + "12210816592786563975173850937247594401582085430897698766795696447223454826466", + "14933901149105284237676334791785996160108290333321693498322435129559137152007", + "3848529433916624869590379003597911090976938589461403388133685310398004369431", + "12778805225074604003024964969486878839359935515509480774809299341511161183802", + "3288267180428684202786697419666969564766921974531343432588030535602163038467", + "1272672432174256751826350693883913844502039730140570583479554071765667798207", + "21130828804874452930669244946376257892693846272313548250936991077452679117587", + "21254559353072473881932828401787134230282801383134765683324465204971002861493", + "4116075860631781527931204624078712926526805345818156200756399332393348685924", + "17435888597009729827411190999389277840088354756277916760187756022854497211746", + "15837398163415665169712832984380121382150588321621493928953938599666110830812", + "17988638446757562417082379159769772097890681265659458369075768452342579854303", + "8144561030363576879343874888624208577604401139613622673042754207987577727758", + "20020299925602421262203305284307419339160247406220693128040712457114283033661", + "2945951415037890626891130390523013930737768652394758977777336357159436605764", + "1505954324723537402640844232704189835623922400329086438898375859826553573763", + "11851584491756305117491374581845512067704002072833714119284164514457248861803", + "14471204965036278214508938537949717553799007630471016532866101610339050785912", + "7163557293233604902868673807221391042191134560333950452577270522828534690707", + "17291625782465108601367695465389799786592304061550212130987221355832952230827", + "10240907112109243116543462081552827576656826251172050843989873656917271396422", + "20702261919346727858635106264046787321170414155594199951578791234276181642650", + "16678253307828004252292273162411388452019952018258857370242272543091326285541", + "19810917631941180098047817620026253706643400683524412974923209268916769874447", + "3357220165225360610202375608872621445880880830154732998557832689480921421791", + "4392285438534542495332422274902727975330102148971785438164412161504066619105", + "14642025133729666610167675086855441462580619607677226879159952689184960379911", + "18142623439987890999821892559271093087005885278955082040377769578204898750505", + "11769399023330099592616157336702104329646487200891911089287290893650532639221", + "7261353756299584174448625214367175510387913706095214313669922259027644778060", + "10406994568199070863112470594593301582798997458844791396920771226539013327304", + "7475277967562870216712397220016587384793504784585573136176313471517144184018", + "9598064630327104406929367986473441777975480987434868213697837347643980267620", + "21137410002545951849752865514437404724653771608225272412595423069852350320648", + "12345612867231779996383303763804719815752861524077922121654106906093103051400", + "16461750199070055335468534730937701659470268635084522644824623393184528879703", + "7829250842543018165409887731515254191943527926556191989558018633300783421935", + "19801151644322693878208767560968285812646931156576102755771403150148125880648", + "808770634664491371274943928223981161442027957963181999892266696287962813461", + "2298122748772261447929855283951027113218922003687701626762072351622993276571", + "17407798064458858450209051887305178872029674498718760624162479511390762310526", + "18585562277464562541666582720366573863334618817908062612923861658144918595030", + "733976598693219656339731904831283238690050114241501938501377743874139460889", + "11316063986696838098122262534148335669847478050407756877728672233736962269417", + "17614529714381496379478130066245111825610297227468263851608027100133421612826", + "12110694197729365219340374599835523099651939156213930558791147158357810646901", + "4337343008663255658976574468931581484970687989356019720784093082313510905405", + "1379188959674402095268172673987199124815512095460112504778179157481327937561", + "3116148242507754420428768481157196067508084836097458698846114802493377512591", + "13306507137873332434793374848948087993544118494881134631519748904811343155566", + "18496878480807017010077624766326681523549495609998881196570603040242554712562", + "3940126764022508707486095199473913866137718790062498893812401335738707507732", + "10030078765792498033316282784150304209584388923549357286679864120250994473810", + "18519871685760382462428068450331593474924737719734568498029727699878543899254", + "12599428893576891013523136950822667754415283296587096197120138265392279834128", + "16038578953099895530943034305356008247313649524436132877362941968861459073483", + "14319233878082524834510736727226054073026413911339853399113450188859080424272", + "13710161613540579690732775978855380876556751245265568031703536595040993113748", + "14958726446649273856607176275240008023824615720456760403465034344703779274727", + "20935428111942360630758629263346308597806819928838924586682307174931367773605", + "5826394436548487315966647466017047216786257295199620110266250301500717796281", + "31401797997389676486806123612280306684597605608110075525648021056710776011", + "10784171495708237485952707518956314344821522727746927291389338644844400581452", + "11604345371765580191117799693565193618158448665352599382713281103552305960442", + "1378145039624937931836538950217364481423707761527018494355648047365613434790", + "10284294167221806561993937798090888689421933711157676807977401896199778472860", + "8233695574758520342808807499924062869636681352769371531557726871630696672029", + "6570581391072134029876349038190171593169496519436674767949949730275868319732", + "4026501263908027819614805027945064360196399012004574117767831931274788631138", + "21091098569404004244061462065218203986433580687172854429523306262593782053656", + "20711772916118045406356429185975897495222240215931761100801599257137350834799", + "3165519312799351250309462589160165591299333587158531489859211268084164422251", + "16470663723473939739601217501478624726068461799539012562455639586886033078064", + "15672299304945968727435591100602007503785845873606917887638890765525875123857", + "21393538327627889838198844493522533627143658125568123117776524944297103649079", + "7688819203734248199049004650451546300187194458173935784579101984183800649342", + "6609663518412297884695057080546416278366560290439222127471462938252865438638", + "3476303650597281786976907813110835564442121684386467570637538230409080744769", + "20633582549754495054832414039299188930065286005370053173386561254823483851717", + "18067076834611402459142612082327591538480657933568191619109271502102126814407", + "157209609820117793892254328219308970217366919934739036156851508233236414461", + "1848396116513925340973398423998379465460554039715233953825786874352442451413", + "188642786730195655565401615804782553245486295156304142809552609651873793325", + "540089254487190924787439362270708251103955915909358626209177199653451469720", + "12796274768956950589847157187031845061404119522843128177103898080653493269942", + "1785666356337148874573621868025910291826158842346617719666738769156993598966", + "20649919247042517528354490854561347316237285929352042389729444382153378749538", + "9568390566108569727471722677925269460696523515877621230569682954652430518787", + "8590683334740232786825518158771304803451657249486419816607179533515442407283", + "9321198393538172042803957409292145345834077448228642847843261373640165958582", + "3651905214805616378360839954289447530035139753215923648216350128870943481828", + "1324345422558073117779462079218851558068746895262914344818945294328678893083", + "6666363895154434021620869731925915051086919707989020578203743660669796175288", + "9850757893972463103359995012900314323213006625927501272997539940766979170137", + "10214293226445704940138790188111862069675188797488928722469679760666574484266", + "16862124085118494177559484642483513597285992646267864845521573612482278871023", + "9172340118369291059693735314505606817316211450324955429310200429408035954801", + "1968992755714619414656181112336357119271845800144345284299978250769356388249", + "17192498940296212027365280042755701662136570107224000496521552617655679821443", + "10063385968535643122430064779260670089120686456635080613693015398478175344193", + "20101961459945738562625328882763768836449780661345042148985756598106706734632", + "12704305975772252539534386080950631076046431529894091327218544197389260775334", + "3008242816727585639441748210631464697850194693570485141354082562181236010097", + "7797705698071555811456747812384107102104184812467361013142453143842134807658", + "19323240331433203844038522035479659453946066968727795017745942269828428751105", + "1698137797127320576751729191866734754105401103859852376273763815257758421427", + "17656850887825900397821271738817912328294075224643535784810269137125067875996", + "20755447986835730799031196367323817361150623932048563112034040627213597261325", + "6221130271964372280138992636208062417325313096379273438539556580491430711297", + "11042709376363248213366896208587241517252100440844476816212498352999929578287", + "987361321094619571176752720390429919723900732295551211263814448408232028205", + "15077982986114392945859048373768437818569856001604485167476360943078774679228", + "6278894644165961404521866714059972066255652200107181684047812674333675794053", + "2649747800006903047073625320829560088088800522557851927539477888486006072675", + "2636278052351769676017824297717609512488651850924228608531372135635042762078", + "816232991472315395984098922575496846552245086608787214581606973359616326446", + "14372687274434205592004117128588852491871014819273428668840779210928924573820", + "7351401720390274950322621121981079413650308506660552567079785209176949174210", + "10275293929161727274572318228903710245677747557851999483919909420098936352013", + "14869686444606195206734119702227763209172799407142930791211203702643805341518", + "937617196362766626935279232045712623531859540210120280128165029613358941709", + "21331527351771920568751070369057714014285398281585036009305608379072813379081", + "4305436470381074948146072259605215282335211631970525440530773004228212378618", + "5894273721571292784412707230481346442881109207745969297947253583203466014760", + "6512250441044591603946512492071171861967500633638753443182294740883123881284", + "20863871952569294813936866452848141274047362082838805921071316386912981651979", + "18788566662709810970880679984141390717017951403407913908833463086244783373013", + "7784927597396249543149135503684024377171301321636804832597181795981969626201", + "13818519831569592521516488188127966399245767953522268350556654747680372036664", + "10515208647860053151690062640705322684876580250632027862984821874343071549235", + "797604926079325807488629085866693514275115789253871397971708541758696512985", + "8741784289526985522570446847275649913333939699807282742190607491216732972386", + "20966712704043418981047968701828936463778140093909973286855779694780086635828", + "11359697297415630167449040380538108774924967116147664240213257348125754475868", + "8070907838094569287067982462230761680706116783989613960066342967469297961118", + "1868550288036217638713133945402464194193242298015503906068429633793800456561", + "198709459347510170000840600179608479136663571567208109852828485236018304733", + "1601154135701845545733926027872374554514541574822026314034696802419388627041", + "4363994778006302991481199477873248350039564117453810275561422974475581105893", + "773054378219982710451611471050404495804413666789496412742983455527754059148", + "5209426340109575519362014651321132459061755868557415513439993327176584352934", + "16124961412020675839394907565568143713078242978522632778625312854364651991011", + "20812496670075231301471694692369245988519082317145989298573032859079075730004", + "3312489967581906638742585802390894285073229440039144559060030129184388053832", + "2967475373447822846542676378804990140732835322255774209561143670843223463335", + "19744585401442299381952694102570931935735276268739851233412754166721728873141", + "20026293345566344685499234599699178313754630774489046573312844763673073616936", + "2611303659034102517884318354550433047021831422518437228002960700934925644951", + "6230291832603218406134986471162106408091661326026848531605999413028246206577", + "9126162046556730019959291776456914453189657463686708035601186672661595109020", + "18827736146609035067773173111376739253733288103277133456626928961785293662143", + "2328703958261360872869074208611873245571971231035163763965210852182760438390", + "13796410059666172174899788866809560044715551934510722965495280798363043241416", + "1593663256684781552813616365605526150610454082601584196604084376715746899324", + "1565874145189898288764434737762721576951043839540107044892767693968417810945", + "8709849304563896945461696717753976956465219721409993781555147204068634555572", + "2994256803561260177499267243802460581941891553208150783951937342406846377191", + "10452746656507347152042187616753027475507881362159944564077673851918869542550", + "20130580998875572619695450234900655050996104101008767761546912649074040426200", + "18926933358104691474037431437316089682088433006245222723356764715400831411716", + "3783551594057498940671877156409957274854990650480535806320220142873170375307", + "7919031943604095374667473717154511882451510130166237539514111182596247372692", + "14518552587329209714850286012780632801030157943402419401997576700600952906519", + "4770764028263701271241862755569969531641408032906982530346384375773459918490", + "10866502826034731763529371496585294375373238783964914673031891984092997621879", + "4234148117462322266937279401468367908013627589417699250592523530383852950379", + "10747942066055887965185603234524367638106812660210378090215017248140719240336", + "2587411532912868255102795810490361867789634574022411742057853375399270197531", + "17350061113113681344498080520518808976916692173267298878258722510332360424059", + "16490282364669098969805528215926442920328903121380947471680517193373377657129", + "9274691782659584680377375192682066090127280485689527337429804211265749864190", + "7630965482352419767782717986075793694403609453648729580916814032587325374653", + "9483872310024003776681196467845329825094379763716541754956796450187787638623", + "12182966986735661215639970080491757244218854808156498220088212871061979325833", + "1853790963611367149183440339188924598268644281518961106776656221408171642714", + "17425077915972423995335545370701802959607559878032910147159424242864219303096", + "14571075346526399549826264845894977639678567831720652860528738036970272895919", + "5627701855249158721927849603102149698163511782011562166637339712383551336091", + "3620805686755372260289125555061886982808014642356719556961142525373021656729", + "11556995641752009899073583627136467840237831247117281278719511600076965602980", + "18960242154096055221658318882298412299294886669455506299567210308762501113202", + ], + vec![ + "9174141306060971809979631725764298697615039980311809306145004207410652431953", + "4847693924685156250211477469465516228032151306221739650606132660616428517315", + "19669833054057639609249840291533340493211768292967819468538893000195036768991", + "19800508893433268850924828171290876015556093796000695603651522426066333836892", + "8244699449852279148780456022144420353408196866113049322676048275081354214716", + "1563672068712965454176533719400672258364596155638916268717470967009721945171", + "12723223712027468580318230235559705540011996847167975439677647504573149248849", + "19944398841194165937952509356635863229327574447452745793253427406349161295763", + "21218058308392585368594275702746106483411305671883946244077923955757637296177", + "18442884961885927579732373746933397748806426938144021013884176466434407012116", + "11138408360119814115926439449668526422561003790198269766757675305576549475808", + "12724564576884231109847024566806896391934587839830522481308995309797961575379", + "4897733190252075532660075013731462724561461746919488679609618967302541674417", + "4797748331306263412471031924618974997396620231469532262170060449304337691527", + "8626839560132907403537141283531395025838110825355541158539075100658769738351", + "6096293906324574249636975851522292408228519044739444932687579741964974917617", + "2351617695830568421216396081605990689071283678701192113347036659596049514149", + "3045682390398203085155257535118136303069379656645406266260961816947178911890", + "6935829264874515341379952008241845470659188886156484974987865751370715745075", + "19847439266968955911971997829840067368072860877451092633069920565944933744280", + "12795097343831149148337906863235678514689648096503928066579129201713661539889", + "10424580232112390318877053133877999442988769389050776486274146627765228950235", + "11651452649618223740363812212607761589812354035139843126315028745587570714609", + "21307929358023177131550002602820591970791247513576735567457471459920519084552", + "2579908580162153663820021562014873149811195641589016321720930006635393981680", + "8198198178555784054784079137247244121807775986273563786249987394640289859893", + "17176088986876377315956611075288620878117708836881362200541916957398026761276", + "671389874397910339333118510595007038137908096657753354622355890021074216004", + "19161949137729278558310070194809106779119877882343914445178348849980058405327", + "10827554013954037091657804154642286174226562252063767377995268439458401752538", + "11693672899474469123468133710607776304784343543318650064064636202512816205843", + "7026547767612627656560992117440221331093280829523426249915938274837157551621", + "14422968137896343032446633683271253661000603582016449215470992885331170459671", + "7685352543184863430081115767111935982586458632527708735083385591291346555502", + "14089009391529192464370954954330128327830078875414722902347666490457756695535", + "8424161061743752192085022963953944100289245618074575727145394775891645849043", + "9809236779073852557054640507912802523501426410996355424610807253990040160483", + "14100245203768962710288059230665566265892855964739454261791429988929622355986", + "7775683622333704945225255741567928967674629526812606133980425422182282014012", + "8739247215686497264451630351996892836638898510934389758205488381695687859658", + "9431876969679115468275053745264413939426444105271849398322497961102606290132", + "257914055321743732506701382989022126153391940932933566664491918941925247878", + "21801414068435960590201256257290267142214176965736081788536576642934903066059", + "9465495933537134443327560834432669768951376466867005153580146079082722525723", + "7862366214258716333873810314803222267215825847232397599183717032713290878315", + "10701164906390193792620967030790214270231326273599373762943959252633779929633", + "11951628827727068395937910010248864431667047516686609553745879936868276916066", + "14268744039571470490378560085356767818183790841094115879980723591887874138419", + "14468215915818797151199796266933432577607248341385185700017147731054148927023", + "1523824033338639123415809477892820349580561577160869448927791050266158538520", + "13559991428776910947424645696251487328999214391124402586267086012691140984198", + "18151203063828433535061866995346135260543721730169485344610433976436663085882", + "13436242600153492361692256644258899977135098134175123174795293078081801647137", + "9384556671429507406657070680351030238568956203341356106463890924933167416522", + "20321079285577981781556986944841048777999006905303986053275199507771332527205", + "13510502130738135726695195328780836716597947131948116750163533622597187969844", + "20903049289119144354363108865308751668897757360882852151457514926552553533040", + "5611953645512225417723205546533389174830971368309601830751921473015551069534", + "8816886019615642422040038431962872654062471314244185285424018745071289038220", + "16751828354835345790163611999302863949792305206769993810746019449909446216365", + "10421654749141018171116296259626916395875529220250947127973888230084671091757", + "6065225315766552671037285757918350882361743810888619479819895087632281975681", + "5737755346739850738724717271213687543479332312420206954339242459110768587128", + "14770522272891919220644639305274656491731294860310497013287297810648680944682", + "2777394791070450473479179489594969793054480209411136328689318984981401732197", + "10039559932930709555975364107098145624058027439566384376771787183526929807647", + "20757756003754261934858081777796652436155530474748550156383127600004580439167", + "13253166894715452480712170898662712132411702335275401581167208877688374856806", + "2037004052447343668129085129987646907388123739343356363273464870501805506884", + "21829471491172175426560705585746893969222010633542962882847909490991398830669", + "5130395545419191392223692116621486075405299333195732914002649716762739787586", + "20333821730990393095934147177227294218344864602777744425090741435432040213391", + "13629653802252084129446975515814037702423511189484562534040643669977716900228", + "18489091892360842692678715136565494502607711254719045543684163289077857041829", + "21380328601365035012832876315565064374684993115210423862017233170195286906080", + "2280052193465635727584791148501382679094142036232980037838088033232747821762", + "21415541711468815972744677841317235994302058341802530962394281077076174148777", + "17146992672828650459975820445250769505470616910596779130798889014378635881076", + "21676475584514120109058208398560066698690773910598518925936412952356431597439", + "18337052978997482578725645166749278142628133291693686105612531426715865276143", + "14864089429815580405957698645045711801464462794754089671996837547347950054532", + "10834607317840698149140890207826430113987295440254355899459691878793978994131", + "1157143498448645320415276909137008396665083714591338741616893578930275511205", + "5027542104048754930085470328670427788489455916338375169351586496298129661248", + "1922685817237874482932428650501872692326329693528175054457715565489676406535", + "3071473720617798005831658342971536643616129392641449174655528578463370685788", + "21091078808046042460442535848913779439792606439995062001271357804782672390627", + "19773167374024045118471391738750949555178717045037157435777574972149053404157", + "6418695831178793575992210834992785624340084513619644969535805236049937971859", + "6317875495482489567338519005308431806047606843913867465201005132273298011425", + "18001249545956637376455848019549801116909661454019565655561439372098476761813", + "15530167556609139699164228289904946047951254183080358784988008899829027775935", + "8702757129830652230304011519426558036441096750485189115358314568895250616455", + "6369986882953061252605652398893489899416599935424066958291402945530517772170", + "6842894437627604179732847187262933342846269043996061072487488027804029200046", + "20951621154051947571647917571547811655800779287153833018533872651413529893817", + "1219277535080749134805291725937516331501172121638812333911793209536894469364", + "11704605822590166851511022757496386950530399074796545751042566537118336773236", + "5983427701962592508775640503988144495847156070437130549832329402380170245893", + "20169091361583397776908351163571343158517532527313940288212943504015977979442", + "3347733015762117176159731683196584632702931062411889821726902331981723958255", + "16217509027282489850987935065936382820558307489954122630844029918951230268972", + "10781269196927764524006466217779648732772805761839205677745819812868343369087", + "10568911823766972365218731330080733630028238366288098114239172953421915095075", + "5568774544682750792074131352530555554984876659733959079036284517928264996437", + "17854353469028651373397049175548228061144941710027186166132671198740388767529", + "6573034112757039329551886086829829282007989555105157401271097204633906940776", + "14069627287078359391137554212536883450595451640858724555679971658981340584258", + "21119713641590541511025673864154852875977162278614553796484277752677323191505", + "12802116677235410441672624559825044917295689876859311183079161588690810005363", + "16037054471696658545113065872215787085337497333273419984439267709950724531124", + "11698654309680908244303850432833183602706804558317993513795996394673734185716", + "15147889780127043019188099948246961619198549928908180192590946633702778981583", + "3657342516407201801006680507925024451922115018712017224805778401726428603983", + "19776786467141868744713630352693556348834540992018636838044610844396164981103", + "7980994848490005281733955776875257044050741738176865989521982608944874160873", + "12415191330803073018395217955802011585094769098717180100014182475381600382452", + "9300986814650530426668152137665814177758578011365736727321578452726378799933", + "4412208980274764197258090802604347599791567698589180187154608728755887977460", + "2582317668924231956058541757507620542434237159213236485179804217989764223164", + "19860814395849792324574773787600734118308975251437485131415273418632757301303", + "2765909129639570206766170018363951893338720647679193401532780051354569922989", + "5402210382809272147099442645489124829067576777592680891367494969197685281513", + "21011104174655621871977821285307554463403659856745964274018020456838460357574", + "7018364707286303918877589672878574811337524823085078243421192184715151775983", + "136380103284908296988715215087018020601815024625535396780012012453684253071", + "15953315437474610448052466140270091879233956524793052736202793153707558909889", + "5912305909658884889781037379491781973092020933879206417274479331390062715252", + "21575635295587180789566592951559325743281772394055590203112195979769645712827", + "1541325805478255472079288730846072146731241030100908414806224735345400173350", + "17207219201921814683730773200330679841907450967511507012179337438654141678023", + "18266907794578843029196926509122804272900478710738403531664855427655744759655", + "1204224895193276222782842236712348692319665277014183965830735736728887994581", + "4023246588034712778784328407820569751989619386134504404739514704773521558127", + "9064437981037864995763386367268294611921404895425171966596873454090899491243", + "18733802217274421976148972926716884457128521840010001893311936746027998476583", + "684088380644531080099595788833220377905013807951051638705160997709156627273", + "11994830816367980341637110785269531718699655485484715851375754143223090344544", + "1831724566362300629700078416489434571462666430381219293205871349415506993475", + "476710745682537342427691635955087951551678644045621275039835625280220347951", + "3586272766499559446129476613035465343616602918105042144185864609818186807939", + "21220348736799044560439132291243370111879983677197111626309132298278891334631", + "13683795063599185801186093771702503913590598475095473714851383723199050309401", + "16118007386401646906425171859166434660243697555307927508268622819509657450614", + "20930641024767526790605168032291665313905337763598128831404465184891980632233", + "8098646212401100552303711812039666794078834386731698810205195111722330322418", + "11585783577173465460243373201831086724911159484415020913089605532852648999143", + "6939053275662244505087635417541857793206828446247848992283188764105131966721", + "12798043540382494855660472922674138947867597503468216532170157050160462426199", + "20713389801600667412553956346192236970217099413304167366340548074880917096741", + "8708207547232102069057776099666995672015399188924281674772351753887161579745", + "16016293152251662056020528248861487281148011452459422778601663166015837379163", + "14324897997637439510797191208789711173129460994362368408063402682894248793270", + "5652996184880208428967511742390474289004021508049280419259474250332590598159", + "9877106633097964013050071703002221796318046172981334418310092241450453368579", + "5385816971548914185604875069230499528103133871233951354186676373318036241822", + "8683091293306949708478955451280670950858818602696102489349595054818146782362", + "16854975838650963077652189417311897888852709425835763860743171659164792100482", + "2485160816649177905834265823672532710299580013309324666453183278408904845122", + "13571692148185502188613896013359942531817915076247598483272449919094247957149", + "11899399615412173136098732970606292047945698835588882297719609812145308198009", + "16827672312681684936590464376780346837611857292837989006980972390576065571472", + "15588237822592586948064701827497915157359094833395277985658706133691498343174", + "18356642512438827417103800170157877145465512961188328254773957819312191285168", + "21642368145757804795143182901389223409544979732781450480847315495418822041608", + "13104082060493963869934085622104709047787444250961437496674916673804812287386", + "1561532086277971111804773016487251313460788916643968126116038406859074212104", + "2718320602791009266532615731130512762296058687816604986701989820504700684864", + "6182683520717583142027400659687593712743548729948584058329789905227082638908", + "5757242145794370726637363237313640925174531077560764545993554185332488520899", + "13688467192244237790806289073845563960119021610896694359815485764764608925981", + "12528461541936459922472167643986446262977222390263675720335825628163511159437", + "4897268894447399415795897967133432014527122426051771866816059363418177665482", + "764332419588242767884018802335623760055144509861323437945071732931233600264", + "11755468878196093893190753985692714003062307843033761257593209352165323938879", + "6006022813561851182403581780143813226749481175437001910923100661321563995672", + "13901542382190510449243772206670622017835690746895066410475076631498053123535", + "17648853891656481911225897080296737974064729032668806126284849597245044343224", + "15106333841965710929952896897521673254279668876709612770907537801609875568099", + "20899315415025260484895459315726322363345188136910564549344894025053466430346", + "1409310408943258102775009950750654615881913956151269414096059752250092035807", + "3899088673345731523976816322438172722785832982334214339521575164464706226294", + "21406686765584824639201351330529610299177537976609066339927938099572420696135", + "9121591670793901722224770893633585291275002987585289305307167711146944200595", + "10711764678410479049841945177317023555168593838022414378232020467195337241279", + "6599257303974597452501135281719536074294806740553273627128065549267140155175", + "2142616913275380526921597026822750992917222975992774063376747381991404337593", + "16361086527663411948363284957489078505159658832010445114438602510508720771278", + "17122647864721668762640781848678028227021534122268561738445496382823789619088", + "21708018685042482318786273055293241752114005312590172460099480713746031274624", + "8303630654111760473056607545365338851734309857718959193970615705292826806179", + "3658686547507488906491014260011151850549759409901579684176172268581462329020", + "7720024124908065424512743488999250878143598904717873371853608249805302871508", + "8805244918657836956533473437651380347005779399042661429698187314657501156241", + "6303681354794120075893215838935586592706844702088252970663343726024171795351", + "21512507181643408509426104627003618425209526633080701556628608990726677651135", + "11835373417333287523801757951049679177935522717858158305516568595764125190183", + "13059698839045014411602727811400239840163533672024084777768305507840091151855", + "17635240655824524168378284083397931667938326555447077097306236826752492079430", + "3374412791113107178205006579112630099131939030015047870738873452427211677886", + "649711083340882271985565833699379436167716866997851102439037906608755280128", + "20002805138014565226408902156524463368767807620908543995020210484077706418135", + "11071355197960433041624284534649121637702414580710232237233568479006159191217", + "1105441595020980635809093220782460032826849883993030969714432603468135735502", + "9652765957610682812348919340146799318537766051849796416434577860126024594091", + "19248299650856496267902926731608572596705132576830681367365128976226233392929", + "15285802367070100569572399512275861017714681455564415244982064571963339715277", + "19970416835730683993734843405673457882587154729456022607061085470691843864556", + "1017865638757684714433500504002748241987153668285974836527484933462490771227", + "17284848056169793253916338792235498052654877955690514601079806604278964099314", + "11718277105372928962350331838305733149270432706448484259807630484543527733952", + "6670793378364949883511003949124179112275066568088468958915163969545409700112", + "17088789393958965094855662340742013087397643056458490270185660553870734946796", + "1930788514812600942005320214284180860980345276633471423966020111188605196111", + "8844343159753729614645407314580317697758296041737296276765583948670245312842", + "16657939543606018325703787748629433167511611178952563626096990460124133990109", + "15333343644239485619497914931918504163396626751908652058758135581206765801100", + "16533875915742793452819179569144271760125646811168930162441077117553849625884", + "19679534317472082858641184998487299940737032844519038845860980362664393659234", + "16385719932525604857740698205965045007053424961009717093945644387917936681719", + "14490521084213123170781774542655088188106794646066074998587858678154251198444", + "6386781978322405984893078797365492485297499058328348606653460996474947075858", + "17508047533433736707046937662428611868296556965172642086594091783148965906980", + "14904597000414815084666285064575232635645852687797347860862157463159487771060", + "14979972442969995336727018758631782107138089738395941038626891064816880204567", + "5299243186271864957800928637599294208954109271450189950375274196644046222516", + "16189884555052883188473617525411302750109401983487269295700675997730645714379", + "1645560170870292006287241616671417605853047420339675073261660626733726665673", + "17866745974872498136933906591373095763114066893081150553715211393380040095383", + "5744849574386643500716045532645657520001448510343827372577217716983339773799", + "14021966200238971589811034967347517039341058556783068950884921208853167419283", + "1201178089866013320759085637098781870734315826415474628546655403142858044361", + "5875644793836087035760988842421852197052681650818034527831700615895391179258", + "10875065950479466897559006840696567433921014267247530366235539292597441428702", + "2221662399199449388725697795500999209427453463134383582414172135385907744785", + "9758513532658579204941116584445291102215928928145103503086996542188799521709", + "20879593323317766577775570558015407573466986714590017262168011643343469361329", + "17225846522404915080676699509636264825833159640824918876741681229188434930856", + "15189442986691997434021855855358620506645387296294217783597931695143376252483", + "15973617135551858849206811241799666696907820418171736027820254766840973764431", + "11888113439449420418408437784450952639345990804839507528208325036625374967083", + "12365920814385241227394825974928370916184942218042429533600397623369545597697", + "11966175169612449906889690852332416255478894176917636726028104087408060623141", + "11163554022908212145274813635928762748847331295589087669583554722521180712379", + "15273476004030808005186443499782264987539818978741159793745891769358221570633", + "2013969196885866182480519514425192091338553670034650196068995589691938248955", + "5008975446746271526106846692137145404766553748264648461545948417006052208130", + "3926749194225734582453671614337621250954608160208554883789519551411469033731", + "1635544156808471185144068767649088695307748439189898784051754434524720057896", + "17144944482517962143604430553750908864860079758005337246916094084534304051981", + "13823503533305241872793740090687668844401004819859520464168798913603662683770", + "16335911272023134851779534303717879370955813837529588982953758998930285394340", + "14467284210444150699969889681308566002886261365990840091849371665183151060295", + "10578205764525658336257882813734672799527733392763965031628376897794294290414", + "18771425328697137255453620743509164311086906349726510394566012237817674245865", + "21804626093983212038528370352039806004465345685985435415809095637323683466452", + "12056805308954301132385034564357716323176447186932453788072119595595483786736", + "14307195735327805282612857510308008767450554777122724855715789120735513378827", + "6848201070063637295416045855906784325422580350462489495889308309540335269587", + "631364713487758647973016689203003205602593076699875191323345338325349259049", + "16214655556434201961140525501007839859074077768660052713461045928979956365067", + "20940788212183642266181811368870506130164462254923655617893660245551698033523", + "8257440848494309435270838240795567828478627302119374684511017376568090372435", + "13701089242130867705897643891164147923878521147124165292045879194108024940909", + "6895272953337895406509859406973110417619874994579965619097329249292199573333", + "530437169778092455975584310016745919549274205817234464915791595041990209639", + "9008612822403008353420189298381046023002474279157557733428254452507266389025", + "14863423501786052071018008300345884780479084379412157784789951872243409629758", + "20091026239041315645045502002997446404106877721183777765607724358538559881231", + "11103877261161399045807234470901399725912406134008627937945079980590775715243", + "21529163495181909351665093277427712610965764606448489357319207727176092439794", + "19540446772694448035410067193880900774391072899517686330271100773183944540294", + "17549510450820803306426739851959754252204444648959723652883552677325100583689", + "12252518814610348662318155253547558779974557529822012236107550517806390105567", + "8058115132085119666951861652409945532276905989404523986413207631657437321956", + "15916100116790431839835734530362130437167135501074855072245598938219364570910", + "14256533476494466694764843270015662315303617568641801280831873052211753536970", + "17865471381417606502707639037418669122823481329049436020149405646709537112534", + "14015711483636570179335132940981982618090553643653746531174110949872682031017", + "6075776171664976866533080327142904134938121198707020111533599997509054627652", + "6357981809351565370498807027309828058036389418343890944791766504532174516243", + "15145296985037303761634018005118672316118004891352906450983918852209191841446", + "2473672396516437070485250176897956191104549656554290725379242542480862701754", + "11059085933391482002269653121188853142706883316754376424538662772943167665341", + "14804069155713123448375113552227724310276294677318593116834685772120057819258", + "10146378656966122923223443263705119557842694560695035707977826044606938090895", + "21828309590915152213768434346306434851424116996828875020020066586363340244814", + "15568879616082229996551157805731419126872501425454775741945679993142071548779", + "17504079509060638501918729619244098692140123800571022969294759717277257664716", + "2998311560047298465700351970612785742605093777116697796464434026101441410385", + "20229972737818088327107446854254558628041027965197447598027135778783710740259", + "14884874200763033520375899992902136897590350894844904733314191389520252900641", + "9619409751736964504139815024141276029474791187139050183491749032619248817404", + "11534029087676783672833531415041588991838838078174102967049055562568798961925", + "17106297093375816944137015955705541133308466659538554159312635106186252148471", + "21676736161168806529097919794022110433487869702564846859065695507460463414524", + "12596447704589377083704857810305080195761099125652005594925931498073219198049", + "310943124066162607352831846280730445558498286205117614171844835745706684432", + "16013029710570597613246104892930389004941711962070683476555063566372534206859", + "14282564976066063966062366540992448474634085812789771416509095817495183298269", + "20757241092771652500911491636894210910134068426068355089789205706892703219255", + "17084251309147907751212619949757520468224028014308500329099194408342072624132", + "14680350698112448759886861002622963534698534998651150537754386791270019720748", + "17739512731440543100681958009173086667000199263945053345384367808940651002571", + "8967486063900234709994801661246451094429250620940593387993430620369318619734", + "3906067814916986286272005884942051451306945488494283077675304366798199289520", + "2517004675157816404807349457307096161030587393097616279110332574293494030636", + "9995302877359286298434340810356550712107485295049220989690824504445305103587", + "12849909876017357260683411536833847986127911582040960825577300322066595609115", + "18074515800779889507358182860997188274134395074469953155084226981497567860114", + "6692811728183968363967959295970424292426462800383828091752006855360167264617", + "17859827663908740084792157440799065184931609649811664442236242315795442091367", + "12243409340804252499520308602187370739653046835019551522661290645230850934962", + "3009118420068966587115224335717185828292538080040896739662684632413054772046", + "15856202298588272962175258696610233941787471472716811521132004805327415486141", + "7549804594729480554341356998842376772514802673462970334329441043324983960866", + "6390806437030742378988258255983502109201709511321162596105974797942236431761", + "17370236522182003753669946647208335160124999930136364231371998757664000198520", + "2261672244214630177095236704932243497157963117166120717011661647779055001646", + "17325026196605130064689259977831126468940872193987407658419640959345091161632", + "3631641025220845885502691330008982895233731506600778684638817282531001457735", + "8656561399441987116927438675277763317789561532507396244334062468892541066084", + "4069166732330197412844703565599514109399373916243310212229125901351402003915", + "19808198732373520522982274785888742523226720967259539531129335924093928174880", + "8555796834031869022510134190573521699378201702450788201649007358450530423866", + "17759660636058865290579521740750449606781204755231964378855563896473545202303", + "1335826395218609619260020055566056869243760115287254209950063597653055872566", + "21596200365241795669701682696176077888309278223833581800772036945674858315765", + "12619752319673193899296833725747186284394167228468888029626464753793997178599", + "17420588547980145067421969830249755561311178399975476925894947008643385243007", + "10337481272389772505654575850886249605422739785111225132545740838911222864209", + "17928431631046752749930349099366498612885288622404560316665023363985966878427", + "3075798659324203306711977985120251896073145961913793478792728028765206521425", + "4639500613932181914847461422373341918892878975546430906324216810326467690534", + "15396322795715441250300995201889120935591602515487993982711884319616897970533", + "6391276937505284102735701938724106665734769352007891548547667448647832351929", + "6811373320779057384916660178551330838095673247430496448933336925226142036083", + "6590973140323934807800215988687710942074412987201753370126190631819398102173", + "19364648614154949386936259588484266535262135334799266379433252509193375956715", + "4702754284612371917466042550086249683933140314858807272591351280832918881874", + "1081036249074169248236179367049085684430282426446509768147097371368406374049", + "18548093223441988703029589168425055383154624592689171393242936199350770119589", + "11098999608073377668352846814752381891400020647878345005629685447730764310163", + "16001262992680194260590639872321865154716987495605624862471107193457192704714", + "21696229443869118415905915570780926763029898831113534481730746953640692230062", + "11716215712634983607563947056324900205144202447594949676250978337464771243867", + "1778908113733035314726603632369389424542091991692308812147944884836647395775", + "4019081204388123040098634987844274011285321286777408246805308194144238418480", + "3473266952388383063447927231564219811787341139731701190625605897592140631276", + "10457881304788072618845101933412333126160339089704353596608910674508961127232", + "14926101732700077295531234099443522459232814784151318061435025890154852791802", + "4036967072197259618286839959572768559469665646019907384624959071646231971399", + "12776716624632228928613396031717959431597335742467953143594165782617234803915", + "18894783424164609284436913400522166453255844750192864579927645453695213022195", + "6303809107919167113924303987533838414137996606980561570652539716097058487126", + "4729698693443803882717817492985796053343431875965792864932005291979914613160", + "1645790034267553926884568714540144778649055395816210525904813567839945991808", + "8138260225269705405100573121045873922755899939885385491610389913906979427176", + "680936760009829486282006800072001712155424246576949107399338687767760991887", + "17240357869291182045663678468827695873425113788704614245279840174870850373113", + "19100963939745621863641468371111320143895293700517367016077996431570157414340", + "16188989656090417148189510820963186890780289777598053654241741803194118100843", + "18027402882394597868782011288920739982398714370069420860949975937357531046151", + "17780529984916796963712255733293310230026423072958099290880849386941451922559", + "20004531511171838591303710792081846238092292916166965045929062171308088520097", + "13855731634251510230399834192704620793850325654395687428672253016405315169901", + "16872938837392115669581040432902657478544143723662502779821325505282093696739", + "2541555081244462826761076743762714962901590548271316707071685417008817634653", + "5136424039269088350807839181761422963254683236279333039713142751702136147963", + "19216238128964101420135465007632926445321991494181045543846024053552797518994", + "18868537488540023742258053821537824724371813776839672880900985865823137839953", + "18246710415801024039719497716350501105591286880983169809863166130543617917249", + "20608694004331631709610739723463009412162748201282986294016482926528443868949", + "11318113915971658853560322943565673154831611543653209084299774855226816037778", + "16240989418312335385576389959938922684406585560688799437547298624184839261343", + "16171299673760267132909753100946681733778389681324959987573199154235691694977", + "8036823955656422391918380552495301547890420665617977624790236120392727764522", + "20269862530534739231936251654244170650781428788816658397167110617927916774329", + "2368678892744667199202318323282128737449992006513656480477288092472671147090", + "4618078962163037429845764284139891171861860687111566735174912070413086829215", + "12695350627501306162901105159009497730633599768443844225981772758225613194238", + "16356283146491744069785034066388746989409816380917535719898337817088223419024", + "6407893217596287850421377738867081146106659458551198123106454022096864887316", + "18168868018352364136212098098453930600797374324006271488950341490483455519349", + "18352629174410142476418438008157117497168118524562206830585500251463010761689", + "4344169393287991961961456515301754172943022039566219343212376057129143739343", + "19424839806870716108478074501405697296961947409763509419111261767390677718987", + "5796037897847804302272999466834285170265203646465480652521088328457333766863", + "17402105801450379889120987010453669096275392789725153915905747267778100864362", + "15540989618743824352651126288511222263828123668208146479603617243655978402205", + "945810410725426921570254447269595873973858272778720657523509910503434094174", + "6962323734045776666289031609372270190654631739266635759799844631053633876675", + "11382945272742312954364642163371436855283161775445664525053938433459897196647", + "18940251871958826726849623572811640436342841713786099464305053400421580490631", + "13969540696178305383564753026163726563325318478290740131984853424331762285147", + "4841983966001277917879506889862519614692143906356361564304719688757862622407", + "8939049562492171082419559182596894186639203815268680721033389307282239000385", + "19265363396776097866041313346787101192508520582744521467413665478819721956884", + "337106861429123598189388456471513480497137213511877011021531147545809512194", + "251367482782327915297484770356856386307188967585026711663629212746150191478", + "19506616511267234489421548744907283107923549136620297132842391511025844759064", + "20633589633280372440758096707466273580151526293980868749421563697429194761212", + "18833062060138888612708634036427140134887774731041742144004707524569102994071", + "2927291160590267909596732410727396533948837350308818016906834558527125752899", + "7095572562193114209617459307511041110255341231707924363346373597653253806883", + "14274988113217913224290208839851596837329960221329537670822013510325939323091", + "9965830780560026128320556230399915681196410289456547935188741323403719404039", + "10333365845496980935202034863900757172839454015352626511769637076650624839070", + ], + vec![ + "15193892625865514930501893609026366493846449603945567488151250645948827690215", + "8655680243784803430516500496316192098841666200175185895457692057709359214457", + "11710807066713707084726423334946631888369490193496350458331067367713412617049", + "15442364818086019103203999366702499670382575019009657513015496640703659810202", + "1358747428976145481402682338881091555771254635226375581638965497131373838774", + "15658002471767984962034589730824699545808755102240624650914676102923421241582", + "6420480504329990097173256112095253518339231893829818344055438052479612135029", + "15457172495394305353698644252424643614748461590123908880271021612601244389162", + "5745943350537490600340174787616110056830333091917248931684290284533019091654", + "3877253492903478989342845512796806320713689655633086736499730391667425329322", + "11257677301507982757739320943403112189613848490812422490591766717141506751601", + "16906586852467953445509312290627525856126394969718997799028223470195783329296", + "15263589725854108297280528692120758129000336125328939290924952731952242586386", + "21735940039489460025710098364749096267519151075908323637361429746399161905338", + "20023056608360522105358681147781839024069418874082333862551226466128829664291", + "5677500725280079960679484373333947430817198394184436922575072427342643665917", + "3080516739494460477657748111767941482024045797587058388950619118994388252853", + "21486496065617100719537932626843898998311175055335457507845650282870586541596", + "5371049178920102602305531530023787518286335086323221270202212974241707302466", + "3074817222296007572297581554183445947239252698770067839721345984255386069425", + "19180807038569629573914331337874446591506172622522351734982093457681161813141", + "16937785199372956273358037645552299688842385008757508130180245705952406225194", + "1688218397616770248184651775433764527272029131542529408516364801909017591719", + "16315958669815317541884966612581197291281164499674338063931623110684590850347", + "6218230753007070123505625054833158632732536069700963073464625252554943737669", + "17774528060285257656595928889288330429565059134928074258373583886985960212139", + "16197131592052727313460949906369199026477758140133103701908949020106767192893", + "13418604038232148873269488320329340508522225417123160144993642839875173062296", + "7265658443160253752317166706266927598319661172006072732797351716897681315157", + "17200150079219747370109251547638276280610591698078334228421747259741754887", + "8627121890622175767416692555014275717515106888840919734160364408960047296494", + "14546964505431549758350267964924534495477687922558528647552728692912697049247", + "17132720822762740343718421124251772119916072270451579802112353604446214831761", + "234333065870376500756753915306346778417056884715946003873280290982247600083", + "18375643491701271245209094287106352436174133929245169725584150600992143374298", + "5158448692161567615645197008737390561357077078129599243188536485308363800282", + "614161645152783610732075198073600394068518413590650990586931263981193439341", + "12661793104597977909223565537293318966803153852970198322604479648383643541371", + "13041905650419760925682179803296711066088286278603171065755078690359168540579", + "15006023590144168506070897325649191051975999212058008674224953860265667513015", + "4983349941266961584317889823965291023669365981564144622292227613558024302012", + "482274340065333833495445682213681402212945945150526736364263233985449810602", + "3966893131006556898236790392613869798057510088913626163333804949895810673044", + "20923301526284527685000591080290190641416245135554916208054502046381491809443", + "20838692384005825835959734210506718428443540957544929066941550833051093000166", + "8282357714606447781782716442854085217089572080066047419459610560432999443766", + "5410651444876169088887579490283094453001167796545260026969919887357676973543", + "15276966646285075387317940436655285872037988805762800567413073418506412856419", + "15066911464727337689573664613158712498015597773345106524271610486257089622849", + "14583790985054968382519116885383608902981814292128186470697458065499359610203", + "12059090796146479535492139954279038037217093044815277624197659219529427760034", + "7273811886044732271171500579064359282424476926867187108258957006777685922641", + "1463086899665237074608503061872751147444637332808872866814340325832200880984", + "4403177494620214359779479537027014449448686844655371530169401219256448130398", + "10860968418848589590932601250051274256181778387706764281989724391784015147562", + "5268786978207139542368199165627108325282167169564314266747401266496556301775", + "10683355823176907476704511935094343405052640940909677712096702771871787224727", + "12998090263935761477316698114799901126086030852595294916463464609721875730852", + "21401280461419124637791689956622923839426783908187419462727763377498739154778", + "9827224472048063173905906705579289843819400982583185823840008976971109664519", + "6215804144039763858354471461864183189301201862376216122255322421321775987311", + "15461308489200344015891625455653488930440613755785081602434124530381300882814", + "19336334695450889400681207491394600659946256404722006637851709906131899294790", + "1712331165786355540802697725399423752392267480553199895882357858951999960061", + "18153038525983970702748717571053178456148003321236490384959117581005013333018", + "1080183517033034908031748897211289245459330899463186432840251241943892326023", + "8948022108193679628295152361559653763100984324221629445749311939820327674857", + "9553342289560502306921915013446606435600388298465288181461633559299564421155", + "12714965617376828547637017050548818007690047452402682720666099310241001848988", + "10945704657865102635748104464461970844653553427083981539165832149959193156197", + "17511714411688352203059545713591160825310809755917403629838415797949261359373", + "9253691969419856285051096287845246422848295397226841130282244592511676512433", + "12218945350859454581754463621617733341764245716874083264842931063272433793037", + "15268139709971695434346690496076067658968455677120655340969837725391575270485", + "7948825129295102283421620705853168119104356217418364837218892682579042520651", + "6887299291348589691868712194070626390224806410428583073294593431810559288717", + "3610235157455454109573625364057240708256027358184031380521552355839155549623", + "16532488069063334064099666525339953823111673083177894678898823509406678724969", + "19317517725107761280217103201908049748015068578935276576200982249386084367574", + "14980901224290526859762385599553818204548992110637275324411078408232697158492", + "7741797285700915051013289492475875831764653137095445146268474269974647962596", + "11964233864746181868467810392101989052496076326472717372132104394243614334823", + "12746657111181947224582102380049766839578185276220682311596480990298620200286", + "6408726946032901840418309506578019708113712492100046332894630652186614300568", + "20959261828945984489015610988397031913577918654575078054490013338416801523934", + "3173674599420546165852740604987014294355430358334465189504551707066179193914", + "16110281513253204315524614633789708146700074483476149119440509845258215816735", + "17135377580103690088853370572199271964414896742342749305424508776150797285064", + "1405769920008485935711505753346340073052795087429311991287498566024570212365", + "19088073362945853867763169651582894739272002359692597239222895238839593467749", + "19897231284455588615416169252449008151349728648961637517447194842672488184146", + "20476415629812014715153863754869742189693986277342067785614833846523246536739", + "11074321446706734150375041020583051611133090415774365192315805856051215270782", + "15231367549323128694183572409135806408519505225209496441892541205465727777072", + "10515952069292929457050921929301902464262874744159361114100398880194109971971", + "3216370118771824418364829250073852356774095079734089790620447714552849459645", + "1940445924652458480775282556203659335417827058983719042726494187979000691704", + "7899310668555694144370607061960060230071621529123669746309839400642332452086", + "3125410912833939638823760577011271607678545358020637189655641109813198731542", + "2980079409624774815878860133121670095839651294537928173829312563570356348730", + "3766498515736372882285796238406751547889526137955288498682767455795237989580", + "21751217522789414135074956130080241003845828660310903627224390345319859795839", + "4947229586642010378772262640583556676497656670779800090478805824039760706318", + "2168676839236948809859825591626629233985269801981092020040909992251312517552", + "21172906642114648036685108008020762271569381607092920279879047961076646303327", + "882675742500939602754673078407141697482716600335919344527751158504426951699", + "20942968937722199705624825492102184647835614761458159157410261242387423597787", + "21880640497503102067412608072166388563991106464538369680846671301780353850077", + "17593472026567804917122179982860735087124786197105685847979050530954084564297", + "4492875530722152383516030266828166766820778742874238188105265500984280376666", + "6799763500412433367637987497601148507907071065930142757525839585946238894092", + "7812331664758167657763399273963290017340604299019483750344476103319142702775", + "2222332747647756867926707541092465789402467819000336747029352557749400316077", + "20438798382149666667185974604464532451975024544676922060351031604444896151494", + "16155157103796724378615022758633778903205872772589663310774455593497441785913", + "20281325298063880945091623185126257485818350714264176365501683813650871716911", + "4922178080989486450454493110764936742315495846015561426329316977670113220071", + "19579063976700768282784922967523980346960151903154507737857728349662090787824", + "2458828873355000645851832396764221987760639423132968569631493912353159373462", + "21166618206785010755521994106737991950548963896649678270059527421944129497211", + "9131643699583013708059191290958290089892787165715294157378879201986981390031", + "1820371114511473946932363841206094088983972935646887524223011276305844153307", + "7264184404232663540867032945940974372967974872966180860960243405462016972362", + "11228656105550475045610757902396386402555430893045183008968975441800824215261", + "7151503559113638565935009743218857812859208253653498318591469659718664783964", + "16876040581364499037941813142092448836399042253618385783944016186340703846779", + "10334125383426918152464737478646460879481305348617711177774418125714273980769", + "18900559046103390399749767994653107625464807708680067464279674225251110804100", + "18685667289312169245526749652972366835289568864080726348092618145885982989561", + "19970582871354083670567197978171723431124602481748785146813441774826500485907", + "15873472427137024971035326229485784626398898771525077832924901475242073457867", + "9090803292122260583635467396769157643561973206888822931647063181944243467413", + "10156295009710074552070572489422360071526675259143523597882131082376797944708", + "18600630374968456966046654667577076758720435487386724419578803020365834014000", + "21292291483064245088298314957584631356250347533568992016547598449487977536460", + "2784266893057214755054197979675795184619614089277590464548240934105557638370", + "21206743389683892419024645604723431382001453245850423743581664552645211926469", + "7915761821775326316473924816837591351530533394717381318596295803119061411675", + "21881095237485064870468603451853549262304643738646051878343976465227744077912", + "2011784725603622472271597952122938645154942022107573948889667939904597454410", + "21059869383015715705096974077910228193608826877524913363323189378554601804559", + "13660545486380051482020817701263881806531607595506890631732662177505270213284", + "10831091042775967380899180760062457635694790868286967266013231823406639854653", + "149288128407476550494800886735600251983375852319258454101603889073198917321", + "4032475033542195421623899365282946172767274020529645277615759958662043553317", + "17860535012887415629230166789742533149365132198763199254812432302158542514395", + "611194463774512114860065022851497908950074400927073001695280142990812150583", + "5518364261187313845085346561539515049557757056751872639492957432879259341390", + "783263978868449790737487156609432867806742277074765259237378374864740012575", + "19059339826992310300213673274315612374137067865428300882729551175173242291657", + "3179709304184015397125565132235783368222831063701934511986753856772139349894", + "10954198701843076039176000728742415722273043852061382139560487789741501275316", + "16411266672500930935370066093245284646483148609897099268661795671514664627451", + "14614816948231085620934132277599546641612327229810158468490195811014141518325", + "2458257206135880430320027516329707989817636936777744813891328347210486074414", + "13549483340434455515002570470395006683062583844603627042649952800864870013910", + "14465927800403373425828183741641078057513049263889255157342086762479739044711", + "4039391352709218793104596256671892882216573882631238721514928981154171136548", + "12750457082077152291009387792121930725761848879916565703854704756389714536037", + "20703941646953337308096638741387402857948436803334980867971163138332859477843", + "20148755487317949638981041809982361196106823990400472213765926589941031736503", + "19035096428824471222963574043396024781574056587456391309795571372815435282399", + "13597108420431213178364236660710194375344287228654817880431599113069659963625", + "16737817219786305757887002253067607822378794077688837656791543060369162185533", + "5164935079689729145670846016031605160169301936105766707946436049006171651941", + "21653381930704765824477248798502813954284378782353810890869232482999795586793", + "2062605478140760101860087118379474541965619844748678233207247884294051836812", + "6841505950265078437298089354417829781031272459823272323626556598403583002674", + "18723551101558427097952125661588457059960574026361073828482106612260297969553", + "7898804490983679270754258611113569895515918945891808074921872907759024464249", + "10882278698112390755842292529204069263813359338030917602809789513528936860051", + "19447560013395173052961224723195565400117958329259001072560983848146677205053", + "6251288025262210726686494480483550276704856797649458538460443509657307219922", + "13176666617050786358406074057104742181338809005466316548399895981897535342946", + "20703225796049910173111490454489910459787604528779911406172217267261190895618", + "20336720518722954780604743873837334696992422089627753769439653667292899832714", + "21420427865372074512365684526694872695798980614525900481233709853915806389425", + "2498895690812694987926199054702295457557454143930759961192198950277119149872", + "18753512301709603592612141197073246313430368834576850495154922324845448997662", + "13229612292359498096055458608547157785066962647476451239567069089111704445000", + "2690879919643532184588441383789963956137193400890598777054187145581183393168", + "14142396602342548413722428497204107502988046500369932366351553161157672540408", + "20448725195660080278132534867269279218381543910636641344871383714386318629041", + "2559459540570011016181396098001618067535109329950570139376049832813577592045", + "2209294835847631004298393339896770055851570184195462947318472391473531519454", + "14610669112573509857774678749257346364319969641690596877040685661582231189775", + "15281088465087253563674405311018738676067395725444151577815750152538449780965", + "8600553033773805414817363397077178137667131851961144771667772828459236208319", + "2748346039979601666392027583251905158817539034260921486084376270967628661657", + "6854960712378511006304629447898292218014632388505703802374806527561178043857", + "20207552563190343462280438839438087615024485494479390954719687107061991587248", + "10281541252271366635718295778088948309847900730867531177275273130071062184625", + "18855605847424121529776135453072696981767402526737712879984848146282568841809", + "4160214035780913418097601322951078913381556877408879904436917334405689553255", + "2122867135885631508183413043949777333811557914428796322029495785048111325437", + "18793959580906171893053069386015945646795465354959679615181136313144978078417", + "1043591673717355695648236328597936528752358227297053230241551190351813693314", + "15686469257015275311444450012704351019335987785561570672026138336552980987277", + "14048856209379833670666148034655599475317994357805584661156301746235313941815", + "1011563953969880478397969933799483261900428580241502003261587014788238280391", + "19240556623066672446907714818724971233422104071815927265423017590508305430997", + "2121904286573815063480388650799381683473766736407678915747169455786741101182", + "6724437969134367395210139771738563153857495313330774537559578422672993498270", + "20206855573383441961836932177838081339503382415601366823182724056749038447809", + "3659051978213562322887447057085386386485486575515693147713900345497451171308", + "21246119528547168535908718411570119652856799993958321864163737649108920924448", + "10446114322905404392321651684574668727564081327779662579984472408056125404335", + "10052242287865403393859620372179811039720807230902452334457123873762222543944", + "6373462744579965543231173757071025010089494620309953425653057223643612177083", + "11716070974813426833631730493593924834405915845847679294742728105127112594434", + "6451284530793440411577197006976867289209413848762574411101073727224316913966", + "20143217291446069633369261481904349401356557325260758866598205109039367201468", + "7741896897172494958877302103827661518814930985518070029789560123401964418102", + "7414486245715284930410091802521351113719159777210731898112598211035848096490", + "6480506916211642204624111742530825907262535747743645014149694168805302825019", + "18349725066341807634895742572304899830893334427067633858521634672944685466440", + "1838291082333887710851505844271184097051704051003105078056248035350245616867", + "19201915197596065583046168024521824662441686729039260890206806469763190071269", + "11253788423541320580105520117231178489492440242200599071301755928628199128159", + "6048832714406694444296771635481934823208451249770515560893368035838759154821", + "6398008918881249487422929614611145638894557821587972164243877575640548705346", + "7013037564266297435879776776659289982125632651326438965546874242685502904730", + "5942504790082366811245813670914617310604940200824079289270465669331434165301", + "14344789199380317440464969138686896230070901882253997360605407637865754361287", + "19920212380356573378521292048728904573841049083972983190424200459025557666792", + "8983390577894750782268266038315113359711163721228398686939390484499979421166", + "14953991148867572055684497824790735528852361750007063016470842397064705671772", + "5592033578501586280289038012647352732276003389059749788953239057845882297561", + "14076883072716069263619564306953450824526010844333044566762059693672378725675", + "11108270411921226463443318601950168860230077781212396032908932369105145901793", + "3681277588815101350213324449908372578846563884174807724121308021640034446476", + "7194753190480156904207319938161903897566477363779122267985209483435838216959", + "21241255448366937244332942306324590869759761073985963892514045368815880517382", + "6203071960722514588958553813186803009742459823360660333787981951206442471249", + "19041823565851118046937769551785013706136778514067168239416647071096062639366", + "4928136619692555022185087228378238193895894009623071873887735418398682287593", + "16266329364886004534411977872528706660422476743809029518681886596981922182359", + "8814684891729998059175829142248330760704444206534875755023421115211106199303", + "11072277000652722690981202459933101924925520292174200155471966778637063588914", + "15889576313969861857250394875354819627977602318110620311480656842740292435237", + "6934515229262494305594741689326968268143898236690173897991110238064230886755", + "16212991575388366798683594066983659236103186124339324856776288894513503543244", + "21100508914867482363389012032457112622475533432309937238082785660233880354422", + "10381104469089401657446748653199843213201270332853172509558263968565255702795", + "8849389605935865968361613766905708889092097013638425059146677490704442276611", + "4826404934194100291623537890117339503344940312401101713754206109744511979962", + "9981819567268652304810465083896863711149056310505889216307212434682251812603", + "16218484218588441290424553684558267080330286201433140852298971691458926313766", + "21317661296916247018967238829275056855142711494630067664736600708605437812892", + "19523923008662567951910986132173659591346561824926093935331274289896011695634", + "21439241836891927940168832009944210084078628922824257988298290967895179737163", + "3818036890597976956138669961319975835941979944306305168232209375279960168960", + "10212547715001519604442389033695156945619060410131175896383181616280631586732", + "956283172524544133830416114111944076629240232397666924807554743752464221045", + "8545109273807246425343308224167362024331960554428088718932211551700420545275", + "5647769597708100114837534314408246331518385631750569421373379085922684908872", + "21776221280695269311212391423788179027868152904973644113087833004348746215729", + "15989020831232836203074762591626149244364214836699154611339161287030952623233", + "9384665943619921791886218744024370375464874104981653298499433530463000935024", + "15469006121097295841026542766455781293432005131673839148320165243166330403027", + "16103671377537767724271717097892044266704736999841135349844319906338275108222", + "842367229428650719054831004741080336526228967970570607897528985803108607790", + "8752325400224955775788313769797750158375262384121380328719514077259567119347", + "4803861091350023344885030428100876947830986453029412601567992550504530969575", + "7917553047944370948250445233027936387189889293110390303835890604428798853681", + "16378323148632546424902611135263436821435778030958161546757828745002247975096", + "19873719885630097137106352132870659633926425645300622070145979694717581586592", + "20324790419158243246762098227260178678767896786893299456278167341205663612964", + "4358908354524026935988729716331497263147669784003421920394531784876541301801", + "14403952632095852077754539203207047943619815438482171213105824864831554185165", + "16410713482142323347391147127545553384558868490870150984280601225023662513809", + "7304216341846662695189617252648753140769311862815448449926830269690397729157", + "16792943782280077475956215580025612636120139194657275471595325031090407485768", + "18494329391227402645175320826355306995912366111176422593669423022411884295357", + "3277597348237827068690736756050060740435013727549848360800059544123155276133", + "9396765756719511114743964794180256605700037182617127755220919249774110852382", + "5637053961584389263881381098869862042993858662768294676971865632259649027245", + "1752142832257643043564515360000718468888861086573246457619082905919623770956", + "14504506574384680785750882507533398260948836347427103366421836731538357314790", + "18947994518078004413210940685748534988014581551965984303066903086446389273117", + "8931855168578615387850254663107425567403115805663142600825724478150698936342", + "10982092525200624040399870568387498905840578524691489797530932831401946309626", + "4738907023206802373255186532236849256768509848242049657234258536668430260775", + "10888145285628319545262252531874405309329869513560101920454793431198094714989", + "4767721624212785367044047554655794533816937807005608600525762243335180089923", + "4054394679973840378112083329204220302222586590732553688297938891619998137578", + "15390471663419625573793381445844013245022413344196724396864223784781333233143", + "690498740448849288977645176879593806019080276382495160049117613302192708860", + "3326968907274045758110436838010900592335267522219473049427145975873344598768", + "19461545874830130561487975864151403334363998126023624462211037468138940028328", + "2255249425919459031033123095731665691066980364231819200773725596456576056043", + "17139538647342063569964264947811360956712827863014723985947727876623459280539", + "262834317961189780923232082352297808796511874872711860311746704570027370416", + "17784213646586812350819691264737755884800773322574478474130308351003659945289", + "9206479615073686723914227166450906925650471865894639492301222855979337534393", + "5955379232184076713510750681781395826148323482009739159408415185190732125682", + "16345512244217240951729073298135981012471478596479891072149124888060645303490", + "20053701095030547796310908765544502773063879272854547881438596069907281565287", + "11519146559536679602608982593432194283609736022486509747046459824035493513614", + "10868663839942247532249591973192159672852196011910414460124452013501564199585", + "12668355291693420029179738224611760713369106517542315102687346083105601320689", + "4091011252347209563858280520339886760216002486858313383741839652119084430270", + "11416347683590132388448480763970462739172261435271326798646502987745949753371", + "4462763980178675172541782335457125059884067698347130082276003539434128058577", + "21728891122467658477520865529973242372850367356840114983386033432316519759391", + "9556106604731806817435679463077765288658189491612307664294729425381901530224", + "5086982973132652080709554654284904229374030594786774699435814748257879554118", + "2278505454992311041650060186856758463754878439802195559533882189615578260695", + "16123495070352975934848591912315341924608875638550779884194576881433498909405", + "13177225503435100563531015597038445430211235761527278782674200718068329833622", + "11626932451843299545922103072142674578946680165802341368625957942237790110177", + "8872973246419344365802198448930136062421718851114220299577394844231810068090", + "11920016786052130191738519934437207519332291620474831138559948859328822621221", + "2773753221970604083383541092979093729869734021029185810064937974430862835870", + "1194583082499114147792330367943150006952486615245506995832323057119894886077", + "15293312601348482070373672684782686300692505365845870624263228679370968807837", + "2292156760291800990693425534213440357167359161992251338587906324724034592198", + "20920049766730284147153707151387304988393631464951398563908410768221002588086", + "3587899345078220957148828249287269521408604837648269936718299413697642586126", + "5857527906708110948691023855516662527925762284342493618496858248142623857037", + "18312267494676788897591109008609888960798722042916784593521762607767538629817", + "18354455618287562133438807735729369657256664914390381320892039403006410339493", + "18594037435499535688023807489676900345345731643180370940972090155512943637000", + "6361231157299815359812386352981667048590510979947935475914610076041390336883", + "6503045850716008738909204934356093641022474278658078426701342798380459107813", + "15826908470360778431798326530563200301151807861414464213699967513881040969457", + "913167165738148713876672473302437265273760468892350716109373788573860454641", + "5163418960719047707254162004625467116036830361107107814320243058319914687515", + "1852750695670141634014249062360862036043602867770163972096325792863710036947", + "16164029969996795952250343426848596535809001568622155377829217918121790073916", + "42291476149937488089591434144089904529405222471677684973768504172369443350", + "1329340386229357940610579826659090359930768580941108555938139535621252899508", + "14087936453397725507000489457270864434699508074557952952329368237400407748133", + "11454917885298514922755456675259734718428103879515668717779418480236210705323", + "17749966508430836878443008025013283275306943216523661550528505419303121693213", + "16617298839486771009961431205770630163409905047728421465641369616889696635464", + "5622873871440608391107520706189063847917690892897751818294742462879871297589", + "13537715561706278379083684257583804567523085149672090320983273122424669242274", + "12609629910090871112615676094781247031353826207267723991911250780907380059468", + "11881347692420971451998583525696964339513193164613288356598017302547676912004", + "3620434358220496198439193226313617496907852030586214671337652678218740406153", + "16586456872124455799862826347901525401871594428044067424833235946565396779382", + "19602593015746956165116919928045364895525104709835703557292833702385934632182", + "2465427491077301663150648330772125184470808854603184374760649420983178107738", + "12521323976712195518272978277895155774288446093713549157148428964880747896725", + "361951232333654306694462853852464888974834703718677826403016226307188397185", + "20048343816024297162848487251896481827914904696805156112188099141327595641104", + "997638030405613623344188782838773314122493364653596616029491564227193697621", + "10932007654988104622042938184134556963651043067553327861790671211490960094259", + "47171599193060570819891696279547021610376047998583333086685382152080932821", + "14669115378939104862697280661831896914139331878760241858539421915983017116504", + "17868874372855679948405169936193924176514630305572838555185339642210810710203", + "10178296575837129106771098084407669500326673901243393867574658658064222502028", + "11497182727976130924559852428316615034304736115488257034951588831868596612725", + "18847036158089242140209840241495282890278502700082131513222116906134183113862", + "15514518995390761662346743876733004358408187550386554449789531199638765348953", + "11474102901522012346251529527050392650125347221410246734211005177721289856415", + "6612195415835443084676700243243174090072629504450965229103970796390091290688", + "11572474094368358234669561324969692616275099241307798860733942350364532366113", + "3855324911963410548772360326122995145790506408472649961229511965629894550308", + "8802640003128749594245736338745752744580147773009816234644244502373660889677", + "15676839305513015047736600040932186843826469281853634239081282896349443894145", + "11124722103091011602185413968164672678635980457394627450785290630813993266691", + "15087674670944618980358596427703842917302233637812357643695687556421910213028", + "457555060782651847600218200815104907046227486293278645126081160142069992497", + "5340353060455057701755599760342180989590806327490432497082435572367648024359", + "3289809733259936118731355294329652879189400852472418229718273887860572748363", + "1821386174933044868215348232606758690922944887434531299978498726875279584854", + "17399236630582894158137572250502674699298844870791766041927951699287421557453", + "16772722824042046255416248879357647708113647471330900665176012648038469814744", + "331374066696126093678097185404981758791664151917354547180452342655690460271", + "5482079579065945934120471179616600325379965440378196448353560421120276746028", + "11861638874356162254375133266687016527365630872709665703116365332534843803431", + "19751278476934230895840638614095718373810690662562196455711240141902305648888", + "21017623330912840225230534280017695045717261514215145256795880310933667407841", + "9692530233397639077769939390011937602190121885296235066426091743618448584134", + "7914031992737639503490179289412369887137436318696390718781298556229610513180", + "5046304088054212585035723354298412694927209198400753780585596829596665931980", + "12735457541003664856181534137486291132119134214862779086936585300598349629287", + "8144204472889944485922664106370529127382213990656088602566223875490414163362", + "5526161442679804982165840590640681348630369336752481706044759543203459722566", + "4665464612431440885211271075488840033628676516298384234452346107374012633528", + "8451965709652752887539585363308640999657377914501438391781526068371105983117", + "18990458193856163728406448194111866469438835810342179114684453609893347662421", + "14602960690767985987882800342208585041637986661619503513589079723840776294824", + "294650277854196485752526848096008214721988745350555311479128101695333774927", + "9930361494944692931597991649915857642608730961125454734483697613693272941776", + "17972565769620820679641368732920396905240248490243886868922250461473059009007", + "11842743032528966560856860268344505094861546674985872961254820091273444880060", + "2260251491209762630871337015316066081541066308706934094017641769176593121838", + "21336986809148977544823484666876006147697590184356254785752148187171367963063", + "15637234083283356311249527335446193685599985235080555266374006156231977517227", + "7637477891046186378249227336975234440873859617986704147458186423096226771577", + "10435340982947407847927678888878882924793449778165415690957335683641419176012", + "21071574044063633264442120715854514033847137356154103023224485568597330648075", + "20085745552872944745120547909310789275453780111307008151203836541147270866122", + "2369255222739182549768488367357061329939116877812397072967912842660453854658", + "3320710154094663715463854219978294133429318041799642537800174050047893035878", + "2437552820481788519744888712380245016748276158860265401041560980354471184914", + "6687580113987208531705167517979176727449238324356562435678492283111952291541", + "13835828959457330678345759960614663723017667326485961761361157914420441377430", + "1823843951353887792473925888956554516299304358703549730900495356152013614424", + "18229384804985230011714562427207966412342158903455811854157839446374012856695", + "4983049472282717134994110428470567601005310848076496400503178535459679438524", + "2047051967230753763135778305592853785901616983565528680886843131244871631064", + "17059505494771925862841990046823342770591010831955480339095397897088168520686", + "5845823714127413134610517798305104245114036685335948729450609519089263487144", + "19810252752845594230307894817800427820113926573704856490871938876757561680148", + "20741340243371419379519807725035036726040739024854919427690724405113594586449", + "17305746835229988220561638584011917989169628535378748397361130724475478785704", + "16273970657972145440112726408308019138099820274904080726219726815138597785735", + "4927605725478881247988642936459897069651251926499343645614635597380235002430", + "4076655226193629464789557616268492785057128805549395585385432329518368497686", + "18134767316186963456589895259454813585756254459227058992203617493951135964914", + "20798436806114056077588608064161229365173163847083955162560624566238528904361", + "8811900287453512972593412116532745098600991077158875340182906101108258578231", + "1611466530857794066271650650204918615746591649578992581483080164777650137733", + "19520757346022691586967284723955378385034675472244175822936613026597514818901", + "8258287931139503595713718829279050060190693609290797346704848518381891359704", + "13807143439443425137076128013998009581746894329904809421858222329599144124143", + "2034200548964915935625429760202284220693125881760822084201315022529206424506", + "20594375914400911567795140472107624446159181622166676420027082349633992663301", + "17773828019575037451999782968066986504577459910353828196403976545023426528432", + "10645884969014005687699860915213473815514464399964009808411811895545112650817", + "3135829883501342672772973577699379927756997243617424917654928164800203666496", + "21807676600134151299257078976418813484444183016737321278512745883771478511369", + "14168063038909284721702678019083222059818438340503980617872573468231611140141", + "19022539506931505257153342575586362988716958060936788031721967221986624233067", + "919797128086310623571009200546035983274688764270933413427846490906074137487", + "10651353481391913627770814216074873532920753703051075188645774021198634943682", + "21601553598752750925049978818528421110707879819831249175157596816870100048288", + "9544964974935674319204796617933096476421551193682156030394816088243121582636", + "17113833205578964054057051521784698139661258340576694677296240312431808476286", + "9889647672195559279745677506312894570402108521106900082889976819798270827735", + "16028191999932520938901585234936954312994452706490572504997534210876573833649", + "19224701772787524647172128751148104366752057774529591812815327738829591289117", + "8065294760892477625290114823800398061529770004833832691347498933238361039736", + "8385011404987806129246014860479833290406969218526611328586242951296814426438", + "17626526623257098006524211054563886193098683828265081734658432468695686509315", + "9760584950604786147191288118087660976225563461953070125437519145090832114537", + "3282956645059793949082172795607530130101621492305193365378997603911833418463", + "3788543541342252822847978185963388795825378340921321139695221828685330606335", + "5728277403393912877393143174229934529937061751983246730506397742038949251701", + "20532577038632159357383817240596922896191478140446876998140515404169184846609", + "6138500779693128517529525961343097735306947649093633133232282430353593175172", + "16387038830089541476468870208162294639575042754761542956218362331966004300870", + "10184264376398708852688445921404363179240954227345322711923845040842165453208", + "12576299651793170522912156101640799825541149618303513174146382191633847258859", + "1340015400080181141720946234858756484323564628916867888877667239334982793481", + "733959369856163480135680991009606990817015555938726628110611986599242143578", + "11467033813562140192244869512537566463715027496952375979909160849747976831918", + "4619667645046391146577435774790188488541561222783010406420406869960248783331", + "58552761198135931030902257754896948615688045302818928845814661296914920622", + "1199849881730507352706524556330002080538296688430736582840314007371442152147", + "7124502590511184113044595527748024819132713282667933641439666531514739645089", + "8623660134669459112474551498616256867375253975034970808437732784494772311361", + "12655669439191191182341423414424342421477486764113555800095493091893820045534", + "18432703875775002490514477493898870315422995231506677048275960580528644904682", + "15467220287938881354678249472400749704814316816035426814619089032223454845193", + "2851120240492392321044027263769720216640877441121430445737594074121655318176", + "20519914249934881206828098454303256358482675671718589102535780334267934987941", + "17275124961392392047135728713829752470490098022504524438869454049765356211723", + "3323710067527231515807603961736782048796606296990840839366613937968342331886", + "4468708240622802562056471128793253296493002925988003094771284205007772045098", + "9006494818135081033869830730030943407240565201693254355620348420258773924028", + "2624130417875598753127999576825019766166727976335690685433712946223008520912", + "164131399455376615654870570697119442360078693174350746600132391198500093412", + "14931668887432843139264972187415200544679230597820424081936926034478502874299", + "1638753880783574431267395352024193675000113296497173968722590753809640941864", + "15505380865926802396097545843811910443367233632805651511272732002583232431557", + "17973744614207669251901495093091561913998272050499760575282030108740677066624", + "6137688223696761009295745609563284204827706564566466060484103844265403078408", + "14774243062532823236792831566222119634320864630838624098798648826842418775856", + "15864970393171078370207775103899428499600152663946379517190945807315353544891", + "19010063123357565300336230971672519561204810737546730911549311353159512986740", + "12607162829921425080830052984475623157169603642577010527391007035133383807243", + "17803108634879437217723652777640120469990779759700458421844361066182881628345", + "10065874953507223318296028499872542865030107611981933577973812883589535269142", + "3276471432535144390388324850641020151392959100393035635141206272558418581928", + "7532054601401798035926415744768772852833516520318445183340725930886329458991", + "18893822928119227829016544343228228897166113682019317256005502643243867377334", + "15940597493253236451533839310728876441657428995464658827726295547815292644378", + "4268009387843764409267791203070919313017052533005657826253994943184768120896", + "21611251949238422413354051947529388972078300717392131751061464498329326474580", + "12516447001729804412674006874184731098280474050775388553768469608793631490618", + "49838549447142926741568525697026885045023997277705726329780325103507790978", + "19763902910323896567698991616245963026306943100978479625077573937114135803058", + "12029297973430627253212633299020402005457460023136429653800185001711727387314", + "17676997725594777991384952086633589048516371093397126876621255518370680168503", + "10567543371894667303450346380722020266352683222046730266924342174164712049360", + "14583364850544999818712646438016435003942847076919084667364987497592599663937", + "17348091487238815837308569582101875357715798351834275089190053280855958465528", + "8743083090296259283603789316855921930102444739264013461469099560398359267240", + "15114064505647935792598848256320570567717917317803629185764147361301698519005", + "18332675991829764561879941291908436508530604635608341316693114747813051532006", + "1757567731797951053080580099911774643896363235228742197150882457231133285549", + "6526388717947413328592956348507481629843816325885832861915399601868279124246", + ], + vec![ + "8243355230504186170667337521705529968548180153769821936979698914169521362326", + "21549235422807751640146583237936799392598740234259041629069949854834009192195", + "15309683586299089746803554818142261058154570215179112411063662706557055610156", + "12007539402495575255755232938576927941514879725482443887151392201585760698040", + "18793669376013417649313139054009540629720623019893420956495818743913188610515", + "6637074549079529416739232814950531409613090469922787253991308038219905474403", + "3042007484821627445120830225760006405192082634864137749621636257026891883326", + "5337388510268581167254715112479133594089770138749507073603490761032513368106", + "12325446798142239188409242319577957593792614990556679862642230477712636037037", + "676789245562467194073706116744095779362669155912771165373940448756070927910", + "5854747984773506278911353281567883752585612596682487681686710970786834920041", + "11245406467967785626327694659468342056789182160059009120973665143197638081760", + "10395601815816075071544509552592627172226369015806880764151195346316980080894", + "6756096862783612163697577917108261850810460757753491809406999449771712474223", + "1708595072322964393019739105130946639405776432058599259998973103484499438306", + "2817817145890818701877539103826217929456570347854153048034669346981432211659", + "20337270972708498869284875601749656006552838338471813066271573323209168221011", + "19192338172842323468707146045612196807750411464817516820711948717057036544820", + "17223253657227310295312621282100531845543865578630870272599545474783775759681", + "15004735209586276209064505708625280228119288986650187909395010184201059452346", + "3875652974956649356154345677088455126258183810851242537013757276075769588050", + "10514447960615206081458524578173743817818597124482828867666984705327684376752", + "2087647010835075851760610474040959236825470174942075295716631067964093542910", + "5927163251920754154392384551305623830535034440727310604898855074616515892551", + "20585333621997037505291454298836355589763292536744926081563336065939121006537", + "19320876518201905459682928158170419256739531666800973485138890064423348282196", + "15942638804716709831210239594904570403189415026144938623559274984027906868220", + "11197022744936474661934096628367688581641778841814728682794507017845346201383", + "11034020922250561671038205476395109731446686553549026383358725302157324264144", + "7574933006942933995255906769787776608010920618615581322603847524789684181970", + "10061361506744906780155460423367413099657465765582917482575074226383566926764", + "18611343221859570540963418999548488653944851224739716224660835306206658947980", + "17094203924957299390365889251598099482992645049968199405515681968938743421467", + "9407145832890449495071969940777105644547801064593141904558463573167881762713", + "10921438560879150587765515492087524756046482460218342400194862909363870270743", + "15101279960899220452674629307354995123411280418550386595937683027146194547144", + "1872357133681596467751878560069114718371273548294363719900935160833598069645", + "15505500304018853111989216259257978796595506623204851206292254759641600763191", + "2079667978353221447444850850900204451820443725835104896018664141845782871343", + "2852655320672908960411014862634757863509253400797831983637863741066632490909", + "2702824031197306101989338159138451445088523866133498139857862801497066633794", + "14553308731276493692643101846551382187575566516925133957384350697980935154102", + "4314969815396483242407853639218064117498232660761075778657880116870422414637", + "20236724297078811959918602376319440958076910292454596856154100774072250182183", + "6360017115980704736383763605019264589498600998515606807745670287390050560160", + "20856970531105411628054833058646203890148287930330473527735908484791842390307", + "17691356258507144960616314395885779533907781694329041597441621553108536658757", + "4464167934150673174817562382299722091160711333547138388803048452674668158635", + "11538922347277268848344412167140306567742076984016453903533772667841006045703", + "15558861252260038101730449864896864763293561339637017072015859069059083288561", + "931980552683520059135814229579184511049009637966018180567726214946979768011", + "12746506550979326220422215987591117730943427023997792332255149062957909690818", + "16416138987000536018990311324687201169959549714116951891693452597169869821726", + "7473835750915837381583185047008243788613524206396316652305987269933344653773", + "21223994082372071324452834147900730753626104062167370333103771844983134656961", + "11102363694946721470818933128034696027504133564649607436252022322296041603786", + "2666835000155694643357391634256423691785613060199379949509682292216642706081", + "16883033667413528795407641102416904598130659502290474063092941543309042023190", + "13093053604456598783294628038129487761924241298889312497497820946915331319389", + "7426349812936697309541457521193139970366533826612714195359894150484429907425", + "5243217285990182677741567384304278362485372018078770234262925321063263504918", + "21185490040917275396474067542756068684704036418473170810170344320388557093876", + "16181135763579884029508432324330748636846464150219757303321560798898398598349", + "18088358880437096005757355821526785623101357556483672471222924931365890201571", + "20418860027198053484245336569800730261127301261293595190270103940460998981236", + "2058948081811170389115771489993053947061173620273801887242248130631460165879", + "6353796008567532863300373986154930294334380098977007704532496889557690195858", + "15854609649070278722833415779491666201355987522519101725393408435189057056690", + "1355942327518086746604287131396672941922424788908995789539897301592998007690", + "10194046920666955610804398522181498854525794643476895032285888778350918459761", + "18342608728256650520630397534564293474806178807929639999068140223470256007117", + "16101948218093381908101491223075947943147313203969904451859930796280152622017", + "9866645853452683082481412876547916795343134459981103407915522925093474319332", + "9309485422719740772955698359258466728180120624442685713365406080485336040166", + "5201701081505060757054562398073722930344229781365241858092054974705598137660", + "5279555243870694216927790669819597822350327573071817682265773244733785382064", + "10661662716572743893824841881707597899963881485303936548294117975770384660590", + "4306964326426793675768869124893413588264762573088622132302954501394542576141", + "19945975928045383298785833694292459276727208605892865429301546022994613804030", + "5037834331249812829239656466783521330249138768989720606017856991559732121456", + "20693877087308232030611148201802513236570270737947270986743265610517665094074", + "17748932969923719316564673051784340920943155490113289807023660243301385585070", + "16950307665556055391386715682532553772527550247031548278958142572490582126842", + "15034211391483347494286112687349366897258989065045859280146461213731663274520", + "3455096385235320554100221104677124747996171720170690637998043454239897385610", + "11220329458242704347549150795173830262585759464331372299692251819012138352257", + "8230076319752658879891285909687940775399748755759819661970430769188439691274", + "4178690445391578185009939705412120505162313641744671740163024993195883735198", + "18632680236376151061913536149173846032710756800956417249233907621575802688710", + "14168747730472612819827430620596085566004981811676505988180237018638188025380", + "16777617016129912124437138351698263064579177499617525409625791377061066895460", + "403267570119386144603206457308168792379980670187570608148634410971295877610", + "11045890302538505532103216886575539246473207034538532950483165910580782953337", + "2632893274667647784827087132221744991131294771819888858265016332574437797556", + "14022461303364013571172470728150898521630042996798160127819093871974124417229", + "18349129573612583311962846403448135938849737390546876598640066736462315682295", + "8009723611300112743690923532773238474616291315457276539919568488041436720507", + "3287586297388209299132232426281031982329712892122181769502106059441842217623", + "19893256464101780566218598404932657965361824655069879954668551189408491121155", + "21779954643920608321663779655887581582907923850271820082121309309571440586162", + "13938145028737822338330333388496944993576078307754676998341398757402576278690", + "17280605833933949866452995551396279974325968699794264573823990818913515933775", + "11562775307500290654949270847967546133812416593099094805234457839659652146289", + "21556021192476590536800970202944195471695121915357500612310904064652863447972", + "17407055226077297021071802288772735837293135175537846248261973015744713174949", + "21295838064085671525042198277220548723525913660103018392096215316189390548013", + "14589917958236435754986191512564058641868109230240077937707647376289105324812", + "4538073055458854134606640263494592220617270326115451287834630189270577020111", + "21247609438242282269742265796811514090579388884916478939008977411932487423659", + "19263560475610984724826226948356735903574936974192558145730920786586162783055", + "1898614508331499418660051276594019416852890004788354240344418815409520758722", + "13346547977920686435662774643991891597826323722140876186086635239306340843003", + "12144969177194297999321084025481801838621405926243412487948189180755523714531", + "11624156909934489978766768065107924627236090741698411458481638802308500352917", + "8674349037900011131899280296161700067911742760618648557038290076406601619864", + "18627233188669469962636721109716646416813512041955577645627776298400086440228", + "1153719160094308748956884656041023320488424966635003188538565876464091909764", + "8000003066081501211900754070779689975656073731442793160620896624291841806771", + "12069801117560082050163959286673266840809976769131514316118288648293224324822", + "11694828863372498882861202648883355759680038037706633938668096525787115759720", + "1181495201505177954430275085371953511604847831716865494220845031383860562941", + "18321980275956746302814628602546438645691886543647725888694024551609678639266", + "2785661975937033521551267460848061931764727388015171856456622007438303671899", + "15557886094116287182932984983441793820379366058597052543066101158081817575352", + "175179830261452669822497364983291141568331314582563701393865403724263011876", + "10455128373814266139918350629083299308526836847946708764631040462916637941146", + "12622681406523708498691044494295298210175441851465578469593208754136900020434", + "9624138424345877000077746656879336097173254842107184716328214933320809030543", + "11726383465426411877912203592949370178096897707629953853811352568008881233112", + "17566146584557385507728086844334319515338136183689530813551207417981719751958", + "18423839150858891406289385710861955437811779173242111498197433255650436048047", + "17408376662161624435555256564084894291578222902661202310977717110546842356960", + "20995943422377609225953642092578140203148330329113983394181012996247925741957", + "10409490873284794620245703460832015892256721643100501421596423100640512505920", + "15047062105747285153444463303020356100177963702386173227676803770571846532695", + "4535940688608096040988822900684697329863791065464226849059470519882399535780", + "18980357680792173392910397806033731294240363676914829395702138582894418363978", + "16468042735091009392571235146440392007609078458297170996132218787642722263238", + "1869769403621899262774247370472546961521039203681166934356431996537822108263", + "6151829532330885020831674048300360431343535966534922988242884341920915237665", + "14373964388615044752046531046884609884388869283450342961030080770253954449754", + "21429869771065858399481388829822721985084474326196139156050788103070270663923", + "11836916222341149344359827526882466618136359738495035945807998286429671739008", + "4542193081188277792793758113018430324598765345700596639963408884670534634317", + "17262340128494663310404052919129368521415818617921877469042393034218456907650", + "11614110585474201606235056157412783071151951301104822431509283035322273244217", + "17241248261774133453753660970137875514052923171943595080766050681996607133130", + "2990875140768570679733810173464987023133165559726680992079139149034178002777", + "10032389096385585741539206260012253444831624820404318451026478423856181568200", + "8391217416130739565515338215591963109158836617019021044489286448654465296819", + "8553700889274799411012667201578367398970695661169430162294018618925895640041", + "13529692770771168133213371031275281478756443444824139121847596546264553079152", + "14478949636372928879378459122088894160202116364833386541382488835123981766413", + "18528743543311452855194545818079449921167163839226390851954136986727320245809", + "11724222260540829258562889360923785293478512718704276634048783603461995522859", + "2652532822068043785753514309321715043229885635900630208154874285707479247265", + "16473666207635815797882774885364997250503755116232911726426811919269547851975", + "12436631741803099512327160776479880302093882812091908650798222524569929954222", + "13061081443094122428989571162147084312340276850316867585582410062467362267361", + "20909566607465067204267258789556187669343825005173558971220332255443231196363", + "14278016202378252898173761523743422243750790190417896338147106476354187349947", + "7703701752136585609667768350038563449121231460368808945757767724712186009894", + "1622258312841010773225479468430896972269503924285598181547410615000034107894", + "4706114868510775588142857635375822293570353199661120256611528287780303504954", + "12723022498690150801900112713057006417552064300221766812928489357200260312668", + "7736508633931646965699972944684083339925061856252811104228904321699984469949", + "195095354858363944780141950724441876473553677166595890451203685104276178612", + "18877614091447727762374351623731936445361116363480970639310200637662433378180", + "17239262588506530491210045452642505719938421789517734104955853192075731537629", + "3391556611912995522919492308422471958888145521362922265487749943660431330300", + "10164629656754294522862462407441648133619259920942013682702008716587122474446", + "11939828733425435518898229234599966533928666730047925120030711579782543312731", + "17335155958861138542643885799966192412363788951639890938680530110842555336617", + "21068414996957890621467676209673805582866493104159841584377567318112060433438", + "18041291613104743972430309067462668732698702146146761776321539150844598296986", + "6149130772490689572076747194977244577047643214871016443290724757756394340290", + "12105848363324940274456322072887282559016226587661485273111872063034847034485", + "5683957548001811989600472365740829603387405501208071642225953069881259762607", + "16529542077365261070047716411124689196456625611983373158922227651721798753876", + "11961524596519782767188645738887896272947446382672325012202336646508449392990", + "9785728068011868312995387469680578201705397880590293454099364001157116688561", + "18127416268588083447440821307938591826251677223119815897950307944959875167560", + "19296461637807972438220899702591874518336722552660488565818484435311224286288", + "6801016831512114134395242293457679538495311188529990156831889204433183626116", + "2964298470426582070507861407971247200639242211740381994158541687335361446525", + "13485975887078791259342768620261671076376983307468484850600890777864999230190", + "18842264035089067687391583729082424222425351385494040849910540441253540345719", + "14703642210510851071131854548671393020078600676544458548174965732036621712435", + "21220214849253889952179905879367949668848598115028365535238742829171770487419", + "11808561815315084933226034934054773302447242219261466208644893422841430468026", + "13540888692913543742580940929469376532537583430034252053023468103862294761259", + "7244161097354558003276348625436123965060461415149286453943040900234287411785", + "14838699086047571226987010390426316539929576717533827724866261274778253262656", + "14556703155521968503536618488028548581329555701042498979115582733446728182407", + "7681623302896593715513288894378158777679657507901023568046253058158573848701", + "1088441387469941348668229287331864702951247349577784177659963097331109780661", + "7314603916265509104428110912296267885635061026393352039011815022900719549691", + "3986211915826218802854255636104488183733664187834078111248006041750140814882", + "7773946401984571616670752866609685859292708427659817737120107917606152933392", + "2842014599902358831415178364343115068084073955515903534808862171830738904933", + "5310724334723991338015239276468023426385678184604207589409781216959654582406", + "5255222348968955358505450804240823699077014235887887249383824524518164498567", + "4683270496545943333741165516340250527555279356319043788098737100323469078711", + "1419863943011284607504318632953959861647793372073243840131919334395882404459", + "7983638904317557271319561780754076927110887040374328063199742162092282580125", + "5569432847705373609838086039153225563020182698189928344759413994203981320990", + "15459233133041758499623402905899885787129812358908703405750502906067055055230", + "13557004098047782158753673078158469379829777184696159361573537670440394932233", + "15455882302725774286899673141535924396516348007554186719344822187820635072053", + "3420919058826876625284567898132572990967515410265578892047210512917031439632", + "20100418454140979684745740106982178755085746706837715848777042819378494283102", + "2569258507332519764813672456351707773863376375715947817185409500202699032309", + "11051426796304102496144764766958179671506736496976882366028801902480842422589", + "12740229748287653735988491742372785228070141556372656548689214318469788908817", + "21628842595664718258888324339774974922449098458375293925060310284267692457557", + "16339231976272978519029290439531768093693541721039081313180796119705575069472", + "40124736742096746520902512885311967045111742860721554225254094895613700655", + "17732965892472841235257958105891466451086090480423956940377743815006013439", + "21822629194074446176794925064792912534191501981075390813302606875002422233533", + "9308214945046921143097017249780654286051601646816113552080893008307002107495", + "1407926751839535775233537792971129618756456590720440342541085713782189375466", + "5640645423977029900985251540406734874840031539109774937559862819450972865688", + "5033216407501194252797695593441325021622991729008118693554186469034086370061", + "8067057037475400447259522316648004416684453970851364075976857314405950145375", + "3763719773038467529952189678629891209905984306908045328296798459182240539135", + "16939797418368521863388331657892541744299855742774206972703171911218723184714", + "4830944198856568835319759101429165879092462296316662230100861015921313890231", + "12704214658232136513943612645116991664417275945120192627735782298715562058820", + "9273823420095008025667777982828688153052061387261780450903573585273931011552", + "11055274871946976331353174512200687536982312509623944578515862663278819898965", + "6608499500253253446996042326570359354182967780655057286059057541317584758989", + "20888058022129906086941050692798413401844596394165346138911969309287247738108", + "13297667979268130800823342819300433555314639138313483863899090834749801969571", + "18968104066692458124571065270953767119743779337036553042450471941512165236867", + "14932841303199490878640323744926137685749952622800747995690439854118498001885", + "6250599214474930878673138968631643032807502364864165001640712550360147900771", + "13872044280192246670253542029636668414586465840988190477111017540404431909403", + "81456119668307937036914780206985985650137679027930766352442712034886058018", + "8178364156193615628946078892680068624209694278864784660439209878556857933585", + "20847565685305938921688196081711559611104247746032524045765048360946563554616", + "14790603163347071870110696142274029411377352843070075577069234486581346354229", + "18977464663780407707262531952390299277523056655145169930121579582916387871374", + "16780630803676794749613238124686604459373604071531057035207376612438682381040", + "20186476042367781999034353334494913683828163385175556939730585228743410724033", + "6782638209588187356802454014110236225878206067794807253486060610876934918759", + "8993456778572039939715813797180666624819850516232234360679317411311388323391", + "19966302498904269727099815984264954717659138861990152509516897188319443441697", + "20169703794592063233917650314404110898564218327366603108408586484609331826027", + "5979829627203584558315118820578826847995466683728103070319484562170838879477", + "8237679343008214539352062545936737645555361114339038346011678993504862443129", + "12382432100828502258569798167004899872248210099869176340581848176730802349663", + "1568185664985590267262857882936657784210740515169196983171026814738347336756", + "21214766447038120613598232832812136678657988502205964335817205381807920739938", + "7692941991237742474520327457310452870153482370889548010226143053981890424652", + "13595129445265049664221406027681079958478209116108739005508499004805469917071", + "19188096071580221579092496028987371780642557049389322053081699235155567772173", + "17975673380464001374676034638564230054429981676012676440863525293845130019904", + "20841685157342026757711329464299804445471940020955209397956987009823404283299", + "7510778644672212989684926383821874729073504800968951172295535413714975603558", + "5412964648109092367425127656145675316528154462488440576988541278054587052058", + "6998001450950528857399821530729656471745472711969582871968416561472553420135", + "10017795190513370580285083759517584035694996563220913850722002288744022757377", + "12113185651597474067026664715619946415749981707739597619454641751791169267554", + "20451540737363571466111039734160615184627155382583098695879349204357410296631", + "729116950403569953818905038668361626861855541652418271170712441039707291924", + "6874571610670154627346562968411422088198077609945741147515101915358108207688", + "20307824547105117373454598908217917152093200208838326389260620574762152675045", + "8758875530447210792904496135011086289851932865540018278850670496425499052683", + "13224694410602002105805224454797207933944742532123981533211431845662395381395", + "6621493224766717216701548708726891168784911176896760330321592836065310482866", + "13937858022779991611039558948054774910543950212969141252259896915615778617893", + "4917806030251482092362529677296731621677399228082641707762616055246746126061", + "16304922224312728276104330461175394847795848175925462853738047204383447573035", + "16678452722472429203861326329044632626530032631343862086351886162579978046420", + "9974691111613144697061424119079539196535411918411684404824080439336446439564", + "12391128852318795781829794456501239823062804741032268163807689059014957151322", + "16376931186038869228971542812469753097050036606517944132293138523631153279825", + "3057841358487505418761470758562979965285993261118087156094367416201750095404", + "15045409518037090814105826994439679855639635253710791541219370329682069820225", + "13442376736433669968016223589180307683361433436806777011753497283272674012644", + "18917174176736242961299708438032963296686220808211170958894252981698475343631", + "11380920704380401611525239094209208940853859054744619020167150893676619275400", + "5399632748693319676480270098239871368958944610827825094400876104909425716392", + "3072779406768337118240884091792704214322792415195488652476136252175179362880", + "8351873470285292321562674159922105545256148886389216816367528787141186556758", + "19039526722628732399365091326361517675801947890934047817293511021151913744591", + "11316453563295765895775061205389385485172841919365628835333993250531664655988", + "7850755275953939062184858524678116551304016605992491147837939252676680785208", + "189663666172994057560830062107872734380479327839628938168402275701561917176", + "8944554955574110171273295960753608410178793391130829960067372967633462961614", + "7116498249918759493875054905542634690892118438594298685578805598675410965669", + "2535963611074434631003149876163530430931993688129878286594756194015465278460", + "18022460558081751594574692271414706303627866472796139479944146908393139741182", + "15341193598946540230880135952221211503846552166425406354080863978843527894671", + "2942431717153385426545606490874257811230086292797817271859433296359160259239", + "3009774438756820489964746831334449123894740822794580986556997529296717581423", + "9496138301121689616049759054935646143502980987880350156990306735995260671175", + "4076156724842725224174300000468119057699244699381290980710548119313376968129", + "20301500572584246879220468905731058339249778940966192891128325027181404226629", + "12240449395531309263037726882974869058539543342019721791945417590157321444565", + "2734576041547526732946886809654954568832411068107541730145912482251139322538", + "1913611111144137178181099357504813610426696502807761974432419767623037547574", + "8323981703091520786969788588517080546120036429535328021157459160571413370125", + "17608089795804665912003122420873117027406690592641558991713120617999818930151", + "17954961401611739290579723858653246962839079599354059880628870682426849304674", + "7693642591048722104105715300765742636898670019493041402551952316778508785882", + "10925165536949195683545612102300879902373347522535838874708839717193999335745", + "16740598974035404805544189925980303793846400946043080633235004418045311113846", + "3028458114292500648266975052798389647613432243149006395166123161184170940972", + "2817600861932061603203157785548222970685465773360278995551965365313604217882", + "2811366666795973435332404603090484498270752802044239619104866535127344245139", + "6901007103297959557257110184636027233977945890205420866896244199105220459744", + "6811040256124961160848956238308470640308462502755753004833080999365205628787", + "846642049586630199735666112786431409696508103735494916428842550432654381594", + "13061166881718302681365231291832588791959186056326831853549555763101859584396", + "1581547457654855644173875819143310956457964952802128135344084991507959176621", + "12591698412731075291488515328885878994038884715020576113812619060374399968487", + "7129047166046749599109058206849766841261983329246180789653876287940952140294", + "17780920041966559015242418384239510699940753783778307759603993814380170147815", + "11411967002648206460094819913767451172535988461576286592244752756526683869398", + "6535147980143805768211908880661065989475773196469834562468932004056012068981", + "12872366293792794368642323198969017581196463071340612957009439105182673573396", + "3845096876544992085668616039795853840768469571100517631039776002796484609549", + "20386025860348257305841141103130861239832870083066852913792413739711579490278", + "5663975388273723452136125938377376330824298621841190787892884430812699456136", + "20880523335705106555101009571713688438858731841737802690910851430800496104934", + "8664815262171336902475127109386834836220742848950659183106085559300961747316", + "15212672296023611959246835252860546019670000046804751249547303425954183847429", + "3786255974807528210793957400325837912933369979823637013145025357556219775102", + "19646410587152058982763388053845872310164493339475512721275474101828150077273", + "14407426259630290801648546162995549804322572985407158009259933675410180400077", + "1275955073103101917295562169849127375209112030395179332033340866715396722452", + "5487750760448101899937260261898752719887276580825994742322208269609306618405", + "12414079753210256499611439235670285717945909010061941159696368398137523291140", + "18058271753030912252347026705895506604519018890772902865355002646910918153759", + "13935235821735626611156505080089322797654275868806802361406549798199236177528", + "17110498079878546324718511787669387410942622969712445909354000807236690314957", + "10687508266469903792000405420136150569946636272800228999781195239976105560612", + "1277956894120355360649091990517188151791867400124079104247693321263057601099", + "929982009519538400155920125117423265869657236620766216139182914925009802954", + "16559970949358997473575123467518158994842000800881347427572300986319432656507", + "863852544580033885106607226598354103099120172650200980695458006092725115354", + "436810575313416269983882563851323926836428928449351162094565391723605483516", + "6334913013691170767138698286357556285297887475783792365865857018173994149486", + "17785859069146472999908840832788077051672090890508101583397157534162626183973", + "427206014337914391283601765560115825767253196347193816620589108299037926541", + "15115704735938262072587983952645382098893412471333885175144579020987265065203", + "12017969315449748476118643575203596675122272214009056004034938899095907760206", + "20642434407226804845623813766397536183962927868804716012482833199686414302852", + "18982318327848493301474677819747807686491978396022748137991684529478469330097", + "2306193794828709014215315860179466106408084703631347012188232489780230095671", + "7060813397820173935956757571314686808083877731722252822508055423697679476893", + "9925864312610988474999359617458205534034473691089101964213562993662824159034", + "14036238569106986370932971272638702550236692459418895654245682921654874601312", + "9509048813859143088347263336607686057099400727479311504780670742158653486206", + "6842166521132564137619008158396211111980991013087076743268157882198576269675", + "10217353423046013950417213172971567565900229914457220187215408404202554351836", + "18220384419265532097596052952017594673237799959023133602933674050572298730193", + "17866822945198657177461453619458294532377313634196332518543246556611008452933", + "17694368679979949511817467967015330546905282492241200905890171992458134240678", + "18971922685739566979638356009544944454629162680819328093994329160719843056737", + "18684937612086669383439812199377945074448160740155966772829350355651237261795", + "9235876281667970051504588287667786944160228843888838710239865727309603061015", + "6187574163551283282357553100017400574873868151705871779659681332774938473442", + "17196369096305464930639002419417036905613312721767481044644254878990952814786", + "18296927216321111202881056198300973553112302777685079899199090840516364581791", + "4983948188027170589078739023086929105628955321978589464920358286161528573448", + "2276814237931645487686771259585160667452008745791625290365802841496721618760", + "4138273157833414032755498052453436990872835066620446328921138739885868998379", + "5835580830979414828575054128735121537583042482361311845838347096674448689116", + "20992630219061340843601881100837482710979119542034786928296223633950908472388", + "1118381353525339785976839119511758587763620520383755136959051018516094253090", + "10337002023922138844951367775712178432524190386722995225923120494344904079950", + "9765947418137225404722546740514250763898752374389411503005283184253024586058", + "15411836962046751164622748177831913963909013265942110958658714173394711125370", + "20722527012138131360820192152290968950993396481440050289358737370268218859591", + "16585853587281811014582898583977502965045639444130273779047322749735299560207", + "21436098743421172924014781240823435281025352300035264733201366114473419058727", + "14178112462860881459540462916598447735177675761773338824394753907217898488960", + "2590560710846804342662010467713568407285290476715663333366063002353018991264", + "17949223181156469858379065899254284317305309247290121304422294912030586532673", + "6940063127036366626640075420306454154706369567406835284901717013872681276911", + "13212339415583029091219180722363760875223983190396769244985733901171214077679", + "11143838426689049623360248250302972103117784521940658207527698432687552942591", + "4994693363062895106345077091869420711664571716019971952890352464184561249569", + "7785839099197795033948112451740381108555553042322704038905686323540025631473", + "15291655295654923849266753004503491258117644584862711291502217292211074445996", + "18223946690101945712849081159295298164630378278313069852577349403051751559726", + "13247893325056509281811135293440873471348664328435966021736203439379360560346", + "1838627965154116499570588511051176331708387980121591719463695143475045130831", + "21746931323535899361372833028120884537569529325326959379977185108159655128847", + "1569229799996373000993208676467175871896208509249271061977636872731081653113", + "18668959729045139805375896352501526759923123936419773886979446262254907152787", + "12698285530824454564359053510831159718450594302921296519937334733529589738160", + "5743752602883180080321224936560739109224279187008023590149271256478879997507", + "17615461436426765950762679333452659818080751337498512367037395397687644820677", + "4379963027402443949761342437016192165148025657715626365315450970388283739261", + "12622442863880120105122485141053297017921305018805552070109568547893924027508", + "16493349884995741255319414030015325273883108492981717376626952633010860098410", + "11501183900713163689133184470477728399861217340901493951105967658399341986313", + "13184464903575565740074003127437693743650101614906307232173855163739473476900", + "19056993236227362680720448341933549082689888775458266843506880469982452347227", + "1180947252747369471066257076205537751320494098262241412291924855089764608729", + "16229532924404554580195616835338949126663348103713418556119694233568376894947", + "8604714607572995451336310555882946070542334844212691610961393592348706930493", + "8362594100280133221998296898045505539071433915735634439526614339277300552370", + "16399159148365956463951582514857891684943332179297226423628752792536028483990", + "20791958918883897879651946680726738927333774947616022833294686415482396438838", + "6976099533465307077876553477341301102578695004868981952387720840685240842560", + "17588607896443047770053818219711270035985826074286753981361920802895326076124", + "12865981806811655044812914486873432317316688987331760480657262748139002813688", + "19080259696546964979932036247707282742365340353585423017939782931928015046575", + "5475353703257038456872747308072401784844227202792527428899399083236860900298", + "699444932025038530835460727165156424336147795146205258896894678525124927461", + "15695622674480818777943366659102932349783785381339274197766151422625765388038", + "7644428489984569999599080644830401450294253782967784792584750934960812468382", + "2484044190398385977417569061356693291812041338880061938702052957819048506706", + "8456986467797277421685766156179980502998860530369856189405630837033584471075", + "5054041625001826317568038929780665383894838531896986763764007995985738029810", + "5197336058480822437408118036219119090707158130910220019747427914262297331861", + "8896147437242770809876821567936215621570430903276974181159659855796295866923", + "20755757167342693300106178757642141909843395817794855978028122598254488316281", + "12495257799325917448205113238508489684392516282807104246531380538192500498286", + "17639970982424592615983334078785592256655637539816187733799215839326807071148", + "8140016957188286078776165555436655378303814378750387793587919949009492167586", + "17209468066776420206923060639618147772644663380208004030591040036263548572020", + "2619409586309117922582791327977378099828554504012201484641253637770276078843", + "11172679254412598275301264634812740710430873755458899712228629497147611473029", + "16829502099778629987235691213955928527920624415791356237580609633148661633897", + "592799060717298365629187138482067858694007427100574367745567028165989185342", + "16864381084532235865281462338072964457337415344658720676113860956416999505572", + "1015589663070446561434523645329239389344944669662180065723984179503017360337", + "9982212112174542265411457778485410853904388759147308861218634697975431894510", + "5412525702631618381358272227447367851318305617863423359948039591381065713581", + "9852930575259000100332996271562617389630146990442517175422889296173516799181", + "6036993105785310658467845672504384047591296265363803946714632979523201713762", + "1821500632172143873156399122734194851200445368324858351038486833883177057468", + "21556520116213603298246786137688925835788594639953568860110645708136881336676", + "658318860971707056155247027603536846915894897192791739866840963356575472681", + "602842622617647573132938965729563329852165494525296971607175031334298950242", + "1151063223719891516862415316972915766442753873652837551132768558136109394634", + "20030054542089253165409106868864476953251573918915762537158006593968012247497", + "14455078111822464502989472874268580626098857184523941794725425258923962713053", + "1699191450188970110166570608380346465689006650580298122024202987580198200132", + "13971136504849280501801880342723497383580392506287195375689019810750613223527", + "11259011415071078991947983706483998982146186263873384729739331890304233635860", + "17741270384736018529047001790810396141344433078911295725171243367964019815741", + "3617456068852846022110280599700245470402025130645759911795429861830057016581", + "18773989857774369564707484486703863617112883499664601804221477949481034222590", + "391101570414854801618801587626783162239406618115954162053108159404294160435", + "3752824438659815340558915518196975380567589032517034180452547083690665271869", + "13652227089592801810376789544861979384538590096633526007583054323554301421745", + "5753030785259259818058977992956569985665739253964735992489420513570911607", + "12794765444364718066463627091127875266371595037234762762560519184694440318642", + "1844165267423966444579133456200541636533189889959706801468771335509321515822", + "799352162562582415493264759184613437140226428304061991778193411771388762097", + "15915114786946818157476898276501926276831197920612814619300062353559927906953", + "13041871949144831370743756131359537126101784549008553888408794912277392285626", + "1684702427149441531010110315726002248751792272226034774456204740385384491604", + "10195318610969070608511028432066597876456281143783329459466964443360549551082", + "13714193389971576085579160116206487363436474313560046541969781285568217247624", + "12202470771012770210445954644081270058473831351768121852596394422757629850892", + "7784616613742667796197638965440313242748565680231200921682296807888993222090", + "18581613859576442652033888735999982405110741068271804741467526764394720805037", + "14828223806255884089537896775456938290494683211666564494946175120085694803958", + "6191868112332934762674478056112840408041237177775248347690069948259811627101", + "6055199518589075551800066499277675747934144570099354689629636497613775458486", + "20043219892592698889412649805669712950039510114250762278667968995416842502234", + "10591576812697540586115991527347511638405122244793393962099090930538459086772", + "8146910292072979142616688207315340017602882692938548874592904341871514175303", + "15451576003386544225828312996072681331940167554848966592330715947662789205180", + "21156998090948310800651324456525534600543417534335507361948830316109451323115", + "21421497039083336739241851024868234958744697872115637345287618993148799764131", + "8835309990713613011240324096693076755485475658999871502819747407829989219746", + "13102158958973358955423565573049580406238531533936309830903999596178966162490", + "19927703189662863743499379923522860979653455328626544661291243971618992342837", + "18417771183154820005238210056528713167003520086953806649233005148247829186154", + "13242250186667974182640987653516460478853973058739850129463954545512907574522", + "10971901023853281329361069638276077765206234747340067637718378767976633645829", + "20436550472837870181409690438226695091760115955076127106091878852797639823191", + "683842651763399941903331243661454687566310039977770092715404267515366625429", + "3304534668380354910105587611199035768704466410761708200478786163367382500984", + "14327892159763789670354328059011011973128878640806462164819794130243254129821", + "13712101990593648405837473744314130986494510088132644940425089514662460031793", + "1270386163717136732049662990020454155453019401464056820650142849751291739739", + "4559668312052315567004252521434018809625818725552950834596073025095274632653", + "10289456013947128246221059115194021747046925564818529566042034047888244657473", + "6981981682422059144716871555026845840161063380660424650450978975416029699739", + "13275723002453843398308458799872954358948259042779675411059905047590837397361", + "18372074965684100000331046096891533070433189717560527825752357282553296305210", + "6007153627662867365254986874716350833679184737288669421698890656788831322929", + "11557682792813633323168221751485510314542594132819842305598531070629168100143", + "10536598621155464430657941977974614272794233321865085717974545329727298277125", + "20566123440884795144385782557360498238445700080133152934423121801124172346047", + "5484210585392274768700243869223282957415576141086566136019633416151230114084", + "4675266041161206862174450141632759296562489084453522360678052892725376421684", + "14506966485061491552710372008504993235111668026216492386033611735228479487468", + "3682565950309631924420685101131217452257499881999322497664342243267291843503", + "16753306733039910894513530708776251948831720207834805689601646616427039909037", + "11892397629144764406188085785897237236955294380381710017192179450763501663923", + "17027229171478232498721421673139332166581061755210509139252013418924500461243", + "3560458480908782960366816146149753544371185355186140843210760460011482921556", + "2523290942811919827064721825289040221770310594770466909167316010377190569820", + "17586848354290518015476851435178627882600199642491204839902589087637701736514", + "18771893348474501482962831973790983143756587183687952333177929270650139940171", + "6788202157749582404834375771398928959748074435244246320016871403739257327326", + "11025631863450004428764861086496374449453982180198151399523240056816657483248", + "3256907622263919521402687344729539839835290137654795380148237049547054026004", + "729757374802086603625382264910105909740146180896096383332210024077887641124", + "19863253866253150070643618896444516678169346690564661550005769233120838139485", + "12468569017378925985548033310919519222810416238732327538088208928920140959143", + "712344748962578398623451251358410865586764243720605242158768608887082462846", + "8546087066371010720013920767653366050032317738437010080974697619001241722483", + "17144825509786899110344839698077839239721239583625175190269757913667929043953", + "10651563297701188942358589203989937961905153035428112097802788565849122022100", + "19602341346389413323180922571631527509531683866957468565049297030414658843948", + "9238186664745057178430953403953596421917515090260446457039212350976296818523", + "263640414028390180122517954487976369901122460517389747631764885875587715955", + "2311641918305077640172935641310996393584851078677397516017312506521775283636", + "12911852110192471656473443086611566556755106535388637084532737811151296554463", + "10436700004928765835031725654432267178079115705246966695358470216435798181674", + "12755555289896266917759922247555708737024386059041699214870911784508162783525", + "17390583422165077903045260639521919716984664232208360646931078032292219709718", + "7412526952366864882775200227476857681850213243362827192310877977391550357930", + "5016060582872027330190350728607317487069057897723717249157495640519710863591", + "70447200134990075406173842139872041532268968648265338736409860251327029352", + "1545500244158153586647380894391367444874762740407966854865957002078767363820", + "2082567114283705201161441383508830647153064041365131752708347264051557391980", + "7773933577113494097575644205473257493685202208592412633139277067190461074505", + "15907352821797623044340355088248954282080052141018731890243639338361458586983", + "2453390435048874114321626738320866552399505338711520013030652128583351121221", + "9182038581165182763924458518550360578443802241218652973210280653624820005202", + "13176557622325900598244222336641110473108400343854387783748570353220729582767", + "10599983241136666078578113335543683963633036808782400964809769571709020578918", + "1430816790456574892099931300141571059151141389317227589818258647628212654923", + "7207251746626434553568433426934231676780727971853793874008147862305418016123", + "3847365229378532841231862621068765430417579646617713430532944299440264931969", + "922422158589085666348657924088867593873646110588554410818179794404300446471", + "4298485174770134050325487753075508760849575591910135387686931072102416450479", + "9475141350581193757416877790061277619494551108434152557051757495614692231364", + "7750163624390542388958191386016094472536166330496081849246099823270737686866", + "14363173695671306304956071467171940429435853698217676411185837490356013810171", + "3402134714494071567155197273072160417049647120230862441840621369782667867977", + "11378968132153772980874973211734670604659991740586197794619174704886870525408", + "2500862781199005154907185089778932765489906994365960644306361544820582839768", + "21880931942133046355810983155922578513531850539420426025723154879488808270315", + "17850206894189265929807971665186479441938275634968267590809377452033564010382", + "18427883853363251276513100116480886898434829323430684895879968439179171503760", + "18758795974827407022563870795763356401215175366078230621502388363785425038612", + "15672649260544536516531393740985073476934112035694203841471047634286525005174", + "14497479780124030172334631091033639981498927489925809517218125709975200816290", + "11190855071574099336548308963044121660452976926988171712775481672446931541539", + "8339442292395337481335048552147626044800877206694030770577319544121541364092", + "2461178629683239975488518502624530284391365519847067341739449204945212652770", + "3972313936510404965199308344697399140590038866586718833591813109326652018667", + "3224811019580618549699828950033477378112059204060062023677479068506440937528", + "18443657715765406615721041820828109800966587434816919981514222787674698772960", + "666201271764511484388505793135876064418452477237751508215203932379618265382", + "4434899717815685275523711262432486808621984251515429736982413712108987655422", + "14584918585762085382434085071460369807803840154636220934254933165793423091295", + "15646480282455307022430957975574008173154630787861430193406352480280577045711", + "402840791633175231660910669665966910050981784044822648466848382615330599909", + "15437492296189220094817534101128968523410729375545135146260659057729649968314", + "13987760171743052442513877961667805977500573882586118554487715622045738218279", + "12589095501858681021442730872878907609617459069328956803139727387371467358051", + "17551064250089164193025672794811675406761638177060737129533175904585851772273", + "13500706213131978087516005477128059726177752268287240395927379509000435850498", + "7331629294073516250840302816971095420668983701195024195892939287001016568514", + "12949377725980318589136021850295478499564248427839661600142796482665024587971", + "3988955063770305621858590171391799353484164878730082586815877210936858093890", + "20512156157023978986265779260320491356890557397261515752540394821171756173724", + "11624190532749034673782735319581023504009231230729490439584417709012081446066", + "12473562150323140802035699452896239306300376623759190078147999182702752528013", + "21504777935543484323252258287484534200045631968996932563017737909760083499017", + "16104745906544338230790783632377375683831341202924378150021598903321494336736", + "8312554144734150053969625169851557776466370096299754626528722906617398229171", + ], + vec![ + "14715728137766105031387583973733149375806784983272780095398485311648630967927", + "12450793357728630597819493697261391961392738728208603858426218806728799382497", + "4427733724068610336929510244982091587998132283636864368924406075658439074153", + "17863554236640577761956319447874252524561947852685470820159498661269344021716", + "10723868775598272126873918500257797117892409794706524915527428530195343520361", + "8041366806917098496431513544630989490693774700064656765914266570204855843526", + "13046986480231887538692223126751085950758763070227069247275787663666591811005", + "20228999562936372999611354929112125019466353738760451044697249912024766542482", + "14238976012080913074226552202264063302466135977295108038770514743089287570221", + "19486717852389551661121716850619781027370627632295683938875312739716376501717", + "15733057748709959668511822511174594221965585899587926036013893958610587491491", + "12041333229715539748857491855115983195198694619439452683631630426350435252478", + "1829888811413627407640409778757789140470123549237476514374669162490680512211", + "10288898018349095056494632386514957183841700001184195479721999387950102580094", + "7360553146019695788111059047354435502690072975650576744373916804385350955674", + "17476063720528136669048514677420727796180556343667231122803521620226101935369", + "18384724266969916899691009636435516722111206340289089258767862754828208946542", + "11046121967047431151707881264774621308937270618998625466342467829704953599782", + "20018232138773775379089542131722766973741687507582662224374276186775807685863", + "7926534193496947015875888176706209291021745851605316909116853588598743879034", + "8826996877877607049084007876351017199517432230182001641783930871320527792100", + "11760708819943554023765145606995747732169597984739408998714117029765838566505", + "19598000655770319703844060561747179253151181702222064644764822676806532882514", + "15036675263180992517064890091049355832990063162957265821390555448206776251789", + "1053420874580688637503969479036991299021138740018858993455108201424412879748", + "3723543690610038931361367959096800720510056325209292666118208798533818425035", + "4599370243050726453512484851927735252841106375733105184316191846221056036380", + "18291400382386598447603657416871816375751118990979359745849342284893280004873", + "300341627009231088404894405580745838091318300821994947846008201887884150151", + "13332605655619720841053062902143052543375741442250678582318225211621890248982", + "13197729598850829723360679245789196039442968018972826673455394330035263151299", + "510788688496484172389408566109007465667555285205327059265048317979249570221", + "1685584118031999835794907889275254096486823415278284757369286336252006457602", + "15103945090904102223538479231258677032197950627619049222966748226967974852043", + "6653802896618953033344296077900828173967467309849915708475948018848254380036", + "9254803560511166426410537422101769642611302194250107918342410310963831784950", + "17006557344160230194691541621666219420787918477303225545533644141096551358258", + "773112329554511160545400721342977593377624843987783062638455005748446223137", + "6671483881284330250685026918783029584764740571210869197688044338476895092050", + "20812941492969561606721983530907505914064782270990490150214736286311482532652", + "1156984923268097592347582093730300227184163551449762803735684309575717323017", + "15303159756724065068145651405407765401796657934219121639364061501460295743948", + "18999785075801878445291021498876384414176522501978873700451842582224940767334", + "3782716983967799050957535371991538595453996691838733068933109780481907925378", + "810443910646366078824923626573819081371243815242873044781414798707744583851", + "3940687718063184864573934886068875138239553970085689518511531571139105765743", + "1222092197964451545227395363538155091563596468425395922702697716100572937718", + "11901775018663948557424314950737290815973735008800495766054692238446226616230", + "21839369981774608005059280910009281502958794510307248992429390932011110951241", + "819873152679629471918450179717035855395702808145570990556719950289951175212", + "2918016794043041559376798791171848118057043459636680115122516324180788251680", + "10788401265856066217998495397128704450484607734353922353470809976686155443188", + "13599498756047543641157208425687419183141596017402196474108059160235795892976", + "4993390793677030007023804867617329393931635615810976661139461248253851471412", + "973050533401342110180605419751137563184725082821038770229241448201970125921", + "14313276246574487682858906899808269544140218917497205965354285099641091349756", + "18746777136177241043722556179260854313319807637092383577312657349740719965076", + "14517023428366357570216698819722831600577825429761151189605029742824536459972", + "20223198094330596704408798588338060788093323967112845691364940702136543962642", + "2924401185705980722600796492514644487545258803954418619331883216838542308543", + "485440919681570468713530641755278841324413691217763990572458853294843435089", + "21560476826107225363638525612645382878298890750874072774141701406519608285783", + "7856508582404120415593106596945280577031904101959961641860467517902309769386", + "1505151890969527772884247006998953879441745452105187039442954300997320053301", + "18861812597641777105968621029392243993700881183944538936666186678355756609806", + "11964609307983840306843122014689504510236749206766494519381451521217569407396", + "17764783391855759749651949748230026302359698415337858912932633638930034077791", + "16562247632438820849068750036602367255890087581186727955070681252413797347277", + "3341595358840888933968836940161983842834749603437573997372892853189756769506", + "3198140245778498430686233550970322127895441994253754893043542706415030678798", + "9829840339700031668849847901844029075426216057792062644639239580989060312114", + "5999422607425238131817993672620301343082348300090537110946144186609066413585", + "19901271533560906428202710740924807375620638454776660078183104891177283526156", + "16697165654181109350158134734382046723004976300078845885330478879604895897280", + "19171906568090360833249366643372143476587242793789646446664643684138123124668", + "5557557332632668793539639636185643553639926364115539987556075445308999628265", + "4797522865199880517123583692586561796505378758857130153602827907909887751116", + "15409514194242892627651944305634286919424076146534027188938906487506413405089", + "10407013998132974348561594118793213466618426284969698091916131778477581263008", + "2534925381155806875978186916525958864791165037467997034976228683909613017312", + "16140842893634434452708565053572928560639256480905937421023970743339301598617", + "7517617592925372620130293329989654305076737363747701594349097857054039164182", + "17572708764253481596340159581412737527195601517063980704204677005617144607526", + "16697796470163537491131716229045730242536059781538196375577575057386248458494", + "38275164685285960308550480834951641755153240877853193094138358285155638204", + "19780228589871041196871406056718374983456578990309085234484187723923738516508", + "4573417308961077301452769955811063226515352449986725327722241421281202736681", + "4768055042642730073498433238804346134649067788593835428664493008393684000706", + "17566912618951175959416490797476610679702184562687840273697859062459883449046", + "11477598695424707935165112148975667441147635429812599883095916948275334113413", + "3408907078049921938725945268376819484694115736385272440041090673225197146180", + "2488590561390551829094067182419871806900177001183027832070626654223650976899", + "12116557895894464059885135778994901345424716569754903115015740397131803733982", + "15881232965640921626180413777392630630338847181632662075996983398726326426432", + "20914323757596181391651855665547258251038466184617935369425714249299063760685", + "4275923143992397246911855313401177253209967573031785993454148836244404305934", + "13098973753894185378061607442839048669135765294488505596582737281481575045554", + "7995472162206735324879506324600884378126850726543803581430135236761716527753", + "3690915804478314734124615543749602171459078573370790663994412906012450478823", + "1256453655839486811750227055618146120819862944082463957526146264573763714294", + "4406492967670422538631080907830590263463047897583684262207883537903678091970", + "13380843970691717863215678292643800288491103227905602355694129412234174194363", + "19680159398793220289979983679401118779763854719759576408245027038965290325739", + "8515713472495355510508289305321355004480161123461789103991491891201940557902", + "18392703846804297332972535728243845000077361414687818948278976164182674947067", + "19823604647876421559318429394175186838817554072847524297827763377975574273192", + "17719715026846703054856559310322577442906188886145763860157972477138788247667", + "8745282777320550983079435446349157218001552450433897097227622172209480270781", + "3259368608255603766247016957318442624095407655100612967940789373312058996520", + "3379679235619387594255002628664818227413294377266729211815713998759100259668", + "10282673789366804521601844018863748004632586596870138135887183100195194767004", + "8431227731426467642712572981755086675999345721043460063547234289139267810255", + "14117058124827023634266519281629142766485227596060997608233088670325722698559", + "17113232771025226173986361792697170950811880770802373827827162227101499645884", + "9906220434844104062978204733717072107397540599291396561476275675218575564970", + "711369587296778404961826907371863989722457674941832862265420496583620086218", + "10995654568685707735109869974152491589223292425449581061000447170660561828729", + "17197923097868441003908860864777521604587651639410061820516916970875615238246", + "3121715947184842829391029463556305441693293825061846129844634146823663627601", + "8817835750782344079827519863863370969960597321588294656839911940551490704717", + "21074199894730915603594812797833479514843396752652846676596119472522115586998", + "8903588044620722375103549330291845285230849782400990458525441823641905996819", + "7157451412319473873395155428325762769952294079544485671397508107346256362850", + "5366933733103001902997281886950280717532636892191522349820059149392915169558", + "3729196254269053915687004590799382892429870424157270200083981101426772909827", + "3918096703119862723362353838062260616080657756068272173354821697584630247209", + "11073027330528765229119199873305594827907404967404841004751556462671634016839", + "16424651511178205757967439516888026957937418127900739730326874335888617161971", + "17036562818332519536292487256920458988625450115083747105277938048739292827058", + "795554890382567685751618566957270321871701261784565632343709559354970377145", + "633072079840093073847779349151531317793918731920375040247534587265858418734", + "19421194221177975514787747427021411300539454454371387008642591623632727982196", + "9954719107136377193496025917640974425520732567100168938432529522254697824571", + "8674312532180246290069249621352567303340886011365637785384772665860996736758", + "14809129550856657213168714888239735820810817787153747648450536960647330811703", + "18479959092813678391370975524549834571584338614798320263799188362327888537937", + "11754080849414921164216607793483937490683185256818320971638570891360029327056", + "10287736699385961112844233987245832756528102056561178731804188514133469579013", + "14370616700332892416887680617217669883953806003377620695037833373409292189021", + "12131262377053219810698216976753909777223459611599034218924662817794274728701", + "15129974113281645648506209149692470898425572316691306513209191313993708898437", + "7871644959999350003348485402403894487663479920989578076708137744830000430296", + "1576915733292398470896862707357585951921545131195468346129170132189223165938", + "13316238922195025030929715018519212370128739646325014577776776032463179349855", + "15160020868051885495078648274966503057453505806774983308629511566464684311627", + "1692269682153339201433258246771340974628904846837119864247013056373782718416", + "19628837155426033423644376042848583705054394443378101622337255362403724735047", + "19222966046507618124793516210121558272031295169005274768240595331459420997142", + "12990748614547458190976906297393525840623470679364771518133250166378979874463", + "10124996030376091099517250678153357142212975502206884325977282211158514276950", + "17630673366223237394418802287655202715156124721482801416980858260564381593966", + "6743037447395702022066513290929048145404894812633440602191382691018136524423", + "3910195434942407507599129230554588207801501224467133349280934483448828467487", + "2025953242925331197360540874793022332074847486979998082380244277507702608951", + "17290925253475198968609624243667228472127383792887388480830073536530705682760", + "15557314422719360545874148111856256188428921052029295715627017447052250706766", + "19758557148246918190283097589287660972538989627091387035573386136809005998935", + "10859351185398338650386876904094285059182038967427299340069909694684844129362", + "3496018793417449121342556434800740598384008787187762642325224753304909741349", + "13695501250971489187692201493870442254612771332042272465953359508617675704938", + "13572242195808512474816152630443442412961099907068902213470234329372028271256", + "6257061132956659095252686302119011010885219692712894010340612889095488866530", + "4330599809632843338876238530496396340118064854909940219910748808728579051913", + "9157987606978264109338780586425009211347479724574125407732261019832259951031", + "2328698634372378957406958821467382289342903425118775270878244960387352862845", + "20636525922386221727012980541907198653039323429055563362662406273278160984146", + "15847894355448175995216566821171916679432807087340467956339517156584053817157", + "1942360378421747943668019094002571732886982847410366696537432314848905467679", + "9512432294361739988724195228775769058251373607278744642461344881575127503031", + "7373765909536890992660842391636719615263272667672747352621337161184389163446", + "16805165862480928364732162070809175154629112007405963636466097184868514458659", + "7667777941325858499291332847392489530780564386762784335358233711706517931292", + "6446208647487337326336908745536052288215677968074882840304817109073334759485", + "11285516171986135785540153632137541881991922296507010937224736080386568662797", + "10115214387228124714106659470937696440920497755599449040012569123044717722706", + "15485618097017003479590081826451772255273462073640651108645768569284210541135", + "14933383877101576453093795963534828854771957327481830015228527838452944594646", + "12699366929120600543724208703956381057734625711467645612998923493410472579972", + "12636366946456086231704939526732303791619337704833963854669708252203542584210", + "12149350767700952579168066320091211427411187251056390220529300991824437924228", + "7521252564104984899409328139379375498829232271563704354107116269254046402507", + "12033991121152464927378622393121300999333393690763174606686511857615848602007", + "17232776948709347607296344257668859070263618035653710252910881198999758003380", + "8692908682458431891302516268928916165669902656866484222966303081483718910104", + "1253076047322637463481069610081050841277544153675308425513468857300598987482", + "17753389824587331559955818909257943804816005297310986968447179587639048799696", + "5220269242560242526244582743085713945173060875457087963936380952653150665967", + "17126848126303954156127690428371193690154903947228604938919561454676410821149", + "16844245036721981603144243350071451732279678956963696493069130132912694448751", + "16797761350119564409426534689125994845767740388070744929816576998448097719798", + "19353620610135120026060560134469588460709151673182029068633909633596535108020", + "19135326024992044270104645311242450367403619348108625528873986701416220617679", + "17665816362466043406415418194780245586053150534372814020191541209753248047067", + "11399583108978058354832763133747562621839059603612742599115200702193127837394", + "10094334549114303273265943473013412623520307578724043117639269488721170750917", + "21601458494506173036246860827162868889968956934810679234022762622742359366252", + "6386580477827919478878489737663301647954047211008970416851133263802072756591", + "4792043837032853062947152822210390150724912812294333339974827814683543135564", + "20876886123310865680023706563792643033695666593071136348323857270657128199374", + "5931154799422838405687052216230902279350178420072288819326391251206607447359", + "5239679324690579237822809044372316561806419523557737441242604861240795339076", + "10385003741667422202343482240152986976068622687279646189490976516013598227432", + "8464156248644168452015929033942509092145250244998026718035923409819766539834", + "13177537753162628205208392995644675716264814191265988042404781479197639366733", + "5919477377826036950488668794024141041792143979412430063956231337921980979482", + "1351402666854456730370541080745509803482004768817122599092881844387000676155", + "6818673776641149273361875347660949176445649468306471072411086367313332518455", + "1366646945884507587781123424154966453464902291438811059924651777083838835678", + "16219293249111347900064666257423013936256436002819357345030961998874555359000", + "779230149490072246312543789505064727370429119089791148581854356816464370377", + "18480337167389263493513952937037301086055810692872257722500635290543939189393", + "1345414110418158215433956620396568245327910182467730711109133441878095212920", + "12518315654451653143886317929532883727219058399486775127781649065277400104111", + "19716171362713656659833259243590727588692449255201500490000859973307782246016", + "1865072487559894165339723956247507020827160163812334855490266264867949416605", + "8915174456326318257703177400411158958853446829269268103252573093652570933472", + "20191934956657253997484040571514242713447218897800997897558899754776252309230", + "3900170788760364547006546697350123842323924137566872497612605525517074710000", + "2242244954905694264442292936230335662862827521454977184433268725352453968501", + "17212753633823250440920113486091598217346743686574392123683302470302281044057", + "11939276774333100126191320505078174289237596631307779156488772314461752488631", + "361355126674011999247836373885105218009746852422112563922207274436194144681", + "3861054771271956681986534133247127581996350841974597302976225613765246291116", + "19968479093411941747037123171825881488638273087679549521610505739311299462846", + "8537196135596544183619390135426012949552627827993128615534814021127294540392", + "2438879838432432949185118142364194193697006515067980632650379470739663214843", + "10769366200854175394348657213265947929465261545591304593688343101111720627317", + "8455019976119342575889554308499186802278388693477937667704910645050957262689", + "20644389417984700539779514908032253651696357386572813102276555909201716748299", + "8820039786383750409041489202684137325382534899692778928304664068322226640076", + "8636461459675525672530300171201543901107046823820677414340465229975162161919", + "9061524648737340075438868917468774023866583922769991567001812766008277156749", + "7602969742956570438827438826124187210014769304752116695796494779120606534919", + "17880480383024583813657184645997268710007005482705400161841684734099773182094", + "12468433127385453618607022105559942067759302463679348320088817783890080634670", + "5227335513133160328788197758812517500875193491652227971114102085123079105787", + "6151293357148965084809035339276030775032864902311425722089088413878852880603", + "13699219811250783019541356007733829713463891996344484242492968708316395244276", + "20523944015644472920486129305620987253227711059638489683670518491277805771642", + "10421521516830672217871475174620176828341870738569247402138774913961149048583", + "15243709334491280025949017219424981672670169674700467979049999809115231651422", + "15516151337135073170256217447458198066207320794936363948307836943072374966170", + "17337341094266438501679457986886656365327787301649468585664115813920643670255", + "13262611487153423909813660830277859169133522588408913308784951544213550636850", + "18531665394082016871726276363920851282983017715104457591860421181826617619235", + "3700454591945927209171569025131477008196191968736477330379417168348613474972", + "3604972001659087732761769946443190920343158947813896848729866695375607825911", + "20952949990925307134028293094501736726689724950451065635729323134614933963162", + "9405357171465854081502883779215538022417071330241830295392540662303830897477", + "21638057691528924765719568024989208898293733581278465977164525893773900371884", + "1423261214711655336057796638966786076518765517452404205191550645234914655224", + "4051452662373209612509106830833400151748328181316060758960838588997502328136", + "18894191275634392250799133342573131067016712303481664374003128715704286175519", + "8319722910647187566775047002603641370685637216565762886509056643924765393708", + "18376807271218398458453428415456722166053637869198381036620575958015471551748", + "12035584964270041086110602893321059914382792217135345721427943800456312398294", + "12648928151571890511419082198798501903838843998709266232987169892491925610349", + "21412038262513052722667255278175073999553643537758589877888129674442282140610", + "7706735190856341161262212613554225730619876208755452623628315796884166016734", + "10999966015370832078836488333389544875338251739488999274500058322944383211399", + "4088296406085952300442596245852961024918851819760395990644634222875937267642", + "19399822412575078284884340953745677500886533272999950579143260384703504507006", + "3008499431966541245607724530938385192395211534821775780577277325698653345072", + "21447244586691806434401916456546893987941039399147865009673973728056412619884", + "893624395222035047010673050230651164575948871010677581303166873938544655581", + "21402344785412208717452894839332459679574051179708007417742748857146495441368", + "9392712010553327328684355664342647815409597079361837524976044019430681532876", + "11566000613582826375650817776243972243778859250974226949316472392849073658674", + "12900046757905605731200852057204734685283283637014313056501123642345467590346", + "2147232762440136333246788660102778148879449441151868600321283583777116020664", + "16301766972982581403924204059742972933467455194833897714073756335881543890771", + "9546560122931098895129690583175071306095759562194496054583390881525378967396", + "3814097068175987733354103462855355721851435755267819873064912557751073632829", + "10704509016547426355599213335456446765914211024738080860797634337598031536580", + "11921271012710313311785310319425095342886561942032945429395596578758895308264", + "21265249694322068914280109016742517903125526413969519857556032179013285196924", + "7207578215754030787157150149235357460121567678249968060366462431427104673093", + "20820013978092841458072065536574129286011620075823185493370309064760526240362", + "16441600678335369077753559950421185577542163640313037056248177018465084864223", + "297097313501884278852369638329400055327872945847645211148627847628970916078", + "18298084629287541333205519012404334789930413367615524379442280529941257264699", + "15206243674059814574375077493088319889784970587286591062649045683132661681752", + "18726053049188513051286348977772545167577661574609708038977390139794201099882", + "20262858185621074639529176348089123044694437795099449154711162805012934737131", + "2249345697973053772423677422936999849381692933292653912080014325442939977122", + "20814726663898441680439335735982981967722006066824203970896314191676769388296", + "3816485989624386223507317175678560807682224519267326958526058565555245734714", + "16741230612980371365533431648017361867585544111098407772560748428499802539906", + "2436865301432265520692873922135716828388518032014231744012990863912440945389", + "5265261577128499220460184630262997769060828863581478135168474766310582001180", + "20550548783058990082416235781987882123241946829605049684648813233836863290502", + "21523044301008793877416122201092687874337292497403523925455260117417170777735", + "9283421400783174646451499708802113832695004549893166692004850391713463380536", + "17813773547838391112844362681067751767404443478918792865885006908077545151618", + "16486730475669947890512191574075897324037778751496940417084163322433837359720", + "11367125189013824464048785896422572845103707778462525259651446893275289247873", + "4759445724467851058773503846834304672223785226936531021666916376323562671488", + "782273457631193956426744043048759353979593033245260492990657945904665284910", + "13487130697992008212099652811750242205045881544509489831523448570173633517977", + "15621563974535086891768796441515013364217522966350445838133979748032034816142", + "19364835034502915244801518193980688426244659266819997726035650961451415757173", + "21037385853462058267099182407141652124171361973889761119816789091401609511088", + "20434791917020905003166852059282129255412677606775079570484129378535005615291", + "4835039666519156760310260600042269943079463379265872618778854224413385690994", + "17796521681519947552208651467058827825861565135255248123077469895978163706264", + "2823350440792171019111081223801188552138104039380675927963458669980277420276", + "16030935304664378631941573945857397096373696981104104381156313618686049806120", + "17523561865544155408760007908067668065236326734119657233234283826019015377013", + "3861341406966982603014220134107636493882146780655211775629734223927755221098", + "1327887013530867777305056212037691710827939709365211251951525926327942169414", + "16874372098146373517691588057974501095408377103185981262983559391956463291137", + "1335930538845994150082853775454018356383085560294444442667355553131066129276", + "16846954448852864630121063053695845658867759327963014776419090787323732938912", + "1910615356880143423765930148112668984411979710628153215580997630269783916489", + "8793723522335768214688108364110927144836722932802666660252079036893034856492", + "3725321587522884864935206279104882080790553804758085564413847527197687551835", + "17549397166194503933313005107479073474671951786436058351827338574279485542057", + "6575272615526665941236934551769345604089554458721499014263130089965203838692", + "19479945993771870488240738504390121923410154808673876321101554256856036124677", + "15218540520084042504179141700157006972641510542203443030571191341196460163766", + "11605382280428426652337162672330854829498688801746852913129963366330544359414", + "19452583367341408020642116770501289011436457479987875413223766731278874726613", + "2498463382382553480222037299113185800507848748313035345734629490930688205092", + "1815123960727364421144419865126922339611466868807520419660969560789979822474", + "20531692711768862540943545541715345229360673134388506876856593310216372259130", + "21106443640856542784867046664180461359993554892163126756059125921876166419615", + "8538925154199646282458477113696635826112766123791239931164489946578874271866", + "6179996393486486548378164504724190431464526698002381214818146508779777698063", + "1334556948430115939422649531996020210538905726908545666936164977436729124944", + "14555087544451841622469763698691954343538388285983305607235034906273022598676", + "3263678860186354326206053303615515256258748076250020171477442794745232038780", + "1342606052959540554052550853649027290857482440100275878202185177537473434874", + "19067318604617984900108104413860593038444834168491290140413988853573796446193", + "11453576191720077983310542494091726783885546118293459348522522324645101050430", + "3772400828106882724656632136643514300687950364203707059277582466654856015909", + "19928616354232846804233301414766074864065580313304404532140360351457581578733", + "17669618023197654971616078177762451816976570462585423216749814198562722234016", + "20487504497482961764356160511764652912371612840137405927810776425577238052311", + "15959943319286858239034503624455112049217253792773599324329593237810330429519", + "18384331160163107383609864825156022277275076414745740108239579270660154123750", + "11807744905122445070761653068499781933485269571078706728521902995972849333739", + "21636069700028297640587439425598371999203459272489053044479958900301869951268", + "5974406255004817187688462241155741022204236935194897255519053490391727654963", + "18655439470676485950283686008645538637216956533059508817637925480405213882893", + "15164692255429309369428108531856612257028649418370969640920631880841690009016", + "12342219963417210875401056442100023070134657858086394031902694268469750570612", + "481209231155250366998260270814874408671884781003382050138985430923825730090", + "3242985953168013112117560001466320034030784952490866310190327264524235633420", + "13671160391160864796369771052335315926068131063004086507703804642392143876725", + "16716228406804746939632807079686149044089946710213611348848847599210659020138", + "9496049727665863372935045496498617414460003517119878231671018103126084599100", + "16483340875218689502751737973203780724082025375353804209734656041473116836207", + "2627597076078148403546873341483726933849452415436198036537442451261384383723", + "20527956374075302103516613197928664717455732919429461243667758971357150882342", + "11711450220231538029408058975978592998998598526983681112180323327131923215776", + "14877293714143600802178367397934915488570060506993092692625720179311507474506", + "17326201000468992158693082078045140389930457394232528033746431682308160431934", + "8241890704089720408679017565592201736334812957892898769189351788325500937732", + "6134985085876540657808139826388808003135254271482158519839818774839726308917", + "6944918715501093472287921248184355748547193680657762762284351108190443908482", + "20293371855859360749476040038457808453751087076170457949707661658124460443795", + "12686929429491234226470786986230897140429036877303905464553700071658994784104", + "17469937611674874489854850805106365496296990924579100118175990663783068480118", + "4389315288495042551686883151731749050970801790377604942482415778510472384968", + "11356013296312574683565144017425132580728729177241949155779586695189495537084", + "5103616537832821778796048073410908442363049367034544148603830689894368565040", + "17797731362169406634431131949969435652804582561417001546024888062211188454886", + "14413974530545126251158359344156378502844867672748912889426381728267720393327", + "18860675036245741580291857551498220749884348391920381715922087052471051304459", + "2078681010293955893545295223175290151677764183673754633340142745613957031877", + "11594462210573371469687203943585180057860108341927961420756260896877407822187", + "8232172476137304604696594035794651005660416081930158074561971898151387789159", + "16234745736110953717672420346414210260779855851076189537371942811750295876135", + "12403261277735118438898936378116787991453555210970659659639856670648844247938", + "10260185954137740247486488192570496092684935183379388125044125653647328054023", + "12655661577981598013787126068450556825218951206788052328715378240540030673155", + "18875782029492829253540920061867800401544385695523240332551730645990253683286", + "13000939909369679921538945109975441940863265779072482929455684540500587590629", + "239651505606383903278277662841450805219997298453219985892834268956273681444", + "14053674646208577108881262953518523519057705122297176784230960366018789686467", + "3606574524342197944154321263420984044427893927972300192386619594198948706444", + "4925738689374393290519002876270198297196104042467164940497567711764321354393", + "9820857610236925174040210045575219513594477725958302510866127781620764675531", + "8644935227560188528158307606853375529544842899940616765747319983176480635667", + "12589563927120228887319930197852404057542625019034806374830349240796880735981", + "13728987671030134173563628755348391107370774536000844606094840710456114349003", + "15280672692530045491619672502933299001869276703035606138561063102232345967821", + "21236672540209166733321925277807375026701626666734236841532747395149863205571", + "18193368154219306112046312834283644566129199372283662927472078427038205531636", + "17828956732555553542546753429670551891943977601119756829631880115504235233984", + "16641047964358580103472953437535358748387376425127849904658691126285684204504", + "7196281413799658043487145161620082973834461754768351228587249162400339111893", + "21279455923934963235610861427104388147894350922169838127737714784897083581830", + "10868227810739752166142906769497786680491652628709341836398414527811509748689", + "2545479497580424357309396388184225593698470568625667945691755386799845345027", + "18560104754451358950174079457178017278416450108044438296553162755384040068059", + "11209544817144484509471895492404241079181269159060632258040504564376475442191", + "14007605578670373547623429803718323316371456029307063658189484725071020560017", + "19316201371814679831554697580647476192318282119512681720915001227483533198021", + "16788142218280927569387096932066591137887806957079516944927766625343518189548", + "961359518362994763330685811948798278197676602059504713988410706948791494727", + "19776591693739287332042935252284088014720557305781829207369487992244783048185", + "9480779019638564372864984254416095889603560407402750333423136372713778963272", + "7812061847536565125280880398757948966749177710701972331770694629380983832516", + "14806224217889264732099766866344263686300132511433376375954468192761174167878", + "10982734897602724370866115596864634266746118759609469486863878972425453415519", + "9054801238670111257982773992849940941038784597792282084645523468554872244495", + "16788499373458165601983802204061832376825550128562541027433580619384299691535", + "4361212778425224413929793165968418385407821814716394404713983701050982051159", + "21198869506404830651226227162808186595284220877501140400488215541390720176503", + "7255012904510681544072472510832565052731304049336267892176928038570971034121", + "9737409770400739938717035426255379270654933363992002237053138761832402079248", + "14206577906412186888550704503752653056320975796075254442765439825369882967977", + "19036632138581200062386943078412086222459679497578993523004498970778925638274", + "2855178582526872375806959544405581665248537620420194093904041355969926293337", + "12896727255458884273207928529421874672712973447260798892551468479503233439215", + "20930350939164528694912500193219456539952966506926646436560438515643683077210", + "184093243282405111677536457857692693581379037444126410664343605529966199122", + "15658149328429348710722591333703516363901544310832580304722884306208924451465", + "17544235160628712643216064131303569753533519783718786133736357990785709619346", + "9378984995834426590515136439048146470293781405649183047514776402081048834772", + "15827462476470655610816981948418438654022314364182315935007413461648751735708", + "13474113844360907776462232979612140726930720201237003164521648175005015977732", + "1846676454601041085237775396212630553832771346942418764660365576890630152018", + "8958790186410745003596973786908460746144469347369569174866696175944574520886", + "16716100142556090678395507171596864615262575578180211444515549196841601774046", + "17584363243087108058467208592097637069605249776196694465943790236027601639916", + "15462568643993327150997687623907692370120490318886920754261967569094539968909", + "11670427917584674115542198398366950879185738970881616803513412243898491416455", + "5883010686944177614793479335292002976406988590121850032334552332298599405710", + "20848023045403944451304856285219275218146149181988087184275301094312642906291", + "20892609628755793476767683891284835591758207667306100001065280698890821585620", + "11041559416099382923560246079300939393371149141074957197352566129686429429340", + "17004024027027164912556351303862470964296900000646134239805113699616064012220", + "110742314120280698533248152539115345099402903868297760208823130532853128340", + "13611598917097489441998314826578736196564311189470688979687759717921520208428", + "20362978391139708024092837231934567580385484740720090300868417284017430844864", + "4130975720087443718484415210347908638971321493417335260526136858657572592254", + "15799784358302997284875412214187555553319485274948108081666806701893845835839", + "12410480753305882251320943831026503736012757975027018073585110506521877824193", + "11835843853657957571888855948788121206617247107501669280697395787347649231752", + "3326313455005237548503557557286834479752096887215379141590090769222516357133", + "3193633369267878319453517203588676707547172638050950764150162277144428673066", + "3543696055990388683071939150214505536733386566291338758519836333135488212473", + "453840133795717001022433249997110059635014609516452256954528366651276289770", + "10086004265216215714804100477403907145516617200748655771783383139854288214070", + "18938459257787140207383332020952460039308194017940327258304986766920440675756", + "18017538799787896442217663532610710859333377084532654794368604069493775630216", + "5517691591172342790575564654696650661133600869824307632295945043592492062300", + "5846204096126701465613249085053971321249645306247508562697696901334354225619", + "3177064511134248081568628736306700282095095665917536853000298191943047784014", + "7886005759395499452194553110700824805018792487440311729836576312028682853862", + "19249432464407391173245558257296856631584193393398113008165174416171947900609", + "16818455958785909569371690525990846776263170512884599090849081099178789681425", + "16250344336602567919050898941410625842485562539342327155695417850618940905704", + "6273998461375119044609362240019558608655450921258416376794979330773412610302", + "15933077340738498731035173703791932079747269039222967104684412531145625747085", + "17631878023023477567294765381542867314814954498487832435087010633074888584009", + "3387656327342575368928488173891176548794878068816523542226413637288662472792", + "15770343706243316227190526252701886989383556270818375222569120097305537622560", + "21025947829537149117391184273139276031347299127217645728072786010534368285621", + "11728430055160129100077268133090903533902452454196978455625432056779499908581", + "2184576630760971645143677026393147474439766939689140114811262608230414186937", + "20744811853491523948066896610767067484129121010717068573365370365324040781186", + "5378129452609441814399329369785055593231824205814541852039878139773312247469", + "18082900764136659604287793533371380099349929291808230688664846500365863263118", + "10463958995559323021196963984934883570109613942564610388110191948063546468897", + "244120224370345949702567256216804961153505781666838608095297311545160357032", + "17924705581798291273661662368787600134425123985006190354093511903371507000154", + "3107793385049037773698181795186417899797325916401357881664725445733609110598", + "5665818573123185227274537904890713907625420710982346291959547939830358917272", + "967322682615997637785254033877348832211978156650281338584051044602311410196", + "19419941178285529854771216440310658103611219351729270204884834098822007849679", + "6901963792883328370624032472781824547409040392368725235274158498520441238159", + "13721659825627300509722716825333808233371435398666022190921612703736274379535", + "2784281502858555298249063959836879135450746982163416748737579846439268828933", + "9904373282060708277943634486822397019446454722637742217276784802015824898651", + "5782567592658163731724098371574354386783075175203877502094122152538152467682", + "10854330629450460532485325799036675355255970975925867222693267730198057197195", + "7162558805520478103072398765799613453839879264508883857822705210986309908966", + "14561060495007338369036260685346480181377385446422680685283066135483167829865", + "11521954935420160563214644175207412771411940789064933791820101643809540481492", + "3893071612329582305940837979511590531534863287842007408024123330272447072664", + "19982770443796802008915975147614604175753586689418309845602797606117149147490", + "19714753609495058998670661272525609201695470529132258598980221623379639411831", + "10656632215192474178114431876399520721084839753473211054259843433641616176373", + "15519943627473966175746342389219894179761085602008029155282295063466585111230", + "429220418726674010600368106136723992478318707196454289985261340376476917460", + "16943119555428737036287647863079565463224985076466268175824843518378134856246", + "7079268853451648384434335899135383974808119657387366504271184409878695702895", + "5787261347913259367727842908192773692002199385877294080619854106978539332397", + "8254314874636465273639128395147895313719165057850599581478980264860146008069", + "15417738281457065064716789110361253613929614783743035738325702945037527193953", + "8995940809050737092434676062651493038351424361820394016896779859938155003450", + "8930952966754141446126393622188683431566029237395186071059700311531927009283", + "9012970415439810859538557593310902447051948348093454112737452817814629449500", + "21700461010267441715993595978543322483687194036588160210184366057201658507847", + "19191426116308521669196161733982754533604260068907220372422504926794231257150", + "18022413735343984488479130392027693687461867574196874267731354592562070094392", + "13853879871506882218224060020827336496729967255850404386800036291019021382781", + "13303720125164503437055631247918150173085142868095887759030649510172293881844", + "12463581809293287384469946044562671884924464520288697069370030386140109068261", + "20468619377263375923071378952981485015200979956112400596511865225946853604157", + "16682148710681177357125570715056314888342059670705617513402649433802720432267", + "16299073895000203963165709887505572454180623116454760411179563591228007694413", + "6439155427163506786329349605983728674821430800627321435200421453561910062302", + "16531483734580605436075637034861280240342858648848575098901014901746112480232", + "17413802217650584016261506268242623594956116228659732892682224912798301233645", + "19833018739354446018077109493089909435818386368530968355647208939546565982905", + "13005203599293796776324509750491064421128717423989464867065044987475986374420", + "15433711189444672576513248931602290892518442446252602686878477157678233603772", + "11272192842480959445178012145556234469776261923967845001064211055340129168135", + "21349777755000957327199310930646977290027138137542241555905014230683052104267", + "2414795183415356147955181901405712632718942970568205736628916600696077941534", + "13910388410253717440990758214044472114511432613509643223811561885135488623236", + "10073917454281511762447567386654530277776617831005093724557094001489771821135", + "15674657915196276639699997458656008228696751013801231738985398708672037426000", + "12030695425048598984176709301472822771003849589255577773183310838231109921591", + "6658172369461756755506276881582345916252610724131747740625283609123100367529", + "6460801016753822141904293563006139350014125998787400018150863192907944207957", + "10798491465896968361800574703868612181389697312199241920447162078078725409638", + "6331917501914253534943383807348566698937757752033630507696817298838693259937", + "21521172968280414216108032807577565012642487518706778276505136864150789112592", + "11443202152743097070847729825799673217706162711935940510632741405015900516668", + "10360970774813507384412119692215277392320350056791930702078433469299837875151", + "8111678922881662305935841208620197469657237670526301850210945861223648259810", + "3828566775247110089904016755996284741548002327940628727687176763639903716661", + "21019871488460899469684764817167629979753844957147537040703291790231271795829", + "11744049805554498869931942573519884330545637954557542018916739662277241821806", + "4521092770491436085084640166923844634777984445583984077999595768778116564222", + "2428018726292924561718904390333390438951211767580762396913313600061529081905", + "2672992591753804066533616673591169777906973091506536575810912266557203322920", + "5631180351966611479340932319081124575466459942666630580683510336616679680271", + "10149209329290376952496655294191511204529081153402908137750268385347783758010", + "18292794133971639465196495021864699906132845458944945214425906730119328661326", + "21442863185355178191454777233963814974940050392649316620141474331670970354424", + "3768420898310640667772098495371174917665155708578905018940113026409140957987", + "13677778555119984843885943251631654212176086447994430552012266440677394344669", + "13884681165958999171515885225547717032289759601884108191367706162606597842698", + "123196094575938824660055152882088188411485715788351262262924974166600702398", + "1121836698372380581784934880625694675020871234049336489788624481922395781738", + "20941331435492311592529607715649713508861806194386837398916323083940590908651", + "2470912827043971002614412337239267059969980871643559631900987795139200233821", + "10806505189594612637071931546921663393081238567888534876058498530874738324701", + "667951375802630033661777802749339877422061577764798227349674331630120025667", + "18416355600415187627018330134584431345513028652497077471935121971918269469363", + "14167152054564590179475064444026440101215733530475912312508414765738108715862", + "18633695428427030575173671831485026260967985663658201463236228419717189642766", + "152822669216765741203342297512101138657182497046533047369566701489981099230", + "13835701173750333056481994253160471551109858589047436642253159392878873667798", + "3993942321148722649703549241999711668949060533276325947207349685002693878681", + "15582244332423092177434976075689385819450099629893355758782548118218073388706", + "15110236879710270343688993144525012407319759236015974251051640787524859884359", + "5104405092803829419537383694663582438349376353030379488011426113631155364320", + "11034886586481561934231698674217393887518948538322130743646058638919797229737", + "21614370562083755709911993869347579638113152610927033622836963904672826178593", + "11909716327216431973191112809713028257963610176155315584304717743448686635887", + "9670047520194835060472941420215502268522351803257892125345072551055025494562", + "8752044341583145728028411582583224350471084864272507077624316823400738066962", + "20685513123216586620977713797881862528998788503897607377725195418550074311551", + "20219162196364967181713755472576994456615542213293827108438968625041058321145", + "18287830464300889532838439052863785386620820747210980263612361113628554829988", + "10146051396529576924597355409059465520468869175466632446875430377637660889879", + "13466459020798488583841582724067017412922317425102130151754649408559458307937", + "14062280191830459071860023268317938748180670907089383563443465249500572357980", + "18486553995294693573565546696966437493113894571993019524170031057367640632085", + "11156566424349445901806390826392443373766529722049710427351550423908421767094", + "209671637225069235519570008386635562520193585953162475265417907100134848923", + "17226989944018790920809176115775819865824823495740082575382169759054625372382", + "15644589951345053163188258692419292119540702867922222648564209455819510994564", + "3689635641036835670663293726548900381724135109917216986885298700630212836435", + "3367607896403464195671402279459329078003744183784952830994679539910724667259", + "6227320552634621985217890398406127207902736210419315868051857823685244516725", + "7357930890687295365886228617478473072206575811998185548162905341534675558305", + "9337019296542497689612612043175604595811913796434346282222317112981594913389", + "14658782859891978670907070276103444826326577838777644289370207112293812556778", + "1700861002075407761970169168361393086239805454951858464329713573177596208454", + "8422307882422345667268572118847227804767508317685246864132851358134342544918", + "3824678171886439611637777800578730196591582015637069631407414390326082519384", + "7520989644070067743500997565082513560943860081670904302057616063200273050286", + "5278276919931895959830110725703210158384647399821914390314400092195592076331", + "14590632939277529585876696200177152214896495867542780671631701634592299041714", + "14365499645924743985349770983085181263329435144891175678390938245209017764418", + "2519790270252875654107597063434691592006935573176284731324585122712988059511", + "17688843544040778657269233842324532395371012201506418912518394656290716826075", + "16584068781164994465207120381716024087231836173689783891650623302438290695506", + "12224860044594664185598615945328866758529752520066027818906177267571423023661", + "13664317767999211366109254182438581912610775541954425083255023643648887081779", + "19324196860555787958873349597666822462940695051471419602454830948112942481945", + "15338841226759355791277440652242849878000656382388414806186764010001628984934", + "11076363155150973228897602285090741665942726007445165132980573631249449594126", + "11228309866140794620879641097623963859536328868056691748463227126359575786386", + "4762608512226640372168720665137259637840828925512114281702049841301872652787", + "18282645934358125859102195916568492018711932725386725562892735740355836227532", + "12803228415054755333149187333584509982900042807310255834005394843350472605458", + "17675693156369747720817703064233611574822178844066411565804543111769294187197", + "9900029048144575309490519431063332695303076438539483419053219772370202428926", + "3684590949621971596368895784562632626464811455818343794800044114209066071601", + "5443335602638685057982926800093482287199751584817191972983546508574786160090", + "11352900694666160844325992247118358443639716695965864728670968730093466793722", + "9836739435541786452166525951732520477055729763398281521212184905286650567233", + "8222926590877635625730738050718327099397892409701316035188479123499338707893", + "8154558268770648194631329585722892880905143452138234292827603893129808716905", + "20661038342485310632612091028394348057035659683250957045340774030445861865592", + "9136910062528018177460276667688174167129493547069053533874280111057356360561", + "4362513385797089229061458501847196255783651860098500705320631416351847846956", + "2061137061600029258110405980965338431925491466724330216028866028449889153371", + "14607676885409772552908782897874144975643999944034675480739173900267789420534", + "215346512487318428553079809620502708407272005519315271404209452927497999118", + "18044026902282362371439577283764019415115969502361960218708274179281044595578", + "9652478245641134951513165220881528043195466248948069255527062590256621034842", + "20994154929281322813927859895894589885437941429166007529912073756113466975582", + "20752721666010515144550782025078875036488075535083563976118804420187462745253", + "20857028711523544595627940704882176284224509745902984714255291431664146535922", + "9631521770540523913735742126933921923952197512938165111866628665235591582568", + "18950423265182779471595998716023482060645307106263127634953888715515988505533", + "1436791836740130330138273456892846001841969807914099860317370076565131805680", + "18145299176463660895047063984288790313564980703886502044680749544519011424826", + "7008134596456692891696131297028980612714475387065733972352529833092170154127", + "18054087496593103261596842546955317831262607456582498514349407492750291465651", + "2460661191051979147731673103829326449069370361298340160666765010767300969003", + "1121019547339042268901204213478561141018690742635442229019134496736639790078", + "13486140142607002128358893931572108539446504181590991898872881746144618091798", + "14485083458755292442253176062192342099468601222388603924363708902524652589634", + "17684636079328478898730536417772675839399177918554869673260926729643471105206", + "12382939536995562937141167025903251534081453604974163882762565576243762872206", + "5191757256912351314880102858899907666377813090645991709894707944196053941770", + "18397247107649643640823283145149323187327745749077714626730537494597891967945", + "21508632378351416585385353654317189405917247727406155133342616741543833680788", + "19108354768686907995261340253443420621814860995097718380505789237761300853182", + "3649609518051015699386442513879956346519312025847003339036530556474594795760", + "11893851425092314587513815253407979901615516208632062595457152391110352908805", + "13296593391067251947204447959241604616835056311051696511507435925462940176830", + "18493557674615580922923001229788184231889430766683327472934879670006059540367", + "7669746659590113244880799806073731587177781693253502772068846650012974230120", + "19370654200032786851343971085637480775724705092664059950989935645178139099864", + "1331955346226787928500793024038189892044219824334532771311923855914410290305", + "14488880297827410405382492933041130286687512096290491259710680579157544248910", + "6760882547908259908954677726421351194118695606292587659467769365205068189814", + ], + vec![ + "6377232663526537440095439257883018477761342422116697881186123375221738885878", + "851539971462439380385862352460596759101811723695394639617127852578681769809", + "8777577262325190174206575699458733195047013200879424709893142671840513604890", + "21694543997668766291509756109744969193435163886467863962355853609369758783238", + "9577278996811393500051721677710083593799044422389686435650597107832854019185", + "16323954252044716897246121150114593642230197187021287621193086593549237094775", + "9789909425016820105251161906130605326280235056822272235912508431951118212950", + "5766700650277227528545902607164112169119010038912902265869378685414299620760", + "14342521005374081251816746055115831251291272287569749723238975882435091047876", + "2566050045458470252423704003188705777658084864238473334290159653618543192811", + "8762700051029310248153110133778709519032029454737126719215892745208105815416", + "4708553466520767412303631379034292236924119642035476122997253385705160556618", + "4755252554118675759917549980023743559070421272488077422007409392838436797712", + "8781462767081720534606018702554359272062136386754094559457527802951016005606", + "19167810216492792969016670752653089791475857662598893252819620255611011677188", + "12379801295054424513880366937656969081677178004556540562031393564676230427743", + "3873349522143254287251699452075145107916086554326675869006906246349942638560", + "1683302064923931554193379270562867202085645938091131834974486624990867609624", + "2777362700160137801933468204963311934247500177582714816722898763176642740860", + "5330041075666088752029210636784758218847391095319460299231210692948196701638", + "11849341704739004206642161112350419905150271791787570525216826204427280723792", + "7477184099861050355308565098520563835117942875101546634259876195229073147282", + "18811741129290507103385501216699521500514849038287903802256059864942452310117", + "15644162092778325718506673614750051639809307056147336506838023349115605106787", + "15072682042494620166496832302000289519302436589952610199010633012972669445593", + "7385535266916101728534366006042662339391797772494836337087929961280561819695", + "10606300178546340442574451452231017670874690381506662581848460294140286741651", + "343808333592012682122858022517390819973432303579818622412786360520154826142", + "6686378289544833739489172513893542192299224296746579469451376125664696638046", + "9325668720082019512834072623751272154060473105966456255302021143714657867878", + "13237356616132921941407245964289360960304194019926733744472216846697663447262", + "1723892942664599421365079138681309575413323508685958773158319650163306910931", + "6845174279248890961319599668687600484787455619619716546069389087383603254137", + "14429592766972645051919899517480716657546426049902884218808698177731678278944", + "2012555589304829161260427679955782678928810146332132910441113793264100264511", + "15162287124358358727307007568219331690174000191414576263088727973180750593247", + "6171544970310792508799412092397912280594923286679674322244394636145740843662", + "11560360683323732335070294251287274796083850957500974817278726137032659811346", + "7954890646285422519425515982220441977570181574595597355546782742910060927756", + "2121066076676892095526555416241546752515994960009188371572036715916593676611", + "1030002705665772802770205305890036009903459272665864721338890073927102958060", + "16664112528630425414995233349042921759383114978671010908728891678245502701008", + "16224205339340335222764551648549356562936176805367408466634736640263613613659", + "5567916191875465998022755280584031341089937668974064792042640432034217833475", + "10561503261915825576621563677167739482566911623771072553907339314680805249099", + "1281495108038254322634503929806042733441491866895195579580212370919762081047", + "9600700315845518751455006692480832601523246124684033595437676082879283709816", + "15989333248905201890715282122260615227836016238448185882687783867814184655170", + "3846245593630362971844915233982274952290718501967612724027782949411933001202", + "9981027954269438386336412342904724691774209648042702865994578173145958992921", + "8623595877941915162474742420309695649920307514068323484728910858137792561119", + "21103940025922636831675399050467233863786411927021772979799068688191712316972", + "18892924253208304354853962839524897599416779246859691035714354037906441368765", + "17137414752196927825772499610314584261795745874954692214656847237011815603711", + "20412422497099028107138997806006051244688526968840932637543351831550882135155", + "11225636734520002481404086272590673372060731353304957503311626880321065568136", + "18380442589598191047463232737740533198002604231823107797039491680652883496794", + "1080201698768913889646664841066956319958767123758689784419321296338840961295", + "14348402455238680465583355269916779409600823120873923092214453448424409970818", + "3841435364722615893087024818655055436552226081083242159440517874888324292581", + "3408210599862246992363134715624815235769905293647431849706383726509064300506", + "12828232946525727915578787875290899244261094596690184893334123105536745936334", + "5483797730688489537248191960281635992343565537360149542110268773175134131314", + "15646042484365011867018844828962923289034117590475224947755290094723626891273", + "2658047411395048849255440353544966399245817841159701280361972904541325691434", + "19496407504291857422030801612379213952698163884670351003527359060477191854359", + "11599969200544990318778456235768317543324325704256981991953010275435791017626", + "12534635949431553834868179572769836881352677117158862189611147293522496413113", + "14223314197724082301050736397492110631416043159307338723464864105007185825079", + "19822547161504065277026677714514212915462043072809743766437313193660041742198", + "18248624683501549165279508462273639851850239430868786828229911137041335077425", + "1772507929668430466250295341031184282507314702999122972093244182511342701791", + "12698826328883589821004991321815018437184890565199562478819948777685095680390", + "2107256591274868946942358544310209350935597133418111664653447540003390113607", + "8347096431391887603955816523197766644723983907921702200049607244690524226105", + "7546736802459880596530318577784618006564482951653292089248497980343037655783", + "7337317896163766810388205540011597034395961854295494001017429381228506327036", + "7657535373588628884973408484470050620893383237179421367090832333743641042323", + "13132621069544809006163499228792832380417930375502811639756010731409020775733", + "3045981446877420174701593721028589257508837379178848429319604486793747007869", + "1665034234802535695418712526119528879364535660712727125979361452433857586005", + "7153904853002570654968228858836861252211159237410458977141045356668538557384", + "20486065252261216388496191302294274939758504298167574880569796877079451248375", + "21146476302842253436461025615017889905755773199293419435978649511293941220143", + "4692883070549935264853696204165792104522817067387529940796053065681435988854", + "6240088307004733902463222083449545201088681283438656231355545734118814247048", + "11555561118019341206516697020813127391202363312629469259248586095720628837415", + "19260547999655668000047734411254185932378393753746099027853756019009568507886", + "20469506109273046972497148219051635976793704979896239651651205124084812945608", + "1236647274759658638933992315999684238758461477931896092313814863963831171033", + "11384423918232921171964979139440160725835135313593824548598134875347314405204", + "19785250372370249720518667471906851686135385809175031332352733767471970846466", + "4246521523867165828929729227115582186945308737715737226156399485201514735146", + "9952732737001449699912255226665360960719170484486452179287528363081995818191", + "16411145939754797754686024918808322973332629854064127851496226070432060579489", + "18478056933955827759744830164752062474839918604932227276753757763884050277828", + "13204687970556138498219183195522996570326298997850204255083662628089078309770", + "6486083806326529246393301553077241033740361238170679962888274443184188794118", + "9215573806816888307072467120643373006129084289252457249266574218300367297487", + "5157456141970297671458245970390083650482632128904852982724214364911239574334", + "17822680498490868828738779948851745357227318213932493146619109948725716270324", + "8322423511882718045936027421959946860221136505721064786938368517081088404769", + "1146280240837664421126981150154328736275224000612293306261498532925677882509", + "20006445160687044351950305884447426432577260116801089758873885688911862838124", + "13132217654381318972692935199671458140461723506405656953229384472487720023845", + "2321904844688587860096390475332685957247396755436885306389445060312694195758", + "17673723499361727802425795357032445257876321564734597671004472729727016538966", + "17836648739374245973743495166940645001620159031723548669336786509810303589036", + "19509523664323410269318198214695596019790796169778932865716910251136766472034", + "21365014298519541792222476772118358897898683224332026502540401938408420183049", + "2443777802329356458012563966932795162347891060116795715814546844741577072487", + "6373148417441446230918754690291753760232604931431996749195267137425734054207", + "17543938461501434657363693054851238526018672792888706636605942303973500302856", + "13900881200135928840365427722717255359580153642574547892815287601924416317614", + "9982616108044216660339683982954165936737826707471259937628917232293660834440", + "16457765153339087464480638859689501343872608914611554385236118860039346779707", + "6882633521418674793651640056518599843365736128725139938457347614662141371026", + "6558481420440543921623853603383694405865402572023705828527015346924767906364", + "21119564418700154632542186570611885700968350571284986971813890102274419338575", + "15668498634043584871060292933787839904928585875204605204028656239629550300891", + "17754567428791571673016885915321661773655444664247443414002133544771398853149", + "11486839919314218506003227795241691164988634920977758623356018460082101365168", + "7521215937712438604222096500164256001666624136838511497877267672752282058366", + "1168385489601974578347279341199760237159478798101796567718644776242789923601", + "13117296414138131801834212262010987517820472685866772554743932452738843734333", + "7749628482107487230728683638475509704638633069294493147970362304145477016517", + "17102526463093059579604328209955502975564943362848110499048688895825859834607", + "11877937469390065191819717631885427975032604373385026940117533544572408798485", + "9490483077873795676333795591814325768750891664453067983811838122306462917887", + "10191097995163502256819397252907242166733175440759424521233105453843778654820", + "20532048353899648065110204116821557712236052515292993984913500569982446829913", + "17354150523998248091397848718695616500251280749145663998809707730330346369346", + "7747355680464214426243190602078017576496231133574539162185621451748634683393", + "8756715326391069596985357282435500586860011252775122994945356255643854963530", + "21536474090524236379254986352553305027867958936847041677436373000730213533274", + "19764807787330426181831011653714787282097837960248105858049804952757136862708", + "16451394978386784206980716591328721244692005310628203853347165858510983157051", + "9958807580185090358651106618892828843813431270139145526116189671892797920190", + "8667474404638571999095228348352836564567923532278597424241711463350637692261", + "9754527193113710990714009078343561220541479581342251031659489697296746296505", + "11755501121260346797952875679164705763574741595098729931802001980529024314166", + "17343653273660706017905395390969914833245644319150049521732399082825162090569", + "1010378412861729818622385301577181571311206842734096023690850995284550560689", + "12422787992288066268619146495902983268274452848893191113634050431879037454803", + "13351916057777123695069150362950067630454763506856720439068955445038740053267", + "3268740447474291563746626019604727880178668296496938516526099943483641022899", + "17039539378002212101604857371251026489627400179253250833603358068705093844865", + "12719626976782614661983190476189661930540289684710840404911817755168256363569", + "7334691511591452788631693316255271478502517924558055271367172394856435073355", + "4177321927122082121158728850724807513004613701936483594734414988911849675880", + "8517156232219806038206488131493677748028421797072860831547349043281348142926", + "5916342138159497146131772146268734765710150180676587683832045824388433290036", + "21136252379072914855830890952695340864582847490462136128874077543348574696616", + "10470343058787342159878702644341062172468027793693540114390435145428370707552", + "9367903847960780027900264774906616911120367129803429048363499797310012009648", + "9181708529218875829085211480957344367690955470310754169594385873272587183681", + "5161879954208731149141751476094480416185338457043041781556700389106447006281", + "14144603730790033561496908848503636331176898859925995171200576238014458649562", + "17528475461722173509900495818763366567939364295306035018228011778228457876695", + "449678858200791083139507971543902712490268199763356935472396275788444419520", + "16677139862336739737616497869711537864422474730319606529800927093596292773684", + "5307258894824770781811695261046705160386034275400321369052582330095447609528", + "7066455454850758706236264136180958260707829859148436565416678574940588976717", + "17464972536694182038180604993012612781624892485542021128713500864406129068272", + "1827574381303563082711258077787177320452649985803311391711223171032580182910", + "5005224603218153811845694200896301756909926774436577539307979565859865998867", + "13761569869627153404623558442531816440237410187883129666707204029577726405280", + "7377645231791556592153877817212695036000405157132509030366572797100109551371", + "18929000938053222386693378771208400814166536156735504775432130183653620422676", + "10277912490419902146704238375228669373848088391763413912903062245600018539005", + "18100670309576234559559738539779745691447892297890679181953368845735547051936", + "19792596568315968292503371918803163404916721074550555295039247624601141857094", + "6568943725774928078767297883788056758405958920376813493652098209237338819058", + "9708751028820311560873537735196353024741491886920686045780276405320332052014", + "20999195425108372543557431885250084840784235258564621629580763574508120639473", + "4668167020556593094762451685509419403613005848434427529155545560914783238805", + "10260166712816802791730167674468655124354432849926388591536360669342400828562", + "3588854010476278115364011192514859807683141010842317346600561233941024545452", + "955642453625490778540666824328669289325333312941525596301926803494785606357", + "4102026113333601512449185655242077481750021570821512149654744152647996823622", + "12901729067332459436197297782018174449541114313233855185609986472102830633274", + "1432400515841095916662233518576616625504866912337953671606919993429647264779", + "13448330934580056368019676193029108114576729981976748604441994201646531786832", + "18698774355061680075847219006041299002465669495077065798256778682757699200357", + "6114255237222848342826972972054203376750041293553715842262723528673741797160", + "8290432657858704891136963220676191057527195528510584103201077577863568506432", + "10245893420315465808958329213978599152040050245022584266757834865645078424612", + "16235075160725310956334026818354575166666144493784149178325740173109469893465", + "6096223185593495310139379444667947750109489326578429517185410779366192202063", + "15140535409353326038030605492985074291044727716595244779768492745470176024609", + "2176086602170005476358821348469239586548222322021168089824748815230407069862", + "13619789468668594404222482251836770591464929359372018436289664558758704681508", + "8310543107961996371575168146304294641952910047046695374038288287522235989972", + "7738370036488385965043396010845927300705713772735513648600973583706126470834", + "4479339978160586717158719172802732929733916533373426058515465717943672025882", + "4249199078635815430904933856748414549211196022798648243994671515262509861644", + "4217767457132611540965149700331359170343442048612092095364557557503970133933", + "13153296757017742961007840475261159306564053749538202546045661292791402275573", + "4396888098805340064553349190742819413046668458070694926676742098287729413444", + "16734434548572604008363129496559919254718993826492605430831689587940707338899", + "9015659000250675923210953833943081286931414181352124970688626484896488861379", + "7859006238840384066905305454236928888746240833462349287375399918976884871408", + "20111156231978127386472936347996655414872475039138926155687856551161732442682", + "20628144438246471187747981572742727430082255446467380395647482352559593647287", + "10829450719086027299358038584474043478547531034689618353624096053194488185624", + "3900905003848877440680433966861518022290921312029158328541666844523704712962", + "17855611209216805679188603521771950395726550847102335142668673883933213178620", + "3545647030011914165273791133303433140616659042668342263053968795372726840341", + "12285059161807384662955183653994648298401051593713604819454219983692697182696", + "11819552939527124997493513022814576182004246358800352547817016502521627790011", + "14301577158059901977856927221571457807294693699285069296999743154546478489569", + "3571634356329355229397931369891424491520267531585441552581855575412868351910", + "4493956823795845864156868703591503758707793967931549819151350879135230170242", + "18261935892851512416084887686097384445742684392402688030129684935728742717201", + "11603603642132262206403092178979219208473125803069223229133184466185736048060", + "4847487817017177565347080569283215504545281846426948697937793918200171528656", + "4069745589764729706654816299792297539062872486670505242875943264008484198412", + "21282151145529600768369623290936085172906963145870658008436208961308948924585", + "10721916615176439690683002129869911178402752315827226965537395702918089626824", + "20400924989628432852029073867249809947097995745931036434033577251949709693425", + "13612038717302251316998414209460162307179960669236088330389280060785328588738", + "2142054298626034610320009155682451576863946725173133307732467701538715335347", + "6814074799679801916559787533428482395152765022569535278039545933747386331226", + "3320993272550636151137746220977818986579019792097013138902071906802378678391", + "14404588996507110096126959822135132305375204264088975725278990285893078946890", + "19449156048766944910033639666691724350749810714682158108539166281157709899569", + "21600390672948543610212878389553096169635598817477527886039588952230732642418", + "16122909565998431497578901034409662715618749437754826295511086000491610510803", + "3814026203802323919937341565517130280297397500968227915639071188757380515963", + "6912908852134560099979027891279882003635111070588372993239339154823206466274", + "8991012532130902495044589989450658026455069044478725949828656540931441650779", + "19794616058753707346170576299297623557371037336156984230370345545981446397931", + "2577593820399732466692625387687370505160505291664134162589397465829320209836", + "10545990182245838392125531729060296377668723705525703355179325185018108067002", + "12532592142366733026886391992589159605208721772700692225488484422366892623887", + "19135911891605926936423877585461852787990719411437518367185457251216578059981", + "11635603342092216271740512684448806260427922119693878652222869987036671146111", + "7718247137511759231158297248913810065531288952022630354624599924037308251451", + "15449533941190926955831618735652142785144234166497064450979389633622292725920", + "14793399192194938994493676084408874396657844744757917843286252680102153699293", + "9379880417179271734210305738187417887144762048262218697049423795232738616822", + "10001585874846875226646763599153358317197291234293545431914341192928883246454", + "17564611789675170872923370710570629576001514727841256489502945348076745512773", + "13526676577413987112607573245391605865887231830025935262602066777025060147701", + "18771091487566471187260929156402254761992313431761027006794035379840343952064", + "17672790933843778353408361605666344788858296349839035375070469185645819063077", + "10136925806345148466019786355963896194230642602748938687391144254701550469628", + "11669469369568255529354542182318275079656673415035219767446071893709388727608", + "8717156787967537877037123555054580463721012975068417092258849467938967272751", + "3574689732222366081898222156809576015147776290993716837975298246375762980084", + "2936447189567283726966987004410103389002644634186413346432130822474131530801", + "1699723231413680239740710996339133622208402062115605498128909025253321290927", + "20239438661176091033530372775196947702401783521122338633601531691101072063415", + "509222199143055079823531599510182326541217708021228426946045537726376153595", + "12460587031004227589188413497497959758507406039249371767737225675508588807598", + "9172361948368872306701383997811949982264909388810393902913358291336142380374", + "15024321518919789320143737927991052999071746110692384451602809887435324670247", + "19363337726355099236128975299462078599854604247644073095764375371642393487744", + "11352512845451687563998689687452223516295911399723160879302208800753615647616", + "5033097489048897691788022265636063060230313291173145751178865731392231547832", + "9342768693529219155995840295623046316860027403351256239528640640660995546250", + "11343407235843410518451234635552443892628096773317032816539735746541252484029", + "20271637634427257791277080766628733956630399511643807969496831759934416995624", + "12979118904307784600641734806775453265865076574307149193300157552133646759000", + "2609049561347471594361989318849223604030501563821990609941361801853208873325", + "16638136645184843973996251461253142824084602885453534706330402604048300209367", + "6290150467317840195062942193162131367777911299731759519747208884085640022080", + "21846903793348064415550139579740558481601211918214432128739680250084492380404", + "6881355315007836102200696266576401649721953812561017678365140505591991478449", + "11991852144633415808902015898168146769921125504309617193757255165202163636329", + "20579180498569037366675392921380645532641402855187025365767529341564826966764", + "4127941604046459390852849136716344563624191244387948802112999500161867081345", + "12726445769512078351769013120614118104254671239018619408743816227993876252991", + "12822824504588887927083519548290538468815267612767490908011540889502830894241", + "3525790082239104371118456157894419087904422095178764587616607951352330724979", + "5534817540911273750657456222142677256882873311813581893871590176089715612985", + "1615881228089658726040568147025008122728572958650432110106281742560315865517", + "1471725164982594409495579793735446246197281099521356897919523009694047660159", + "1375309198078109412495220212570536673190607625762682203229827372752214429058", + "19114911117517497826908513598723039822664580418797141695087511229965144677908", + "9628666313906709051161166309431160628627430386029173286325286404453712266410", + "21852518218578549606473925058864730694915463701150591631085298334480610743316", + "12775432117186202301959614842766511797651599815903402927721712100175714308106", + "139892473068642488659633517109052420816080027074176062905422560867217142259", + "8678567564479314009205848092936065091488089332028298130303782323700697895584", + "11749646464324896227459490085151303579078783519261033148424203540751860385879", + "20522934943803615109303532925965718163549240564060985032796290524829499285217", + "20661244899066232726889114470941159662948096319289349895724712936883638757146", + "1712076112157842791409364964341168524175271666408017461435914200921357859979", + "5274198338007371549113715286886410970476178374473353058525495747031417868052", + "6737897812641394021946938592351495323837784050553060267876717564065727066209", + "7802413308864463219891658906834234180067201307743855789866460725804890591074", + "10598878996622948168711729113266592565050867869138946404948068606726933771770", + "8999501853368885436259381006393167420075229434053961224100936590306072807402", + "10154159140828416502096070052350440839365634698281866510618130671547713671046", + "10116420503162714112005525549243891887026278400783073704446798028762825422117", + "5545266571933610687233921232979606259360579780771113897229483385986421780729", + "21233107093610116862049125654360754386798111684938073821049243264439651570366", + "5307392033140435516838295705521564813869712246929667484768640666687057034707", + "21375317482759736213193607973501605926935171024163842355374965533706641104549", + "2517892809920213533018018674089754443879541674948230773132283920676903837393", + "14360345633113115894388501084706102426582517876835778852281477705852716869669", + "17053269301717416242405268053150416723822210193991005718303172171427452536837", + "9906602428995106334942925928993621430906117671319208771657883136890126991982", + "10354603022009709342018106446249264303223237761462844795940043835225457441783", + "13398916116699661698644750814188836272580610770712272177556442659081018605804", + "3191149493139822128538617654520106298782669019011287540692938944153323592958", + "4691984423256762483396977170887219469164634656601998705889064679728271695271", + "17844101314007938193168524989091446035911779338131517299817551328197378135054", + "635635940269936042377013194809642013073936660346940411744079076043939544740", + "12630888356903717277892931534313436641457206712723665520324533786104375264085", + "14140466574121870700874387789856251566070511990708575066948924661369491559256", + "20370174501238434846223710470633284656345430614321812270231526837854520663574", + "654339196866659831266130941576738975707930915283825590200406138066808189370", + "5107706503321722709363385752813500904785775244074262870879244969961234309572", + "12198789333458214522406820255828653820578540569170284513887146039452722146485", + "16249136895399135618027043741098607972773831911496869243661084436243711109565", + "11074204104909151859533339603597929732173550047640253831218390211736882449440", + "17207343273400097590016935219508528858538698779767314862863101225959250875891", + "21262587236682681589242692923329018584317630804742733118429592061667487058638", + "2280753183511690306986430331340197673251265546818209861935234085004534230414", + "10254003274920664842497725816382563578440196723429688051808776569200020879745", + "7759959068226022198572347902743272598191759849179433231578496544131111538092", + "14214316923200364820492127575076874888948881174491573576707716667988807435892", + "9095544195644789922175839073929462149959586189940443682544660563790242551416", + "12649782796197868227327830841571149621604197483943737926454728454326532192518", + "16667983042241619901264223140344714132852698906756725392221839503187142280785", + "18383173644675687530390651274503384113459403614974472825953064896216388971884", + "14628141534803375030737634780152921077373561843078668533631869027351905379871", + "4561456211241649459019745200365185624320798675863426759321227045069329801664", + "12289778497980320566229781933786841623485008232338420865642173363101571189872", + "11975067175623680843959076988032062157059134808170430074549557650028254491562", + "13608090049838182253377471459358669610089588259298499702848843266957511619603", + "18788677463812827554544269966162484344784391485047475243938219549074331613206", + "4881667965195655156201239071358064948072146968570883219541497388280721871638", + "16809375763554448903183355905513828131657823301396908506126469252462160193891", + "11654980193951434743713680917141406309927211470940308818720103124789012440740", + "5948986090649283108120495678646398398833638108013243775975352725637369743753", + "13726437662355203524944601292802877111764435687388166013301616156301223567122", + "17457684154769676997584569814978347304006793119360850469355899127743508915640", + "12768218426713967613672155360932690915682228245638743313771591055379281067565", + "18615157783057763780308448635283319685644271696762198401466573335150909781420", + "17036103965935103170115214353570299052411478859579659720326549902160146217969", + "7150547909167136034355368387927463942994048322819447869013206336674786486676", + "2719491185384365067577935615529406253538979948934035375021293885648964670289", + "855254098406697810192507782360318218871612459371212156821698546331701832542", + "17895542168549626634871327801987932137768300191314912757298899767762396172426", + "1291731410872228901975204398582554998148747251553901848273154130903421546804", + "11405841699040163552814729751623598889450140001218648787184675782417123019196", + "20354628821067577915648505449000078769730992376974982139779446148223962828730", + "10728669080369994464716817080074001337835696213713101955141340296929303809038", + "12274798389677182426524706446934567986140214102647161938159083665963012760572", + "9739537323825422719175243872981907235223292084323462340852187679763167385510", + "6755543715589769777862111458854566169790920787355600259792850662088606657716", + "12126305553295538434422174339664597983843351746744739132557070380077267264590", + "21398878068159838390422213444802849205194012142590812651530393856342428295355", + "2774987220129009778448086748836270418387857350731020745311729965596748575420", + "12913355749231079157637607439722332211156795881932799993926379679617800720875", + "14002442102691266344434178456827064608589741346471232083579257521903268988567", + "3222000382376546717389770889292702303294866053055569943089969635516575396692", + "3351662579522271904655802780809448930926900026265684873057471441126697202347", + "8791346592452796050710862947776730913908702488104309880248248201823569029744", + "9103312554290751080966931020955359921020213554904162168782078094097630022273", + "11400762474859869669799163954742952133400547669811496311736883651700759825519", + "21705862854411927719091657128107442151318579935925212397760983799549487960024", + "43293469326476059594440269130071583321324378720711508957751360314337673988", + "15189563636069530033236842376689049504908622054060814965474910283072587672143", + "8828581874201341688220445093730125081012615712886123840297097551587278948881", + "4848334665131642773713411076824550175891079883511516252808850475348208758729", + "12614662329834759430739626588497950629642362637723265950380896878486239042155", + "14266355110863104530316810105072189601641436180167942829728212140901379263956", + "17294822327168915058983104150822364346325840621309612804535067234675456850670", + "455077676048323303580854786776522812365753723694705688462762996845811407009", + "1883008531326217838820507370543781882290544271112198177704053499351851681273", + "4529982458238957976485223202768823175919650810452913863593242825418350872543", + "2955110035783314707155321084983433537364088072013879095266124577362993635626", + "12967684942790110900749491528799008728014372120122130310632754743394775825471", + "2505175694719163834796124853767853797404065196427012487113935936321224863546", + "3664644243909527501109080877083112285282350623935581596506736195569817810680", + "13149615309511991949247749473495035486060198385636687198656459619774102038819", + "11675634552285986901460163850174089221624080337542386824203554725353862698973", + "13792241875469760333807839408609766753472484847219134966123237707758891089388", + "10227619387285566606296112062328719372414665296090743089679008143127205545123", + "12205580044090621048077686897527020579927082420674484835775119696613313405371", + "15208177648579495968812696435767989756568450150931633443711377393141060828911", + "6203604398339912597796330237774861234098507297133105016255969208311447220376", + "8763991852009928642943035844463232737815423121200913107779495834216057423172", + "94997075732443240393502290740081268967228130793200255689370554790238240979", + "5269706528648934838705302424114829386876854870088873864481541735257304941866", + "16262872163060208420029150392453401749302720607531142669546071916932112668551", + "8143677473154806611855981628530143157024134934286519271401846470427527147543", + "11345763412284980808950171535310000445785575947246043879446971501610507015402", + "11334003190684962190461352102674801284421167648666972792173808801060633658405", + "16679481501466111466382494296272414977838924060754574747550155015005991797168", + "8717276046214261706367755700328685033217888501922395351508483646143935055357", + "7961818553714372394804939036958306706260485011393197807672306728909086465660", + "11525513262393719593247460022637862437588876074380005892794471847411833011017", + "606475753731577839300896422047168640216288859417610094202862133247546805011", + "16216814510391154599441870283663624434960433363418315765904453229709017881340", + "15952802892860522124812654660125132147349553268606042022775941410073735145502", + "21186206422036180717988803903908106965745591204944021637150186904241796046981", + "6944713991333056327587649834988524981544213700413795245346955488518554424877", + "10185921515443194095530439914620341794217076478391514698779175769548747359464", + "2855919726997577698604215361496800379220625688194012864111223639042676660976", + "10433229026112136773123800168383892031427061365180473556460194903407247351339", + "19427072191035469870347195569940358526740232693667547123354147821192789102650", + "15644214414239800285411872105206002044880495869535621299007907341206661859610", + "17127893436675976263942308327239908450163069123121279064123261885014993455970", + "14398850115453023364458586202624524741971410970483408578015632196929131043640", + "2352925542671141920909613532344506652984644250240272091365991738093178607788", + "10172679680316429884824619312723045840067903978738893283037233244389430249509", + "5772194565812391639885669321239603226420464592665765672957012371288977540856", + "1472565977275775489403659554782408548523807638115405629626295474159727078930", + "13025952383827165156872004962772766979272981810832459552230024272976389350529", + "17564758020035152957739819749712385631015761969283204715352893305611222727560", + "16405514590762165467156068648660873304776064039873164250553927264160414855875", + "3690038285204558326066250230248548873947936562327070117434442040765096220855", + "17531140782998595396669833094850381400841695523035934903128529012616296113949", + "13168227885444297954910232183764567700890648537512256626187184942939569199780", + "18839532764289221356950914594613087164434882227921649252297932780104554074257", + "15690093187279544523035828189537219904387587938956853550130942856247262425989", + "4880718242480815050016604653617276367751146942554754727539176934739417585000", + "5767740184065294852679113060036925102298292735253600173647375101014669432601", + "15274885987576180935369267696345371924274924872703997373047266442038483500100", + "3761934589481780435501384301483991135823312027596840500386086136996196777919", + "17076314579416028845718050745024649043011795764818118471181221546277962595385", + "15977291426665432194805519962451891500931039499992781917651419845757747435870", + "16614058019242709692964725060926415535596693985378737640811775269245183858531", + "1462290731020299561638622134991867491216642860791478796650694102212515942203", + "15285513431606451603488599544624561225569394909979737124945690710031449859251", + "2108108609418057784389651014871434275919186190456064517027979476451544519949", + "20785453923508177251995968212118965193411372702846000862603422090322205734041", + "10673883530097564269607463521683039178512337022363698173454334902283390457863", + "5454676180617583613153450533396644120109614173104204009158828769649771925347", + "2308424025465899393970422846699273218513861797562089060060302466408498727592", + "7521209677407171946300925747807233725946342430543358547121236461545495788103", + "20573024021944297233730031260403312250074182051596062256738574515403295322803", + "9021049816696497696749267473705619787233696328020950183323376943545740004437", + "1865524799085825018345305851095200435959748376626055576269783687778950437080", + "4246459257342056987998012962024687622955192847494871592608632692951161262129", + "19427245695591302750338395658196139112041609989862338102918570832807123117503", + "21526088318229932910068935959652342185575362039375447361171773752278498069664", + "16191911454644420751852228901282854304136684923356871310294001321086062307024", + "19158667047991453406718020435447139785846629820299132383267410641682384370576", + "17700355947456307421977232728703295781949761612179494776757369350093380226828", + "4397993077875324081650432554749300545148338322818494126385494799541152486594", + "7333345984549245920976934899658584162392463672999208699449239323807521633803", + "16152786000399924760932165522677937364378936868710087711558780050692283598645", + "11094138063712503930108269043784479707229515809275605844595768164840752472377", + "1142389644011176261530925136868504263430131536560167284105696375014595703389", + "7947253178759456244011070491140902256859556283134055577001208685178146652802", + "10134714213573683528928794197545260748403530346308318902760067810116839777206", + "10883490621674448000789965625707254871804535943755669299023415555830424281655", + "3066669045698349285650257964282463204141692000014900401186330770537030238642", + "20081606580658665084706560391544166403289461596409550084066896490552960642479", + "6155695406737593769799569079058291297835886186017961489006277167996716002247", + "8515542419918302650566740947332213674125125387478669670818010011334680546574", + "20110138544602147391149732696810223660265845787019234225223667638537351747051", + "10742562600839811890186721654855708402836531506887842079602293928208078373859", + "1525909842404376057305890458989201366856740974115647536551418424527131666169", + "9234236770964972467234889051483942687447344616202849085905582739078803045160", + "2606008597707158849245186414604531348516676367660138989733829835914698874012", + "11796760152309688912186437926169763667717914107947289133346750725444306914631", + "3851377590989618761627509080069107348568977801140383151662774695134986557597", + "3154796336225549288800236024241376487736097402096695364582935878596928093781", + "5510420220665221034838403251084456079592641926247054056939582862556776533398", + "16879696361470925506825934570612427394067380616879319126544582610342257894977", + "17284649816450637986813517327310560148892948427878914253815654865007623581799", + "2833030338755501226260678429168722114253235114087325082893557705288230185302", + "7251727070132476220556183457711849452579027647540936257172964240637390843337", + "9575600794079237337218029036087316899348403790684983148717343996829930177678", + "5979306342004680208572077566967420789575741134560343288662733429960668391430", + "6933647198431187357226909706624729057428619440859131169201765838043283476077", + "19168763196217781731628598897969332495895458624888700544934409934147951462602", + "19829437720109810470479999873902618673477593710269745617035524777507262876935", + "10801527657674630131387061424571233632050383122132208702068470808513632048030", + "3324631869609728132956796851449123815871495758162118183753705724979320742705", + "15660020223439851550062020388305149722004313049334322631321250103301499692604", + "5967221174170233933880215190156392465136752773253659731223749128915793467895", + "11552412840249358443126681313794466948899653896063684296070673264241626555196", + "18888906666917921069717332466525186303442592625253328171173131193932302764004", + "9002714106167484391528827546180572105064179104525774089102236612700713696717", + "11048302793662652213631623254022096575352405761944396370134943300004028917865", + "16407259962762436968310192538354523615684343650696199890141991953040162326636", + "2164382359471710099213275616027528225843146809226250465653585796261822737346", + "10534211142437421383941598776341383955315039281979396571221999789915575101599", + "10666069653734320813631520107249402109728041265475244772290972049841823847727", + "1065893272480854115563003062650162503958098706206084024581187831004515125777", + "7996693710845330472615496754914137367924796497947601638820266498225398515015", + "779460584637732468426955849206233546264346430125102128427414524469939157403", + "18123487244613769854380966736011971128129435530397043581264858565849669710522", + "16483639590865705054952634993109499660754781472434181022666579985294889582777", + "15847717171578788545161470441065692492419364914649476971042627117577578420712", + "20212509548766945890162316166677458350544092615956421310024681889193656903241", + "11915838540527285091679839305488591026042934314354878896592571753630253407873", + "1911170159637126384084881836969666801716554202345435461162520643252230842951", + "2519681707616189243873692429706701363981726227724944939887394039132311807155", + "10525548620009931418869190498282964825273460634626042574365806552478535675090", + "1007287689974936217568163398662729361983046876939574893360458678319961439943", + "8918601076290071318953673276836220960249911431394510238963515695083601259416", + "1730552670090087588255812224149698933189694835321291448382024953001539943933", + "2692212389857059051251821082045515561682372012597270034725442717100828072430", + "5182752960118030069130081328243349382982053565156074602369672154634481788415", + "4186223293158399083027342405464955373429347225209836842209182753945871255191", + "20698226478040525601636369286446332773903307913544069889209490555501069209140", + "7848961029873906562639009619165977234892385836769337804546327846501965814365", + "13921935366749820642106107225856381492084238464963488385755265898574906919842", + "16615913254044390064369275838835406299949293202833109553557777908673881552518", + "17884323890135880903838623664692905800576770771455839982202688346472867240539", + "7671240599961467753478651569648667266853727017714987691902517051845809028276", + "20540170969725852651721054123169504183251616229771312133024479630289328600435", + "10885911618876068300178014171685162848957603467763239389113505729594133953822", + "1821956738503711086168534921235313501143676686594517809385967874500584768914", + "13900019517803309945097308036151373598859036005433194049333364013307418921490", + "5611521340578197244418404931199594069340846248086844299279812110836336410894", + "3871676825880602831129014665958446397148564007925285835033897270539554583982", + "8660883522091722908446182204212531338997991855689061026827047565345925404852", + "21369377758054514487929500216341215576252815582773920801000959327560535036910", + "2412441365534353741053138780929948381707801794421791967644299920632399699760", + "3962365750665624098182798369136001622041855947680873409890054621414139629589", + "15001230629104674518532645948322038846835064889071294141298776340346988584355", + "14880674002301694876681052915629907300219131310128131182663986425102259721980", + "1824158054562585980316309768551123828629842232225429989383412402628330419987", + "2859240361399565691265380107352020662064288346296545444794991049234114013603", + "5166447910779952920543762328108096228333459621541947567522578212254733957716", + "1007490453560275100906582245944853446313521002614529198832736321283620962455", + "12357031909288499920427512282658173574064133416008366625033457013843709684703", + "4908601693798278768903637729333835430381717019653632816739154645536641464365", + "3707521788266753982537350852704241176639571081938875735935799519005785641932", + "843648473424733414090549470297108007717585086811320449394192229155836735429", + "15745469357122732280411707850429686485179261034785562466267026969795211925342", + "10805521614324416411410037641243182724170403739698507241460822321812492198235", + "12065996971764501469604703952781437881524721789450462435182323617647626062291", + "18470487586372818033539023969236693307855263683332928699228943202064060965843", + "9678881733477238994400069917737562227773718359505757479334487914298656155782", + "14188770590313091787681039051598250350716760439974403531322107017294575035366", + "1689506610383677575502055715917181866056070036146379850885318902788859831848", + "7506643906911078844726412866492372822643011138706243954480387034094577464841", + "8878900588521598109866546176968107824348718386295453778616479348498221694856", + "6870283245627392817090238076492510507322368840526195161203457308149059064709", + "13770891113056770539767147807436112954402870897635701688432832537357791153049", + "8780360192411651104527843471573635887257900701816701590232913021169255451999", + "16171874311798197525909736376097249943669070404511356154621097034937309906406", + "5735048971584791925371366175024708991668126697294479025115320761565490442773", + "13229825087263355390945471875664433936256658682181329545626347347763439801377", + "7075776235062840728475772028477434335481293337646194082436681309306082067951", + "3621624535897102185304108550400513064448572525424419762329290603569136345699", + "8909048763816678806291702062491546272181098732106818908949274412295033032278", + "5679617488269393681126385387791795896201443046828191787132461584904242222704", + "10105279386430545635207898937992374339634464824905552041741267814807859103503", + "1875089455725008938828188584567435691926878726057441578850433616115151436992", + "7477746898926143788312536189451854889707733370802231032871868774692083118085", + "16386910598448767875426039948412876247096832715449382492730559004257126956048", + "6033432876275593677037331179852069653085950234927263672572175689211500790655", + "791047936106933006999775822964245334290371384592876255418502363688874468612", + "3694863804142244539430013086697918876101028434198177049748051320963196192691", + "13376682105243214738309576815193178135837123827716063891638620885853287922296", + "13805679067029770984721773402288418271927678860935425166689344094492548637057", + "17136756160106085935819446871311843510549583818138636089816199715569134828084", + "8109923439508142364330425377154151984256104223553589804214354752109994937475", + "11286249512402084538299269911568404171399278286810916548813396237061620605376", + "2584359677745194465235893977254941998996461095474372764450843406506578038395", + "416048929518775256195106483480946138344969950953407172345234478269849793039", + "13382468529403508085121262401866099849753365548098731797553497976873204205709", + "18439376867491626480684697800985907843592103465829293677253579863338320195923", + "17849262151524731521717998935547978553691621192854239431428238124396600612632", + "14944765584636478787586231666077419063063746476683640145827104348311750508175", + "5770158920816863002535760768793204546036568907423666489027939717423021627834", + "8964766646616906071571735802649575322546984330618572612076303377141821583837", + "12741361066585979303854420163873489016934487106922559130204774618881492938249", + "17155650977068989655844853330647332708031817408933941319367040661444377002549", + "15853135222927269109853709968587618568449580274775781838704992776508260190624", + "18643405831130205120436044797258461126994691116505008303058249039104682298143", + "18381699905134802759607923737176411134148483921733890751508148644565049938661", + "20771413818150322305462626795715653983446198751749952090107489229813229842550", + "6565230743597462418756949526565014729968376635326046194605417268756666319845", + "7773589221172310936780704579925742277511016906417542067089964629576305572209", + "14322739515259154048217383149571029180760938501429981700631245079741667837826", + "2211777926093208827662641428669122029599076421656786665814449320380720581777", + "11959359909854207341226535525210590494408522984553921513181278512110987087969", + "15631964544474412103208297614910610111786652867610037534463100143218625118186", + "6469947677147265061709088012775208497425320831040679910475868641704170242154", + "14273192286218513903657400373354426033843730885130815917604222619132646117025", + "1642305946822726119533692746690241550779067996073465721872854402290979482744", + "12434472654594642340204970901697561562492434882039006954954548364905225683590", + "18258333676501263903599077497799178704676070138517903780843551776313851126197", + "3154393734777301102615783657097778679162307608952237328943250741895135803910", + "21442924273325571860480847088112128477994226721014369494483203254968684948447", + "12633492209679682779165226815843032818326050497846354378961630197925565487104", + "5676035885913185017175294353150288993325590895201150557890586619719458353554", + "7971989338965840486933559372839713095797443968865735128939127037392334228821", + "21734085994605079916025200907658739534755548178328895704833631044593482792710", + "2240615781815075893018901266381250882106533308854256044823388761533658366337", + "19418757086351270137870941178188715468785231202435740939860626745964359442005", + "17658978545567285097910442499706845586224912649469751675828983048696346031860", + "5266889563561183172939683250833485521316204021724495164611029326138684723583", + "11128996685041402949333841527417312168812141699419947558499636572258705639448", + "4876070090528783965375514438749842173086453529590096369168455769565070437109", + "21181864929467364713132566956143162901336986129207766576689052262132856884478", + "6923705508410326350945166370499325965219890886201185955752583121521122421736", + "21369826512764035509040463750392604795868027299873193291279213169276615858803", + "4704097774288345414783119239472343512761071550401447208590340029300816575296", + "19335571041256357691347835994830129551703141581491004773597606130380555930148", + "13594634273515260829141229000646270213542265375428591690952060475706589483636", + "9015254066149037983117828022107604427546915195306105494160093361353622246415", + "4212072016320343773172520900223699632828574585381041109390033407447488634385", + "1102272748190005338904701274717935307889530536456982005034282115032583335111", + "2736300324710728232038909644774832551915915663932530690557461126955645530086", + "4835822801818378290852301048163831283974282641105089345434084063764482095069", + "8984658187927452678459930452346646248663512083082894198317485712426204609811", + "16633394668571656740012069555016467267297836103775110968049070854163114237249", + "19001311544890254637467757524866604779430904938238046630395635640108752440416", + "1519708912837962563938791348422981399413816048437970967955664087668434079252", + "21331841844372791543710397499630585987940452814579176990050363535517182938392", + "8907423080053260966677751803467685389883909962381980865910698188903614425511", + "20461083176034684313938738010980183835826228331514122093950995340802162488751", + "5841016603634386755712428070725428370463826644379529834480138063613116133165", + "6169966163271811065481708286709332934104602048421028222790926506913444959185", + "11123503881380576416779399092503011764979780788355198614717104650873109848686", + "5910862578643213547409014086432565110080048906429291885019036113399453987536", + "15326247930625028665035962093121096584742207235393215425729927190450227004180", + "11869108106148533163877422279522558694860624980410176225979111384418155447592", + "11642189959177792725300751489041811935498998846917246538427709581655066866054", + "17383982459896791073091103411713034950019457760840905433100761135071197717377", + "12914884881288305014413720371780941499071473169305332026567402160980899728234", + "1208570539962036907751277903184876797538221568150857585830629441131315144451", + "2323312703274674937703540014228631818729280611690694743543061204714949346767", + "17526158529185414889134597474769916490357130956125485429641429836800727655323", + "16037781653504828212828867735133494813590828457250361954137565662868257318996", + "5407222038113928707955890035984954296613370389143420455278606330416003035740", + "6601323218218927237946555844476405370566012808107313139506360075146521702852", + "21534867504549849931394770956914633425223988570769925897030241609788158519056", + "11391084204734238133980784274225569005339670690396227409155789097373857915450", + "19888681657658973285687297761078939800531741094128408699392512189997402853670", + "5332577406232753436405602237019361733171667669604896195130680826397119398314", + "9059600173937645065621009092166260440988612068018047518892978276246308722168", + "5301647598491778367767843695092267888185214608095464352349739315757388259345", + "15071875841892141860455823800612444188659593796102096590428526661356131692597", + "435778089283152858387915635050172265804266241934673566488879021420875384052", + "7253178577349028822394834490127746885621571949621301778647260365262978601526", + "1165301643600280009322317413038719889346104283514223548696059636052018656756", + "19642265897347119192967952847445594557926149874981543320111550169192389374838", + "18546070171142820328487316603855788669722411491547154393382552167546945234264", + "7547722208697813989490512139624144757215998986985181672151192221427497361833", + "21765466939205480830726051696775103538365853268148356693813986094780619132689", + "18066916718526031105430041386116904278122614509807635157495047212689329422097", + "5567247965934318360602471432895130358745239227464728512128895794990091560111", + "5268155242325913085615961568740884974081158441192412337454790453319219641639", + "15275184776514682919662155241139211655693258637032715569152004434925162363584", + "2179932316974455074499483491372661376888522799119184614149917286749483028849", + "4621405833740121725678441855520519041228014222881228746009997774516764304847", + "18479339496682302897017710580780845640172065272400836860950352488372424120096", + "18557033214894447033261016367420154444088029564584596463030003672186270737859", + "17200647521217215683722635843299601233863161230648748208816378327209201596867", + "2688200399163263049122461117289599907708945179806531635658550456745433215543", + "17415923022624960613510570384321832458760537169605919976471719634183381965151", + "8535568676654346647569376193583594529721160524940659464514765031328360321359", + "2988350233499987577118772810582267364198360724494195915351933486792072227259", + "6210620559937378902878629998722853418159056595764204463986805098114518009318", + "18310997638100731876764014221687199913435941820864752235028122857262977059320", + "13808863093029019329144013239638801163784179422135484562563156294580677444841", + "12914098780625672320372746314998075565042263956380764233047047668841824625393", + "3305953095296069725576430940963322680842934544212876613078125492458854044556", + "17822002370183286453254419050088898676184956527990121400547957986510775899261", + "14649614548507754711891632411080554775651572143141206447218484052001553765684", + "8109105787614676314388565737575005920261787167569229710081822989465469152070", + "10883787415840547710621559198296269932318487849486120162467244238276174398529", + "19330421887539219495728995383446553835046303504116353064676615752269857951508", + "10124601385542856860656799235536724683363368315551234368605365576161456958605", + "12586681054057195636996904714881904902720157016325364273875514276164279959735", + "3757165144866249284026269096089043031459271594379869902321741804365836799544", + "462405354647627586345675602760959074379157140949064478801734872040710235207", + "1920260561025181767428737283255569518557618670101985233779064285550376374001", + "16427781514757709627041602025461703818581117100125480080881962557048804215419", + "11285710669637766333985664845741699850953316015510407645702772350620723934913", + "12651159368793078896654108663982020308442449053255003548814357252262150375808", + "5416603170144910655254279621683623582654125103494176617805338836184837812634", + "11735378404903665808621855768545179642393017572035737886573368447383433091210", + "12201557541429330882554855185927797569372240536806738921957792139394552229540", + "12315234857861194987612137097457466405047598720463755079786381945885951345726", + ], + vec![ + "3312280834382673867321630616941760639861515464094877629805120494360011490649", + "2977163727414618213643725802224710174200189681501907689708278449275625624600", + "17077799405481633745546679084486353025600250694578521370656758513725453934742", + "16210306379465933080277173890273457210762404430253284947889895341785601089390", + "12306944281360832043572721821716739944034354411349456264300159795863030116042", + "18510116198433364516004461541873904955505055486081639325191415467980353992665", + "20171004681243290383397478639749648403640483231061520437292129889020927831789", + "14216817867363924461443287436236130110420963767734530602158091056747850914504", + "1744244489393376249430799522637230427855625055124494576876957852023791759325", + "7129585843913921821110812028399979173266826966429993304814409727296136874103", + "13988404968409685403326663187671698975733642730286219911002602918437679901860", + "16920638563061193407835052036305459051417920006020130846290964059193276218943", + "13427470979587753937642822251845898994765547507421045262408080469011197338416", + "4951343674183369875263494313821909834781608226183187917185914650592996842297", + "8735432128376864913532575297691608527208524295369583136876379560735670033436", + "11506191123509320764309490571835590914265043835723578099434891464809664894670", + "11860937155601787389575216601739962955686948385964703064981923542694893487713", + "10828014814384016644406621862814561467981545748254521286078083584574739936124", + "4776443388323724363690349417451836132533385226194261923508005690866227183177", + "16639991477904781874568583121789695884424899367435699700192357030587346907362", + "63646460855616590932366082663720069120195784636826940928051354727568777173", + "13872046592464170980392921000435473279488582870563366687909512908597101541579", + "2903974073963148433036990643522953333905364588702098342158750794553468542208", + "13972003294927197976860685316247379324920372226280483226377669417062686947906", + "13868504415208879955736997036981136359599594082926486546672963310629609974499", + "8628454286960990606041697926325623221475249983044062173729208432417417750989", + "11883151436851199698252725037318235080760967011947670552161788839027156740653", + "12343286145222861673187914724623142745362406729785896500578210646320713733295", + "13240444180513188371213070455300718988708567038226911680564203350475184135088", + "17225520781620743741263824011599257737446498734177523099795883619924078366351", + "19655364901125310778629982245392767297984820167114789563931170716695750865678", + "10695212283696096281132213692300627346215324066996398339089365835910730561368", + "17455827037964560948521638828961298975187156274841334841074216120311558804710", + "4382628454630801450077593442523632674983545561122254229775876033334437860489", + "2989679808908579687765205532943224072820070594304724633625888454150084631070", + "11805110513334566809098991762079653200819786354513225301120697798603509678793", + "11155601993809199490094826448585668454159517005968097566996288487138255635079", + "20815808565222781796761554019974947099963702926722486604443826341123930622896", + "13761986205307878615460321377264044874010177269812603883540272922422883198979", + "4128370443753246025606114169149035926146394950057754901868026654335917771101", + "9124459754124711043815747649012164723935008821275709951889811488049411892152", + "9090302853448501945809598864330384875455576758214379286451409039855851539841", + "2611158043123761410817152601927516372222739024190920897371539155273224585905", + "7810155525349201315441227323507184944737106980192978514457375337581213081055", + "15249792920950763850993517571920601821568311809174912509031437118050063777525", + "9921562618684201533699293485620188871082795988970107962425978745591461148033", + "5414565587238798987874533039507833069864773781235217517734130524969939419113", + "11085088558016600803149927568829483866020077054827488081829062938808919799801", + "14625818170426402577283649455001357862456467374537021301451880577607106726350", + "11633423263664104506250995123678976351167359685182986641330218055115412525059", + "850899676511673590463501492472742017261347013078251845952824541653369171366", + "1151196396804070641219917031443763313740170846357000808196046482153806050391", + "14335865562369989392722415692597018596062508514718062574213639729550753584518", + "16165235837546690396792041022833296046736592940683337326949013823518030323769", + "6408098377682813619850142098164780329355428625834955152400958576961120931380", + "21423078154100258346688828020904418548554497199114189102725137286930868022538", + "21410217312460027364452766052404349950133415202888862471159726884511205456308", + "11096458349387432633781125315606380225216758142658673665437375204143306261827", + "8325016245207932555950059013310928498109578689420634828690059159861280125058", + "19120379171572846193451984132488836287003157955913585086569467602620121544962", + "15548527549295346334479152412121638073047322389003226521019746206475690857238", + "18510388922178653264949279980605433471616208226685385338922150015851847524587", + "10348792093961922144290405029634579505420677504732581441616360506905620826544", + "15620530873228503134208333316448182712901874887467615537478242394290577386453", + "20675095206674416295505702734315255961699360732886837937979451051773488542601", + "21623072135556656816074223163769126264910877148099315666172910436076676028248", + "20081866552983231525843337497912436927329172443916196026679813450860013966767", + "18951602909834406133815764463249536936478826139929746486734639774975812472830", + "2378298801465585253495854322066504889300467395616155363977621341822205588163", + "7488881447347091058309495011922138185162376888083687541108039236558708660027", + "7225960736862847948475548065508931552528126330579521291190635548005260333390", + "2970482373109443685421063487292183827150496377598629996362474558885993864176", + "21734396161383902760672518999845141937065773419899625136190973118533490737305", + "18986003927424880427453510819113519633513383085919786483726156674699889468820", + "9438591792749742425198760386567115998731905494024392289196006582112950891516", + "1973470346155075248881651300830631935042830364217214073906277058522882105581", + "13791817954605171888781711015587425735543149752618332811612012047724803625120", + "19907506629242812934309078271817109951335091294976452370249234742626462372763", + "7913144809845970358468253448033359382532594356254540499933138806226450398795", + "18786719915196826164016145948631708915544945593653634575689240810328854731069", + "6216910690440344513687669938632060553287695043116654216600911463719413604341", + "13963849909448408572632889978998938642084255012365277702934245702305832007005", + "15044749345099947962217476120263824356898437745229321420257770262954985403569", + "16364542436173489908162544844694842746117292595057249540926274681176755002520", + "18582462045999492294572047486602601352613220856614634390669602502489215736186", + "17106926147340558311732938581031578597532846523271406141897942990261560966411", + "21322204968737434192679865858477095835509790782891920152044718985713652659654", + "5180799590809942717590072710973007480225145652031514943521639316776384894144", + "17327427769240537767056224186269899694170666500703858813203303301835294225360", + "12795226231932513901983143961810913124288321956641191561823103283353203953207", + "21372571405151778511626450096485639101933984498294796899325401511071517993005", + "17332300051629640554924347563178488077648079908152291094969979591786473880576", + "15196566643676483159109284476812079407469293035334983717971092729005139246767", + "11231435533605861369104078150337976075098864909987541459896164149690464379912", + "2542242881481757350297627187544723135156543853134475677976341602496676452300", + "8414397814915895101029770303847330205837773505982623383796193602695811122514", + "20884120591077768210550361293303217187539921930869577755703150175746557517590", + "890887689926165781226591337399563217015154445631688026491595317246205069769", + "7911456647213080390137926613780820211986390960677915664339039845274033704843", + "8086506254215085366863905182926378819432294332631815038288539116592866850975", + "17073560037383161517623747613125771654997798954263090273202401066518468858416", + "7837662874931236958961077774488634261606966280628598881837875736120392183663", + "1858593536177571875355498014960393860692560832490490994119420708212532861823", + "2029815832061982114925482969267067531992443107339599745974909462412297854269", + "9638056659007828434670080110322180928336402529095853481855440674860675897358", + "12340090961747329883452841216385303705169146011720720105809463143491658552866", + "8529510825845236935037301291679257193837435275552609577171369718448324660013", + "4191225223853834143599110761318464286196261585898512902425773974473427757456", + "21023319679135760820789157052868478773370737850886002789313940767424256194356", + "2142526213326613906831098262446296658577569180284637200640763867788179991570", + "4585042017455545746057351957720948202590802829528217263388726369695890670411", + "10010708638531752335740573636867640657039858389864446260449096531856416238708", + "12350783923218275606957028867683130968505845112714635024839732952215498877130", + "11964414264447046767815837108295373861475122940092369903025175128013924386713", + "18615506936496648840383399989920850970951406181285583088268789582149764054418", + "14310480127014971068920522498055725465578544829224822580493909725079522528728", + "19209887743481507043830894286806384055543596834772181434542567255388724841969", + "9558553005299269735641084020216560749888705968385627813150807919487080187993", + "16359179198438658598155755638987769554841536772865629517841526255130745409879", + "13009386603860183254204895522650012288119732961876401784040013994677688723325", + "16165167702867558446013999607792716532235725666199142971707303433269257270111", + "9526255138490973975321568846030367381138407886665647949792126204681244064012", + "11173959697998471600463637717719289778917744248391051771189332133876606070906", + "8924806383303834750473706479936998674798746387089627706877519918521882863256", + "12086306023907343271920056592137012844278234154616893254064962297944646888123", + "20523426725749375175935122656912302902564898343946638208821839160006976692150", + "5207650950010803883388912523741229153368067016192964642079270816449299041225", + "1323145474328028634780912405048390126320417262412962953050466352509016682042", + "15985641921260285694054233699922160344414776687043687582488491933807565444789", + "13031771899737217701535545098380455304311851135903656399978477292561795000214", + "12687226379083740035799525440904048416465917656850081330730343990236073218003", + "5180214195408850700722613770944601169370584228844657891855699394648642429923", + "1934847650429153525882808430984088614158092322287601778579498068360011630130", + "13221691213669397834454903625729859574410480120034103769424898865047475910400", + "7358428584159841472154153892839275459119229492180053472830555381908577651936", + "10890590867941343184378544765218808644776443230231834463183835780142311216436", + "18106794932751537043075991633023418950145862470753313409722049187095789146702", + "20653121417027117499755994750040261936029675942959877982604976723194002882577", + "14917693547715740204091617501230563717468258023142717832164274356453628117609", + "12588115399854852983923905079011727575933343603816172388719899888494451203866", + "8737187804839661132607601320524529444256555649101505512380882940221377341037", + "20181178129036534248081631452735124104458169744675071995191727629040780050092", + "11847774883596070919125373409969812806188814542338127843683622869859801757028", + "11792352762436629909341171173296810156800556812208317414203786807634354583560", + "5039046929001603921830951923781308597413960825857893817665425268295870771062", + "12894400705986579725245788498699203221654445738734999525700614560206785491732", + "791109735149055364851227398853119460170782218988367975658618461497391330160", + "9962476109282060862882002156903460806939219308562214446822701651882476052460", + "1691961422009775486402337895262889415967004356867228407841177856000156435254", + "13137802539241815005932694907183711832661205367415214501122657136334369092290", + "14887252261014844625921142022701177088174316290502741584519763408145964517832", + "4710184813279024135486906523528903891606006797755655291767343324947631364837", + "3801875732725114698561125696836343357073399124260852478317754268545097096329", + "9198233237012512530002016044631468704269680036230256629514440524866699707396", + "19696310211066978020836317292330659233588020682368119069383469865122133603780", + "3893729283153055478198590025767324126269879907832064182575052081004216821718", + "17970322286541481482483545514302125656124172449128411566036569217140609597331", + "3173691464765770821621367532348960211199783691357143005417355252781889173280", + "2243542900289123996173306626857758207765925487218044091235839910321809834188", + "9813067765525696381929832954440764980538359181380263390331469145209686852182", + "7510903347384066721813722580189668539045635640361656120910145998846374499699", + "6798424040308056706713925899327404372610976624998869962003671422212921682045", + "13151104198292309578579089832681402880200764912297978086539824832412224275732", + "2808835590734075710953411057250272995919027932824027538775542577464926767888", + "12588027297759258617444062604210692398005597744782878589643963326768133974361", + "20852428689501418456174033597851660248293428726014401905473174242849532966301", + "21296758342898944987847487225137782473520558597036376704831839057822676021941", + "17749269130031982625604134600446353874123371420684509963863640790293640598822", + "12696062143950532198094822890688877675740383270827145944853466311043725527586", + "10234631072965977425954576972633890272283449053659669736631819751429358646105", + "15090656934406651728161310654614148547687401766770336347796859883364028754099", + "20297283938807526130287519089364625872430415084303524848853727877999978340129", + "4196012712072569404315829685460688625941300450108222372646584487742105064258", + "7048902004425912498834833883033670218379565315927717075817051690008258707008", + "10997088676112427186865690409423506551772833502185768664867716439827194601092", + "16660341545280246485425051709291678711850828822636537086457121822086705580853", + "7507040282824500274626435275567162035280839409294222666356976034789594111255", + "16582934771732736721752353176711248084060824111646755305570354937871610952840", + "8438441158635690733311342509710555256926785449251473129940736660330253698675", + "5997879363655967621652122271982901641685668225243330913415373444659935275798", + "377781155818540738714095913828188470350179572448699653767572496979822660266", + "21256042944489939677834126729197446597005830228623173527229465226360970832340", + "13462424045682641929202812263623136219335301688716109209361496824976128063539", + "16369985325316675741289392258637854017776166143141100299636544614904713590628", + "19352389303901189710313048379405187204819083083443266317372096723889889111665", + "10276552469336314142974101263431204498273961662600910736694134016860746072245", + "18939965576088516958025629296747895354615121399053610843001474455787438484084", + "16990956322664851054201977827659189601868639970054898553161570861097544812211", + "9274107214677498542007885890977699632511964198928324688228114708372115109190", + "19938102848869576488504727710754065647382136279175127752338128473551869244856", + "10947279549049485525804912961584261533182913140524900494781425525696586998449", + "17561385783620224695956276284426542193322408499898012489406312644517964425011", + "18549237431132768472559432396178388563409301393950605704070488347475233640622", + "4658944912168763919889884489643908669977608714692544551960581141528777169667", + "14358732577825715965736448712278683090180078342840106686279626250756225140484", + "12281258853616996819181958497578187729715864708853144319900642420503103456870", + "4069586715276899433893814672543891090785738315323966746105563017207510306537", + "15392338154382717986608388646339008932890583126043490476751054331156284554276", + "4517279215584833397018875849825808766631742728356316383241841280072723947253", + "11495036315995422771228762992950108862694073551665420165823433137577472792783", + "3488400840286926839516544735442894876148193865245875295688572119503866691461", + "4483680161275568275540790250446502401130629200290761781427443716038558729253", + "3942379126490099533582685851970104926928015397351808560877929535138811915511", + "11401623410099323944960285754389435394954200879740972352984594598244147798394", + "1704354226179051409424294551695786920602664449209459493691051505451583669264", + "6126483205301561395856828080510799275633402498512671992649940369928504550860", + "13606061849345999397793916269931170782757704442972401347887622528829626440142", + "15164492401059698496285802476110470458129619713288710918549993367398699891422", + "3351459252264231792942138342057542378418534651418845818443384064630601195925", + "2825813294993932054460723098369264495005871082754465730530978747926493034970", + "10630213694333677464069113775948160876773011480314305970870772509710784295967", + "17166444840808098079862920467594387590120730690478215313452389257289409232274", + "15584725536763467888732670366582920601197464509911998898151204840990765872853", + "3842723796922381697995350228115144219456021393259361082360164730224542750465", + "4831891679443026612628331848829793784542170442278947325598774522135713804734", + "7600866468317146506151393588648544489445105617254544624521183070936649742997", + "10583687645778325936823793169279337717905917984785560251868239070559362307278", + "6634607933824414124502549710741812867975263049249231353927669720791801705509", + "21200609344869125199593456019818101515444150712862469998882355110348140005358", + "14361714336789127058022203168794208348007919167174968797533361168719704534209", + "1575806469795254200103212820564180016945577757735332906646132503588215912546", + "10628306906313535336091114056786842183955113155572070406112143573768388478883", + "3089301712473516586340147971264840334080312453733093398291612940108596635146", + "8645795529068430110488497380845362622141451911891312096721911200350500642213", + "16022924422796267113820566797422451700895182144910797483992348492987850640540", + "21668559624091676943635408329170173151410363888407430983622558174006481336306", + "2072754212505196807037151376956574080111414803663052483852517767215769333105", + "344312312360359816789223515659978323664333965848417356296539814750513103748", + "8220091259162471818305840126401210949950597324344081712931466340258084434570", + "8081153019090920515448830088459971939645826679918559179389404338475619307845", + "21449241241855558649730447089457892378091011965030006823417837265982774602267", + "6067510299282418337933484892144351034696356612154920492037935939636907603847", + "21026143172839641048848076059364862575379425612028621103404211601411048352556", + "6070438567473697541030303531314185338530006003062193748841965554522662407077", + "8255130291759907832990229603685682560848952190335394603030993899908706198932", + "9546680581091867708464426246566048171085730057856647964775027268282058986619", + "19031748019605060953238957886441808197649034817040423262874632275713470713752", + "17096522586030137478189913833428299115382271406265963567569748424828047092166", + "14391874539708946761001075594593462740437739522325069415219274013650732876769", + "5044944855410335767979170189040984719402886766879289090117403978287180516155", + "12039442478120524220891851909305716836138223152615593874775855237995395028479", + "3523930243951081542915282337519662387190599423550808129910485998252637662407", + "21645562630446380089971004272761942543158290220498842219097308729146973947053", + "9635476273420949200636403482900193969961822409725076345852310152023632001652", + "20108896868320980092863545615065555118659548096203643467891646750271137801454", + "9185755445384903374596265352827852278482253313517817304360514098507824628136", + "15729448650969486701474329849747687235995473855623732466626603841221282908291", + "17997496197003273211403721001065741564292124821674411166555153239941325690779", + "1543484160386036195130212436149054066156454121707864502325718576708010327712", + "16363634132599571248968010449034783328746466576733841612125967259037812456762", + "11430550344567145066773020651025090506980499195413761807852670685184554749682", + "6562474642758509619875130343887830140546824045253242049540363976665708088792", + "3689788469446976140319903688077885060368720863836266384022598571957269060684", + "946161443553297471232017467760713330784613638346698353392670707850882897488", + "17775152994890017803033120420023910950366134526347402563464114996856742054838", + "18013595561755224808896799093133992096629438550898908281663802197342115179235", + "19550347733494203322271272376490635125389454301454207200256111665786140278650", + "12387591141491143807678990173849961323772837586376210707337202980616282598236", + "15615445216195972598868146032329101929277001754848917412471279643674750521844", + "11157377570334948360516367332475698893237579888172217677485190937247699033770", + "7130958205578403403951515578418204249480908544151977781217247531796937245530", + "13077463747714835214281685128657845926832593706922024570198457201391820941874", + "20874866618752897980777992983172936086725928829995747401713185587049763764007", + "9529341163966534350579350780474173060797076273832542721772111561732792505333", + "11142469391388881243303156644691476683457853027534314004453288809998298235798", + "15759641247899199119184149872340402267600476479959598970774176792522105838715", + "4932052011278518183097054145820640615211087138945458940865038794988474891006", + "10432003521315545222242540576886148188507417318972677351911763831484066879233", + "17415623007807480250676335953220588929877876653555913668269587494499596716638", + "6058502442301803313347169632367675883843035525640290470399543491756905404121", + "4937981332834016148179351587580087161307555148093009345111656135565068163315", + "4767276541447291442260238717093680167532207332378535289044385385412677713720", + "3479278227276561004751092786681066281912177752486503430342235086646969267132", + "7943278285140068849348170498689330975011840215116541312829451500334428180149", + "17789865444817191695636689430256327163274376252861234888599341795716694863906", + "11313788642745794682263060233193097297009786646629777858110154245289610960322", + "15451318802981646436556355654707114069777551322432702430595933898879175343955", + "7517270022604650172540378758621070031306040808100514057036893386092388842581", + "10009597627594120061689516706238257978394705225993629762944338270984243419952", + "16287228641302237661911798504390175297327206600486567815634358152959586818336", + "16488249714776419229986970659608164306511103745532215897060984940903927940510", + "13354728687549843942265910857129412843774195483459170542959107134210371522923", + "5514821690255816781301952402733589181643174815400146100135235253605240813486", + "5770103106430309708547950317514210165673407448643264387718101428086149468878", + "2694023365380190286709306630457152471177911482921639231487592222438630988204", + "2596360308486910596331175398746479488324950265788021808703834738076552537175", + "9039714509260386661160667890165090643713802650277924766536703740888044388658", + "9604908742305952316295642934650340892998619318487917447839433792965436657411", + "10397877232904834686750242142278095498016920001087513253065011131758634311960", + "9647356378465005298504489726754445992380752731297711041297431711301119823740", + "2901124847048448064485279495316677205686659456282057720583010224075223188929", + "19604711253175713682339631659824057587938105276478055043815802354580551412854", + "13814073762260094871340458282144499467492241477826265605381954643255652812563", + "19191277082146641271114112092268339387374938745710902056016346575725741574791", + "4351438127825195342433511764688522108908810411430938576079426214421798123584", + "13791103210113222833666019051739255444684344392875106989928046003279497788407", + "21004705702783423413965111534445421935934565517891538415275359891592731977145", + "2271618466337144497382665490697115072125990892802659816927722507215828359501", + "17558902460062424775395394774553055038697408789830072471787389990226026812332", + "19541727150539905151936633341362726755459275099586077305409909896489919664394", + "13692791587272861333747931194991249996571271400946738920492512367046518055252", + "16261859926552105019169611795692937520291616941485096436900016176047007140288", + "13364750885171681175943985569408970262762845797040643237076315743322239541667", + "13621972138767941419375174302334978784396954063748685093059444353200957635883", + "7816366958996751828525267034982378444982423551198678280151852851213963203655", + "5387588357364980194077356588147257085032575072648295273044945567343418708153", + "5561769804258805723102666784326040807434856561620360208220095378441903055863", + "2759015372384926293713393955452664141507032820282479368223024343744582995438", + "13981134543106614704319578938205751472742054796552637642168049339888724312989", + "7990257879146478669584929211133379459065259065980981194790047745364505663434", + "11876127927930413842997415069288338406075999315865409152476435081888707100248", + "16570551097531678234333612414250188586376313376162262758608423816369347322204", + "2912142129158840181609698148893520848427743435752721748118484524307819669362", + "21302711682094568025057587941584825132522581272692339045586256377620698610739", + "12312191496853376716581371884132892074646637957572159612619070514615509157537", + "6766172753918389200603997763713298721859536050788836585740520353915599111166", + "11495484070470791342676704007304510997655718568304345126153023374072812971624", + "20811664890504244597768955277493527673608205732118233091518542968150115526854", + "16063703627762167667966504135478113457485558849729490090362731172389730931393", + "14866560644094965611504947304610358591133796414154206860995591970456047599477", + "14070826318654672995642970624813758954945583937085129314124463556918282721543", + "2479865882631638665546581282869746245766113042059506929860680221143260292935", + "17398990555602339276684995684280302899074008602689308101178965669933564447317", + "608876042484390223586509032355843816374709085426455763238940304845396291392", + "19093816648588316664468783915406437520968219066632631005915358745999525844761", + "6752057404464894771427129594636749868828376343179986455821808045648379935245", + "19888182199106435309043037013966294783272588579928340631894231065863897158437", + "10082296017960550499269875102368612265371627577709879253045116718197037435449", + "14616172738414901831631177911088055616005574740810051337096702495520738285374", + "14075121030661423148825920501776601474873747801291899275268094618775991305078", + "186569155903133115291205664388893859453551519692185893083420327670792162388", + "21299949831814715974370318270705239305629929085336938013302553408219032200083", + "10073139629403946009284887013173459030563893496973388848686052378045073830551", + "1412198475257996126786641438141729921783447169582336707894995512036569315500", + "7779964092821851708726534513875342218194584080198381624983100710844846404634", + "948257338952437735532120031007610523249359469819114853137850805544224196883", + "21258332661476723168534084283213459804574003007208501208541743948207937371185", + "3571248067320885077052671052657329120832155265049255115581589211873377624342", + "18767668432875868101830472546043294158876614861521938962321471373462978174897", + "9300839292250613795491408219504842564421473000721822643979051375966405606193", + "7064697496603343927134791624768930499810256237088252983584462738613126284623", + "11853953981626198865163123682396255427116155876021473995289924391523536239496", + "19771876990095842377875081223218001588478929046615599374496234715810020326293", + "14316898881193279100266501073998404340884941240727243537292505454856307190130", + "14519359582636080510571261582400983934670011637905562203617314435861115398211", + "15526562931305845620715599917553561556373761549853248996847605985788604583130", + "13078262487107679022841832022599167182597313495640194703513299650288211974881", + "8245559318515118189428376002847388656528865763199194533482833563489439004385", + "14003329258302578586391126522614048791257224051921678844212914592624869865893", + "11725000422609501650997806599141176528384787601555518111375908215801780233121", + "16350891150329350826705554113837971740496662498788954571303936439413078899269", + "6411076187467760129645481080704474318810486795627076355267125729971512535475", + "21018151777353913573383620107187764987866574825379316352340980146357643078382", + "11358423056150266238825561031091840555431271536659827607676154506546242135862", + "19065833030377987569441381994102534825367123386452612836837050555888073644388", + "2562667974917478689245733861669928078902565222389031120733094553006681886960", + "3471326938285931978123873023764646019622642761349433536231294291602304868485", + "2563009390241284210001442885151389460822558817932742264596703331331069807625", + "1840397005312580276560341795534676176103474806443827954038672349702326279641", + "13608853165319143662474434916965520937512197511217746962619929048696566631710", + "19629513285164329969551640449451527909298649847319347893901982383162686335860", + "15394390010333989214892827255905093646577605209323130947826658910240751517983", + "12111906652703403073758441298171362667165880553934054594322507856217299245492", + "4297972575899320549167090598678621826992022863628750628394635867928789323371", + "13452780308396674980210210234355016032880538147426066947029982271570560372078", + "2831837563806484576977992168295535809035697292555489411927347217736327891352", + "16435334356674630023459784576727565801947528558756855054741604645713144334059", + "4127342592978010203327455190780821692230702238156658795505993209368069797198", + "19169399182658468533946629204100516992531108319585640665183465573285725904462", + "8598406387986750884545663645357594697293280337319305106433909682884489291980", + "13371924317816745712309163118606793190008424593275425616115063429517298127531", + "6966840677637301630181597160079353321163372268840965853806074036299182143344", + "7360050884120437815429010939441335996325758450523147733433611465713273074289", + "18472779235592140583651215638681270421642809888489121199352426980884551699243", + "20418315222771285674269327640703205319629639584244730201946908647328012868945", + "17026399731940643956418206353642733600139323525975091345342870617418039094945", + "12615211028716399762155462346481728092959316603121559521948867446476882144221", + "12396056279980752806946158389419170236239219009894918041607073720841998512650", + "3020267183116758520311575446823726815087071183512473845404855371056317502914", + "4656542918173262538192266136796850668036544742787442489947994092170369122585", + "6651580153776746866797134628474708113133308603326248742448594405127096716435", + "11937440848975251505322944134185172341897441221500443657141975704868428110206", + "1361826966800566560222081321267933925488529241685130208478269338961124266689", + "19943953771229014728170188452413560878330164558859793245021220185499985258316", + "5000555778712004519940673290591853777960346285299451454158729230859752281548", + "8421588432304272431559534311544652626307929129739056740355326524785204863675", + "20295164315900629557422352618552527295246432971816033569783057482727719683327", + "19373368537162638225474243050103347264636555313351270279300938524044041811781", + "19301584842175411162376576546656332386749822306022512981007234449440059593206", + "10803063363791701506227503212696578672701333662552932509544627248354076467851", + "21190447586310863247879892773912245039992873717104751727825265558975676297878", + "10001943442280168315414950100438358800275893831224620600570013850341287546634", + "11419108205602584425734602189435177828356990665164795815514509894518490834107", + "9176365528060199110731574560388345526295591893454204513780413140739307927521", + "14645264015971411259428809172021065485340465742768211283864072707152051283922", + "2059680394752128527783641175137415159902594391639901766513696812385555753758", + "1362210878544210374207986445190412893419873862912439530157828224644264948744", + "1767020623225215375042202191031932704135774649037849280381750341452687445659", + "12814227243430147774278313161816717185894261558098935373927981841389635793984", + "3650392274313221017884542662769899210301363377135174682150190146413571526895", + "8605030053804448166307797789010404548793085578661466947278557642849458591843", + "16243873345435203695859375103717652101719490281058055690129195139240095336348", + "18253688126813406708458581768662237883226849901952565733241285746065046225854", + "16894577548232574374120767026426945561995165201786189772245769116072691636300", + "6423863987901820348928069732046971656713531289251833729857903917381295249467", + "2313252375346549956530493743826026444705500798866360084159932659530274395286", + "14351648867218654569586186362113600409735374191851776253843321710353903071560", + "12748602702906129786642754668169435482057869301200357523545630557138115816573", + "9709541611470508424169916733595066328177420935464065317839768365879702321494", + "7178910033786777709713498702843323736165699178795466682345863412805925816311", + "3353312211975166615897632007255179570864390972587901806985844706830647691440", + "21333017166590824659869638126407263965780255207193356400088734092911593056348", + "5028007188794597820037691911407426228966175740057746443994889279713514676932", + "10712123321028508395224612143524241532236128389312765281020405990000255535908", + "9585591632158092894254183832493550005011787447364800638915694118244958639233", + "19409055735214428219964362626158681016506721676720000843605743187110212915626", + "7013515054956821958187738653419570061640238138610196975216698509582854871893", + "14195757172805460680435532998286722310304400118130745528870652671654335152142", + "21642947172805342858920270288264451136332732954445458690700494722054302963125", + "17447291201981220908729619112892222175870413627521563259423394750686485556049", + "7142752135376494645636618098683429493620847661912582079125447091506892873792", + "11965386305718013298615263282817897360134689279593443592649060011294217114060", + "3269287439460269094755519346810063037578116295811194645623863516503159243267", + "2159120529508493457616143785556394024190164584915596617279717229000244189542", + "13216089736556667969033672749174760723927614320685905176389819791237404133418", + "5739580235677403106179902507340026453834485061930671765794215270028117113169", + "10146496637166180435280660177294964473638207568055892239396905092844948797979", + "1559489237751880726567698923572583310388557924219345825351973445625480867548", + "2596197625765120503438747432434144655985802369868414306286213301002200644404", + "9161937832793127953923149604860857118908349157525975074572071369698144813595", + "6530106539927673920262526515085998672101393587740796643755609207665748320714", + "3454043461578374982297475090824066193432500488323777575207325753404726668622", + "3344507163548616813143529013320782527939556912905450999614978312225536850079", + "3495919320773160724078129285494188621289377270658655167666727615227933095154", + "5516726577701478990891722326081439827260225708304904918582005618283443949920", + "15205180713187999862939063125387810274995561110852513151225711366795547591281", + "6648123555675422871529580981673252698816139021904779960285680145979314385726", + "15841058754930028584592543963971426098015345537516384042315342065435733026794", + "11980445534925903182700789375967305825089780148976633769394511063611931605378", + "13752792603594569707463845404055123799321888887568435005345905056744143696677", + "451907199992567080921227130990235823262116394775607208182891587168397627680", + "18806959341268394516719451015233616843300011700855654586308376758853129828525", + "11666933396513215238559883168577675591860013867821037377342211197150469047378", + "18261858072527055096686249862512380033913481610225001662137639137871862029384", + "21560391742011390943072232187892257373961479111877969400490351307926336615662", + "21610883506787217615067001898587573081135488752397283684823910785658887961932", + "8408073424459585535553155824732179764716727837637392334926567945546036849813", + "15210371269884178529776581719481988665617111145372052101377092280056914506280", + "12126920168856651470145408021353708994570254510986190646194434994722466503351", + "5069682791790920726869832051575701989172948895827442056320603681340757243410", + "5206562653380935478898854557078389821615981777078527007269200218469247241072", + "20147037355698495391549064646435751868583938479186070809834102358533096530919", + "21648765692141622487379563405180427559394486974406039602691179361899957186441", + "20151743655500786952430656789085402151537047806266385436562459773879995501836", + "5934394754413383977455838937623522376367295023125030069688154658727645145368", + "12603931706566171371403975785994747990660901389421654410200684089833302667483", + "10026374951811566765702661526223829303823304592138671853309110795200798195351", + "20990348049520375634430049514368373247447730079841716683325070058824866344618", + "21619384435032220705428130345438986008128830023896849505537357845578039663569", + "13891656325513979012692764566925054630715484836057215275096343649598208636064", + "10516755320080836193722788097986010349704472844888704193817055904209452357159", + "1187924241706018031195408222997566778745910130631759501304388015015493122577", + "18176236836230977144874543476758297865658817104610686283126581582188371550406", + "12485836949850082215229286502965907740610097753180405472080386359153328488121", + "11294821091195763819640094447903397628795163768975434879119673473115769322204", + "15235618132532149904626305329048631193060570832794115447207190941624057752196", + "5247748280389397626917162873894276536157877881047135043409628106456158304439", + "12327729816009770868743571696284061855435639336028566901611215454206952326402", + "16602949418050455338609614423670909880567257158111141482461606053345944875474", + "19481131052077011331591495382924133179323957631986844309716638852480977994007", + "14534922095860994138017014871440884145230839148117198707882202478464336849118", + "14698138249781405938575978017673685687915842935570150501408655390412744761438", + "2268825110634009232539217412695133214837154986621027045784924577498202527005", + "48564023730318244345588908855562476416102457310638224830024196372769683274", + "13911429573364091016107598756829373924944177606409768266066645212252032518222", + "3072739629129037084628601278178937800555134277149245900071628775426363271424", + "17673164704069533606602950374212967524179027652656167791832581708629862878595", + "13337993423730131992645864661190106010697884214050744564788997133642079615746", + "10864361443255322638825945640705455870794346349925711501403405779308411872877", + "12776251935559898232336391171201768361485692108948381787636663233683642720570", + "5216617488642405414264845781989153364217494006051772154230377224854279460364", + "19003202417167005941636726827739223707886908723467287344441657245420838333458", + "1782933238160323149082341139973263731861637960761110984576354403382733192508", + "14049307335094461452125704957817909153045723261672970005965857346428556786126", + "602292860913070228058919352868668861608870961459967730841598276439118039884", + "4532222937868395031497077142687900610420285824272982687191592830322532698890", + "2099811981776987988652353966067242893298763890123265819355994207644365313244", + "14963517993601358850130080519783504904964131507655857293183966569857011289997", + "5628630657751613134050345890283140633799644494629647136869171968642164797575", + "20616991532748403949152312405329497795753361035307036075488592441729795933793", + "9449735601866166994904286297362590639752895462487835902681383517319419222042", + "4855933188947772127110139132590210785552455770275991391814399396690242166773", + "5236021116842873402180213939626155398638345682608182461380158609761274518585", + "8831771317829874397651604675303149337495578376535991692269465791612651585623", + "3088758799070584868972772977513492687395394215140736055156925465547111629717", + "9647320052871832007164877116869148540664111922421469306708391658695763892785", + "13710265959446629407575266748628250489155448638018691055085275421674700880156", + "3990937300186809757319839603902135620837406120640478356311388415953195593911", + "14183797850975043036582133930342253303942122026304366900989797745492038794070", + "7977072622674677196160507229659485240881624765782396719467815964585050639939", + "19241094982310069445586766283605264689470697759972104264399773392459234417767", + "1955047691838825101723033568109446280275825112435318858983042058841632309045", + "19123726709557210987172343550395139275104840920797922301604764165695899093438", + "11017985516143054945716095457545428692165844636350104202156363406141221710733", + "6698050162913212144220380056054973138261583127187888344395668170533632399574", + "9966258961247618428931643789269822918034016436345337589754558490493039380634", + "5992044103018260979899747087179477222620402658511576502983751749529532417546", + "4323136703775346257297028867845707816483717723305960372757928628051725499745", + "21794175040927871006132858751099842549067139289302899933403157116500725993565", + "4135205477235797120209733594064542822286242795709961420067213468324705376738", + "10502424667236605030274589825431626273558506810276755098371543260305259735173", + "14846816339594032738719600614652189538461600190221870378597670293625955865265", + "2524366986896165752091070306635323844562001546653320749202855717200081979142", + "20323646694453380576115350130108187509393437723558716335846490151009228206070", + "17922328316124774235198837264790120266787900323072144966876142176581863552114", + "1182636510606531787673615350114833983759075243161678674924493347851641505038", + "3207389967170019411093119580069334588690357331504170475184259410760933425232", + "8369192092815635351510306100580533943309224255100566350954698456188479225517", + "12030967886392195699293618525716046167148513246572831396936379799444387335164", + "14022141444250217995147289887822373716179013971005541169826207913322588624137", + "1819830254074794257713310243249746692467827341614315920377358118637602832394", + "5214222934428733796770137135908910242359102117443480657729178969918179851403", + "18138416073511031005117669443972799853597050093891426716335475360173308913456", + "15787040542336098200132680028140910620950068073851656692331927514281562129727", + "821824702031897351041935750619058900741428025283533564678462466715419856156", + "13945650681120508256113693182498673378848691411623235404408416652983710360306", + "21565424898814963463657730321537398782420569248706699928677873449861325896156", + "19609389297655053996872511820224772969554473578452634334180619061868015512289", + "11052162512235765382251445485034453773059444701618444976454738201341222665559", + "12166351899911550354968692629609980749815884172876848625284742219657445788950", + "9357004866878440721090407569238169828150449366762652916034388742054494762892", + "12114234396159313460221530030197986658691629189646106573755288837341527114909", + "20697910091918282046230152102481351960195609070206375824763685775389954486953", + "21217736707553075351032987119547432929010871168087324138739787787951139001106", + "17381364776399568820088774691862728632279841729367582228657079823795538435769", + "20943148878105082737615418415826125298002846495574584317442899738454169666093", + "7269215523851335046672197634746347471983448536436098948086942864058221294848", + "11453469784625194830295468663538063129244082440999065497133984430619118688913", + "21018146386490226380790917971415394820859127890200962707883416995754459780694", + "4499764602796942905287899120539899295087557389188509075023605344029409757976", + "6253534478331781543005728896589051616547370996281199587944537185086057029207", + "11552217017582571341399470106065323830802102428782340770897703340827468796773", + "10819374663920577173719455195284609222419659549106826920462161534187158953031", + "14349899656862395011953779288216924779057701185080247070042551534943737976358", + "909510146269051589147289065017979551607491214019225341737807929286505047312", + "7822991049400510485087251716608572124487352922137650148085367861004077510787", + "725869830997965165130427627260429551001667760503255008495920045139979612999", + "16876874328062330437058858063404447748425817762641981976045186363782457609729", + "5386742002628492275210178331450945300388007254427357938003565527570148075266", + "5628231453725040085705711150750978455046683883113398227942371706440013519778", + "5207331605257641510707403983352805778295440159078505882865873548456617745902", + "10535586157456772677635305329613970037048119551546559771279117980166707049697", + "10634858349767776643334691454267563140934605084766184133586786266071708634005", + "8194830686373761187394649827049013218313677678976649876752071409303459578949", + "7100588172111463994475475315895964854310052675103208262359133193362837864219", + "12698529806569743134165801249102690453926582725077752506169518961941228191732", + "3192504222109013167359339531059877081526474219178099463731713708944911915094", + "4863002263813052668246974534880517465192842993905851311847938713684622052826", + "5692128778265216733946522388190452257048514412064102477216511313048856040820", + "10126422206202050217110361146045359045967276838268265874082659894234609632351", + "5816818864633980489627669987512782856651289984181002178349070605011354887917", + "6571596820513333489228887403836877987952738840623146829273932098108053062770", + "14089943097204317257370418561461590134190932157472606744328891293369209454647", + "15650892875459028952140088123225234819706577582764200222337535746289113550733", + "15234537030902822319330116055095343429285315370864285347784850359841976501754", + "19442144493035982963093880999374393289974808397308087180018619507528159896278", + "4787721462177588970261549242306006751465139812381940463247588627972131957846", + "21188266388402646362923963971922131649215999518682418035026104849712460470697", + "7670120618673949888866627327541465118295930474243507723982310418317698793909", + "5278076033158881738611446681468941913572289692389442858337761263328869905538", + "6812114686357362596670812436414685361641526159356770435320544521002690748983", + "14512519858142345768369977582630083215640443350401036244408402584958380156234", + "2883669043634370578290606585906686768752718215322315377961330865510599230166", + "1390031372189262099215439174834826964629551818632345640955909690959488475368", + "2621604580741170950714666893803390793334876512814103152393976192943909783864", + "16238046050182542127054230631321316896861946156551854572617517416519877517341", + "17244586605437271554745538098470023522850216211045683035769492251097903894816", + "19367233841767536085266806989570452145418274769895229567119343509637411304432", + "60922694691641059020255299196606231058588207683679919735176896931621476211", + "11404572340596870708576661187798839793333697334049619025922813709736021654992", + "19297195320049214749532857744175644917624562154332216539771469808458364580570", + "20000477679980055675786972902810034261882771620364631737011618258163102706803", + "5145215345848317594229128468995966108988375785838942899274867896011577909800", + "12745405048448113216886186019434813760835051470566703617056298583393105029140", + "6134671602520394562619244398452258969460576801019889394876295515660617122846", + "4914589878992627637690178006894707570950584383330674027731006564533772285206", + "12220903700447451099363813812957915640017509928028867600267100643383476677026", + "11628588304761135026225316417404056298817057087404654319307918387873030085506", + "17423007615206678675839176821651284038190814226425327926691523730687305701329", + "8965704645750924279210398083172931641857031322145961884520939048845530423732", + "20213900813945518409362660921232075042618186257655700796304283970465683248159", + "14023473417795456589963878717505287517669917599805973068283274798230783465678", + "9113725901633633321816296721315176702107873006937820201651085782607150391484", + "548172112211359610960958872299753762974328909291797522208085351481742658142", + "21706861375052844683595820457538688901531393763031119579322611763049792865067", + "12400834297098033195346480176669546452877127463842848395269580039592533946307", + "18693408652472113120418656602007998722418946699666834214243410738193362834090", + "5734025542584829419095063290697837279993460564092839068131370421231150830964", + "18924630076511631254945042871273344212804227110396594419618744375738473997458", + "21814215076304113439159605823570645338718666334965210428557308969012775849083", + "15874497347478704100595228569012157674761828758432798819588001847304550645894", + "15630701992043410338260010633128892269216736379332650587736588580296378591400", + "21182316539430719202319661920312710080481096926576230463259085025268931402724", + "13427632056110349253603837202352903590918428276805663374267217475392463207302", + "10911219508671048309975190269088900191221091803425594935965400900508004506426", + "9158857576095512666804740759126852985161480036845995629848333137432364744220", + "3061727900948996735547324485012991687515955796128872254459502313385493651603", + "9484087600338383596528641950981362023967545451847364854943599173866192324557", + "15244438304843092101886329998640757457334825678432810119480500702489267475870", + "966872391220567196912397735096848312956568724961332613947113843660023937964", + "11463695793311663093741054432425795718511021724948561649345772770204331282701", + "4301779131461344863695073848029046722184482171568650483218315300688393400530", + "503085225024315480816360605760267420357374543116157267432728664696709665901", + "2152733644489285468694016160698899213530839938744988003049661235638107035210", + "15311799268435674361916200869320882308334015315600749428161144697607223747926", + "8821276262247279528293843357774532114304424863546634692022624148120506964899", + "15012215707563410212327951685975753138452396938302623171526151974041926043490", + "3832600555460338220245921307060619972944914111584149730708347861160918312276", + "4945512356937128186743764272299580525076021077689098424357488427223926571902", + "6352619611110096346414873193740535134097751304211531527845737802795592283759", + "10487432688972604512516548109465855881572886261854521534610720416206960307854", + "17622489873780167264774227253975567347204222125869234241462457566132215071928", + "7633353550826098030742486107449393642153730338494606903118637850284315354119", + "14024635247358847977026819464107185157647377185834445176147145227141238505750", + "3839080266490755896783758766005919524636645447105234209420661161997449147283", + "14295400414627547924873035695592309131547135626435254888679625991957098691157", + "9699346383521400413122931609905328425995515996718354291677850659748262244552", + "15892775179188924258683368801772704553840476123482448500492733919344870447160", + "20199645131225911044918007618559374867899861460760047877106143132248914945273", + "12001320852019564045161245401305456578767647373680507608908849898281736831048", + "14109337051994798438391426281455499687696832970217465295841248395157195678143", + "16327788711327335703187954019173408665382603544801329348791567767829649582022", + "14165345295389244679058422302892574411018017024852263235622041585515310700440", + "18098328140605761490641355018350075852862336947591383947449899194910504851997", + "17531934292095415392849417096338882004139134123682574245576387314018394586354", + "1733676683546726275638177172968959379163822515869952716090548401850898249951", + "15390925956113897743988821133394062442365524479421281886945584164621467664839", + "6308234777769318028945248133710542338224131559387923000379414195599812605031", + "18195940437872982705964842501287028751711989848794835251153117845868361639502", + "17490283477945958076039190253339223109810835246475119772790163703167194121322", + "15408396829836728115954873718864748500411741345264521674331031030247235075214", + "3551438511462216897614311415123740632223550882497302889988823516019801455881", + "1211190935634033568807015118513121103447278874630092924424376539010769615279", + "17406162668797550222384115208538224445727151774389004540678231208053715695274", + "20479453675762626017106163313206256229631927324901802475043478933123265498282", + "14782425647559744167182627327791886056751281195680302914811546501028210982186", + "925131908338441634745517290354664216337826881101842522634123686010539186420", + "8849707997196192490777875561563208206085973382242717199438345627124299133090", + "2076454320990941741925816538401596397546951763404696804371822796867698785368", + "17173134576726258494214130646402838956462459094351663199002055284799185636563", + "8278507153110957550422039807446721030659313329793830361362606551912544734153", + "21454855413436031867439010732150284197408206511012140387650556740240705522088", + "8938141846612216610282603832016394896549074914747256157702516831379429923666", + "3753004427655295864092888874901033504634515083469621309422669489429783662244", + "2366435134561993720630980767268361780485124472309182242565985427615594260755", + "16165357231356434631067514709216775568766632800191759830231349057343507534434", + "19619001519241758891959372143573350428188652312535228885075978087065138604446", + "18529730720102625006097708392875275467779439015225330756494090875760858662324", + "5652897308997297007169294392190006281027830654965418415676327691132517009477", + "13575260089385236342970650047136574844156206796954949409405285738862382638131", + "13051488122183433321152129519814259420249179451391427892934958106760313757814", + "16675947070785780493219656951197382898525312482169747446344506906972920892954", + "7175175603774065686796811506353067100604576034944842446058484596637779839013", + "1400986416499873501628131339252214540318552035055662222249409246587339999877", + "3464466866466806017745751012761954006390229805750136348643137486103734148919", + "18217858987033907489494644734206558680641423556261333390037659204834788684410", + "8406086726746620686898224574302006639796660679494669881199476338693847188630", + "17762341698784805474670945153199304225802481228485479435097247827908738620552", + "6223712186181276291315191942450380783893133091457934083606193355728586611122", + "4282900419919891825156385371067226030543529098387005106356966928605004273721", + "21245665052422749020204481966374990873300815343268950998971123603215733315757", + "6982432590923765268313576400153302843651774940912624901824133236148956994132", + "14604328114465343527250129263480156133215257207190011581958944554537477242146", + "4419171346687102376610201013900704798965178967312178557853149472731770706501", + "13245918223750947891398096117311260221538002135539665270124077712098715822692", + "3347693769680689230259648405537595614327886881079646330652620517715295020391", + "11097962153704048182055796266608903112109780112107618586656031611021161690259", + "103842991820173997936966388104541957651469231887516072360536421395334472158", + "10458487052445928020397135451416595147106158179032040258831628955988702925482", + "10900270612365580629478084761129851570116267768979394602611763172198923647648", + "9903203205178879991225339006690459919299342990467545909511182892622541268992", + "7206476963459461034052924246171589839665793743462578906114104789862742647145", + "6264046538214382166095990603552597379083154170591970728068583459736905241469", + "15208506074912456921421298757607137779904668310165212805848069781063650192568", + "14399898497646210477881830976333295219807765057833256267205543494652425724063", + "14270878504405431259159385066097540588788179903719271466546150744452837482681", + "2299469207691207005105135549235035285287116575059039549352935321891742095084", + "16821622844104951063765780536072686283845403348056960050391910562149383647957", + "9956020421344380373447956054462292461416421439742573500231969663699680679093", + "15930825774426452340437887971015387876460967360948156787859264923789690195070", + "10985728214199775151782154608793108221074450547445302100877022826701613291080", + "11760641126739413999815391346325702178866867588284525333479386263279622799779", + "12964764722311909921385583909124655151898642200440943348322605636391779313939", + "18379853927966213921019716164768637873544788239921704156188954474082485441441", + "15296912762863693782000299085702814599512114536531293086824435743573000852798", + "11055306663322542945964813101077231103838051414460193023274311763885249453027", + "8057951225246999526336336507654165264870114340865557474750852826611325059180", + "7217454204491887928591297314854022623729750971598154127245160014826014134117", + "14565102336180706753392276751790538414003260023904084675564779334184014612773", + "16932806574869520697162166869588187543526028859439455500295852309668070598166", + "9179898117866495045233038402762881324885778157159220317172535561846474551689", + "18779105085403868166483619694881906385652758088668461143173550878413116028260", + "11214136111470281555815079365242861297786837712776840366750250043823307862209", + "11442992999493383524983810131599863922453401327420664873617599429059754849857", + "16271323289864628346209913310699661044288655850612550590359075955707090062050", + "17108025350334840622180169650569770852833940341473970170517727066627050749958", + "13146248878892216294361188166665572028549000139712229260161108594416711673870", + "11427372820077962289622241022478467149286157602797953190515658164839928300751", + "893014835995617411380095483743111031917691099253220683676712243294883268295", + "8517095300100078150575343693630592663417209558229239005971749331683768522616", + "17927627171733468726764817859266182414919024487868681391571481635385817509424", + "2910874756516541331498135086483127887295864033618596807255973264881493597680", + "10351245804237587891981133156579115865490473264567370459691707360959684413970", + "12694211070090545997357986982035119982901819345677093614248046140600061957940", + "20283022323110075954579979019632104756139185475301167929737027848763705068147", + "15931844873010780159923673780071721836876296130477744289335217834580073984287", + "21827223850924748993974869213271539530659373002293617263857321497283795014386", + "2216130285102621170883691491664557599377015082391517585865064633069426967470", + "11373405503815639839270612384753272139664561558477978654573651197371057815151", + "12313875735580427829620793313088533328915412934622924938520740599744871213525", + "15142465061035421304605695594042142745567585293979295525147576522422528415408", + "19911123862342914329674984568751978176595422003509172434076759943963715219260", + "14262659769499978824697304563726231560538953195673261355416096174585413608681", + "14610683638141173050828300939153196590488444327501938863403192781649951814095", + "6637311287977459850936997036417480664822288091481538821362996372726159029429", + "15501657635921439788956110615190078759397404083654787694613599184616807587119", + "12415917438013473637314666005336278137449129158908612828862230927356476129214", + "3977073659694868689017260754925680384676839395821102222683036599314487912596", + "18021973030966989893231530982789547568199608264409750239459349680822956679629", + "4358661372125150502295103743733014495017362384424029473164385391234779773434", + "12150574573348685676904261170022340625206210894206500162919624199154445311926", + "19059564947466261856206182248882637129860510906425942015878496836349348075264", + "457803388293300831659933030795457353247367081441494540658944608302089214573", + "5212046878650303869536834084593989055518385481488493726863247051727387374999", + "10866963984944634834508529257930003255650324224099527428753028080514409460102", + "1567398629202166035061920213544077752133493577343656913517034032663760331227", + "1641623291671542299150077964313362112100008772415510478337935841080756871031", + "1814807183609065925468700221459835599229505862471630844515076416397596161921", + "18676591284813153230980779135662585498854259214169962891045023401932640144792", + "14582715392475730800118541203172619815507444658499922254753619352141615271717", + "5971201190012257917828174333108220091030383280212145099110362036705465802488", + "9745736062659480171553506036948675113562718355498342585124081392171087285152", + "20654605762814031873434196876396785695406369410937473580199071780191334325949", + "11844011087342569590330041590202444612516395732736950853259915342942395929104", + "12960729918447154570349545809398065983315955190065107446283057843674060399978", + "1912969365260456210218541172655492769437123937988143458973053151759348812779", + "6751278463897225433246228986310134589363747158535699169746372748837067912354", + "1405433090804402720301319240257714728362644500702704160642087476923303165683", + "17334203531491537592342457272578981899701055200802551766264091441355104806736", + "16575765689548766530893504204590870417122916172019674976779799591211403721688", + "12220976854598379181285138460526335801169364473128100040253401652070229420610", + "18565358629024373944198152078259356971563125515748886809957420832630624111647", + "18999218340435052500780578040305741910732238048488832712380773170203478504280", + "828469997232485965462944215365058139980461203417902215718115878220834198655", + "2385864265255813442011864217087321326832147642514277043179825610035123479208", + "11753087538734344167619786844012793078263845468062416000491296071906848786208", + "15297341509659920002867070368227904487875907462849401568145592054120947559782", + "17451904737564664855895845058410281315610351460549944647244100267528838462231", + "16288687708586503845827496209229686543350756101251970070831852982828263987682", + "17372190154611010904681208309570706747429785600046317069452832284803213135091", + "15112572862198958048036722322696004521384539985617885597335257056722713402392", + "3090030985871039557449539908126365491166718509536148045184016492804246563853", + "7579986120189318071341140006570025802626137718385790787108832330451957404666", + "4656625991418301006397045145531617124589866163929416672123076523538862286851", + "19496020801558863926434816068743011465607626373023757648812447085667986307007", + "13805329389975072362759970417944582121050551902725997016013251740633448473430", + "18188229086122124131780255528737492392822604407289151260248713532509782736083", + "14611835504502434276576475613524726992333885650957002636192032800434268523844", + "10917400920268125157880112072188560060076995206390296416305594303287585051306", + "13972231301490257961012583958703459454850999751676553092417436936263553366685", + "18328503113772388769765260234570641415081417900145352728842854263561232438288", + "237123037398988309289930744284813441581716633481073379308753775064025864085", + "1703922543704124573472350868371820728886443372137290454699485327716109471338", + "11303106151640297276547682977648977961130813769323129464984120148868796657869", + "780835119818217499025356174332702814577120774118181250962295467182217505278", + "7513776436281774240010548782707301011961852707217067098086617267751060890346", + "7053386733865795782344919942070418964042982090173077686333893187474076408826", + "15388377229685250353999590264475768848666849601695837177100149716633411294502", + "18619784384287698574679414094727959444475916860934398666937358687019863556637", + "6843956467116800550015577789758902600855297665349663446001269088726857226048", + "15027464760260005079316932218316611292633334115528481474392667994765319146064", + "3113497944921123135464097580210634621999169473508798646842600013742294203321", + "13922875552185745353363519166943408041900501129716418917715862175817917028807", + "6529143033058355347608548436453630783222625692384446386966623567146527415664", + "10224300829854530831381318053279338710395220281107133842738514322747972858570", + "20406735123273907201110501114056492687992916259913310564107896450511212255632", + "14266011897697023567365526625467367950062937074006135166584691625548787309637", + "6266823350995327247595338658995148275348018579417278062676982980758032485160", + "13382224019010393958018966744541120649420499028243672845532376433487586111361", + "17219221786488657036851893458562210759167416100910882352030211878162886142909", + "20162856829897101788693886235042939518715128237037117615656868509223185121680", + "19255871117980259796107225408238738190418971755264416661458453217281817348088", + "14978990393744959794726413403754476561984809686673856442658550453303982912228", + "19671827853594912540136789344716532059747421261482796306917878259275666282337", + "10732390517629598846596313835253769945253387192277550975463036830136687386844", + "17133415435299159639005309603217597288690767318016826234871049396559867937550", + "4238294829801619171082019239832000810131476156600856298143082034448277968560", + "7722327574874647121890494265704613777043492303801725732359520952354380593844", + "12398521387704975769381094211621106917110829744392349946808293674617176531795", + "11200776058775831613393435187103560072186926574904464246036376746754002391683", + "1438209332219971409989076556134294085612745796338690516891328286502107803440", + "2937570830929161760131625263024599999319751748792625900610926821498514203833", + "11043761204766219739712866580984621876221567501998807492499956572251345283273", + "17161703263657344245212174231645949667297741070344916641526821962829393621412", + "18619070492541478298708071132730313137437250499943292747935022132254737182602", + "8379242582671699656964937417808578910591927550726975853520669689659712748923", + "4461581878275014470922963327129159901508275066328903674248853463321568123734", + "8814779309876923204577653695802478749454337142645142889405691969158642344463", + "5916090197404556150255058868159426544438846565532168821258584401868379000295", + "13450130187167769463125976820336122102195298233114472585228206936434118870547", + "16312137321831916960674847439149202523298938366384118998520447019250845846949", + "3523905992883153108179926090860559860264215076129040324355490516029055984441", + "9941544263434232818617581658227915119072214833223636007382930887032695504069", + "5866421591020072450638507367259603444231034674337808358938713064987239652864", + ], + vec![ + "9531912189466476916568861603725087174113765795321185751191491139118805372511", + "19478790955972117697384547134063865214385101762783978876290106323878777065880", + "1060742683967616999946346623778902980408439861665078741681079083728753884980", + "903807389911706192945621790390189278582333785459643079682311503388875632444", + "12234856961774107626670146718644057065701939000315796085771709903680607765980", + "7144222788160875459149511026237781941826966456792731247241581603475033054157", + "20698157631699630824067955298548696950174021158305550220448729049898476869972", + "2568732479597962725718653653192533652726437462680484211279054254158600287844", + "15284734447838568972934847154537240941736455475374750484902463133334986327455", + "11481338136657097309136761414013103615752281785975103234128467186355727286020", + "10612154017018914947135783711517517074090274235061056248497547614631916723906", + "3055604116750189009829046286798613018321687824856056202070161812694453230232", + "15724356578668121294244613323219019733691132614053416867337612541744349705307", + "14233178571890186223833398248040498471414046807101676904418988635462870136058", + "15569884202072601673299042953201725479510195453493088500579693825511613908294", + "2457655896277475748047646151777860503559023325500398204274047419681119270433", + "18721554772384051287322049295337473476448259922773695010270945658995924622719", + "14475562052679553333706265906284533085428658760049287899372861273825967798347", + "15603408794229592986464670990674550905587371918421438132217255356917740776753", + "16291020844798157226549685169153735235751877205762229274467406522127105739380", + "1562912009705461355815110217333069399929165391790667011738168557818438924927", + "11269000847557922376283381139665491298931690005135341373215203639253515983197", + "7952882227157644034022652849613475711023423321505769231128945987664804912273", + "8477623463686474590677390307150152439632807913002306242882220286965657503044", + "16240923770208341542260111895431885216031871921759466016566105290964433552216", + "3342849074804996662671621919352322750206307938435597035721084610748972602222", + "17931702006999673813463835283048793204134166638699010505988519059393386641671", + "20775104403560355444897460398679593487716638645215251442678183285176599574637", + "5119120619161234799144523865081220455479981813019860080498855561756227023701", + "14966757356872002341966020507506536570537078023526508344654458912397759281515", + "4186575643717625662604368224196455420957673000117335440321019421096129003102", + "19732993039041335023904970539797704576579318087957672270477209415993860238993", + "12070985110749915673363345667648496564808814323429491368289711883810537898097", + "11426729904237322553170474047093755836047277723343283963043397762276736900525", + "3316549680511845723553051012477874001156246623643069548980284069093192393275", + "8181145696278572952623145148622555644838658979012817598939443116317742063663", + "5285104126733708771192424010645473580676014333664755598112717745696037070786", + "20879460004955312065246827250994614761073401947673232470833608851757692374701", + "9225423353187148331993357472363957540141522302215231248106234750413173980751", + "15882456781720382955442431000301149138213435084361689595107598612703885378636", + "740238561845104412391944006282242126007894120431256540495139829806405656182", + "180417058725083825889148913143008776006125349068680455724690397852076567353", + "14174293519955274733372251014027762210778831326913295280840585355009025244339", + "8589132560109336742397080501737400189892522466285798728529721670916644735009", + "18608579889305084432265993644262889161778187018060113992221234915464772250895", + "13255562856310211555141734542720764165551582487712004861593515585281326120780", + "20040426996876166323258012360708147873405329072458300306708975663127708222443", + "3623806905318973096995680510049954452579199046783492207512283522220396562822", + "11410684734000900004860241940266501902598169149094443542781697771532926351566", + "17135477854627682759515989387435395911766329083966273406049980901836411403416", + "13593359321772915930716800157828568438953037754245269488538472861500074218774", + "8851367853862849158416384345517193602505667487160151588020171989122435915705", + "8486258745426030087193511835657366486234171438833758681014633470174920075557", + "14747490337504821136965789527401826047053378817043597026988257648138435336926", + "5328238162919446506214025777104049372124957345257645933410622558507658284713", + "11965770224330607826076459132274399741504292999992921785782448737931531131492", + "6397958737008655664539418210486834384969900796875514632281024739857683313517", + "19298424785220686762403958616740155152857782164943661260316780711617884649980", + "20019455348785333548837795973650078300677031008828877476192963238765813699604", + "498876295068369039941891884133922605442597373275805987652958540603151502762", + "15673948845182330978914854481229214769118100412263880346214990198207268219918", + "15636974430517603355685549955558840639961287398389993551242671229593610041279", + "21849352155479133596816471850644588093828045783688624472176874329078465894038", + "3710541309080539224803365587698192640065043989988405183436118622332060826084", + "7400908409303343897763329119747918294591391269311551693625176644493777241247", + "19095563460120996369257805492605849292942048841569757166438202426234116219528", + "7450358777752652255259382961368280387692895530851667830759412613948095056314", + "12616362847029801456780520061399223912377922714639139395311897747307063246381", + "2962396389753936883856793158008198181187288822144777145656778061306062270271", + "20597083262311447290143345580862519411179427096464102505623468932397345199901", + "5990063075931255958499395880323163728626495197217954905778613462408195572191", + "9361904499252311797420064091418454593805808979929563324171298064587942042557", + "15745983602860127054098117502599813732367772584805282887983680577406906842509", + "12155753549127535852899642287036910835293646693799050941038502705904075221647", + "17784550108352452858546269360461997898333463961938370347697582728197589684086", + "2486952113321483072183664368099776164387734014933893158352223909214365526945", + "4903545427310923282188595180893897246459463256398425200307382903876773643166", + "4976787025628832138140129068285715692634473783721132777844066348049003658995", + "17903848557396420363857191109854268785343114927997153696759251220232810232708", + "4112316506554809604392743006504568448930584669405889138267627731770713169451", + "12564921445775298869601661793895717261354052767541351424764087809438338341443", + "21338339987232841174606235608205622863643553456415651377028015070388899605425", + "15088854240518267391884187352051335059960134381458770666979402557846290860043", + "6521785672326022155410896706891346109402758862672817874198014517122929534795", + "17713017733654015756718215699238009705617793897969373435335184169505068045344", + "17520588725173134454654213370481559878188684108641520017594192886986887410727", + "20370353420481573040209617326772538556774101029132132235011046989968543481853", + "1112630910445876674622022594317693604373148988518983600314476620278857862788", + "13947879294001229379895373879944689637841444307211953543137372048528771620266", + "19992733820130513366333925886960985070219788486605690639975080516132656879852", + "20463686239191469605722985205294798392601180601616445763439546206772797296794", + "2399836272490730222789561326076432853292090595513010294231690669763899804691", + "1034212741378241815550350670737161423052400169194393567236687087583799668630", + "11440929010875860174917763753574001672473682534215635955505970318252592490865", + "14189960926817818424784917441574757301766760887770685069486059337906765736963", + "12315331213328078044898516229848174792421271854142211159524426707506661472774", + "19715153274097520886062594899069030152815615989925596868748132112713500919606", + "5263645674775021082527379900656937946372398687742691252725061831191269814647", + "17116922705336407879424247270343125398621621836566941362988393678055183546962", + "12965075932786028962944831711285166109972351055088920502397645336995474504411", + "12016029264981766758974787101200878559176627961323691741528038308088561486007", + "5729800253105809140207889626731612970039128485797787837679696331851897001188", + "12918441439057112476878371772096947366502166083699226943980929800631997660377", + "12122801182972566919042281354391366131886072331873675435499031824985283406080", + "2001381046477673840829875588313268487326797326656965151814712798463828403972", + "19581992323872865003173357646589705276192927057599589666190396129558198469295", + "12032241883699803399815479981073900218390137511663730251199663857029681112519", + "4486673663248066106745570699596745555749344770988892121755846988206654958447", + "5880818668316784480273321916242537222449799562687899946001667784663774949284", + "2395463900919111658295342588362130947823185484448880299307422522723986076206", + "12950983061674357110606111858229311283119838941890561558124046774295323335099", + "2237414129351562764861995011211505967387529547007150899242139479636758409691", + "11297958435815311160313316754342324739290776717531068730041530178142477489469", + "5317255860197359110199894572100590405469568868980740298600361799734023336258", + "20883201721979021051679862781044901365707874143019054645125857460321063446040", + "8182241566037373547703812967620649098015366536333588850170211599244447070966", + "9597640642388296782056528355964209027337895123387144965957122193392739912194", + "17036534143228977344737945793420370811715806669058615712195620745133828881359", + "11612810822316258618979762480131695561483235138506664012253801075650313633340", + "8933421864748994361777741056369848147887887317449099739627452478628920292098", + "14444070019150868677613599250973948199109012042816076287250439535772792130077", + "1409252976182963241946410463334709182528602279352691338387472032099813179046", + "11952347912832393217697966069578145988700552783923451999893230696985016550129", + "20459418532142116378042115609733769114184932636063412849272885250416180990630", + "3551971311295052916014184741437675944777358383038912487497306982998424802991", + "17338671457268891559841041605089015291458737421392337141908035764681448452092", + "20968343054904260510606682646995696017767749421435540168583945905747214877394", + "21712394213185895207403184789091961285240129068046002376558625468598692072071", + "7634967402785998572909457534910529497063727153788753124164657866643377741907", + "19849927917300065970782875128524194424999119369431340457180683372405088384137", + "15012913963479022629048645986836271605475934011232886489828149973693122241964", + "9429717687014890707005420840742234597648724772846627377968206537995029096421", + "2567188197501474210757971384017028034832891166328657542206945684532913152990", + "8254698320328328672738727190211578815599233720695888584349629846354725718490", + "17284254811803472776585305793072871837922263190673228934354908231725144505079", + "14935255735939697495362584034949670161356891451287847368207930876840786524288", + "15799240079228766996307573606262831147697506141321254927976890662889015144612", + "6455095726770008351501849656650198371508317633852287113372325351871125339180", + "13561632639572229898860364095655372109051739280251390860766414218384828962360", + "3063643914261015068338656198636076696013594289440915753234546912875968584726", + "523490668760084285231907250925228161954264885492417167368043075448446094968", + "8963235882996992436242593243282162189452480942366129170093695041925502792493", + "2822355405499444122504244694377602321152414880107322020961544617958820110728", + "9900962978347352867015759419877923426715239847996616012083851486863818525240", + "8479746689369630997477042503538860863372007378750903762662394891426272525808", + "16389683851395581748247134197181679668454182692374139436248933773102047859911", + "53288238128999697340898456182379718764153689572603829218558226366719788005", + "19367926616347613140684682635393090785537600078242140764999539757098535716540", + "15865351283225378120524944291419029041302905726785499676495076019805256191362", + "3306541431760940351422576606963790342897050957331449111434563465335195596604", + "15691440019263506851813897307599578949230726834044329611626721124859202292981", + "5471927110529676053035425189646230106778686168719884591627642899285735629074", + "21754232156244379849332999145646221923063494235010275675811923863906511018870", + "12691978963824033486319665437354495198660382717956695927101804737187302726210", + "18287930615191907073854709805749959615454583394829992950666668999967617218809", + "19651897318700114806760486800306423305046969021116395207725042165875775288623", + "14108212236437313184992367338426565912378398742941435667080068116020546770682", + "16999325425454851471000400176480399381440753089661500361295692310681628123507", + "3115621817064730307934070024882122808835435845590131246374645701268123516395", + "7664237802103239138782764325077413451892718354904226049706189059570344004578", + "10628974555344045044380058670185713528581140293158876652923633181919907860739", + "14113596208322488293154755998142305972509624369829087071590744312673264784912", + "17464956897498328434098241327703906248729109105264442832066471889452262140737", + "3306236514619936703817193243747225028048646569484446722868278187142340885606", + "1623642646505969781806818562148062924427827461950798461560800231691312453957", + "20812904557023426950178015281581063697341146335603865156046444216576536158479", + "19408105996475064309045111496637572365855316086113347445820645560307476671120", + "19899769975875815048945884537401834080401837433178820364413791122141366658704", + "19592039825831921790276944621603771771915464940035526138844575559711905257984", + "16653297576149039984587104062729832787430849403617977005988111857683169262526", + "9600749907793807254814822662348056400280037933879058284466549496283747987313", + "4481371968429561669477332540784950497549507725682080986909471734800926537000", + "901971752662861027246344512729868409066847439697888994727629776003122477075", + "13205641411901359105414809635566186722302364256582520884685762468367549487609", + "8771921370123544696277176259984315154171500395079224159374189372693912295384", + "5174975161282723002634664972730414710732925114695376584668768207090019053678", + "19270513804464040100949263044402755496385234769453916866026752765439740596759", + "21159175882383323828715178134104379154154907971797847682741774730382256211237", + "12713085776674794152643870369276859988930155976989577699924346924648017649124", + "14416880591724909781230787197474161383703110620356522560207527702042764430665", + "5782775977590089860582593877799736603230821040322957359619681360364621989990", + "4476458740184410293724437897879594951652562625859170065641235900450054752275", + "2721806229175990973363729919729809746838498228946916302499795336059265315654", + "20919749045138523262313225773151110447007311142131682485436367417448435698000", + "8313744079461516868330165973726769605125778482558912200259619501953066297351", + "4735608165219550054732197605756732753518722979705458514715803339137012677807", + "7197734920840596436781335789800800949073525267054200029424794448358957228282", + "21023118349982419692339995216673708826739468972915634059252379895041124776477", + "7361684304536881013610906796097454038829033466585052789468836114776912784525", + "20982662530655024673207268539471241193218964077396502472563124088553292010609", + "16223276590978113205386347640415975693035764887550533934078089596214147119142", + "16855050273631391834996719887442993598537594692357829619807594900987141342279", + "9447886870473301829371932239272857617552029212858469830955734178259213680272", + "20832064076469714454949991237859093636177561963691091143340405814107144265638", + "8639667923938888615556970427023258757087537766667818930469506422086574042603", + "17264766416417856804513499340974616744303058001571461329697855072803549043449", + "17850880215521300838724196369662075783108857625488733013926883881070746974079", + "2555423581835096020752363625782761123603321345233444467023623837801470232396", + "287393475373800420687070196983041262901299109116957212897422666775745916878", + "8502409129845334137896426506380979458386453989629214624807418474240644490658", + "17018903560361810748608552810594034468026623560117430817297647108682661433399", + "3048293276213593502083356886493678223968770697826563060833194090969377703128", + "21410637907835525984647506144589609474305790431496436733818309371828966617019", + "11884766464429332615888730245830253775469158860120272524933094891366345545984", + "1404751133279371734382153067811682787285007352946819870030827863597870240066", + "18533103709771766357713924189614422821669511650512088621974348524596673777463", + "2245746843374377731985282335022648129668387693745835052018700591124673151499", + "7164798246992914028921284523335117888604816694373355756451086598952914018661", + "6547792475751145534229851742995938089417776974069115284886265458921995253635", + "20902177810420148842648372790754609922473216539157084210657172695295335305053", + "6725829188763173291829388608607411327488468861041838394166490103857184986505", + "14877246205681405878429147764831613204208078224683457409980647858475648710211", + "12044111044800943869325640584272709159106488829628178962818078442934011738582", + "19605231155471418918091518582371485038913937726379108169052428896528644150703", + "7703294969707260451899023373169584023358521879520637237253564638222384534453", + "8646812596577526500078198446696820195057640949335282980927180585777038852172", + "21737243780210633072252501142816485256289884504027720271756348514717163502438", + "11106629219138713431737722416937344915839184447234152498273695046605412717853", + "10776231601191535915748483208366958548059216019309096513250460388757814780540", + "9570793764533387448268417521329594330897759769553789809193103230402046525968", + "17679865315281322154038759337512810633677645281028940369384987651076506608513", + "20575490035801628850469464797342943234291317406364190353186174477643080398289", + "1245923103633930975302215942611213577964303545113844948930979175833556792302", + "14007490679559289327503467618330290757962057021886611213555145469946035182448", + "11028796735236022300483437389742268989348582903251651368706557599156682851942", + "1556450270937619861275756908441730263281625715497049030992434005539032147429", + "7378353234583563315614681819214067843031828330274954124783678863055213281539", + "14666996645204144598922205770204386241062541706976832276647424877691625110244", + "753795180391574509648864319525512763100129705111118203505048417049290278879", + "14153559413038217294172720643039696432149555359470735393924921410988488820985", + "17643076982867120574893553230100217333463357095994549461637738074442388603622", + "3578933943193517852178000450144796032210613197873582924120228282439809826887", + "15520463167953252157432945917399723977264972626433304226673421190651502497625", + "17211537329205623730168383274756825219539545439487707555783029872192419895198", + "9802003881898384149235538364488830129822967333024012959168976309447054827302", + "4277753965394311084543194762939932271526308682615262493526499362805330624922", + "3936260403240718180440931086768355327655463497858616640513270218914599723610", + "11517607066800361513856730397176372083814736323098377670788083637850655404741", + "2243213365229554429675654538157029776597503775940697016289614299870958139625", + "19764298184704281654348385808308820923079630109704889940202178107926508921613", + "21440201729469627284915716136426033084639303793622913729895539439609852676483", + "18865357344271429482320975819341476664123167289165510208121280487753271844163", + "12062934463233700754743031150573591813637006411260363192982014497532786059086", + "7253500424782653645261846666860267886758998073654253013071356444451212225059", + "17688280285743970528696341296072681725398428119833052669003422836815517127146", + "7296931094727393709963826609746626294291079390951795401509403882666626382710", + "8944898010968990710934167772370816765379846965311172103743060979737069116954", + "2895100563060833104606048044246656016901407622471819180004918143679777127583", + "10049128690674011365994516711808882356477362224046294433720641467109283787161", + "20809590643316933764584815864182144455784236186778450054494378700964393080470", + "2552959198983852034601282535517138215015669324552132996051383405020865478519", + "90024510729703363025920398892759893841503303159824575434815310687587906738", + "2438402150656393945636277445769226062775867161339657212187540106747284678617", + "10290839327200266675482043018636710429195359786802953032677212989307055380477", + "13366445998820331718698457472184546771717827833722684584206646838628538349970", + "12045715338147601791240214774093449384918104627310882799600088155093595426276", + "5295711531017416568724842720044505396040371477829249515344865310124246197980", + "16400063927954668864214504458520657934817841218321732614334739112437744352318", + "21834268150215228276838182444784793459928322632598421948695291555558497507284", + "4036405082017497222204927366812040809401891394189816634089482334837550533053", + "16758010699927998006904853632308429832737059675503937996784758385473294734767", + "6851323788614107612891077153396783509346340795017622764414273403342247098685", + "21242220940510077461844092816930517552035812681947928514550083154475902790925", + "13803558028343943472219115813916734264304316037691831164863336831915596589841", + "3876897423511600793257560948470150518761848801469200306022138001232700430094", + "7873917719931107857936523614722924550340227737594948157729078597073258431354", + "17791290553613199208672721864188024787529313403544902623821727403694762316966", + "4509080040196139117885863635009327583647452661266153933806446923314670630597", + "7350149044068266301432651752105457672961955568647004218727518439545294630219", + "11503072465150450162639608555774152895393875487845468619329760555955360089418", + "20146546283112595992038998747954746814923105695946909075842241737548546646215", + "3500661581148278300592438598016870333493992384875150415517341312342838567317", + "16461061867640825231072403805782329353930546739796841505995525047519357319535", + "17419473457085759867681212087981664406569947154579733189885809269345147937957", + "10330084554893529954763451132860402555512473901655458870771994310647661645739", + "10271306411842332407495000297515719691188342515212338700550984155237667774587", + "6010614376730465540824294088181704678570427743830238021642540164120787031818", + "12211956821531047081164047854393417207581329108889222238958146335322243725690", + "18026227011560759880520787750218090767851915271628616216035782860818091527867", + "18039053385352422125173521135868691523650276298214786020953324823686654441993", + "21572961947836757087236508142423227244602094617521885308238188781724980380450", + "6952631457061838308719934275464119179697248499343745875411129234786468691942", + "19811900079804396120677471049757957457743518480096470411626681111649768726526", + "163137711020707070877550733174177256426010367795106166965347507600009523229", + "1531253433144642057738530256773902523524280331634644205392973718192895734956", + "1179712150612295194734157884657760659826346075044485388437451245699680152105", + "2729710616419138683641657553469892671426309689560939345577118202079250084236", + "14266054344296739494440365684098525973737250786286154516241047965660978988270", + "15026169712587706365294070206936700891511371850326743083547381843796350317368", + "18744742519058537072956390943103111325221767611576688631532967525091526599212", + "6993198712278030093898597400393962073007955430382142820465138278710889662382", + "2553141820754951085470014997086944345325965756649326165555729099768987826967", + "9720940197604032044323074196225989267295799246574437528002073812640512558241", + "17856885570331180579712689563952926777618369125836010409080422245803122826927", + "9316647413775248445962902207564207480723728241070369123275732644991331488677", + "14494957198204561202111268006350266984666563257512595231100502951029167863544", + "20990514914454458611724181917618445270429441499421925167963524958552424564602", + "4471904645719453756418626304970674400441871174741396875599881395188613340474", + "4684361390046158999250125407700867527620809473388545037966531069883415383449", + "21702205249470184149930308057282607474924454457414198304211876480322358864836", + "14125417641286741606705374243071146588481196226954437992372061774013416347499", + "3703736300177825900679993762695155186404123107499914435631005694624510941992", + "11732888492654216406505460448043873705030240266921135732988822696780340208946", + "11933466978112969006229130396464073172307879223153980062784019174478752039844", + "15171000950368540171664824924290660122824429581656415561069429955358449308299", + "10369486846273450968605452628621668108353399309158716739554974304206007657467", + "13426080534643441487992009643036732384216019130391445221701162507604477874092", + "17202835976136422766224805235458239972845135749171745123647755691905320873240", + "13003013510914746051603710434071109936447243726946691715828935535255320452661", + "1672563195387555152025850519159013960714433928303467231569405674199001377183", + "3249806644396047380745964934114987054912135505311534093405371566690536038255", + "10852655605415322578561541069174913253244788503288539822168510897694861970183", + "21180450898750475729531605846185254439124003420647553715507766256141444893981", + "17598208134643261198923845998804266441503487273381944592003474913329048411161", + "10560430398658881239820534975865220513135495522309722014743734708940471341227", + "15940735766678288146218967299383564752403182243037693045705028442779781161429", + "19840721391058424508364187473208857771270089687968326863727366457103504697646", + "15270655938735284505864085399423312069359806468400434801172200215370037695961", + "1774340315063362619348165557959238806046379939754082379283600857530102490105", + "1931473872849034457489264575043647960104881629328524494861951197166741512954", + "16037503795660448209256659529206619750469738788435653064537663524466496564778", + "11157294725944164923451173319705000360949753554936504404517553331688337467555", + "1860977382172821235372346764481514761526862185187436148114012977011560461667", + "7554608809888667328824255380474433026692004240460849082770678269699854930661", + "3812849161473445621072899414157938525926417912914872301607900000816805333995", + "3248257538394266288019799770714987078883986769586407060306626631267726656239", + "8987711810247040851774885826425578893413569065851274558714617106373602624631", + "7002808931888012021033077336936962976185388694934605760542880449764655464580", + "16260071391005121724128685673030785454758719454582904114834030329423907463299", + "17301316944867831355470414388420250906247821380560618462329913428602672908525", + "19139125176792761113369565771916762091089412261263410641074341972472699592037", + "8186463001964086803315373078973607488567538876768905527322257145549601226661", + "19715283945499391078117757449557157262014864431503022516493444981357377102136", + "10201366877793384824708227219959809605313335011896439133665539069386849213406", + "18657679796594390529926518390837962343392692362549805385222541270238612706361", + "7486580570539115306934569904905950241452751461260355433571179343632374451808", + "13210602081373832317780583229140785143149759155013988676929972636959898239126", + "20547921916591862189543062064340935912295194861078724580978244369580188659785", + "11184938862747662895633149920437248716375494917732149572183883382335006977362", + "4564430184172465843641043220857544376847031874739611967386619487659896927977", + "7141474497649513954000384074955704105249233034539438859110223132506810864380", + "18152904455695511183625765039917137650094202440231199346119983777378283365068", + "3538073603808733813419298816140509998806938194073503117538238879763400661830", + "9176198085009858977059589400831647890975463959467315677036348254379008625292", + "5358677810755468005042017406822742639656433122059352601335552810789858869197", + "14917263528903508585499167331159661105708948785920472586609175307188322712065", + "17459264445642522670023202416244479198596207282984954048459225794985333792860", + "18314375996391380435570814127239902609672007999779514985166492867417826496725", + "3831982114680501270333115239154594098561342624259756350309439921335535690506", + "20912918104665637113589191778450022009791755974254243256571842103252066396138", + "13641033990672188411010402475025484333293999311081344333460071084676167264579", + "11812251882211406110219542299092601278938747815723382992634003397593468524422", + "6831378526897417143208721431885442730179700977603214290972417120210221051272", + "20566372962271447276247738296560940562676849411310335464134428317792972599703", + "12645078398048087182293658191360551774451388858142913314124550234755654392800", + "18848512616065065931619133098070120448249278799586409723288680340437325332421", + "1336951976108001940844939305921535183133161033109848951359362159969984671656", + "12850569062942139371781102794220985236887099863147625303610933201225464388254", + "17095240610637692323679811976243261972135774635008876108449261858372552686880", + "9827905280236638995189439521379169431346954052078412963417337148237294494508", + "21756974284757687824674400821247092844224232050435881799264172836701664225928", + "21071024335098050648915070341467331164448998348673899538194227711074199728981", + "4575704980288845009798550678411618457862054601563658773482638217288811407473", + "17637889149961283902802970141414602882266203898461797433796173000208254839889", + "11333768795263093043465906246853631488076833683398258097056018834885708094795", + "13237413177387831469342322098638791101575804381099372225484902457329675766563", + "17644690723564191319649063794394358703071761592119981992193950976485565800738", + "4591566130221102246468298018361020201365534301234509687763578673293960293459", + "16880258892960147740975242226985692424894589139379261939284621634132328476631", + "10753253761433799332298845732798867046726308621524332340930656512278172577964", + "6294042736878992012448357827249736553756583459648169754872627240315677645950", + "2668606416345354198548309314080005930475082556427535177631964192773305378348", + "5244780336143933367304708103198860683485779489995095073384345019218674347056", + "6113177497707123341972721698332981926287006839555002718473535918066824223034", + "19824522867272770281143825792424101858356781501070734931688923418771980499872", + "16889524920861245550560790459946848535789402393200784095555799980201051324212", + "8007523897573727144887255293212203546407757295680344011669395834627039767919", + "10043972607564040581522896355091368769007227979993058004755493078808025405133", + "10815879623965899246220301442969866254505652264417667341661157537312485515831", + "11534947289183790267549825879293816175921818072299730180286203718992863665552", + "19282679238458671513808783870622522462539288436443040483594699323647255140729", + "20993973658899710060621174143488118082242133512002997296378188507484929417581", + "2207137584463635998515616548473102748156816296061395772266322132360447742245", + "1906176303891379788266254493945992658935732580384991589403254257107793504984", + "356009908103458769499047304270411564071705602811825215044685668712377389204", + "21267156990661889558161221297711049481880243497825013463587260785886036981575", + "9511026544995595131578865949819301816364113937202346971334793771967449171857", + "8732150772674776446232653795368043280414095192666412295836279503085501609742", + "10433912179719211724964521733928883750833304291050102060071171195374738464494", + "5892753211405353458072760353770523366305643800457149832130812257775605711230", + "6072173732058843459603530982418555383917328480859906190255818160437636798624", + "12537411810282601586400313857613201485446415283134353086651901233218273826014", + "6917382548294560607950289035591212885648538608975878502811670462230179895717", + "17522466317865452308300593635805699602531255301383258028599652149794243037329", + "13441087273853521452830121325700732493909228483089386934391461759499905593163", + "16343244966838333682425984150791963916204788641990311583878776392583665902296", + "5360331632046740140575498209972912123753748791093482084409464010367952817372", + "17326261745640411514384830391871497358293487167841563962966663074154000109605", + "11832463539058003103012764436368900007479300735896611859760466513560696030373", + "9332530307651641794919337618798260519292245685937263028043423079258661105173", + "18589656636963228136343540063542372097923688201290293330159927366465643313091", + "19867733544550615468612533273987508641098463295160374186223389748717034468502", + "21122779377039628476144200947919650885809091682970982371472379500808529828990", + "3027016623356326893238890601376138710903222026620502687697483362378227239102", + "18688538985571458464198537125324538836996766056814806096466271703920561848229", + "7011851562043637216927989978271187524190844597096440024970832115577672150682", + "6976276474543022394298383474157051715295086098335344328214084449502467982887", + "17332130117530488369551999111592288939240424248347612731261799228734373086235", + "11760005773828232229811117479200618747574322788890348748345009852749579147876", + "2298592572354833593457878362078773063587664198044761229360308644323231458063", + "17955258451898615415362730916130728340133607644716002859752428726933265234272", + "1495160066997898368260698151648234013994510168440470178901776412293213577673", + "9194083678217569822513593808499809262580238989709793956568701401108176409146", + "7873147134182176342443626581580817861100691419143183095128784103924898338758", + "16767569430250994260298195321407617265691776202802582359934917139510241475993", + "21740623993440608033243076692676917908105712281956015230238773795741552902992", + "13259811027098062609299383180292487555512077058811432839971137250443197786014", + "9862179315713982904488948286370860706178740748908626205136774943873439543978", + "5283798667794826693954690769969228006851703579605314207478989967594232871036", + "4252584733067990475653166725237187089775889157323429734580856783776820381431", + "12973205666610532230766585121029838349435114222222352682635156927179891214439", + "287633827640277544463301235265949941438188349560881553375976425107945733447", + "9088334958857736274159987824864641712847858830802240840003039491617952671684", + "2339929702478917712535010770935571285116447513742167345028949204167372785538", + "15676860121778371383024352289403909294835655990865877574625753945822204636877", + "12764603061737711341313014678756180651482961849974746341425597804386430732092", + "4838308895208597109198370123135942497394705098532245211834944708850927126449", + "15227615340505775109084051882530634970803647261111629470904659446755110863564", + "4983488923883229416538630982432842594678976476580011041728398828823305128910", + "13108944499697608875788753758764556629418014383923261351989967790696566041318", + "12840723598548611269818280989252209606886290717050913748780108061152527561849", + "2952676596403552231673795576822772308483388566436384169804788313391241403511", + "6122163114479319080164532464109940924328926458633830912986940513870140253539", + "10991285734105320972904604247873526149047475605919579896179431815938092849653", + "7998273930160782917435322581864589776803914924450929021174489205585585635343", + "18738565009620163104846733922742158284630011729404799909319718271414375079555", + "5267789578561581666385683546519898747246978052550117863765974145167441262779", + "11531529707082144080873135819545917882620969294873081450175882450957416920799", + "18780101368050367674291690806832757013309591034560713102040071432479406757422", + "4294785451272921756656022299749163922793391211928749628139961864310109196353", + "4709384663557124742915933403093691049837232917871836683405730215659929289723", + "14881387167977166163300411047605462807380624840482159906092434632182882848470", + "17882364021447094903093424534075826770336257800280425962604559742290433282224", + "11702502532561044172145178834042891352292518800414731965233576503757342967409", + "14059864978850405613608096166397211820819484772138676470000521070869797461865", + "1780174207952381531321288553792322300359882548955331030522024911690744446923", + "13028727766195703514931384985263026051594836628118028800883341509333550916528", + "7588265429164678210439639249095081332309245305288210445601269417147124081569", + "9642265589138009721420730439928969906364025440120910401773575492361904450076", + "10203823545087224726196594521548654230876294164502500177215475003344697986058", + "17120058099854629688232575694825768634386944759592366006492580522962871073389", + "4222192359475448381394699451673800507222765289015074154697111370954996368735", + "3993784121910007141124454385846036053674166308991223986620192089439929299772", + "8723933019744566416337803833347252328328807961776170035099333968332078001000", + "9712660217038548658606833881897330347907173853654574936296015219821374200265", + "2760535358459121107870901826259907500576486637522442431581134088651955908758", + "17117109873353435959712351313249763994000699552995926658366410602189225375298", + "15834046942685590805387820842211523319635967093317938586307927436446892526328", + "20540611330857822392080560199608407622162192943120548518785800532555099780387", + "859364761671967022958373408533303411107861007743219314752373993491065278500", + "2969887420026581030514962118165604753428498019600416703008438822193590952680", + "18165207804423071202274226128984095583162198441473954125845000361590859071975", + "1625906472120027348769657238152772240899619610263515830625917309020852034930", + "20463932955653127762144495520150037479624855783264043490725916287067071124695", + "4638144661708592747026214570394653618741348169520846615537958570570478926642", + "14161574596481971299443338101939032775184122775644932228990090697003591542529", + "8347184524978696917103428134030602422953969299144482352589164719698955992623", + "15813323300222999983430155647025925955486314051966513470055962106024932062990", + "15881789235120065904292616653574390818033995432226550640643889297228035707625", + "7140692095240485283381028349925581289679980854833888018152159900503732423858", + "19010917339674038277064587622527941116247281757743905062456743932291367865210", + "14626089726879708665184866329935404127909714172968102824599946119716141024551", + "7794590602415713436531570478653055404401179384350609462543106242181653823825", + "8762400452539506415642761970628528841241237486958209742944443338295503154358", + "8591771034219068368320608087757251879675617969654197927442982939795060041474", + "16722135923767200283514351262528880285637400101562656889998995533577538049598", + "89464124861540540773346410834052352014308242060847315373495379383140228595", + "527363371365063907312062782674442937761539145521639367736772902232593392996", + "2507841963726490485511335482108618130879932510673214677761709746737826243610", + "11600559113928855816564236457966654301430060930571013803772049573600205529037", + "4588632959705258135254026492226920833318427708514752831324568512946022675194", + "10614112216523109461118034753484116653637318222297437542653638803709000476069", + "9172525851971416839816439437260881435627775489982516326698553647792223169716", + "5792656644651436247861358501481981837063028644438334753333821264192324104118", + "12896881102412475846285309217744373816727588379892851016206737393174951411645", + "11468716910877986153680082696909131615615571055661580004775449060276352989554", + "16433370238306953119452573705529989067794419309147323079675422756276020408035", + "1273883504219371718563834088540759123243117439006904940363157254976913033897", + "12035946135822991823593467599431819071656367730183582372112230833767543617935", + "1584468415480349659495095652095228645182004993793756033988320959253925346826", + "21386287619427343417104905630296265178906342440158736945069239788114458023785", + "8353771053561995945612416527531464945642582086497086814556341372197660908215", + "21451387710023590786286131498774138351594763095518652036485484549943942198584", + "4310607037909191923700937557843586755810945477870372794480216273434853743125", + "14760194187040766562518106717503624683554510559335502353786749509033100485706", + "1989722256243159180961728174407914946336713030784288455577084756567680416269", + "6686508185527220254925691658356528598024140978521686081374828691583660936030", + "10702736220547282218129581824426150350875608542279410979052446031034395032616", + "21518832429688884430739158604898893220375510075322761868526168806092202700739", + "18894734518217824473490018310535147336620281698236505678307171602214420149474", + "13651080497325379255207286313267854150042736096310351308026009382931418587349", + "13005423795701931110183481387065874846108794433756894029607220148510576563022", + "1791508640954698127256430696076954377556671221508186631495000025000560314469", + "17328036510202475587667192203250772802218778208191122529787186719212302087427", + "10657963758634548274685968417226435837089760791121929723402922305566484150027", + "1373906129582260565717689440357805911187492936689152530374097510483815842229", + "9959127778329368795108284764279846598110110516049484774588901738715030294501", + "7658768549089819420025961609972525260083323240153758777076357709438863924071", + "8556349016311563259984530239150734383111823917689615136867121162886430935013", + "5584784611907644257624218172047384219563403421449130896517357173082649535408", + "12250428484720929053394094480089037225116943346244386549438429776642627577081", + "5894587027805929865241808030013307622768224687737099108946198448930484926182", + "4640000227377695114780725548825120725107463526354871525420448370293105443156", + "14209260856334030476503172714481690115491571124367768551485878619193949022436", + "12847259932859893012278918973069680996469229489435970735462165583338364177587", + "14707765228045361126133465815432967736466274565508518646993164305646449611538", + "4776938221827642494595066454007024055266353658953900973539209110395754289252", + "17345634507638246813894504405651157443245008745673515073520154967327741083655", + "14030342049076255140305545268617582878430218358427444316024303212302433090888", + "3081608613732784391792246085705209806228642369232320349344206761814264266822", + "6925544598968002128460287121334907968171640408686960604018975554503413545297", + "6436833790683001261207201071187060041301666725391276179040664384301147754533", + "15822971282077757697408895884235910303980603368062126288859779292088533186609", + "11405645516905544384081084440311050207481018746793339445931815864373601814301", + "8057851956466271394801907096296448157702512819801554261185443997708792484686", + "13400701029177845728937105456192536667124279186738602373189177143094323643048", + "11966218129646969536507242899486947493941404691279766045602150839476320695158", + "15426703084090770508053125497176086377457196612492405232875849008237608103107", + "6284964429736232174040445851307654679317453722810844133660225254133335862235", + "13541475439682330895175095901646773164410091796597408681295206023344696136380", + "746760486363018018017386520506969904420399625886684141218702464564311095236", + "17524104887381990279039312517306518062132617046713819525042298049738730617133", + "879502104476554315787832344079612181105511927448955207253764010183827781938", + "3322243116889956217287455917347322211093649776443788544812848110324752380511", + "20993304704177227988946248236539217504266305909163005794546179470620336655896", + "15297086470600095823350435092749336605284701364464412627268330863173424318784", + "7926831990131610326614433341787624222916431629609577848917665290693634271603", + "11271157869866761687706729926953398656059815012440629723619426315368733378302", + "18299023753364987381538199358983670915973737470621191699559419975990005285294", + "1896523269277706381575521315908503178186583134996425770080392278759498041496", + "12890346501512276297101448381578516979954988837373044432656264176509301317397", + "50683999778726139626379750772467319774169558910797110829251191014869406584", + "2535823474764747710030290051647541950275171691698903262059516064562837569538", + "21244438525273542723000364561332886617309030482024380299212725538173747919525", + "3566180332124074953578668480009215478053830658040437750684892555496819182968", + "6934760873335280553519652185111971021084408206212600990256329557702647231767", + "10716990663016621229729175818785901525044168020825517922453745625674725718755", + "16032906457960033408967366361214250817346739568272864358714497113973437241307", + "3843556041176221664914001975628278428195138883854078534543734996192939783081", + "20159201956050700213417267403145919082229292739510798606395981692137742005549", + "3946206250066677142194595031646387588831888396304269420593204783498093935566", + "15041638172488186356886629449548027800133537204416329580391498341162500701165", + "13278796648404195845011313500265526847654531008699937318834233691653233844557", + "9401997027308433525669054020953469816624994577441846092561461184007344721828", + "1983197599992175511555068756140439953159901969155075046465287261273690782516", + "14404434816783701517710626513536590424905462755924351795276920360393132910619", + "2265153992995968786485646516094566778663253373238301922539677847420824091645", + "7566790081073938501935204486802755123563765476651717380300659543531475311797", + "847895502208273199247564103189534214890423186204714047221696858073193176165", + "11963011786171849258296957535441472428283852585041548938428326288746833247421", + "17377294274187023009074658259347356248383537938503682836124988411002248431249", + "1025753579901801011233785198665030697958075722777337374303401815648687864152", + "19284019504590857558072823196945292661333776228606049953803221551974077538854", + "314204118013777455817026750462293710777754680076389729833422020056750157783", + "16480262479960754021966845575947830901794064422051876558808623512389290214607", + "1118467174052506904085279606913979573425427098904935496893365844271017524128", + "19632743231294128529717484081406317899334378150630214776880617163102648968680", + "3911642624469919820429547144355740104724495930390962678347147942124333228884", + "2066832981312040153658964981292230050110396574171753029116180129049784719869", + "5311750082320962006044746418239249402583149931726835813032955695199748985964", + "1668605805291951132468779303736499626803321838797652736089353191224211818512", + "8117527083300107176240321998574781132203045054266787492339353711594335529709", + "21295747743511951700871264160352432803897824342737595930859857743511472313518", + "17388383598447322131131146498858701344049179581377651662282122447439867066027", + "1285164961547283134680842635704675169007050470102043475645519044978941760131", + "1875066966449664224570600546742926101175136253576702799078476168727336032193", + "376316574400845651365366460471492295014146006765806307181242245707469972823", + "17926797393698382320151787394969682377223530524132422415508830165925306730854", + "12888413344274698384267384086384474622307363694087246755565655858542934044367", + "2287154516202749380541196057272317720805230156010471100500251268302509398358", + "12091269896021030725381711946583875149737687962883250402694805482865888072651", + "2866048333839936509265230844807967492604533527765757660355779329791008604324", + "11835071699154512268334373994146252361488263767766913426957050581680705629365", + "9769574961167512396467538163460983111263497516872198932597594846643533678886", + "6652717430998307400653769850757364796566010885765190753303419969578998970914", + "11706514210298927940240806568375915039663039872151275540599047371226868127844", + "14503173259289644850389467422758670421081762433800521230764199189580041755978", + "14819563269715549434612532555018280611140077716865566327825448288943759458075", + "10680481968851597237106707337987338182186447633463836689709242917734021556532", + "8842568676211857695295830062491586931539601201165478151045194686251206282625", + "6530738090493870852939715092085577161577833016969257865187421674974986108366", + "9122348442271830670856465114634608231502719005088669868106730029655164877153", + "8276880496499930549062062890222235677345093568048001523633692507246467514288", + "9159103567267771525632277936018480854652194329966882107551346987942837974086", + "16469319105211987147868896414457254521940280033150431057117354850784171354412", + "10187807714948737954868581824072203418416253898435396377923197796615888421106", + "12487902950231724077418777903531512931204649041424531773792238730349062186046", + "7128847500406995361903929050204217572789701023482188856789540128660376992648", + "5624482939513897521205456066520453513748417048856589163079298385117683428424", + "16435310041815433976313715464222273283215148709695464992270777828213079073600", + "6053110243546717824144224394047966869215089006720239359415188827006018953287", + "12155283178966050547567835509058470201630806787546132608678364857190533906654", + "4516299433052637321210501303761417706907638446979735686067715866937355560218", + "12667848710309960989666753249292981950689410172688589690222261363826904222200", + "20798966719534261926649504156624517814254561351583702314533431478871413942185", + "17967549743799112209110259394081841952094330269294500558315036929658897218889", + "17847714150935110939206476281446907230585782511584357788270839909827513843593", + "3706525735092193828297046649401002937097518714707649753675691741717374375329", + "7951431498272893618049118070591089643051946670270891223821638082032777246372", + "18050610739452223409884731462410705437134574403721907246909018802102148878228", + "7681233452521913704953218921671929208806506209990716853575264423980840711485", + "13423126358538883907022095116894303361057277664724853411599423630968340874618", + "16385186307753207860551232886321416660506143479955163989130433021552655688751", + "19863338799254785908021006319182666048802363286601429063342266271260238637645", + "18613864443384979831007688916611349052439959929170133474546815634162799183324", + "16332951432721718166552572222119667287478083212745997662304261644978474988848", + "15183520838042840868796366239500461619005160337097541115133209726620419190272", + "21819632461581352219365791577295628387565956152415171439862191463144627141459", + "14048533271709057229957508911818231901369646548751751133058494379648714713155", + "5460165509823151384714546779805746551465260311624889230527298343216031522426", + "7812320479950772901428223193019468171803323319794359571259026602925413892220", + "6861047298949697363524436975701264276161947671796912946484193048676968364570", + "15350000814702190667768044439440847568690389883802239839819456575900015807805", + "13617689242559757166326885110516025083265117478902533337635065578646035372109", + "7476843340092386505769477537028758806323421556263090744578424892783190404262", + "14826737006932111183364733677544376863485041173382717893595766527131815613201", + "20482222533304950354756700764919164516648372491559583778492129756055561547902", + "20761958166613639518991371846609568602964716408676516216766187562459779452764", + "383428479364004198529582849470440881390404766297544454256885030893768964772", + "9096063532469724072461386558496798312147772678120608720218640484115240695555", + "2933312980053872879818024429044461919397766967639471518371949595510364515880", + "9299316327986512295931957414766538459714522358165090755549540858707887390215", + "13488092390166418062347006407529577052576958127764741552594028975338262414671", + "9829077383468426688376344724031102291718825262406301642537010429132676001524", + "17306252922949843274941351200908184305715966839608017699204279919732909328171", + "17087445274779483487397521683084452362124400111143021068068404568736590545834", + "12957176730411938817539171430465916443660534484629425102659921020718336935547", + "13051137677735706323786222675600013609665436923061132301167672971485926193799", + "16896405620483797393236904159048703206424672638297382666760526590725313874095", + "12982786219003483259026043754729673677479461289605569386777999422861331902406", + "2025152781178874327274493576789397778291707054468278801142821085671743579422", + "4335304794121497318666085856101737871981186359433585080678838732369068576376", + "12639022425423716913754007933194900468141465623346480681305462334218095484056", + "8623058240535559764777469232729047628411050024996927445391922152785990079007", + "20506335897603554545414833361572976187621941529253485759521266317325651663673", + "21253532477517846630911098846446871983813752932337294199438073014278982612265", + "7690372912700906813961714562821752274841425123411432854046237798188463759714", + "14774079281706150157728885377093724231164512116594840467791057304501073046227", + "2057509476014278951878531863305791034496952436217061173216277825372715694262", + "11541539364361827516114507593054831030438985642093836719960774810015976254752", + "14751271529818502394785288765886130704541730104426205431367858892918636267768", + "20653824984831671829709191706456216192511770845470322444515326861038843106056", + "21794617070379980226628233360507465028625654800811478227502718633923944312665", + "12337675699660313500286885345145964244870355316351529815248466118884457457726", + "9527268832683847687152279087478212995292048665053951255936397859316414854388", + "9676848840550815858798680133955006115514366150476209999729932493584446713554", + "13718457801302749437654397628557435190188493872576865462727587887532950355038", + "3867324761996955514268635801392015956546058763053808698121442586705196957126", + "3945177530905850250597694151686052963152292050981415927393741981018273529029", + "9473309264240097344589730948390594137445381539328377904021534848676986947654", + "9996385364578703060792470002743943020203968343080025005448261317627854389642", + "7671875350824730767796781844941080563527207835942241721783016221076158519589", + "21342545963270813418079341151020475012992255268798567166496112378940512106806", + "8273987785742417777035293384288140933989540189974521462819168552306151314447", + "16162516432221515902182136237589464594279404001688534734264317844425059661177", + "13403805360231306331901242144909690435939614369536490494604700225511328976179", + "21640586272648741017978258285249302214133507217427529405059807093199643534960", + "13103298902142682492657114828016226525668797790480257241873358498200705227711", + "3251016042668618943397347852790038985788901898321438389540422820491143534512", + "19418047092207786422594959190763286166909227118034848590779428639908706128141", + "16018739283901993654877226905623763753431466873170287728134253742237701259452", + "17444946939314191179654576411859454283526068144165928707555552999432295774923", + "15128585121813842824984220733706899232093493608422847549831681212432769497258", + "14256042951767483060181875165067417249801798477941578288282791927486641413650", + "9120441102041865153472829977920901798404277796873323854759847010789551358565", + "16824614118285494743762017938800453590083305489658290570438237922163546634147", + "14066296536773839458459021526010711790088172526749593960873828093107983908643", + "5017173601281838065126812757441844223954524936921765951283764327160567691674", + "6254306109691187702602819001106221686689064495376117914248886700342291435542", + "8093845266500847743481398069264511597137596242374064050252920004891985410254", + "3578536483838573103029984288079029549393340013685219142070711310306738904812", + "4046636520929111297180341957015914921052723918845140326762981717454102872515", + "7446649718318072281388998859356555024817100796345520325890349758135889404482", + "12366828085438268177090003236704251656102820345668679675414643578247762288715", + "10235336658636336826692093860668421345161445948384101191460476320469211290719", + "21557699385719674753050624572282725191612933706864216121110471118396873804204", + "21676195031234643974566056999169564975565168161106308063972653296222128822558", + "6363029841115916180506080615867336124807912331702908898766344258887335068638", + "6363363442473598630315975560083028488315295047134858188348763022659100540644", + "7124701104304155953181957655778532711129611550814127577450223681003954918096", + "7205717562822389401795306034882449236003182581929449060008168502546885140420", + "9228704321577313703028591075278114440418729713968708821922150367010520752433", + "17614180549151511016628006398127365390131305608457060951973438257438000114647", + "6418032200333926158639440620980173724897942384057810379437154014285875236793", + "1563831431103246736895130173427149853201118983353993792302858411456255839836", + "2319462743404521965297213055024756354349733992588917297296444679195296432311", + "11927767606932195981021511932625975307755048477176557643420944859867328059951", + "11544836785666122785129617591037772824479186137515525949187438698705551844116", + "1594554937757396474339277849284988438779469013126014780880063013190163371374", + "3090898925598297656723114627648156566177591433413115455417582215942083585019", + "7446365483911748580722671015415226813450613606623816089630076567220802142731", + "7277804562221458254100595374565579800145724793164291138831756170012315181874", + "6742114513180059066582114839865181272182587124666919323239785847603969045455", + "17639336822454434815461281360929380680585323524008927548062809039584978130493", + "3577053914395256679157728351997326335158820293078866565158939674077090010647", + "8416461669288393104752370629542582785060946946477447413022986748926853429341", + "6094400991411029253990297530870616276561743402531435382960598226029406844554", + "17249210115827920255165876773266699947215322191164909559927557290769226533383", + "16910579881016588643870584852395328514859173042128957721312044020554328803878", + "18548800605828674358041671058553674062230830447893195908258772241250469878618", + "19911311709285615075760278233929935358167599770457743440215721586291252049005", + "14163239340563250030474254441824385465013759375574504142110478434407678615583", + "11636512441693145656185679602762396225011813780067642639202962721478584613773", + "11627821975451263242823912745820712501746551642630006051017450144021728526893", + "8744830043459757570443313311577350804214757169018319227421153827794333111861", + "15502529014760684785001315229904504412899268461167235130234930235025949266727", + "6349631474781311552829280885666605465500111601027794362415725063684145078154", + "3750521632595755853919734934063554601358504982902984744970328457148877834110", + "9151149071511054371049219579896119997746835784236581257692741291133486546793", + "19272083712610290647942715418077844472245445971738771938657138000646231164194", + "4825691749471936225587218745193692386291702673577201485716323799193545312215", + "10104595857457718843210330892798628518507052980455210862700838136878095844947", + "583353789674136864282067863249996585561603923757420881823548159264798915805", + "9605601189839871334439279312434848410060547509366999421152290140420275791851", + "9086947882810130237770476626970553738390827239675874433366363671998428155733", + "939087130792625935525433768373221635016459152936104283115879791761930303865", + "13281505022430256926767514868008519612680654933479762886734630854898956588159", + "8791789091182070262897704714134448615861594471173394864103714557845783501440", + "21362425094620759937169033441239641099917726266745789893288044988022041089124", + "21264794594588373773536904588090499011630449220310853635095227724956212914745", + "2201440000983888343103152416697093803247262395826184450099145084240341640621", + "3661470436482399872770855708635287053510507688269721514323020960038319367739", + "7839180633039164608733079650881737341586893235460147432870429877862925246133", + "1364292691308802542355455613317294540368840799230835218243676354771769346816", + "8772467089802087196594680904948784186594877728663145042664601336273257169460", + "3528294282612889297228918585517392070857581849271784732463382671241778115171", + "12283613789492092234060914364373118974353698674396863245366831728069561564466", + "12820604492590938203463369747918352644874868843295488782427665387357902190399", + "13019897240405241197543377542947534699610589548844172163916774096781016951470", + "8978439745720039208900766235834036199032761881062736449080983584518864263026", + "15222275515593239598230012498885809744566950744378381699594010392927659453864", + "6158846143100676033784762660398310184837175038752344358512599894306514737286", + "3419255917977463022603617504775890366430819682320515293245827217078337799273", + "2797549320616484645720845216321636133654549511963540737715630452964584943931", + "17033753202496389008484483700445909344644255293271694371709019653557282454651", + "1896525136564670388001069429586407694373044886658459111231294687089652643180", + "15410846104953506982390647924353051056630524372570695181992786623509940504512", + "19568185152800187670153137856814273221696851955003399523563214963248234462015", + "20371827999905064832491002071821973934784290275145413746617942892257828998608", + "18823477734276348993184895526705908983521278925070561827518170646437136680627", + "5656115226652970780235535706189807810471572029095698537591078819723656151288", + "6421434687984511183440563950015895899135677057679953590513947822215985604920", + "12040384754225464077155439050798210419160821091649797103441177013046869869449", + "9772259825341200622705640733478002328903138705997041039987261797557503104066", + "16498280031288829288227785307302736352936199535492502196962915104633784174443", + "2797879572297199909334218711222177900854374704477138347081058961121300793309", + "15644078107885262244699428484967291897698559437568086329631330880220396182101", + "1929505350484341295824323699861178332578728141996333839839142429658318709684", + "19829107623275996909068882503976730773632786622439634235897400991781002387336", + "2349581104996259830058120951667460730076264470159293978617685139254844279099", + "9982268711644224005105745225708318829304834159499232155579405472268549361372", + "9283048909915207444384413611285315401509676836116090349272606584736947471535", + "7228184586853275989616881380719765868918305938029647325846170897121335348738", + "9356148801854782728189493667017091537583121935960987886599900693915567464857", + "505609318575290577159607039016980344147219758564644283699477849237669899416", + "14366218774993226370408163695258733389122646477999733966996434517776361579764", + "2089359639878444205205504625611416181841134778729917955490118311877471215733", + "698820948911402576486200349924367867353277614409335325450207885100392818369", + "17294754169238631079500422194295778880256675645147530605338890302947026045725", + "5743114444764763990137499217919738961538814043960695215081370978081114004215", + "1532354676442440269809361487929394117850339080897231885482935144216545001511", + "15429279362464933048379761813318395642886047028548664371898579661267949218379", + "3996365200571187062769515782559269970838089444839642643284597375210723401466", + "13855942579438265547807447657820171102227439902714241541267837936207338677600", + "10017315263017624310653373895363026608166903346797759979504062819071927130663", + "16907630432341625239068562338272519243551636457245225084107387534876995927261", + "7499064872525146201613148795250282369576850162788310832273877734938523830020", + "18958126705503391332983508022395142836904315587480202621370522508346095401093", + "10592492611778631944134585106195825750734295091365114316628335289401976557048", + "10233935978161235645487498670591630814869863266947595645533111837358567006403", + "18003113120454128964459787917100973688362112412222897499908177070949705432002", + "7039143063746813662581342159908917130120555794319120640254054115394750527325", + "14642639620523941644124538953496946557379420056235185193901717503194398478844", + "12510354962778923417991098874785395513418703200631409704677335762559702597729", + "10937907264762102220987756825649855260366448774314976509103469790406549413612", + "2856291402269285412222070625188683697884675330846580808282552440813379388545", + "975737765008679829063246253813268073237541742979444038400325932865440884821", + "10846579585900798888776933082578183338805087990458930175500622018769094956268", + "4529602228982157635664617353676370544979061743349920696434925695574883870240", + "3217171861525061655884750653752887679288235310048470832136609603512214131719", + "8115945363448342140068934999604399115922267391140037880996539055087195710388", + "17017061429975823256838374218603998280878588780557395344098357759667769523464", + "17414822882733297737417491176979790219384604825369331340721407024843872424842", + "21674124478965998545524545297025636759207453169144537816299040483190680515948", + "17404327139726954519140759696696303110669221199013129437266401728134593481209", + "17114525865368445983838697023323950751616885090116888636158517909348665004808", + "13606067029568271979635008642466621607360271211611257253834134991337929444625", + "12697011972510852002753203688496604009044404157472710768958968780456921052232", + "17908262237776925120663573891641823998363270265606050465713306950786509533013", + "21456469759562487171462776687926242591840822996019886989101706511495101340835", + "266521013932216168881283771008004557613060760037591295825839278412991410595", + "4426122336489578598096667658844019947877597098504167195068107491300030650784", + "9386708946433085177945274757444606300245243689157314090008872819375610588441", + "17571201661176038894845217669278055388543910674470132087585433795958011317108", + "4455879691406517778738600662496288227132258098615032267607735047373372289231", + "10411615872931007756022633910690379435607807745769276655537160609832149769392", + "1369325193519585971093564667957731115918550810109208821335573114054160355015", + "11701493479673417478812558973813785951698004134589804881992366063053267216767", + "1604976076096057025943679983359889701146704427744088265030663068037609417365", + "5807894190026658662778129602314598491833803649976558289016319277596337255402", + "6604568830557716159440870492208510177395633789687151427836556682055398194130", + "13221322798326908033948171538573182873220915914806870794442015701313412462981", + "4276423994177015753526554880283981182675757973382385661312000468093685717116", + "20731982721228405909675906960993136550102478961136398510514645316644217252635", + "5084416110782735943752807643199425478022977025123417839387489101289418017943", + "2080632875247898509655097068576655654491657659787279629928499240720334125890", + ], + vec![ + "8798508051216852101945770298446195263426534955832706109793971883081428107277", + "18986509933027080134736570007084643366993581436980149915398754460582000677690", + "12888632176077676513526604615863975427335143923557716212154194444095746278696", + "15218363940991075171925464923999816479227567707705088260324253815128900522726", + "5123856245344699442960347063644568841985144075867098178729206123344695866632", + "937497109650566529304426914695525196119182564119552807475810542139513078711", + "824338985948318627675352935105995425917124279032809525354560876904860400681", + "13942203133811236390194976449250445722833065335401645546668049734024419803027", + "21193497547768556525501812324063541319364865851059424372272833186043647143451", + "8250588221665279725951753271334754148372733354841465281007604695074534688729", + "19810263445128123914159130235504848033923366950322026683325764129239721093372", + "14320576994914109414332501339118128158228930675887122896979269439627163060693", + "18889264041663920880871940494605753485014632588652805059026615325416030193719", + "10698608730721598729809918763148392473410663742558667693804099019738511427166", + "5471531298285601691838160103499608876767872950664004448938449413930813358696", + "11457601248269622750970644898105156673192281665198367584690238105651871243849", + "16992951642443963917535033612483493956841253647152989134657936764458589935104", + "10681874111209657199770620844475073359855652721773538207614365548304774934508", + "21875342192304323235337862160226992419479798345905340775869689347883470787957", + "18783598503862573040343207522848147282351537545213737188311758281500392127423", + "1221270316640374234025430293653174054626512367600614548099335037611485538579", + "3803127350594514565431185919859613836470412459545124433755604579955078714169", + "4692704173297526575662787527414813729052368393667564364621548376076502682045", + "14877344233698668087821753402566363996983618742746012792982725192235615274582", + "1054560579819817802724755404438987869385528807680314510667741074193700268397", + "8052216208184446695555642950254618937388238814298100142455645263386380520432", + "21528486310986712871728288619461268100617012373791428855787397091733571140849", + "9607239927604256097021258716815750648977424271874860033430637795556811442682", + "2196312812082021938532541315765264710074289763312450521728804241715253452502", + "6605540160838083938636248853297421723407665107595785929587574408377531474397", + "6525948153406525772960169117380395975817023461160134354401655738067058124631", + "1003556227519998429556476568497227440137917634405973503866888411893650518666", + "13677584968475474622689223355399150668790874783709262452797580070344541448836", + "4272686670447940608576804106249107479440735969672981187306042096242258850880", + "7723948150633857131095670967217469617626942590283045283879017721699420758313", + "9563761958679910727099148022742805150896356950128100201760652748527446140573", + "2850052690445041465388681891454371728435601558220304706587137488583335087257", + "8526775100524058290507772716277462650712450825299014127801740914862834880076", + "3867865860920444582361195032750323847757212456942796852256870663055781206979", + "9531131437985240947539412881613144640386246585015365913532093261866651903759", + "7876176264763636659141490167245054739857019306622806711043600468363729864093", + "19195073274099628437757763039411307254419435216466391625117296911023053159082", + "17201596318516267959242478674525331020962456681235714852429128354797936423512", + "16429987001981270427843548313178399452962937303668954688438246688799503164984", + "2808058398896238113910841463946229560728543614115874664163904810633100174930", + "14219106904819518684382581834179422938681605045169559176622588393807041033595", + "10530037691301443797595179975755234137332257307745168342967468344289118325757", + "1780683171090951191330938815635375996828930714110381590102230999415067289139", + "20631152509228411005601100836698224543066495368069041690921190101674287477942", + "808499207021654020396580110067573161997188131641925849629551231827774305643", + "16396331554632517617174441423319546296247483740125085437207362257874071846324", + "2856734177282633594896606301173852733142847026457749184584827572766646232552", + "10322006004350928347565262203595059371944943723449117022023179276501731876345", + "2683049508597401695548071522846046195699870016352617057472448981851954812059", + "16247545910658018105089170383534659574306111291405960552997108542031333797235", + "18622054704352443244328095600847746501599243235049513145116519278941866455272", + "8702183185743408572844482710294629749653378107594727106707156268893318308842", + "14401120539669001367195063486710670290450638416936122540739824928865783084126", + "1008932491940705147356558882115440120351684984366727805937095853000783157110", + "3335415470820584035672268458205991064638866247863327497697585974180788121327", + "675348271572506248948949205284720732296364868854507020989147277054109697307", + "11425495584918585085965114263893597096866227224211705734884180990929134229634", + "3139501819004172358132939651256683854267531704642601593201346365873114559764", + "5783084359807455756426365693604321435940832494660634548089377362001475784943", + "12095094864547001070709219304829411865589880746021553931719922191437710414601", + "16318907049539057156624360879403805284297767691522365017367114160528018468654", + "11395679267999229912686944592292906403252965050428182621076890662342583575672", + "20304144520028193526915712162937805381528012861129116050582705669151951550558", + "9782367134866186358061353221055410602027546275400836771195060684844149613708", + "5028029607389470804476265609647401798747012470860555590083631854016382301797", + "9007627713847325367341162188681367076476205669132463414398773746849014673910", + "14471089911822534831111187815987523323244485794708545360218598163622346282781", + "8019180100611273479652907195647951337613619920735853449223048779220663542855", + "9047131412613791119405208650892730556330287685760678509615107996866748231916", + "7335839265467743759482091707230237155828204454352010285192894893585057443442", + "14787708665825803366954624175778735065630831061644057898061768895034788739773", + "13527353131634051756987040962797036674382517083826397597671775590484363794989", + "14976182354818370036095296869648510633049821903988384882639150664375161429327", + "1560099560219605560187324067315095140041097722845226997637592687804174779694", + "571671177230396783003670900588907847431348620829409362554250135027808522430", + "3595401819266532411007886403698316446251107730632935527816690692561072394769", + "5582619801223860759886119158956613012355932421337001876528290847176002785551", + "12913958465964733555039885816821695149723050623118481194340346951472209298707", + "10229385924925018504492473674441213223237935733123654864337438778499472034702", + "21343876046925805512584981384457719270702482935633311830189677174062117124219", + "6420204451041321645976980917151646516083600244123365393849746698818033299571", + "19251614427061141498566572096097506725955838624383486128732046911906166384020", + "3637855341628865891325364423447146770285076487178203925892266081818756628695", + "6291095057555542758591776864498618016939082736178179913313186141723218066258", + "15680673126007380386463552533962308522676179674235002556809204723523731273305", + "14161910492383069106925371061927425194676913565753916851706224000847740857190", + "8408124204639665996401588312942126836254203358943885876199057731364731921137", + "1799578340300310412035304211591366593871976523584701800579066126447600019902", + "3622312705549913151631696033404711371424834689912295490477655370864706883384", + "13273792131373119117124844835198348538764494606049066494384531598904652565002", + "14270260908863510838028225519201826231421616414346395482851862560529215543207", + "1633715375341396582749269086363023314520014551113162582725277839347569228786", + "3306641014838747329624593360740422499925312234577578401365800985267347686108", + "14198045292969051397674853108255956449504264447127690313478218267366467132592", + "13392638089393029927126006202520304645547980480174653070691229667854016527907", + "3343885948493512216041748082192584170148231918609106413514016808164860398278", + "3168199878854830552224332084447744849594328418779455928665560385861883941432", + "4414580242526682670231711868212166617171432599055299074136917305689841663873", + "14102753845056293060191802617344737652392043747319090694003879675100249630273", + "5573924339361534820906966230551252047468623512376332682801232194633308271653", + "21326161716444219551593862645873877150197335577674154159730974795774063715301", + "19432958264517639868400987651848393946587739437911153002798497064389336859842", + "11213890749343283806487445619338898013466071000086191642490563684623068539710", + "9198922780305686612592947154769134303077110139729181291466776593119643796878", + "21136183246673473125620223552691909911094171966517655547561646692807715461191", + "18418245895226184084223713341727403858051315526021187444511237652982687814864", + "10761950212602122725874752124635327020166823895511275020048089638533400314021", + "16715137911831586771799460729625653907150059087921720918100590730689273071536", + "17773351327415330719066508685011039352345666357995827941409702750841801825772", + "20231347041576378765333233596636479502524461869780149044300194060543432934519", + "18064563634590190884918686050639690971635922850408462075670660991277444012424", + "19311806793959939929201104461555154688516767798907269687984909834335608414545", + "18574604846458382436863152415585348247597412723601050266815439019897542533749", + "6791419133329459189167506884324613003818238896866734783035998091675242101498", + "16020026112470750192482231650248429937269643957555877460849721131766422051297", + "20044512180053459577124670295358045421070313597307221323295226934357969810703", + "5766192039785553259974801912149788259566859976619902528741727877731329189756", + "13160650100556144411626475539530712035286115503483655434606927361569861225444", + "9256036311849117781394603817644061414251138927392105895934210116699839233747", + "16660304826343536533933296919943639532544710122612011090406376521894383168856", + "5613106871918486982198177086366936520094704188389198685078718794779139126942", + "13070598684216243718425316519583985756848433332087820965201044383296972346897", + "20442537178749694119236982549472062379434941056035907413284316764767638577672", + "7934380525369255820244941077363404413659578389361986061643146925957595190009", + "5098727584136148023264970031064585917675686576590855834683443649082028079965", + "20311250985440103383589841574737981968195820548330595923330987127317084128947", + "3768224286214159313341476585351274256214081884717440852909749729584659454712", + "9851045187544813632080431293737874643968516290829960553238754661188963821885", + "19015242154372799267961042586541854827581444440949327264389997835941454171864", + "6579883332160451787803375748174162900180016921849643588581107525271529605849", + "10040045688615204420748720492172111994988850765670088109590569954563466338788", + "16200830095429004437461800470410149972585288873073724430176128378316485348605", + "9682955393234868764418991570718636989945366768432593131049879027562290804128", + "12304247432606946020923001398973669839540045070461170795482951827192644001957", + "18191542513141047176662091981681722742011120542435182371090664849144982674881", + "18914155310347961884616925389217412880576183372588965807237661684331120845382", + "13251661686687775186900604006618374427674252707179991219741046747323389941232", + "10249194086135063113459170280632696947988284060709952681203426883826751049011", + "18372233147890747575375422462203968154682646712463764047424428288714397527619", + "4638790386282413677458315728120268440302827185471341027696814407017634332517", + "16623961311962310335126734990913554770362581939583424145026358913546809927380", + "17860493200690068167535851968852986582339867469465298666834410487621188113531", + "4608305735853858774877121807982873005510358307902418229099062852681969399389", + "14245817054597951763417586258404021745844024655622942045401677661648532977993", + "3809843434538253198074703856325693344260532607506586917570357443895551048805", + "17962207143381407459791220803798424686492359980872541932770670422523978330935", + "328196272784453082510222579442282995329426913454628381427614217629272010096", + "12374354866281475555079168690683958365929973901084398491477273797832999248789", + "20853802547446175043916531515983184048009878116903616041233975407782898112824", + "21496110961478435785328603969876614135441859248001892605282920516413194792470", + "174344306580108774551764089189043165870298462272157912012996952342994211137", + "20687536797191141044727224925607387673330130224876076220260820047611716395831", + "13067331728561732140381851790951434625007152955086246816336518249186949048391", + "3797862935019829095677036741096081889651388634128316641681020782687008662828", + "19718547173056417343715166978970272483152981364164960220358919266690668222316", + "4038867288048886379019053981446145278427073511478003014108759946240227146792", + "18486919751583655613568544119169758176904901075587096135301780773087898879007", + "9808503221335286135961820380426080374193561918865479543141400875074697503967", + "11673653729232546119337900700609058880494248359777926756321171038104786463073", + "2206576012107962093523647385331312771747616647783007209109500521090968991064", + "9800772269959998996051474306860888030671933506518907174943285018516216530078", + "704115937219213713489981656324169840669771632134921090864597729550810437103", + "10450936455424589171986477572900171091143445830786569605988162504754224051029", + "21102186642636060890935221995324710172830713161820423345683707273376266063905", + "9163879187976750746407679763920281609583516506018439539723656773309855786163", + "3472157821801145643950882966615731480318387993084591537527672102896556538908", + "2411890225861629547000508300507629335095483162292985158748687911181232049540", + "4985409929001444812993021607654361988376208034496482520498705351994820408485", + "11553789937761176964063895958945223002657928340200203811287735666719305693830", + "16847788791386791842258653027481339013349340413294800029138566625971691859716", + "6760556724223230789549789607828387138865070021837886343220335025271850431529", + "20596400847159894247975659733452116044170283916549483164302854789226390584120", + "11672317719239435686809215238371249803784157167042393847476181444969645566102", + "2540677976918908199177572390470632080272842549139729131369041579508211107531", + "19442658692908544356400723297250992586408893464353224580638700020103731410947", + "3126219492402213962620193562481318950501969435505738692018385930122987610651", + "13815812651475874136168661258439176421344570120756240838952907916838907109528", + "21443943680756083274641262528830913057368026549878765925197792130996882062057", + "4605307168758134905354332635309004486671872776946937049460665298426311628483", + "7482893834680785986692353891953868933021236845332400077427554963067657007377", + "5511796194740668281350270549653399801893104530256753635073935873703864854017", + "1743641818114051150153510266165400817966589635697948934570080374390326493276", + "479348932723256576178997677888058436058269610751399398192779646687149113652", + "2911895727864385123299618342188487304466499987531177084686132082340794352256", + "20721945554748282229818069151690746976633856763939126384250406858494706697945", + "12250582445106986808691543043877193096050336861542243284061992098674816799798", + "1203513568724767846410309911297210537295690041996158208540636591896498697618", + "6015583439542775167098811213988699848659623500530189193065070507681443523926", + "13090526755326389175310761634465981673176179379933796187486632857997253599596", + "5204691423542830100481358117753845481374238423707185894207602712512441617128", + "13451357406880550276974168929055560555273701366887507611491312211347374906677", + "10327523031922710495308957226255708193968398139079389167803483310323608563088", + "20154372727310541088852354530646786495003018189623626400646862775892232254942", + "14022574434398251358742711545518515409780772767696241502841372748111287365393", + "5800935164580219886597276061890276866350374777469717717325353972942679889986", + "4950159996548574497193804912590478284182513481419060390699450490223901282265", + "16651189959199821925513089100199135327590269582473005780223712025001916349248", + "21313574815160763657406207917526347065428582824748947465014119238740065134827", + "16527537328830884258876707902602749401809299353104838028680907493128406403510", + "15328203752181180061659863584748718494669846163715366299567183721088123843121", + "12748724793783078481516963303490468110393403014501561235873124284244901317221", + "18960625031156828010763136389916953653374660277691326636541170359289051172470", + "3539001622988573864365704884620979405364663891195701665878843924015974631139", + "19440492503123228668247641573804742990539424862427319298139890001019307139951", + "20663734806319981969735713814960326046259899097186434280597673603867647826563", + "5638550801924521462873676353658824547611149604620661155399127974706744652941", + "1371302213304057480201653073728712066696195910046203705054614806040591336203", + "14916746318031473743643312292935459967335724866678950897137588157969538011797", + "21398978398848411648231431125400371920346497858046605440294787662089399525957", + "17030308547789363149594169656019066171879481623126106541463663868095060483032", + "2440921987343876776494963936103708492610319137255329185550543355589457647052", + "16578274606037635679209783094809437790602872464993592428997022911117833097149", + "584917957795662537423624049408930838570931949382380283787668885015699442922", + "16787159358764578324620871006477408253726184555269443751955596568136636185295", + "16078611438600534298861481344414944861682463507987217038328914463538066716267", + "8692607842423869778858860521189757647588004328790444839011233818764128397786", + "17461551965098847063794186994739508584146822190403534677972847044656722703432", + "19452713388671907333636730760656181158378071792518077405629847148635099895953", + "12121230247359921951208053896624893871654915559955364031951409301929413070034", + "4704830575919764561517254527427722789804729564629801789022427396957683932024", + "10670972751746137966064978863033768730585193577203300636726301560193269271720", + "8049285218762936069773906017628569610476710004139648882735854791085139873871", + "5327956364112241273910968805653529609896670219677653687154759819173833984314", + "16209531049562245939203780811684066382629831507349840609244051677227948603148", + "2041493598600414785071850826176044493717208172684229753087779307523403234108", + "14983712813504746225033809615975227528832540720868897893292242774973221269350", + "12085999002734552992271490964670175347983721486216238101789957598273374406404", + "2010148192415347724117376912318888220341164166588569584654147279107677185092", + "14204865408877079652583508111640133693635228399739312350971609944706493769468", + "13330601146337713429471908542961289712020817519764634531830442337284933922809", + "9363311761287060295640718461317428792455498134298183595944737677775311452049", + "326899109513011756995862753231193478723114246336082392308214181273112126881", + "21599711748797254989207108046977874436425859822146104148381410251216892766416", + "4801349866375335173289265243677780019445351052028447809305464229839535867723", + "14155733033291624452674783678844450106798515225633669031829757876654937709468", + "6249525168453913775215814309255487077884826362528424439922890888603955052499", + "14928369633463338040897134982481546746234350670771618196847518086862393490357", + "9125617238450534926549876296505645845116172794938106232962454586773357787390", + "17103886312678741706528259762965004480595318892735191450742885850881411200483", + "9561133527164819548073730465187187729026229121272078865756229410363525806403", + "15499339092812475393717971205752228601033972883632736148845868837711619810573", + "4039978125105304479934956398755842410107811106124175538862554473225969256475", + "2947887467809254352800319275622693352512539235808226814612313636217910816213", + "10793550355337981917672616521499626144068251539193270238994640132315408061844", + "15282024117559604646251556024222379207458577842775127240432856222129525583388", + "4938487600404790095807201685235688647996095712925228362099985579653367819830", + "1580271313232748704254579894100134003454608977989565849293374431242287188203", + "17650488790850180925611249978112046323146336603695505846749648092705166179072", + "6318477650409317961441518371381185077182540555690253948041195660104038462403", + "18703841587534276582593528858657361438697750302064291203982535124189673569022", + "6570410039872146139634976314211122538651252189045191357367613723647837080131", + "4058984348953256905202616995215664252962770887032362374636631606975462596559", + "18730155671005907941866324013560951043352315199408210078231145351355402093830", + "9651860366009425828519962206873795166909640005109793700260660519169015873934", + "5838396248515026677336009419348618624164637668061855829375740686597759055073", + "15333954343347884216807246400300286008899152705372200767215516907035196412750", + "1930765848863686695612705389177613542562429368163669004940380022708446841540", + "10766617427881894379670242247746203663519351900247145805786260703840625869300", + "5568751333625760749025074267385171935854368145626703295693077672484816860148", + "1572988267140272358245681202814159120843671145631424724491020423275325461878", + "19938390788588115516825752214441323822601991108943527563767657270216212171610", + "7571912702211887911260059806577378242621772456912160075153794388926572849145", + "6858979093205494695976875629144179789647553123886814202229119872013338208712", + "18762692183810081055439753242004382629435200839335768813270546391733006575124", + "15741259924712306779754554541398522471185852133913466875986860727321229948852", + "2627808396704584386154002253183040546217470699174068260518444240974395718442", + "13588659892026426409480708338634242111974891480475426278869635762077927444692", + "19520457627758833811699878500944809253730386938893290068023237193482219846683", + "8924178241223608091232956607418048641173985923478293687667819226283386788940", + "5816806026738575389886097663948769844875803508574710698349454181988557080269", + "12475579687915690911104520027833630083645170013159764349224529153441972577879", + "21056333315327274333873056478119184274531121536034759580035646721078602460263", + "21444457911856423029054975409869505918084415765420838103204937942386934191806", + "9510785855264774762445687543927870120017463057252636764104600528992946593471", + "20924833468200564542464376523632289762564405387275694375761127017107713239560", + "10520571364539530696429244627292574117914912315372596265076914837029323332385", + "15676633242665585964368692080238475248556973102970746891067091503318954352189", + "7605218463440663468653884707661331637891252539306862971462289235362996643878", + "8655416175027898012639173297596079731611447754405890380300192905812736625807", + "6587158966606269464624627411770696301475420343587060761712532930272301692115", + "17855802086555955087208003626983743721607218545075932831175321330674668519051", + "19637270878895908891611772385398729322367589125936403809351999767233932966110", + "13844120079506591315671522711954211929776840478156307979285069853392230439385", + "3896487627666829488959793133071741228529962458688538373863209568407375861949", + "8072922412250331052234441827200394762964253395032633720559799911487619573310", + "3446650687448682504274462997312106041057536787701884723175991150707212663201", + "6750980474265223706441237350209360446038939446132826129894534048737231433466", + "7932667749655660682554204871450377554998925079055538597319130615505233358592", + "4551937123821223485620220711817547511116227596903370212234130807711068597972", + "8450248183513826743946014093205824327880033986070570398677443659142729011179", + "20523087403588105156975104084680617581362696346833272310988747947340344564064", + "8021873326844086700417206574915335220587232800373998588094532336448721297757", + "21537680614486477925570839538957839593006098192503694567393749587557940631967", + "16568393458463797316294547173419555682646841229029462217197639056287054602566", + "16910235738263410812877183305130636228751042299094737782744154576712268208807", + "5629150734321853465674510845327410940323442617216962413835214171701329559576", + "12765131184498380847448550948920304867469527623961001350816744753387093810315", + "9954645632746218155245325585480173993199044803581756137160936155060542951690", + "4353999640943599221118281386228426498583991413938542575297776232303344359940", + "4598711079993417664489400987736110506328223791894077517963275447468098340703", + "17317745377506102303500052440899062192892945727209124468437255966617200532771", + "12259326473392165107331141130100680225887121989921120777667514143912156322366", + "11811238683014115897858309619999599808008425203222607881921631505596453270838", + "16699081706307776306233318220897168744889069844294482997417168581249408879966", + "2568026780472621467236485312283731374848886964214142251868040165224695316461", + "14887991441574434999687627633546581501365901473545173785249018184685706196387", + "3569299155477721143892535842178855295807355615071744617918410766174933059043", + "5498879700681283951662042539400107058426136148099967700430316029080607629654", + "11559660278391175834668360323261696294263059777402960440249595574565141656556", + "2245933817469402341452987887837905070237745132811239554297372991710914298182", + "16951354954717164970902473060335333600574402706601527686301478774700332367102", + "21515931335312240603168050753263038826093559960430063520103206567658032925825", + "1249411708644964091210440756030782120444589186855809089574669034358018078687", + "5918980335509160299664261285796224662727132279493574691223062837411060350013", + "17739377184579332650035245294019601185834503109963479023132769460626331642282", + "18469744224495747473457470133545294362819142862108342977835066995529981406258", + "5759336781416534390511113561800934668877971293662332789437681884454072500101", + "21332416097583521698710421619829192185814871724795352151990455290564854827164", + "14991738373834450996520664429440490206770501447625984036209559286781489862494", + "2292383959319987392245961291693765909376521855421393945872334290076611679644", + "20271880109317422113691100242542434637543023179088091376781918851828907851136", + "16343203714510839250939280610780086672320399980775100100207359519193914733737", + "19668001496569040779289443981105684833630116796777138243208746501411413314400", + "14867270521039301446053304216390047301289545979306959364422905455339336587211", + "9929001722154986846461251622116577110986506194975595510541659282347340459331", + "2420696236635890839197290329114608036709859071844599545644710485458818798726", + "7023164250260724184662962208964026963022579890290101367655364067452762770181", + "20508512038053113727358039673385440054337319672353371627416928672143778575477", + "824002554707189532054522614675201805472052085843409216867120989167259938133", + "8081396930643908204119214488991799751072819763127464506132047127093449268743", + "6146669896192112367311424975035875525640347544666981144388229137148737996133", + "13917552221424458749711502489369248582964839439204214559672563587245770000377", + "8550914481815010035465313609214308578203171728075688232687655725801893823141", + "17861710896808118014581828397113186358973121215643160735097781787683890541230", + "15595160929818874035046864313228963482705740989418337036962890306288699599497", + "19810339910854592376336564454416808551332919752503671392907063850461349060479", + "14234414635213293332305234819580453743707554476430770368421971402668862484872", + "19388187253394163585379147543203559718098061800153070411989058669962446974654", + "9996031022330018431773704931033232677865134703871368350999928908636869707277", + "4533799599412452199761684722747577946145532223581271248962115254510905734009", + "21793143524581931493773122340799146504825838409926910431529124937257399233338", + "19692292712401787423885461139555179726227035221831860547833110141089023723875", + "15361825934066824966062130821388513267446230286103312548685290096230989843672", + "5736434899097417592114701714539404395501559559582922276628268708302331297718", + "6034516841645751575815362526363553327142008775019507199148571280847313939846", + "3103941851061425456578524836013350351780973108926423482048781831843642166611", + "5899337062497322191697133509006899741055733585263214548357752956989422491317", + "963781557317112122222004641921550021210279319264222273713987285679221730138", + "12218612067667826360750451807845242059538814081271178077944945859470451709133", + "19859087159769083790343803575983864397266266537354870798907672197319357964101", + "19984410602577911276019354715470427122599274838994470055478829965861962241047", + "1510088699001180548764185585999030426586790568209473093795384481408826277110", + "6626530934788897049551186952232009297818099583639020036000874279734503731588", + "11102385732315753875246033828526944990963999730005798544082045825035822583016", + "6109966799003077547022556221879889867437672084424732190493586606121191686895", + "11639465666095459063122039730377449150155515188121884615987948657973378713177", + "5087119226269603720517452201720024842953062937909041658414079638720935374157", + "14200979596305023031605438052846373683912540635435718213758112152603438068044", + "10504614528473632996392119937661213820553616805799536317962705212982659337252", + "16896136248461578519387122861681641687612480673542507251813953270071486348436", + "19381803511891750827107737252264239897475475490344583112691529084617905175082", + "17541407330179013144706644068775388402691308134663811736351559302382670708984", + "15072973642203756087665519422382641458207399650896006837784917038035882610771", + "2308847250281515017414251485377760361489324048295236605725148595795546323946", + "12235376578863551481515043248404435233298457594750687771966106520323367004722", + "234919702386651406701334351738513931283772159856053034152613436560637774413", + "21637694258900941342752268794989465303386552780774074413553118735507317693719", + "5017784917192017182377763628730148212610872485218540677293490321169201123948", + "770314504054605850178900177028157853527192178178763005605945816624700149604", + "15876814891128030074112970202145286137873841691080700504603122203177789175573", + "21396093758862368540558980838454280107203592525118989209906020509719986520353", + "12395505038782995420136281017345429633379095201006284425785781752730355509942", + "4615582568807690433357570528319145785065741006112040062899881545600179737110", + "8043772689074175830138340191606864001269151441770005593654367646112660385954", + "5087060495541107066427394344208368671085864351635929517190622637786924485857", + "16943670806889779731197043466545390303516518769717829999301840370920339407796", + "13534112488771797914884729261168139500575711545907958816183168317604824618336", + "16825197446319806977233945546803116869802796446301133858223973227155555550786", + "415637162126135168159032709770391274308945193836031409779140830879628784155", + "18387406758235678040900363524694503366739497074863830200933011303909731885356", + "11179137042211084753827842203552443809484306307318317082624519139125401844623", + "21558283039234113122805760097265638181763687038555764258857715920818106215523", + "984886258318300688046776785673289560458722425831829175079944141959971496951", + "15742677322520864401961943379313620949320295584851281667782848443614715444083", + "4674404119733957791400958869998857022224647524150794463860851329191601384751", + "8650727403827795422786989886519743572356271096745868930429292580249174286422", + "13805664248894834838953946033892670782284990791974158822573815009987148935892", + "16425836039758852245186956621689031634149287919733928973102442742483370034636", + "12636984265967190523588079564146349074168180793368546716527682276529007569774", + "6817261026014072560057398041777830163991050944713581458081292224492038152943", + "3842591846766588833800550804121369117907947886204778461433916219752157723658", + "37271592202861207450496704231110333036724082963271323982767766566562691532", + "12329580473492550135339911865680237906360631219143722403158320079104416996484", + "15059957190605121115530324002634619045808465757564252411076061916764353063726", + "11049964099753534691591813062197785153610766318641253048434615460367998657091", + "9547664187984679827610628603629867733891345475510185082362431432235944303887", + "18137012162384412674879864487567137486356542869085283721914855801046173331814", + "3685098687651674532509991642879131752314328195366675630043049707942891170584", + "4350994188192128309646975341689829464702598370274816402210557114155641708487", + "6257821888636845714306847817710237763041012260573089876542080703522920377274", + "13253018959425522927774577647502632317700972755867567550168937147898302735178", + "16128520328840705715994832968486859989808820923410546017533359121760378044520", + "20170678152633918405966535017048153387865751946705242558406886703664743352398", + "3126527212972403378701842254033672681067914451311377332339121677604578433846", + "6867262232134903047018643171960682024396916574079628095990181177262447593546", + "5158367668931323144529189475506903329651812153505452886267986731006143844385", + "12754662355373887343223103245500264953264637968818954284520990187567577818296", + "8184207275666143481387081482876989547621596168672026673494737134509515879012", + "18182047594483210481213043235431614943690478827150601132558952379296218497290", + "4111619186005075315994626077421177613466749589414703176717884282581714875444", + "12594743600504910896656839099899310633582995354714016930508253088263104106919", + "5906863249972407611468323572776947701049556100204019241509444413033282677489", + "1980080210708070252092652836439362225685565242001269004929621280778029319218", + "5317272650834519781155258400716302959016815255066834148961703371077671280690", + "6755929511172704567967900822149236127789264783942356076752317952971695408598", + "756653592358533438118443361825151465623048518178327516109550024774438883797", + "13279387228067311574007940426672455425799739197632963751860641037826906383836", + "3438061374000905227745499451129047661964429059615559246017344409320261233525", + "7182178722730387940200728260680152279805701386434851756285719594136478079342", + "3232238101879637288089008409532896629862152604458430213480076264229233742462", + "18349073713456068979069801924492847615265826078935260207982474961526062084683", + "14669461622305417414928378093274931180941168738663334898025953986981156308555", + "21025953377099738424497295026970813014051167806216418684116056037559447904210", + "5255626714680656571286256248936450375168592101301691959500945926924371301668", + "17282802977887473307860913184523745285865757781571388182716814513918008763083", + "7631811346302144382630250446520799917692672405169780636309223495193617534466", + "20646156492067995141068212601070346054539051558428492870963989969028947901818", + "11672162857509134152068238052735772663152403666766041363264045356497052787977", + "11887096806532260110007600390059506485935624620297229185953255478078857108029", + "21264877166054737412301473554803422700628127592263555002579170780541880711583", + "17943121804479032447858025131006451946994591698842739992603829945304217132668", + "21741868983463662771047103688885133076855201341036000119862339617136906339944", + "1382607011220387511006322153920014038613273342046755883486188937618436917353", + "15178143370568504520122235741397126257210602740650018201900729283546561696689", + "19508797361068986472532319429763120919434246449052564653746851758275251767862", + "15432472447501574952637182729640656917161000067956855273526749406835661136043", + "3646314932117173966129769917469131878046956851972292537742853525040261895364", + "9954239280150263233476434487622548184763162539776975088510280448547072413987", + "2860206851873890237457963710655623849107446413141590367626845970849894602577", + "7624884329556925103103971646513913063945080531848229875878001163429377892391", + "13360943796731806773064110741074091445121635972973177327949717322801106153527", + "11667836424651042501953164038576384835169183507720986280095771344954386965055", + "11905856768069261907772799745883705412557691028383268959241049562161180720782", + "12186322648958524087383458213507970038354441682650936942717890758775537576715", + "6206377886698888214976033056004148608870783801657981445534961772175420895794", + "9985786843211942401460306610504559890644549482837388550821870421722527522129", + "7131871091468632512858311810874313071133138864883788963707243539301333044473", + "6863753977467122978046292703800221264317404999280683794261458513920582107611", + "21254007028585610870255249088819596192746568962023826736493046170403367390748", + "19316740566828302369845437096994220205932229807972164794086423967490376053120", + "20911390901671963410235703071566058542087894960862283015326592608742412435513", + "10947283094344459246570251013404575174415295611564471102942451430110207789270", + "6629514866202977686466275098623310374411361176607669574816969946802287210730", + "4306637640746831336889157874035984930432037257905593217438853767758187627966", + "17842812026941818985973284751293083233650807309923063938909281923223690155081", + "20979787799837328650256681706033358558090809594843147876018416522952145532570", + "1084395234163143750754914936597532036526030294466367212427999310133490427432", + "16411000398229165199635990982959651490150157098065257264356189234661735382064", + "19529434083761568271396183578711196610141016284281861529889071952129275189455", + "16011589790708451889651677332030836112945970875991060135355201890489173794391", + "18538965510307116327958097310749618403759736800399477577603041239573656036369", + "575597941676167484590243546673541695594364334533047994047106268896055691965", + "10154888807896331879146339491538875786991737788606023861857914666322797287434", + "5751458975032524671409758081174846089867075271579936974413090425599016414105", + "20537352507069134091732836326103935751238399876923560688100837864640768370820", + "388393267636951339744357828936678047569934289925965154807382118853267759467", + "7988929055663996471299857775851374803891818939607048669296465149104280329338", + "15854279006908524023292733986043025770694447645646628114187722686173514956837", + "4521251106518907970739816189092928174098228189505033700492425014567434676545", + "8157530011801905607623371429913769514135441595931542848447134013030997901186", + "17210220909122096186649472888037140871466614174061205406900504999283153448361", + "8320732794514386532143557744115700882386234810376915665505736773284988176468", + "18545875500168811775790253714678031231209184655369087385271022571392488988409", + "1254781372250935292066700779746601577560528849791669968836092329802026798977", + "15937651643466826506099692353730922248216886908526960483039790489122416783291", + "19652922102341829730840072437940487297445121681514373922080785529489772301904", + "10503757973376634172224417598657275807288376059467419568957007645015644121173", + "21061128242218898797612547848348324784465891245956046422267179077655910293944", + "10184102990558905165359578703213338644155257878926023646496853865036973561949", + "8666994074867894594122739436332358585611317775525945925668413909058593367375", + "6295777974744736354234904948599345763309384248069989076851179466032501025536", + "20085880035576919555482594174603593248431976403050445801147514535176924689286", + "13336325268431575611445366940808804882241012554243123490613520755178670444338", + "2851570355537839451958385805591947257368741486463662868962800334058987150319", + "15338540229139367100976188303076834527818508966890772601613851240301217961568", + "10371461591548028790292642751110580057135152343433828303350260803216047518940", + "4721084162843564631441880187545375946421625038671651491284453281351611614059", + "3995968148066851603072141668595964652983129138292929197582737382184727007080", + "7098475965261931921667649758515107427024701354618287208328785191069896296466", + "4781012573672976409656547624587641549313089733225902702392506095387529662756", + "20670752440000061451737406274881546918677944716345491651236710839944182745380", + "12418063571138285842036790076814486174694060062447109012153384823149675212111", + "7811816758498775303698763826175925041786332617584902402713486064657610670887", + "12183449593956360154917192849776155473122126272381798975678273623416489913308", + "16618893942934404572687811463808390124523121286530811532642072424179325845074", + "19406638405969722482660214331214972298000686566522726077456272338753715959695", + "10114896891602590148908922343663289384078633766105057337614271468528428689648", + "12530180531394196901972330325311958484582015641092775027610123114239686805374", + "11041683014697540482690315932778449428117122302594146671576370088141482310955", + "1320027017053531867758788220662625751339386035447221928720928792758652779021", + "13527787960362675982060947767740337401799422304150087254060335221227857899395", + "13729174130954201937892395283664312766703232538508820386345658917013576288760", + "17930242209772558948222336463641867165312462566025913611385142785331060524930", + "271824668681816275326818116439977879809479607030794905583278884369947027319", + "21830554400118502077887728598392792929421142621825963621109394022313090676569", + "6549136256809735363529860847857612644334750418268339758975670928910634751983", + "7685854228210998769567354902455601859064733599194469796921674884534065104568", + "10199783250519025166562439623829544304874659364288345176426181874053651581987", + "5943875072296398061174787119165901172801385563744445220331957391085927545285", + "13636089974665970650510341064071559084615387545694695884611938887433039781695", + "15691183714733597776429523227229000727289026713809002490626629956876436864306", + "8559680887315082801378048339294171503207432568538176257628851229613536726966", + "18821447832687547445675145256777518497433117070692725937727249438327468662992", + "6902270693879104513901555672223877604634815519768564032457887408714154308149", + "12608804561676097764962511762420917859780577008987930524021637882085147011561", + "7174750477556160125005376833627217199860110410544187422988321767638233689286", + "8781965602133662570225157578692327961234407570869748187793896100695809894931", + "2495257954047729673944010345252038964921244093254482708244520480856454194795", + "12653575930477027537985334989917636556832984698125835912664116078189272484883", + "19470590483773048893847572401709923514063082100887826438398963400593333077944", + "11678017778870321267772215799288011478356485408686518093768970488464471570815", + "9301784115496434107640350077213193580405719087632828834385703690626291479546", + "16099421215741601628246289398699744194733547679841291314995675269636375173366", + "4867605962743281143953362108297759970476738545503236589613425112307423983682", + "4859206625232481008184446590986037965722621081800881620559019195768715084241", + "16443855645256733949926481287606183123363410685496841842812427755007713532892", + "9353805991413463049534222956121131872491808501219007024034024595326412518624", + "14757310122557726344690376086282772240920466622515316103858910465445116386412", + "16114284659120061956470765697027372797086020387576070056735297826889023849274", + "18127578981196629851526283391389653885301579188814495702848892722544020055239", + "19887508599212070022669423370158309019634146887428772891372609912344825709907", + "17077080577854056476313617926548386910187074435097123438936357029443634140037", + "13226301328255541197539849574625779242079524439809722670248005223174137129917", + "4960598616057280250789993806465640165902417481299684068727179165412382990160", + "1556369617946500605910052041338863524761596681612901076288655800401759143728", + "8895728155072279486251055328673751534060735340135019245331916382281698263288", + "2289316141803713796617375788451106287296998047295030310234564594074457471782", + "5743949466604344050156379807720583796608463915463812355649877415948657877907", + "4559156913695113135073666675680811432586708455776982064075705161369076248306", + "15885688417651643378493200422346595912547116850227590423936072108251702846077", + "21139883658951849592430718453383511736537035608453172046329191674046781461328", + "13189533415954149093943176775514655876533454488327933935436293535694805743518", + "17844001788757965004299951292259913492254300676101367510089343929865197748634", + "14359892033284780014175740986504899257072848573639227540808851637993574775227", + "6889876198036781721490463051636227217181360455045088258864933221139868422058", + "4553935378945711471651589030479778870996380701114996688086418055402491525866", + "21810472237273957206786186837441232294905360772841197216403531161190353859134", + "9053152935239668672905478856994179206897589996100634932367989982680043213185", + "7903591118665286176079138425128951407213501136276774569998580497121699548452", + "3172076359801796733031209099819054888498110723937314354878294115265190182330", + "20407097360746745587552307442773353270033615342704398250066232913190814364080", + "7972160291863108260254701271534596191348694866685225653124288422099219831356", + "2512979638327596184144169725891221385374685508088897591186666227523755823449", + "12288114406943148383409449156486826621388266177895423682705260002640229785204", + "2564780208026103504658010005943517057775957652807715289383367743075323562762", + "1819044074894043026843741029300477751989386054356309111356865205713103547767", + "15212165170979376853314159201892286112393022743512259361106527770911103031833", + "14546339564581115494538806968483958415473632518920533358183015375037245948160", + "16595063968058690402196826920205083730698910363019110917303832709685137640608", + "15141874678776355759677532828036793885858184118820760885539425739465598163481", + "18904695587436094037528024957965408339655627593105121455062940021234808627517", + "19691835186381553427610210595631576698057108693742599090126967830113381498354", + "8924094782966746617987215970302687574282302360164926405994916025579749820763", + "19703754039872652879247596753980834118500205932499483040527802680246732547089", + "1222369520366776525212393255592594259431640497623436777492426726914294313696", + "3172344047869282929514752164510333501174713324035401216632890990510873753170", + "20343327187307675920101255252384731549311614062918830229591450503470004905005", + "21822953127524319865522817898285572788301109254914611303940997016471607290917", + "4119200650145180459943542547991468255860361677520024595215679941381226516262", + "884147971363709313208000703986053560492302821976778288962675801741886201394", + "14179192339259619376051698338290832703239802203346051877647263334899616811424", + "6257894125748014033621520751032796530871364895580315426488126143829369988797", + "17601756798422918594467038792255913560794153830062518652267418453154739852356", + "2915639491551461190644833247357672612074490352906156209159755564104341441334", + "3231102949049971084860869512445280179160209744399934744399854930563348405672", + "13021414206859086129077179016730508855909534243359809624497206694614763740073", + "19605385510680022189244399663099114503798708854480688481468264949015383978657", + "17021346162472827600023529413773560471771827295900312703532075252918830714763", + "797420759229039341104668808766016498233266139304135256309887013677831743527", + "2664903618363391534009727112222679446988960947334573306133164042707563086517", + "3385549618192271440433411525547058485954511711526716925876581944420956285378", + "1363918352372003902477247159358100870270143261694504161661979865477181878601", + "17725129391688775312434400553484246750421349715293800612810825705415551685950", + "5565167518043825254697387383458520114240098359236753821784350172714709788630", + "12992626143189731743865280272360549263209967124441767673953882659571766635538", + "11778225061486552005541072536540594809710403539663018251279263069564781285228", + "14855802626429861561613008610208237167206379805668816510881008293269978954622", + "4625126357862179281586979391899300109486628847801168774082540966299465334987", + "6276441096446918387723860737716070503136385106381280295004924708881687527278", + "1795411699207968753627657631153296652726915050129212264930465577194539027180", + "3713335398598470745490569816543380122933539387356971485299483223125101783622", + "8168117481656071903083355331291793306622574934522412245021942135108094609781", + "2572110912790333345146174163319527254651486901580412685997510630243717870169", + "7025592394229454054105021012166569277499787182283497117950324822835039311263", + "4277841334216388876550672363276475272389275617444687843049115977026804420380", + "13307361877516070836117482544809649307840244674059174145753388549462990581425", + "3541353704173386321602971434547681828071166901335602064587889348895191485268", + "7930538155879758019336102161941128485659185437630747910453735785539584502699", + "16450196550849346986145420593545138121140944184099182530791513972233505276989", + "20317647049317277971067996601837330680914292615460278222929651175763284855258", + "12350794170306113031197271161768360871903372891508108500893141893703359009096", + "3619108225491731892480788907918892224372271737626523316019551923666189881560", + "7777524276793695474644184635794903798228263960698425657257115554422141857963", + "13186962355302544705310483262369096768297692231774754772442065918802639434148", + "3194922763633786447595049582526939577788731446948460263029416154575517633713", + "21764951733983707370163229447076318279945301269175491715317016754083795809766", + "16974123712053830964644120202072593533882217585039161631414357628477067270637", + "7898037946204257594215947941255236785530939710112474014338432350690918260891", + "13624131700578417700359325726622999876995826287578399131977467236788609909029", + "711696429159123395134741628515974061981015372797792603239801873522576934606", + "4454694656802154477610657384658122890655367951456133773797632953453271645583", + "11258512209244601689743240316900203988033840864814099786180897714348264963892", + "17104789493255338246571119977680210285205025930099647162805883642403493155704", + "2453935444829235217884190831569908545427042660161097975703593935054598308172", + "14865321440470518031331143407868649683664573698762400527582797926467679386433", + "2136979677653477082590579842429661891149048368379361009535231454875783700576", + "4168503404348819959713764969732410335826536398112246387116198899646877476352", + "14962916637001251079646555121263487095246900845890500033998976380566908488027", + "17784806976263484298477702017969482877962979780736193065620735094916915015057", + "5811232683701857296108715612406368550641241305997590112572736833619623657335", + "1137391750177492513773516911880446733365510948924761786959001414998578780190", + "10377533661461362184186134231677823404558478220398261395966741229154302153880", + "18289640374707184240721457180013244890344288557498104400868875166742835064845", + "9860550380532355283228261241815983394260110595913388936133793178666677861361", + "4874241362571018447675014656968369345960496639223360836208429603461250621707", + "16865415500549523957415217905648921092860879005547743663493860402980518775170", + "18108918097362824701832555936428153547583210042204293837516231255476913995307", + "5814882735302537469013996470647050035634690283351899637246825700230993102653", + "5547108529079575186420571231950116618134926722630598104799502651040507258925", + "15206849009490203249915056590647669409740170384956300636367240071539934234123", + "1640644832819090575801198338202018591671247888384450305015978486631872758131", + "7653538136953954504326423293731449069773903668694073045541918594328126765475", + "15742666466329712178554069131786945235650635594806638864788722339397338233904", + "11117991054142148551955400250530241420008368948211075786946321189997485480110", + "17233487593470788370048183367208371204821743424889641470669677410605695176177", + "1026146833779113517033720464553035587620506307190537032842523110750653300409", + "10367733177218458884804949015952210383318098385156812709591442491050289560766", + "19795952047370883108661392932328291359892331001341014799939338429402797554384", + "280679929395977649114582242142346138466205172264485418126097892277002431011", + "11242092785741474644852794535567939603118987148698147008638503870866024069513", + "5042585848515676287159684547630780418802525904631814404423696800729724702441", + "18076295265515176256373769387621576799326851284582393083567912446445089657195", + "7379637951216509909219158222610656534687563904425133806818053082443647546922", + "3558112975941340025714332950698945379582897448619066846171473836063116876320", + "9573479360636272045463430387538379484130343682411115582933078720270296582700", + "15319039730033276973173680817870390182683166144884901717164880903880948070156", + "12614337451840522588925552004159363860236857822744920028925848657320885223697", + "11264872143423037953370674414336573063122101340016831919048747444897009600345", + "63551039484071672399844495086043200468124594386399597718753671627443264469", + "16482682562437246084957175194240509231216838776265462897558195414149416246757", + "13173166853778783824789219649923900522843999506901247221151336885046420684780", + "1129677032059045391430942335190974518204360360119509444502890845544005628775", + "20501934327919041700769005911882447290880962332107224642437572131655999551570", + "8568499318929533224876840771639984749494220751421895884374508523238084517848", + "21259500369117112858046793730986192593905655365067722159200063822227414197523", + "18511737784245858621248028192284641888665848600116211193811323135518967443130", + "18603594203971848110309624617834986072777858306337940642153570206399896731227", + "10817810567854821052648176407774626914880746517118466269332776843576340211709", + "11799868341887426863553231699803195039814149532556595741528976577115279703638", + "3757945008672794110380903983621818494776340526769633536295923729698796873418", + "21147309772231799704776665377393022398575359474581992532654566514687692622381", + "10106161080097685505341186856462439849936096425798398803226308955853233801088", + "14262263816750661321610690116851316864336319623922717953366014185440110799541", + "15141933436196205178645622716666344645155868155385407285733185900575537844179", + "2528094772114073897461542081006497820213677180486542945691028610950494870091", + "5348148742466995347556247741747876445951253226718284728036059196493410775291", + "9450763449774399387985728640759709076413273047954552646207186077905960887466", + "4298191617478880606290362740003112985908172107645583722035275913358222519724", + "17030410720819419348097350980058866817562528502379090451415524440163140111041", + "20216724013881673832814480585345314476760112405406761404693215284600396014822", + "3030458477787500523997196317647453871345579139634715247165773460115609582330", + "4498462169050723804654682081938858063245557243640516301148855502903911071559", + "21824371776439248570562107317467644300343374167209593758174011062336014366712", + "203060629973198497909844961226448148781283822023116446785885526153889141133", + "13138719392503582101372758211337939905779727766832919837180755391232674501632", + "20854865022095468190800800251894006373048202378274232848293926367900444699099", + "18588694216199791340977948229034824338778108765317256872571104367383806683819", + "2107723674718153039822163382990954569558924075626979445723435572951885590282", + "17271751087973139804430243764301653129405836972234862722312699991367221547648", + "6601353498913454241063626951167220829716071681800808284181512008975791453478", + "12008446685921630692928691914657961114819560167122558958661337441533504934249", + "2546793791363939452366278657165405700949349144309147725354170056455546924821", + "7509639228703028871971491982924543490428017835786144030628769546327315651842", + "7823420836979374535855612407590511724416995784427817183396908887631615569374", + "14440328435891567217327191606342183091806515070572157964854472183381142306857", + "13108790694241576486381404587677845524627865147145072471473988738194261674445", + "19983383938241398968439231728950534655853676209193309061102635732967764422018", + "11284194381233971884183176274626185331731276627680969047259362251065411330490", + "1897088360814614813239622717000987185543144416227464749796311263447835372290", + "5857538086726776983838909617047765193294143035705320425608254315461849915283", + "4535962484854275896801499258204616949002112470323390523101255172552516276853", + "11469688959565470671946358524828946654780293319597759366277869041848383938506", + "20012499472065515722935879215306365340460493343044867272369823908813586319215", + "7237913775106599492643101364689422933623029044428357142837972864389129948204", + "4449045505867908852965142075471111990333999619831208332535528566178225482875", + "18979580728127588847832634342765381189070401746541099544183581604411319882119", + "19208895793797113211677254952138771238229741398313608749033216609457932026240", + "5693339887541566707818870620263679026120058867908674300592248504295030161683", + "5766336473322649734565794453161873006745293312886382597938223884039108263098", + "10170500372500319676592379791726967782477062446940064561124188934689788907641", + "10715178415744481025577257316838846623444784908902638764026977772991007652177", + "9619234464729169347469055561718306819745671419829947117176160764784203967441", + "1725396226859654685724455004555068750501182150139117204170589152838896682582", + "12812488682187532465982035305360477706619015575668642434536603051988822585780", + "11634913038840687277143479351580381035995147077852960651195662158592298943782", + "590921624537670966145684975359593307845422616174883518035926979611039297697", + "6994357700776589034911446516222866144704328090940819982199063011389928815878", + "3578735612848960352693685408184960756564585180098599373883079061082850989193", + "21635405933646037549623279995086014983969914199701848464885184930399308788578", + "12427020649229929152974948764411072254994310665316587507041988661972823471907", + "5659276116329803541059701770846868827421187991242679567541795719989019960482", + "14409569205409757172710837837486699395207656272569912916147975142886221411048", + "20174498258690846561506592709929532887311794985051466436061305284714705004116", + "16144749571419779339987554753326186342500618892721832987727063682036648217899", + "5806868382318732909919594488299463205875390466419224655308941273678069453955", + "10943981499006299439884803446608501609107021429051016441936415463311572940454", + "1924938839701254827227113595273674646757106690922839787536830436278569286096", + "13979276902289154914577571664895855335838520340557113744304695510562836235170", + "6432081587087260245919599999151100148827877257649626745290270317469081484560", + "5063153002404552050282559105432952932908381495673902142745313078991883601735", + "10740439837861405332126185761433228439796897508030224612484767505999462329793", + "12737167379130609027705740172196108812439282784347836729589408028420120550933", + "9849845512381592257963507051167559787679646439786540183276617129759373853388", + "16005888832870175950940829700448047001850581165998249926419521156267014986701", + "14353647838770412975928759605442634240665383526900174470441397150591603642336", + "9246400563934815769990941385821129793696094365581285133233487333862515377594", + "15148965960125250145435338272790355289745702742252704183388935133186551786499", + "16453015856957375440870560801879892220374643048047546023962049108638908959772", + "19594591292469468407344408128396176069613863801953800169935974455228391704047", + "4786494574332708919915792733222077450535137127349578261762663721149074161698", + "11960730258074073655703739602687794653921577208052039888289111813676118677358", + "9848013027466807250590439150605527564079804116846510943688041755058752584380", + "12899034273953092771018315472337883592076632199623658827592500124277160375343", + "7444799755957099892747756300128576168091670485463138298855357993349282405924", + "12078602067619785815908569606330383217651816461769668980367782310218465279082", + "13207091244585343533434026539671489949734093160191356902826819065281696899279", + "15052325250252549646358568907141836154046676521102037138442805194847473405285", + "2221581370746975227443137026410298234625504939466881398515488592571828181681", + "20243663867674839549278772384366563405687275170339456055149452630358919741102", + "9989690042997457961300135390803031791688603090074435089369924996763771804683", + "2394175383139107454721858241476296570939299158552117381451586030928104650195", + "14935488700038770608988697394320688627203362240444723637257364275046157727507", + "12264662514829291661352518974189445073203160920355111833291657311513159477694", + "3827639321249325764386581723986872585672536887010319153929792264348424272953", + "13487908627465024716967075008462681337662334712641104635432980395748705243220", + "2271300450003775887579228062020983806396606091928055215698274988642377017880", + "9536464905154879502929074042077088011040150301676217212058440880221802373843", + "11224187363394886568032176392622806663752975492652881881608898287407285044670", + "16348011463839183311989740909923102224397330137023072134870626410011917574109", + "658746784212845073728972419644775580738462236799285816112006815020578863053", + "10913847351958014923157235635013414182776322060119409210360083568773647202922", + "5174660898410477334415120015192903322111803813558785977142572971088703464181", + "13435625677277740540867428279095038525759637068783477185816475988654675258528", + "16042236961096516425577672868221946711334425239903980281947225696871186016946", + "5966732498522491800452631630164751678119619125003570554878083612603498334850", + "19244421524004466175382168721538493986709062021717633087544245374056859990844", + "8953835349510092921386508744116096000868233729683707705475555286120350297772", + "19400107730527891751069598685513048198266948717144299472180340676627124394816", + "1788315965311520067508595535605409766732846405132796308898593684888230118195", + "4155306741635339863500551353079678816173576578036039224848730508326156410064", + "314333752297483020587573946457568012126281776266668278461087690408622762890", + "5814056308990522321998868808930979798909264311720790843253044589964499450995", + "8139572664280730177398240130505806903517588262678422575082094265546418213115", + "5891945033023051894996849367995027817716659752927558196235762187145542665778", + "15816119508706283096011330636590931848201515685282376441014894443298875354927", + "19765585883129509218113831432898646175337020355613337867993568030797990428267", + "7571142440759701899321724284276618755088451471808734385481644938761653631281", + "21372154727662886563674173359368281518527052830463392775221052936073944196548", + "7081252182490962865977081179238366944226264513969472284114001831030270956045", + "5223967788855971504613125877205268444815856778995847146009869071113923642838", + "7616031404430100022715089975212311635761004286094499406826744277117238450802", + "2964922410519663416704982602737862878081135021433460716313695716253990013571", + "21452022130413843576926752262576843465698254258090353961686699256674273446998", + "5615506565720477707126907662373732333382367798591588442255590186949548315516", + "13758529852543878424689721858680184284389257236387722027067085472062441696813", + "12270911407050929770266474860904788952698196744336477194920983584641315683723", + "2263765921105974400584982144019036516750696376273595921030243162454245412438", + "3726743207790008961875115608784686494039367610419333613620227882409630379219", + "12237143845448219572451550465877077742264329541226110387544727976984528388742", + "2409964261853228751087973651622025781333933822306168735290950339250464395333", + "10085433407143766997440158468408095154887290417940533218330090781308733416929", + "21228510717137782368419571997756416328932321564657530623869061270948157027291", + "12056471592273332515153939599576024781202771932636795778229482480420755693571", + "18213366556788088673353762707270651486693693110301565439057413547158475404035", + "16502444929714671396753943873426599559613041594477504659253173973027014569781", + "15099656057998038622782214961934296168421711158711759920714280998215172598701", + "20662687648270300297638887955756931826210498273982273406811895752232434295545", + "11319823310823640550774639562916849455005127799420683680222546170719181512525", + "343967214752520057864245186069476394037815047607811439097329864410341714737", + "17503305564468603693734885462989691651193248463782636796950758256530838277067", + "12741605036457271400687978857670758064474033476970216981099556950125969853613", + "20739683061558313403494347679816765721522415874620086168947803143944678465016", + "7946584883804172227025447539224245968128890222556746649628342784345139347190", + "8360187202053456670989734732945943173170474770371603015342370151451116337951", + "20059356712109588518115933733238896878411826141623495434229710888297572857371", + "8908929255369499016159063161703140212756115979036668550222341666828086389886", + "17260316215848220480068274186000229981635360232751196604952216075987952224195", + "16051483806631795891066047812303438046492064609198644392699337542232076970683", + "13616797129917979734082711158569544985901822934952385632710806021914842733604", + "7121878885265063724099916056725320671644022545379534508159519935129387437912", + "5095784041111849236518098036393508072376638346067943869740820917637881991288", + "15064740101319510049989912367011238454044423307266600930402588789041306100213", + "12407265211081220177754043474825255990635653794940031741483370307852381609406", + "20223426484470923566833281385864037127075416246760538371032215131656571722149", + "3668052915108225265616930005001596789890471435407321351170819088999432868806", + "11366073886283256473381607209905269811061951337531274662130606966429881340996", + "12582247379355857288799702955077607346019480970920005735329811307693949234935", + "20186938619235157734865577521404397515633405422805778974853873925656656294650", + "16122019959033821849412382360062366364664833933984337294691025029563985515365", + "254648902211450148074737989547563835748091091484232032343713819397130138530", + "12172833228273710344765296926585753189586602769025493553315361303084014018635", + "19945409419975886341220734130071717017479702410716315294907303154809664173135", + "20712004789058289094102947689666173023011192678139675809444116843756511650735", + "21105077263104009643989508074340105423304108979860049556255519287288188396634", + "3538342015671194745411221642090452794460666099501316339752121561502241774127", + "16008085602703591565147288064897579335322875565262127586367715180903703479852", + "21422197449727241817827580893411034314307602976542729347805156844487333432241", + "18799547353759539084890273894754702756999055145524208124647385295339036997359", + "6376647829380850860365386439681035038162788714588766405462355210239582784337", + "15847703763893026451216792359491847622491103863033823028942752373064363683711", + "21753518314824757010443592444509359716445873322887601064788806456493419038035", + "8018616956493930393966420225569393035642655431019891986498475494315186954398", + "11747316959744110931059298678334515105505609504722662598929370282308351440074", + "17893257331206244838570586905501874457956496735362457086282197258342214145307", + "15961328802051806619901962582575367866289226289028087772591680471434203677570", + "3236169926687880505076444874746358996739587258202752103445199991661738621601", + "187795190351727293099714304116924667408668969503996281039583636956966649638", + "239392318624502435385014551926381415338676314268675338353695671324466753801", + "18674319341781941734600473869568608233805776945980535084119278717005726916471", + "2771345443147472606857365655976656180452504182686204076714773790025740827296", + "19450913042313320002652706987083488807755986380163327474873550819278969177372", + "17088122609555961904069874590098938791545854926952758736324719542173158272235", + "4614324608780439388145100049836115973615823562529414178724078049067318464093", + "480346578559945500918475024651677711916332481019644166207062710283884336501", + "3976896258432286534710425248577923525874780501683135711905396303214796569813", + "19747713021879142821191045416534012039092734284665942138998895512844748934520", + "18114341948852128544591474427791645861453096798290403362024577582905309063343", + "16389740922636415377645046847550552757683160597807953014971200028725848855677", + "14347956677754622309732029695580580261895229653511993172444407254733241454730", + "7001790022045736128714608604018732713668075078108360908557371520612055210303", + "19272993806385013542500911534969336370461053778032527920746797814743961064937", + "20666992739220376856615766377753030050985727426420972006419425641580077380709", + "18907720165776367637548948232402401955322000052328805042740522159109944714138", + "9414327783699575006459158047005978347088459439117502202158387157394567126641", + "1092342932834424845294743018087259301897007735031966382725140810717192908054", + "3953337326499755715834998572085254989073834902724154050737740470058759118498", + "11743581671122680453195477167432558941219274023697487318378655108983631999472", + "14292519320039108746758578387893135567048390166844266988895150640926606231575", + "3463895766707893138928153619402823425980920627031760477180165727285868840115", + "17329024693549080975670914737054013479227561926835421032215803061920086593805", + "17069029917639740020306921281456148921331852897276386771126788265397483239836", + "17757973687563632162101087414879272203562112761746273122681647748208587693311", + "4657037101807059847480820087972588696422739806756490960821623277361176723008", + "10031688077390763868641078069309072895683953752120759377432369407780311740458", + "13372959422785941027234359900894463458148714364727687916236363488102034358213", + "21074689968655856473305620806477766182958744184789493409758269023421704332636", + "19863757339575916461434905195916835719746715041076078424686010055572742030617", + "20973060471220514134365115793881078818914475917880454352814198678201263612788", + "20786322802664930407040804724442246307149058272537433665345242730163006184858", + "13503324543441706240017837835235888103455201530371938269103987765298019164241", + "21516545371414182695629550949358554432241582789246093981854557932438611855051", + "4189468076924692882857105215398697213714749255613940842113064700527656187139", + "6356916718613787191898160559615069566270314091650855868585425220261360364619", + "5420879043826588690498755027108627480358930425000012254678542105050717051191", + "20559290756493460657385989162478253178529811977588548661414171937311774738356", + "7472237964286313816282734103247675024752524054147218881654824853569171740182", + "5587656354563280475935968622322500135733558246449238814064790207486002634312", + "10477445403396666334274248654801801931117602962284736619479124362799824273546", + "6964581565574090633894249931196779138063582556737680018364077690424381812379", + "14818621060919997284977287223747030674994127751231587714382341838934955054844", + "1561884120897562502024552266455149126373072574485245753929487036177604271779", + "15924114719216774839217247278498830109576711552882853826759218581616445364187", + "4632899269214938328686734061038562662617634605056083586217293196683107383226", + "414275447940334199421265067114189311498845927325050140106659496782723706629", + "5919841635132642392689439793932385370460582843320887853171556097049171723480", + "18213825499960993231734509662843099396992046367870190109129172304011496568015", + "19071949566664135927732333412944575018103341781608323568493744120181505633727", + "8055738556659889630969832748361502052460816679489752687851823555653740856922", + "18552532582629910542384644188750599556440125083583835015670173118025699825513", + "2484657787081400262201491424080522833403834349414361745080712757081157295905", + "16983418852456608800545535696543676988540233741050842886180695807863040864765", + "4238493546779457743228884918170815223308340385056120983013226844661472349292", + "3630132677030524902934720712211822367613329466140887879132884750998038269473", + "21054939309368014542663397737839771438999660373085660928983975055849174262524", + "7764728564558131555197022370274162477498111431299751237388301945333560774986", + "3593305607441900907267751550709584072658973999555174092879673804077111387503", + "932359123290353753331737736031582956463161520044366983625790109891741693526", + "4731952008769040870131753442142911445224559732127188801247458162044777478911", + "1306090977867005427161468095288389390649620350907397989639122170049060845154", + "7759081335204598570892984361955322222987825766180300022358547686895179745422", + "6447993445251075592811527795404536687284747599271434406060192746881971850885", + "8266912047004162765439491557705745929139857757706428454484671294796825044391", + "1314461175505921794571050454800604608649221187723852105188152934159567388084", + "5074821468464653205102869421665358795805465509804176663850666382476895922210", + "587081217806702661903500809062925367993834792468594461022841500167739579474", + "5264606314717935678144297576598053656417231843504500779254542039497862460981", + "1236480134248818593225404950925034920588295884010411301784531216110810413323", + "13089489778236095768305861425107967360517148121020346060932975978601131790499", + "17291786821946523371971061686693758035121148041300868933078085040228288669056", + "18124176342257558638062889896829524322479803409217207498687844328613679520644", + "9905839015283285144616603221521153361618378103985458187286841556656656429741", + "11809535677454857598311615002421831170743476213662971184893106000202355262469", + "4105258996829645758179809917480600473288889890938687299484408634005613929027", + "7249994723657380623014328438319457303659016324967581867420756005957067258456", + "8317023308476962783876206275099298475926201657427120954368652120829272222159", + "2011843005377857984465704450777581623363347938947142463422784260490016214095", + "6478487501062645568595261797056495837204909062128557523543545066559983302854", + "11332891765024246297067848612322823673574497640669814572890785705324624377152", + "11310396674827683007586339726668223579107272179969446205517750064367092175748", + "11099104200525710724155767594487158698174345291601467682384866473391802992487", + "9363361473795780293736671173763300702046469200956940247194666073284271734887", + "15151887186592767507545683753579108698447339021705506097120199743211891524973", + "19881265730819081878858384565138117903272592569639633426872725304622695029688", + "3011117599368717267651471428189462533336275598139751114483084233022831296797", + "17520576180525374756578417994075832591962992432186234756248866458711765589626", + "6155276852486738773749976144225301808688416984253808800735523464178476071704", + "8631372484444450448190767419844298467817852912545286035361602469665228507964", + "12205507917633391792796810613135648610406161967483463808645931187075958213927", + "18222453239521787308656394705559544583576244681888667325435264246411012699229", + "9033412577366915161372037999993563962295812571996632039862860993305238914757", + "19454897503553988644403623432258520403053811767663131188695520064462224816016", + "19817199370883464197796219716426529872878349643708961034403389904486407134637", + "21290720498988312226623568214384902017696056682729297309424737295532449054922", + "5006872489582777310304924776769064242968147807589802114955067857036564413999", + "16420397542367588392786684585727906891797023280517827697299749514608736021404", + "1300345640934139074405647472243803688216465663235066735522675025627248393466", + "15474333493788166084607018352361187159147026284793436327548632778313351493399", + "5141280817333171025837036045163967613491344199994520722703718097693724060240", + "1674845241197502896336080529694883486542569529162942594482168089081833468174", + "20749587232360151357799983517636847191215950040853109011833662933435613635469", + "21065338667581575565163875505871743203827212418043845343534420193848874866739", + "7051564661861006096495144384640210161011647325580302475305699820188085915761", + "1121065721208670659965039607998179107433568481023836429952089959263844401629", + "2114003185799450303041425285829091463889299614202766352631616657722513595953", + "3334382524507526203206024024777692800878664806770692329489928846721311223692", + "5349561335050661854855918228516028812586933810356904077294751460675531266141", + "5703041280392778021418588942391165300974701557007394123618062308100870196445", + "13673663050707034371520553032876769213055293448602317144467901970892454938354", + "5483873888673148302254721546416819014852587382013956934541598037319090821435", + "19318734450985860065269203041456593881321456849029001758521815811241052123018", + "12851330354839827372437628165464795441114428848058858241484775351172304561256", + "10041542640844175943995712334828870579034783871076347036534323366252583734600", + "3589445341963638025665117050103495399260947312205835568650192524332423143081", + "6240962307810325966231999724200767949498419326824465195783823947519631917688", + ], + vec![ + "21845584790817078371458083471368949437776490472877850604640045078191512294989", + "19653529167194186573342031346879012675435131167180408423801998487681049609228", + "2161640783454164110262374377277313793192503897274785966059544028153063342839", + "3054385704838408049711788708109646820127990212588286684954516786776077717445", + "16635301713639076283966918721405743045341739008997361549904617279107633739991", + "16225725689449395070421553385264743934675104858051374243666267106604469686859", + "13459300344588917210133884568970491767131781128923804903594902942358152425193", + "18076808066595657160589765822638228194586496301618971676220813553508533309824", + "15694780501519741286439086116053845024521602672078716577385749258673935540935", + "16872540725652861460604107748085417998883467929955001131816309200004825208678", + "18265717256656085201140362928095712147159090935193139243454994794524622317396", + "19947609676398163035598483882491861002323351300468831661031717096486749401185", + "7065348755377637300800426777670517297442798705186719592553630280930183590981", + "5932961695686545421777788171001317503890223201383043783424823453778021729657", + "2576876576710667081195577161905871971928185892200731156106299121736713352612", + "12555802030075275558510564984903031995058560722557285963258561359487710166944", + "6675427211912119210966904017515062933064977336124153512189140864737663166184", + "12222599141329943348111473622063042423140762381594560767061296463113725630598", + "10362975561623981137760844569177506781739504467939943515494131756563631401384", + "12107531632347826132438108083855485290921771214734424618157420540758495466114", + "3236413362865617508850894632024597913345632839215458268170009274560894755059", + "13269640842689962370480760671727240164372687082286848502921603339956981268889", + "8634391395282326489812410144806386699066840792624899435283359752045476380023", + "14222253145252965302168372301361324351891663598874510374220732501857922860417", + "8638897321585802770244834010345336579513350450350318876059595307827518698264", + "3865974821285567306249300995778666703191077355251395536574998983854299535502", + "7110975263857442529682465175399830853738223819847928136560110618272170902664", + "20747849667914457283564964533975989346418433637631755372433299771504842519646", + "8525337218586373689263383636693181730826089466845186214607593322372086164902", + "15284698092379128147447314807096025894174546974949762301780223877793581148749", + "7697267363768037767923915437621109821518467158489394234232803683407232938777", + "18552464418403713636184903318257896949762751716606049583572368219578636617127", + "8197992130246671682417102475937137101180310399693221498504232752936385287840", + "6267215711691005252092821645736755217101102985763928035625443919819600833817", + "17433952956528062441211440388577119958754746388457390005635829394680376947181", + "4412646415700345908595327060988016573431087490581498127967913815901104779675", + "15833191543444816612091389727640172975373200401937212078659643034429446372420", + "2496738022051529758808536908421531340554055955151436808600608028881270179985", + "18865821309368781007158439319264225124435320863391412688953283839208730853556", + "17356853444859379852104523062654303386446700475095103588258687890090805160214", + "13809314487709569674040696128771324530575306163394456992422515709961741509179", + "16441057642509466688507318755613056920894191930223829210993196564779122458374", + "13126203992288988176378067141418363395032818736472538500506274256104409609416", + "13787967697762688988712839398243982920760030565931489906532854488857802399817", + "19656786571615920765259702874045834039087950126970888730192726308970074475638", + "9496155930249199891836650339371004045437843955429376625371983436016490755144", + "6133650980214690439646563353939486183333374330790371938934927663849630407389", + "9878750392407472855744714788091164958447986838940220983459727015586370325700", + "17172636845539329981401043696407061375416986816086343858560846660524543673690", + "18266068074968659293646594793202575012585251834459743250538629833034512472364", + "88170009072279647351671114053985696295758726185186407584170034409102883821", + "7161623553009701438320582200845261728475099875569530553253147102879319777702", + "19053465421328592764143131116469165876378776722378567877265112443326967288533", + "15787821054928042708896412640384007326676625326396861263135967977164294783653", + "9615173323505112477217305395762776033870692647990635021315350642536385774258", + "10748585844981412663149047472971001536312431184916168605042111548221680655573", + "17193496554920138070905215774356694517155445370515924892481894322048517125340", + "20981027561373479728773182418686452188471940655764396684028110979140369006827", + "21262972686125563821794309741130556914165641373895499794098515843451753652777", + "17107658081493676403630365275304097003600155220274082934263833389505162130032", + "12511894931761241561787052333055670047472612011302194769826693935710455626001", + "17806912269440585736864672280987816575618315620498245554921738316327046304192", + "19545292022072695979212345737390709562681030627834519269858193519314537426533", + "7543800958185166098093959246645025070235714806654195385000743554473922936618", + "4625475082534149232161405731224600013881237737740742465433231001161153655617", + "12763413547514960206069967971684559703789391875445961922711624474020658766608", + "16881857439631059221437594346198587121133192812269921863116602807689099485795", + "3231850141984038920375478271531925025564375094627983241106994584163478821676", + "12025284448956248150398233143375971306100458475218939512492496426861431663478", + "4525425011555084029555870990647344303529266577387030581756537915893006804174", + "3795501990414375556011654275060518399449630146003978757203023617760680325709", + "4509128642188781738213513757207125341975604363824265606323571895455672910764", + "12203047758734592203615462366516348191118610129553686354997732038970187121007", + "13638687018405787761639420673973367506339916347486774445588648879776248875396", + "1480604348496719441038523415355764543336234934845679094037807499653490493865", + "10692361717579001228187212442602435054487474561573499974105521910665132275515", + "5007773285184019093586454921139763432640822738583548849737728483623941935472", + "14779176361271780821074929551122175132746249520990325894754727749064980218318", + "8810869743928256631039877155210471039332035226261305285283609913565228857800", + "18154173501970140404154140565370264376529820426626464552253141823874364956900", + "11656303509442573507998921894257944851336988233800378817636452696829680890350", + "17658875741559155615021995850723408238216088494443352771306394733637591342379", + "18902160612986281748775835015982844003483334222891067179969353562662106908838", + "21595310045201809412620701470963174488544609749637202383626402540041734306075", + "19479000691333638193560008307308022198954460138615126342769742031420251871629", + "2972632723219534905306782557604159867920608086253824361540409985645334537267", + "3847529854347283694984727631990010016411937091308711649053729266492176961919", + "2403423343103741792664345548178046472942690163193710191494177026621927723840", + "4715026270007766325068951713831542483223564562870257986931331836552507312912", + "10050946066792704139911540686518561899625455529845988837175998569612056291645", + "8462612443675205626907647847435181314299724037672159352834910195653278990554", + "21536131862603746518459731430650379927614037990254564979711500405913555717860", + "7888269140704212528047592848167567446827917489655456773861487833453406017069", + "1170377169572693438349285053961454675716678619265656860173330864613871015536", + "19621259602533287127007734174330694952187330430513568066156633937834027643682", + "17165866032840109673438683878903959007172818091757219456373859826178202458170", + "14933333727688891927411002006378277183547380244506194877231532901665762194033", + "9343867619319423527260818064767777717418825720619434299636538864535443951928", + "20583768764298760141290708372295636534035063275069820261065362331153347542719", + "2577125135040205550039517386386446603255535410845714513971957864613256569227", + "17000415305135911202592785659979381300200707282766529883690542305214226820231", + "12511346220893686853445962138128902612419766727072608028194026193211924059518", + "4149764657828794758957335209849218835702975200988138041342768571619211624011", + "12689460200478357229673533402655824055278729189265502524126586766684281271735", + "13400421547966647260602415862775725767930791927979420926402318903131836007467", + "2276424802663141757392692926366037451925020546827329479674513290273559843227", + "13207120510498253902527141512742067250437249796266587317279641215901655830896", + "11305234940211349140158920206630802129159533495623653407204084399910372957174", + "5497774303468738593952657049388763226915569613134333653497976925327612433620", + "11780989050042171707300156320134237383624870460000896361633114513125178671303", + "11197132624111031475729914540831613691695800675640925983729890209473799714664", + "16140443794881450163624886684327741887016098106725788249967592676372285999349", + "11051595327889397185166361039119529941213436445931880650633610124485963060123", + "4836646605031570157308209197698105249544981108702254887271797808619270080413", + "8209009069949005083422618473679185090070432230901987251869909206753231145136", + "16474909562767173375575972470108350369290532718799369728226238753027802729614", + "6086055204212098936385823679317209054285543683998660974885049732319143968166", + "15845663692001601094665565765687773803894441337616861842355695214969875896066", + "9098420601018427903226168857535773321796962623728010568581252061114961655465", + "14398020486027915166775907825392652502550084557499300430668649177321914768934", + "2529766650486869899750453189656713618464482405989854109458777379339220142554", + "10340604539765718682878802989600110179317791699890691229388614321680034249914", + "9878876288226000720310995235539201725902546228232202248134844052052896121876", + "15598631334601739991411156353640870142866768421111507060679644747315412727160", + "12476036027414150699365888926628033468852849122092016651897900659667567319806", + "20803031063635652861008057675598133287087428947752503544588637173888166669892", + "11219667600899420706469792543049361323110081262796605270588885844619718041942", + "9239500483618882938511561535687911499489200513580468310964041871852656042420", + "2295137860333409753273134561401486593645236761709753752653034489900637997862", + "513840239484316074257070195372896343613221099656421936676995152626965728304", + "1240423160571920455132480263739570027604064696559298170495781456216075158172", + "1000335770452092111597579881696386094005166181014081250146819592415429840281", + "17732477784640275643871498532120858657597895452427536145089468999578047484919", + "17170981021801009209909418054936322243367033572422755593175460758882664580508", + "10474002915893131225108722048498342055539813455822038310171605686332057287523", + "9603348396520941909404735719689357385027896452682306755794683717883589365434", + "13997990766251226080324180880591113480591220558468819674906322487254600879864", + "19165322589470665586312376362321121865643126447966933590462565164041476345514", + "15889877351963901365648141054999234691357421706029208812358207119735767519856", + "8668047067682657740038874191537478915165931784511538119786089297082841482372", + "17366185630903586492716760756481097015828466883331247688174482412238176387717", + "21876355395221655909209577718688727663428003322801196411166438189091863032706", + "8342495812890419318878301820827026193204690451839354912098079931173365213850", + "15887225707174335160711915570149252929623026695369026542224074924735308390993", + "6378295267974387238182583304654614405910401210077434367173120406371155400542", + "8599288341856208016511446109829802753279910611437057243892303163654271528710", + "14384569526991087242726852106274677884712683003324629101047489219988189168985", + "12554858483255821944447983588851327925753928086067377749529759729841684476442", + "1244202115364291386279329790002693224358694897412189477392486275482043163127", + "12995340746860271768506969399635216407185990298275348495956441603189500195767", + "183027008509741755078232756367286076025735908110463758323021771971495606238", + "3573731114015522988150416514182944755210001235963017910935079689853918043877", + "18230391763740278339288441222957881377176531304894832542129957275399074045651", + "5937391935832132177003018978899438941930145516233683431615177125757010459652", + "11430996404641585372503695535042106445550893936697962891622779166741655729488", + "9708207568505790825432073778606679121879367766370067169784170835562004694648", + "13398805456168802594406839007653458669253514721606724093311019328402863953525", + "9509172607193998955864433140854270981598209332315830561489225794322679022459", + "15502257451151630587420754221500874210427552449899109970701467342791400645333", + "11123898636843146818131986172543146864729611142704598768652657993190010179131", + "14756654073235850387277128827870550186598788932163678015197111219905127222330", + "2775596743802877046549055877116659535653734622828894792515801349479916697007", + "19393614735747455293764838818808226619563895886462373669050764399041257455003", + "1249320178045456927170274664240781660345447736835400684416240818258732622074", + "12796406917637014666646024992328239080230785967810042823382614274279812889313", + "8731150020101451449969760761271583792743252060005019647239509560664328018351", + "9933789898048906634037638211430912333233899173340437080629891912049447301727", + "7466451925704968193666093494894304477671721132981190905063657961063858961172", + "18149794038935586919422942907608353967008376245075544434132167909292547581560", + "2645338086907790474420268058095037943265764190369965623902938950146047558570", + "3805226260777608297922168382052882356808083423152211373837998107098065055539", + "4989302370880302660583716899846990066057960576662320219898456979014043515025", + "1070415851697739963236557454136444292508409566097266743684211383600964969141", + "12944245506579810779814853939016758644439439209107032516878032396119045414701", + "11769349237256342071683275569967616595926207710668472449278925821680308524365", + "7515880964846644263997052091312985911975830135506678004303761824155773875060", + "12464357909913670578721898201968110981078170975300784494985401212355213230335", + "12531063240134560397069746994123636136169642575111678588073390651101761449521", + "6147041690306761331612194490775813268707261569782473045839636103273693189609", + "20058953556654404269207947758850631052629168082562724690664696238258819343333", + "10671449076032051862717464872993019906670729457438083279869852502398951441572", + "9263778898906682528709315203073828182663073896201474787696895934306219116039", + "4280018790033654530253273135477384827165889895493311470077828964783579313988", + "830995672310831856642518343627405480722960393571135174568745694822266602592", + "12314576340884680561277987296413286687270775984122709242612931854296075085360", + "17095784374018350334250759671947431499861842886424783004809423482855754115377", + "1870276341686143971262732849057691846357227120913088159474346497974038463590", + "13105414947588023527779026549450388391878972150198998416931943855651155797800", + "18101033009026370452679604491367700242633788731529564744613454397646795133212", + "19518878118392884244142849835491751668891129028130325012169949275103156284312", + "10048097099868399020473369161831917580618232227587941412911792567651718843249", + "13720988503976110065971074438615366180970893755318530444813194189426744566454", + "10206920761042395073417518236713913313307032364045319534823111708069826397746", + "18034670825630502903409981952109964720282628088881306395156166316305807778803", + "17265902307488235822005808880948915593987424296023174321440226533612013329935", + "9429365481099850064016032865663594803605999180077296169535937496741805848481", + "13186685927810765291293578499657740344232117785406437983769041709812502541933", + "8033401834359804746763731113146503141828920003868357417085510169143014612168", + "14829873372988191912376824090708426813704721449621613864198115299064092181833", + "18455257181054398358097306328798734574318231150508108527392947186444436439740", + "8119199559569330746396633117116309013908934582593556748077373019932279318500", + "1284108314616459045915004998457007728060914233392140668856369685125999999332", + "21852625948025981910963631951406283302114607467722317806583142816175293668265", + "14294796340840469579766310516350659401625921390409030932793069064155351769891", + "1783113620816999591947201004404230259494066685648885624939940386561651976668", + "20631226309571056417714477834545747594141696200863258945898766619654143617824", + "6903938062054591003542771080232078653298889568458433305746325082721608484955", + "8256881116864939888001716449324731552833717991541507741291129726788047573071", + "13687405621147199886679388919812090637070670174751017085656416653029285459311", + "3996906960213710686419471713152378445913261990655942335471489495797584984773", + "18609485914058202995712970621084391939086418713703752883448366703199989106805", + "4737790485362149715518348725962346663474900881645708435962411601476490671749", + "2822552925802124863166593828171280409095683790486058849977885272116463701928", + "10894109234909999803847798036461479359595641981247612273970502633226141682614", + "7246294570777241116302579516225659482637719855779307083700855528941826872713", + "9013198024264523556163293154624511496427558116561774530941327330691750043628", + "15151960056770776873319338589377108700463507419234056013408936730718825997019", + "13896307757522598026854039348754398062744662686124720657491498342048635150631", + "936098917227958663269717535799277732372546413929626514061262842640907378836", + "5762347306349503820330412402395950955095460299470544346332901816556543923381", + "9011672463961728692091882997147124758454879881989902775354620290305441817434", + "8298508790377315029478690089877957774356278876814712068800423484382883105746", + "18441738918640842202469737660313781809854248733890462980130113721522272925935", + "11674356714078179732189659380227960024612632400322071192164520398757529418670", + "21741531861742185590228910897294372846694047325677774428196013372184305103642", + "10726909710822683151420187237143914110905744739798587383554354069582246136039", + "14605794533892313551961292244462849563801666394421863533658628423573613615452", + "18252321089326369077368015604893296999452816437445326292408407276685442204248", + "16489511737082127400599277690755867907347415222776413612685047279770378853014", + "12335271216576477339942530570019671580774683415225712107993157387028894905764", + "4626127015007086021549442631728304324061443778286284381465764558652325702889", + "20204422163930758830873934092037602825061261159909688022442254278786267559979", + "5908008139766198991903649413759936674892226062609391695798412118889649830013", + "18949218162516421406624855541340518738013110700902674485216824630035967277648", + "7857158354961323527996104954036841386964344142685428573605787682122921808752", + "18862506387045373228977985979058776771998946696427211846227485792071994347569", + "21130307920872379374988363214010414716464915978170248808030666261671221223543", + "4313228149429751211745340097430041420420928800665815874510443032063502322102", + "14718887479978402714517595897896626933964355973039392802299204272000024809808", + "6557683321264859885031038170253446373377122179252187766556430383002360480784", + "17122476831292693338874176268140351589680349774087615644242938775777697017452", + "1742427015172593656128486448061422456944242303531295475988475807736737942042", + "2027235762831193006217486738647428392424417413253628143597055373587736173882", + "18820467606179165158872620834655197843967670286762404094202648563691406576741", + "10125524296683464483537740904779830909043887085914486254166411012113004643542", + "449309305485238804235855079592488366640797157061432847068620096879828248038", + "18174298857888178621780055946769726501913338858930582293820343672195281946232", + "6524685458133323533884653748284163312203275737781993034758692071803006590911", + "5779446108310889204396522202292098396120321905868746440329327547028173740638", + "12117601207346032264472777912492513317230650895345305236204051403325746076935", + "17386891799362994472985052445984141822367941805998642899269245566076967495748", + "9179366996073356039318771265789433069240332535719540885945532359919512410546", + "17951430600208154118507690771734790328820535928178248368783575740656844500941", + "14192271756367781523935569988212364655718978403224403387301537958961554173399", + "8221174165933793163901377098818684602634389528051583218569555199418881843300", + "619524678975534727395248661178123212036053322038080027161284774894885656968", + "6811391538547034359716390364062047260775039349151599816482953672829058524341", + "10339322550348888691122766885623110168053574406341544663827975995639398303045", + "834547622517043358034390288436661677632262995429052976763390684889069201500", + "4521430246419400790694482286290497928487157243703675820424059696776489816307", + "2856429439451475378239016597030439586695051951861405539943990631405466607827", + "4111906549409128541352401705521383327041723912730932911918947847640475304547", + "21238392279368838153073998702772069142756942255081903718049634748881681359750", + "8024413919212120225608654701439440497293912481641002166919294547045993866155", + "19638917754035941400580825054915831416618623037514650566052024405861982044782", + "6363298127640737755043533904928034878703559551572444680730376201407773738099", + "5951378579926778764731263132308899303911912211097125509228135617058018449106", + "4299191983632622048727922852687899517860127872286225781380066638217198421795", + "13147530014153459600370443470203975351218206668086180214214011567772265521187", + "8932462500427500118784677946351185252169486521981702322459979960456999825085", + "12898236202423618599592389067167172325929052486002381527830813712396261626227", + "11097514049007489440587570256998525432340163981998515135891661207289091581098", + "16939217257331433664860194883658557949066598882598231827843396489027582239493", + "13558314929316884214321653422030897165706985165664325794343497050086242614930", + "19068785933927002693740188802713137005358154768592984493993019480794959714772", + "16219047234078325455733500620647043268680743772219676241027699492382953851822", + "11566125486619880337971321232314420701391596877779136632592874470668332390604", + "20247803830618639726625840441081484205981495541890152476410337155981008644211", + "10982561928418184138823352638412764796110898816926174525705905870653768875109", + "13865455597687551061085338688013943905732507438297014744008229213490729171094", + "4623686187146061526075222395870304599606839445854872221666055672567414423759", + "1297323969860517810025547983969849541225259454470688019030850877270479215579", + "14722215188887952104786572716523684038024407798912988920497316253897978502603", + "6270780574886929570568937279666637132374218041131318249843841984676427785096", + "19252799885324088942740034016032302070594496616562387546044018090542659494091", + "18789886326443791197255838067795128702043473536108855579944353350374719120963", + "4004428942869607083699086726311786949460607919907288923307322356602295441411", + "2877506877897472797549134443444956093653579146599656174810841010891875571228", + "17100820986561348145097462952844272816935030845535472396118435485954197036467", + "1347748684215721199150368750223253727950390142032926135229664962610534785585", + "4154005697105794859947043472401749393077233408100643568245878899626874345951", + "20933456016305992700551258430315019036248529816811141758791937748005878705139", + "13670472427221544431486503737474464655536253995920989690199387492221273597238", + "4887586768965099882782231950154450807371608927535862225573402423957578253712", + "20873520765554306935202238707923195971241799663989834594965009759362455236319", + "835408880898587261190035308896108471998627156220026699783039947254106997118", + "1168560772528590466033559747490971813513190425644816736321133758228053063362", + "11336620716299236865855310989061343228509583886619209006261263862998075759368", + "12980812954661261543443724949390081395650693161075132555819949729859873154552", + "10084252406967656001546402499122907140953995723236718543311202294822930374906", + "1644532730355076490850165587814950390528959259229216666472798276024619697665", + "9132885938811460827145905853169583367514769405887568204387077749084430489477", + "2725900975136309543580364211869546121021875237274249313229081305044972484552", + "16649132526365476017376435851279563056385888284228253635122650167497125722498", + "12731917506454877589272897239554773638029036473865906141458475592969608419975", + "13594653727945200481903485136384279897657772670755085103508581365171774040719", + "113624989360858461940510511528457240403880397732971520793552365714387680115", + "7789436153009366426158912863312519977840172895511546817589361831513864987768", + "16143950714560148477045714466178426422461079160482754992311521791041688103078", + "1313437798365489562563299651704767522651128839986029835665434483203980839176", + "11252213995691856633725287186457035931029344732702134439407400361778220541650", + "17736157138183214282785525992210357080431726331346127883418866302441793685861", + "7180555369115773227093283745784562042441101566557650242391581159039035173198", + "12210449409908576791549312007483796989239766997007433768188337654528776461829", + "21645736522624670721457131423612680680560891552619001261475935027688037968464", + "5181231859976513481992166491356437447372643837921293039933901630631480168455", + "1822647984377309820943781894578040991064004363022862403681677456868041924061", + "18871375493748399832446064841374255973869421566131394074253151764055941485971", + "17104418810613244184376069439022653413672092602368197196666088607272531673120", + "21352793177578730013875622823471064860889253282644816974785905687738185801553", + "9232438288408252188083609458343687653050330220889343945216702072572457878290", + "10304159048240945355136802455474936548590756836618851032143486546904634881690", + "20884395191144673183897559585241012685606665510456998263218110825947409467914", + "2819516448715556874313717913942313640504691377816634987702668983716292161632", + "7032608084905515886212514778954491987158299018684745883187663518324250619807", + "7559036428305013706113865872782652760220550823937161831466421604635377603666", + "6166475941379369323404906159647871547076193215494151224037609592313271326716", + "16084803679651600781285997577643662084204923480676236051120275962952234677286", + "6658815199061181195201720843092877884603511322340036305618980989430727251871", + "11532335040368550811455379206918215244397070466594500713220916970398105300183", + "223243445248411375033016341241378810903532891138952541245228108341484828711", + "3833279990355432746436294779672706393587026552206068860301259597964955244359", + "20021860409670140006280261770552932742716715786145482153861376747693162890815", + "3960940475385499730462877864983553000107482077213680397288242993303568324972", + "15345391775733994458108231909936015494312151716517317512597014646307002937890", + "18277195779471824870812914706668217603589292191686932445872231693455894425796", + "12683998657212817923655738438553698764065257310760896868569072930043411914592", + "2381616850898069714423289625050293940364468416728632275660762167225224591308", + "17069254001556064617313013226218540738464402615134272312196301018778835133248", + "18389457738365086195717208767239120166429330517506018082718782715649157736034", + "5051258247116083013584227503078370155132486498229823799712010421955209135014", + "7997986980169935581373258514853533781209293632678320920663489466999553296266", + "9084231611273069170288134842362168520424117219722297893241580415854843050029", + "9612017102514059830687104062100896777017998502712927457146148255553769995330", + "9213917869163068338076473790268314749980273391311658654819685979854193078147", + "3968414534544698110694188177667553264094402034823447016934487565301236025995", + "13588240166210590726886944410340082403890007139576685347416857458181479170063", + "8425770643762906660570072207190993414304346471106950840911695090319487814275", + "1652806391920335323990555627698612547647520442841733871570123598479115999627", + "18130776043151518739068856550215769502964647168112043888178493846726550417660", + "19135684176515473484108881204420762325009779099777199113513850601885029049659", + "18523926306415003163998391622948236629256503699057039444767412461271077678701", + "14918281748682691271882204320979112338671695483566718123583225923120856316295", + "8780852767944315665594165432479024772720494001116664689088446074574987494813", + "12668508386943347180515022360355291044387148272127255261058463414454846963242", + "4925095646155669413280350202274427260281996843455607837164490899877054944846", + "2595801235413574965831438899756848851934934001108314616806171013774086568322", + "16016207465062828688275342866097975914741628027802243675066228993942777558633", + "15024416797683381415563755573423733835281624847950439607885361353822729812986", + "13470695612540254635863734898909669181168896043111281127113964919743317383261", + "16896649112088097145823038767205812267673142507502911459577638240816340032813", + "5645009976160313260833270091943535119457478455011072475922688828021639421410", + "602871159134715892643983949134724748180211299390344137568222174867411670803", + "16865933848430240952665445021880291168087114172384742393439371745277930120747", + "10638616947682848797128584610899807071800790542581123346366834113610995491100", + "15823119882352004895965266205059246906297215947121418230976223923682971872516", + "6335662959372125109139397987508104273775067107064186843021683240893850916670", + "9015302195224176847832179354591500534302983921972567063156768309852422127379", + "2916208839246568972516202976435804092919863447720049244313355052730101892832", + "12462097677706957512887764105458513125712391543206668564860335255817545411462", + "12645432056886268747459912808478608940565989560801243474226148714998374680219", + "3180920336675161838114354449885404230480750260525222632193238650778186030194", + "2765038069658052652090069059291472962961753315554068900506936144102952443412", + "5456153930594691794518876425254629839890061712982880399455001410106283524009", + "475588857464770407073340860667255085025964024669275489020055454066130988320", + "11604422213771358649337761367286746573670774367844043829963235653833716835011", + "21672531595983328080827235954964353440581013778240609331813180834517858151965", + "4158282767336475980124978163165153479336138216933325437750412278000398054990", + "6032343652765967766200639763117076693275022773598878447445516751911914770612", + "1408994971542022766817088410277637989937031937486427645416181643240579694602", + "5675369397167644827707774427966283386689036453227097867563588663607826158481", + "16553836408556819911384314376537283029998074714786222283151724244687848945589", + "11305817545614916257748042736988000193121142053044277161651567885557438654074", + "830935697282120891923075627410843569046784310505888414612692179620161520408", + "7111327705324010951262975966073430392141084274376586856008212260205185475707", + "6041014280799995292240499899142298082182090482839684923821947404991902712715", + "6861532079489296739625909197765090537384733717891476126939216899495278356435", + "19512767336722762932902171703888917915267449700217444230295060094846152793374", + "11362087145491820831916999481856548088963000118526088967383804499026380829346", + "5345785713105818128869449775136443413167434948951799284901802489769456439786", + "17034353050546517551433657764343123751855407033689772581554238132654775360523", + "20948587704598096549680941899688868509854524167514075706787714294359604474171", + "4994911941995400553330254818898448719600721576253446166237206475949959408151", + "9688152172628114951713346942216519356583583295750612146456768853572234580639", + "1343745797764784294443121474436370243518251011725658365765145682550385976553", + "14125032499992691719199426991191077310874247595141214711018898761632184485555", + "7693434862282992683488101124262547552730705787109948156284399632991566403920", + "358969526543359888045815665915036908949414956460937733680920722686744813444", + "10388303602140838746049100800433273910870825475060431768126678575590491651119", + "12003019672927505670536349874007900046436859031431459357691867861903891807068", + "100475008966885612365204906854412018822648141035087921833385587863034960276", + "4380976634270985228665957813264807511324931231158992383372104805973515660993", + "6796890817241810905532067210297629305992402701618394901609217064231507595098", + "7688772779168713577464553940881893522916379475003769246647902913600123216803", + "3719641128095581959416223705616297841752773711170097329183166123897200928969", + "5811339040244843745049016703193592081836868671814750017107506365287109556605", + "10595470132919677811136446867941153163237789139416696690218064678877242827885", + "13030722908918648575860840375066614839148197698661025042088986261543919080554", + "14270313844057453712442736904892342369114041625795108985761645871570953543978", + "20755955200977796735614094726104970512145122846293966132203844248531587786651", + "9582579815411229200777590264696860160165980649555432144848137253789895405534", + "4350662320089661964064338788392477540031840065706369837322029959874552780000", + "11294656075067466938178599864610454390092206885966775647004601601555888026051", + "18946972646213006149468901853070557711933198234858050136328420076856760929327", + "6183788518442229735210652665497651321434896307527846329693517674734369795163", + "4357012315121006561105620778754114894493832281520086713987988030701496525309", + "4220425144490786998849301484441980495606132822919030749888073325247349169938", + "14847262709636261703845309954203315918152776098063861666522433996841734492983", + "2207604061881398535333278399637137320820424466179707349350037157354962490429", + "10374520449793714488752224568044645973292278262497536584844249765683309087900", + "10046752763001031505421631811000499551831682948464981948135908582555522139635", + "10191830759680421095919984376117799343794470779618993576052513294297834150862", + "15076222662591944075232357720469485509899269012588377631779056491168825321779", + "10030346249770809877262359992756272056670319300684833869196768465826414079904", + "4720507975531560789365265597577186025064384475474978173798769571327267599028", + "17191621856092434134985786932980111138837305571579695662259528942970338620884", + "20148399372131987633026319910847242205929076170415419364579318442517450329463", + "18536741510173922435024204045457609239829288683624474544615917164289358647693", + "13631750264342742049519923217689863850040711017202360015936348405918551837067", + "15610854494548438731317902472235239790464071411103741562289565846725908998069", + "18387668285066173974072893371855420071639764562065930624890442086525580978084", + "14766673420520500288514312076124802867192062521660406237863986898959550205282", + "3732185864446238105520935485353063605521423641525603230547743875428671598696", + "6726861976047081116380559824755952195618930495454092484512720173680677582718", + "14230102015194545129058653592673168907799074203765469344837164009883877641547", + "923698051441061755360657704655014798009775419003226945193621553062258046271", + "9689434574353223411824004165283477169808920117596652474458537007050145695959", + "6653548670473374668472111797166160911158340899061222339537692495508721251614", + "3795538039750942962828904073106200577850807348658267681985654384336620251059", + "16992880538370345570685975897913090022139738512471371596260168908478239467806", + "2201111019550353407862043052634082471477202486461462021878536956251970423986", + "10288260758300652435946350416864664136108249309736639494191503136475372654927", + "15709483617520776431567199768118570957603227217270596124343406544848127051229", + "15072326085023560939831375504206736581668538451241556883193631319058350825383", + "12561979699882391671048614617301093740097194439650447103774288376923816448076", + "11578577676104716528266269909528660737784973170422409028333858070522660907599", + "4774024940257712349371248845501840009544530180895035663519273410524858053238", + "6492684254039247711120616647406430763803059236762636509335603649550553435616", + "20771468823570726361366818215861608649059576818091620623157713556101748134372", + "11770234809483645852438195876591109251904813195909801267796013610714277347278", + "10322220982623441869990996687272667519839773221084466704263092761447901512117", + "3873750796549479014998049182061589742243147534079044128452565125163635863792", + "16822068107147748367260830510353037256008422122115738671496285709779217814945", + "9894626898191884524351933214321095257519558205219922050393534754827614996074", + "14601192892171927905999982540260020693712306290426245380657926166256138073598", + "9507456803001734658737641235710642159379435011566715643921196640676158132603", + "17204767985326745166438450937743889339302038750836189001173941240117259581627", + "11493735704785585407432943012768068237407868899310349385901453142043105992145", + "20264601365705944529765978188292560353562052194559042151183990689436087045415", + "6396166643103201291451232462310280514789516631380296812005611744416636341945", + "6058445133237719937803819124490281802427048168421128127070586670707895911082", + "17832742609350217271111001369998668554917153176201832135065834598001176606174", + "8301910681660121964370976371283430908357731729231997961142961954342095329645", + "21200700180783276860226125147359033530111487784446376587831447494082008760372", + "1364486141742658225769231963526281625713690163326935801558529383187840699159", + "20160669267302086336804098028492246933121636960809421351182280337233197662342", + "3635162620064198915033858334726014494627531534027138539712906873727339940152", + "20185829971410353373403938384712968683319708682735072330111840928607967762630", + "201523690073703194002962504757428126542763652704293724447838128821998746350", + "9709386469732131146744455888595822333747765095732359649526137736669486544832", + "6214797208590500165345122859363723824928963832472111818012074756679462353515", + "9545179136572857559578932159310632305901168870225039498707457861463081350889", + "7958214991370581593237224161079745627463630298171564909746677637838740804837", + "10143975143960280812282386246733660505472918014880729933237691446362785576312", + "19887571710409013601609612720342170573208042935750400481652798519320490462147", + "4385901431531790517574018221866017216283585679251714390188554453215701245125", + "1664624586830909279573419685895566313699996183243836877427547144869129575305", + "15793134038802395204513915871355486670799044444942914383804421551073411566881", + "20470704207635104884647349860077097804311221255557839038451274311052027499926", + "6142526726838860239230170135812194898945683787933736938595250804972260353985", + "13855192186192101570267811921410903075943095341965742420598451678343592386178", + "21061758735696246985536203700046768078820850320825204373370639318620908053680", + "5662817483820503797666808429029051672943117302178352551614788456216271483237", + "10333519634297417737172450445440333857302089078097476871967805971949123871529", + "8218569982501149814702324407474792474686368395700170296571212557818282461359", + "20233400137285640555195780437569385053497789288081872387339896466294226099158", + "10072372834150094335098617649200237176564672558260325152023334458902878530776", + "9266800412877476308026297272156162694485705323044163059710847355278349659379", + "5276337117659173276438760052296857124207008171563166875174912047683077401125", + "2006436301001583347899849869571017812498189722949517889639315185888688453745", + "21001242218121797019239763758294108685115914209711313091446808028690842184156", + "15200074016374430224281445993551091543367930754541238817021042732526880610544", + "19710826528876999823124699885521051879572010811534557471867248186884736123929", + "21404362987802009741223312847298906544768900296657310719715972255986007016585", + "7600530036229891939971232581797836974827849142680304648314541550980439417795", + "21632454463616985503979878539576013805183337895553881618120668190150301665261", + "6334758595723856085036854188165101506598081801208405745986881361560891199193", + "8057222251861033664666134852961391247267479839150238989503146436301385103200", + "3219721852432286392418844864881031568045047266727931843416507933961433025545", + "15429997317679838542619650717630685653076454689042566593207301547187192181410", + "329174031237742757022307048272693803097641581155976690924186132266202912753", + "21732746139483704151357074738923965870601454140619898689390165872304758524581", + "12807082000874685484816248695921308340943213652025179667821051958845707861874", + "16064026146032729000070008033632737632876654216683451632905581465015365908257", + "4212217153278443796283906098959531995688364954974810176542537190766858609818", + "21143229289970553639173591837823586981543332391397328546878889800049585920625", + "16608191574158019592113901126750079565651977188938492362618874182536574927398", + "3624421885419003645925287958934125046517213705659854889615838255824651468218", + "19316501828231774431815118527212477451661840620435061360743197812130426231605", + "7662891999266634580053960517366506438335835205004858023405036760628949585740", + "16083409929948531376998108923041427957606534473459279085237388567102877584234", + "13084239861882397209044914415955882241492175271640738785828432132564815850793", + "18210156525345343590423422394059177051429214768712959878778772626339164549874", + "12315464041324428320313158873511378501430493994363943547122434404767709664853", + "8954605304811330146055184939317681861245665333018873540562258152334326318605", + "17293224339343743174562117843833611404589728024045173960633854068798646969854", + "12077428955835955218060944701333382577662880272982277398053344529073555194830", + "7126046228531607392073289758329125358329379287853920330519927955077178144953", + "18588044234272060286987286081803360761513524944191766041826466047510554640918", + "20890448514466943340126201065304171306189507157689256667534257974304306634091", + "6801003349507087476371788107799511762780253146305342273491215439215969413940", + "3780619510354109448305575837112860323918556596060836509041441555683227828678", + "8767334354313558924867811651574408725653000725545478833889044301516199439630", + "21066572358677074507410523123848261256731407535120503516033231013762098795269", + "18362039042064137510549303809539961459419339855353944845069477034687076489851", + "8913523920256755688178316396983509800293437764919338700779034854771483564854", + "11723401432613346160205532396432709544892024270048469362382924649893252868073", + "13215697753311268652932970590975269015978051881025604765698398517094385751294", + "10096875802507022627888049762027565166660383010058121573194339315127497644823", + "20585340774134656767573027722172296226739825364200221949739798500696627689159", + "10445154974790139558211972190334915738383718443406895551366524953208899973439", + "21673116720904254079937605482525822519756033862602805679958950491098633276310", + "8625969092860470813380520419082444422545844964279894335272441710185243798934", + "4025288875907887520103326512414690750293134922246866469493778161136380894049", + "14323499257030290650454481578106573336673758956871892054979745929234879010775", + "4477626471847050821212591107628903165841822822191648560679737237549532746098", + "6035678895480961290473069135353807694173152744259585744720200702817852056059", + "13087537929005319145430419950489154179166101298384003566794942619862071759110", + "15034538492744821867836124336381708186593112293438604485661643830336837149153", + "8805139564894882006885168301537536158944953788019925854273090624783308357258", + "19958581387726270611337524069699729934757503443234146496516617463929755531372", + "18594586877352581063978277344172832910008313781358399221576815775414535759101", + "20126420557250610569978010728469817957691099256920991551072367148797364737827", + "12608220343122929299383466116607513455913710378844501818574872277345002049133", + "7472029840435729707246144983643315764441672074448478483499047549570431985698", + "9145534859649655987690711448922451001707059796425112511085875363972462746054", + "14833791973699407961392823324214593517418186311389016446279089810253909982772", + "5890565022258491346280610728283677536409667465411457717498759331136098626193", + "6036941096143250281821883413338948223454138251490770618188682101941802166158", + "21282043678033183106352077009071810571452663281954166213888935238324321673748", + "20333804372144772829649652869275200527604963503726633269029046116798625871599", + "588131164750999180590569033490334261814498911261696634224331209454245047082", + "6825251444721740074952317618082720927334521807162055931163012547596328197449", + "917697824543986394290655655205290161739195186499427989467675782006019774097", + "15041830455925245428849291972925354368991606611607025955853683450026323915411", + "18474239974098280549955893651580150846097530768719853315252027744284466692200", + "3611784158748153035104121136455977264222066374809674800505557964617331443457", + "20604659450393945231663570065906647078596881678092078709858521111095167311757", + "12029452301550033145724326108003255082656089254479215038003174967331098163936", + "15356676148621236051968257733981998213914051180019702757440018429006515803587", + "9423362269266211501814985604120446961293044110825100766698681717935266136618", + "15899728330782424719479421593102373102438201721567285551304437858855478298992", + "19106020154064303782722594063821106692950347246115176981502414167189733529368", + "18908465093637028691441981127932774442154173676388632144231302290143899401470", + "14007866203893293106212925571168282959988090650409909185874688109550330526342", + "5342742216767912627176491444663860490435717482395442556665323768598286007506", + "3597106827456013501397704575480537495681742041904755766922490829038770978602", + "19345954862705630389044258042054882168728404897041130532396992253851595567599", + "13651761202893618441816264801125692678934609890284167560926428043317180108703", + "16448172308101926626190311626973854215995348130429072972101942893358814042334", + "13856649049195496934480457183055513754814884703875171340785294070612325977764", + "2593658379576055879381789234288074898757591156526450196693707683508079496462", + "12697568363447920404231218962893478531573733618972364303216707079428387821529", + "10578645587798138343532931014102095372709438567484299904261443385326645475306", + "9492492460728662705158180596118275120458955222134204696298182307017420905508", + "18064023256112871749609279367679203109309609541901431681842430343172099602354", + "1989944422169611668560419735240024090899518972063746001231995750158992709746", + "19903230879435744155012664241474498197586597172440005182506995303093565641777", + "1728511243862152479724441453437511013527085523030423049007411340644871969509", + "1750200860549110325737825369011951368084185262869489413254703181129282667828", + "8859957468047988361338455823958226995655820770262073750796277641227321341998", + "19127783638542055071733839851815229696558338746338070032734486927449049698521", + "7078877955496438548183194474726026189811269821062629263278508622456589680639", + "3091324818623570240229136118759236075549426086908228304466341498471627762399", + "14195346783292343393569267444162421134560117165562018311694222203790784872224", + "7366930408724527902650425878366319531313526362168392830194685421619273052822", + "2217598702761121052299380093973147900304006951260118283287015270774119856107", + "16027808241528674736324615318588531063352235936452195394658431080548186455840", + "9737799828204615060867766665537725835381601825573025977613959705161843611093", + "8064501393540602388259218046920616332801811052858376733153856073004571475196", + "16842422805430821078995618361206787780503480464556310995070028960079907202166", + "11112062472802799468234830097050518793604563281810316763737206541428998286081", + "3996143098194824093843968965659132847407504849137530967999278126450907191255", + "6307499143062236655628471047117923013144505466650278288900215043429516667827", + "13925586587992763438435497293527949989397459231522640308463502932206077961500", + "13087452945182453051277972085750711303320428980713041081211383304385877763691", + "18093207370226721487749634577199906992299418255969574006908138736154443387826", + "14272048411789712231464337625432600879032064592923947963806042930999866142049", + "5112973508688401012277318169584325529641020499005098236869086381676918059371", + "15127813438826152618698293752642344087924814601034550461410899877754994198299", + "6969695649962440087033894050377257471647257275605050327543911409064993144574", + "6755033946723836072389613036793975461823169166252828563832361834388266522342", + "6630309846049050707531890910192358661458908514573775713066783149977348075866", + "19770918646745678079472470109308561511682083548143260940369403377403283034557", + "21433670531093667302664653834628387344084043216006808509497422477800438816334", + "20400712430451351419962326556923150417233487338363092310732550512855900177990", + "9914702749825526925254257687000920413318621895298404563035360292451257098124", + "18640006733688503648022700222053193091432820833842731658823766601630552120045", + "15001143407594668782401370718438100322710708950874705625981291998887962398836", + "6239670443845313966789296359213657096108058977356600364820415786936148776478", + "6433425223024186602967246363501689243341949458306873394092764835636608100956", + "4675918067976678709774913862522295500806856576135412363070396552169178386308", + "1601751749870408670839362316137379218159388455643443723425257645986433854434", + "13458041123361531838745263080315252882353509187267791623642428828275334206638", + "14471699013899259577738777309533960354293333303365529990782321200494073324178", + "20280430398960027135010448240285550337237252858424335652593355698129656795752", + "15301671314764260006262782923235826735016067425075232061024349010859210176138", + "8527420415370243636986575303113397012724035052720519440118106033441076019119", + "3475125085974928986675306749785419160190213748540322213576708845513558192234", + "9006980194944928107206620529900294492273459999862240882500316250867368209534", + "18341113199274904243447557429147408702772610985345439625451614852727142466679", + "641303854841744162878167226844505402377828908784963225549380412498130669293", + "12717068536550964634894133707393353175533641253913906785707227145457684209533", + "4743152264042462778433711542894756531279965736412696362852105777475691147133", + "12538011691198347241319721961633309700824171196141123297965193643240835576405", + "20609816220088637782952524444771775704801169004700347625707989982325840582078", + "5706720205331708334089980420369782946367580993809969730038974418250776704373", + "13670952207260779576827977242585356546757059123105172869720468020067882252702", + "14767430839071230390516499921530329576551075490453331584132978354378461913602", + "13855121278897572089789093806573448194721330823581341784260637700156610112500", + "9029464346382645337824607466955710618425900404147532427117006888589723750841", + "1522014999882036268928376276435422913665212355063956230967744832498304066925", + "8291362624704304963260873050727236420693946064959013721544766852407674345583", + "13589063181500587026605394799486087482543530017803239994475549538732438667645", + "13329949252386676079710645426263377053183751810973538969788279784055444085091", + "12386680681590613307772862225908657100001403625719832791304530216719729923003", + "7313059443635334631417408104276385788845981785748211548782869561833456731323", + "20041885136971069417608157269779303040508951822068411540615824115716811017904", + "6497376869042504752008054895287149835408180730990917088183191265348284326357", + "11125724561218240201117500197186012921479012541837342663628667709300377769139", + "19153359808996709649541642396803885814514927865816858283729145733607372609049", + "20152572433538554395328147373104228881714506304708579989936661727209606561409", + "20016439558639474267285355593250644884337484988031314831332037478976434317958", + "7527238505813249238885718653381005755459117986453125478349742303052560976989", + "18157976047185465548629298047288220777638494790360987385974875362640599836871", + "12215475598068312355286440209098941716275384959997573662132780937926913300161", + "19661498481048830903134453807971238540013636804700867840177613880955309016673", + "16552339415724980921932277403793066165528259484794112971750640492538556458696", + "18694143444126088750994041983752049518157493949000498621268553485531912882317", + "3713967579039675465490867111467075244841382142654006118952820645091137343179", + "18003791572957152311824638498878311682370695010264579557670846595111585019742", + "12786965212577099174577804313633357821910340940882518052929431837084369088642", + "19196303109560878004701565248872597636081713248916827960361832879728708100252", + "13503920737086542355495290621258674491187655992864087131383678759555676293479", + "11236070378728091455898747203990505205014449224981497050563648348504784872625", + "9626632821060958086537183012642203130268418644318548991569123722985663518084", + "13034637881184844942276438495251249937482750510182566475651230823087848537016", + "1332579251268852603948763724532278782861309157968811258504214239247723497919", + "4440487054089857105584789445239911365602192945489433191329897791642671641022", + "6061652828396333618822938589797622370923339089332057787863230635686741988906", + "20443625886285957158375952392918402164453450517610142032850159380049279019890", + "4971727988681610479349722318571442785499756930724120997617079739694258077982", + "18834174333023112536455562602096071615163100988222060601738058925300216483554", + "1418358707619290124642186511810240322979679442148132663647788839527968980321", + "9146563693745266063334532013679253366507504975079644933043489415067653903203", + "10198203189192787713857436708411760529883983417357336104059303759507359385362", + "13457105298809247477659188594503481428440666731107167805254859937557834208828", + "17541364687047908905500996158296679129819772711555398152345796866338919060112", + "20692063470613585148684100931411467587781915529223705941251811918651910411051", + "255059205539662129885859108675295842232143503986252525395037544865412535350", + "12717171726844166947240066436059787676718930218202178144871226850052045252628", + "11844159733516000729567466032644347431569093736490287481873540624836993897027", + "11002794037358979127166193862990494036532493412598623614028528989937231393875", + "74143635505641635571137692594773070124419429312664778822553090280375104361", + "7056329304173643667817989532223449288690091032171349984912618184745639839146", + "18641921263165235968711057500989010664480104039807002554029677125998810936539", + "19559632820551845243777081617723524038257569278710484296878486836843940297771", + "20335178684657665532201643783226288715290471620876336715869115375508863124376", + "1400810679146237783184309652518065868576549410755675655514372408922313217181", + "4901107318137271229016627403293875571324832946427895930672961592355892796524", + "5432919409806696890084817096600220639163358481346859637851564223110298016918", + "7553857716186244958951458648858814979749280269320630962042771757992327513590", + "10889701053782808013047326782283414392744894366555627960860238324337962809504", + "10225055313049541990073049693496605844127702734102449597528239569584063842673", + "3178201789995579607967020188792297279206559603024986232709441132816492873170", + "6847656471793158091195364320441056206778429470543211654532315995128613054310", + "12528583474708786099486823217911492959125739967840882229222854187982855300696", + "18467266119817551899238434875437425808022051394845724810256998883759435014686", + "17038264911128891077018547486438046001433165723713308233430847345626729200927", + "17955218916742955831569749120296633004126823963586826720707041154002710061824", + "7211241300282304354221867200910811631523632613327223837945134059614958044309", + "3903314564827641199204912578257298318982395101598969445634590319788688787892", + "5807166989004370646148664081075213577472290526426314941919217927313093625705", + "7286215868775919967580841436085988310805057373157718787975640918960675793864", + "1250176112708058341117534856693109443612022505613546634776485361294747168622", + "16240306074762036443870399577875028338427695244117010393369806321281281115890", + "15136410716022250493730454911117704953380153308032786787731835907574927894490", + "12835091061815802274496704101037812141879397401714459083078081237987781157602", + "9740091907879287748866588073726349058132835900960520906530105112062664428632", + "15914692134543071914048151066306389736053927516189539093172532429297748396169", + "981367411385768085119476192577825808403597700860420619441932576179905250934", + "8146729957224132449602418119478334118295844780357577983658167000941734233326", + "590349410629703152398401018217258970865749864711821880028353966569788201764", + "1825395473256187220041087977011184605194426952605089376835233620262931194993", + "7123515738169356759148725471278134181610254795201025187792320024480691382570", + "17726785796913877143757425784904469412799726913909454284181133920507167220504", + "11651205611193223879329620496932918885123654427491270580240367578782038527170", + "7363492115511936228845261039013774575631247230570693094937015313770023603570", + "12933273009260754826631125313953911983930122727552157672205237418903525642215", + "4587914992471995247205971137457571771256946895956563575187244174461900917940", + "10792746122808647351065474080470836119851772052088452562229191086431179404978", + "20854485826587742461285159604615271614646978291370447131740817759213177014870", + "7837325030066353684135714390699866228186086140255598480265046489057880448954", + "6126605304840233971990826423459339578353618021565536707601222973624714560789", + "9552503931384803960142614388346661043921110897228435044165354206374121160883", + "21548322368276531554717543098918449676251611754505041151177909568762524391378", + "18180071753020516412865055548290459200005267908156340472690024449103604081545", + "19794881883035632088168728687250887611858393833912902865301935760311201163703", + "12697056234954492550471667283339416001528754790033934829512770385731960713685", + "18997188462081688420104936253788181208943789361069483606382840563484861418437", + "13583525090048988441168973253797491422796978202781056749054729098883924623495", + "19314654753949754551488116156913608959581776089474885903850748881648064831000", + "15181859580303507964634708303997342165949140724201743603210372837522352052588", + "1716462370658876925282526879979978257325560447381441363811430134855582914078", + "8712634103130722740728459133380535370865755065924597276965974996780521350960", + "9765286726648838325814219419835139491993130621932376855733750920135287306908", + "19656947946994771960907781244127562602401277628142459052853670194718812445557", + "19486837510931007596539998551154014974137134735704825357816939421451262185625", + "21628438018144169767837932394788626973405404712219671990762156320628259145217", + "3756950750264795710121609861402232041406397600142239597456522141772347776528", + "17539873562638205978340064500452614593864489286304276949397837992418594034919", + "31501567535731664761828271772320938243617735864979347171792246363428922702", + "20352331732176385362929127748957145775981304217223745596870640808444167485233", + "11226695452056490225554147366326688917799653401898260586559601210633144658136", + "11279669284613566548129429232553360604072423151872075897074160621145515173607", + "18292000567925132069216498991188575797602397842406377969706321176845302551280", + "17394537715319668724138949078062966852069669620345676386188677668125466782838", + "1758531732991926253426465239898570102024009352558916489602014827893071642025", + "10665213752467842898072683993424618781459586563812993734884914172018996230127", + "8324794334508227576619756931916312729743681044831168054976090310260143855664", + "6775442988166121274859488523805155937302608291295720635827623408019663108145", + "18295585939863028658565671525942083548149809167848494003118620027469243247255", + "12098041580596728911973293152187391806099106269046962124832995425376587592750", + "4179756701368801644640767039230959196272720402682759516084041748778250510583", + "16793512481702225401381949294778305375341554886405623120613660547710034551886", + "4048682207452656355407948755890074205160404299490847582459375283758637453365", + "1909268909139397440485048370655601811806256012192941212480683118923705439669", + "3957160465110711873678443805553768764193925226448354732024034759529771356073", + "19248605214573594176390251265821595013690814611654024253657271349217548577373", + "13868854211319335141393401333112190211632180475309484073606978199113030773403", + "18603198177495186944736198329095228547417364118472010561530819889899131849965", + "14961608796326843943228062129500114917120869339277918579779470648489101516583", + "1868951368661473960034964936590962790214375788389753455318330338723533775732", + "10905586386180037341368454391941851073795645267274288454955833628477189437397", + "14954127679800529819463733123869358237687206843820983026443645382142721727317", + "100097327222806530673180018653982439008319453432490738486899052539918122124", + "12805546034879808469495394895280673159114129337420886322224562831326122327785", + "14806980879080756440413940765209361196073245400503251970584575890653655885707", + "11244087337738788153622356037614703414595488262581651455100475566242231927775", + "18570222240753995928776550172488765352849447095675359684346868515783858763055", + "8341186767195379582987673341587963265518616797937499770780867790468554256976", + "16417432689254526830515766295056137071443518160358174458760640472715479776587", + "16841713253514960386433216886097940412703740575768017997853214356595383383598", + "14194887769957144867963380965988802472719946120213216012427494993012946567028", + "11529543378722012166333054004374317420377342192547947476057235546295927556027", + "6228288822288755015903485852969606389475962987032899088796179982251184963398", + "7936701363023944576389877378554772776727492414245244625252565794409904100031", + "15001498940712434912521276724772034272928752268882108797824422534795904619598", + "8473716056033059546731002019964120133827043541589774095605930332153697155158", + "13622250811616358253729853546023741257702106070523773564774458974873057917325", + "365025468139420802765331768400644334625920629446545823780682161731304140845", + "15188301566744221387195339021364877229016833910188161414607699674936046087814", + "15485783106434451702559510093123052549288846753530302451778527951586674987922", + "16738591797575541545215307991580378005049794467812864864257667302719418546088", + "20925739023580534811148616300343740428924012857265956692833219002712597286911", + "14889383841375932440118953385212403650477118619024418302909728838111026909639", + "16907126208352189929083708812165010150781216847422378013750129578871272492354", + "14806337329187045580963626107721698864963698880842972656340333617747451702084", + "18421538430347165869364634373319132341572867349320823968976066555816366184444", + "2434665826886017086044423893890240566257398976888986717506547657107156268130", + "4107084864210868857776853415457685723648054900688044144581857773829680966286", + "9975696437638488842544898875508262421180359453753428380941136617917797565651", + "4271657746853939228219168189957401961257189278944783942462379137544482211389", + "6197095881644784733476508213210037292053663537272178396466755799876037787454", + "17213367299854554180584426411972899951945067477706586927352113350447914118348", + "459213126097844409580498104618008133837044548373643874721876534610892189686", + "11543212578280368772068695265607222577889093375221336525779600349460584250291", + "6403298899310300874478654413460079479003477532791552465502800475489780616607", + "9679879153779101961935393514698788441597495345714510688032433497970373876200", + "13406313792037016685976311954559553063553089970578743589839464948184425020319", + "18447061183141116334003130470774130153225311547501637266696790170481921881877", + "8380490825353548292894683756629385493171585978110314915970595197861650821468", + "17748778220767931278266669246252235981422317813726374848817938354712043425250", + "6989443852838917282085440504075239616539844445576232297589557657622451430824", + "7831657729066776489302369169030303662771324770284845700052063314128105065459", + "3925294941359174220766529276974428294409483857727579665373968233976340302182", + "11937917292063962829330707841648838486196775641195800632090436249316243167473", + "5653419633240940595644007985875227652581749358539968324306529520430709397246", + "4710671638294627998538116427765044043262338916893456876128991289569569324348", + "71394869385825610742630406624465024289788832054180269745498390791493509963", + "14738615985593994376179875742690333041302941042023229128451420345640432224466", + "19352351801479363044593690690009745314348194116715421753626607893015433391861", + "7828134540699329284695760683671979636145431779072416908240193138816504514982", + "21767328407325385326066972839469738803639297445302836419167176746643393964398", + "4363185783182535592354777041764733165968243966270266866378133278599205816107", + "12861900092547198665663242905083918119082751367916515401033164113207531942792", + "26199715195773805806030726371679979491296157888969354080702618784507347327", + "11422842882875515086151723214834881230789142279246505109137919711737652087050", + "19051762302341786952522177542948673906514656868374502102797859778503069987555", + "15304419135175746829806105353267450572438302765436283477928256874309497427510", + "15459263896362250252603436487314027681146892842462223021544635764438917687246", + "8022722450368098005612162808615173120915189731064159685566153653273958491689", + "9933514811147109956084664025371824141534792260321581906034445537159964992688", + "13658822996647839872436185744737341749492205026854185105317636224609004876375", + "14379624233209822191260559290771588358188218543089774948098229868966603288924", + "14958992313261503520770373701642009455846990471215730792533739030356906680013", + "3008264124109355458910031641850139041159853342729069847393839537381972950880", + "19920038730043462715603813734444323019432755215636179923851277692610637123174", + "17230906594400326225968287949357928337332875589057677202822764136401667097576", + "3295851897757873055813703602696670024935942454512523388333117755329662151138", + "18768678356443319065659286382395610390856779982946155969362775884999359596659", + "12460586127165955570640740026112067337902936125125513451212701690418329829958", + "11058259665604247832957658651506896706861484197660414347806317812760432481190", + "4480738127714472007170345890289828985073762836671874831610164101698497828459", + "20356672216023774813369271811683185179666462848912908217024611254147608738543", + "14909200607508800723484308159953932832836734953149954532962622450689109093081", + "9061041843468229886389838095513813426084716371809563990186852970500432288083", + "13877210447577198749973239837374936528445414390167357430100753821987322195433", + "2839635391670662085450211408709683689698694308308101424896263880864913513894", + "446790459594546823193048947973948688374393610819268956164912891701746906791", + "15513394297485200475424313247005293190116598898090270540649465364671255026149", + "6430544300458423909335482367339522552006494240640365398487065900428380674587", + "167620735842959596421416387102879695919480795342009064535989276983300514975", + "7851629689216713462310956055612887013872895546158054432042649792702892211944", + "6972603886636382162328373670318167574093559853635999968924151410796861460213", + "19413944499150555955013296242054320685906330193520693124525832782341915610104", + "12263527706889748735493918123122221596983902069097405802497958581677957096038", + "12724987466750360537318099905062339029588038359925927031456282032606443592484", + "9708897601683631082524518294366690107466919479336384437075715215088985624241", + "5574496517333770677326807465934080003942242224011251056548046091202080747952", + "4426233947499799157064891373345188337862104712594055992042177154581455374210", + "3323616387049914826284182817146044206680302487334156840960948301224727078983", + "13004254604656434940963009501076991894817629784556574456240824509876544606284", + "7301157702835275823143922645275544869236677259284614641634323459896004519527", + "18085948818621221598774302236513376768745135801526414290158313790799455691975", + "15187850571325688566534378765892972581891242830408971491252049503437712864152", + "1246313402425855943486273052355580345666640430821848725745627960892819218740", + "8647394846748193532697036206738902082890058434940110730560542269988727795912", + "10064655809228901737773717896104022245084200167358447082271876992404069509689", + "11849936299337845059630533008021049942767799074160683892846518986534992566533", + "17222503330227123082665055850287821041983218187459530901865330120583332963245", + "13182290359799776528759669559625168410983932493170206108456280901111876076502", + "16922308846046171347608544335458702314243928203269931669473214292617484140503", + "7558805163644194610710740916145749972491144028571940176012011742150030513578", + "19569775351485108268007779638227959360722563519500120339858146797114197471682", + "11059423706111256170971735544260170713544645580527143674901672982061741530101", + "14932750633257527497920398771292456029356930315768552640257901217840963910603", + "3437896142924792262479946438539259423504563537021031548773393821349707014872", + "14782805101152549038431586973278748014002447400847124060642259960939628565044", + "19682001999640336579725059393603666122104885151774112167088565361216155864092", + "4233558995266469322360257198354042357488781625226686110373130567271844168385", + "4047511689246902987779521780711143086427724115291088073014684434358317932158", + "13856430748589477860039479213633534029749590309123560510004500567115861827367", + "5679747763871928801801426015291106236767446876847749477902392536633742028903", + "2480123750122829773963967496725275864472046134363280592972001159541781284867", + "3193376214066985989527250356837102042223952356102254301113166141443841024511", + "8062756153672663466999843325461563174537500405375937887043564985230465866772", + "20934273497132536703003386629581299436206618333301538059074736379428757199398", + "3964142000339408965627671968349646710437601305543271274636785761452376263382", + "17659047756049778260030257809618419752271933594271518827815853540916989127582", + "3930343349615664221386794926953117177573248229077177174315534154624751347076", + "2895341264817391965072938270159088942930942041694844772295944189135688863196", + "20818043348799096204572140288297522915577162783223193328931006646915252532953", + "2323581855503968054101786162741712103392782241966098143187644164748998373979", + "21506950445835751487883729097676565482258136522526260378635240810473639421131", + "15652663761688777314836638604018575636259220826467294056546736904699739444415", + "7055942973426557227578535789272026034619140718369524980726263182702440802766", + "4949575918974841751734514119081067078445927926837555007268000255590820633720", + "1398399621038450203083960309132739969264364773648996881599178794373813964423", + "15732506799197618296258053128654220838812930643001991164136863850944185860296", + "7794835596508268056707656540537636605902268233816181487738953353424162592877", + "19334780957038992630028385332334147629399375592719839985802296503565556738324", + "21635034093671076768880953665214862813027411650977124864184274112296716574632", + "10688534301651495143056852052149424865722511689194196231759927113549978532269", + "976280778878149721615236526300424224199708100054560296745736876703945737879", + "1661236337347680899528966953581439111854379099437324147973193626090712244045", + "376684649999303144466214973033408609742031466036082108742020038605665915381", + "4455377013377822379614998251281676782235647079572422444530523356359416320717", + "19050227778954804843017265242388770579299436899390433258601701002663268793950", + "20176738847900667540339144241305449384029623393968373234965844794399275414945", + "6563336600400724498009526486444642892816244088883489205001024055715188696046", + "1774742053125969896553337470644410955775448576794083561882186570750312466523", + "4720344938456070076793371724100707050168913253766155392188169516296487837219", + "12386484928350266729292976320659720907387619673269670145583838638028478942370", + "16511156064543308562970376025636430728255148453048880933972883964686852788569", + "20161060548546289109867983393573712303980348945862267966490524939511877278515", + "15745678416495620522776570951840736496696200297848371457414809276602866061937", + "4371269787269411959190919691547618186773560770535442840395153562222650962400", + "9183286542027774648383851710226269089477011745654659545146528858483988667073", + "6831030131135399650979313302695351819580359371445002328037898607795789751628", + "13808316624446561665670975675845412570522644424965154154503596711464812129094", + "16573850887526809906244420164574513771138034770054149931125695105829690192839", + "6536774143914522904090235450413350153861560318210355846585220439397199352760", + "7433401028165144185448409996814186253264882020047922839648723974949988749262", + "12023679188187515805015064799648457297092902074185758947196154639017034370941", + "11526239531876823273618354625960431640864995247037473959040869772406787371745", + "9965606134640358953439667616539308986324141554480312185321082780157059913160", + "3745993460581557715489390110730324720335274000085036229679909083358071037494", + "9383224303353982312273937680493563948228572509816601479265308820357851069284", + "1487827268845002359294235769079867448539095616058866371843794273898817464325", + "12752425410737476015270360130920506733622111261208379938979967899453342006543", + "8485480321269023262044616550897625372838041041299338618725089063696829309383", + "12576415359894007789769349543504370901641305511107563487651205088993508989632", + "21051289172896982928450490300579871201387152257916628019163266526462496896736", + "15867382033370376377605746630898769390447074387330276410404020451043720395429", + "19175636645506837972748760370147539503389959528500852183571848133179892449157", + "18562815464677471696203191443459870050160752363485379734113024945295929018010", + "10670914301723557472895334516073215554660627986142935452136687134270343499283", + "15711527071984806338686904487656233279759984782956970430068615997429454331060", + "8708497335606838864375996732302168404094513852034309829472484268061328157580", + "4188819471869628756655847772489988754984005896205326092058226221066145721122", + "6512127543122355053584585699891817324589817660150620823656474181624359941201", + "485283184789889690494624068272585257719678055879291431306025716514618916201", + "12469998319178098719636931437231251055837496888416524814745527432538031712785", + "1296020106178851400649855820617531046666668928599018437234450345456859444266", + "10011446468770184926016968173857732818096269728811257796162165899021103326404", + "21669153150071610384945525175896288044525929994331131233225247540220566590451", + "20224127309865911841696813826362911446819981503618978937382553360215894705096", + "11995994988481623386912855165099904216709109375061333173068533008232786187218", + "9410915142880940758336239494539529470909530692135279575732428340046078892946", + "540343796196110456420764600417592095932817965626261174892651237721778500283", + "13684306082150121243464309606855998092353075421168995445316517423697818489078", + "20162825761581699251737493230139692067729837621407892263730870777826880180708", + "5352686262532923758786328475456237662420216644156835779261605606151509572690", + "14736042040902670309572407869335525984156459843045648740121852458089457172710", + "15139196852186360494919007897486360521240977399849539996614669434222882428974", + "15545525411887550591483485994597179301368938856348279539852576272926020931130", + "11742635236612776978185392287408975566560283293946133146948657681925156152641", + "15933911044011218543142885674144914931062698638612248403425430247045038884929", + "11224528638969252327238392070525091272297579382723799722332333037066879094771", + "10619901685741876140226157641208788563882472537632692937793306998778377039845", + "735284898318336599638540292516453331642892791568249331436388578312138627149", + "12167882615164420400306427908818494367178873976878804005748498778042330871045", + "963476496798286039732969224933550470500612316061831761002103352026702296408", + "10589214229258115348033093903975219788029199245856576715398082842145004049938", + "21557152519616203688970824263287584036198859167308821872261472006356046725775", + "573869759526051245236872936984426432239686428333700586079723439581218874054", + "9487380087795664760223154980070289042545005448274508346993861027000032902237", + "15336219129184315874932697305233742095099896250996970622669888209373427981768", + "18404646237880969912286263998117686518695361809473583072241508209656556652987", + "11799938531163685220849832019747758716514224719376128562353011229084757783628", + "16344281677414470708413218213072185241641469290932113189992078158350700334823", + "13411067297619308137174262243170594092291209705703155674514006442224279302094", + "347096376483719141995221552155578565668868956459941108952060114777596679497", + "21711034316629345478518202673372708169627878437940325626238942704003376069559", + "18882050099612599842940192045274010067786778900079004241849918566254608017530", + "5764257238170917062706360501552002326820365975520365982521494644422134554879", + "15701229555594307124251301047127236083745198814231032790649389796695363681317", + "19380342180431843316217793484208165905833096992510614135124089140124740832734", + "5309407894979098314097225178362235237148427064335413702368413584568729847810", + "19819307748431076945425543888401840319829139172119377623119035071741874422686", + "20514761050842041997240240118048816505140382250318444479066954659053422537618", + "1344402086478653057873064952452423683387555289301580402150577440089615404706", + "16697255135945674012509919105064086681485448643989533271078867487484115301326", + "4621832835239804428359762201398927678345509964776233571546153039277467113103", + "3806403786653298341904285490244574263470519864399252904304315511448068846106", + "19829858960657679259830225387994355131769725454664502533894857362311545270016", + "14331370957097049770140547885384829422939088321033246508644491038102571773100", + "17602213870111183187455849162801944225604239824059691632957781727843794239813", + "13431951289066965496411295925849182623784324208488725900281691974104907065599", + "16717461480909222952301202387848079129664321734166452456973177865963036285419", + "5335304795187086185403562863303763030150108130150704291401586263249269328606", + "12084224077193103457678419187386990486069302392148214780322095375978824542075", + "16562074867094659170642419054272949287052074902139533680078784684856883275478", + "5593185214695460463728593774938986800789091570195563391333689785627694694435", + "20794081973744988874029976307272029218401016626938878812907621515041534590160", + "15290613354786940528909115982493729676786892068577902693057827852601851859953", + "12549551509495378488198674258192692896591847320322918116198744545244343099813", + "16172760618549958023639071988927160267123900859890153325976796899698706870529", + "21355970775695686114097146789571025790146162225961276367865670606869296144640", + "6758388446832091660422761046124378827605916325205674642673860154638138778023", + "14106404069959859167123546389809591376301394503370836987998148793984105405167", + "9246221316655556670219438263600949328330249756179668376432941216174575243170", + "17439153852461693097676073072037755599321185173017314957734642307684740219597", + "8081777382362242695298005068719699951526876053236775057946263871745968444089", + "5955062815471620144467538738332476050500085920379711183343040972831100802372", + "7135549743533376618652729621156592228493633259664287822661943632128030927327", + "6448789306286809743113676641229641365524354356867853498363388856986312333060", + "3836133206442752775823759632522089911744536837043185745585363415112795166600", + "8306967267195429229901513132397768469414281874455321140328179401973148164445", + "14279081291461540920232629360665447603436685365844514491245275283801357455033", + "18801535274021869273717064373621101183756544015062966551566128881569524508060", + "1290309525505162730469471418279301124342110934722264762505649909318202025429", + "3684482296351796270499952631629720483486993312250657634474107248508746062899", + "13174517099719760880623841117042663715711430443825712743356469456554740491052", + "12367323038661460523248093127080567074978912019587769246568174104058982359108", + "5328098381011440170293483375144450438184897394389341853376147620924621832373", + "12027556664769952184858265393953265399418219730267455116938121684665358512140", + "5729732888348277625941886883620484887709258694699060380279441089816970092500", + "4021085134780294168590418396176917282024838594185209785357282466818023080991", + "17212964584402772629081731052343755101208077509065176631705574779383125014657", + "20917432005010641713392920477403385909781374146458276502002720501978360592707", + "15295563898035514774486916635776497660635050791955975586488426545424778559212", + "1359821178683766445655942489230783986383452848368510940932965034076334706033", + "8110406013405532283463947127567634123149268608919736912581466809002624991926", + "827196241038734359771319428856617658913932049877272548875698342240161957065", + "3791462512735380195191512407306530846222290938359064542531485381855595890578", + "11406017533405938491480863229977030648871769070115386849200353035840595881771", + "520670302347263967262738466276891414776779776742458165836379179429124641366", + "2448606803226047006259184225386977543879941109635656602752172963011710994490", + "7191227529515483786577983572625758056228282372415415309332838080598142611412", + "3307446417204024568218448979093609311776086880103323372856728466529459088211", + "3813554883592508538818724202670630447381185344079357801330890767369937578758", + "5250709431012867357277423372041580897716232143432581463056922412233378167706", + "1091692079685509315891403580607856616782786079784896146193867325719999481531", + "4469622327160639333299363654867070539113198566013450831894562184457403911597", + "17323310137022557438196735285237348172608407585356043526322906169573029382454", + "16687301461250715912904355201855034952586622338354151885963080312175524252322", + "4918535113766539148632870060160447192025270821762409737556656897638575667317", + "11072593994523457387389747805751581908149754361901108358077132406445033545004", + "3195233728052289389067022539306995714028010328554486938319749354377057516714", + "20817686894891259203267112342163112292566908705837357461445233267117792429649", + "17690940439879640319699865226478735545090209049084370958835445823603146902951", + "21690628807014351230241926358993998569388931315165663025074689739408667261702", + "20459556268654252373755265495950372631044421643418312123931432417755203158692", + "2615012003869030558909061586486157199297754407476408867239954327608829725401", + "10416424720480840919038397124331362407722217483977166150169359209212504823727", + "21194909967788113390574471899389319930586662664636393856442773352403088570772", + "21522774899908415003330605964114671001565200771087582504208866633533540017562", + "5506611081865651016465481008237511873335242056816344474949867549242155650988", + "9546865422597752417260521115459137295056544614907437375816616393702205361356", + "21830596616035544007289279751950703469521180388685936491933709578982542912017", + "749611610802485176586202226352686769865522163389912514564999424571463284657", + "6755555316754154503813947597597343604907450943684878399336695170747270471709", + "12170672505455181293754012516713072944675179516356563083524964881029055628483", + "6707337549725648754250613641063009246814657413626673293152655855698987287596", + "10796498904992920132771232449170737749742455370471689413932089087977711623836", + "8377594495858973573128154823903039902710916666748285670520960751078778241055", + "3984683583649079079845275538974168947709880082512288559917193166572776389589", + "3342097912826432514400613460588054784328673911636756618761620185294261653370", + "16454683529696451672086473280821594992928717172228276559555553609303793416523", + "18807899744562842658522302889072060930222870278511178161869216361165074271071", + "15733467702619867688911165419464811721151813607792971238595067966297846247623", + "18142314765654743253383559257284245535624730148238939832089390410939642651454", + "11195224772751371350570884976842181779831961285896963666553011681145523971069", + "439244367736881667144534860370666344402105076566699968755250684892603338082", + "10704735720989025726743300184412677969903347430627186939154207946469729017738", + "18578876165452990292495920166540986834727225658030437088905164910953626259691", + "8557967377050572410782106944750290348752651270453684627569664713996263383548", + "8761045577121588487744533431678957572685193486431511842082771483510204390408", + "21449005117895748002614414579158497856671036353929407470614229969798846654314", + "8742763699897193075490808534988551410985191733407596422193467672281063172120", + "21640560187468057824325948244870722744182743184907610616373323268039154063822", + "20504827051323136179653341596769205909304116951166709098994304340550034816775", + "16172525553351370894318080522373536864524825253487998830365706514360525527841", + "3591685457927038924948528662301423116917211093679974227116322357454330018554", + "6023337410706762262272160833625601604408129024083079173527188974815598816732", + "18314718271070348604361238020618683829686900661152269809595508349472847942062", + "7836153641990744469550400020392942083885339413553995305082889896550143678297", + "17661458352757891434962464787581216134089171454741034624471153568868635718017", + "4122036760441574620764812242429576792598439550172741100479310420694208068906", + "20862235339890611718235387895311549206105487705288574136561783190763219344570", + "2850551701806552645458448511649317843914427882049290827414738885733429831896", + "11247005361958789340696615426808536553465247486357927152826112838641725799209", + "595377141566787974548956333113514797724094819448399289547808025689225222483", + "11650535249856393960142913145219442364845834591244773725262296147168383099202", + "20317269079309969728497469029861008772266846936951222269809633626856816105008", + "18373353028723033673212698815557995243796350917201491012576826029280066160567", + "7414281571887634589036890397094381209738607746338326059409587963803891852201", + "18038763738650749116346354096722212724532913209902202281075842225944189538168", + "1770786615956839194004148114013826935077292967352199469327501774219607260953", + "7488031997643483244135856650059587771856081877857609929648968340004445042854", + "8828420155416920637244885570572746512651260806059857940333819295380848667606", + "10713758486859145329962611447140189455100648474003892294431344231535672892839", + "19425964738010714594065721309197326935753724219070408633462182732050226443166", + "2074939531697630696938698501714595125993887544292881866150005180791766942998", + "12047170609336587447894029543834769407220306562179617939497216596360229783300", + "1264730939976742978058162525644425945405879597356760675420608373469615068174", + "16770820387947475117163540876143499701759349964098540413422664664028819497801", + "16280068293625117002805552395286584356701604835446863786447466051605766622630", + "5829399622833188992426267462479081456743106260845929463922917463933701539334", + "10637584899633814574790036401685386209425675304700886428980902763634911791837", + "9291782595793252587833322197337655894886134207930378095811922971339860144588", + "5951026341599354622946777675133435736866407426534732764305509667517377130324", + "4403580174404926097288523091529640188805282235669930524399146786861723458014", + "5003639966922553172252470768563093188889050306376650665702227782913343083764", + "6558197410800499965347594156873334155413890035105551024236266846011615264982", + "2475377503098365612622742300317283313185534384082772531168342518799898155594", + "13480086440294256226712366993542304396509909765502683907895525126405850150623", + "6847184584651376043889746562956674365958636593745423518578010394007380039425", + "834503921046130599339994952233856873876410737494970024102211605603428829597", + "3623466614124962325494185109684730979089103423795285554788413052641061197022", + ], + vec![ + "9296474750911444465025945061626611450573261102544117494435956219223872045013", + "5146180402642819236271333772846779841277266622562021651959982317806096554632", + "20454733758478774733902661862471466785458678414673417084919753466149262477949", + "16939720779646471039361700224425387363963401457766730187304254365590145685643", + "16508379613071589632225608856524646606497744838305343752126125339777944056893", + "20867355466989514329400908378893992018169546886162092188544040547127653338003", + "9562028189723224670918372808954372258890790835333923497839294908971703629044", + "9227509288281899178360094205033853851646604997619773877005168383222677033411", + "8509781355418257783679010460137433931646066572707591432255985024195888527301", + "16352397893201944000231474199631274335610857147594743875684031732171609392779", + "7168331327842901194795012403324475179802342440545189729818235304752196473490", + "6805437858781519809721294519096505646809998863249026857458359222740400480093", + "23666102285914319661214067066621928648019777016465806627627356483470384578", + "4937167589168346771422573562192746031838523475116047879790134471501476035621", + "1346691375815699628539641638232212515770545677194558930872887124097351576509", + "40983635009581820916284370715993074471281691288657861297464948812277259151", + "9682480346612414028382424944197316598030638601235308149341398931369789543950", + "20156146228109066705562831124708208402478981918458654302206305397911512087980", + "20244794515708180670563002449620617650597057411850557162891558214158122324521", + "5973431503164625202944413714022034366590666955289560873103517106326649624937", + "19003031440781649161524015602474761488168160816940423515211864494962159501828", + "20712605099727052887180337155696823289955789009114447705180663042338119444677", + "15751263674652110912724722781530878809312788917232502379399572977178318108528", + "9759440007076373606849230657528653877996372541705166313887529308971932175875", + "15753864581145946158736383142711883662973744817981456049085179548487345401534", + "18516736976984654236711834595195133682620758902795528251669085852778367276210", + "12505762035583653640932505661921159087471940695093437071026827168265805916842", + "17973475260212324625956540069233265735155253700466680406635575121618318752819", + "16893238145591341977099515648275086133943062918579997543738633084157810726656", + "9669071880586595648023284909288486812190820353718106940661900367937844527179", + "9349088318894477564190440283477178116641834248883927901328258581942583077569", + "12186865849936919100247208155624431297953689379595363409626510899600302936538", + "14191429059221279746000430832297028082244506830783856999765158986408198377932", + "180235525898780969912044806879591313131227292826139844018949863600387171682", + "10889092107911649889040150092427122550710450427668385404292705974881857473203", + "20693842198933957663269842304864226322478103663024796211607837700737760050747", + "19741631810531886646761927581354810838182866370317964412790883870153217103250", + "20514964832794580053019916730141871673725635044918093569735954402314586611172", + "1563081830952767432323468775999289294801679977848773475307184358241727189864", + "6541733180459084604825913391805383670644609126706479690307729028795470985900", + "14338950551652503213249338133071776308134322059752479422120853035176120166434", + "9445962403225070324948180958168278025259517118659455884585920056598191781467", + "17673503748974627902616087621827013509215108587044719385816919777868083778907", + "6529067669277048252246949093210874205656745103861744893697681349157159395382", + "18670351688014831561846153455584274335550086431574922181801742319576494275746", + "16087198812208315804697003049026004024678636407919712012630915190666161641979", + "5409244980912243970485734262038475969739470891565229610070736907935180643717", + "68998709875023468479905615300134369445692715997447916337587330860748485243", + "20797106660136416987264043187693969873379408007710762405000375236313842719688", + "5277839105793767851333321356647781091931078040812937205152067116052352239761", + "19813316014421099365974197500618281012606383357022846408656988057396247542705", + "14651854807736789020692970167659260203349703129364255901280500438993569147591", + "1953950121645869390891017290629473806985703435547819703015178261443018199466", + "16966986407661837074189113052135209905137771226341312735335483526986039302676", + "12196911666087491240035929989808617508758866763772938948559650992149525851273", + "6748262588722620715981149705241221174138004527637005241186672382866432164292", + "12351853389756326378242661122644604232955500481743248231924728979085806709527", + "2565243335980149108987604508873860765952308183381198225102084152967125103373", + "20241307878322245036836862464964995816180104231642798486998859049422772672116", + "6173912854438429518808824631217531662651777757438489861961683800252069642271", + "20373949892834246498138105881327221987245360841655169052122063615887220321147", + "13269719501466581754706990055797732063851220874637620573076979035861249715318", + "1578162066274870756439515512829351279665654675138074205960935197210851459902", + "15699466465478499203883101572116714566818501168342041974225130392829754730992", + "11504917391580472762574218883359632564098492996052059988389474392975779673931", + "2471534962918953214626354556158558932844702538697900698209059019370494276871", + "4461270721380837522838335428169036184773145514356202591842435276161887233701", + "17625308215993897091838888595388246859096597033404108848402595233466786865507", + "18815388073919190191562579690732647812407368396854637581401819367329270645091", + "18268550843300936597164339843045347137811481798420250960800057984676328312298", + "7226998309855583456700105258532413039112576746166962016437941756495852856370", + "8072980663193342911861442510877605529321700341477138509973424538392015970628", + "15269265747158306563417251524571764365372430081923021726955952481038826679058", + "19910037920320033458941731683391812599895690039363377502140416867160565960964", + "15486045363368901308969563040163325835584354680260407824152890548047474173382", + "16416166568440710996186902352749667794644450153896415669656858302452107263361", + "16719876352169334284151333613847626557780385036803676145001883066946186048592", + "21250242709366561537300491285945444276614074599553674495821079421382627519383", + "18996863118748910427791883574992010505069161233259708625680518449974859990560", + "17413984989888387572882804568722637770904232773299911698227583956354366272051", + "21597529235679499224130811468867862981087206894945208547265972184954721846149", + "17271950603399986802591192304636434671723720851969778588610595038101316019146", + "6798909530322911571363098835602980477334813891267210720952461019098407212259", + "15221754151466299785203667902143040752208351220550667002070154445706666401776", + "9969565572595685753856723561942014758757583006296150732797865871416014191612", + "213937728610177048236193694412457038661914578783162813372436128728852762482", + "15615646173777239619093550417838782753019556951599702936217300723011405507797", + "7718045694050546136660011535056217361530495049313202420611728185159583583910", + "6681562236645975908164269312121030538373320810986198141132181047805166475529", + "15406190007218505433124772738477886033924738791369244099805663445061334497251", + "12748925302440648174656014197639220978675653093966691038521528280766659388490", + "18792405480956246176786158899722364426273579869771820318487342148987615503627", + "21034833205180901867479986245412384531166379419487221053266758595893395608147", + "14993530305792055254503672111497655292586320227932876629369830030730566504041", + "9717186869050569007497148725820161576480205291215534893842038294586234260310", + "8846581556095542298416062056868302371695747263219967935160771313384250848220", + "5719380810982615524673837631402222949910527678512468701059039624789965209167", + "5973398204091658428913223841081738015584068771535532858878785900515770169284", + "390665721029373984714826019392824960041229454845430836402699997231854884676", + "16884099579377843869279635395084651641533249525144181522512029929860550566335", + "16716976662979247989662403288430439465273241484103184167374388250697744962395", + "1289338592939287230201589454150014946433268062090374613616251000166621302313", + "9317927644121595578754625372882467923006787730797841509740916536436754202554", + "9792346806272266839932159619375452292987210695578600955864989901266954094960", + "9296083977157792888817756474494818556822681079104304334773148774182988756476", + "10323608200914404656342379674401080164220099943588034731349775904877409752904", + "5665882510779783554356611639475035143817214951779938939820232711292028607543", + "312967658452369621468725894369965307895839161467077935727514890821777198625", + "20664282841391311519597819314930060269576136022608505709992590417893434920589", + "5323350315725158992622594066490821193659726275127554898433661812401256218478", + "16039351301369040724720782620886565139379163954982512295794534252600896786349", + "13820693026747418768344065399393852077420709727979822490883114289330249839237", + "13797176144228246839971555725332102191019905588045129521304130064480669479604", + "20861818286081952786814445160300163171950644955642439845329412070095760317081", + "6607366884537402504576829976217056754321721309944806800456553075345742381854", + "16676075047056109258708765855713030298752565264610707737757882684833916800885", + "2596872657894796835537858907147863451585796495210087845405329734139833282637", + "1129905770296794876904745792895109768657958745069004656310552795892948378584", + "3524768344101236852745306230236516385903870540974561871381448404874187920054", + "990706121628678949689364737717640674078492689689565457695471004362978678643", + "12808278123975447545284898374625872643451056414444411676912801184092083815084", + "6358161990036632946294642058872786712712883111056083194592676110308231876108", + "2793873016535929696420652383221830923887713214117784093571041985641508474546", + "17305928010614439499134847438771424617226147912488714899621343734884653872709", + "16284502064199759145361743401051151176267685561605421586130298595070638086967", + "16669378724978577155412758169912049188272082311653969294949527064226374258950", + "11861408925173541719238679231659983919027526756130925399544116580983984008351", + "20319862771317637561778652893808792643716731292949307597222212602977661607952", + "9965217723779805596104842963458526859730058546126612755023280528059812781678", + "2231229143040861060383699098560329742848948229177358362095200642518040448582", + "13286704465412914480712873676020914803973411257385854321890647189069199504571", + "10047635595882320884409798135378777636448641999630707194378323292740008960417", + "17087336000400458291414525340830964856800142468906625956701637877370905283650", + "13535594575282729223473509709844990145664370669908108705760692736046179136608", + "18619202345418036145538258744454149503028380077256559127312056952610301046510", + "17607804586369163727645967551963527279187781470886431384954240254070602283137", + "2633042556450301220705608140363996319867584411611510879359782399294296770849", + "21731768638998447189300033743436435314153248129574741734083353285598244080117", + "17469131737454037935821691482173830563221091395109883834897114256467877636669", + "7750519651616125729362402583651846123740462681349952291880562002377909054050", + "9447081032000164239615272903502339392629921187510175841323135270498903409976", + "21517411732531464267453023393592251500842566572268409532461770072252092958721", + "7876501141846244962862794902521772277174190799788868978106856756141641727971", + "112943796512817751182977688559740204117088697116147815915631198815244286561", + "21045605911504121416853713285261007893735116973950420994950288232723845218749", + "1753558144636632850096274509984476357404441186238175375676398948118672743832", + "15932527070583999576056453444162361416070384829011926782656187054064561647022", + "10670345925352776542283780467580832901477001176626927666485810080031890388634", + "7579612604695836258451031149594628860115515363868399672164640908835805063913", + "16678286807861813265032290142015890821080993558199809221760573633471452514219", + "15414078726286200869397977002633905619706559151321201886825291326757189172060", + "3948850363929702397926233515670425547474212215810847662989169477642088807390", + "16676527723244281555583733536047809454744760551541688199219131455695588765207", + "14087355065973222250918209290719451048172913300838805685888196087459492418503", + "8104074033209440421654249234822181338065658024406724406758136674393332631833", + "5187207728881418438225188748028865692950088102865770071392557429006545519499", + "10194509841193628413711183989804813746356025348148221750035788743075560349210", + "10011332996303549537072329954668024063758462809797094286213034989313201039795", + "16531650268844669358279021502930777186663937554425443224519161588787088219270", + "4198803426144178314296362723890875454161257236407666607766866723687553739691", + "4796265297638646381399181426481493643799772847333437062194249373663993441511", + "5547609802231005143723955844984958632609857658378828698100141356246667308579", + "9238958429509576319892727665041354560043563806656332888234436819654598656915", + "12517630133819614217261779870726755029108748195491643235440705119352058557308", + "10368014234449326131235994433740216752312468149182650354075154587397704894585", + "487465315883418182347663117280302929984519597673016955398438666336416216955", + "11185034244837678424376295011043645561088905841730131726788873681031338174455", + "7421225151165727634904994585688522807152162423870550118585658136113463984936", + "364806019968570676099566454292016788351689833960119434834370974516536828706", + "19587762041779243275917908822520467733740516572707471226884058208850887204874", + "17699580368555821903526305272862177569486615328343491839828472034082952960813", + "15739138573269259325517597876326746482937619260543240058947651429318524320399", + "17749815771695585990262895269645295514834869871993754550237121995617786628911", + "6001281843453332722738415535953496883454214144943230146026585272790933236244", + "12226248842279453247400486197034488006724059955941344491855600110495696624034", + "6471818050024319863197241387509891745688912297463370650154695351698691437030", + "17643944916136214612858153024594861226464689685432825152621939791565281052364", + "21110507073644152221308742247603861828087429176260377204494317652281698346306", + "5767681379026755049166284789191192306599058359643996830685583640018326835809", + "20268516002780303771030074402103352053711651542638490897887552979541377898692", + "12422024375924323315232423143442023050941283872688585715847426230444960244284", + "20694378016149655833849108581703458975143197158248183642690852771732423212333", + "3589864715711417186674798636588069952164879749159939425060725590030374075726", + "12536189236026623553502052145542282538682809966098904986394409008189339523570", + "2807680329090948893979159370041090163078264208853025987363628558807228757758", + "11134364423913486029994525311335222090667061028008118303028273833566959977738", + "17354950677485256631166408496835881692979370134492552889236570818914175381226", + "9148772489395197941519683034415731069796332771510468178434252005940900987586", + "11051248410353114396778661421385387052304377861539041695630551535039137025728", + "18136540266852326106685174083549032333515525540247442551401150755180487725743", + "3985310545130175559232554478662499499843818415947765870117105273186277940709", + "4596525943567680471287409788454878169502290977680316722474714256040545023213", + "21372340401058762695712005390221995267277180470546612925471076506446695116009", + "14075257312640062020412482491643121652025454294874360644194100265009640244905", + "16944503245314408069496787236107780704688926045771652862339257733069860638621", + "6654085263135548167634225627185378533095825854533316762555824971264586187651", + "14273015308464032668640083373269579252065688901233915918278293118708191497972", + "11764478817260526126925152191588101018315006712336605965222952598280968869260", + "18155813367404719470487615677791656111973118264369674303621800593085173024524", + "10850039649283761393985741586943649861203783935137056310000609328911733104269", + "14974871370837446170027589325430324426484756076474120128444445200816418916077", + "11027268992558661543645635853060250199670617750348905948352231500166030736735", + "7737561462151958609897925089803246494900406501278116203687665387696545068888", + "8536397585289585667363111427598616854125268561779225310999422087929379230845", + "20520653588911738970276129298340919743898256960050723343166349159335019506767", + "15573689478722985997422248150576705853901581704550704092699815959211941071233", + "14024637413191434115623282477558112970843649259330326803453616105375507215588", + "21353180477994165001466351039795610246389619571796579650797005446278678838787", + "10956420585038523642301267761059211443146357574323727078003385100699325814188", + "17634836995596798350536418601573649005517540866687768902898663209263471034753", + "9073831830223507407321206741538426673038288500939999264368323867070102600399", + "8100061233881358546546217363648928017848251671613674228091904606518338839516", + "14292884686436880002999028765738013651173249817687065086024117836878136072418", + "17843666610704256421074257091600372846189412242040711501888312601205476877610", + "7418496999733433074316279404200997784331208365418356982886646484553701153757", + "21292338796777812243292835631771215001169911232666037289555901315952974221584", + "21287440813442297789932443224883752220885368198719782335487617798315717278443", + "12316285862536720021079405901921917335504024672055930573734336005894401174699", + "3187192338760815459509556016254324490807541427929108750144803145054500486833", + "6482850190067117044880439000213252508905126459059393355671593783930481002065", + "8071947666795842917503687890154152458243669781913953337362618950188906690910", + "21450848111897538423936810899713904909897296879883982984541189476327391575319", + "10587193221429670115264727936502940314613396351335733485096445152378181351510", + "3385684482890050363671196710999686076700864071377274740326856556910758812375", + "15982506074704654515559890904595916307715088473006256644287900474899011899187", + "11051707205631537861946095917392475702553421314274237258592689190929368154151", + "9251268179161501251470629870144065342881530535654006637177099334228872748754", + "15384401424299601275458808814042231311961518546060835375991090955541715971658", + "15147611899621647169760029554894128666862161642594655274320073446753024250644", + "10615109639224170479859701634647953864795396211020891058926612120687182210257", + "14404478161927699835476596901422616937403414811611272602635749875651680921447", + "8066178271733489868262162784101440625233465103401108911752234818690246453789", + "20248276648316419325977215090702991722051292305169422329958075780094418699373", + "2029002457916171156303194520244672392499138634810303216935386602108547985314", + "21075731183134253416827200425853801562619807215475840771703429923133977728069", + "14328824218343127380036695796575765857116907766196272954596277912518546721093", + "19160580257616407097132379087560824295215411994049928358528355359904099586504", + "8132787339090119872084216324271073977022107784441826475259174173324397592845", + "13163912407679060956610455137901723175820157380216990622789851897632818074059", + "8592347632837509846508131492057017267103172209796052768156176559923311880083", + "7893378189384253642782385691967744125589477790209699483381677762080196748261", + "12191210041395050728454216483522186705806341251170673836903099334267999015601", + "2618630263994051322846776438484808085311498796047898764787894432257296575984", + "13304976328362334711292646669717074062694791647749224129724116074506061700957", + "624484185190294110172011007497433523891084548746061325634035014633317325140", + "15930533828823189720011923265223664488732840218774700941123034073028585645207", + "8694885217655511794794497743599831016837108207363203834593510913033381737488", + "17331704109224667609742217259848258607875799984699711250101367091564505664522", + "1143531962761362035993655799963451435028684354850849659611727546724143302608", + "5414384052493950126841753058585655621007207302558963578017856767058437816718", + "8343436020075567035126766344602793013516161248129384290738181959425998466132", + "4832334637017830367140676637457267590768883455902783371477346052893423021577", + "15960489327340978673041589758216206637417346222123069896632550487782527402630", + "4557633726568362237945959401789921266559833502098724828463753976542737136513", + "21292745345591415194683498267204047392092318996401128975083535124377610645118", + "9328286636803072400168705383927986130049573701928015781654594560657277940196", + "4380226305886799411237881027947999265683595948968607979382183326525427173817", + "9767464780410766464122903880954110687963200055038905098288212464855640106788", + "2341077008076389666838372736815646466482189988625828995627083487505977087946", + "20374148380136094638123722089889049817432074134077941767483383760377289749825", + "15787533442296590591714142770598935669897792586470392297628568666971956576563", + "7633568663576517885027652011215374332444473983000466204175445586239040766264", + "9580455424171239687884724329420941872151195359863514187490663550878551019919", + "19911295277809179559313541842789165346180998073715101124443882860854846201169", + "4696263765841909122685466774945153879000521574484303419094957852664345027123", + "6118063668634440692631158765198200090955204724365488531804099825202234531440", + "19475295610121478762637598544231725416556110773059271107863119783250115177307", + "20179584065699439977644558242108004135948300904465438207718184556293527131765", + "2676625519221951651755843575492662910087680586300055636532952929295968711540", + "13080027857034076796318609243033548218531614593023186061246772929628249264284", + "14263363493033688982783626695159053510832721506041125050282934376406154340703", + "2706925569052822828207630841519123914917406668054471879280093987418440726365", + "4296262245978655924075197707705805754837595446469608588294115404804858164512", + "18695068331082119185822574502658840301072706119995753204333012565779354028154", + "3337114200744822151016415017664070914837554273478224716426789950128003958134", + "6975962532267211837956893753783559781855104297873769130878094791004904433267", + "12231027575978562434100945363599501995871801149646690231633189894762835916126", + "3888650643207658707541674459822797638642380807202524073478543941467788883170", + "7676102615198465271172138409531332193754467889637067928466752449336805364120", + "9811623913757464238022803188199125269665959963552692281954397128646993550529", + "156972936087215783948417984577273879197745728180593700417440323304914519034", + "16588845615557743733989563376260327418375621586848888397570715079613783522187", + "12011819284668339582414804793555969540908484357893010282713633444914261107919", + "9658635959610321892309182692375199095125363721825304507540533137281497868500", + "12870566947850007704602286722292111587887779596594529150836676091488336717955", + "17017505534531720639069672825866463960388881234646040969078396172375695830933", + "12828756987874340466657656310127299330830014385503837959876241551509225608029", + "18359630725137047619220866091928132852289131876343072040435717392635728807670", + "17863140285101027979634946030001422306163257009614315954049905801373677640874", + "3360582341611046517265570944669154454984314353967466888538655993528259823694", + "17502670755424560054495200659249586316206934247721147538443383457515937914644", + "13927649797430421409818392324255621487749881718200898095569535249421816857631", + "7898469048097586242538236499785851298058905177690518852139435937277393454608", + "5393613856505501947177492775058221051864341377769606207857027894289988034452", + "3985540196927201732264077382523049411262602519666474163140081018404802939018", + "9933548682032172763206440650514545057762401680471287764841975167579530669418", + "4792769546077532311825527426101752293398958175332337087633556291257147047450", + "5156370681902535309417699137971512021677704826447998658592162481410036210325", + "4763178997526214364264420973622397594953864683634906055109407846288865926145", + "16580450694184328601228411647550657642835283837702883837525078295714435399937", + "18999059441252116518985471589622428967202991051386298987587330050320210739909", + "10924663926297817693040648165612355343542542868891379772060513577838006191064", + "21154439824594463741340124378173590149781737214921140238108168355994559155084", + "19866718036982650474956880892863081255204850747846301678102177541466640536845", + "5726341152794890743559900317356587749078768834368131432915420083753465739652", + "9721930072810562138700262351588760483759343649062887160202262013979665248772", + "8692713109320385702126858931428174471319167530336355188864897071149715822466", + "17412352102001079694372298819013833514973940827985696439109156982412245617083", + "12672821032755011921233434696998986247906307111868736700673800862103166163534", + "10172970004738576173270726006846527765711642326938611469845242801391711907951", + "9780618262465502284058891490165578338456400832996467543839344602761711940182", + "18851700024286130309776897830342991020297865786362943013731692845791157935866", + "1702470413617318930175864989944938294024268866839160834076264568335720361305", + "4126610465068493007303692923323412316073023198781919888394734000856090363586", + "18923463588257096056764527998950010226471775437259486940005784970583303111186", + "21839276475997615446044485384539051285181302015654907519320251876081314334359", + "8307810470154672938299109640101353533274474986026012646848648110658880568963", + "21681710504784331775159894287594944141623698915845714535863219742158877134446", + "13673546849110664891321911520052734162092211680871085198884137808051757614727", + "2216218889963359247084339208809391515747106874740680794343547186367655541588", + "19115757323666287625233094721549891816100455942516447718127879700062782999180", + "9695759088071708435257280035083149593819491025470776577904130348886983773393", + "14990538824992915759805383740971251128446161760292769615173978580109946105509", + "13500547160059033639084125020765777005399100253116035721012986936873938870871", + "11690620517343570003741441631732985155983668292416671376092598551135607742688", + "14092995167301878045390831717670181419253620142006537525508736966592448309458", + "3134074644668983555014847848317126775181820544161601574897784886291988720093", + "21356667096328346522478877399307107750543924879742810010071567516379232573327", + "19328894389922014017090671733282649341431509867352021154566219841026028128702", + "16777200051435195914695940482277275703598221309253917444691069340963118078548", + "12283150914371601398948839801032083610952821665268602559004246563482653048467", + "8031025780211541530328680242940535108474848337234033825435617391087509668168", + "11393488534594129202264782038673712630583567081414606145740976603672179543089", + "9489545274708587303435157546929129885054870357881034831999260754593859728111", + "6440297901700932642712459656137166095980435930130325599953289987347675780242", + "6894515864899267480052927589331916028154612433273964297048946097044117290367", + "16589536620737203892631819952021004148548871602361830198268389856564419047101", + "4583341011172551024562134025995013510576215116397573125685762514159294995879", + "5241434939346739917434576806032039711895865193494390109560604956906855912755", + "17826720085025179303670747265498956093868413968174402515352860124554701720680", + "14064177224800231170886754123172947484113558685092392153296947056267698509591", + "20746239761736290080585462212523994318855974409743295600262800987044075760592", + "9275091732796562047971474774056076192970912855497078395575768670699769126364", + "19100348980922843718890560618583044384034028895423544873772560921345167893474", + "8069571373226602742410885815839523915870737256484068502291195820379222033494", + "1467293981248465102656318727619114500592839521238995988554329217082570049573", + "6381288201275521270245376318098534380164014423572839901345374016424121324717", + "15155207472765568640645705040738739058676005958167290460723767903432317983809", + "11827307205908787524136950608324322915050519697651400085361964512106546953617", + "21374803923432823385981703420361272374739614104998515368780896432531799636069", + "1620198401197709963530631727016287708030541032398661971728351531077981225770", + "4800625217713746765755573372019630732124860961049092382553729057755156854314", + "16885045113746325978276736698359862381890909478720549186885416774857519433844", + "18940059701033384450370562570502818829119108154708321114250461189220553261490", + "17028163572961360646345264113167539623561504425952365941615928454393252072957", + "1611295667261368131094571297996490796385397691978293531693197200319130547745", + "12046456112369511110295802643603369841325461255354393474919590204243253663937", + "7754599975225661234551263660451457408477000193002987626530262268904609507135", + "2313209556333942391083901663566216617901599085931440197682535151404828238681", + "11201045085233852526555327068198185072847585214550933934267941732063511616826", + "4467724481011544137869960218540384367398804813150942792543111067523429032279", + "20757297420378962887523157926616026028995120570567878138398997747263606554197", + "17461435854008784993561933203163122288937104371480871856334630516397129102735", + "11233240529972088933066139644647717712375997842172826165553170491212908480221", + "13986517480527430903609790838030615892444640383875866009325809535938190376249", + "4301929714047907689515030408708811722641058401192285565408232097014641811166", + "17696794946098216231804098416293622660221918490072683908415671549432039016947", + "7439888160491058128389941744272690247182736763968209695016076283096704865863", + "18423890552826544443227868172047086037747632248290044990903922658938527547680", + "2138999949837278709489832079964510183830385901897653052233908704951853637094", + "8615970277754580973564280361114435711269905378483503993334701750963152744511", + "1245284434872653312507712347938909167594598148561528981150147522912318212857", + "16245768201990863935411700653063134435976966358871420800168843470345424885412", + "12879861318323580145511978223564804504159396722688932396568245995032194740594", + "2552284675231253239834976615340003107373525651900622595931449498227491766869", + "980488019139379194961142515231528163966929536082343598209628148732322981564", + "7264750621517252424008399274736151499285393886075664598878039391991523050881", + "2234522067987218377738399151771319350093309605278907663987720519875860528325", + "3115352127411243786318531509959426596747177553864136971519102170874744602976", + "3667866402502198123848250630765454744061857715778956702490214141676773902196", + "3070805472938376665214561241692318374677535766486562510422267606125416243825", + "7823349275852374866499481666710330223055954666864547234510846404777769619933", + "15260540996982618345416395328474127890176746670866447824367770359510137832585", + "16879450644311650077796590833984905697151358442225768059780947025899660370589", + "15320151525992649849875202875042133283365355535963553893454329179458534031485", + "14632268633016875179098944659717422356155019599115523094512078700042675723400", + "2897293850461343844445387032809649396366859437152990471719742534237008502809", + "19666002760225463497556206571085469154515092232825812732816997876615778032614", + "20118956923652349866364311940594053676005949829436315819799225245413961818420", + "11744985056755246878773405286931074634975698324846162530423833591918403445926", + "2015290434884472026423323399096281629968890595973845384920383969402450506434", + "18249544278720359760062826368546772272198884406461513288445757927068468076813", + "10696154943059697598572810332978032400281533074175542497867727183214332201316", + "3971341798054170372777083512695270701169903687966221904151853325481552612966", + "9959663659512617071119055590004415036736845473755952682208877124936249722622", + "2181868782404294694165345801562667214869325590275262047722759281472694906368", + "16543662218198449396015118334228119894116585978443535797616874635898860943302", + "56444853332991525451220607324577660725674378827134771417770990653696578708", + "18053264525785010356112278932118420278769989354317119094784720416230380388033", + "963511433856494437037779480692599956325036862396765985497581773445785537789", + "1640548195682821030419870544261428767973895477020340173398587128599119014650", + "15577232677357883687312806010171709394572550002708034453277797023283497421495", + "11090954381098620504472251563901051846520004004959525348540953956152044818938", + "17126684395012300883745178409829259734795520186865031009709416122834152378303", + "8422844186088908124243365558762687987341862957152073183459572023886741121702", + "14662184649744589649763530324217861015549859438835398957469773613387857845572", + "21031377022606130696364565229626602575982595290181000993536506695259062820250", + "12689389963301532909415546365757541009330035137990488344057816579888146178666", + "5214588312023400585366048036841380932988375130807938893042700215898846591858", + "15041893128824524382738883860992957588596383766546394405410998279035324015705", + "782192460855406809992878758154419200401389375934686194113667662665836876265", + "5661239444927590688224044135973292706791664398184824354673135332710484834774", + "3817944105257087067488946967989598283409314186886285679195995872866829761906", + "9462770371773357009169408492776891016388486549772557694482752113820123322232", + "15769763308905916572724877727092407815390795595917044619305377061254245212113", + "13771914451667791497863967930538877162560051760275863288476375824988661915333", + "706561967078473376761233253052150494577539018705821668938338502999740910843", + "12390235197745683286169097117092390888206044377209705969844343148646378449506", + "11145596728577531179888406424537808841248238586984730649700720979694309693930", + "15681641822349110677199695953283484509740812777386467296516920337242539284544", + "17574569459993920657262688380744386356573422289886331853177778938058648912158", + "5624326259006122381461763176192388804186780246412847832931645688163746289220", + "16496277716358307095107833912170230220700599576045238114071305799889738129888", + "13435082168481898917836725813624706153275101528359222531631402061384065427333", + "14145359265331851785705850315135126451460028716020850520657056103361268407457", + "7994845231841309191598369070742738315856499258678582728014902950258896103570", + "15231733338108258465034981184038200743349384534550555337079556783807466295081", + "7713361220902077511242971379188205561979775701167781811837114504665826686675", + "11242747039712276301478013037107867308448907318991409117791290960665848463140", + "578482125379746689637837359294794469152617864738083364508568444154322295070", + "10411717957746838375775334848320973308009407649021455865417509560443692302903", + "20227006425680272844667600313609218155469920109215388465037566152370252232679", + "4086233333215814731082706125141659248745981704236593522409792368006446650781", + "6808584534953180557531972017829075410964510413317063595166364533912498333198", + "14933356623681974917513408843614143743808832277364712291262158931793965369957", + "8361829936147528340154802497787058714748584629340813748839621641190595526650", + "5538387899076347863195504194335672966840748823754653039217054113809390157835", + "6615705984888160955628612086123920128735091346352058978084487503753552928718", + "4247091362085081761865598677360777173802067597672781739780149117241511260340", + "4974625254311074298909209418614844254599548567616598303359921500987075775722", + "9907284404283923304330882823688663208972219438730081750092058981162203620514", + "14238658152145851054995618234965786835935641311230999365757330392464542182773", + "5602036166343678358319941150088594172266643715315669628685676756102877898852", + "10315461382975117983285315435824656113503983463182219407091635205239799886780", + "18907680493658247637744313142676664986314600274326418373879671192300682190898", + "8553303723584937031207437681614026720261370015480962759264727133956998736136", + "6891145490942460969785177369488530687011218911690606251233112277697487049464", + "19170245246106596353570021916624717497160367167030242670159454715827568730304", + "12963872577905157066479007532642021260793201956955966146918319119859824768103", + "19825143672793576100800645640010743262329532693712002233459160547290155991184", + "122363237387596939334930000068508772338726942066663977131612051418977350004", + "14083010722716557573128541885186700095072789367734739290283132018427397490175", + "3438988943822218606847460625826294857914025511956848084069535393649532189446", + "10824463893276561180588773956898041497815337719354064658470380006885795713398", + "16647020853733827256761129360155419271528077519934494581063879760642045999108", + "14779736608400098952280783906738683105991809429340153405681057429405486938750", + "13142746404873014842415191205878004285381311757484122253939053617110681448928", + "1386333773150414920705543260472370744952980454893569383627231135209073588228", + "9697679119976816079822213749536923969219789272727871054723095487806154942217", + "3460404774244857416356250946884568630025797822298777944076755223444927083223", + "1247968706400267099989617117009880060043946421405967563604685657862139452642", + "4507768933023927635709805634829365165299253230159842263645571663212665854586", + "3426290614024232855381062903753364259178390841306198547519682507770097215868", + "14446229460062832266543880366376059431654246279317515826580297094759435628761", + "8067953446832743759173288639514778564060677449303509306198517269476636669111", + "21177512050925009490126830107933732327927737774182499659239090599165030659885", + "17012508948587701524178526490941665696519922093511307343255635334215277408110", + "13790566627669249053646494281027520203709518820977624221038904711079372230598", + "16958540834863372886175563802155893167203339755555003665356919890498736433754", + "13423323660143533006628485455163236538714121124914329148003611317540843114521", + "18732130441148222818743122719599795831305992743627249485606912380686722650328", + "21744582565522883116221789698750442373155722956313850154199652336110648496029", + "7144354002506963954801968243991190427536676772300009377345054067229788469802", + "546773555852952995236535448961524872583912665647152978722419200006698017228", + "4115449488659159484679292884434524602598250771770554945188450323754895822199", + "8595752591492411960378746550184866534422481915151040676780917721787338199480", + "10165757572338172851680092102888140824090130405358311565741520039277283108819", + "13881202481966783370628597357006999877307398877082297393247431527668438855111", + "164079380527758195225075822377759030452558671627014461867366308987961032431", + "16597854725561663920154725753244355279782548142174519652163898109297275062756", + "18782804026635015341133877400860675283426234307512052057983648925359584161851", + "11803457827110915157916889714293161979623605698803927824089144848270204757888", + "3790889121366006080676961526219702764983373811240106724685417109395959845261", + "547387322589169298813085629174571402766705427171260176930124272482922852550", + "2250336107342267953278191463235028062291289397936525878162948482155007182474", + "18878522248575655887331882357174227152771300904536167927922454904180276940063", + "5479817494658339363150524278522534672218076031607309533533517453567105458144", + "14471434214677769494999424343272645790653559928767779107202935041996962964627", + "15118605967190623209837537416237887934162874725496448474965882020979641133025", + "3039022034764509339398468046956573675942916248030956764556346100611315129267", + "11040960503243486479242227243433964824049773842453715686240136222246114618507", + "14522024554516641986547166200505804498300840654246791399797761270595590503741", + "6835224295711983166071915743041027338226822865794125690776093851440377194137", + "13366807550844486078755421216059218883003148232530961590657899207604510596943", + "13252007130485429909186723432146549270562877209239528962258894291232641470721", + "2492164358128252077612941728095518049849413853275094452844245158937134153493", + "17908308295682961911088181370570531617402259473536245650574431533231050717409", + "19598815799287079436414455405296166262841941910574843546435474727483962158191", + "12296683988787359527811189035096536832943622321600079901820428531954001883453", + "3034216468239708296411368036093482845790159680213461245275097466511477145032", + "21365662921308444671672644376862924257055009191312934218262773003828812482536", + "21643121605272677508602911313799693744649969093302740901187983246336863173757", + "17106474623853535335864151625226941722837229436558906235895607080504648969673", + "5170493063367583327862860118856811567748934103888623432970286709351028834594", + "16157297782580756629356992582192341437261969693717401640006420404649789675283", + "12726902817862860900369295542623896245980247290073629666117912397630392192361", + "3081548921497703421223514856379500960238907095191066190890406228882786538855", + "13383733165408518558908753036431726341720113406574723548828112868926423953299", + "17114711170811861625673949085444809223764181246137324935055609379301828432643", + "490084821539048617783395755779188347718107877537460289908857747116706971191", + "4649874458515161549974867873095987666703831126237318082002364917533053056940", + "18309757296672459101064597681764458207032523283974093139605613289699615669226", + "7819183041486966141608141659813055168335432042159899601871834502038946555307", + "21341796073041881721040729550176895226179046215340279859887156449076052673189", + "8964280440056181910087237574225882257648057952084687707991537684955336080915", + "17214415387533445163014526685896715822410886705201252936359885537015585673030", + "1103535976918892886566275197010540828604931300309949743647411661976344710321", + "18989472017473838846112035962614068658032619400049135145786394166114140582702", + "3261305106935504628452223922938937365963307399148885220969836911471709180672", + "8250352610228962368545929233808273614693022774196635878184474970985288333646", + "21336821559645974200167373849850832540612240140661136750527391104897925746090", + "4804442184264650022914507746660662354579855239722797010340948536245079163068", + "2980351069317208287298989743520302917458165906618872700838358045014403829821", + "20789178264137518066753395738491570400211130020259232167860189045958431732223", + "7043197173168479199982037485481873468680373055205806152875339535752299138160", + "8803416368756444354155108122527515347994446936702934474369040166131196644971", + "2327814593413519022021694520242495352706688146726587158472403609475455845031", + "21198136442842149822575509914524766455809483419897291889042050133491478146813", + "5588816308782619790412052393435292678862962083038610001710149574381906419148", + "18457707371647662280814248200286355752809748069618488571669726734664943098742", + "1234389524319480772210519595012254096434562205385972935754568629194134383384", + "8590188161561261795686801611365857510200794054668578839168287669047410512276", + "14470989978325106127256991312163042779416463931400090968073878504946069484989", + "2305522070968512786738458204005001050714630746016184992510479809638463479749", + "7477355783744938536562522733347415235458953622739577251175967178186753572312", + "567290291622042941074701939710650764088062581452752653943609540087956251460", + "20440460641466928749670992266608077799893623138336747959135603955102479614007", + "3054829023148086310176268593952124817160009906490401089445995945337990758835", + "1115018683950178281685995685874075286310060333362422341896901389981346771770", + "7809307874649635295815564328714298840313171873184814252455880392757245438416", + "16435322760021026161995603943756578712245360106977256514516858926570158830261", + "15538054347747268689904146479533221617920707373984233068744782064536586402847", + "2145106610221843472170037420099674539983626911224263405264737836509031499186", + "10469489855508967606640238771767546316388755621910047798817316247787439887327", + "19269976274347330455450094587958438145203987980029244301223712026832424496030", + "9442647357194564700611387347788116150552532746010343621293769302924097756012", + "3958820742734683830021295040986048520026707752457985546687457778678735761219", + "11348329600459394107897118896385152488024741286593365288318995787678872184581", + "7994061748163511046803584801099079098965771882190345699910054751589174660915", + "17839452077827445141859404251925903822746019560954563061323642101356666431414", + "13232228834563301156710808377438125900543901439977087615805079133929315161755", + "21820445178921591403441486524597609508081666751352389500190809234730066099269", + "16335255581063466649418326314739202718270800342509998411094897137707475468574", + "7815038267931398026175910302031438514210779836695664391140597883647619726660", + "6108907775060665614604453427927916450888778767561273091208488690292223394553", + "18731401306874205485033288380603219954710608893214981010860304692443381676445", + "9207310842136026530277150017022352541117999932757777916196896007342718304978", + "4275597059348294910195928059046438407683127030809544814087898068951365707578", + "8551726143850685533914504884506052366269066663000473253249997846823470852089", + "17722307737167433007678801800007589919417323324870850217087785924251047414732", + "9659885913999670803985648240505723928144853579243576606650908903914665975845", + "4127785260298795455679482228123141201810011802311941172182140342819621401490", + "7995480315864311476694461046721076190059098585527103653430045699541205743090", + "2710887853573837837653751249029686223333938865050270571041154550753948687821", + "10929954836518904033304553904442567770321295457957205208591304372724905918438", + "14001950425766109615209637768509276336684867788731325954527529665290504740308", + "13093798171633818149701610236482430519636222108426384974269506295925166461929", + "341112534319506600537441322030652722098845095671561271148940215817004993097", + "6110906587902719841743444555887325466851146812889558891609843725163499179004", + "4611067237252812652698946201052371234192768537459773069641551009583406296376", + "8813661389433335471770742946393259278020908342551057181562786643750538648664", + "4082028201221928661179738646263379819458232996637786410627338568216272942842", + "4387150015663192481675842540873436578480016784260152864928968972841893485475", + "5798957359608013690581519427700262504660783802132038228115867574470007647157", + "19722104754281068600204006779633412991974614044671884281702255273995426048636", + "13237763289640918787304198678609438981723281080085860502804175876747302592942", + "10350397375768871942356366001436797697061286173475393119069375716517495164748", + "9716754658986776864790426519095473979615270133917036668138857377100462313944", + "12559455792963077442707649745319068600669837985026401838048238001273610719876", + "15691474792343664499590886820783843178573436351948842211318848415073250926238", + "2315152971409777702150694033027032394728963326020928261979764797562368859984", + "10145932773746660365948684949555749309408495540108698054181994778319619682499", + "21031755674184251231918386922772885560228556184297650283520145773416227622020", + "9656774539408247026483766906298154647226870877463506520381115258572281833777", + "3703366867036538119931629591865173960052419475390100010069369247148720449735", + "1155849517605989105668895794996696646805146181003961935923384046079365333956", + "9462523438649504157500459081869061592673258626587962773726459717365228540892", + "14558942402093706312345858485376818615887511613771478694966109228129293547963", + "2812048253376149161843484904554248272181353897695109556171864471465530141300", + "14581264237688674105789045795632866727938915214983870169917226308316644895751", + "11806517668379902968824386871470726201210807458248501270142903146174354419162", + "4072157532402496076820064418241903939352389786658517709744366379045220383162", + "21813972253527564887362886309776329116642260196272043100875592141144150622785", + "10464853353887659405357329005361733216328104382848677769669050984745487501147", + "14772015460668233451710325522019255336227111235340236488781640750731630746977", + "21527357780259989210243319716428302865642384867695769975035672624351025254966", + "10891794486798293092627071927690327494090905691269827842959180201330098409394", + "13485855904336418644657500553650073084861569474830727752625752553358760580285", + "11448242815253167183767068865945471000243774132210813929164325622036544013989", + "19410155802742110833632454245227392555968304439001009443806761608484053045554", + "12167659128474886745250741538637209499739823457162062821474270919941304684088", + "16680628007927224771777485144832808896272748488695838488671868152342742322186", + "21298757047923195531118553955836039383942072424271519371368119907422148509278", + "11697718923718927022148851233178802389523673168156063462791132046990360699116", + "14786267704709019791824267590492332247783919098705756794174245591027365669638", + "4029431706248165284072638415134780930697313346853706588481402470852714029894", + "6637339357364931518321345115064554926275905427738668939829923099637974821930", + "11721089735875799137502005413616822380279134029969641544449026524015093316862", + "11258510956066696357188198767462611223354755428568008829233602610737417570759", + "17382216798452684990847332797566093767764854060615116426813871855967998298795", + "4497813725838018324398390685637602656336499793658559967801659260213189483769", + "9309905740467289615895980718346712749846725019185323382202916800724200146653", + "4554312255934480823406477691403023661425250519363902231000470326223736059080", + "15877126774677139396238817617140527789790929268045936819000883506734809330678", + "102909811737053415440204213495606488273727867104784554516589930311835745189", + "18412616692627375517300170963764173086100899052204830988805458281556342306704", + "12497849254798953635808071778933029738229621492383212989583296055326390275927", + "13190130371739872388747884175792586030658495147350847435106581404654633998344", + "6569505978975360946878354767391913708682729333577071120385232557177773967150", + "5486573030999616702165020120135398562758060949059349810688840632469582165448", + "10402649247177704342444720212899468852439313211834653468379224661013914061054", + "14870393688688048179476829039887867662050219694351765548412705799226912185902", + "11112866336990864101289111553915677602536611363742637349719791431991672470238", + "786378775702052024749700197105312354581130448431768994347811552323325137023", + "12804314666740390639363966758718513558509859637020806917779321368800352250093", + "16383777838797313001717025761299072170493145903921192845224827276088079299324", + "18390795790658866542478563024059495583921996400061610405032233720404354578517", + "19071180375782447479569450482330188708754447860024817674265020413221587566028", + "7064459298524756483115092315588548572944660746058573142372140457074172044435", + "1201661337668100759364810191918525473246105501963149603200893010746984240574", + "14673821426225418930674597108599685172091690120026694257549676255194170791868", + "8007695637892306475030605880993450528464588921978475145792244443873714795074", + "11523933430814198704411556653252358930965476928606503642553530463689321317157", + "4720685254208822524727003349181010163122046826828742211508555904133729568685", + "2704641256375089357490975523544121554540568505171014265704664461404637160537", + "17873735287572402838142305036318020439254672746974064930437541429239252592118", + "15591377870113101853150486245422804312550411754131695747717894157544182932395", + "18680866424621452161410252746147706712971801673312117262770403991597018010533", + "351092121232997110902065296627489736041463573095030601146795248738327912247", + "21410776897919290439576321311168524383878404907590335964810760611175870803022", + "17236852113726644484641871544732171570702656662638441976539579805457648241354", + "5141902767347064892821277441749408218256386923351945667768202586451461154007", + "9061206488410388460545711706154817066096529615910172728152676288649487389209", + "21501744386281091461333207234319069661329384520296829009605344252699881602491", + "20523123786355828261901087089746676928050070859811577818342254696196216070331", + "16340183952833650334331728750144828293293213803155672739442287164371494962264", + "11997165266626958619141537683193751516111129514708339075256579474097934990898", + "11727998419969202419125826890516046584560800158309390774599325003825426052070", + "13931457202894182485995482691171342703522733597260457019434686547274942752168", + "8889288417892697124945054364222718721845070552969370183001562339292498026982", + "10607636906736209971474676586549274600168449611294688931553643349980364319662", + "13373379279879419696628639312899105244563309886546920506727138704477895635504", + "7516778707927565776230701116353902556324173125006482617300170811322359880287", + "284142816263687923446544408203711951081978657997111022490562722442340431026", + "1663629018772009800050577108421747444431944008366135531327269967377988698730", + "15976560170935104892839671978479711290979780560451333565745657902528940986567", + "2821894717706045749560970435026664943807315523436547166205412842765540126279", + "16411046577927712325274395387891881155718376469810123282180259561336053360599", + "14830500017171075678309056171812981524309710840633235720674153550980163992149", + "18516862946535092410308127384088576733043708186179828736204273213102857123131", + "2803122828384011132142130003641446021361083629354222582787564441368260233535", + "8655048519095208179618171351501333519098495288987579151859877773818991560313", + "17628029087309631764673769418575149062296127129914718449980855676793923700717", + "12213182221565572417730092495989703548687597794044608925789870740387528958423", + "1942254992896992114200957837548346988692238147738540593160350563013662461747", + "18572481120445885888145197091838633506611989952111678544191205873056195137330", + "18140911659901689098303870986888083953843757583911874535981587625576530534701", + "12576912117641358754786901787127955567685279522839676706223096592944048067439", + "9853587546273100154686512094544697531675085181100931274409412440600306885014", + "11051022304629047704897656757884328402546597879242426643246383517726104682373", + "21747178377575326127773834076417831630856635794535286757990250257220185876652", + "14802894612148412188667842094308733856318572638297904757997514236587913861527", + "20978447371728675031404263158195920974895558458780873769079890388209356430130", + "1037699465155917084450585874080268028561047537346921243992399961945112783290", + "4796870784617170576045350520159043022811420003073563826183620782718648038813", + "9853767424749529094226213721882427217388134769321854800041359789143412725618", + "16909978741728448404835572592895383783360498936047937910269991412415349672326", + "1843438491269241815751499591631260690521017848626075859779071605413804154431", + "14341379518602865584962107623594010671519157088756134035711400970326861508492", + "11148607079771922951721552848779822101435560243853139254657574581631328105418", + "7236118376476006500577604441648338380683405215283131664289729852364085763863", + "9771089868728917573774866709686459919517030662395798431632764214674312680212", + "9110797416046523350769719273137039416172041255824880845510758575776777500007", + "11171998782921300624880315291878283252073139909993544537205045086249176031551", + "6662646470172779612845827572259867913830316914285246080650610642251248472832", + "7840073244312824502623215399534211251667887906747521505446844849673459134791", + "15746172297311038934003440308705797271829102199583540526113736537482577060229", + "6886137335014489748489631206590121074634704468637741127875111216297167474235", + "420232306382434242751186173486850411467398307214919587427625546445400284482", + "6101917029417345450869344197624899835171925047771043819872045680173320199082", + "20902767593677591005062278004159629853556955206212143592781424915847484793984", + "5806682782059166443158221425741828463380368530625835045262822570023351074794", + "8937322067898730475133772449174500828509823386740428196387523130412301822446", + "10698423376034838319080023911266285836831986186258448454885030311257173631094", + "3804139705083664873950903601295520892698549383396840546300663481183626370753", + "11666167980086510761582909817125709232688171970969608229444060852628732414641", + "5195343484230074513500630640199601466733948553952258905990513870281939709899", + "14624340714306078897305540751958284706578556105002276099158477226484579202503", + "10083361130687068583126496521090418135813124339806220406205408201612392141304", + "18641596433875997766126569678124500204213295416321224520910179376654463020142", + "2202590858941069122312246576796422760143586439968023643572584733807707875591", + "5606778593839139538330184356292709713321311150529135965207704575468867770974", + "11958475164655758519149540613838246197985233378525766327961909142873639847913", + "9229670488942629146704143920529444712626724835075235979345002293458105678408", + "16754443145923358894066339463409997111280948017357151500387305969742481068301", + "2409255870410101963458063337813762581436752424874011147061097403491760442109", + "2214559337316973031881614971177447231501212859777889339656681156270831720504", + "11421110362488121690592793129931919017457696830596130693048382951666066696299", + "3820500999703948691966833683611482557851819710346178343790665741738753374219", + "8756218158739871072278901807217627680176358846778830959237385752221505342071", + "8032449730250468861487267430306584116120245106718506069817646852031479757007", + "10243677923364935391432160448413375676421376200851541463326287529268288890040", + "1128776597481264636069298358213640667665677523022374620075432951501260575292", + "5644093261057489381910165706940112111858706948653266534656144218172488885132", + "10992382177616530304259484530083164550434687278050566643026419033635199094809", + "14561508845841116001383562169823893923516132174944052932403904968433057227748", + "21863964650894297432572505174186286520067123468863642727441839125002713095277", + "20755119936203820147240015392257516486599536773168698212709082762309828651450", + "16959073226608628398387152299601201172109169744065562346871992262472923761717", + "1422360831209405989940170583665509252269399522601915800432845527860741426172", + "17389326678043835536716857792606285368419240597293609647618693076258681847452", + "12197527885708217940863852882450902665816552534167940448166754267717839462635", + "21611570070424770005490607477701523898795002037965850794497143400504853790221", + "11892102040110397810197867975938039639353004723546075702397206087388005784781", + "13516538003340091563836438322533954338394185445401173280530387106662595790317", + "12048467357966566407092445451263285920234083860460644072449020638723740218202", + "1735010081751908190348908571270511873519447755491670766716880879623393561967", + "1975466263323070433866371621575002630840361531361829342799564009137157551290", + "14332158763257273099367529977375112575029271371581919959093592722516585473205", + "18308662877208906527685104306067008785578317983243580533749074382175572194649", + "16147771244577038318738675557955640363954546932826136022515936117397152662906", + "12310104474053287506280911462776789511537301762185944649254713583392295220302", + "20727834941236374148190412755081722896905078541838912474132444343774737989102", + "4772017368444066833412562265277526529104319024388995968178984274657250179860", + "21049615822069603562632099674853063708639208121642147875791882100914627258965", + "17646394174446098039184464463379581654623553955171175600685924254263103033801", + "20405917523260600357692281918105892289710334792700216851356175227972968907413", + "1288067623955526969302061242482178260544140652671589048188723078863398511616", + "10835537730223027316050378066698794099751744792059501849218931290020119106372", + "20990157274288749983727934109087848093794137769592917583326461140632177148526", + "16941875865072358419938961334635058647868180482918222959682613586029647175353", + "9843778843793690986119532637057920745140536160016368781021998854678847780413", + "5125414792982533413337556654533863770640044560799521974799727390524971858377", + "10864462495296760172304401051361158670010031482061392973919136670275578530070", + "18638188379508145413237672170551928898223721010749214505292285199256581752296", + "128198311352192300389214509562427846527025583978725552204209143056794270869", + "8639617857907165397429617535993768106671328412853872156360019116822694247239", + "4844530255382539675437452182943895677920405337347260379412255490567182702620", + "1804675521808388615789572941859899805904819249465788091888167545049898256219", + "13789062391664555041422570123657413036572356127853659660401072142070681096201", + "2984776938673077398183780839702246034953572367406345432704517332443032892459", + "2992847031807357662780885550148860874749067596604232667894089534007015158636", + "8223481330716908812358677489080309112433589978061949842348101802357147464280", + "15829423985005178435297810317464595131483457001231013788201382741845473393508", + "7862158982782368203916129371016962734683904219117743973675651114828548094092", + "19398173801438973209233005992391884365223581634877055780725266290648936529417", + "19275863963890569296158744143112092383404704340976692571347641477470540348192", + "21372102182460094800056358406250397703306525384763761377034855645794765423371", + "9476173056510831081934271835896053957562178504127106649888600367232767217909", + "8945631126280639679924915282277004291625109402912694487894772245410534789081", + "7926253519938744933656970287399447078338275573508348380139787090085253922642", + "16018716520675458445608001155996902645263605619338294478505090713237417816972", + "14921856253793536148329746401088158240205455851010569917169181161371242017185", + "2079875574141510421000322245042248069972426430967618609676296915817312672160", + "15980599742048633141868186018745285129739756803812956666022183903444182047036", + "12114893033109318668956482804227784987572764113979211644064098174620745412136", + "2810098426330816525728669143855005497849959012191227510766066867001705905451", + "9142041039259596869436365631928849454803241725030479248178020744616036193428", + "16789255887716308252460957605463144609726855240455194269099382954389067176728", + "17736208027338616948877595063010911429975090558603409155923157365536120955065", + "21150410646727639080649942919157548722144769190804322567561458114897439673110", + "8728923715723554965990413741235509264041126517176940864777942728557354521757", + "18877975508697261368601618378333048287851497514619873601021983336084282573042", + "5857216433172498779920090709564798696432088771454963449283671260189720263346", + "3416739349758310085403544797458080484340996684307665591910516201053743952382", + "10014661157797313244292934148350131996657869525790562708237180274791925878615", + "5349492492153439871560702687440086188179846024931646961141799958661743380684", + "8090802326500729500530853058367718173141097881651767619458686010109346379147", + "3243876699925510348842975998823895571182658963945930929780026879935610333017", + "2417624030466093278001094443901143647464035143208058634414228940495675339286", + "14707208083442648370683763592077614356382972898797034092783713393639907619177", + "17545631002841426592730923010822053419499562660641460363423698482744533869073", + "4421380624161160840991891701912641613710893511150975452841445166075745805974", + "1077414817249103950943875081715965937853608110732127257676522759590623460183", + "3507723951294024188735232556852951953031271591466929388169161213367649583178", + "20744267413157655820488615250021371239675627876287191610069053435816486296093", + "10030806611757571510773710000844469375387967668887810533790132098599140714696", + "4098608919187988291739821341638774871917086915024686655297087774191912756808", + "13453755776490865531134267856063521315028666076529776333450382526888445047611", + "11760020934758140446049369723964305600159936563536726075225660675360331528465", + "6406037247037697473727755924858735296546535018055390677906442691851004797953", + "11557358458236767995494355298696159281191676229524038284238319440638237580594", + "6743825309135636487078170799646071187521226570701598888088674184356446519660", + "1363740300261508449387087660891142217813794297057216573859174333622397381039", + "17353086088993821287882776856791148032337098217763977250246938079060857079815", + "6718938715458951426350417166499489427024335443629489544367851434423278337209", + "18987489742261748676564017446964953975327326154943753293417429206546308211028", + "15965659332352377592570514074534026676650570619443318860049459797446297666148", + "20815651494112396184410166336624878644932077115689590904142342335838514032225", + "13289193671002988855733226768851953818541013880219936597636372756289364866355", + "1567979574094562858336626709833690822884595003609499722973068728155414218656", + "1406894286237293229723971624858421702390230552459261102967091748375073005174", + "127524353461789027170460839609791116742773867221485647945877046573898816494", + "14964917196913210912898635537202273294898388149888129789323384554911752584731", + "17573975090037837489354042024755208350546900760352759486497415097145199425437", + "2810800768573675011848063715305835214048305201677436448513685242208560656239", + "3182831596404920556846849906787256417421227529749874859977287400581732004348", + "9293221288281284622603029762317683176118521399756060307438914930508616154161", + "16673727443492180846335195490607282587311771952172084861120865668023189607968", + "14861970206393540357193848383493197595010751639867769363872113104941368881368", + "1947459730453714728047065931026988640356317164613412029204943819757651786269", + "19307336986855330171250472129691969885469727663168817962206758878226019668925", + "16272723820736869927531258455696258295545428585773733795358621236250129636598", + "14067203273048816019998838866277520003916774532401938198162316693809632760951", + "21551507147108044169777874975479534770240991315165807000112720115356653530622", + "17717640130827927258645965421731152649890280820567693269781156985346197796607", + "8341245955737785666220035421009659068228682392298228242003714027037157516189", + "12339617302318828762816142250616108419519769797732290739230041883942137188888", + "10123566056273217296763559440410478584111931737336670797432668142672222468278", + "100330409846079023411270961296323123363585075447874304748480232510662674144", + "1571586856190083814591643193451371280998279878174710717174319810741950319455", + "17626078717574459956178559042223066518589738318146553446930321898524763562145", + "12597474837573442277568310997313425097295312228965354157636545560783225415194", + "9282629691296360927786998265863490172450342562246145699272852196309154676715", + "11475383910279033220892799115018315595968658540404734297367799982184306742405", + "21207578382911046461925166339581302823631975619749313031737447832273859578217", + "9867737209512713742156966231095908977298144995685201344042106615506717185970", + "5419250263664842635521293741086549956576183663718321466133029062700646217820", + "10680301618473120824196964202055452643700003562180874183620820959381512387087", + "11734118782551011491731695279474438578347930162244424307495336916011158044578", + "15017496989515336959243556328206095618035482039354824183597442359483266217611", + "6511621188986288868363472995938291798169517601112511039955221592826262879815", + "1057116630043531259822644333647831844583791045276219751332477616072560117378", + "19059685203310882657000346272623187407133972415131525465827340360192915270217", + "658459778905143381055609790895794611242407110015004801647654573954369450993", + "7603036257465538034144194526422841628761243007297872008772468055256213693455", + "9990634061622229590998343828576831787715708492431159246049355339866257946620", + "8811452869339089802509557934124104823648284438116748217266488878838078556311", + "377338624779519383654026004940027827544698613282452894117899417005371283013", + "20372432760811032237009833207612531537192479064270437032142982696970398716892", + "15755624168218886509358985223476988622172061232118179707766784427029774962558", + "813530157019364022188179508608879306640863329215682533796638454834580544088", + "15366777101659035236493629475299055460750095122762878608882821037262385854171", + "9412569363133467481819948072214644683803929543842857451635373713673797937906", + "7663229432381596357590875498997196481050744300865119408024986926994876708428", + "2802277205489381760400127515311144092806348374683690687945106801827615715524", + "10398607437116928120233501860786387490329760704107357815670770315190035281298", + "7713454417232694103114115606745926964297583014900008700978733395172058088330", + "13765632281005573885638376819890884715413389187071206345092080625293363780493", + "8481595129822383153686510359174155430289248263706955785322350737378030164791", + "19662013748902527903033807785062252860528102610990787703983459073558867314734", + "9308675917577065974819002737905225232234719968566022906777214056034139366532", + "4460305046275724592205653945612957908431797179457871442468403547092050276501", + "15300682822970503133550740797435695997154332692415846288727432330759835456273", + "12237959719178772655779639362705631668446778143915144512652426042298606318280", + "9074337685905972785113091085216241647906400276520026931456332814507320753649", + "5631451392609103370493346239448329080866515797549576094657011047227839672541", + "2121343968626452536232091590081231792044261748288461026059952209827486505534", + "13428337287072393077290174792209446111049614088462784414150825648173933048712", + "7637923455564736402006004688931010233604652911202725935144306827988683195798", + "14145144407093909228652602933032447693975357132368479264731012546112843610158", + "12996083206893173697199261663271430611160188106624571072102983887472126762111", + "1731425456500536573020772566007405873501250425959888611809221924317821607399", + "7235256019731839713165664289644294928953374624035695945735560280325779220974", + "21068041929715170328271489788020516757451181357080748023363808822374772951544", + "2613269578304019608168678260820703130619030694547890166073793773931717299431", + "2895580764674798551230549071717806800560472156999393716124982187189744328131", + "18080627279556321451131957341924578248984242962783250736655220087867816491510", + "8541787229490752355605354050088489559660583814306921152210039891158051673167", + "4773003619955976464416147076099271361363813479523212833233541104584120420092", + "3902657294907605469046100206491238707374466860318674919042657295835038516976", + "3278680356861257827773728996810819308957995332730291009303523122858553038927", + "15799387696541061708257049935405397874115977721965461912710489016029411671536", + "772127417921361022785917422978769290675101599783616547948775836973315286693", + "21172675246800560416431972802840350117631380023487771635657330436402785697771", + "18058055216124783386618232874069776997923635894599900180049394490069349321671", + "6486774126469831729812548493701084245613732338682698890925717889323571075377", + "15523424894706974280109344562353257665535312014690869100282355992032362528753", + "1155165761749577539253357611425533199963683430668786603835581376097524220748", + "12293270303409235416382105881403663503297523019464344830582632347870203818941", + "3805029697086536368150132668072389131584837710357108954351676850002928826418", + "10383737378445903520223858424352769490012472500810730319338019642336472424460", + "19139905240360031959084644666653677709382597275601060683474709422388012451876", + "20662040367334915413974593999171397527205617639639561319218396170856657836909", + "20693045501044506792047932972050932132614012706885392044610670760209701329210", + "7646436833143149025181986338139180317049527836176583246163126009131421429218", + "18468267029773286403821867293367277456399346317458918979624725373649687804739", + "3368837325920673645463134045719391515049969019513957645401010913338888270435", + "11009184090818836540140614853735105475441340244881441725231380311734117124613", + "16438249098211375676141540117467319174575385934122099955146653152842543524308", + "1978426318655034633621003481179913733408584009919893346861360079137199026039", + "10835870076129494328875329193612835430557328436367520958854318265497818180707", + "9238585128656275231406470048000584932169168279280345637986485301554767537943", + "9860408784513424998732752702463731273076404557148376009384338221275998841902", + "6200534253629734756957567067039457925613934220422888818001227120423772581272", + "17839095320044387599555680620747734440255270476109368494374200849742690805825", + "15852210333828297391427169949742706715097259872699655846272917384634556986629", + "13333830495117301018203298033441593118871576320662915411592512406905971557482", + "20141868252044007123768212581882815770873248742867175012988635705853879924105", + "19536412434762756561256835516560484981006774488160067264157868466806483409550", + "13376474825075395326396381198361278687546895119831914117080307993251668968861", + "5426793384935877497416931155195289044246814775306615779818726668069304816143", + "7377826504693724659923090551901604172560455391003832805477022207542742841455", + "15304023433512090811722731808458537014494291201252876199903945357536893662723", + "21332460757344630529450409218029849205820804076582364819572498755186176235236", + "10123770219692666599208924625777509883271312561577690589112346574716432563379", + "19563297971101084260099820862783813929179580711197283960855071977068075400435", + "4447207254069026895839688542596519844298864167018271339727580848327164733569", + "10805247891957676953966507705079087855877568284903068495618773980630496028625", + "15247455859749071900237435930523136345408272002648820316650110622790580875976", + "1523513713815340967949299977561986680730112913862622151113511170812079722460", + "2250092345142769832690429177076582432571233932766644284160212961505086786198", + "5502205194489637815685308301009607192191278776537698439488907603617260091996", + "12376110094727246736838305034851207905481280418572145688559541407100489476563", + "10967543165217223191795950135818653456591208931427429972754586323384063916713", + "3007127915385490247377746617881910295130312971235615226220093403044166435816", + "15039810425747682043693978030807314591772830040893968588297240388685740832440", + "21282434502237090849892927101680045504469095017581606642683944529102929897154", + "17820554077495649709432871866725766383400924076526622095222498536082233385393", + "3238921346058569633798159055732162989904755716515207404910193961449538091755", + "6963065779232897395043653410311694992252845745677523471496668662716409139923", + "12741150889158293058202855298380167280366511627706735086836055155215291856874", + "8505231440570036863755136868680092759740858472921139738784962359970555463386", + "16263867309272856871859017360735124501630350380225439505739588230939592341511", + "11994652030543012675378700484156120788799454178056174639173766735729691200407", + "7531001745062176388176369578077819605743320864253593427909699809569190244306", + "11827863847264026226445842677102655786795007713756694062525124054010199531855", + "16129429107591815909860445595042156146087129962390514833959473605760944146527", + "7516787161495467824155321560594800913116855117833255279107222188682607365695", + "10052162714097814491117037623656731155747350728656529965818461926126557689689", + "15866126693591846474201863966897712396582450897559972801925967203045783070614", + "1769345590280238074854062499047542732649300047644326060581629862863392048630", + "6419881722518831944067972549598430355215508824227908646199374367237336336567", + "8427762909619307952658968866443552584787988597438819510120993141703552873807", + "7893311334904009120458996652611823451343891633360441850908461624396886564456", + "19760139770663625588230394835227131962944058142185140281511805786245566094372", + "3986819882418057034478817109485859706183862449050678809809350743539886112292", + "19462740190201175689471975116541495793256811137828607475310915853573746549315", + "4950984269438746319161544135494005780273536398471035822969257463380210776783", + "5114126436371649572151778297196722433397981560175541938915777654399517384867", + "6500502433233986724406965295441070598123907669991330544723786166137230830713", + "16317287950659298477752416750967064455139776512193467637272228586615836234506", + "14644311768591599882243015624322844079095287900058309581805861693953854143586", + "5280400910054229241086158902425292691428494311972092134978890627089874392610", + "15127218399002910266114392708998449241113801821878209170134753716497261629315", + "2711401246560501813405727886361637939228317090729002412066290100904319360960", + "1913027325164607464956755298437679573731669261937548387089448543475389709293", + "19048913840687946111515885036078245399510860136071611921573959765837451269059", + "21295287720552550910280526966903203926240851680963634134437049744624969307301", + "3797109693086078196558789257647252246485873371501397666934942661912968057995", + "17220092189283987679009565165189257352830222471797888201205665583060198133070", + "15728859045813610297052814756440978974533041326711446421367132340721185641530", + "7915246674314292718027357038683169057328569313936589479968179748005116348962", + "3168535062320575750000050980968432832479836571078965478747377498891053785750", + "13803986818738311305292789973880566948405947108749390476554600214529398575121", + "2887021549045614886550633658248390500136948561362661868354602793719521054017", + "9835777853770722919097370759265493857673816349027354889969484370501195735082", + "9162920843634529872148328448437467777954760966007230180656824492469148201817", + "1074954035830520777949184022573483523978030205622574039670230048399504082838", + "14573949395382273725648387764764104587706358354270169802238874779976052312087", + "392719645589691148855449884443988672799862378880367658864925364810308158541", + "3685551641667332817505970777889874263257498648047179005679069858878239338060", + "21636266924388678043670779518381645484139129597308274177295703055588816929542", + "7321567536895702720774368439707249290671186165402244403568878177007380798611", + "7172839196695686167303608986523055739039297465612557351529643226329584487820", + "4229778574282653726990227569322645489471840372440828936529243607112035640074", + "18782302953492985194605877986375464862209789031757664619583780308051670003641", + "11974809482173143458078984920442199204778548823433238304966088658895546634996", + "16243115558222486864600123441049281534776025041710213515085053285162975382052", + "20774412501254836125508172987964523733402936236671082985700605339729723682856", + "1483065919525318072073144044234229344901017930590205131905224416619628808251", + "13859608645215082985012618178187462696738961393768893322969190913266642570277", + "21405376459105682082984005097612235541120033931778677874212817450507078023807", + "11689238963312530686999514700594656275688886785400717773550428094425955677451", + "13770805346049970815658755939543097628223982878203541747699103196190562329538", + "13464128750778834946158208517080192694313764854534278881787962107045396572111", + "21407849640224904790247752730042846775377573219483807581870525715728151019912", + "17742435015936027530371893290930447550842976399042348699434701387859868436168", + "21607472280994630036571403820711131618277491312399185275464826427174795496015", + "7688464458545967396781600592854525549649369419708847302997939388491317131965", + "4387376978957625118504779199367262576455322502411234315349687321331690532595", + "18931911591990162605728772083755015441650830148575725789586998382906047952930", + "14640568984073443762298448827081280142493018130989596534711086636304928257387", + "13306842042380077440486698326492089142513016713060879132515526658677539077869", + "16831808713587057769987931635351931409992316524094756901264732606403438287007", + "11322415449116068033081427912158622347193126836157199640557951056169543153666", + "6851604032522439802523441319645962614365864202922586227207366934548805743971", + "7865033453112245503939716349145002950003125074081779764874079104952011815860", + "7859039670086457402270651853161690366745490673814388503748678622683637617076", + "12915205729177842184089675224634811499690953326168778547490092950902372657871", + "20937773388247481346344394224147944853257697507105144044681818248765914256516", + "13287060897761790924722969858390853273555324655965292728828260759103061260482", + "13306896346810583848301553492224587956204219709116827141735317644044595737290", + "18219814232296473414944129594782177430971511097498319923644038792152844524438", + "5428298390273304248312869576101694361963883992477060046151268450358678474516", + "15201216935560530575994837331432805679558316705524948725894686934721622162910", + "9185979018761703837993382762881735970994413889163208925197647077666234876915", + "19181785293325954761013200262201782189994371972312628530578184844339518606203", + "16637687071791710254394571978230957592057586799407792249481371375691006025241", + "7540695380854477789623159801158526233798259867148336812702557598270412992232", + "21033749924623312349444484016552328468392604073844480133610758629121621931608", + "9900738449713019488539657454744856913719757496206137183076543026413680378482", + "20521005306019974539700555774138941983710853964949502636832851583991666071090", + "9148625417956423696904186924742393962087070575705980963607554918018064302410", + "21162567363216498523188025354796572574708404245885103426215436531517848841283", + "516078530341213545969683556809078278712251743628535410360059529914040404316", + "16846240853341179628895683480146570801780199580812523539736983428470139888117", + "3725063466997152760713869763783030250548722164672702170123853642540240150907", + "15613553480222544404649584760559934580548305353382956847869893563076098726977", + "20351109682633578989341194704546173168875553988784930480177729322649037322556", + "11996883326331293630396983616262966081550223706386102455796773922602029237495", + "7467763103586466278784854382537524995001493410600869109792768087664508057830", + "2017508073008218056990628353468704346479637577205333604607959225275229259220", + "1314798328573144515884513072855778523099366140621231925682355151505185465849", + "8723657373526005784062072282873485835867170661363188128978957400998918352651", + "17008566456391889354408264184115222893038736754092176958021304795161250142258", + "1277201481182954066723110290486484624236238076832533331201564897838632505678", + "2883326282329572573598657327001707359250291449257261775578634065330832194673", + "4435817386812736743435792142021329255563452933485604716406076651024269234049", + "12698961373404268899870565736817085990987024732021219796534551825706597317720", + ], + vec![ + "8089493102530595468824649860529717181797071865148765611726566631095271469313", + "14191702863884040950201894968554909828474263688190658890761244249722339089253", + "7127251756910107506817428481560230656411897313679770455026102671367474097624", + "4637045655841785226199626823170615348821917492997807409130873179711115857270", + "20694397780982417522377524687614391225351058648522092200738043379276991211973", + "7370528537006777008458800981771544164650999525791518443407240929647301337224", + "8357863226135085648491488089966537213596680483394798064376015085804610201240", + "20367087512494301090653054692863377975501664994817680661442168333644299005261", + "1950307616347822794878104597377932622958317394806719432040871827549672571148", + "19534568412595886801081532478416580190048212177563706092280027206347120417736", + "3526428150493332211163778868665379218281231008068537115657136020226387337771", + "8661888879209475483716403816709663500534119170711151152467249807886559994385", + "2374871949454649266019269203973683966955509927334059254254180194157352943840", + "2602346264611026079459352146265308073912201263350931622963181149514761751618", + "16750875216633927741061710170647391823629779494283192027015930006045661350833", + "17325348607596842041611786882495470980592459865615213806942169413086586278610", + "19257833407854296241609506861086921463964253377452883026082737974007723285939", + "16875536222414380047765946704936299627494679557426090013440678117262080229388", + "2035577529925145134060996791483051399276402053645795728801937468290487364296", + "14222296831200170749164428995066771764920247405996747821740895709606052517407", + "19708208883712347371628256596476366883171584832295648266624355950215220127904", + "9765600454835189412776212142789582666145342479104764891694616693066517922502", + "13365055082376018935548592209736650793571432301565333849832704476237136996118", + "12420692692663472732723794387493491467146971343430969855362995088112286983728", + "17844493444787722109223680249951335211927705850925719012603471955916755628715", + "11924944537382281343613401541176014853361783437608625866798343540969055542140", + "11538333989403053525558050588509973706031711021450280471202165095037142068477", + "18764881783775503232423409005005138632447539466481045749407270569758537393398", + "883162740610443285913150132085694648635519837269866659825930623017971541006", + "10171610390436513861069093903522467940290613868370765425375996220687957422386", + "3782996878040749700177878799895260702330488343333700969514291046490113056911", + "2239298968343190621001798397134314102896115970272480212453695048745962826317", + "8391302051404015178833807081994898536482484463022532956140929200199667853077", + "18096164030411129129794470313655227930799185448547884977984818798794318221345", + "14613424916575212836238688482051323957035101724778773648452919665593660234946", + "21828842930708391060823304507355770719075227415675853536112390374449711667078", + "16155169369395425892836659730715232159396910561858841286007416017756724971516", + "6878543234699575736262375462702866780772252779651204219697567141737826275015", + "6515580357485419559928692633356133128461299871887195169242750902403019378404", + "3124270150531482035695013416051086970123461406854560487654035672981879561715", + "10574698281815682641771693421115734396172155905702803455639975437772362072107", + "16383053320907906491543185419013182425229207444535577699313377451249772804551", + "14128602818455692582904557734609240140547347183347545360942329621911511704432", + "5010441108136365108046592406551529565385874487432431586303802326844966531017", + "20105162467673383983751690623903949493335895059047083944279780871549558030082", + "4243524359837792598965046978953117623831146922720880550516255054678204683627", + "13992943040313469850370025986460260524555943628212884466234808367632688288666", + "1356223459509978352432345061666791804860823828908318470584528377749655994659", + "18723939192823222870283999271398340959482157197116460943968917507878394278385", + "20636603031793247786862933945150382157149390912061952007323929980504212222032", + "2128636310217902240014588202116301452804441854659460379597999718252532068242", + "8892458127594737495267008183431736534386964959413452688074230438040504386190", + "11063574691797903196312547816101464620325790434381460261930676459857164737407", + "6204567085759054728130072959301910137692879386674785283047171198023995380593", + "801138962072186678791979363551434811728526970692377261272697294596118718403", + "12941641588837981062781447688458784558606932072335320068724403816548547438675", + "1341967686547656677059175660053506857201298028878352128737987225407679621418", + "11183207447266717668611438117401741351804062326702937903525908319309155043524", + "18507917680426998547390822933408085624123062783706353845158832700169805897057", + "17600956758590554476691669608345000941959084040124470813766683278150335958657", + "483394656899715242498310209793599026850663551066988094136788591643990355221", + "10090827432001200203094010206359607863114405903353400294360808239534902160627", + "10039403020393871677252553778177538625214464462052976366251977746523421324197", + "1924712848258707645798108847678093621275398467857399987976700805356929290674", + "2167923809975069342404582706442301809782773089534518796665536897555762931998", + "17151337466618795643803903749019481111634005237742283518609358703199930418386", + "4693404918498880046514671012411195415185134287680728307646808116832211396470", + "4839381186041991439202094011010050618021148651243083039175474529769659591768", + "6368336559448535344096548979708211013055912139451892726345028976601375052582", + "1969826462127986113899121152613324197819709993940588880171966228829834184618", + "10949071784553227115382687140318153702912633728202525040498248461299029092720", + "14575837988956266060370685891330921140979730144767405243152428616625585430364", + "19025750374860322288379311751788635906611708349625974065695544669443137256255", + "8764838805814072278932908632105809429351124473407135322177352737762640822858", + "9508966145895699172941574119083482387827406291145211775810415653335356563307", + "1499853062814132179278896768948580432185360181707926223121987796423000161587", + "6748895423937754323870965728722563272366114856617067613970193226138372983547", + "9814536604142267489967579335173323116472525708739068285104384748406479290252", + "18898551978119284539473648820403307265793266184829523824179430658011490726286", + "13332891294455702171134399009213907075208764727059236543259960129130320074982", + "5806223588823614408610900761814632517803554600662169829453323047489266087405", + "8600992771060584427141790133616651912076186875693672268259185811464115092188", + "2345229032656719135417406942532115442859563039531904661440827899574284256939", + "3389225209359511603097670682835778418062727662626674404140526378611375444627", + "4001034682434006987902788179906586121095610380401857294704332109514659937637", + "13067063794999809131589470979742146742674772176859338210659998590141023560073", + "6021149641876662294720012642501582990883570287510418620500730995186581678990", + "19744062936660555217694711756418639275110313106982456779211457821487189038259", + "4280146833782122185864346406516267200729946534205141990397627632307276252746", + "461380210352497258096433274264553487092816458993207056498455795007735775326", + "9185904966475190600688426658264723956468287249470549941528026771035049026029", + "14362213733702712122544208087232387435453213772974406963783416068708716342186", + "3376133122839395926880247692021311631985267298172444834121553500975764354590", + "15430636928872496244042298024957854104479533547499980110816885404855993587926", + "10066678820787654667318707571747478964216410231002799394324814978870300697084", + "13679726663800139966289976616317518761749708307387522908283736372782010246091", + "14452498713182028708607687209189129972318024710956800636750706657647839477841", + "4665977443169087186016514814881141858109411469723890078365751966690257156734", + "20673947333628935106924449469424554002781432367358546714606202194020160454893", + "8371937803667063739943392730412639954872585103736021824547232324141910768530", + "4915642891224254203187535229956007243890165147322145197624420790022741880987", + "17737894427474715513464524059598818432164911920893615068415915800842574129123", + "21257751286440468433003251296883236318376753833102432675015194790356307947025", + "11831044910484530710733100158936923549935832556815349526083442021617595246222", + "6362553671302097483758288321671356788227750730995074141338726755028234269753", + "14935971406087488699083625022278942628959200513244202282294495763807242526556", + "15337281352851760799121170871115742187805714822038335053341937333897883894609", + "11913020597445264316110942608419581486071629740611054234626537590474050938474", + "1838104841451540707239841220222372684744879960533787766800248740162155353463", + "12200586110950172266226854658183951607066336178441918240404611233042731023944", + "19022187774400544497696484163376638844589760125070799280660843859892595613745", + "20574219232807646025576258258887918895888511977194786423008507430088227970769", + "16786939539299167614623107235117810634408742454195124075763115939760135086779", + "17293110551721019748567259052064171353712124247596873005371071440209356535788", + "3515336831584582569659665150343000056186787768627351931657274076732504326799", + "281425181255898711371985357453012506419817930424882315501470435479210893648", + "12703632225346671660598644961986122293501524448004046573092229475506828991982", + "5589259102510829346047060861494427710966525350274659545392525201677772096380", + "16136155264962057601674035851031539132207654054954939479993843931937787809008", + "6577810342827559587628942717478127045802616172145914985109615391025970118111", + "21163359977438641650165237544426961690819476598192610702649950529921750524674", + "20111680006656824813833529237249851680178501017371275377838319189183351852836", + "5575615948519348213741695691025005079345478514823636124237182535409145360452", + "5523844323060926872300411712160500242292621601876678751811292002367512597282", + "2341781247790586645587607723874752210647545627417738353111026163429313125329", + "6287726121450538785847268284295299220404766172163996260292255850387839422364", + "7510648235102286772333427364079209113816404370140168905255311267436378725194", + "5010598474035699846534727472507206499428871255705467310762876793528248455251", + "5700900579731011250748267725185425351062102227041063523439252105791254824347", + "260864497479086430816275699586473497971665832148788274905912958670548982986", + "789172560806161139006535038197594038879692565830598745604163919464033838458", + "8100205286609553283468855217617464988341806863414698413875768149192804914582", + "8832923720458983936387697888604823914542155000760959167380458346468815941677", + "16408716740290439442219096309603613006830415688534254451854294128560591562449", + "1113287770802944863484309232492725673552460311886837538428140710794437752025", + "6321319205351235108330320300049882062942831936257890309503365991814791003082", + "18560922321672258596191688202021423732168869299915307979899958586924038420673", + "5189180998718572459539043290302679421565915950138990634526371489122263353689", + "21059088895950142731136323564720383671175925416797251043903234558355128937274", + "1758538453070760465213341124871446098960577889968251388551632606564361922354", + "8880106936008308891697048583874212287093823544613405494775675377028797786924", + "478113991762053058066048308336221551443374933771727184756201865704929054005", + "5364027562586142278013801938849728210749918309705526248720112356632858758221", + "16060418035757996187280816105829974246762721308664137780198420107462005497738", + "5211176330971465153824216149376981285606706263050338982115099875409328929990", + "10368007546243825628853187421535635929255384415227635572356066208437184727920", + "11427684168459596442778160390157786957054817508127224050241732612892526145073", + "18949700777601180659579150208396846544227381511915556752893455904600581402034", + "7164876529024763696100437397763470424696261670104386888341279210050743231742", + "11672893202716354350665939605724566655297144836947407230492353459173601370992", + "3909710158111676258105703555994764863745260060728195069107924779522169147903", + "11345007057187618276350650167405783488453459256756441881422153715183450077456", + "16177820285119478894470005926103825065477859050875380004945625842748711696023", + "14605787997931757094644238801452595674783310674883843033832086542610678460710", + "8394271758834499906719701243007064560791869846306715272114566194508664069904", + "21873434283102736245937985212570921154785039540401524103800440576512828034687", + "5288693737755970825903341313388382553570352630888453317445067182115774973600", + "21527468641133089700262079461319550104586512157305923587305104390806741900810", + "20906707665998407830558869509347677772600060055286961495556134628147066213175", + "12592377229521260472205372896527756013482774884841906928184642093380092134349", + "12910817347876296654185371831758099341950752668436921762585644650086702363745", + "19707023277054651202681442144426045379962145417802880101371794305270012080065", + "5370501568233094600905283140114160955385907390235753857797923590879732764547", + "4893525410028747500809250162311718717246192757157339358355279414251440821369", + "16933412769230696712087772684638356779344465843185712014276708361284654443016", + "14752252971762751211424871408209457845535321896357466404820315342528474475265", + "4381439374316418237204510331759065345075613480172639747277311241150560517970", + "18983302468109324889867853797358234303151484569217755559414917991855202421044", + "1720853637877748574561923069656227402993841601703014320322493127808042278354", + "18055674086054269831422192610284879350534759472987936156359168085840018213729", + "10022985435599924530950214242280707656465340100733875531414649379326713485700", + "19489289237398317489155396973411927208825921597467958674111652983018260376467", + "2483969152386321683533021854216836603663579724438750369420802837936459253247", + "7096495647820661985493920714559517131838710818830485847136203068929085317377", + "11239734991193991562092142618442979132467959527040707676033760692044595660477", + "13964689425465688308583323493745924709191744803740806305195114536185949582431", + "16765583167493792488003846249322412563932361325466895848165782639635509252698", + "9258699025821383775657130802030622351199780460158138938453769223645462201380", + "7261743362549790776698831974802523106349906457536556119083744106556343806039", + "12174595715224479314914773177369125156875875973781826642881907250708051936707", + "5012109434360646519756574765661145101042498088812514145017131935992653466441", + "4700224901573028234520633038757562359543812027452948633531739331172070339917", + "5524276305760203869547781419982228286567487292869238897177777816144717638402", + "17095081617164642751160867282346616888601375485714887290060713493809720024377", + "13598351826191942256399407492736185075239065075291047608049023984461266141081", + "17308136644455825036504551600136601372687794695239587014243640872267542984788", + "21730115551932424945388178787861491256026727823117641122401508483473017057216", + "5949879858200957394974414439063213931632579243992797429658708423363937192395", + "12803563373135008849577150591968062878999687541475931973931310610530915756774", + "6197313147432278847012935526443261694774265540021863115632496255704682355001", + "7028974783929794816060380650452873559926928309476428464943277081506765913122", + "4456589108827474318002287720505015907920793224049488679883557016295128273089", + "6100631380538490803600079371988603006023173019183128962118450543769518343550", + "20177888580254527519697427670797609876974132471313961205784103868094735572841", + "12073238358529645427878918495905605836088922482621503239707831646933770345610", + "21856100081180867115422179532992210187999850914786148325024530634866939637660", + "12466984235357303385071417632152615871825808908890287534009467031177378240877", + "21079839629488810269856967110699842674750899640941851601070908540874579869846", + "17874776547285892135748821889881943162098691701279199858985484844123833691703", + "1051310306073108765618056817409777616413695911463346454738615844650030082984", + "13201928900083471438265807470373218252839081683324776881761206414378175945385", + "10190119257010511211543154766347424644241552289850760573785222226686792626364", + "4688333485355359912766058497543573340825697092661609637162867722200016624599", + "17123765906835174464437643379916457993692257252235548439872663365295598699138", + "17134395901615480964750415429455088485278477136528521479550104193427850309670", + "17940167838269540610857156370913968688439335297994033024607831012271846654207", + "17211561414938780959438190369003172382648707317417933395371238331823656001285", + "16098871745352156970746489901084198750790375003919623885201193800062775011577", + "8110023901773238127710250492301336333009382423651067999842110155158638763822", + "2422126936887420595943345532312996428472865746519279257114791700143799243285", + "10404075183720133448708607844137643895914849868988851944394714021532771347787", + "15058460651933569411128043696753704901683095219148372416995175617698935925786", + "12723555146577954839266714779606964953587444931552430067661560518513484316738", + "5711657581065990142612711893585463369566607394626940266394184697079173482347", + "536257458004844094090200378771107142207864145916201798362001449936167413480", + "16904097338561724274235483260490381087656469057354151613747001642006805270380", + "5885951988825802528717928331501311151617037953699202264796404860914064325522", + "1638374036867203230684496314040773233229976276864977417441853704948079389600", + "12231982678049991867660964493898657015614690001085229646831798098083877705620", + "15427058040900308626278462505422407030810836290013997730209614367042825008816", + "8287021582226423932765127558352821849475385690410389396789209560743363809623", + "1082920048469231434960727235146664410555571531264546962637788898495946680112", + "12010985569823806398477426440616395624594456642130882649075614403521532359981", + "1296330560506042268524168200578272313263311415565810607634491211599523487141", + "13952994457760775342452243315330031022436205430079469228726234965837656704462", + "16985743548885988875181345488907967382109958231690477723834234297604193256890", + "18108965571078287240054765173491615799731924163046353815744437352944165909003", + "767530387704511846542067425779936904920481384673999726390798572580115466105", + "13028307645272598127894992332463015335938472044187733546491605798735426870634", + "7618140648066747223455845067208730203353985191563779819124185938438241329003", + "9331096408013670718143367691921833484100954507332698681105640867440874379095", + "18801244145798024592964955007270988394935375648640247134868164095028295260248", + "3795459656522732094044639001176736675357207553263371458375866920248726997086", + "837603424793724140470838171288512417218344963365692836088493443407197621528", + "5831962429370013799978251611601242499647911352287066127245255213245836916243", + "15608629818832866779793800587027419050315344573115273720682841552596629193016", + "1032806196152635164955947370516112750756471391006183599859124038948506281039", + "15696208732714216759620197747787990886188845785832889089576996959760026394301", + "7234583861372635173862436546128104054595325252312930586077823639855585372879", + "9351673222157837029929340587547626334173727344379333167413124993232252858910", + "9944025695384518607461067522154717153892207999526158631473359210163311199905", + "2388456762928315498284690717786606118379553818175612967295585468860359388241", + "8762894023280585818322989976434413098781364467068028456427186735232945062041", + "3982990592887978322236356963193527056120817698005386105106663667178275102339", + "15305580827228104504176089928458181616132990558748682555478070517971443343847", + "19329984412710376451608868772829323267202139442902035837598118892290482040500", + "16858720166203416564086563387111926247316950607110418622245689767918477559853", + "18241191751089003023961171787624834962373436019307905123520202159673396473314", + "4235718345809909430818933555490446039661741163040234575769526351800272747420", + "19336790216312697816897624746946849815032853517670504993859895524588720552146", + "5833407345693372936993877824926303626341316298366978224289402960052763586750", + "1246029211601050773151495439169819001283925818386531745942667315981536442422", + "15220086581176169769190817430254624676596374261069714496878816359393753960898", + "2365861900029834138601812494207710261363070950725532631535675557556177296586", + "11098939996500166020441603457997996414027684936264124308565274744828953147961", + "10188381420146390141161084654671015083379584925005132911919536024814384766817", + "4842017639583023386245773507388835267437650777401251470903076835565728725044", + "2473142702350888708640145449504461869356502285425231033191892981515210840933", + "3420689338060147901034254227618353913575873772273264928595432321920193621195", + "17283074258384994096612354354366039962078336488852681769982384814157770475001", + "21006196209784588036771700052971738964442512933084691689593009338899209077510", + "18493924441589584713280363204662484496255740376612784432044649586411869984552", + "18050342602178255577111253065667811034294368946566845729063425104069824407598", + "3678841436690736561960339537207788725774745841190361109044779271967419319179", + "7229032436003943468306271354423246592397907094971085338195794532946516086746", + "6074543407778673702938004547803408348112315194664133731027958879041743082105", + "20066840392762178519205386382693042859978931394221116082945207576871299356172", + "7392805501563984757154630589230967797392843859128971344647830779113836888313", + "12821992933916071670969878929371658252743368422895791718047075564423909142719", + "17668945380988406358182777882021562738916627352451654449781528301527115094220", + "11046977583853814151360987998476416244370762366051844251247672138778958746421", + "14002539550635338734484852844372585460419306414704948112807545053902011590061", + "6606166606298514036368338857611676551502463523513184670555953533985389963443", + "5446061818493011809543734117506203755719984305445968514525154959975352640311", + "1111668700238284277732413088411620500087954609586256178652323895010927817926", + "14149444834268176201304046807022441432518948094788616136522350328948362984806", + "11145589672768136991149928307436511325152474561287303838823870178226307903252", + "10735913608001838681702607049464645919999926391398426712273901727922068271016", + "21604506935173200936527132172076877500255612688310066661071635553990139487164", + "15839861321575777225084554553361389979179161218821903561853968278717519007256", + "4119757287481158134674144154651639875410119871143992982693366998676854632059", + "21215853876753845584435912754954004476252409742434691973232278809063267703513", + "6474112081529376586123095858950370103982143188298415134572081362206457634840", + "9652012486829416433331453480518336590399776148133427672040049807230079809918", + "15197188648271522366095502604900923401004235531330211517976306351199244466155", + "20653133196783868133525869637730316323485671920172550159454294692576016995406", + "5372382735415117884334788700701478755716540017619541377211078296573383620529", + "13207488856826369213621681118482759531513035177166234417284384049787214456366", + "9796874023172008376751307392327706745295558991768659529793122654595100420492", + "9923897870015737084413253850361127061692788482930375413255676958440886663490", + "20345695890485642585927456012849850866425595188379347054737827588286474285546", + "5970363931303429796131779726714445177344307738485323531135738898520018230730", + "11099832493605970375935052305980301651220466371839450598182845139430995541342", + "3984084259485258180085200952180050171170098877962423607492480207306084313759", + "14795772399510400766677766636479617776597353783628606580152075755214440944984", + "12411649494499018184602448007465543838785542392797557004957418343044512599298", + "16626394877225947777126043865023393002546807148362691512796749460115071596438", + "19056586788697785123807358806982924302021705802715675015657752728604200366151", + "4434453689868848221439283056960830923558217744100875597646474729523600908085", + "1558497076513757145941997689136111603376893591128645477322800957597462648163", + "19855312204158524284458047353907697917092904349885231039917644721609392348549", + "14296610840588714282305996857377052531476403374009704740496611471123591762688", + "4255702919775232787836264045412364184259731261431115977947396605020618214051", + "11933778709913818332041068411271656949772987367132405709674023277986614336789", + "17039721891026771364303633162265090865698196106396237296534502784700270974342", + "17677496806049413057414389850646660368467945835594987026827101037177591654389", + "870853693457310346529819971157542197768087609061398183617507613118675541833", + "18050709179661944189572532609164386192084249162773126990656248077533050260547", + "12509892200475964347152748879404468874956023169504780638942472032501158691943", + "21290469710465436947459252705329759527038443945558987181251205171276803887919", + "11697948989002333965655987240828667368730889495575710652080571936823918605037", + "8736925166569662118997397920658489847012314542439353474077461070832299486573", + "15840706598368315931966990863663232282633707371020640698077565307266653295218", + "2336556619780315190172318504866216965057876522664932909981631800248874766723", + "3401386693708589750154291055903211427275255738949699968058464589080271508279", + "2985234144487193053560662812519604867522985647272968908108472491424983944847", + "4708870044417079288579972819522537183137753616055169595344370650267781985451", + "14922125937386550210569417108464732470846637538443952204254988396764974305556", + "21120247382250540544939703044577215089934389719849213503558017786742393072448", + "2316928219829771701890891437885406935408196453098584469128414556890718887397", + "6617231706636202699892427347447707304283374531129890683057423079735568483257", + "8090371166463272188656489380541987815472988984318384965972491395697937697839", + "15553944362864127106455863696894507934518908899701997904436263710171474812555", + "13969363969212770297664533823398202808376213044430873505018872984372178723135", + "12323663899898371993768411530695190245261583246438004058508426675150745422627", + "16566875439359417854141203050455423662432117146587478868034846190942245403701", + "2085197407488705803446596939115235993629808393250087488250514287409223581616", + "3353163139852271259915937989527822937141797275129253304946488349312180510876", + "12046433326685614994434242499247496702084910418498830651483913063626259071609", + "14532850787151304296314608701306827699237853461438425922459530673831764439726", + "1248026383149472348014634990743396120794718445584945878307644249641199424325", + "8057577408830637105543973735712037717868264099646638037329061041095961932706", + "483318248530006664922961695058460281946793595997210254862751288308569479187", + "7597115662868588435202039449596982590007976221501668730777779068799655592123", + "20576588045443131888269350588198092656950342341724106762422448997215666028156", + "1519447772833782137513870714676314005111021965782273965574223992106447841589", + "4564008647036378562957325007261730909909040333020122431007246472496053026801", + "20058604285923748821870861850977236730661592466138938301165874489677433369725", + "7444135345158592582936132362592519965410456021574373937334236087846829245911", + "2619748000882337075816263795535027848943303678661676628903586805250445194179", + "16416961968137667392446268131224700809614764673462182053193218154672694280838", + "11133666903754924307114887742321725054257211678992195923482419349863465176820", + "18185607668620497969429683073093259678523061297809671850245875139394070019601", + "21100277417778348195596070047101971906596351310455360764416675735903158404363", + "19242696527172349562445561603976204919773321047775511674132373901693664836452", + "17641599298698108341739444819055281386405281781490793437098263180756526877861", + "3867854105746657505538112984381791148258872175005817045777653312620913799165", + "13673929044343556285939637616797217757029992003977067790150536754605693560092", + "1413616068844936743999269099411394796998593026467324669628149625629672193659", + "17182247321635044788965517464433108044764735758956097220834338897775232927292", + "20369403412607032840251975012855741679474191692713337906759271231600952698270", + "3368021137971787382116663997287877185273428758197831072626886100000321343009", + "4058882993633327642712241699549337354407965426094339469485649412411970892252", + "16745738472558294852806571713586420783108275141805815749526145532281107769806", + "15581910313143599067008775890965264886765882549739040775187928885624726340064", + "18274240790635707268508029122006654280996256674828118427269328392146795445159", + "20233611060321484677112349983942832171860822463387331585688221516502467246411", + "9578794697709092641086862842633192064390680557390699768090648655701242260988", + "2852999879330450264744202807076238853213578558186322493583347517686662265691", + "10741465223456896033565854764716115079295016404042872162238148987621341827594", + "1287682441013790555075762279132093640356858755666376578858076731440259199594", + "2754348824392215081276565702993043505186786457070961060283939332925679390910", + "11228376276796390660691597656113154929593350154734497204665175991737933493193", + "3311568804745845654494413785473574358224291288129470978720234105131364795989", + "13909323234259677397453192122165186938471237491769110758142097116703508140857", + "16483726454860443352311561107200491233365512138292033674515348800113826861405", + "9812016698744294015002456058504358834959473085649393839832264042031387688048", + "15177982625372891489410897122382147270421146795964397363826406495667593673282", + "7519222254582187388553575935727848524972644886624855076224718541180666103816", + "8301393036521557606902463799168007719863879180990998631543603658427343679902", + "12294426767414989378173056202553334768408199886443392931845979330633289559578", + "6739869506728400044391551988623920236526787529007157619233939477743084008138", + "20069013437903411974438964103206724858007754851513756279456345172264534005155", + "2018834934981362435424318060070780595968114685660751661802811774334240763265", + "9403754976005906248280231838299490163038233707814936394634595857779528197369", + "10417979303761863711608825801177810340012849177958033395021985945544086842565", + "19604977682052360618680964990952175014659495782934855476845092758883928933035", + "2688418347826037364876739676610309530415711729219616027453603183128414252617", + "19429655781202385023906019671461539686080096105536288714975255299583443223945", + "21850552082723421744247018249567438591533518178485575560955887428084379635362", + "5617579950805240697000723938792084314286755653766678432489519653418680110671", + "9555670299297105865403034351288164766308791892391888668326102655956067266166", + "11495978801695516791606532228534536491719831441531647897999875880662918322774", + "13446240540987488159401062110915298027617715146895881354765875946092155869706", + "7830137135198409572038952404379496414181899227574329472045729197166487746395", + "9556039398803760338966955211123892534270670688829455928022562515996727400498", + "16536848921900120010270774925810022257466132951671306792158048247185847693295", + "14984883995702583492812943326719347992462648669814085270913355351041523830317", + "4449876409800178139287642958372279285293173800225271693328528758341654973266", + "7254404965814435397385340781449208987509307430022649779312503908065673947649", + "13482540945483896073831058930261724641277618328981766008722675404404347005009", + "8516464109813705017846673381161730340458008814885800650922822516855928737104", + "1123466447164703934328741246642448511199513706388932930170273400845584259512", + "3549658554379334242628536166107543119171319090515117171924404150785175614651", + "20050236656917680705173407104625075641049186986084540346810909246977321174260", + "4328179367990709150244102496965604541058014472966567986200766436334838205729", + "6523960484836646945629638483047198947455910731228954877131634689551005111678", + "840118507410487418552201986292140077062095264830249777356756239948365951505", + "9443485141542020716795178817254625558092596484849541439794346219916656780924", + "17517352113160950398700094980635300524550125065948318365273967900505416951637", + "3558475678500267728084169977682358432036940763329037251571517902892359350060", + "21782716253294940222641143651977099452381765047328512527623130468565364299843", + "5143600969269719315523014195013433247337519583796238248783742413790148311283", + "6937766932051718809641611511685721559694403467477888704545484121202675152929", + "10248511959409536383597760784458127936162061804351541810962726000050652155800", + "15584432188633520588792022977848834701354218910295071102828776215462904575092", + "16507813940325077551530806573175229924530686412372461197571308720671287449933", + "11975780799589036547299759597975704141323969091612997515060812893050562154894", + "2743143635303404960353780118079778350398309245035425997562746911685628540405", + "7772317651868959663361913464992926253491072635393117289337616002993612374782", + "2337519377771760840047685336751334655527732210909930380948789294151305059244", + "3616132500009609097754462360087761508553832847286785396472146776175233605533", + "16840763079152094997371697832566620838881567041180683145570810083272689914599", + "7660270111053834825889064071065415593147131275236956990950685283090480450184", + "20762611535627684584459315491847035257226740939101231132494468375819869523509", + "11042134071344142565200223013849882651956544247603198913312251335786248573941", + "1940414570916441943786503544322754456611678071701943345167834901237861815275", + "6726537219661359488596406444614225094882184675245355532878420949418407567309", + "13510096963519324808490741235975735288120771162631019837713476375721562862601", + "8622373819062603720895891234592129397052930901106318032071934400492518897707", + "17024729268332782126190150627072810510918979523772816301096433100167961982933", + "5587788522776741050743071285424100117576582212221222382824387609679897172811", + "12388474196602817840033287553175313493977358457560707192943603355479290048023", + "17628897462697931613283808657115398230150168968257584770836553363771557372104", + "14065018459890202233327759187460200256630019200670511082974394473420421694399", + "6104554150616525884927725557698817344144889522156367737688769378902924558634", + "10992664178398974993431947901729345793474931192615930318284939994040095999242", + "10969415364246753478074755753965479915045305330246521543663858633748866143603", + "1272956806332000540144317124032834499397723122931270496707464205344164793800", + "20231387145101186513751955054188659753720260135170026275339142463242692015712", + "13619345053559735757194629501334040876412094886252831860426351060385674007066", + "15232357465863047438256672278087920688704670526832495687820763524016520668185", + "398623536003273726220923501915643032345957170167431833298597360746684401150", + "7028250324917735970960317543369047790628992348836498053408852879988541935724", + "9906086796055827169135574765108677877733074061825927799032011921038506005373", + "12788056119136131846722253277450922603309904756259034915496788214657808474919", + "7651468821535297879836173382809170069017662802420683445433781610638854359553", + "783979512383179545035255083828374733462990002107251717097983319441159249905", + "8836457559777657917879541925866800908082149812458375523311279635635800064090", + "20144830911959238621340309337187081519944136818938895654338696747143855879203", + "6287181334311526693967500402732058645490470512478517679915786729496945390849", + "6578356433253304325301622313818326592018737710185433748824597250242226401957", + "7102838428799453179757741468552077315750505008228901893660362447987283707998", + "497820320926977947430162841325159818293818758853362781494155076005984229339", + "18160985608878840530944322708383120179552980243808985184923313895763861919637", + "13151612592624659285402557051397602661028781656351284997191435582230535568502", + "15780018535650817819214066689149640940245062275718550628416824107970066217991", + "20500218068755784102392504532520873969817687889455201735073738515957629646567", + "20888440956803193084788282136946318522770638138510716565517310266646640263708", + "15971618258529607882347998799169510722902529521092131094419433339204085498306", + "5293253007777884257810026986010586149314566166761521493811000336947093325852", + "11581497334099661755434309930999056492294889088823870215073663090290932016658", + "741799820113052677363038461398206547514107871854645629573511776865766479899", + "10132388802463968927185535273034278687094388163001210029432141519239006715163", + "9843127774781963908073388692741045845788815542021009420639678295559452765837", + "3807159091255176851037359514650776155078897682364934585802230436085278410633", + "6904224411392552889784909729922790171388456358295280077234569059475136886573", + "13434461271014860123956911179522881298853645897065359788401689426904150525428", + "12249944872255100022137726834287337969948262832177273992983186014370317983705", + "1080721436007830510323005297307130991703868335217850548555329006531295547187", + "20651452101643029862567543402727347872461377965899451538964773059299839803923", + "8902465483627533643127758766065785114055357770410218037441501029224645377694", + "17049203022284218301986830675304595858941437294636126244507847280421482250090", + "20389632733862984292955678575857173748595770609738193230360968914580393387194", + "2628553647311400613862969480533814764766703257834731382409245421165389763845", + "7113709286888238485244587172405174762331565517345483851543315956528264191390", + "21099219272139712501072896978936944264265953306877942445724732180674664269749", + "6196322429352603190460567397253889100476004214002852031233332452859780069595", + "8042719870001485176157425681827371545772290235839006373715151688719449512739", + "9109639854367609940132679558323551490721386060080938849664245212200893608513", + "20820097690315610819796383305043844702226083970185755192990081897048903995104", + "12474580713368057036057537615251118300654026141156267907684197425413737128906", + "4935701571378317615941939703137616736056472417588666676180454398865436202845", + "11284674041151323919882446027842742890041719161934493530658741778909469887490", + "10784274648620717180750449830334448050402020683452763192417352842268830091773", + "7221763500960709043106304906102942899855402476011030187016839441411503790134", + "21488573117797078819915291564603086854092528370673732237937703606783710747214", + "10368071846917543663295415889847910771342838450725996382692227125006143332582", + "7189971404170280285101021558651838178138415948558212834138150417852815291774", + "3622212247952623996680563730554546964978269732772060499110124228651174919309", + "4030261128316895271453652515658449745835613736325708089700420629607904984695", + "17917906020983172293093657775725231153822157764639429745264576611859524349990", + "7911943088433991265213669750138013059877768768674246542956611065207230165349", + "1237568201834766592374215633649772138875843120827112365624718270590862573855", + "3421335729151995408099494011827853754192179969298834320585486465552812589072", + "3983646153441614758977318714985842223356917025653336289250912061318144657309", + "11832501153279122995870584183546009144756312133876131903016034834496067810803", + "9642188977879773189558548598547584337663289943484049782260608599281971392829", + "17650631071515565052542834438104124084088724345481542312112093611903334892790", + "11727835841766435406263157822186528758001431234638936084294421464770985535272", + "4170985922866379778894675975982515174956777531610306941710536931798155932713", + "1904373901352853963433078121273558128713374926256562753718137883724136086536", + "20310106358538316988108567646970248789216107374650929380709349367764652307148", + "10755444294914746017826019531774644079659674520334374055753829758342372728308", + "21544150011748410796331015950886967191957797301800349750025078804307553228922", + "4791178272915634809747385261951447492552726497734131770971381971399880792451", + "21569911489317541997628510847711750299218484102264583013015022430206018097370", + "469129421958624587885163292532024178451414238938436764179429972880411747423", + "17178941568791193515971789991206572943108926053917999875254005411851993177716", + "10061493654971891913017572469769124056153763544567322059270144080123216592759", + "20809728888234709563099636671010468945654173909532666727272823672496239666493", + "13977154471274089663104412732631175732414148541501676926172952880637640768097", + "17159586220373273602393948660742324700378108883620196368910126875831365156676", + "4073500643742253412297370658761396932893392648826435730598979045413705716932", + "11493572320701213886088466735117685416116051720844612287863434228749232164029", + "20892362924238047571943713393658663337479326222564740924788211586758905169271", + "11301788649359616808987952401363629750830090004562424782094282760076674679757", + "5346827191385082048034578496490044229132766705726383391388695220005635473547", + "2201675309086116923924279562154152714268152257293936509978158351509848697027", + "17917826577478806664624583743441880347892085028222113450954395295616044284297", + "16998236939959525187887324821682683276592762127156629849525502305788028877451", + "4214391906402965555572230230924694640705243512659675242003943183722900957472", + "2247036556360539125313249919915213027481437766291950011195105972974928736865", + "9520057428546863347057860284023378728609842187323632161028631359500291204227", + "1828736612590741052604266070553266072363175828159047049110641125134408628099", + "18248580838532636162005188449054546706775958322552749296747411333541139803294", + "4834174634635006984667037697276171446677972706028785635301769650621697899268", + "489734793278913821067993383657373029590536750171301785048262915525334613888", + "20786356799510136039282916235428434827695977220581899018592633397684279594261", + "13145806261364716646031477788186247592609087183869974953048604633861380872823", + "3841420600164259772367211721293848988818647274121301867288590071853319905062", + "6882597113948243507685423253840830929482565588666222909972451758645869849656", + "18980943240416553858613600850285792319483905785062366023956546569017873987439", + "29028675420319059649770139934414847448931804950673695214332234580922091102", + "21775882073646833834016763184296217506960385126491507726769771477412306185790", + "21300410381086030882514625793664995316371660662503398686828472860934288345599", + "2472267536757789817746114566761390885333410401690592727164834695142144448445", + "9723880451630142367622302246353057781908243406397604481250639516011253801606", + "15313110769360181254868962440810453125247644871877064008660684649744823174300", + "20564192458187565149788272882595180521577855436987831222746293278781628416968", + "14590472970343049442795311527863765295112518097070620865372037933484660359574", + "17062841617700893799013325463153088719125540677385032193714935878731444635251", + "20200256413643364477818739581045874029732018697196252518589192345293202187361", + "4016942840923529049014357120480338974430876995693135820048221911362323816443", + "6537662900592412058939229458410964809681215707806173558553209350444735100542", + "18297886978176179234700005359672839811634650525194200352748308964228483504767", + "19662609243585750899960608765774063353658909677643282385185291970742157305960", + "10221731387204953046960523786878755517637874808703460048675288088446619398011", + "6359039963669314770815970957437041104980907769543152198211817790059980604175", + "12215821248736109157702052060566873526833550744650237478543432061962195037956", + "5935095671961849761918916493742868190941272209470285856382557274883130995501", + "4892952774759542491697203073862387895082272084344610213076129801679488176656", + "21229502683877666320987735203037721130704543292725579308736529207275811435612", + "17138707315635877064706472595294259087755028865414511496916808945331553578413", + "3064016632497858706665392042657089766401657752838410773325693629079632182414", + "10697546089228176531454125219656376659283555269092429548580998097316823780605", + "6383154332658396326745467904696030415502846256075951666613157084114882351628", + "15399057406934719483674477103950525850935787593222687850952111073065008222381", + "14222385230562109850723354194041721683341002968759594278414075630295191868997", + "4196055868873306164684774878036056827685013979028843775890547079994909550345", + "16495894724764636941708778916623546529990869478284583127943604032121307478877", + "16662872698072302151631604897467020278898785941962087730337008570271651661242", + "11474333637424192324648146148268767361869344542358721131204304533867910403346", + "12663733223747936066515430496850730759882234287115648507227477484368789774494", + "3278587449214209390499027872507714198695688159471276082957385919697290643338", + "19852281910336140665278848076045642033038867843956224684297984081107396007913", + "18119827693896204780441288678080897397841525584683046254519435612134656396725", + "3004914531848687259880590983112983929408762744461212501319599913078181196369", + "2744805266681634195002030539778050424843533661368265264531107280758966836574", + "12003425394929552979405575446174684244823087440054609540505332749485291543559", + "20899617298205776433505248799032591564097087832964850292073852661486959326400", + "18977355049070793830198782732221581680757588074059967640900502934882545605494", + "7774492678097446887356009465076901979542998304721485056654902284935411555748", + "6257979871454846095389581530427518297635929206624687597975380788548536091169", + "4200362970271656646831473686822497724241729322168501270846014047124987169639", + "9626555465339955137152373316361342798468143010897667050837558740100066479490", + "20599104767749131194316802432182162807569082153791773541255180345102031538229", + "263120164166236772856966508954885356163375871249262527569221312470198863328", + "14408635566922092915650756800314844499933046455992065014832556500390343919586", + "5183079049039149593555827045385122348972389627856098826226304101776790536649", + "9079663711634286747144802677527389482368428094888913737002695282424348199029", + "1730279784246934965982679327290069428428752739692892522465325889599043152257", + "19417355737429211343570897838176489391485942830255040228180844510907794134979", + "4688867852231614618639054238240140493597889866886829462162366294959927124611", + "9750344066229503722317800976404611879233170071789439067052923419204148303262", + "3998708829439769920633774213209304480109842139515896658207686741134489049438", + "21119879197425450902002580651023270363969638681856902851675391960298938692105", + "21081985423839377197813891376664505429739140831959287968556239547809632016084", + "2194830658993798006279037291585161216930179214066933009151541946587699367176", + "10072059773309529038163761158232080490764157196414798621723969165341603556957", + "12893922413945288356816723514764026149994443040839144958812668568890693761688", + "6289043885924508250801248626630354568142964747989700154772682955247795752635", + "7117847768519054206399668810694592559179837439189798231517777441723405148708", + "12250493458894268361669347775761796626700321541112455611623342396141245275173", + "7536889841233679602786965527644616779647633163370923759512908958092882343888", + "13743804807205248364239225685151790362682679734025326476864976250180416573459", + "15326756679387284645144696476114707028416084124671428061976908306742987298792", + "14138908940825881278249529554167499601250081601885817719517135886992016259170", + "18191604052705446018115707722428677617894390137039905732841218263472908863798", + "5747208788515494994202809520653945260017053266507158973974399964661798663161", + "16272405226087543462175411026937377018988783482980328154498443115982159189635", + "14959941590764331840350995267307957753388831891977493365286637018137237442649", + "17614662493206460042788564726025968337855721129414467376860164849857118120897", + "20540842142176422103053529661013282478418611089363263097515168740633056607687", + "3290501693077219671357645003459548779173001871326499393140572778475444534834", + "6447714071113870576284977914086213514251182728059734289291667058468678194763", + "10016099876948482641338204267737928588893338645093292975971465694121948536197", + "14390773887169232315205787249570222449199544947506512428178148864937891297988", + "14620241431433245619096116183667070956408821753296293274413828291589840475809", + "8494548643236049061832844399476487754775580533076308093638832054930352314493", + "13769276515160804060029880354652109198182914994347382081537158871751993099394", + "12260040442111865637116150785319893997124694704607680451569915655443058209529", + "18536763786390498498949547812325024712812297365266616445712766917775521176399", + "17646030323097974380890038898728697538005884509434748880882937861292172824089", + "20995211004588227271383296369921345208592928705280945008379701434887289620654", + "14012493030585128283500948776342859322003214361666347021120271800114480256613", + "13455652757462524273399933538052579123871086976307529744949349716824204294368", + "13176577891048443118691856747592745460758840755040242121263455031909886205386", + "10958007047406016650282891454669527247751542376915102729040783130554758505696", + "2403556896710211170900277969533691258363807287618829314859189083914528732150", + "21557822641920534343252006226462178185422073111762808253392360322675121740589", + "21301363144372514699996437466711759383396133417588035206347144265914876911832", + "21504297452761836117267872934293380498824936327520529664563583098264179167892", + "19008511347839951812168011659083529710470044770506456216838511009924048532294", + "20729557973386382250734811255901369475271445538588448251951105355722497057092", + "1596029835560146984478838415844959546370291207350745477127036146832708172428", + "18779043981021734310054761701946195503647990042042517453246401248591799960006", + "18389072889502391007861938373344479996746585336278814994866091596798460590651", + "11450018471721248431523978933304902015421867719856001119788032399031643640503", + "4695792303828705800181239521907223047109864872167868532243675764473118182293", + "7504424077032992561542388084424474665707766373549642837850671808747703476058", + "16201871852003230376980125294909568327837373609180822015372468013683148626476", + "9820880186139182810240320436271674897142093850866141441610372427566550592116", + "4474392152763162487463052318741100507628836016813487468299920576199966718180", + "6535313881166679884076402867701793095581869448631792643683900609540448349145", + "10375499075088306448884735241746141379659872937880333028048697275256343984198", + "3166175670215426596895103003450519990743126408554698027893417949729089445829", + "11825805020940873618060190367149910908253947573120883350031415196553790678909", + "15868728010683147011542066925511350470588043768845023267900379312401595633901", + "439641324715765398013368707995400811722128832670521930633684822154134655293", + "21389215620992100898484688007707226862245610434491939970737280409708735004882", + "13567457078921654178980424734811793190031829390199528822285370655562819201102", + "2491418221701715606532424215068729097730197896717397364135214058133617329024", + "2700199371443700220147927924846092239465972046042951758397024540313204379477", + "11101155530024798073181197117436499601132355735889219480985438157046671552075", + "7696323106369972761130328211405739772776004396041557644397285018363028133006", + "11874928928175576980576459276992229552831994515252269310033260164062026645644", + "240537705962136501448280118114493602675578232672944677099273394877211057455", + "2336277667844723086566433195865965121270861932965343039992167442067222380338", + "7139477698789264606031986148297940201890262913499702250627665080791275501502", + "5394022087396393768813205254315732819042275688988698297882925332496064046016", + "19793961339963600744392705441271116638543292070344579052218623579091649097618", + "15692138411890707768379631022903362931511371639119812810678572491600193150934", + "17870790093336984580535630746458834581867280473989239593391879854453602020534", + "8527221939827993709561548898140031570873635681631994323216682386420609693119", + "20583146985132338324328431878826670751588212915037908674977314222878838415115", + "18286401926708183610455794878977380145855167386311409319243563628128439897199", + "14883280595953108786791995875596640384464375470758397510182264364463199426941", + "15622372954112461900770387091989505548249197707826335911691325586886540655839", + "15015231518950399896087228957162542423583966819051318376374846686122344072726", + "4110275540712809298667287978052760900153083659165519222137609468305797552622", + "12194886690835058962976784720918154631659193272765999855977381550575603733993", + "15244557029932516875079143618219344257744782264536288508363832394149847400408", + "10234512862511661102613686672198745648772485307900923622631623670310818907316", + "11002446381083289255418443918954072583200871532274996806630154250111947858951", + "8253167315431388215871891033564848144784018068378784924459677347009078928353", + "12052362697319720881876615803891693831545089601220691511658506577522506606138", + "4222271910532283822428121153000170791277438320812055898378713943538215316712", + "5653799018158152396708984453701593562269544320974176828284977837083044089432", + "7882074107401771620456811688079120160013843021919844939348749959599330360070", + "16985024708206545567858733579435707605892308141313442111649869690900835499686", + "15657638292217617224447239244820122003415140104485558954475025681733455080516", + "5031435167133277726502623124488830505559857356049078624241295172338778604459", + "18251569826624635398451261785711619709466995337593606694180128347754947431611", + "13347210735949290753346060121453479592654490759925951960015474767685667320469", + "15097256163521186466089214691748103981791931847659339134237546471578704117651", + "18618539001972002803104792202843684224999903989910895364254642809781793542910", + "3535158924054577294148391849345768098132565619653646126038220164469413178376", + "5247111262643738028940705230194040190316748748116050300897903801701767549935", + "21069968641157288335927634486731267978865294883466175079455493856177243522705", + "15182016439888624540835624880056168611406902880254574867923247894452951108191", + "2598270152918575231168351721309733272482825217008498067779287914396446525010", + "5113774671804063464274403166444533180735260894306271253672198506848528131887", + "10007988864825946871589264454040608387449058007032979960058775715911078006740", + "6905160188004006948460048193173293195325586959399957154757620552653693466890", + "11995759573081381716106152146833656576924747390008060686555489446745383610128", + "3012863557306381401755965576258357584550751416148424411503270543269480674925", + "2913988303571063477501511529375194010078206270482236339308347176586704669764", + "5006524049092266462685266534385319119401827059140718482624293936686566174021", + "17563183357860154367150870160675168713982368687948283812578969167511639823963", + "17837755859790999437375156561325979429838685660428780384415700354505074279205", + "16080522246080742261793784524730583623229678427635608200910206239897175583035", + "2848708361765680705684254800195178864363439998055587286174501633177530616249", + "1557519954300916467422364113319270366979873515036760226798787393508368098341", + "11733665678441137100700293713589760125678167060800184611962879142675741739407", + "16521409005131685666434785453319000098069672876632224692360259274386316622759", + "18967967647390654785643258265157841586213408055603172849772769208320302959605", + "17571504476351312669488808692966809815779784230239825235217863108338926113527", + "1033277958139146991725614151292577955914352533347610456481561709576150765927", + "14781931979382308373625511201561097203707487976646566051328122813686985362864", + "4650875909473177450881505980066567106490273485086660264292847695230525365004", + "2914409799137239617504079923731151032417003482030433682771204258557926592318", + "1569377349856381614777306232175508373904011237210172681516586308749045834576", + "6819560317780627274403497421141083285449699366697573487561555149873327611188", + "16783037329427380826125478934695948949747971672927005004484739677562779828896", + "7015004192682971326660995855922915203604605225985220083215349611396572003775", + "4630816304408133216263172876471813378546980993666459834637527842126471866675", + "1048974869579541183196305195192354335945589066090693848404581740062134729282", + "18688715639285068183434963296396143793423259729912639999344372787920282768249", + "10934347243740746938446533383348729597078488045109074394137337392516949956248", + "6293725896350451546716316494602770048720703017891567394397586512386125658192", + "9703640866977553131835948007053717693480506246962404280815291341798629276781", + "7906707563931146558411066527524578790489630019846849753651156405406896838827", + "9702629288736663637774512920196886201521548938988112829178357919548773380071", + "11496918342981223624025423866080407628491859149911166113194854209867880853781", + "5310797284993667039758118840812749372556974855379167283523727209282978379416", + "4023078794142040206433592504682005395486039156676105856122688431286266944198", + "16871217006715623479558921653467262427043486550696934636603562460293483965032", + "11838323559167998464544738415089027103939009770214130070107400686110312526340", + "4980088349530490327691411848559646523716982917225546360214229685978505879808", + "9559603819596004088164413773616676054644268005451072511817132718244048934414", + "16706530320004737946238925506482163802145108867695620161891008305110107710231", + "17097818114876900788313405299390647115420321455669519337216918484593668351660", + "5849414403812965413267782406018277549067364089813096482998057955032613458546", + "15554882868930267918822925407791760045541474181510040631764755534058258253399", + "21580870201101672999738147681605702976639626554191473007622680366607482413326", + "6529597247071863795966038055163209598757897174702699285551712764342047434265", + "1034354038040835970585668649926410980243448725331655567931872752141342573332", + "6310767810288764607994771877877727315289627303796831154815613377794983946591", + "996939452759173758835198197713244238928300388619776170462989538225909183250", + "17830180799634516986244195887614846195714707335284590299432596686170229130983", + "19874559606908304758854510017033500134646556009065042866233430268619254302205", + "14009373839533951923879302153319163529100920268844053529575065501607218593395", + "14946211767155341356149843492542554767905660369447051530372801216288515919491", + "20201956319173972615406549564337979402376802300388863728690331165140287483581", + "10535506466358617363674252301599248223977286129433846317382254624662176433824", + "11029001794792043521930346330104687908171223556219538577009137251048931299365", + "8200934006947443074514571335915268415570710146769685554653690866413931711817", + "3756001813899409241012385101881052052933218317388650253688371184478299762463", + "18636887900618256435910622299263466888026971253061227337296887637280237818897", + "12062360063539460494176603336727957703003697981349882838776879968409682697224", + "11001722900962847730169711618381123657713397243841075174948041047383605406084", + "1100165499084337642558411348439001505815886538759724357694738775770474410180", + "17434145857282894864497601363005649089714522250566258141982190265575138829458", + "7528570688175530782083445771420581373161584322581902670613058236780353068011", + "19971690728399713343613282829324748110469462189895719072018362834001408037396", + "2720175852023799717892872520519566779572526301065275372226989199715421627391", + "14943825869290446747104357486851686014418770156528443888330443464181536442315", + "463946552619089332690398123288326517388219753658554785664250177816814153004", + "17828919314250532241077886046465359044175524922026640483099339343132288207680", + "17254663393643639301497528160788050107293708210389962463123967310041334977099", + "10548323428945380231342461328951126333180031707800802905121494905097297919778", + "15741600332199859123106892774234668638862408966232099592590755994226268019056", + "1447099980864195663747093395834509033779875532198378629216846242387465017398", + "5634271615965881595424665300014723762842669209816714518648909648355913896267", + "8004046094319510531556517659927356739393971820240935140962728964910152787977", + "20150098853172404856553856601505464455083744410516166414862502865711843962616", + "6051577504932496971373107947431966202921763803799244564057047344716078611429", + "11153752935200730615392163240542716924397740666674490478903681768299619893440", + "574571428841330767683871500343913695389489991910772848795148223306401258112", + "6644002840867033629815195671865575216742798076650013015371202918478434067981", + "10643887442409207306340562760946079189746204276290341516975302787467861819914", + "3310054989122793204488875713765468888781168322357480727671652094037000591815", + "14260109332138908810733885305730437910719800845345795697882239548319688811219", + "5210296637174697538908079595920299077484266890923778247313974722933789990659", + "12099525392201549319291916254076192583959301537918452056524775934199172994864", + "7553233508370030852973625898622143795815488816458055012305692281375143855601", + "338064484385145371035100049761904487152067475286477053744625247222287881704", + "21656711030424471443690125986754350631269722127039355499852011488517394872248", + "21372195548620232488593781254533859082570347959319967220919951808088029020212", + "6382011083971938503792804272844009960813722135917547320595901431805157825428", + "10487620314920598491955634712479265679552728563160570833716083687459951803271", + "12540875281457292616015016392478978686561298989814479837942710114246256185916", + "18617705893970874028060182711496372099359212095272496239846702650061090540602", + "18861927157583050522649066858336628481061253205783797036256319572504841854227", + "697040343546965749510304464460790931385381610135802696124987191773265224046", + "19957864832901242629222592391639674066674531543245225621817890196632976954948", + "18958366438463449522535881612753516577920408884030961917206372848340856779454", + "19038355323356709854907634385093529445778219247035572752290295596750121939785", + "1288546653652611126768551729241141142059005744057588910355805731786584613343", + "4046804685697550720337256987447438366138088525717758741922811410001913306096", + "1660762585827970516291367008365082875857712512262252287267067642443918638514", + "12490758580574114271593656499165833022285695011717771020275535753216014465968", + "16141486882419673588223773309048616384177430148674560859754337286781225436609", + "17480842754700647574286106752058337138867662945102069039800534716529718409113", + "15604353321749597155917847886619012355001132908396771273202434684314342259150", + "5617279871415371338336939332259796482400498338204339205308393954394249833452", + "15174696383871305992552314836076160250704499338484600692522408887766438439385", + "7427777880578061171924488290292477545896473049748950597878341286830693333925", + "14092813309756880555589883560674882809640120093304366339387019683168400944554", + "14371461943490927493590660203698012938705485668960202862586478499151043526062", + "15747628341829994869775701388730275581025993393892718204096081193836534027616", + "13069485962326240936812384345060376352182714448869348207061583108709995536141", + "12897613411832214271030930629934621444235734267985429117525716946550585934838", + "21009316561423875807053156778128985534726230082102006144117574738389765473006", + "1961079851137768143186691785724923358396590015729408078834852546188718166268", + "7449716401041984694438903080808992521397429308993548549788139074686598693961", + "541840132011603695915673487977337384196812668637554519620724199330714724824", + "8732237210312418481429872365007927003824155222103189850471564031658954501153", + "6523310024353124781137808005011914742907908537623530782692226224442022388987", + "21509127266455930277492767884139578463956043561699345583365079657386805307554", + "13989299184155011575724091657019750720502004610297338129834280607581254559697", + "13646882669283186383881254452527864262332184212165662411849024353857046450116", + "14437939637607347068422046603975325432039018873549816106341419990853530813605", + "12400256882351762977557053352624357859435580188305833994340851255855777961583", + "5318260629482404358266028277859623051836786599425915900288734688050650647740", + "1629695214770269012001751163254712973932233705878496747522731454404864982213", + "13712071936286520679632088394106925703538964875601961825262229735589116706613", + "16370398845138146853603171685418735096308178156853614168785529502102183447562", + "531316798023999736377588166858362632977098508125317800095882267717108741597", + "9318921203266238488822738977554047526587525733728386955697050423076330149397", + "270377449844572577022377874031006263208440602734811115345017690504339807539", + "9815014161182964374794581271513785279039355629218722105019385525592211310401", + "9645578731676628215511306357368922184828636697072609821617709363109131724528", + "8278125422456187544426337239756968442258418675393889722193805619331692372756", + "3793686014229600852890515574697744650225321239380634655110095131274614241795", + "11274706713173058310395599257524413726315092599580477329306442323879171602089", + "15700516289342200878818538157175110895267852686641088808116754074662814875793", + "14231470645515485158149500150154330504733538042275634884479799789708016096981", + "17009943350256143028237187628790018788978713833977098031809639804676372324573", + "11807744076512700514106955514318371416449053759940168158181473579522112561617", + "8941191247819458429052997300931331828693768320666304381154122720332624423159", + "20810756908118815132070882638097329621005628857845667418135716334205275433963", + "17633865705062992379853990460318358612898966723353031319355975229233791359833", + "20574714650596437874739546946725432737055645662464099228865520688481435554952", + "20753133214966494228304506683244546691191781338293295716113306511488052350935", + "20891917718961601440155839168193313007436009622881050810291426124657399756167", + "15254490709565398763344025189458873521593894682543200304507470388535826802423", + "1579686464441495454543935122179009252394402667945704420064352314351485765393", + "2776164910982632895181744444931014666522951510059312025217047160140701867676", + "18493452977704296940835401396385377366306134685072368153755232646737492155884", + "7466441893052199319529201615576158956429673118460829755667264030485444838721", + "4862779186474359727417788114204862127925883646628388801420770199523844825968", + "4332442906725686065471004321063349183397729160637280571390764069298441846158", + "6235595676847271156438843637266606946005787585155569320931268977334224113102", + "3283545972614629083851761609721595874215964445590540647737064482671122002155", + "20190238096841101806664255153033971438119127586536510897732203034569691981419", + "16218082928311959370464862694396129351602418127064911331498297863184968070057", + "18169106670538118432467974548733501686430842104311041161699612921427327878488", + "13082519725061669934177494520541983774227971959053999627907293338903728791646", + "17917738025521205167709067376626145668664963656058875474248539992479417821761", + "10201169720940960483107286532267011054369536208475708600788396458879352247717", + "10086018032281190838410153727159750412677882106214361975307419597102233105666", + "12682273581371325166244148831560984283003922721179649899812794401686084266959", + "21151848537444166847396763358911125604594195283585946282585966889014856464069", + "5870563280212708258714113395803290954804582727013499575940699343839714979459", + "15818574025355019546843656101475484151535156797083091329572001913076927497145", + "9172129818981348435701876676880219576808443896350696165457788920197063328446", + "10390962755465039363458687913467754632739357930439039623432597903165701965254", + "6161501669511652251380864592245214064026880951608726788784852344316145896868", + "5405539522259494292389572744962975583699928794990075330665720986459457361914", + "15953141686281883540154911071743713625324924225902448030996737734044456286469", + "21879157639189744211597108651045875700807085936267782759316146460177277605878", + "11434924404918060113007413962226874803102772070441693086168193464929290844877", + "20757082623598026102923542323531106955538082419998942072958829717667465557131", + "8919981096169474486495376362947226335482024821650926316923260800584787410595", + "4288509578096166644556335935101899777779885105511348520519380469688243082096", + "16046529059566280251436022453196743987059059184968007751447655603059461800273", + "11219038614950179129482943179862383597500934389713177845014944329948593838918", + "10043039951240572103842000677370211126438633625308559058041812947576435228523", + "13424594313385554045395046911081130962489696002268038176721598695954847052511", + "1211793291463193275930858215285948372240170625699602215748915241438580144093", + "13956919764638999174327326329058963982898576605238294264300483944344569472991", + "21763763972390992315030985535287866258485311664824948828240050483482084352503", + "10420983865433228040050251893025618324702726171912049836799135351770260802977", + "18753701818722627673883564699398410722354636353042388382508975546895568464372", + "3775364142286651074799331885877684616693199427012762917550076623916732710822", + "8292671711306889259828642403052383798091670542487370026886155070540857817510", + "12071789317828192902441324443833143078458416120447222512665882093850970086582", + "18221914904286916930821501101975619932383182494940702022220624561466550429056", + "1015894946220130503079847588005345557311861372219799708101258594371020452677", + "7497079366974677538814211796849030622236102734688468791057012961185250977217", + "10412524008150259624425778663311437889796006875864864836590446801100002463061", + "242804608586097049060214639927231617495599500538071474816862835383660136678", + "9327321863177171873474299501143270493456472132426279976071195256972553555122", + "20841963613720468677785103552893139903309688713322442693081971171914472255790", + "13630784939841089364059908940844732601072559390491244936534430865345773326547", + "20060990491599613171081634276027504616735657335613608029945430961773413795782", + "20369723769740268530808565104818084023349274297831884550854878783888535628782", + "15076862507709493588855118027827375721947856558107489434980647124128748591027", + "12996451838772518667606758612923003843581729895975567888341928977998771990237", + "4052235171895745508958823720790274122684550078188920081778152850440806616718", + "12335377703331511956642047444506386381049513479297082723690490432465187475758", + "19412573446052112260556809146936739541890274154067075795508039506436684757726", + "17815752693042999749244044082547565016456928313463444720511782669652296613554", + "15784264337580227630693723391508440881780776717973842946134335715743491257089", + "10667492170364941836778050228790234453197448653226319355770388144152747476935", + "211997855288770996034164566467801948520054169907247985981041869092209280055", + "4928982790134068590739975426823777812194464923471161503361037698280174981760", + "18608672135933853582317718913701178979379432491853388463545185599228146691207", + "2315480928424106999355320576364787114368100799067708928765166041722251581141", + "7132821772386644248930179957111951717051009389997169728630437453984113307524", + "11744909558588287567829975231377896922260739746243206347301263424478589809196", + "21821616737585515642213483301817094657757210129023396850436714925413476278715", + "11972412756334108055648790188313869962577071423570734765254546184728606861831", + "9898102092275580917130558353359107455579856499298488646821411779664420246304", + "9958879822047499292094876401302022972082946727826778952858218178060652972948", + "19871205375909775929744751330720905649873502681808501037344623586575605895174", + "16601345490108570384179778033128731955939874176242885190845545230306090545377", + "3600818319871189164836691793538369796689767756596935154198009735998848369707", + "20314694248383769870802453966233555256007670655653781360971318867730452026627", + "13418958906290175260633447769067612135267907574578622587604011162488451092514", + "14459952906281539694149485094281623760953141057796841403669604773900469687889", + "19448408956408494949564099234438127422205398424291589853947309097810118078185", + "7825443276535418239837092010081563810404777554695462770138005656816269166303", + "8463248112790565810949249352339633764856217758597371912588298951088604363676", + "9397106702637851943166369067733828452382029530464971824017078972309585633364", + "15452595095505449307828854722016355425252678164966267614739918073395150429984", + "10382687268437366227597120935808669117993505555024110738918156422833458968254", + "8525129123003317464420034009755418250374332671627293582800681623438541074422", + "2209915653341740331756848895690532824379055156318218590971248250386542567791", + "15183382625497370680223757887016738067273663916736248647060220264288252640054", + "5914882314939376011130904692287520473675802288813732481565058295269249787489", + "10737923811176739642308957871008944847331638141618843900584295941869359201136", + "1019349115878726003171044582547108836234898959299710235354611802499665438533", + "17364570285151724843778637821645803441762402239038594475632207111794332738860", + "1251558044831543718478805412133062543617572066062707552512150093969983011815", + "7568361578986094203490921683991770751640726490619033852251843934473195496119", + "15439738350303496845805351814211602783597897988159829928947802101119983398961", + "20009975853963282344191402380547695759759731502702993682892413834957351648692", + "2363424283223098643833999734510060612126929924822351279904900380971757668501", + "17136028473467909987260660923882066072229277631411461261810169503965426571985", + "21423452231832054119549543703585288464249234898851841557254723364015587266501", + "6300803647873111234660196693510491620063330061194984556803532776163457642510", + "2749674757601823156522337416483425720076662114029481936093706226193678544468", + "16123152544885237760040277581510833657110306240025009956733742940484347179299", + "1449335556475943566914615807167633374850907490355989852917815325720800540092", + "18225835808858885369557291110939124591374272404427656191824524491194015802997", + "10406157408365973197865488683230737937923502029339842530701446718978570586144", + "17998633938868470446795829948946241208954439598754402047898587965138352946507", + "17256506181869579387781206921456178119308959078547323103229058358574594315926", + "13094455817799190250176997937777121524540082751332502303810836528817463694222", + "773415265947842731444676861730775210907581945844672294668103420848288364110", + "19038336321288882240456993528476377501756672900320404015643432684581055112943", + "14760627809381104024315295117982862934682028410719221222838112174132134785643", + "2299176916578536045959722453832883315456156391111537255429863337814706171473", + "3669114713225196659381574151653298340387887549947282054635076450967251116153", + "18251972251689708305163349578105700140002711019536040700377516042131395885101", + "2208114007582253724814286240939815507122998650551224166265438176279104064289", + "5311319465851004438404228350963430011209528099234009157573275300632566893387", + "17704997085718018561575909495530243398580104458533121202802306468962737491818", + "1933762715500501610210207583856164878867609341004969983518458273086102828809", + "6386814998566871009542498039659511536698234159072885405476628256992102020495", + "18831772844239784973934592955595859276748324649808851586824036127112826604451", + "13462121128312374635146040270944207295588795209645130037644415451121036635451", + "19731805313375129918425060059406339849106971447864245585064948352741000760923", + "21643500371506858849424889544041572150599889621082729284012490052076449459481", + "138548710091390954908010216339657754049773930079521223071410504190290814535", + "6947102089761831011730597399319184690211329034898095828464106850135090631740", + "12831053427863630744244436108113801672008361468343188269856032816742671083134", + "4223577351003454708551493531516184634014386163092922955496011474432781253241", + "2714897164693927923639587351413494956248140707486375397510165870974510685388", + "20023038420823827383850598062203899779139064378489163672445486337197649825217", + "1767883277856872395041944981599210930967554162796692711247113543638244036456", + "11374943081906439317741616342767018365183355349301791743927619930324701508426", + "11803403776542021481872407152864894420411711895295228328072919482156044458427", + "10466014303238336135169608931533160584648879880459007777160264124745826740750", + "20569672017414729176046551969057922112780177375685900568749563089152585958033", + "17872595105302125548629012813042403567513885359499964690907253660570968601519", + "10903006438854654900289559455490687959429486945748385817337220540948365309048", + "18275515870643256842855966776500808668842291767076613550498342673701067299432", + "630933085628668840611776843471079145620019967645658920379584401715066362709", + "6825394902701793105668667323441283311921241346208501522910007054667207868452", + "6858750193485140252798450603380083764362623733369268015553477107387074944232", + "1478873465931112194691102753288035258894560238829532399713218315372158028033", + "20647986868364738274961741160207964168903320676158610371768611791255451415798", + "4340383424522509172928608655219336498337132315044301147894720301443278858359", + "15694054630954602584443160345828698433451692126398102396637062194669728150097", + "16587614091042345417694939817299154667821001841573634480871399767976428325370", + "6780948432583055051074963887359666213579607675322530873343441117551279310337", + "881888803549941181636901791599886290841557130000792317917044641982716656024", + "8375540908772391314073560695793592104950470672337969483097880419747173630602", + "10968989437172024632405943909737990264150382688586767944077705368389009715036", + "18851349698000339475263334762001556036662188445499369323276947028195614775701", + "17424154833179596191247217224032955577230090491464139783174467885567507995873", + "3084264395291045886600299855227253813661906050535885634611163481700929635296", + "9780424450121954867052166726264263906169948113675811543750657338499901472300", + "14927658638057710246989480138441311677749528914441518171301964877558977872737", + "9409069466927713011440733713350127030613235827582038407437204556786573082426", + "21532551006723685558182869738272976023905972040234419867623013049768952103538", + "5167833995989484123952079378988963389989994865716472465476503880641449880786", + "14740174761652743774118447006447618386992142680472177663315668117221175944698", + "10520805511348878943408706501985230943081177040800761729569633389778724896451", + "8700454790689589285537042584692084777365997167238651880195034227220137266589", + "2733945906594382609383183532639154621092632308934603150907320167864486314840", + "10291836929398020145429078280830166142519086643388813863997726059451399134380", + "1964652348248051415524467307507959105507630568171154193714989224874728277054", + "2594310934406347332341406086415178782930670052092905736460388995932291385927", + "14842121917468246033091527680370186171895171052114757236254597861059412933851", + "10375528532253513340592396399825408524430850232835146379883683152448199357827", + "12477762696438196028925893395417443678494787519078936487705085579706421333630", + "18021944163681185812898833787149788656465569272261580921039580457574363675422", + "14971348947199715425743490776577354350250241776207063239247262831298628671824", + "18527079575564336560853661036281436413609153640671180843997315618816477020062", + "10112969341623453459297605360048147871962444397321717541108011492952113746933", + "9306811905619197449152929816385830205919355838468334319731910917748600490999", + "3201856562784023690028197460260731000729676130502544364740684445045551547485", + "17677019524999624366971265277439539545709248668323859450511125252478150097491", + "13203760042423946863820551582066224159089369605713946028771025792976332967017", + "8225744798383097681411492868795146058284602972120901801159373206820642535790", + "1133207841388716114249120311196903646181388009397557241156806164775023041371", + "15623890151225925383841140893652872349082273871391477651993101123938000801867", + "21454879142557364834852736051259149378158105754952971451715419987896242909650", + "8531552648559366596105198828222595210930697829968414405502802286874489507133", + "9401758966490949657386555283103887925392012048184105735334446423864991206786", + "14182985100305261645378993603110572106752819147111838091043200594637885017186", + "3351125971378624693919014331249933776013465831551789721255791740304274394936", + "19684429419661111328145464896944065690801463972795522382446603218587092452167", + "10925858543362322055288327267322188904474484027644138316216578205314218206492", + "638388255012974167128675944413974318871770367985490338348509002020449680093", + "14959175299535994556786536841655834078592404933909341983568127492171858384391", + "4565657688930940961208733539150773567906556683309024875952853834684480507269", + "18262745015163208046754959923695809473174547423960055443621926543956602209348", + "20270410959376379064306739018970053272238947330997122046760265674383899359657", + "15290563044070434983378131412705775525253143318870138946525234035666015420725", + "584590168302279667271049654248756511695470228213252786062709717390671267095", + "4966088591187905712289854426727671159168968384675952400787560742388879645568", + "3319669368740731092651343449167365623382886973437878280133897523197651190312", + "9912174158284239213664392208740702249961859675813287499135373479592639861287", + "8516379350240514281945243250529128948244709428452350892659587420308554264613", + "2460558416671744640916958595519740745483455366236568201673238589517657340142", + "15249503582713331075672114681569135202331428122201669911203912573537116149514", + "5355701614078955989983290080356684040651137096346994307217952640790301753843", + "7986859541259474766804707773691600248831464381736783455743764933501866864285", + "20896597282789039779549640920767060676459830701472255011776479759676387046352", + "21156365340242937197411344791352838273101399835862399143303393482855843470763", + "8810320994466343375413166155559061722877903255339567630911647558329186883195", + "13305939854322882862383989397499916187143744662574072474260034250574401506438", + "14276510595076120373993498057833260625756300056428067994615344507361982835166", + "20071507901284477038407301846126798169150113895071357904039436046848822333309", + "8183811379732619835362931863732297053435925338898156873268534636771405892075", + "13219046823251102005239988390708288792968718105445890139009709283489860275155", + "9185773756453985715582986632685488321665392756676197265712130071752773976631", + "20421943269702844355893550089604182682047061852707721138511917444304707579860", + "12300280142885224975710767798713824153348765684179955650717019029210821361254", + "12007970010282235734724332615090869243055192449773846575955902036212263482679", + "6158352771477574748962005695592225162907260568470401385862937675095774244006", + "9907046449530603675289866399335586386847026415848055552734816021036571412645", + "20848733914096475328182569300389604774285326414256566622667108340302595094373", + "8365137332145458646854804122757179721555979085847877907343540983873210953635", + "5631007860876051233682685206319236038053360910127434722172168921859697775602", + "7724822014604490732390628840430579796440157358556500058829335597037618014759", + "369071834493409594945180455653375424679250473615867182702388739879310444614", + "16769246200562822457100153851476834038704115916651426939703296994708244449575", + "5761928395342052380070450997738075595651684639539927184355295101970822313162", + "5566206280191314323446398313438814776510866532854163164867370641136219947308", + "3342359366346342054674985507754083252076489241172458810297886110396010701777", + "20961070283557581225918432539907379409525514793918155863543775859703676727621", + "20556180232726542574747935614764094533414324377887922356987347326584809231233", + "15941149549798383978046697670090370142285790712496734802869937995562571324293", + "14600396724469636128403441949598983100001916970624980610808650856082650218424", + "8853454033412621833577484167285435511890615294361962242058729914818351640066", + "7796912448927288083851300561583091259511345752250157116939318925851741489961", + "3163019852372632897541834965385695185434873283151315355155397996458747882742", + "14952700133737822394693717249052150819987738575989807253015506109171868391432", + "18525964798774229541041472797217632579102334608286281048443267822115517194616", + "7102118124262431444884005767970969670970778150875473949502505015348122457394", + "14204638357887780388176917062053732214797428898508168337150295424689096270757", + "3277315675050465719399614805088484534242274431436504827247668714966713345731", + "8238377613232984055051747513217839877952472190522754887201501162730400770486", + "10224258402104106587652987351104010753816241645110952300303846844538654914570", + "15487805061491340966964538995368847025698196465175525562567846982560655044359", + "19219815971577427671261496058630992677146093954055100897228392314454716131964", + "20451783587558695494081025729566916635107391693056303514267562508131052495026", + "10604908883794901754591456082090038158328418399338332217230293938008589705164", + "12629655676673767414687101606277783347408094199429831144029123923860076780652", + "13709603387456116061265164612378749494975357559995793114409724336723134851988", + "12280342248736515373215500158719410978133041790040105888782814256839967443115", + "7016009627584978047738165231314996484413117067952510110423917440240605830479", + "1524894728322552104763656617099911162075578893415210522528939966083567287381", + "16110348090073067974052480411460325713852194746966461188536450049907379558864", + "15849314408692105099989833438461121938666945258532089314493985018340786241149", + "14526266680576776847324515988959661063035583100946967808594725417429124130409", + "12662059588246809623132277946460950856148161126345911747740066547195150214304", + "21784234245035153912497219470830935288918016717870099869682681847939205742526", + "9781991278293660355311618579950123622358901576863101735826839255817708999571", + "9734447579561113294373127302234593360683756540510409616709038385517649565283", + "15783703381085552443769424496547949342637297192593569766965137630433735196499", + "16818302710679242439066482280541233367708597536159643998239357880909455304", + "17208557330797764065799659085570688998141169399652780006230425322841317471944", + "68706297573456724111634550544331343558310761429949721000334906561427561646", + "5166796490382346553866700788131214421083284131608596259217301814881739273429", + "20336383765590527388425300334919391504612546166109544795528892908246473855987", + "3743358254907302851720282727384172290999978135762491123193685721554867920482", + "84015356866358900057683156333277329434974724445531256660202285630028495424", + "13076432415761967873214874574212608989827734997835707946240987502102677974919", + "674004304490160746369333425685153534516797008922146231179438460150958899661", + "18008401489523347421324064960257465802035658427821867761642737239891301877084", + "9230814159278735889507853746171683425354655931537527400639327580576056029011", + "2338224170787780983513724541155899891770286010849997183556172649088034621522", + "13774730216408957127941425099141817093498762972537953648152397183615811260761", + "2008226192472961561861571910859568903805945585734651871281309234451941696550", + "18191659524918356873059208424871952262892246019889299985115112751645849510652", + "9977051090514243658919274826700877766433577306278237326432737577914469740819", + "1382241145032463690710185532538145321402596788581319285825708537098818318411", + "19630880072807438067933226715597469053645950344003618599545076855182567392314", + "13804532717560570932337083609013921209558901545207945868049344755900793666387", + "19508550180350246129831873366824988420065599516069494466305637280501827380920", + "2848475116255698304240592397534148356674367429794174368229897070816851620326", + "12588734179864636951823212209185103123565413699799377322920024420368718083095", + "2232868200526350302935711180734463487873472319399673090914563494365441045552", + "21377055866142483679502571618984666212079397034684178104676086894866487789275", + "4635961124592421709613730717934803195170398911827834468836156717082833802089", + "4795610144270291375198683497266358163517248476702360287932565598454128564576", + "19878139464835077446055142241229890663517120197298970825225586650258704320634", + "9323502139000335770056400844753650839571196769328639117682123746249546059680", + "21112416038547244178847876677595343035216538411439215223028113590182490455841", + "12697374728966756322005607409702182672422756890620656219302331428706184232671", + "9995295835719608174333254715476035051015528223152237761898234348962139965339", + "19838681448410021391386343877373049070883481927354420971134124213644615857262", + "13348946731323604604321164467837298053616765723820806627202376009331748334141", + "14459243825598700354634855807582241585214330632597159841271530816381999251613", + "16398077966528830249699687699527475156225434078425955606570595942126461545256", + "7643106356234289086355359290357087474077139338168525601611444369470624871398", + "20959675452873599571224578970521717955557909237727928302612036851445583012450", + "4540025033646420810215611052662284909609074316113304153198697292213556037365", + "12027414908456226247222654846878068961735365399441671468269489072556154853636", + "1619579475293093356383780863588610629947769633032102520272612883476001600909", + "8181934427490631780501305508522146234029250148182651874753791841738732508561", + "15274797459753339681175428319112732573480605162611546476487133996977708752899", + "5881626303176748491435260832560365957494745248996859086310569735454431253109", + "3420994635070209394832291125346590627475632172750870826118213841463585026009", + "3253514739572615120245273590897551489662089732892213994698961442123844511104", + "20203053841592538933587679570153207252397020205630948336602455897993666135475", + "11921266577552501086897102705390311185747062744461377484820893183533643304437", + "13241907169609567850145071795854997578682645559619300976538988140768348089882", + "6151712987809690919701305472528460675037031217153195032569932714294427052374", + "12205705969228027397734509241211850240154931572324813086389133244191540704608", + "12945812038464520921512101569088203002779738300982254987224395885526111963420", + "11566504805689497712142963920462066842262053081516919575350242192944407040992", + "15723543550582708278977667347083710027075971342576647975064035406870923269719", + "7860258127934035634020846943939055662221372673596335233171159964609391837625", + "15528468237941422862373023290041270186925402725739650209357342065347651170444", + "11352800656251355849609307793804420136337526597168059373534052608892527331301", + "17897134085314321992998873273654112498897006369781950634488633467466113054350", + "17437455229098036757167276638155356442563492876806914963325540308515770959304", + "14997752382200324825750631040991638912810425809039371028733815341246302297900", + "14919207184372260640968465889336548904504385718633186760383678178843526009885", + "11844428225775333775773424762116332026943738109456526441415077427972318646084", + "15239467296659614730514306837963031027890655569818408146133916080687077630265", + "20117441496592956911595459290983302272515832294843762087476380541606326158806", + "11211331365407064571488620323378276965399956667340240900293979889578003659573", + "8791311092499361250396136430755948222248236194668427353488891370543849807047", + "1774021261549926243219732938834617869058292002083739109596586460663663531688", + "2787995130097988538722853640105055460288688373288517482894772682647739193296", + "14036419256752421574134832204020173449399727322774622383219518631065684283606", + "2503905040784637175494196915035325275253506707537902487189138102884611289404", + "21664450508754049721193573452150382260579289185348178882539067755355712577906", + "18957082022313129842427206117755534129197834418112547991189485282634468396440", + "16781909482475992329419834248503013105141202009836651280677498803323770600224", + "6643893904478082560922317503708920766922698054352006836803634579280954593309", + "20411224098851507032152776776680744497103387047587118749494718067214956146818", + "6932200046628136855984161612336282559188694975968887215970260451999517971798", + "13683745075914427134220228573435856137115339570721809234203113630305711234299", + "12049119081343437729871267418004147930387014906392381996118616615174663353079", + "2441975952742754201500338273078694079713769380080349023008653075072257968553", + "1011172201777323348203437837012280331103466286486119939185319004696667574496", + "4513858889726009970880526008944305706495472698013415303299122950266699858614", + "1829602352761774082971266699128478520319034089172917557099196106572588528973", + "20888913280392789424820640494940209099778028785108373950708163556643786717499", + "11546628016884748339883959858649314450404290142635813671623548341391668641333", + "21503514976315590685255962605196280426006590386762670744592186136529638021924", + ], + vec![ + "21579410516734741630578831791708254656585702717204712919233299001262271512412", + "8554993601136913148229849281645942416873068991157116548355045570766869071269", + "8349770263904395404819051886764727880530744217762197718931556224723090619132", + "3123463970516625956994178947134086868722089624251980030957656091366977385793", + "21442360932957798040744480141231788172382126494033577704060991460078536626315", + "10231325350034913697901001930461380417506010080725776869094346614943052057882", + "6920436402694617694727322082450000548200664649231576891284834027764418393590", + "12792717999817516574604019538349201413861750406724026925198874802923611904714", + "7319083527910098850218832163004092895955809799710817531274971443221833500573", + "13757426179233640966146754686419290630140910517321420779897314617147307309749", + "4049033549996591060740078431987567671358359797940903000648212935570542836589", + "18201423118137949240970920992151778204900119273029679711616513196892916845798", + "20625824460928171809204757749985517429359815439093046150315733891121610507133", + "10457729085307334834523167401466014435492132985358294006123747181337070073721", + "21561527744019186913993064335391813055903937050713577176254373319368609289121", + "5599728995155490107164072595052340911357670532131511292391179640158683770855", + "13966745298956307615009517188536529139238646569224392383446375189982202020807", + "17756603569040095098346793596909383204174838953876788800894937537311312048006", + "21742079076354402484587060728532755692106347543073105531119578054037775042874", + "11100784872920528132266123983509067070706469425630493971770997902694662926998", + "20400085312205960400585536190272432205634747302273829805331461533195763963464", + "20028967251238446138082148432746545470729859763361092299497853989733022321309", + "21646094126368547381762879012999402861347883442032865497835121981839683154574", + "277256790316883617863153728392861425598900309956876809085316502674092638050", + "829273940377701999291777589563653090200708284690056650568100074655963961702", + "4606908934947031763433560217361121304957410936748694859993455652227072492205", + "10769441872728289230396615620861141176949118733537017427393172917499470840245", + "19521824504454300285368889620047541794275889938757845035419559810899465345698", + "17161053048471962353174720811774420740284389196847515292313979813334039268748", + "7908822737820790247231631548479205241063360318010733129560952138908448461427", + "4877162162397125215823403409232508291458423909077159953565289381413423118030", + "8487393998302601588798118543133789294087935184558260165377494640490662085979", + "7454433584826937164880257351721993831542783218228578962943846432869272993591", + "12600486335574416961082984651671003440366178113351945406282261259087640562075", + "229943091042136639964977508364517877844589816262259724739584329059854831474", + "5964363464498190105797451630207382654570897906930358361814931706994649645813", + "15027885081212300130366181566116370954163923966760653842199145107431036749190", + "6389712846176883524184535452348872799769012323597964483555439005016828865357", + "13050625522428562689464418495099691361897297012535198348448952952830181214686", + "1457960163867278804802442802649716001232992897386781587117754753421449788143", + "19121642548533119996481133068671203033851078573250942970641264441950592334007", + "3319626593342830359906793887689227542493167081286725783452961782138075389498", + "10182025658554317340763807114890885589336807302908478511429803960136159439487", + "5258867553475471512860996445670851629850555214065072419972647507253648925387", + "17105844700483111456515253413030059462544811526461544189616915804056937372339", + "15389507448590279891790879860335127747331525679865083633840630125275096453854", + "8628144040598587326275852302297295030455205882673458629883629373821226515849", + "7225764039772127797033872800338173049227188735693607855118081892986058306767", + "20070937673840272071045130712690506696769042932336737261842298381743641619092", + "12139783026483217581244209544149124607538399031092603229603855979370010147969", + "17581810038009568123079980574064070648109195109589787948955203592875730952957", + "12141791671600953962785570868053442402210784762014326945761482080946083167280", + "10216251141439191088257104654134450392253712707713481968388586155680061818083", + "1414175852848441331935246181908753253333655328715371554777242869802352097003", + "12411223399258687363418284739063179467323133097416451119653175668156302546282", + "14163252864986479721057694562184281568622251449154036885135516034438034547025", + "18935280158362457804125825095786762216868621594409914695877661895590787449138", + "19612073572528301850608997760721508284827614275438129248352825723957284031526", + "19819295714156197944855748114967530774512172286810045467127912108030396642179", + "11371593776080642722788656520803479745110058361122399788254568568846349693622", + "4027805955664709942434150181718117928301673452282014923837288829996079949500", + "12539691854417510068939338882045915380674719248923282579976372900935687263702", + "21456335515466708235982252733061551106892811579323562996977622319059624115114", + "1466175641997386496752167837552521008018514071345728218669064866303097231258", + "16954396739281784813954958963214415095472216566673098897333193147120371509076", + "12708223137926559496125521072416503266378368566414170219615449248989766379947", + "8788739220646322755486256871812144068464402944468818647293944655221095435821", + "13058732292597055849703973806172477675203122319912563406670103404654094386664", + "8931344638882118593791237662384261193166536469680242356398517062367452395384", + "15456845400516927354313637168726345061971892967841823745636300923188629474327", + "5751588038559337581650296498368532807067353107651773867820816744681643949204", + "11549544825319477431118343561134281237789591414423676704396089395115754641434", + "19234147263577254818888926168924920479297919454657521855750553715796101778809", + "3648349134208466654728357812767145066715472797730454946149007751312314206222", + "7718151953117918461425809889893754434608769559584222279828239292761893621712", + "8845522739821256867897474373924647700071798803600128774472020272335057310062", + "12793577303328701474174653130332291657457728764837263636620978444987214166803", + "12567791764609503071525053111715537148465248715927771041171097136254310005533", + "14173284996087186652368776168561110401474338255050963923163796413857580470909", + "18034666979500281081740131331708674377786999775618310647791265825609322054725", + "21422354834639531449049641141105504766268284938752536446702823580190877745329", + "10861911722118463296713372205424749768917665229584553370962914232866310912045", + "1426840929949909140164228257293070123281693940796332643637029311310121856472", + "14301944441994042783232016477141675248310618781100688243801831318561916576546", + "8689261616262362847656173161257424730101884874532916838450695695508844076137", + "15123977840288488307479771803223205244132730982232338102604391529168092315901", + "14782587644869453236501780556963556761570896168324364501980524203741590116061", + "20171126664277707857959263654502050384578410237255325322075457593732181023858", + "5586442008782671473934242848395070351077466917106669778054075503048330770950", + "14893034316669944289540729978541666240683450933307479859464390524607307041597", + "16358386602267214062406516556279496593235072850353941542010428321612942609886", + "18848866854232312978702044457572917667782740587353338084332267136131275700603", + "1579215194993191651478809349088803658155969078580739214414910140585581589538", + "17033458213089087701892498495271710586197475793707993846597834001983636294290", + "12940326624292849673877504632305122955030021835426715254781235159065401203407", + "2093340797218797584680567638361507396244460243439847174481303012347581894177", + "5964973748129501579884254138099588668727348462189690734364404017042795728252", + "1674681106235348685135834192630054282175690835155947917214719741317698144031", + "6021317549494232079997036595203156655990507346855425821696978600367848015237", + "6518804338080390019586997346732962982860290823548982950371646893604360711024", + "15170463834922876947772409926040699970156014460508617331830728121385518919006", + "15398930479669448663557196733417026149527004779216987588229439497346738958046", + "2669700622596766237628533802450875322874330587389952384417956610466102910333", + "3127548363874797616403801375102757494200866522631068411037184158214286131549", + "6584403272373574724590091428656742867168271029343425772452216864199113551892", + "18683280795877134163038651063011198948877602385157504892093537654399764426518", + "12422086496748175124620724672115957665892586761203533990582978803368996339430", + "9745099390463439278844126903162955736019504616033755299630228960871294951628", + "6064302059807957253392579676216068692721552882853761726090911957467256977688", + "860534097291826421956520903118828583111860517568849548148819591113129410233", + "9809207437695386100460912579554305365027175459186390700555141956544839955242", + "6576375143489291749779792018893403099848704832143183847385791291045583986902", + "2551573667498115865454648920084921687986702958687913960418526064766186248697", + "13043550024569409591105305093191112805611412364703194793685224224072149855745", + "5369051621601119248797945023525768932797813569336410551989722329535217332717", + "10399989003670197520503648853627144005300436598931266893609225004624861627954", + "6159561484143246751423457452493034991227592994791307133044136210702400602726", + "19651431183851896182934111830326153107040303776630454129626690653306388341484", + "14970612719926241839940820046954288242553272322468930717244806728631485407526", + "20461999502486452961875044483247881758853878278954851693532423676388213697528", + "5016750536904085805050275769221233811927007383797241751325050175740220466319", + "17316427284462136919522043989265881044949832745678035885743571937214912552561", + "14932533665158850512241105212984927846164589888111067103835286341225240509742", + "16012484446855626574765806641361955141820105388650596409595164514899481874274", + "4863651915422513654068087402811690721104417928537042800794511645180712743925", + "9478941069339421252769300213729433894403874553023597073962166402867140590783", + "17529734771936454727002429801459948360484278991049231778922771896004721758963", + "12672015814840095133854330679674924244657276110030612294537194913437310163995", + "13442667219867515606432268873704321985951188504382080502019480975401891351960", + "9346556839116407181813364316149756946394621057020562307256030525707411763792", + "11720199480542613604905913885140886560194773593236018605042270421391171700142", + "17713550818981273796962302212731756472046580671829192490772244177376146261137", + "10658520565101402948486320613747540160084586467440232263820881394770094857487", + "19120553688581692745126354026518291549778059267410591682456431863134002720631", + "5837704130879353469974552270945063041776968090189866547252204737075618880582", + "10952573317837731274507039100853076769322123807364252392559268333440123751056", + "16175443191562457274813386127054957574917457114692631929042817125665307085782", + "15651399869272720599280980856510555798668301135962902383119739133368631494409", + "17982602271750585864051043003255537160144994845232276906099438195600610259340", + "15564417296959768207318803300620712620729991326343744282367518107640962243181", + "20488793123009381941807231432363887878967153580282107905075818200161452173728", + "20845615892337349138315927113904389806784179140299978993512007141180651572609", + "13259443846669565093311907999318849863250202720247601058242623542433998488480", + "19583450200980335366984605451914375573108173291577305129314361755545713207859", + "838397221216450052117952481963960818729311704107008458344946293696441980221", + "5011508169974056046810100610889953041631883487165044189687841384459345302746", + "11361142794005243853743618662384069287777450724025651283005645149554307240000", + "633456298666951303063125949977139107258301407861096110769739192555903857431", + "21529743295731761646584336858048296237991767869832124757149704140550994020630", + "658601336565148638150196750528474813469384561163113116358457661687263150766", + "7199686023207207605469992040453620774208316115852720699379826196410259893822", + "6694724393708237460096397340665472114749949954099504252044742926933353832323", + "16157341357004248687598290467887486980266976396840580002635620741183071441576", + "18735931046113570691341052792512856145472697573166865032702507242384749856515", + "6329726929169898271848965669873324332951781357803010027329026944351232247476", + "7061209522680426874403579559245042585450674594552752972115778072823155787614", + "8946282535333125111854282749852344921885511854306802144459174056051518348720", + "3988279129283542026399878866455129212655792613180690875708855315104446444211", + "19788373916759119069273555853960393438264058803290744373422102428664987314058", + "14781179276955000841116554151929609467906998220188278865550170013658249984549", + "14439714507608577761834821622197467839862763277392515730094487176490304118865", + "7462417821368326183154895363497223167855553631230374112323789393782682375855", + "14594890619141141626245598750050255728680683081531253962411791337998057959565", + "10297822643679438597162031819677962851770740929826506853838015287832878785074", + "4231562753232550403815225933022904310027433526532932589628179163940950572874", + "10646529230382575523755302352793337142051175018996981972379640368359926883275", + "12164083461947216214412634084378451605511487715584004827824972078192861740778", + "14686738730377226817976749475359230017849316512373192440082122302915229733394", + "1972065207953025646946682878392649299678417507238551179215236414492110157365", + "2596220810659736571653162812588827790612138851645327014072494893329877375848", + "19742890478753895876191378843357325113803569368242719397467948082842949716134", + "3057722811279760312017893583084211485332195649925192440822130571411021625062", + "5078380046721228959752000757775271056076152925648793679038634805597314364689", + "13994065550182407605627049394529818937411332791332501915425485445970019749196", + "8718903390300613451595895490522223543548941022555756021584328963874682051659", + "2830037047734434537263368457077478915382396457133799070168011318577509852518", + "6351328336589112831842317252922662854527642572646149728733839010100363641064", + "2326811025141337415486606567159001979990362270551911801543813258783748664928", + "20631048108966815289784074608382072458460940973738551134226168800345554951214", + "14721204648069833595280990040775556291988674304257774357769088964732410863991", + "4551815408348203166379129282299441425423743023821112768447969406023780214645", + "16626589364834731158131207695359287961392723773324953999720551575045565560017", + "2119119902895954746578148914775899257881351646176141505001582342610666449702", + "21198001320003994532825962721847645202213745138854311312804244693213734456020", + "16575653563112300802890760228610449333758690368857078853765813869752023855520", + "19662199829424784148957376290310955285339135428848477356788149061587904074636", + "14365866723679934269369638167309959664282416562097092385771370076826961535871", + "10462405794880410718710911820471224752218613463413073336296570316774568383223", + "2875852545895825315521599376020869827316570932962420930916494831426174036683", + "9365014190378730240070787324401488407601702337948470069729769089736757502613", + "10207664772554042762314615033378156185686480986853035052764356129216234339601", + "14593204638464636358074700677706356615788934459664487787695216859702024204802", + "16870253735699395936222032450057462746120274245691549241725232755997497777650", + "10403141045354931831897350467824057442824486714796947030202528195269765938299", + "13491630075210993306306680088269974619065901790274179689314972497025507972072", + "11737690900303251277784941365088697342741256105580987740031964012547987099246", + "6479411522140791199878732386964631711912096436445632282256505752750116503021", + "1083921069605939352705123162314489784481429719496507658938926140311574639372", + "17653617267480348306435879910355154811805166995916787026122631473598046826333", + "14432164328022071373295386637326904766549408251892516429892690428586807444784", + "7793671760657336901389781721891199068620856343884708391197481940646184551315", + "1959765449995198342923438542063212054673245344496347360855620191194506656856", + "13782621216902843666964879695291399503179812919290969771526164415604545126461", + "19078359557987218636232226316587656499460623053969287998311225512418446587303", + "15876205697805498189610174935234268016677320029636416200517433249256912037787", + "7722805406045443730324325528663888753917114626390901464015754094863680930900", + "19209717507699122245389693034814796437142853968464799877186934474171699195095", + "6585127313235216502111419023607202115168882276771320178943546160044534358599", + "20805733952846662054565520828206551321957145521190409450891077526078523041277", + "1584895259816785676773529464055176663163421450657396866598813628883854756221", + "20856003384184708896097189495372900159489733605505425206950257455550934589790", + "14069406225378546242129093232844103602751521581800986668621079503726397000027", + "1028001874294327945398022002342466649656159450229921471622913285892988310568", + "17966371882429795190944428324003030336630819228004846253694564940042988400820", + "12876427863944186509000750451848678588143362805691657085788824358176114653258", + "8715273966427022806434959651283926754916348745411538421378504642158804932403", + "4267431569502908019256597205760133122178817596876838981125845973819244098360", + "19262568227942313166139131845806786518421402326465887345846202580592913547380", + "4587852954486808043358798482712674466376898718708260090686361850306798447997", + "14015025676058610927837367157870446606501041323636097276737547710444897937611", + "1144137690247115846969300874102656726503285917182531654472086138598330367043", + "9454537148137071892124156670991734830383436460928166061331273469365959775909", + "13012777452486288707879402995033258068339583475193898659333632638157561913335", + "14224623168753818289919819482009713832024545924040193705962378465158371078837", + "9505762419233185123340587169990814384174703626434894051560218285882560747356", + "7019256137023476309554512440166531052657689673592456244254319500937647800006", + "17246363017424260106221670693985197925080671704985089438076796011281669775795", + "14408593968797438981684807585445491387554770474935530894938751288467685293766", + "11991202914737654500568163346559814604911312199759237827422720619371707311619", + "17485055588733729741263685618351826350652951158142561188142663313955733134315", + "12950471790443580303905847354051728755537901376411054363054627208333559704631", + "1323445558625272814455691764419102369727943978699511295257501714917998936833", + "8841255445674311239873770890653537908142822789956793926192649058263474173411", + "9401395313777449751102417028930156506880556083443899378760756340424905478877", + "21060472724780336112168263494706975132712741930000971588397970288258676527061", + "3812019230904757099892361572360806117962259446525402823615305574055615634484", + "3514498070156020200040973833201476402797883738073136493951314455793989266387", + "7030071313560321306345374157122385026218445152391153750375781343870025689321", + "5268144785716401601955218888231720448573921710587461252164696334890979118029", + "8300685363844078100354914067414753644670881295812529449763437785519065857313", + "18450162872081547013081002634376155990252513846297828484205311669445865332085", + "13292716648615315298838871484647252965100904015865352978197979561906010036270", + "21013686439245380148735850740480550290335132408628769676980938610161935118557", + "3221231898146718165495762474085749772444251029483710808533124131909521295435", + "21706362586702075336538820540433124172473413960581336734430968480142138077992", + "13486895089928308553329688303040674812803987395822848864346610815697956322679", + "17668589109420826004140047157134934003621500937594400640720194981334871115223", + "15183620307048155399117286900834263744557758459406645835780045985078670266986", + "3170561135789021599641212581208901692806457808161683895582450799937372092628", + "6919102281737620426489877909256066802544737782816026767549277126151765906518", + "4002850049662127756373199253004358198702226377235167238172929008366286605110", + "1107642403321371666617924914652619792767807515753221818405051786669061003368", + "11885648350895482809772026695774528865889321558031441531978106387676087338277", + "15378937381250173939485112486205353769964903768096281219667852296031194688940", + "16437477998115322080973717158007622249171727785394053923880768422172683727081", + "7621121366936849931681051136134866470312478335111295204625916349299382392728", + "21215912298727134138155623335764864420030241120133029649046764814328222738790", + "3170821186412794476815678730429053912378444748965057631248759273529827834502", + "16084650047636623354069916778426687798588694909600147172336375822549887134716", + "5100048383300431022278915674482647701788618482574130930551328380700470664477", + "6089313816847452477548960449234592795575689699536263376449482717087314881254", + "3168424144644268762342999941888553529072271091445061821545187264931478820334", + "10404448021076504176124328273829362151757942013323044839630548232003974036828", + "4252171300134965718003785947840070216898476595813167252439064205515819918152", + "6217524790069168495104329195931800727381161902791016429835385350530530741236", + "10144395323727769803680924125110392290775229805005263966394467735634758369184", + "16773588435110330580333921944382990185799235928388619755745278707444537536196", + "7377711139925591251943689121143870901330717424145420584097007639933333168762", + "21066973161927891686455166855433069549513160220938455527728499561969343776185", + "19501795117214672544349409001236794757366887214419994956397195381058548371627", + "2696597170314397939863800656320896858584168884088153464800095340021127302558", + "21168940252375267860138608985225319663694276566348965377356649424639627399939", + "8729578229953090469373121531954476034394661891580932672218135172268595941310", + "2843049930012752804094477290180353526348132895187059237802643422297864799516", + "11477013052507658297840246977078282722343354831202397750212752940967096033484", + "15309765532985207981165438737385487871403890172460978621511228072457639715462", + "5276795386880565031975868524294990841634011123906486763299115154863864095167", + "16891192769456289619320007723900774808412675144364121907391915236424354884923", + "16679134204463371830386366769161735700621394109825781706069839015605424451750", + "3726779668847121591477372195002524410424328772388206362047548845558525545594", + "309779746952337123192541883987405477861316061278735939472746602872146210577", + "6116943109304762420893019486121829026477809860415070409858407396285217706031", + "15461534026148460547793521829520247652146949380168641953893161519404620248399", + "17490188391720816485403388897026469756907238275881766262188942134609165349946", + "695497190921838164093269283587166536603898439348751059907333515445935240850", + "5740644431998005711645731796487090897088650307567273144862464339327188211037", + "2924287064221347495709747210800576855931727720487579410745268421394700507499", + "14573731858717233227103947986267826915706941233771169473071405909223613015343", + "7774646633424887132991406013236011457803238614262239840323638862678503960297", + "17118778781828713462818509847054627300883191299007086864997977171169360843500", + "1050282722198137586289457046966751947431030691327586256768002473254440863847", + "13445581272162801515105119436838273760359267729139949483205892441735924907588", + "18349340856764570254875357271690106769333653675997142444108297087440025433976", + "4652091796588614730462648254434910913694389428280196479944835477366269369125", + "131910217723821243570751713964244292677069024546597890404591990585681690631", + "21456499410534986907068306761035738313492308692998775099405282472250364294940", + "16716064024995806321979269360595144737028204641252901859053814928024784155019", + "20095956414904309942694457361888203543242910622115746869354406017074786974736", + "3757071258857910927294547413264306476593820051587638010209583502745249383710", + "10923680137512329262337058371790257353876483628009769758330988714085083694256", + "9956537089548622902535815248396999405593511084482117014020012399528161074693", + "20271486778333058297667394153786073312398256884133810048721290976749349792007", + "14016317906581503528126751971528140956503207448983448742275011042750601060387", + "8746591469817011275278863986926266284079779997046001490419468813462270883629", + "8984522535046539605932734670718817033319009310710809111062522425202730307709", + "2799492130108020790397631531338556641981343238572990117018166640120082230253", + "20327087348772938506861277218558621387616751140016505866350214286146319911488", + "16319897245400426733698423830406514849234661290508433522979200712395774573821", + "1519870284239742680329151132587900987246869312347047206488363104123425891937", + "16461017950418215698742181372949724919904570515241953684108936425007551365381", + "18543391801737989528567594217602323229839954429370908362237267818290566814608", + "8685602143546136106524472764079001485875727469672211204721272795643296788175", + "12069346074371335540240816613547917114412632205795651309433084773115960400274", + "11988670992502988905175891565316960155103243235237609851490650832292602385997", + "17403966780192169176887636449182981656483203656331951842717004987726351707328", + "11324032593816374026994927363702518223149146507370165210922763265919399924610", + "20234844998585751045105028410680389242420574978256032494303069254030260948453", + "6845765195018211653702320958640753289098798259762906597713727891164248375213", + "3889696987737574856084707743921432094490126858489993810406205011091688718490", + "18124922133262643422852054628088483763756251512543531233603218549062255836441", + "12631624196128994950514018157378748957581711189609992774676240830354853053921", + "16425963978111788432975031188568839119447517807863396285900192209489855884936", + "8953995026906183527591662774513923704717356851383827135468523305945599975444", + "19118152579552440466145418131293777218789512388294771840750929877489494176003", + "1815764556579131181044414049066036485535218062641438722387702702907845125991", + "879360492005214940756250098489982523077085264774860932709313739626061471725", + "6498879898701797309689806511256248775170834501358785166173840360873803298370", + "6056271365753495672454589488039784798700896703812263056079587620516360143438", + "4967400286944517048147291964513981345417368809842939145340090658371340301574", + "4566978800722351857193060210384689061165216230883853755194411631523409885319", + "1472717723310974984615849145251472221350601734805367368974791217038995119259", + "13776027174095858271746500087697263062688345981919975914318610607049564847265", + "8855093701782146194984545464090688949611436896016570549019701846352658653421", + "18811023873792469394695147052306776708450724591075344787347859872893669250403", + "7691358114878791244762289651247071530769998349760928484062499608910793916658", + "7225113154421014531794800873967433806904006446284346811024995482075131277430", + "4255179040498688460969978231175134372259939403539293652133247789357971307025", + "20794639690572618510879417372961642037150870482839234645523330418091682849183", + "8794944058569076073698664070073889624033307879863301731569984292406059326253", + "2823363224083068189197562122730561866372340074232470613435703914053034396662", + "2975786158778622849913792385919164957459418638496064741275725856836606581091", + "16980083616151411398660130004732794369729853408844917920819260677159874549034", + "10213122016673910048073131434217437693976054853300442236815045010440415530751", + "15622844733073950747464963822187537454489639825890383970141531456136711221220", + "18921846985235911245949818418983694297719932450371747402698247786766269226032", + "3046395690298904837024144793490039860680756832120703445515317729068809524596", + "2583060914190138727980083409436742233507882746342184052904895573386721270220", + "15629635543342892581496645526353345602210475132913669274084203765913418607483", + "12153931408209967834920681475366877980388294821029465748773772434628588757707", + "7196162362609954988171353261930756212243825869655325258155368337285953898704", + "10832344161051287447174100117756887153534049211780756324764320129746636797806", + "6499757948467889740913713634114407800462786362067580649690672122741467699071", + "7899568931631241171429470880810625833615306002278541085808972359443075433638", + "12736525266255297750005081634236953960899087644399368181751506194282329326346", + "3885354407428541945392073706505699854042913519198595640992547091062316682031", + "3816703467689192492336832188741146914005957253921702912680936907951936401119", + "20505128951308490293180094071215431973523299690572491020108425696662802268726", + "12432320732362421179661978984313902638822551634634152631489842715192622778201", + "5512525665270220248672390077644266875095263411830016769341860810913920051168", + "10270457412178462574290846807202840698511884913948623978547229481110915603115", + "17881266377985198177178319262577725460740518218103427901653715311091974197536", + "8510066829287194091329244743814121446186389458433991751573612039633575413794", + "9055280011331581456260576278219343217137015829507887846588161405525910242827", + "8463744395607465452751391868824103994906876103054045629692996934328842962296", + "13446532739903962714217041803396024505862416007402976354796893648172348836934", + "20461627446006946445871657481203161690018232261049944659088120713658680145761", + "13772437540389795965930455187657216773904696937414401755764626097076342530873", + "1466290965505206997895149562488255231958570328271915401528301574657737237351", + "458020438009477539046680120243558779131470015073376206673948106465783075724", + "1421211327829956939723172049232055433330521137466148179271043779757165996383", + "10974718981737058413203531348845889514701700275404265370241257111393906385203", + "16573626183656890049533788707333278060585059246627869841186891657544526527154", + "9919036431890005168544961022934926297421650894480789723772572215746478261369", + "16576030844958446931425212176715038836356393446209694437953952981269181564296", + "14994178560671337822305816896620048994236927709898591331458613992689693774863", + "14008584724223915404998855561309674162219693188120257826434623735246475372519", + "1069086166685137961564210887004146480746150199033946836704942515923278172866", + "4441018674700315637078726026786902627166868710369033622977939038672598925463", + "7640939046542807984747271754089880008702702616120100267707112408768750045699", + "8165588904763498143729494321532511422369875008004817175656306909794360343847", + "7474879615118088486232080639206966699381035482748779385536890548622822072536", + "9155173050525839883630603125759778497949178563635870467694197797566249638786", + "1792459779962272311746727109790955426562388793812453852666007819191763428793", + "20263099395174426853367767733535578420938395109027150658334981283108594115829", + "10303451485708370514931418998595531573075964003678715177508051557531947678705", + "20321308667229656744129683025590573929765537488045735596831342276888215355579", + "3624395650764060579161285692706624195562301326135878674105170566731046375182", + "13334418042222364794805341409419804498243524763983861307681793920401725352392", + "6303209034428307796195867505386756492498981454350686160404486917810670093803", + "13336041503866400337979868539402126587964669853045701517757148479437143265311", + "18767353303859794045974955054051240567712613481262860811042439117400294729243", + "17648775923900705033106796825075257671428721760143056038927896765118752472038", + "21205320334769131061393260976939399463360336322318482845795119858952799244444", + "9155233455417262996613263565710157090618649015115334735214563627053911364986", + "4147290460198895367036286052342502315935666580918696244195287512520682701639", + "20798839743961894523110712994056132446749297094165852697406325453288254761759", + "10967475255107274575063249329253351284200208982490877243231819333935797701403", + "11107733429142106765947338633972307528508245023783319424287268815241476152303", + "1684156923230048092780410733570635056170405885844154942542264956876312136441", + "6785232819930341498287206635949744381666087373588334711760162570781902812411", + "3481752771358613640419060374040695972756380958880755418404724590727014534662", + "6255654521236333133437711371709951475374611142503566953029069344699939884443", + "9785296768537417566104801168235697242332552358062255540417443962942437906303", + "932526381863697246577606288678833611354323840639105626154693740791708621269", + "9020085732775576234837702925603013416533345825301814972240093864457581450960", + "9671990085159642158315772309293609967281764349933520964615615069622307368396", + "19046031964146698646372035717767563096934497588033260517438574042056341692955", + "4697021545834663993102099542459528774847962616007459562129439303924850728783", + "834623291329335140949595387679815012162663877433757180177587164505466461752", + "20037517567993747767719709123741990248999315495577556924410214834773675677458", + "17745162106287481150568322325139792027305354960331855484108228857447789511536", + "15954503136290327680020013900783441236098225469294487261574644367048211928766", + "9447580075462399824000861225221012934395732074158377365412928734066137521046", + "17638109886139425399034325504572336884601102137123870982594354109676619973628", + "4210479489522190727435015049116453108500323090316045735249662201929219316543", + "774908475070259367041407977077109033779248383557848908386935310773126844186", + "18071521129238587956292632288468255723364769404447125651660575676226691230621", + "13782927851189257932376007280798546946814788900735661108027866834341111483921", + "9061904191092017185731693336948023564331336704118962360596629665269886564501", + "4646677737877430404232975108246717822854320420060743321121290725024507501530", + "248089340757097041959106969939801712104654938085655092524027449699183655736", + "1925061776519306799931873233357921480445138537754738082308417409427350959191", + "15090381222590604653751344079459745088601676165498775998808367167579158825796", + "1507946310710275058017347456880204243614713444138569740896198937197257762391", + "9047126832872244897061755443779135724439127934417426920025891305321906533199", + "13210467659674398263347498463769321394392811495810195742972914861526145024497", + "14930790949584540337435206846889685669928690545385782178172828133028677953780", + "16709614617820458913659268544942181680363927832511699782451755998512279824691", + "21741729565131743651162167923641181853909251404044798652854243082519505872026", + "485748276371306614511019734420369335387299906747036897161996666725324275885", + "113729363155019628451995418785166919308643202089193856958029953269497359518", + "17315235749721747007692514702872348378105966945192310104719562066051041365785", + "18164826565342651411070681557540176534582615273713551079309038721773735887418", + "619154088023795178917549465782795919604596935887112076630902791174323663727", + "6356343581175745844660675205642675571886993827862033572086056964311839166798", + "9925861911435596968816749828945917008180837850750226489783169376408872855250", + "20485137494818996958862809145264015810010636121731140334074571507470008401090", + "19656735537075233905125360479201796779994483024563540752004528779684256455735", + "15774243068028338132223253794279808721177740456197787086161475053638867039504", + "10497591647442850110427658608196767909160517521573199077258279280018098065029", + "210438711366620938094565498037172584426960275594120406278610962685482768128", + "14574298645311491089278146576548733364636851707576602363349001846285614576119", + "12543161131818467611229160618197160281328232851440846538778664249828945509460", + "5869885827840429160723685065960013290766384774919021040094074809404023704797", + "14800413279643792835835488664661954863340552091411990276270728475507101877386", + "8706244411264045072904148746698442560958145712983473320099851020229801141593", + "13670524729264788581176214636184652482954266319986131183163702474027069017797", + "6632699635952855355682293206130135801472538218093800086693644923578553649842", + "14292924690435752338909900819628889644248019746008463256978678218121438364347", + "17727807116656957204562694366530472131199058877798797418410969267673712196106", + "11223858699504492688371995874202967948461173433281557745471419331000190821973", + "15274518373659950909975149452889634174127158243424169830798483206460674313743", + "7047593141729299688704504157654237466438485917193869775780994636019451364555", + "3378912259124002728608820025874776626345774864595321854977438303831475863909", + "8837103908146248796259735965370584845236705198353666155001962958984824353698", + "11869977356793268256679742068396296621649101901864556682314737356997442919617", + "16469201342422734819465744813830056682872807194264521097865986969265941221776", + "12990791567002738838300315843749937126679083554067128201599773654099601117791", + "20519376488199491586810872596388233936561051835647732260479226405592285629272", + "13549175857035221960740890286176570298280920124622104651989532035707272553373", + "15234870385685844193215578943145814211167163612264546132211506983633757513134", + "18704889004070339618907065844757112006583041625995006884582590095948666509530", + "2234955996987396568560381823250866120960007674191266942270485492312904973151", + "5564977611394684319243720881013051542355462582947649518330549023416464959605", + "1112602741380454855327102347931753030221494100363029829630354546334828994329", + "9128209482091812367673670394219617423875730198454831896130112252113491505252", + "4376101007428143823621574836425643116618104664163736787280388345260726375856", + "17331301745157506574267466700454501468193891305005371065208065202603272248798", + "21846545094192300969525954249087973697025055955155275797969191218036594965229", + "4498927631211901890366201669629694821005085404418517058539528045609417501903", + "21785888954457119007377380145673667061898185873161711282742721281162112687992", + "10412875728643419025694818649503070798945684772972009054835016566542500457165", + "10759299704717838172704330861378107927462309935609553476471308013349659622009", + "20590357061487044454315237834494568787816431390448334652405889873515373283815", + "15673836157910318771949663697819073723405442238797105611030274826598578081102", + "21475838557209838850221160141181337897147127057878049055974104365046170025610", + "12207716311215040854533276425309298962214124105108870363296256769166507046674", + "11559647355273809434894669847822031984160367996613651160672893927347403624957", + "591243049503910187533030984285442752553334033673422383798814281778102029479", + "16149820605774446835537612705920133098873928503868848274855310057025837143826", + "17994206225397988293963936097869072606400130753097128779836685484696065715846", + "14255641384045583862403050464134422000560975124837558223693835399889249787677", + "17504545541397972120376845150557483982076571058727369435414053758625774050033", + "20262964605263856854671661755299361371513888427580858149435552591882491905348", + "12438123120680430355474353651690582027160630756728481836282192739044572633306", + "10986893609190710977332579571229244255566870443973548516403950419251545530801", + "16087745879258651861681909407049342984746351306967389910641236570806978994348", + "3161560484131968841565856280480697486595651138902139808256180482675228360792", + "13935262764632300233548760451251751405084774897832419843437366109926436448024", + "3163708356211087269300587340868486145250960085863061247671736451529273060199", + "2135858951319828747123196912541413689531634489616050528404576322173340406195", + "19499372732572987294622585729244698705235805453794292187854255591946466724248", + "17894089552733756400990786239277662075181623012822588150546768816557117698135", + "10627476289794518149581909173158184680927686574280550541626159986648732476852", + "5936760260909841348473330607703047527686233710292277373302498592584078811475", + "1671632573007285452120449241726645612097503185776127730816609206067570503177", + "3240964061357526102193296883771275932317464053465161060634703562592821382373", + "1379991033533041123683674223861213511881534224092884787544607760074673933288", + "16612309984678362724113323611405699676953754495433780645121123868554119586714", + "8990362216544353251183644843626621823440172244041771554179888258848070990408", + "7792126163077137553546721412284336099138469839853005420022151183726709890605", + "3411642164274869168109174406711447272755835383047387248320765695217678712087", + "20744964603175609148844959505361532818311810796853446841217756183414690636777", + "15406348836110379416850862929702053477202461331674247559909297027963274533036", + "6655070454278595774182716706613320667767771244472415371150701320921454970910", + "5387596765852619562692588945067153848362301378074547750716364254552830548618", + "11374645815567953400996409579523063654937913407852397185357281079289695498202", + "4637259464430266322534397972465849540585061569384580106712860758129812888983", + "11664424120893756026105365780051433965998344366144921839621845056675798876861", + "12683173829762047157652990138425355617601406763777942452461639149511855161467", + "13231456998217432692837253637279353598668867747754119281167569613817285339906", + "18515103011277984777089818507163076049947207220694092563937098383351447664230", + "12292599779221056615130747040145001893088895814628302035476053980079114062115", + "6616224505832681391261988520162269850555430131210843369761179293711760268991", + "9345928984249349577886491000914566117062182722884874880308419980252230631490", + "18529045286534814640266328796625676801335117782021569247528965805503339014374", + "9213994428894984896264727606602499546795637673535140817494171390234365346197", + "16046191804660661030536723745809610250792224521907784347921533311133924729200", + "20387305341543872348027149959300247251122586296441271309757521233850527145501", + "20660888328716295053746457587105952359847177180169600462778173036912653930309", + "20868619915262427287234582212448052350184310305114553346457821129256743588185", + "17663781345821195819131504897468846255222386958594579879767055754372925042002", + "18948627844717262137578097615546021495287533410031083974644721619152121684198", + "13155646636245265066188794529431453696019006721019099462642586385520674193264", + "11607002707400435612558884657345015071868104333677950253781252883531031962578", + "7814494031495579790510272464332992505237344345380518199394208941034262720849", + "1133679025909186037940532130396250706184367022550359228793807250960294591300", + "17684405674061959945309711765066024811535672808158415749778270434181980067051", + "12226477960064982944626820350420332900927073584585614331096659786290796181633", + "19811925349932047710573270972607617621205677733103349721011497772488325290731", + "8648978019377605720004678083600719374811142735403518791266825142192290428262", + "13576053523641184415871793911230224707049224969614290468646557972152185575110", + "19246597006093201923867388663361027928531131674594730473459152193253618076466", + "658606426123772934076319360192555383963003130831902899767201848799898493860", + "17290137813713243852250776166370982880777712118107986170022122607636402519519", + "5468484217427333109722188824068218191654858622412608074953511216209386470685", + "21574260030527757195923820887880669668169386788465310192452085714817280961577", + "16977301410295947721817774534383483730974509825154556215531376468755351549427", + "15733754299741726976786941703016488589821960074385924955118036049474131193882", + "673051665852885808394122319132734442090810198084101653348055105938368910407", + "13398071607416834058601862099131647257306816396251547336997555114713122083633", + "12550653745423625869263455159672489463891823157792786197798074997479171696837", + "9401077435832768325771683033269047231487705804464911158715103022642914069963", + "12470382814922435426465904555965877134678525017061716145587676176477156930917", + "4934832010550666003820836613445526232712748207490447572633148755378222021432", + "10147431980198881931337046775655000300780675339813128987464503437796530727819", + "568707585724749374908018432889236271799675619271430223486579915440515081761", + "7940313483819289305875198195901580599194224791864182697672727755313391315497", + "18743990138882019355059105644701125600589318429506940478381096888059645669064", + "21390103921503672356366200239425679587351838964225860732507169466542370471389", + "1162301008434626626179696713526875746106593765970497860274277780972785074172", + "20866648005214775989252883040721823770262915606193206494879341544251201195074", + "7345491533727875108206436501195735628498565230546589655556895625258093485858", + "7625514906338569732343456767807169396379912415318224530910677456500209942160", + "7862757427463847382674463970373593668082249748547169027835585420099101631793", + "10678296207725321824311910857643347417220081751912275071080081222106552722672", + "16952109495603736214033791235849594583684019147749856522239399814261672810874", + "7275820797336428291178396248364001448190236523398157638561627689896470220284", + "9969981466756203881447261853491208039919719371961809442805550510895150629471", + "2820683912350770819480104529177445624170795283688064543451455124015297123761", + "1768227783012842108298280205661549942041342510905211540896799241998425991810", + "12106377471628405436369258554037168031059303858931832122655790690193581259368", + "11133684149892568979034305171877692043755100938665306193308979862319584720202", + "17034299967559262110637856726685484180103963204901371145115468175393665761738", + "7846191388095544813700988786315012990125413238605101219369138067522789941990", + "18501907765236851484806470528611625980973567313044760445349490286993727971054", + "1249778578878632628279160684089277562912849600486367726058714364522238473984", + "12800296338215947544269523539435530627816725976238738793426232070819346748361", + "21158276531398412025848449677754681262986399643402199704435277598413275051402", + "3445704611858969368326297112584817848554583085326694642313172161045347990986", + "1096936495127696656709715702297708886712481387809916555366077657639639717937", + "2595620917275871104056107465860277106075411236679259253450463497473275008081", + "17133880510415980077665570077127522127719512648188691671645676431813033706096", + "5074434632642876807560017812171878490042102579785821263047865225182212462287", + "12326838696626858713891707574688276984801973627108664533453869827609207218879", + "12304708188533179803341734069046800825101691162690698708386871234713091258421", + "17039317314373515840113235553933676165005713953680758739139458602566292834442", + "17138392904367957911746841143250516587142345504139102606501472531272348644184", + "1667176916358419631869035157338470222219117704099667397276080580945958414759", + "15905657308104601519308683078473840880633544254678278144766436492794698882065", + "5818790161586099604498469370871887484543304527772047827828829241332373438952", + "12442306790659206506330372960358262675060765197848150572966156688729178055833", + "8830924073341698588202301965823165752475570241349344118304602842583809330430", + "1875325218295968167735653385555759668710537053646457950513379995102733934773", + "17881009017973237557824378556848686261064176014957336169110006388300690909574", + "6685909350747636031504999384308339032316756393531361646826148497177153343308", + "4784523754559868056092042886072700730690109555243230003862073871950864412709", + "3579228774281988929604104014107948733265891035249209872698544379599035221295", + "4279085918474431382642565261103862932684902464862097608738233691705350921017", + "1137759862158982763491809087349360013612105180026300214422560224275715429456", + "19720837769260646491270657488868445833170057293472427873975392955686128702328", + "1718964391395646738355239526986888311027176887298320241700031309916500343965", + "19881985451679395586972172156300827725375888727627109004411614649624414408826", + "12172287315747539949563617756773178635584674160408496477097950831147264075213", + "17818348202724817301996509561425729131915819248800408750587580377834731903089", + "9526094675919376961704973755217702642807170426879590367299173409422751261551", + "3828510101756167773639196204933352052436416699730184799283196091112205339599", + "15234684891802383017182920097366274346299756278272648689423306851279308385365", + "5259340948631483055118623587523845455777103002480150827336361752510452569472", + "7522204711834833179288206454714805063533565847207420606996829544233502085239", + "4461024416231372224773966846126492961855795819183621636555920813846035684924", + "5024379057555899833767820321243484415757741022843259764504133153673503667684", + "1944348388731771850836911827979470257687115271361319233348322098245989069259", + "15065306781743278475238637320889039722240861069687500843669623160807616263519", + "10185565710140214717926900268233420932083773824278146477403635012592608678058", + "633282989982188437228855410820528038963373266773393154835968536206352769131", + "15070609296388507673139787908428420573100959040320412658731328587817279331759", + "21412207865606943642342520185052032190311633739645180935143927525277916223829", + "7878150466166009547688919693656375135045159152200952621922018706527536944814", + "21647238538593110963647188691796027858369673773306895927659346354059471769993", + "499258196791436830394876102694379667868532135110557694409916330175138906869", + "12884932140229984175244344672594504633237231438569204938753218905028898420879", + "13897088195397438949124517043146998392030835253375096249186075371152838123957", + "17292635307267344947001814307009165392877577631472640481991264040854674668918", + "3402459730835246576414179745580422332982133349475000364518392993042878622965", + "16196872248846772839551997397431726113069640679308998300903092440097004592538", + "18016286200009868237596690430345509415593965676022902141045234608328285884800", + "7857847074246536045428962801424396151797565515320554961976852417962849298458", + "3893665144753462156875073379241733664547431241880239458489799597153819411492", + "13248007323269384117634056450668957554780628822533632623855150478480951625390", + "18276875079677833610517929467816562761552054119550676368999897123627796943528", + "5189710327122023109841696471286837143455102090557692433286249568240177726287", + "7945689534444199447942235389147404638251522098536584100372965245456120158597", + "5423767002711680999240727030908088708424949959926318146878189397264151455693", + "5995072902526586585223000514086044396450113354893476089124870631086600489121", + "15275396095782853076485751600594247065910139735226081476243182046971110416839", + "7337085477458019807945256166142271568824298087615645600806448794391563720590", + "672727225441339286039441508031715798485173015033624765579287288711179551652", + "18686118639158601820164163991883068337953517102808111440802394108699280438488", + "14028695600256037576881239065837481650658243799767033132853744776667874365505", + "58443191205451688703601063117164997697541381759347793811443271650169777861", + "21065693836733911343227308560913091721048742432913504195640605496691788221566", + "330731975523362608415899785414131718097488715216703281999950906065685546389", + "16761217887229483694018038191674783405871691887163059178481478503168342732832", + "3038029821773816605245101973977979223409779873144308602734440877027458504742", + "15321291577034359343843366677170820527712726038561116015047816977743924220386", + "13707411308735348097061010267850476217721836580681738128758120478182032058809", + "17436870236156178819421588659602200365388268058796067042132167176029472760360", + "6398050943215095842335686342334715469621392080870897191300696782676608765887", + "3437851705781701539278760214561262405409371984542409183492392158766597659391", + "7124479506211457523573057217353911508610466081033238660465711588737967142974", + "17234798882124088040334413616145917195326697085511927239835381731514314303940", + "7189678840883577982600011373449005624085144056595391210996212544957416984457", + "11759897219791977703855743039753523539327151517291958119140418872939095386327", + "20863399992661754827674386311991156323060622036945800022666816987897813665853", + "19303856056826570957071409328135004293560711503205766767776395391371667771674", + "20145537319102686898024411390314804436660254524742986363357188082440068405993", + "10703774427752668974576334932100051927133058205544953271144849898179475571339", + "18572949224433194996605971200517412011627412770605201537755920772491000856992", + "17216568647787248412779004183002114283399247525457054305327730013550616639101", + "1149230406252660804924057240581611320485721839789360487426995484240018883527", + "16555038770640675528229594153638806495397639088923421322231578669556920716448", + "4570242682115771574920314226681534421057130734296724983202350760375592356599", + "17057311432878064305365984063315754288324488570316135529616408791432610322138", + "10528094520692277398437810246572829940408932590689365643375030258637643836256", + "18372709972473962897678516007961361205643396632377488479769034609756086158014", + "21838679058944462510227588871439974934573578287277780576996692447397949481484", + "1580935194501439869947304184720525369376830253681649968786933642354687903995", + "20467445367185359735288490209436765649401864859115524728703996670394981839178", + "1991219687938782033642326751499030593324853839359574632972633310438247709458", + "1439755133435743273361732699887115117694558270034210455263872898360142260289", + "16300147327505011303454000556020917254890545519131742846543658162285928042977", + "20520490665047234670156871323008670381517271529350180242819417364083994994438", + "6542862810250661443960454367170533117465189617181697143331232138215407738369", + "1321343667823388542880641322895423041177577518845790583045290901719235409147", + "2442599361719607622459103501177302579953568947914600055748918886479950617614", + "5868547468478431065596263697373807107429411676393246849408309043501871198941", + "12236312006031079186182743334640941452854376343312535021115608217162266564748", + "10096437059927455034494932369876021874913717336999122002472353603141122745838", + "16855287129299048285700210681474924546365514190517193678115189183373112094664", + "11157210691115027557284750949314979990011151769293562411757900634452166569473", + "12647512288244490046752876853167515938997386483355090398954955982145642073003", + "14545106158760908532417185649648478400561191409850112377631214176889261943702", + "13380884028176250201501329354617948687842379239413597035170382200236519084210", + "1405533288610146305353614064658402594063683037599662723217460924638972391780", + "12520645345847038927033831419634817247041895425958103228961545058464030447700", + "5466316270148149873459535347452815877745317540692907701390798166221830387727", + "5692512917835452114275964699861010134159019192212203758647174343214228494998", + "20406430429993275527714460195190469680616300984772360022156779114506307005915", + "2464398587529067351284637471629204475741470377175139272463319164375422871700", + "14514168707742420576792107362849792246555009352726620152895701096554575857830", + "13270866810108509432782712956020266539382253503195894711776480924657007904548", + "9134846497886036320932877977667565061646780167819704730891157458029677347762", + "6968315929580902986583144026090679082271496835598861747834541738159673277903", + "7271494204378938515839411341192945686765403888626619615653078042877068436796", + "18286507248612017881101512297381359645934672218434256889911439358758424747545", + "11110097749119198784554144689049124335810208369471373426127558546444086832925", + "8247301320031025187145317669710241636727060646792731418249820159438470005156", + "14294937643948997192288252800043984341826315689719836039426003941357321500134", + "7348709417616598268612977306891011618520420209855159927526151677200518293152", + "8241665213979757536094384437357490949853796937135356315008833005380426051194", + "14613486093064180194908604682667542845871833534699460114586605109597063839130", + "9113009852475701814533040575255279924173382591876828814441237692300104301457", + "13165038554179369948431676356672208670169443113631826535771459184335356285593", + "16086269628133108266869931705626389777530735876369884384351431845165268738278", + "6087260580031589238539099192443153008156470383537608551794229727048294996340", + "20512862830064709642749702619534843169107182362836772285105007708471619038567", + "19533181397104762456673826441378799870077181112746023293729567449839481548356", + "14705683451830296925170674619865205995188883759493716060771015969186134607704", + "12605792817445608495860292975716815181045267213200470818664710066347176280047", + "3592996587655107072245474938139935892949344064921754274126873928977701118749", + "15294232235999070733030126460937359944041996136808334336982336855120910512334", + "1582309023481075306597300823293668340335902140969083485284345214470852671798", + "1088465469116422019323930771299807477223547422484605269302564104323452973509", + "8883047416031049757538663266985855517015256170002963846635310997205392683864", + "3176854208559033403336658900147847127925792493324423188639056424099958393072", + "13232382305984866276823393930594443402152224642505744090163285056748376747918", + "2245408257366834949733190872330311995971080118654860159924241594642495281059", + "2379830947280597194685159865267943843813010731722978284837340564719341924509", + "17826177593330561305441136255554046830158210141415356927118963102466292013052", + "8247294876117518524724460400399686295737179004092227538368916244669417581296", + "9784484010004736296495309204028701732510703011300894445280176432324640320289", + "5252332428538395620048044694806285484695976570037776918613895482295008833982", + "14121980677685920870338206469007772221567007585360563336746921691628879549127", + "6873280017888024918917070095341125311616790755139638467124027157089622101088", + "11318363160576645628856748485098328456894971803038601852118504122294284627221", + "4121152361595345093207401693369225144531013563993157593094962034269624651820", + "20683333802753484511610127216996129924370396812165897253170891149253939360760", + "18647670031404843711327708343249425853609227031502475411522973225042672187860", + "2086657429367670063332013344180770222302417569271803215009583482264097874504", + "19709435480590614483363007409253356879041585154641286906159427713982563792526", + "4258866914660942354120395242129829740747833749509017072799985182732656616800", + "9040566395371463976833369354842616182669127821598268274888838547664814843928", + "4116084355554837400529216189356527577376881089426150781634827262283154963048", + "21605169971906188704396331617487188526616554159276096155817564565239435978416", + "17937211187232154582564924643486965895957558838414375726168318393094228123633", + "1992558685702604567028071709121593075020313087150274357559040691997376317338", + "3218330818271934662263059558699096796852863135305095433439395639068666897513", + "9251019796910681440348368762961435892628247766190360978557679196269677702320", + "15836693249656382763970061319175857247542072239256868703809948219144542014793", + "15037961154776922819548802422798270596812938540093547487192133997609110330498", + "2832985667948889103643595801116542064299718867280064733138075131374461684249", + "17231308893316371049725716015653771498326104062757761825930828499226120649063", + "18440767123713815329857061224743112764715289422724359826699690594664366648275", + "19107308066370859685275861140608997220650596581264375407813390731366162034891", + "5404929079073337935336499214267301362161341750373615311934457105389796446473", + "20619481508117738469430272904911772560595466539395055218439600469328451867796", + "13952701312362872281963137538185820474584748980975982879243876933687953690941", + "249746714007340023576169446261897246896602417046451816160506088486861113993", + "2031064645897563637919423800257706696891919492795925079196751344512608989440", + "11565396752616415432819810596660051755078386394477458037668058318114060397110", + "986400357247356567494096182209394879745469890962659581282966363140769490357", + "4537710479751915891784323654735525989928281714716787626598475607721311119956", + "6473679789570545843051971615499250002895246840329033388322918048865214899581", + "9663160517547239942952324390296932496372339501369452594336052141919689254511", + "5816463053578002459222339447164819661176549623835281810359748779077848682107", + "15884891878740533639319804312855499125304552172182333174674400304788572220390", + "3033403576169643300191876532878569681892028128211422158671974606431741064057", + "10358010399523377550281291332476224381854765291561168350961812145251103430188", + "5609119676745360689177377631509255444635743595725402489628064332010648311979", + "9629834817212476647292649072966526100818069994746003213667948679343174132340", + "9902671572518009781416980720459757101464549561371827438607346753254097417791", + "7861900468190840021147507239605680877304969442537895993247780719118539599806", + "17965713539282108900380748546130491121922627970953515439952089259237956451418", + "11875127160859896409551848475414052588524539216162668112975117611722753563101", + "12036060586969767703747823124372554748937989587973573397651022172958180951726", + "9687479700009208904244134411901361698716242095445472067017865326177763818896", + "9417881715546662771138833677126201059337478279387740105578558474324645551658", + "1453393784429512178361095160478955627301424316545464923297558140633602643591", + "3605661850144345838926820161654200172773665263094741288943401786929181563752", + "9633343945573595088617177094944240138296472849064303543706399640795820973418", + "12473139325003262775761026452954333162901791070446569863302424457084077761149", + "12050383798910643577317544771125034193641466979797280393384224161250051391008", + "10819280347517551331483926007074671789351833709996171469616831904311265033949", + "13165812145639084020184122275986846677464323294523670942848342958951658208097", + "4349080070868874275277456883424617452669491031060808106496345174056753179120", + "10004465749978899491390807601888552616948704671794075232666191099858337042324", + "5250553500845345497570122559573167112673420661316457421274200097450990672373", + "12898588403041862218453963458103462644872308284833558427909835998108927951654", + "18081519381738296094081786403175972446703155704846706982096034153576188388497", + "17428269008435424731165207394539747495252057789950545989706433801022598432683", + "21363561515458498091299721959548254375350819182606817501876503845082368630248", + "7730068437198222255038306872157350083702057866362961468917603557490811496832", + "804393402354592729694369925125379986785009558185994901175413585942989446396", + "5734375191529745066389276413443500729878560514347145222515433157787509440061", + "12298550311942812907291709877922367696701304981384797706564000787234950045527", + "11599955784759432052811066625229516027776562566066732630526030964712154732575", + "19052852164576098666742966317699449063762547007283635113650074658720294958279", + "10025188726400272980943623464270412609902684461295237328964275116870569460545", + "6716809200457040320003933672999684025455855713745782618771058247491604111902", + "13265049252191301811995103374452481986951626617983097109447548540667223415855", + "3243273032455921996687884516398249181181318053856384035604616213401999420878", + "17596345835810824358133183122550505795231799288513594802190425260560699653345", + "14128892069525759858946235743223382009747348980906404699362919849705747120829", + "13560912558482649403704338887070267536467549681310240991684055262800855969142", + "5146650134021051734120451478394931677954544403196703024645198760974236051136", + "3656624380537215154312224820978463973302627823287220718508509926228536255064", + "17192240779422792861455260231600955507004716950247796731699861536149977973585", + "14505892053752720060006987522044578502315422621292564446333307406645448977523", + "897766293354199284998764120885006378362273185992111179631631299334090746025", + "9385522526111341509707107108914634969549440746118599228963287393277576591675", + "16755551931355332730912571473383446100229118084434859591641172409884703288623", + "7998487350537565460407312493288045720319128967533762889043801066609578748620", + "7253071994474199371350196978282275858691584350754099653147319975858840509065", + "1574915908528094850594155669399292110958198100218448518346743116412237429711", + "4964033462714392747475438670479181630637512997723146160177292638427948047795", + "448448106178274062085484969210407935775566171618153143848162702130236340948", + "10632678501409460480908899293916037077432376165397750836428181066894132950422", + "6560185425582299240893314836102079088239101938875583740502225690151903320396", + "1579188680045650467520863974543764324416915154559115370865175124462148613819", + "7512915739904886375944428372792517861202973027020854741625268080946313568103", + "11437215569201917203849322845056384272839597879584906569042314064918170663914", + "12242031850346916661228168797937142001106743366886274501414812437377823138597", + "20754147834828558255955521879849243373047193258439455093561468286224927742847", + "8071694345199003059727834592733443749021397716021823212294503054151589204390", + "20008195907609188950845942022048531038227320303695709133926612651814670959266", + "3407176450804539102225412244906714437508697739163709736953739213204054495282", + "7164733851792769372626784323196646242013517015794517414070184073240948375536", + "9905209321913745841790507726277960404981926942651589731149669818541827874214", + "6005284264922151317117707687482989323446281090296110762814732182741632133904", + "3429846891758468105169655789950721158959212737421209807144772069541192906919", + "8594924739933231799186811828034272621585362372014000894229076451226180774305", + "11871888849049325223331295756953912508046243621607006393548707001434995954238", + "5685905463286653736673250214945816154927032180553041236977187605699044205816", + "213743477607202689216486321834878291503247072102535716407202454507834205331", + "12562677380228777117621213265298559705423375836884092254836487933903100167894", + "8185089847800873546122723865396073717154949803733075710451409261160174437901", + "6833793650798095878312843740119765700866687688998823144083128641706792979819", + "4953313804596399497426456604424338320630850621290040554640752190475708172557", + "2236942430605502498740725704270029328474562664606378240692747298661887827319", + "1513449164658866312019182303051862147479160906256039744485216368678117608853", + "20317977663255757433651259494490740600408545133225161089258307793180016743554", + "2805473554735433677734689688066817081805362311169954998301659824970436339447", + "15815182380045621180846286591593553907538664337286124104922652024183257765519", + "8995040978147557956715534525198883385024370476405764975921649687446895470970", + "8204191497925877512228406532110351579276808609895204619986274589454615200331", + "9804350573365946552974871840565664742492437387015600083520724662514547605734", + "2670072936416198613788578252423721270352613415834375050844481006480844299140", + "18981033918047008332984286244333212532097768099085053122971946780791175897494", + "12954085820551020895974547504930170442441222377530978192526624489157895091072", + "9097838829535778653934125096834099047122481765699682650259018742771271485024", + "7412500154264631610614836174268215158855286123479543445207793653417339063427", + "4570719057023209933442917712353770885061956176422382707340131424665352082829", + "3764270938841013974016907794451240962927342721628880682930414663597308799660", + "3003259409352957401069823305356475477293574843963128589242837442372343327078", + "4364045643562272249326662669199826714126432097046293502341074915354885207241", + "7862497546139280137858115009924456872495405978857024048016201451567691552233", + "2320345330709275050815413684718297588549014329479521210951823856644277128291", + "3472052133118830373581093491758594658278444460214482406644025496494813391646", + "18719780395522132269892764976785744172160001291971824246311191396996341879998", + "2052034405003094013719516407189467871257070713148889132320853589752948812286", + "12668293034393175159240347623878811740389002104040561234210863028172486003814", + "3030226117016103704707119677479690730613220303512226016348578882444095217375", + "10328007796868265401669271360208572151985515933147061282836598786351719442924", + "2936234154384973501504058165728446155253408008794108135203900159696674018692", + "488196422923618399001126311361138933209773385094782255292621218871598725659", + "9986243106324062707533627601692123931503570667062976466271294044444369357821", + "16946417777163299571223971859384083494444080413698624920773661721819621003999", + "6290655646787769345150128237305014645896272983703662862175950979175390099996", + "21576354856650767064171440939276202866931509433119578951322323205119147402280", + "12396222895516500136715256668686614381644469516081823538030375243855459590807", + "18323538829312240204982678233417444895412920429922269434090001873145951634449", + "5312977696789603985280968610327711512128871567270265803635161870118441769141", + "1256303934933114233929368123369287749699533819478400217199836088113619997588", + "1766722289253438664291189751442574085464717822269757100835532731400395828058", + "20358149792139162776765514570327167438522971057031059964363017830738523560815", + "18792275959974477341072288096653531684536236050557537981692565367846354045353", + "11262334644528432796511003492639130680887823443747110676752140224527884546026", + "20501847923202184923710061886495102946632889060597711856226126913397710337656", + "2407267120907499137598716355969720653158717479075795831021615986085480804669", + "17307860167097742136268530831561503531461792239855909861554172148017416960805", + "2162289869674344221546800054490037354766673860343307342595970088710829183154", + "7028199420603561050998184473139307688291589649001033510888359073812068249126", + "20994672525924208572413211399438489393060949253402639946739540459305617910138", + "20173431861606460093483867873896693022082731481398426039971589468498814864556", + "9008871249428555602906292660739315591043104408371562099899839775533895523948", + "10700805877647067083283505504782153003756758200729717834117016178491024406477", + "21112465741764258732843126921665151348166174697732269978267747373163853108430", + "11434984188666323224207642407059759324684918143122828248101482207244887988138", + "19774670032646206729857931401475084640123741797887602999266543966674579660422", + "674655537444683637096570248236990032160746930820910298495859277351312061534", + "12399952541366504233794506479162333424641228812125959815028858786807335765308", + "10787188444435222346993386033929793480957373636649543232735691292731739491178", + "8444484958726309128422673453859105535823575309761203789528942642797742183861", + "6178443234860827296437957284319327879259579261423150811121652495400576061977", + "9312686778448920745396192252021125483491892057300805307510293202738266404216", + "21035346041382930907137468070148126300607151584874513632902588011146411628958", + "21595380874048139145422335028184290720358814546434544789176692636485866078352", + "18101046998013962303174610041325057852573934184601619434223015638490343790989", + "19667939085949665955991312921627801957163412888505151951175612729243786613781", + "6926779782333484660471017512018931263032231687078574887001518725808608895509", + "21080823663408874605869582733040113428551737099016002655485931862299065393278", + "3655825253274650356501864672127621389343134570773587805971056417535174536245", + "3388541056162402337902270805874665052329588330241925030058121260891155159271", + "21883983703980930327495083710303031860831358299523823198355219501906879066592", + "6926045735792789706902681099796161456533517354066439495586980016226331896111", + "4122959111197116982155867189740721062669675590322734927703841118527609239459", + "13219980757163558837707442934888299444190968070207889541780648207183773341011", + "13812897721847878024718349018307386755424010495939938584588468146497630813114", + "12798580293550179822728288481477643428051047081525754174406779865733869649777", + "19217125674525037886170888487423869066614323854468572412518413433041373346147", + "375553361556668826677041847695421482571990138873344010326810282960129792132", + "10834658340690112017927173678735250754641576226700757836412274044036729618775", + "4000963705080416713341998395751411942820883040862140582570351568343523301185", + "11715758996898666999645486245570559500678491132871745818362150491793495427655", + "4263507095725155156517595569654198316584673531155392198157637482244808941446", + "328008196666170836733724513867841384647787752809351062335576481522598738696", + "16052279110256413457891846976681654137774297241350913171330588402386482933504", + "8560435921497695081829647680343824474942212198090196568605135998993570233835", + "18716362840678189592322462186141719951790452060116008532478803871048621361376", + "6373296854260461347237798330630093524006414867838125038061282895929391886849", + "18914705717475623119982002205679946947799155688028604423250694242332863378659", + "3615848956195277523544974986464432491666056752600413462508649484050878259586", + "14319383609652049721947038362087013158818175249064317958973877909037224815373", + "16735322269478335929131611670089342135588205793924787907856943392431725411629", + "20487989084414161462431324456040544956671737924589348832484117290264531005303", + "991404779256099913569561460005735823176831315250109345694197087497778707447", + "12403152875035099961283368388852584749460932263482397452647736730447907363221", + "4086427505181709923565832636992346270489207870131792488930389136658523450334", + "6143243951533181757415269381412864770996360748004903845141664179532399602512", + "866706669878257575355607694505677029531447447184317717274975559227499614511", + "5892914572161311963309388543417806910338156401784367910536431408323821746054", + "17195629524346618476376551682847818577110341498528926796026110967915240955181", + "11821298882555096497181913149211502245650586243756443688232286290083613236253", + "9353931901221302781036617146700068445151424017933715326811471823048576997254", + "287161824097962028466164990607592105232453331778514498980389789122436840050", + "10649681231914156546306210447083574697745999931000675911245824305402757632471", + "234428462053245029707379734508145490630528735544251100489484083890623632434", + "18386986463595680658295944092336172488193344109864388929828691162200829447922", + "12209205038092046905635371865560616384451716418834791040178049545643031921053", + "14186020280018069417447809370772570171911004194015095921511996859327548729773", + "18113938321789369780471778880758786581581161378582151974602552514969078582931", + "4811662992940356526245596671631791612493389462608103550402208802794345444140", + "20758900083194181903559645045132621401748979651195063688583538188953422299252", + "9153554145073302417837255133650907646404668180089275208960151746312542076673", + "9140083548410620433186361969381763393513996980985547052071730395844456832878", + "1995008884754637182207447536267941537231422109677541855619755654197330974956", + "20815985241080261061330547616924867409496454481048128927799047536950736307626", + "3230373257763661238115338374579765087994728137593244037768237534083906441753", + "15848607284344292541615961379571084541204910796797596203678379270219514113553", + "10428395164296448020881569101995272889447042680020183589472054992027224827786", + "13680122146242354024351667942887798315658431983426300974494489056276061013513", + "4339724378247845795006138666633339576672603219988412146454211158778911139049", + "16957453138566211779676251195537106997166365265767483860476580167187603818641", + "6903442910536492004022094150099887848831560470459811862902118892324229883825", + "13517533794136732881120013247194703467846939834067648417739110803619549581956", + "8573259981507024845652388913624603863752296398651459450944134507080250987145", + "7184725885552680253794089893559736740786331382981862187745268057810849566021", + "7444911651670419666243138241908768267984541319117854062859415506699726860996", + "11938251205745649282918409002562222384447298563524098642560439931062370036028", + "18732676061737136378462265890132297759828778436681433094170064283098167411411", + "8846532794008556689131513011423108042165045871690949838768507118557843169852", + "18388218263536244281946085549244925864711296342518046885707040924859244937400", + "17560203902531872086160045342305935339585392467343746359739902020514592872929", + "13543101793457457855715055504451796018964110256417682942067062541530681224758", + "12729201216656193290459172785293440324005383463664790973551946761644249872265", + "20749632282565878390585307142178974866932241415051524225408429188255209021839", + "2939350463247023543160206627078350892997171197177918468376868549940855641486", + "479410521971742071578523736929989300479738063431591906751176641695551615616", + "1215960229051771956876212387168875875104433482328478370485649365955599472636", + "7395834689442865088318136274271999810879214824816761763663704696957588580019", + "3437039086902274758103248708959011309309143141844585728091733160570479897488", + "17467540498235313026126845934481902834360708027699640261573503748997917041831", + "8422995023428881457749354920047230520807267797593122026229940569066728153312", + "11738236614524316047928272298201726059224571806912522830504677268234776263625", + "12990187369429149609670218646295887927962536468837638994349699374218402630706", + "21623781561819578622543358003640431769369503776252057790027825149203611468091", + "5669501143149367774701636329186878874504568114693026656335516415399035236206", + "4575509333922357841125659935390034260822150782714631158836599383911958256934", + "6033679984514005206127292322842556662338038222728990090780027736088500900555", + "20960588060390074988596726655693601209728221976889320157850211914560680389602", + "13413928900186225435324599444732846219399870995843202836070995363431243049194", + "9234295907731818534748337816708817747432963728163044189490249482655118848141", + "8555123146188721627588124037374319613226378704908045460846666824116325453744", + "15273216586585654487893582395632825165015207795457148545076967758704213266502", + "11125068324382272646583837096728304098536045737064996999867399544054690011336", + "14959670405320615939216519873622952189488168539376564485090492806106125094875", + "2609137341445825321095931245531863029306734693698914386115461720635228617677", + "18022833118482437226280994646426462585862398739142688151319660825030833751162", + "10525185277608968809171389414591123336341871721390818817629448578893073864965", + "6744998464824276672348708743166802119889279164929133576466782672127996471038", + "9344118411856392638882345685982595534877291739896163576786468663773481537247", + "5834845743289993456669132331911158234826336724070643002101495844260807919180", + "640787484559876348086629339096900566749702582335998287167888457763777550412", + "3289778183958313592191132419001427890244410850048418852950969831934946260405", + "21884067909485911557708530514125998779898644250629017666973972309955096019929", + "12413233158576537989326032313779399359311922621054801366572548255508592334846", + "19326971287642001797155516864971431422568657490079056599164481456075851978783", + "9713216558784550226779073929454993288918740379425900055346553812422457613427", + "18939709206067618242082635502394826733708666259540151764313557087718564592235", + "16287201832008230557120811959387085081039691924582377369304872636500446900292", + "5278016450516276499641394140854299047658079220708153291463991663971826612725", + "8340962164398516487176161966485950102648589482540787401234013926762930809122", + "2189360335587469104898894044890322510567148239646828290042687811234738731248", + "3449789621053765306024986494635371148296184209757783340117745997173900013891", + "9143283285825049929933624722851126967075894848265558258882809634241528781890", + "1762589836333836268173539283261821177424666871790048223025892425442664744623", + "14999716003380172870846520006929664713498492291151583767005426828389694408867", + "6657396937759865997077915983426704746176142403322137472681086594905173286297", + "15931240944401174032343453067874387364523175128749141502399457399279513999466", + "1093656103569127166341414994548826968375416499363412936873802336598768367672", + "15311996490740753142335607256171022029850671542830487572556180935394970969084", + "2114569000427676164059607854200438792707180857430926937778356703166016436297", + "20765151929972334364703309580474623940346949760797265180189525857343032900699", + "15086899750477019230037863517768898777026828694611906648102458179274814888575", + "11989598054307650119711820896035556261218721126425285775266572489599541225098", + "9091334194223602201291135947382348087276953920280768131063598550602333641673", + "662205156672119277592919023943339579805630811299947870362049797370818219037", + "1644010308269285824046398201622466330980474102922491226041283891094107089659", + "10802230809760799055453358968022894649048726749105153336410430294944584338077", + "10002812375469387387638524822236342839480428168589080127597945114460807065506", + "19156323136144190554785957482371465262949638224644638616894263767724257422262", + "17733486484176040486420248003268701811107138651833773383126692059485133299226", + "11591816707957137858747879818244290349664729458236317322129399152472444934827", + "13438697403526660565724658068461816155647612966569784134628157461074851140504", + "13266011411738324637226437761946084145971285033245245813436082422574964132769", + "19496324867803620550902836425197207665281935127556215366411961309085032313784", + "17828723725738030742535695874826508863700363541225603578112437608777873288872", + "1354878734330830246945810749621035198088901780682128075198035721915088394677", + "14191386869116296861827703975696048379271311137936832987484810982707013486938", + "19738102036337682966068176606232821315534061860926048846327720022609950683611", + "10125898950023999329674512710082674945903794489998271114320266993547905724563", + "9735132529803303628278600229677357854336331158152745512812889827254017680646", + "11236813353178034372164750145200156935906967316633703292491531523808184114591", + "2838710343346183231321901296981007702601287106254090132583144723338172491341", + "2260966150439461282635555159610786965904582998424984888756770014321125334804", + "16205138689269860259863339330838066928731455712034665818609854024821608206800", + "14474351689504859217283250911440109599364797953678136799363008066531050767071", + "6800897037717406782446195419208951974623496109363697062923841346454038837657", + "12439223794583479313810611548866683769924970108158641553689074442900057207206", + "13368632268650760591616092601174213792332738038582508245973036690611922918115", + "13045024155077800673372198196114071407850396125448182263497388710529843778012", + "15909614634449771616261546064207953846549526793661176217246033322484959782377", + "6286523689254946058242241932237494136885909209872547435875613686442878101066", + "1349982286621373054175291479185807897612755726394394337821103602455947583575", + "1790249021131563196559147247422502568900570496057134946620667686190353467913", + "6402193114585552816271289059052503078472437339555969654050634927290142355144", + "16657876151292774412640987426284773353540623435906797018927318161786869839630", + "9323926757990898215781025566456682055829536111985821344881754566754550287464", + "333557759600743416321063171376142987411973146341256173569676328325670137868", + "2979415176549537735678518951412460315394554685744871595392830851604961695296", + "11399925948142490745007741431204138659570087052831458877959853228053991616097", + "17621136800256980786736718847997675507300992679800923101555538476888606832414", + "19796186386112600298425768110232392434623500274218805673491160537841238382774", + "6150633696896276954724869550196553826388538976047912638783098883173888551921", + "8007744165470722314410652720217502653722691054745133622163922368886561184511", + "9311054107030040402449841855464250035701299963653145080544526434268446648883", + "17483374709267556993216232945834980676370211318754373298278341962960548363667", + "10342541634151745634549333576310088998125069092773405335947539258498170319639", + "2491113909032842407846736301373377587232014330531326042787834409966784029431", + "2868273648562988738033854616949524236294200717479306399357880720258598168313", + "10742331131465347969746591645056052543770202432577403033214021867405353469410", + "11262784136416067912059431910504651513874071545823747299137490135956765416093", + "1202953198131460912225596542947265149450470358249744433218140619832526467659", + "1357188592392022798249752911997083133072840552839921198518049365021930318735", + "6860322969159072475760401518666168198209741930191210869338776131213742985903", + "17512830541972446751364043570597924188477065675882039340228337890464360413831", + "11068769874220100931580712510878176008480346856568608049014134908792656846759", + "174592415638141512586004221919684850728269107225502531102930877822615095893", + "4529660791853304207880649392489892958123653811385478713812028357153062637877", + "7261086657925858393339848092042570053113017792782297779091045392687309229931", + "15947797860109427499686809495296483548232719158897790339724183452147879327839", + "20792174987130011436800220305375988411204569143955960269071283382992617027310", + "16004359657196683674112642475201997234099915262640166308760622340887283269558", + "3052232100533123772344258509339894427157169209304015026294337193048877370052", + "6932731371388662052904698619255543785138918159081404052613363170137706724722", + "18786344120940971245053939784556033915582618306965577644860589607674568931598", + "5543910602266336221969634219610367731229996706190750207556121552771043245699", + "21500331075127237626915881392062922706776178350223005349901601754581763355631", + "1647168321391235121087005294677186408057439085818209785557656918589059596681", + "18530920602212288118571329897791681729275409540471839847360270409798512800049", + "4854705606288041012191490612688235100018679131692705525031856778474580243436", + "14030500314224264714821884320124695887543721243139555484376859209010937666224", + "2929002685096227153223014766825090983318157894872369622190343561822695490151", + "19571082362460435625048830465870659122288859631947561605202388130763965600724", + "16262018111979731901324396497332329657354718141076944178560644525915310954979", + "16511368307044025445048172352133100276074403358941934299584092446434550554652", + "753252068911531248849891296487019536968643055711963719939888051839316478907", + "4925743538740679971454033971243400511986874154138434310761553437631037517931", + "6674573928014195188134308196945556256425786164998594495377003561132663149662", + "12252972387729677079741673273643386252988421977551152663330085764982570989423", + "8132942533382570630447992711549276375854208836400139615573300103975003948893", + "1767955113430187982507552626731364143386403228830412651051530991601802225661", + "15911785581735751807295094148896874484829666426702209837871379520505512480322", + "8457495441274725464495320737396221038491121993793647195771335087939175598066", + "4450592062983808718489321963811761580153883581981811699755824744572598554379", + "17594374172823696288546149573800906143533062920680825959471396256808502015697", + "5396987646216038506498234734744679865090292329649749592797349235509038812800", + "21252439428941443609961710022512211297072223091751149596182317370399721579385", + "18205060096578922655953660066798909616473486532390705801648322522122587090830", + "18335844957174152986460129513698965339432209604680688295392195893700811299508", + "2642745947776207344581721189041440984169021288344823913353017838424039626428", + "12274412791221963394261975707650556785293993588861677826296117405653175236519", + "9027620387743934195363518529726678172699541005293960006772332651314467807053", + "447942484646789701542537528994314353531710489288574350580201677777950035282", + "8057692910194936390243001022339132476522458573171096947792809940102911406995", + "12466422220613642034159029765007116066052620609909566451840020754182208020143", + "1171876794208841082635913903308771131102729703148972430947313152931064171577", + "2996001803829960955342682545650059577915377668754838044698518610136674338212", + "12306305078198172670943701974214620978597606399214937108559926306737496310941", + "1710451046983264379967941386456520655992388623198157754427123889803480356000", + "17294040628260706127805165314917539522165501484257724022309899345383198007240", + "14850723644587717617416204055806811021029607124589054329504158261611106696194", + "8626653016235921835569469801636971931942277275560421125462026339476626883966", + "16134665167778000757091607171765139146858141948472867080489695707419285323252", + "11129502792062576191937938132554397380912998297589916298721102071015359559556", + "15130530042530114554524440015929780266039077867502338045121803478767735700077", + "5922013411157909464044985892976718792018167710191222879749916950629215559119", + "15641877241146443425426298971354708474362723075989298367558100488623078084763", + "19807467678520679976886734556492012503982987205100734773481397135264223200444", + "7230620319188569738319728589376800438996515892335609872222230868954870926930", + "18168775389925542038768548563310374943094173375522704828602237046711660513509", + "15879478673208122828337915995586151196586860000621630078538789187695874266640", + "6026559623764003366786032824563693874782053604114144074018595121889075259493", + "6581629708588609217727738860658907189729022916858514019350636298557029783381", + "4392179832866307866014828901921327571555384668846640428849164693147430204259", + "4039689077078382209094762982217352133778072152116592303105622250221872775182", + "9439756845261909878224812875899975078023923372686432160229876632562036997401", + "6300039613750170323909974701574854925454386098696929245502563775636617169329", + "10245787402060152709811414110506095522941754087902005711632016024561712242938", + "10770509446150307821311775909967816156602102352374955635502734154581919715219", + "4445806424060983011786548514585153091691541530320683355086806781784085761841", + "10891448460108394317032887254041302638257759566726100966329794645560947944086", + "9055121436695773704587898653339600198373789357537888127317084878411452883579", + "8149630563292186659644235715307853879418282762593116603286751824944805128957", + "10164520096412584664920364217937374178249615925628067827666302990156368241543", + "18787580258401627795053641123916277779446570183960070225650619813208411270515", + "5840281460603743032409236132633000232507414411373674275076564056089047616513", + "1156412247030909285380152844453068577291565904060426536020772600017567322066", + "13125262001118293895463954353229315406286474760130294037415093324929424537303", + "19341994820951858136925352932235917695982462478372266716377719180643995594202", + "14230192493251599368583155353256194379698225636722091587422432258409725791125", + "18604655597949661495485553681980778009433340166469812699886221824297364988905", + "9484517765495232191823212744512718095318306206191891691619884709082351623103", + "20661548256453489016691122417433244991048818711049327729621550642733467464794", + "17946084952153038878596350204495193827996043096722462577062358769356218083033", + "9008616567490400741620365097652579204686050477332773331876377443733844010527", + "11222501299614818564801113661500553859475257082250633539503337648966645140931", + "17547881514301195137575407746474668605213387222566272868597007270413523256918", + "5795058045621081034219295632641782062332989904715400020698296776847639194943", + "10503000549984486673912349445691696940798628902104169170719393589667267164117", + "20627605141977035823281920467011832985049476688278934213570130229801586759490", + "1617802179061215386952772461423280627948056362689193604176834379576303877113", + "6791812933391289387114968397366223795809410677008110102514189342693442788856", + "21469327667867855439208525645169470185070216384607993883382520539336879692857", + "6209704596259199225388050030516880246912805490686902979517644524792187929012", + "15048235302521180373501889249113906037936587642496276368753413449431797081949", + "18778185090591194140890220889049934755279881763207537060835789721322305845157", + "4164373610491096709440557803327678927854304626112734913682240155408254130319", + "6352957909818668217728163741226734988658723962520906766539822539894434836600", + "2081726858060934846688892186400729209835095311388667168398290314717411097500", + "18997085104017244551079822479039381301947270057658366559839643454880390097993", + "15381301300391534710642382148869618456973733474322247178000572705046719371489", + "4198258606721523455517040976251319538290332464337310822593109666080121166366", + "7557782631251063800473816565466881793101085796415715068491278248627266080710", + "18931480224318877255548108668163955495398356426693160573742850933557366286581", + "14006438920977933743471268674953904657230294434994593269165676508225740552815", + "10552613214353127913757671512246056403297131067811718291694487016254806821431", + "6942360360107439367031353171424554084301681329082198856946152988574077265518", + "4821893567676204037011511732637248253220700045205121424698817536976139720741", + "8570797921121743873772790445200803600261480452613730673514117912909339929304", + "10210191620579364785116101817818502747961980686320008627354511305745123505207", + "2219514545666614569412405614553938185242666345781569909039944438048257115280", + "9472494948236350938478699115530847822498160599193967472596054792264811505177", + "1966000492511706985369338139654568276174679894528461227404736596640963023833", + "20046698515808110863133254224236843582890061845803004799852246848232152965906", + "844879645041461183539563036099295737132271525297913466134251805988318847548", + "4731835924119595590183070349774829521463227171971888362743688018910258502055", + "6665591397442095781758927683212527568272080607916992672135848576259567872049", + "12479020936095094085154050050726399793348028251321297846621658033596298393726", + "13547094394131143768607425506026982562580191167096570425566382504500825930959", + "14726791597032654225590630927699963688874228760066722543952181168729825328904", + "16458751398904781219324882164741165137205147400663608606201428995139003250837", + "1382702744652547575130131869708589984736039127219433507919721371542416805658", + "14166947289061517490924648855242003709961143010494793848406604325534604905674", + "17345393385322406098467895186375277399629828208438881931258894959842597584446", + "12612093274589904544460621781003810044552285394822234432357810635329000095532", + "2278721075910399058699498270814807833560549835812826924195171790394865401759", + "1421362911034706266530649492401610048326109063529762443419685870164992649589", + "20384150845787594899080953437004222732830782572958598551146508153682280572403", + "3495056095843097816017033529053032886550694720911641681721955773137850234745", + "14487246717687560703169821216097612442592434246076605506691782211475316706576", + "13114661974362015466065924612631881453272278776220284208159815065535423486323", + "9628837478322926908639029641093928246719582225562091179037197080152788423316", + "16456984127855989467975376775709400558526389914848937751741445859284106536098", + "19656266549615253685970702218272816322599871270060998775752454696892588799707", + "16028536897482325114555541492282064621526855756620164892739323385311597784598", + "11146275353887579825261271116625263398084632057838305651534583557054398181584", + "1266798071261939757558459653520435752781168817699931045808536221140555755937", + "11242956579746912399423902926753108012060763899073974272480062541403588053076", + "14588608018127887593991920960165305144719061971763947578582110087376128309227", + "5393054051309402705329828859286427425185515439646193479342950065083063200985", + "18307950351265411595229078848795494559405008993794506472277342386767679169011", + "743926999062879465491123202608691059211244131659206478586545917792531513741", + "11450449580887323007201214040662031298427088361641716342102428346626191862079", + "8918057399138776974775397421552812822591030597414174436378830421562569121660", + "14007657148016840281780221954150751472316590653604019267269105557294795817454", + "8897431240575139049076571283304115052162507033543918057893215164198174868677", + "17409416377040437657574990618884772475255396933561856228823381810258648281132", + "16403607054571283311114350132750182990388190855449188101005849970173367695642", + "1508877263063909922036861471040526805004287450733006660015588614987220564178", + "2405929790914208591765690668430163058227989428739435027301989746453297508008", + "18415257845847977290620817418058909465150962144437291827522053905306094819516", + "20783625745588167215135121578855692267545194811392233146323762172638833678083", + "12583735518159929222168953087285648797218797099708802919538903595273472024870", + "15030113532753402263141105846533650021837620483920708638977418255212037916690", + "2044646201923266135898414814098969242685760821863709223961924291978394037140", + "9562505824238493762747890608580676752653265912034931437600706699047478680911", + "8576622443481230638247834729157921300954369512398151853927361194153863723577", + "13502467573105057302451195240442043465889307076796874707692282952243564124313", + "8659508751425384756294968991737225118186991099981336883558347987313410663071", + "992836427155144083079734949008912409782777055146977880211513548288277680936", + "14478698156814197173486553402886358323315401752422792440407576984439553283243", + "20204662700070586254342470347097589341087597032672938910846995633724257876133", + "1812172830570486768023198219726218583158255123460220955281515074543933097836", + "17992769749806193849727573532001735714323064233429371707200828907106131204931", + "11275304845104653478990825724663443324387792098049434796341439810037846570595", + "11105199526118828533245892738176913532701964584183761946009726630431598631704", + "11393903979182757061241357325556769026145343989318381225525091986823175945546", + "14383047179623652129287498422471395756755287245693820194038638652931729043624", + "20453515719997662355844823439261666280107449255435062001366805179715649416520", + "16342434030539357589118587629035849207895930777526269265833103191510088418049", + "18401618409669783606190734020469430253158741609682792710560527808266513994586", + "16076963378432439787360187815002671229026877629938245603309362816936414414606", + "11529521787481754741418171353792184686967806427253956770441456297160607170941", + "12602344479327416239042579290059415968931395895265775848606068378636646131638", + "7874996576706323538862472352643073872193797790899948188898578232811415974069", + "4845431324532575010258701269749667428252187973009639700566740844013627970107", + "14802018959157569593654049670448062053140035551074745737518084801052997376690", + "1438261891736096301511736944301645044858355530036359764344568183983865122024", + "9702483734495711444970258278431697259819553205295174290488141405714571120392", + "745629299152271704331537263378566091569916279264951187804590327433939503876", + "13647050656573274198980106604763314305257288237932552445752822394869548724758", + "16745795700152546519998532820179128558740146524138261377398336069302091938316", + "6511309643923082597828923519243281993300211181014429981003702021394673484309", + "3158509665257424804238893020268668038794684527943417360064521274731483365497", + "10106711021741632147292275661510721445094446856823798214773373487329113893162", + "19539597394622940270506324036474158048415508212083560312665281444636039358507", + "9075752841443178653052367723489194637305474480630925657941805297419704103107", + "10984322959292625191378844945459317193701661510910417025411449026843907121641", + "17927313671844804578171839390174936993766288170716470877087434657482712787254", + "18872143162724264635018175304456023676931184143071101308504884978404622981510", + "20515902079806118899516548385287144085761546545950360604606371922174253859996", + "909520465300399063286844098900959690198495601569624327409635184475032912958", + "20206446745684213293565932220432184473350758436589542174203682990821527627997", + "18202239467972118142408259480727588505962487821013411792329301914239859726481", + "7983919062477498484336722979137049758716865253938030483468589967847498836327", + "8325614337212122905531318376855865321332189692321777599447159536409656516919", + "8597163804726192574292212854626326634508890118939606311848139014738137868745", + "18500944130179290196885189330659070775829081695220678669482033354888226989090", + "7688562031300833340110739922365866810835026837265955101002957176624104705373", + "19947182975808967149585827952170169153114833389807259043148589247054925861923", + "21517453591126354047928769118216728006629501856431780724965733122370735013602", + "19724728051616532722905708549906983762154956430000563385110162693578183573835", + "12901041676897371025108379309395257314967524835524863357724200518477312324601", + "3552976975075178791886580855175573474759766023206417049453393296909032090255", + "19723727023156957738383690313848921568395238875690539785344406327140649751346", + "8448482122526118108012095036728031952881057181233719818597059954973230302598", + "19506723656402269183886569439436103718740054288352928129647596321989076586438", + "14170198227468034198358292107569967739463050831964193487443803219358410981401", + "2398884471809169728404698398912564280110318964918284340553363751754870608551", + "20316276154960569208855163430299959763482642890911503054443013025286320052982", + "16819346170274370820168489515108636849055213099894018965040750518138293949687", + "14032485862625099598391073907784424901699264655891124043486362510876557553033", + "3741512032866709198636578814340035900693212341083373891378285003365821212725", + "12320998007117681710156579111620440763582440787871319835484669927670822914066", + "18497785580008161471127317447391820533755559610828409772165480020067085485597", + "4405750113669275276872999085838121306688869906871946649397890860602053182664", + "9830919490764773329556696442544208406733862221424439787824742578837854955405", + "11656214840454358128291973754562337687978444406755233034850997851692380129929", + "16862314752433119612255838474361565358197526603416756657147704910780503202458", + "13994730783691699927566696508271362101008053907735972055086535421903219406757", + "1854398114607336170485701571991055833572643201188015258021784624866312103211", + "20069390379729391520654648717585246938403361904664791681209786618785720069963", + "19778444821535136400266636630030874919578160245360989612631440824622767847607", + "21076683052368294199461719450518198858527944040936974409849054794891242446768", + "20411961758852468040955165630870750226459296665790953169982230333559560296737", + "7502887992944485334895259919103098713153732704425367381733235650810817022601", + "8197671201190749689041918593081864258260291777801687985230863071067546597951", + "570474345973383626887611250305231463005632817815912456645561438458026430574", + "16187881277000098252637261715701961539204559080071010626416256497925074197451", + "11845760454453819227975628605163841650771493308562299599588237256809780809041", + "12496905116588812906791602937225630677262600876144492834414412499317258049345", + "4202178453449927253051431595142836159883278283876420191896732301089503127871", + "15280239602029688372093391224297422856227971384132785248484775299588777602819", + "7675624637796659741242134119256859966414372807758392029058215748145801821134", + "1558712828795805480069595730092542425736929845400776453853052297930907571419", + "8635050664405255712941235442916789795311088451605341529181390886210645594786", + "20012080883240845774134523050094772793782396855825781678689671532280266223297", + "5332665745663855702669515489737590813722712486482340264533181716182162216492", + "4485713577378561256572703202440374134284884194291407499970429041545518915674", + "11594166683437400908552833183389222213106302657616725421515671119008232951402", + "12334131761060442183744872783660449330900007808735685674075245978195977188916", + "6469473140282499287719023824549928080797880736382216700350093546124693991876", + "9636916035199580181413428196442500662713954920605937945590797926723030993514", + "16259404193045476674338459391599179316200568243819582010383092764410171324132", + "20780929014533211529148194875929589500835169316324843103846798105106103748435", + "19665958036420604957261288306620614439114405875604211377014353932489316681936", + "15722491897648578237100473323139351762887860215830601049261437931157637153597", + "20474814639824406443111360000192533367769367587690127965461453022108167666929", + "15306300298273142257702357120212730128497075786589008381550108606914393296015", + "19116371381269652319147699604019975103087973589614811479290794650138683901396", + ], + ]; + + let m_str: Vec>> = vec![ + vec![ + vec![ + "2910766817845651019878574839501801340070030115151021261302834310722729507541", + "19727366863391167538122140361473584127147630672623100827934084310230022599144", + ], + vec![ + "5776684794125549462448597414050232243778680302179439492664047328281728356345", + "8348174920934122550483593999453880006756108121341067172388445916328941978568", + ], + ], + vec![ + vec![ + "7511745149465107256748700652201246547602992235352608707588321460060273774987", + "10370080108974718697676803824769673834027675643658433702224577712625900127200", + "19705173408229649878903981084052839426532978878058043055305024233888854471533", + ], + vec![ + "18732019378264290557468133440468564866454307626475683536618613112504878618481", + "20870176810702568768751421378473869562658540583882454726129544628203806653987", + "7266061498423634438633389053804536045105766754026813321943009179476902321146", + ], + vec![ + "9131299761947733513298312097611845208338517739621853568979632113419485819303", + "10595341252162738537912664445405114076324478519622938027420701542910180337937", + "11597556804922396090267472882856054602429588299176362916247939723151043581408", + ], + ], + vec![ + vec![ + "16023668707004248971294664614290028914393192768609916554276071736843535714477", + "17849615858846139011678879517964683507928512741474025695659909954675835121177", + "1013663139540921998616312712475594638459213772728467613870351821911056489570", + "13211800058103802189838759488224684841774731021206389709687693993627918500545", + ], + vec![ + "19204974983793400699898444372535256207646557857575315905278218870961389967884", + "3722304780857845144568029505892077496425786544014166938942516810831732569870", + "11920634922168932145084219049241528148129057802067880076377897257847125830511", + "6085682566123812000257211683010755099394491689511511633947011263229442977967", + ], + vec![ + "14672613178263529785795301930884172260797190868602674472542654261498546023746", + "20850178060552184587113773087797340350525370429749200838012809627359404457643", + "7082289538076771741936674361200789891432311337766695368327626572220036527624", + "1787876543469562003404632310460227730887431311758627706450615128255538398187", + ], + vec![ + "21407770160218607278833379114951608489910182969042472165261557405353704846967", + "16058955581309173858487265533260133430557379878452348481750737813742488209262", + "593311177550138061601452020934455734040559402531605836278498327468203888086", + "341662423637860635938968460722645910313598807845686354625820505885069260074", + ], + ], + vec![ + vec![ + "16789463359527776692258765063233607350971630674230623383979223533600140787105", + "17179611066821656668705197789232102741366879862607190942874777813024566441829", + "18653277315487164762584377009009109585010878033606596417396490909822722930739", + "7373070639853668650581790286343199505413793790160702463077019294817051722180", + "4823864393442908763804841692709014014130031798360007432734996408628916373879", + ], + vec![ + "19196309854577132760746782449135315310664418272926255500908899397538686486585", + "18123132816088485879885148351452823314623055244145916622592591084094232513914", + "18436594886553181913092702411547018228276047601279727265790147051821171174455", + "15167500404313194506503404655898040457721633218143681920692711693000769735187", + "9437986152015460505719924283993842205604222075968464846270136901243896809793", + ], + vec![ + "21445376105821232747280055223032050399373725161014449207033808524504027971613", + "49684738714301073369749035791061182456037935161360748355432247732088942674", + "9826409059947591908303145327284336313371973037536805760095514429930589897515", + "8494798325496773219358794086647759478982958403252584257436898618394561204124", + "21251937175072447337747316555423152807036003235223125066270735279039060889959", + ], + vec![ + "5539100337780919206842837176908516952801756637410959104376645017856664270896", + "6297628909516159190915174165284309160976659474973668336571577778869958189934", + "12792263637464508665199868777503118105486490400267592501708855807938962470650", + "17254685306085558791725544672172906900581495686070720065168939143671412445514", + "3590396502942934679818900672232030233017710909687947858184099000783280809247", + ], + vec![ + "19055249881366445073616526879263250763682650596233071589085239500077496415637", + "7367697936402141224946246030743627391716576575953707640061577218995381577033", + "1322791522030759131093883057746095061798181102708855007233180025036972924046", + "20456741074925985565499300081580917471340328842103779922028754640077047587707", + "9059147312071680695674575245237100802111605600478121517359780850134328696420", + ], + ], + vec![ + vec![ + "8266021233794274332054729525918686051968756165685671155584565440479247355160", + "7947823415909040438587565055355894256799314737783432792935458921778371169026", + "16508811191852041977017821887204137955816331040385276110261643892701458724933", + "1804800467126006102677564831888710635194614232739335985819349312754063580223", + "11189892034806587650995829160516587240879881493093022855087765921356611070470", + "20567450145123179140729389574352706949280207113956641415022972885523439610844", + ], + vec![ + "4666756311257455192796774305229624459258864488677689058174087310651786875914", + "11389253665835451896363091846189307652796786468610595637047377864063404843117", + "18793736599347263150867965517898541872137378991464725717839931503944801692688", + "4206344588923325482680116848820594823631536459347642329098796888497153867720", + "1739462481670645248707834504605096139894257554120906850613041004917967456145", + "18514227342636266640333254638454588508118462110178719555586534011641424431745", + ], + vec![ + "17887039315911403193186866703775654467672391491657957999455462537283842145802", + "2824959020572825365047639014537190268717891749361604043531643698340708119767", + "12521547103713919592301476538318318223836047611311454785951907894055964264287", + "8658146183671258251984364885894342376430874614261222570603159082682815800788", + "154390145585284450772861151318029820117470958184878116158462181541183085587", + "7593705166056392393963956710828665339496927193740869686529339432486182720653", + ], + vec![ + "5529559239163081088908568555890212324771345012509269613465629182165427812002", + "3729910453162885538930719732708124491456460687048972152311428493400220125686", + "11942815243552870715777415109008273807076911177089425348095503288499102855779", + "498938524453430895689241565973888863905147713935369405079343247530256066618", + "3976257517234324421403708035200810671331954932478384823208414346189926720724", + "723540703523219510043977323240437576248315561543814629392162302024056718473", + ], + vec![ + "13306548824219676333032339487546407241767961556934015003605485324283250885682", + "7970147269291664639740298762956131361316495463191268382513594527221399186752", + "20633313939958767604804835838065337107615699351647541991788258289962727735454", + "17162090859520817529294904484646695645841022315617926715432606252643123848792", + "9181379842957190051440498041153333325098774266789773971685141362947015398641", + "7051606617662816798224904133351061549832959857069896192072217769241273559278", + ], + vec![ + "16619522548478824222688310091434959542211899852679631815023615875678448806029", + "14965311177811968100298579672135357167599499478246106482433786066289128683961", + "9792733250919070275775594069208673385381167169182805600474820364274865306108", + "2069253833779081039049908513863485270550301879399727430830923273191877809560", + "15847298987712771667136245955631872888473964330474501593909263901393348546986", + "12244443532166430060291409356011430759892629145539185535677568234713942157668", + ], + ], + vec![ + vec![ + "19332164824128329382868318451458022991369413618825711961282217322674570624669", + "12346323761995603285640868741615937712088302657627126374070962894016296466118", + "3913895681115272361294397190916803190924061797587910478563401817340941991811", + "7048322889096718105055545382948709082135086733564574465991576956878202831861", + "10375086910057323893637057154182902576957472442368661576421122036461645295833", + "12765622911241487148932810040772504127756393086809438933166282251044289864727", + "266900212758702307861826326591090138389415348463003233900705815890364224151", + ], + vec![ + "14435131616556129905356866638030823183270286404767286105643513738132789033353", + "5780976801287540146775934937953368730928109502001687434229528186520268917700", + "1618320442446662026869390273942730786145909339107736579759397243640902802126", + "3818399583522206096165108192531271582827953520684743806492664825009577810261", + "11764506724346386316602508039052965575734225646587104133777798242528580374987", + "2414215974836165993714858157462355581258152126063378817495129367240311967136", + "17609437036230923129211608175600293197801044251801590649435913902851695334081", + ], + vec![ + "363438080029711424794236047863047716381155074181485245036621530063262917196", + "535766679023716739184211613469394818313893958493710642899297971974381051070", + "5305068908469731303772738758164870877638068032868328180355958394150421214337", + "10807632568240507366657354568432178961148417327580695024415275247652313539292", + "15964415873358391713354948903242729080763777490509563223190335273158191600135", + "20700362719972015883260687302741075186857660623182772413609788566925949033885", + "10135127975676256977820296631533839366076919827597067890970660746228807376456", + ], + vec![ + "4251490167543116819728642817282216847143714366441358372252125244838181656331", + "7745587495915033527847242564710473705100826890903278244320948416581724663023", + "11741113129223221800185946819924457344647035336264986754437921049066977440806", + "11630296782890656599545188109639399768829653360050213193782325240600583381364", + "16861140446185941149398487176581839232380972247302922484807333229513905651035", + "365879246117123675211400356410703684399715291171114630107795112994207447819", + "21725607857580053522363567649763546934441685061337033780528788383243719579033", + ], + vec![ + "9222866548596464928765000608129177609426964853736257576074550520759533736918", + "10261578281201197531384003420612639018011405529775212563256392340336951230146", + "15644037447921591571869862919382888810859308861783088910843592577202362807673", + "12752004188139535619565478547449108772137477456363099481095747591698702436636", + "4205805109630387448825516813913983509046636797101589615147198457314360427718", + "21047095155106717901091873146599497621258071512562421967648909471775919992713", + "15624165295872926124160584750951090817255240214488120310950503163805737026315", + ], + vec![ + "15064589937731741958666763896598138037875460434244947486199623542160035749721", + "1801577872277160959016940766173040841160105238799805406938450020949902989173", + "2896766420608048344829901127120623317655260981420052771341833288256800199953", + "12828791469509204618898135640019714232831708508424682785876476343251730674999", + "21363471986981372923191391880511344708743312828234098289107697080824665183315", + "21372706354350795416381912271616633829725494570576895047490974943034914894898", + "16006531510217730955981102005088687858079561573088629102219485906666961331083", + ], + vec![ + "2389357602244845938251345005183369360523566673990464798041306722747500447645", + "15275955107196234672088664710679934029171843237458844492987233368659104714648", + "8038797517535218686870517662905230585331773059774130312418943649247287196930", + "17923922393436914864421862212181654800719733137689602673604754147078808030201", + "12890519745320143484176500044628647247549456778462652469313611980363507314914", + "8058516556024397257577081553178859094042894928866720408652077334516681924252", + "768425396034382182896247252731538808045254601036758108993106260984310129743", + ], + ], + vec![ + vec![ + "12051363189633051999486642007657476767332174247874678146882148540363198906151", + "6387692555402871022209406699166470377527846400909826148301704257996818597444", + "5501161701967897191598344153113501150221327945211106479845703139297020305204", + "11704372055359680530622226011526065512090721245437046184430227296826364812961", + "1448611482943320179763394986273491989368427112997509352702795612841455555221", + "11429145481524962708631235759094055797723504985787912972575745356597208940857", + "18021858528471759023192195347788820214752298716891162685115069036283008604659", + "19817577944622399780828745167469547332167999743980557486183403063955748437619", + ], + vec![ + "16868980302925985719076889965831700407328155411673408077166038059874616424216", + "14717432944340806781505761211058502775325970511884444497202848327581753493322", + "6273484270523289845253546319956998489830555038697388950038256377785540828355", + "7726043103954429233325852791166106732104332590864071922310309250010129731951", + "21052353119157611359715869265647287129868507410601603360127523286602350622783", + "14881796557136180514390287939887071460258251160875710427576954128871507002642", + "16341327439981153879863707938117355436152690262312411284193970279829974799334", + "10737675906107372302108775622264379258926415910493665638388971468924879578019", + ], + vec![ + "17652699767629314433191915267767147860052614073432922215674211498672835339113", + "7457854400138129895665591719907473144796504905294990100367501377050420942800", + "2136850802972823585140870808569264373787409642804109426616292140046700710743", + "14029467347298896610468190615212519453678316548442709087191045978401072380889", + "17927699952921266007590534383984238136710494507499176330493504416180410161683", + "1404719213830610030709583332543456268094679432456284386108188509031502237811", + "15774757292079018355173698870903422490868220545526384876021336136892926326596", + "13992040374687149195439840459922227749294794072303579532004750946306028893274", + ], + vec![ + "19895094843870397064274579657905921299619388074084417486420154568847155746891", + "943833985612967248618844364501030453998731991825395875139617731659343743483", + "18334641092245356682448009823797080853859186519922476229272838591594967878678", + "12440287044655505483131716236615633401781045711053210640202766768864619378050", + "19130942564098572936370308509908873069169152245172660555660369853346605570826", + "13687979327148217614616687417475244897906227789285703940171633508277844471062", + "16887921327479880141959363366262254722342925451159884082370074726344024008329", + "20378003125024698406589040864014894045124234695859352480989552885205935609512", + ], + vec![ + "9961553412530901953022991497331082655746860319830309417179972582392489275965", + "17755268665220780466271147660314410613992814315871705414495724015443459797439", + "15394131279964876131165951719955566821453162041574233072088124095626652523043", + "12668230348320365182085867728169435383987570924921845106243310905832768752125", + "14046812111383844816383347755263287603387502282980410255379630204396960343368", + "11590093969266595252327261214735156204516524792938909229175092594303424141199", + "4623517074925959322927421514289132524032863498392441375476410779446526502799", + "11550389531965919926150256242174358326491059727918559332939872696684299343135", + ], + vec![ + "408487396317981846281976563618407581852133413686169882346565860317912856432", + "10717757571561029382519744040791773994731123262749372629687813122941078154016", + "21323787615496251932181222397986048515693661833099659753170924658480548866921", + "20780799310067873093555276926357624414275975377319941015818682052081980020892", + "9948385944800296129032348634683354181546876394979291412116493575442898426065", + "4957033413111065858035065225611730571499258914257595411830870977545212164095", + "5227254936689728148737265263965107718869714128941995977191096572191110991079", + "3582814872786080867997255427740166393615552773099677831398251586195329933975", + ], + vec![ + "2136737803483410555580163900871515004623198990079556379647848364282254542316", + "2965752098571712086281180512370022839542603960309127077035724860894697782076", + "1478525086510042909660572998242949118476342047444968703549274608283885678547", + "3563375996604290844805064443647611841824012587505923250907062088840679700555", + "15461452581843517997080348781604020486994675070532901120353124746087231692278", + "20472517020063295821544268171575414161230806406668271887185150097779785573889", + "21058001005918321995459971112208002381460494177332965873048074199074929946172", + "15805746645980285645504697043988763170971539673993759868487715403982423015009", + ], + vec![ + "7141240965656437676130015766799708612940092856280620325870466265817008351948", + "21418010338098024788434337801477243267248314524079104488811186206038748626642", + "20272108634229595317682817969506273496034097230124371921628691470754475805838", + "16734095147399743907618148751687506877774623133599770145304816136018239803101", + "8439324632051181834455499457268557602816180314723268640869118054114888151316", + "4953900961796661020464968131122569974581908893169105485631905994366550928492", + "18071625983692455679240094911529791119099077429122520426399552756115503123111", + "19638917592063029281156873227053827678889868373299664608974791764751784473040", + ], + ], + vec![ + vec![ + "708458300293891745856425423607721463509413916954480913172999113933455141974", + "14271228280974236486906321420750465147409060481575418066139408902283524749997", + "15852878306984329426654933335929774834335684656381336212668681628835945610740", + "14650063533814858868677752931082459040894187001723054833238582599403791885108", + "5582010871038992135003913294240928881356211983701117708338786934614118892655", + "17817167707934144056061336113828482446323869140602919022203233163412357573520", + "16618894908063983272770489218670262360190849213687934219652137459014587794085", + "10883405878649359800090160909097238327402403049670067541357916315880123123342", + "7439184039942350631846254109167666628442833987137988596039526179738154790587", + ], + vec![ + "2727663760525187222746025175304386977552466570311228286110141668880678011929", + "16992375884417886634716738306539629570444547136030480542879886913528563834233", + "4178586893949624406750122665277033849762243490544460031634329370298105635905", + "2517914797385699886738929430037355069462619900197972886482360691236776726214", + "20164173810534657634631187494276970100735049909727379228976555863615716408280", + "19970958827248077001061220127605534603528515080207197493660642269195127427214", + "15606275977308968307194602612931727810866183872589808138812916593200446820753", + "12261436001550634140750381230737452634746867040398895669545077774504957433511", + "10405309809257831434323731445544896504541938387524726028487604098725193737428", + ], + vec![ + "13408856444092113657034337770571899796129642125690066226794939383190876435468", + "19768080898957882918527124226120459667739640387901357739011662191034806046251", + "16749889646056241484852997428132695501278739424507088920371060969471495213919", + "12331609790192161246735870679870317366088443875784324655482358218146673901073", + "15769331739277556832196167201116801527901089923090632364403958141614820528626", + "5227172275505968397128736045169568430462701766148126842874241545343535393924", + "919073378344729780131814412541912290691661039815032069498359347682919854836", + "17858725475505870077023114050620337312678855554361132257763133392017321111169", + "21805188450184460363143840112266872832328782034569970452376470141743078343745", + ], + vec![ + "15808413311863154368918155104905222670782553225279887458053980771135357021692", + "12828907214414139667587331812274388831051429093098655261887619166452245292431", + "19323880880917307340820066456419195877039970908109908221992925424585030574269", + "17591732412986269470826282099678922890996647592922237928486497997144096433314", + "5282593184575641056912422403901924986019740793240905758215569065763629999318", + "16013130707598525718519250412251656096494468043256226360413191733653074896117", + "928381583587170989315021718439506896903185927814675820160976165627097308915", + "13354336789663524324458402003354905134416094005220899335023797754517805691310", + "8780135673134081873589118311874067764073719549433574820315100541871522642766", + ], + vec![ + "3334957744389892864165113989538814646945861179021194859030934481494560681812", + "10553413566358881045095498839713459314577909144176577153981801574128014927353", + "18894321506279909207228932263261226433242541255661384643559047811974513999438", + "20211894014628303327332299342564779073614790317614402383971270594430055013904", + "16723480621932556506775906903415088312771104391224076734252099577243237899106", + "1131872547334579236404174618548801749854242069301712398106619948805304881636", + "17386814048141719093058723520379257085987299288710382497237609774141718421404", + "13729980537487612221640320393867198844745491357830417754869369043292518007370", + "15860780436383591737179656321807464721751913977397035980422407138400867838633", + ], + vec![ + "14708550460111247278740231297332510059116901767161326454481923990389610737973", + "3132820559166321299152015048428879769905404947939291493327190426785911502819", + "8658132367999084824971296219169212568783540935524918908332001856872807119287", + "21064783047501777742084787259676320053480170916619513986794406566953069418035", + "20731000104011695148048713576219525164619502119638555785381543866326561323", + "17189725817866212967650950297463469529475851286172280116066228706121595462088", + "3310440878606659516028312898499559492876015493892608849966645073367377278233", + "18463918215326370595980949760897480127622730018343709491036454088497976892863", + "10894192430593140913557164014343360386192963621862346779515699758352916852228", + ], + vec![ + "5060610877870389107953459328006060153180283860738879092399406248484265273634", + "9068988823145592214189961315730261367007076042069390630024839612151270430414", + "13160707893890865447331361630522644819624543031829773191665491273833460019183", + "13920568292534026180186486064598876780779571940988254327823480971820885713801", + "3894011501178134026216736522445829906312115650019712122802932677318433032635", + "17895318821130376385979570244603067634449453259842805202694945793852667231847", + "9777993060458301797155055013115849176281006051494461044565335406558308324220", + "16521293541516305251718414192107787058980727971856888501176820100904791554730", + "7744063601405355255689420547832904761861257642931934580021876189691881462544", + ], + vec![ + "5444730929053688962452159157646022068806222098484627080046464163159451208522", + "1524118152994294864739915388438939180298324297960159419600850033701763764640", + "1334622237342346242862023763160346671504959163544406543315614662442562816653", + "16126317914306849967682996412350336172782726693375105190424151365140854833923", + "6345975085253358297751050638846919833013142450462810543971050115910612860460", + "2703875280053263252177031410407166981522153304496807669518295313468095058674", + "20550626512184448884716175825490086259235894802178999642552696391947509065676", + "15013718986700828670892638677446258841869291160144196138236407826511808592486", + "4682264015512203762723381542642871160915706748420642731100634327658667608042", + ], + vec![ + "12834108073603507925748862283503586970613250684810871463629807392488566121352", + "8422606792378744850363509404165092879785007388646473871019846954536829739979", + "9339209090550177650528715604504958143078492516052997365409534971861874881780", + "9141831918422847136631159987994781722269889810731887947045878986971886716767", + "18329180549061748373684938917948729366786279119056979983310618862430068636631", + "2009551904565170718789964252583363785971078331314490170341991643087565227885", + "3859729780601667888281187160881197567257456581829833310753128034179061564519", + "8535335342372994336873304745903510543599314397287086554558824692658347277251", + "14148514289641991520153975838000398174635263164584825009402034843810351225518", + ], + ], + vec![ + vec![ + "5029285279710800539227619495938136407778783814400587102957398897867261120664", + "21661833903534656620291231766157513264428291380933208423519374035927473262119", + "21013170147855726227668315492699186959893088673047129690411646575996043835024", + "15893628062504267735591398483514002406192781085288489283447316241330749546879", + "9860639032243003377544947110034203265885715041305770375052648470285182020229", + "10431760628292478929366440566994655480900443273305000842144090945543100651218", + "4662341343242273661833461144031815716144681076466659112993661636426666579986", + "6674279191498784183427663914511569570797862586816649467168170855788360268943", + "16895097041920841073767278653214275321407577186751547609698446652984399225877", + "8168606076413192332279322347673356872630772122089948509553934257426773045038", + ], + vec![ + "12091567755121016869657080116466607855522522017768906776539212195551888602502", + "4684576201081771194613696765517034834984066296253124029929753160055156611363", + "16693488266039456124835102259365515976900969074532557489095946797080826193662", + "7638443036775258881709317582832080783911189229963788890221615286494482929025", + "10111436214822932149781668218956845833675824936886829015449750181332010388640", + "896682691957564465177669890535917423987915406885797833670239687119295318467", + "12612639059115228106858238115822505521432423470330120640591982767272085175034", + "1851711744209473345586117150836616408053748535684022739058625441026889320297", + "14132260688735080257390420980422269734275443926576061985351678038992087770902", + "487493866037948515547037886552479973316400139387425953088274857424154262588", + ], + vec![ + "7712516772901240105339429973116360243232161870164307482409826131312962380842", + "20295556720945067049585659016570679551265845058805648954004989969704769135170", + "378208946912325140295069471345064814132951473534378635003955801655986417900", + "15111601008893945567629460471315838423301021468457758533702272669431620017222", + "1503682435556321218669089857094247703956565058167121192612334331910088441071", + "13084874799693933186811120569396911285611047490876409383659779579088985591229", + "17464483161247836988344436558341194021876261750085348252730901647076441211862", + "6628743087463083391707355927377412170189936607932592258517748766250528223430", + "15153763588458144568353947674975114179172744555450771328418442212716084083525", + "11217853102739260248713425002157925483291370125178251466195670948291389406199", + ], + vec![ + "11275485266433075885440484136400353724892671196084163231314370685019444807048", + "20167106354875398113371399754994549089359568833089630824992752829251678891797", + "14151330869211746069130604993916224881047448810615413435448712767752320095045", + "17260356243574396880210370581740651566334589568095587416844511054569255137183", + "12436078462666286197074526218535647721230687376129721353230123441759960021666", + "12001627458343654011606323250787666795709808266974343548842843520227918922255", + "15944850302839498288636342399223012131590208876255723227505947857641523034493", + "8444103924869263585176528654612076203716402818569041992813095331662367021655", + "13015682914180762871967848617514355587762125694235380084430680565032083402270", + "16200183380426364054409550129683752323493215428097334915015688753327665325485", + ], + vec![ + "13717643109958965551675619584464549580820722892266661529182798599670194908199", + "19801725181447377274232761944437523251067599053402428862557912155522673980500", + "8260354277364856843022982286494019620277496829494935775254726797533957063267", + "124621144162335766862972192337737579448571172779117809776129849377329817478", + "16488884047551411705397223604196364132975353217876182634038895586664127388979", + "17336432076451490238716890901095007360946878388179175784603587179384718443321", + "1210338460555723584699132156502555539583432069430631008706741082485009017102", + "5933432012048351362807861976737945204535374770355507745694008880123055490802", + "5127952499969178010015035020598142881788437616516517827214405489972695632240", + "21100924218139544842807404598627913291698574448527131003096325470925085906016", + ], + vec![ + "7683521602764604419863026286445694988900727173175219514555132623764360793654", + "20928394065137007852706990901925870323120588543710137320004640014111073449000", + "21375535333469484792161302750563386607223088895810564711097025913956371171769", + "8663517227154706072248636076587789834246541965140682871530851124960776424787", + "9182938389356039217318590654716613493414550996824701664670650439783557720226", + "8327338979442122743919832154397496089418582414082199116629974300650113777515", + "2474727241701323049333019668054716886184808783449917153147248751503852312804", + "8543922237501430855864877057711792269479294116675004771113148647309219620030", + "7863611214303285947093025404346084345102544167615769255495752297507346719791", + "1448902069752048144992778676670381235906144579949631101518897035253311063307", + ], + vec![ + "19501657783346989621892787238946890715709847672294934508902622542828235185048", + "17076525025777667838921778388186176564387475624769926249793144074465528465933", + "2381176586418291387279201678056498732033435079507661703992537801751492053086", + "20723508866659831749949206314442193102431573526415976696387848305764994281574", + "17461795780729443663350296040956479984433953861306521086706732257263430387445", + "14849025218838139413138931958408289986915143240245452275066866730847749323920", + "21207204042106390965753782189145584243052148578812105334769740484186308017901", + "3105302592226642624386332562899903659948819667537402316192380465808886843623", + "8765266846991616382097124552983206033439769882065573909634090515268812396114", + "9950016446092650730639179912416912603745831292536616469358668786853463197224", + ], + vec![ + "11739731747351277092817771330729393674312591071236310446088293450266807414263", + "5424991773995591044103668717299468589013142114099340604018933512575789323446", + "14582885509715812510585748465607279869582209618804039923778041514988867577359", + "12468934763690970929325823037406509081405444759649987929912706732364016057892", + "2792793293657306144108993077959195845478902430027171873963281969527327256602", + "8841327809851437433386666692145437950603022633472031964220924157605803799391", + "7845859360796082275932181771457755704129556353505380746504571839006944723429", + "10731793207832149137187382442869034250153492853628224932026933458041993639295", + "5597792614864287090861003890414825257635680048696075527563498604714157576447", + "2638669099010916296300870639816763122907432841565512299246441500223692345671", + ], + vec![ + "7150832464835357604208338666096132398994318721877322228060899549998179405057", + "5470477812928960639347760417261508685840724903499112719517942324191018679706", + "1063854480993555660259858748055514950231824974684462401269695511649059715242", + "14508243449586598349750829047481358081191713699373322296041764577478835760927", + "14872220983064543437506211589956319796231014912750035729896461676577407407598", + "9523202653584689553554068772241228948237208444616905879849472383190180438058", + "10557133197819890801524243760013157188954914093770589635201319240903423455316", + "4973822148190287060777561091733583032026446820262414806412485028147721872972", + "12017319043066808147670914562193696608548297038020764496633388575589573229927", + "20958507279974171556413354796214800332148109902768069171659933168603089927180", + ], + vec![ + "16142225389165963605704721785850680620029805525816101628767304750729950332962", + "21691255103889531967215183091383836488808797368461467004501598817850515277674", + "13360009791215314413428942977255018953699328534302248245107197249816193370823", + "5270206696221786165451075835596925139630328202641350960582852969440862939023", + "2626561181956261201864606929566987806068271006198808163435823619705436605447", + "5520368836328496672510351296660387187466158872913871354651108826881774455909", + "21597143280250120305740582323272730661347349587666707484376745221123282421748", + "5891209530846741397700015863630938364586207627850850447237189083999656313978", + "1202436381171550812585103405636986166232789491390007497511342220946215395818", + "9920320882147650877649039705433660083926352954797066179512349368247190410310", + ], + ], + vec![ + vec![ + "1098498142837982582047608372723518751721607512716925277273595859756333857326", + "498382712248562027578374863343601618793781182132084383060312181008958381971", + "19040726265283429618662679510157690394832296024968480927415996691029230011306", + "10367579130776133414495805974535693744211249758950881275217429221792836643614", + "1229596364469449066712193908302977020022727834238778132871229393863406546866", + "5594347757215876411130934611555467571639435097442631641074898978663329410864", + "625275312666547608222628560378372315159605662141936411119837279426221363981", + "15485529557721639677666143827295121022852505628489596851713462276650737776670", + "12156576509577081554587930818670905775536581975823788207855134544267814269606", + "1981640929928975005466842670997136169304057407742291166386016130552621471939", + "9375079124430521740651903984797221620963928972304905809259607327125669559872", + ], + vec![ + "268697279437287801043057266739136500465135819021738115532631740070584831216", + "9310725094036396036773344350803037792624399505581573214229419814378683970851", + "6144934044671205976376028664002834283864020049596457260475210339996948797436", + "4985941506647510031967748765284991041503308370910665002557248958100799063851", + "15851062719909725150709309168582658649310704358483047683106225599004779349418", + "9869770840966008659377598457679699092337106962689936558150689057592239644963", + "4964286354328869036674130011248598806906438908586967212984901377099285878228", + "13408525694456518383125684465410538061086669117275911801498275369395798296201", + "18263306792332242197764383101132914152275840410710698264525919817458731671889", + "10401786441956087930118823951510684636068781082958380915651220354850381871543", + "12496745101887166473879957440401384727148915595227764657145046356182346897947", + ], + vec![ + "56825204182651219072479187681186238157981743937496557304633023935549648224", + "7949519580094467639897040111470236633243836928348452962417270559805860514707", + "3509286722306670968352119363633866055096352721394520084890481975258162907251", + "21359945526252146173553061920944871506626324563977081669248710516265311530589", + "14649491209868365229844087258057697734286269047837985905275053819765825128984", + "12122186136173879572357400046587658543826161883897136171993927935307093999926", + "2666476328185593105035429309804341325262753927547102747066987631391232293139", + "21005241858197204874543384881533661499138265185107903730534607574687765896488", + "4866331653274711303641000079325074227730641553230218424779550288347820225149", + "938689939079340009195180604139206414955240264736983491692686499992823741696", + "950493909161345219342597929783079468041198261349024441783356363638640688155", + ], + vec![ + "8227093387774305505218050843028014038423742476679149203160700406235271548925", + "298899716277443866412562171123535849674476895336539413683307522836440058745", + "6985094123716229565713211140430519589886023406928617334981414752732877292051", + "4561102873171162160916461632027561255705058072826965137552144392802414262261", + "15422356128912397775473168682864290042256748428952418907369066530964035265216", + "21534011877473706794700774934355764894917955655606512952257743854629820348396", + "9461908500272520643111839486963426035162115487175673718316249722520977894185", + "9042969964854694648113546554619141983055960736166619708191725199599555275062", + "1441104948831954255692318866730011748129225465895791664253095290347818907280", + "19417400621113450826458192671383621002793369580946623762558060167661227354799", + "8244773274459817591888745631242804467035454174608673362960589130536385507190", + ], + vec![ + "17088086767144106377842029064730946925009348520592888187451688601493882340857", + "12886019902209719236096958359125451092745638766392722988311451127550961945660", + "20280862819329644063010032903732505647194710429034928708829957501178343790858", + "13239701144341900586601825324587185682073736334523805955933121583949546821724", + "2994618864933374534869864629648211464657674590007913715843569952783382900518", + "3072221011986428615228338853345294533299624086589539664037325300531050793357", + "13594276105600327401961157952766116939399999497643063180657161489419638074478", + "12904364780884039213184464580277965622079185353283126471569179129906875486852", + "15088962493677593800057541234990587773412340265413268221386103386021880406010", + "14138285403526705785804535000245522290348086552790608567368815987904186155718", + "13481415964846572771441311017814910258609608797603836070350286657768815710822", + ], + vec![ + "15459769479990273742477151452466966963353767555965255520456901549474045452607", + "8586052864861352028352866296665876117392195296860481710367953704812400661703", + "180502622991267551120688532508657597773982647209049478186474242637299204110", + "4785745751361586866577727263713743688205421961646731269452058881240942369409", + "4583871856798894230250707953295146343968130822948818555994825096960225600041", + "12377924729639905725281972784629126900954187435957722012223715002490809152047", + "21554415644278070156493674075483844873249829791940344144484983897474364915950", + "8390225843490125870104241611355504124284851919520955291024552578484662824128", + "2330476067094130593913781764168287559468546989640021387799865123741354870445", + "15749497374252464770935521609391859230015300749964554524771184068776070217841", + "16817654103281917947623051388088441309787140809596505043937473012669498321704", + ], + vec![ + "9987656178378986905964646161927549614205785047077068310684205046327286932204", + "21450061958292240283686535241652971764195183478875921481624114699420928365160", + "3904617432242099936494425054740854886663050476318725032541401300619628714123", + "21454964104289781104446533610149551385791852085041524046710270949744081353102", + "10768409462143965702783360646769759623397882338491564999208626639994081655791", + "19385613828688830964519526099114207553837496617978489639408163709100497624509", + "9385292780799468553063371906778802189174789542685475364513544798199315486080", + "19882577122462819381545089778080532575686772634821281258975533828284349988146", + "1462201549484596350490921057903425036211202388283463006651220816599917679116", + "16564642856725628254155356607086672564976261497486137590399143770170930986182", + "11606470848655267736219046910932382494518380844147406842964119623341701511194", + ], + vec![ + "546921055225672463086391798419385468083264065960104350335293012629066408625", + "12676737821548820987278730174038033161886561534502963159950183188070064038340", + "16429180804851559661054910451008618941371882312211198495282444364589225325606", + "8318514508896823373027050528521007144041407638548138855564062559664141902892", + "18546910687432012966956995548470714600618104024117576926439677823609854961263", + "12006683905722730408249989907056432037202625403043550391187503858618155798348", + "10816814135685807143320832554644398181525372167669730953193258726693903362148", + "1969445073620598650457101028079888612893685228913473332116076918643068711808", + "16873795316557869761040796336264749169213884122126281483001377666183529927793", + "8441268321647668856014389726368355391497206989491787976537908376817970369132", + "3378086906271763133245748026584767009750550242946195995254881868035794898559", + ], + vec![ + "12721353531573613369892164015903035636498816100971168742462654106875931342664", + "14969430369156214890953989610124286618925370029259450629468188666450865580556", + "8545723361883060050915916338313252821252873299513393695440138873537985282439", + "947668284380905375962163908708231363459059635485281084900173592739603282382", + "4418352807772484492818068921024797225893951828921880350002134747344565378254", + "18146914067008843660990756743559427698617136456156926109157771781314720068545", + "2353279078725994188579023195684884389261433430819033940093641668202046052763", + "18228226015329570627220992288018909552101992748538110505558715089403194764144", + "2251557590571495628913478692960173580728135227602564510397207128937882297417", + "20421664597091787362209209474226188711714308866665750343509458297343168321800", + "8187951594294388715811532560312339537604737243977265499957088579012554679278", + ], + vec![ + "15810834190411667509425096842396102750984990364193499272150958331088983323159", + "16884308240478579935994044823717491481297317573500280152191710196639752382061", + "234497484353824748419812158321111328486478789224631887096763967543932891899", + "21452418791072076854500976656696245147472896609273403517249960331326136475572", + "10860322289080285812992522532751459911253736747190334349942615321085283282595", + "149826608572716492570322179195234088797160854886751475825283168005807771516", + "11491761442863092383423796629001188933840969144934642247702733820824608517603", + "12099180244453415217270377899736157198045626379801787493348249001794558732373", + "13177983303979037999809722097100345612970493007300007493855625634642663397908", + "6849052800275826145043024580348093078809773712986428314364827674907764829568", + "21486255029472594818259653174918852363002807142725698741685253190938680807594", + ], + vec![ + "11451503340703054732459437884000132607423536025797075877436151438425159994269", + "8462539135531767509735697608276067216182907546891182278996691315801807234639", + "19944711893825824667372913293784300313762563232409638194240029859435259601775", + "10396631238556297232793544122243237485091433966091043100758266678889110827200", + "20667999270580360504376758654763163152764187226267414436968564661080084475852", + "10424436665500877000658892169756884171624649701456443210945810183301667922053", + "13894422482417998868290238401966517700776990643618129177567797594771207188055", + "9076475964444407787992938909179730031379198268423789105813333967195259669658", + "20479003631920854685589262232015009286810147171298477411667705150903826855301", + "9928015403359312830073752955992978705151208358029077246413002475277600546387", + "13981618256931763962905358530247354996931923386029793318275706908114940457317", + ], + ], + vec![ + vec![ + "20214838738486568883466588390719332066160511773018226407137866846447805607366", + "7161524737853996242838650618412058002168848579199128467811556550737619970970", + "2264369418377007316930430297757084139629356094085160360541578125176213258694", + "18691044064909968568998201940845291098399339626807500263611343942450116503516", + "15978743992268694554518277110515494413411623432213713029162001242329212269562", + "6711615239704822975151699228936015251056551262955961924747531220602950448829", + "14954997163751606686696628499315041796272082739441018134122451910369305642115", + "21573550100361192110069886620445669562472881453105471211193858578537227040439", + "21785281999660091964290541777959906196912107196794342243439922177000502203701", + "2946923208312508080510106804563669422427642075683605437758174474435322095802", + "14039283821812338763616072949057938719426671560747126284782727998420210694521", + "2531474643515410792989587528850930504447014242967363822821359471367799986101", + ], + vec![ + "14281461695965914110119049602449207565231627068856382054789426564141005041994", + "2155595480001027852247471998853878746887483662385654030663226564169133356539", + "6212474220474204735846033034823136351584003532895558668927059407038678087162", + "21589299957493491709069669042662513245508573637668760884022386808061869005942", + "5228547858762057503048110033821407961973668275986265942002757629551762149969", + "1151995769496843179907951142523838829938796346663877830241077357918848539138", + "16195901973518083237059346288792924649902586274815274684503783828189220931050", + "6205461827971201267719191643863468322713562983419848159871959495317073732623", + "21004710389082547785746156915318076260017385298749146368429985483091499557183", + "10094301525352802553607719810440185681054064961117719137647202357989110756759", + "1174362264673060234121108394303385502501621739298129145129042091221378391858", + "14586772089804608057953886654898255839796797046217599185042293580394420546552", + ], + vec![ + "704103301411330239947625288325002010320119746677418877341164806595452864925", + "7447867166827402056774077383104558156866119014007569966692643297177923018546", + "4252152864489296917539284690826221964698345550054947572793948075436067436040", + "3675525234832046985215853449128143168168428943627479235047788418993254287405", + "20125795627598431311475910664717716586147044241536953058242999762934679572886", + "9159576094573932436478222856304524043339640337232471953289062354187369243885", + "10410289328536677868407694844650868516861553712016012272941004725559785872650", + "18813119519933909103102649065156934680537361290190751928265976568411443987994", + "15043786404237278119878717250753259786450872051876817420168142382486008024593", + "16614805203312302723146840789675006378900903626996105116400354962001922700157", + "899949298359737140980259063526066233582477211127560605822280959405167872532", + "8350589775626940122507262589996655703528509795097550101006133878991750882468", + ], + vec![ + "10881253968160794744779175936360108103824976232977458894007732866457848744711", + "19359742822671794584060954988237182553116341604406926658049749172292672638977", + "10716853194721085390661796797973316855886234718612858006131046035921078793777", + "1194676839570189281149587289656564753779383829131008000754135056646064455278", + "11530412134598354110310733773537950950490005376234226554463355736782774653810", + "7158806839647137330333220334046918613209783693378018773439140974716028082046", + "18873459493111992992450800068055835432261777460679870727272006783676545919785", + "4597339034364379110034269874329162788488647975988086437272199171979371177111", + "16047595573111403874356093398802733070084530893238592035018321960924442437232", + "192949463851654477795020911703008125546432931266166268873310745978202434603", + "4826544617576366487123936439697751633333779280970103286526767080486441353413", + "10372441609969764399977561535165700928227575842447057367716683958896898456242", + ], + vec![ + "21479608666927871465054861416648367371602717876964759897062141685818604541372", + "18757812710789932354215078701254559681588101606101822541277700443926569010598", + "8502339138598356500092304059172334649791727023646195989902300809704249803746", + "21240184871409684692673423121366677112492469214890212851758021155034260698420", + "2702659403779176675766431784851669876796725738129029887042678538644093630255", + "18760062461290937265331504644060340132840729161526164449611377215801441916965", + "9598514148929007169331478849372274288455651725546984183500169574552892743616", + "12460679873938368098608659480431260988399308425323633114529665233186673892475", + "20582262751655750693560201069767758489467289978119794831247596435694971251287", + "7495462389257720258504478831214292184152544822380786356126692935003910627822", + "15847020891468169726540675640439992039404102490965287792626266482436024810091", + "13444178956365729587956577087448840645730541657243126743158358416431709484781", + ], + vec![ + "975733333906184480394673719901416555779305044861384485566696694336272649841", + "3016935868211088289963870855929013645268121688015888423636516996750583017171", + "20123197829824640950428347870445510232078708523077317828689832072338303017047", + "18496031799198869774970797646230665906722932354114482887753612521775690376535", + "448875332457320150287933426080386825611557032389972932765197125881964153702", + "15686083476904717209874986881961195356503069952883501862704199048297926079733", + "7399632407841430295111381086121470926608686430000074868388902950170939693998", + "19157441199146430337309347165554892283908758853741856357912555742738097866135", + "16120175937370916934366957179931217076202557540631878137626313655342796978134", + "17363463873417672052573440102339969267068334412527908172228668014397269133762", + "19764937897808275673467150361977575240242645746672288031015882089680753193420", + "4264866715026149043371443488601547814355809386242957666273811883512215893986", + ], + vec![ + "15361071774597522987390988933793735468585435977940286138223232105339041682390", + "13444894080484049025660420839638753203298145906295848687612728375851966859563", + "21344396291142953621865942956005813155481114773979414786364869990164493168988", + "13658955537084761077271566555621122724333408573482369456630860179831273897019", + "12249794154563702076745009616085271813195258124596555920372455161542247237218", + "2325936171131642979629131064685171177284405924159468319138840745089808621723", + "3077792516542862676300186898187316876000625806438082696997401720196346610884", + "21531439209065692564653170259849715075994439889756241721092517464459744953429", + "765723669836774164873260120197059605145439283015732643840276151768662398969", + "18430565163341347334129211602477703661982280889871625277638214021594755964149", + "15168574820004856312411802521805000105968244665018577358614174215627811033660", + "15609931056593305381714243964783225295053513474263648739398875586829969929857", + ], + vec![ + "13057901697952283349663465856361305032896972742145291496691475939407531431061", + "12802339382735521870414423620343194986509343830854539673006232369799979885569", + "7880620299082787885902391014823825400306816285911629201048471522567587002433", + "11085221899164994413080236199596538381402309021910771788195135649489784323294", + "5662061777175931509849062158785593075054461300898808576328927259801174692172", + "19620207415640534190314969542389551464821476135583607027506853686406083753807", + "1396012663571482634431038119696061726217340333644346862093678929991918911771", + "2833239640477482582925766504780450890721725782645633022528100604619065406714", + "9289278993548596713194730547769009982667061443580050906003394115646319823584", + "255970566924787837673441110425992267446525707891905710167559324774004600788", + "1607914894461957709182037732125046273691353312066921168498378132410220447224", + "18182823650001333075321511247233769219797858542696762318647781137777390858484", + ], + vec![ + "7933393968545943401801081658073805133658457161128306876557740191220424567009", + "20347911076420909832061080138703827506796370399337016973599032573292195453934", + "21705355682416154516146726727072637540254191883301906287097797468012136754530", + "4393707213821090202627671673506613966066953344462172841905371093203947245835", + "20015218870609611793683104277034599032802947064261475029925414549380954228847", + "1857954279082383201486002148223947538500417662449637107611316684336169564836", + "17075622394357639776259605879156105089449277115338468776425087829255085998708", + "7849339039625631210191134106813689727565217371730065596482503614929894173038", + "21512601485458872387622978217203423557092449252654722032828165846830386134980", + "18829825907628826679915224363735067358668372607988097049308159580108100510295", + "20145344934445170391525281095510437469996020214770888614087514859850402376676", + "9313354461544201805378332544085825218707302313348145137439630918018863309279", + ], + vec![ + "10073510764514576042491642785075889098754556687860419668420630560526859371108", + "11174613823246619246542059297257164876488140873425479553593597508295299674750", + "14486836623332191458290523271325176557072260063250466764030358280051240942286", + "1932726606175618877183776650118686155013167275195994452359992776843317764341", + "10586981584735794740885178709528523597770788130558769643251430679627096503451", + "17058731514535449611097320348142652958393616886312564975789110366863616539420", + "21546144187559470183347034044609056404441744756503592317087407561629774536920", + "5681882870231768621749544290358493454695957383787481123871436386675876219635", + "6603033703828934401094376159910456525337139277249114676008536852402499584614", + "1075347119451441392402288921187671249679641364496638526228862984392689015760", + "9887880282527621962449293235959776308591956208594163166185549404667941094205", + "3081779595493746844428351914840666042619592907445560123915127502392400574614", + ], + vec![ + "11109844704163389102553826717541117344605357734084342755776036340022417198082", + "5060345909602600407449982784585458050225699107485058131116856520723613936306", + "20049893406143885619592680397871697055896501875354968455865404394786911398458", + "11740809795693360891733016778293494519471041728488086332325924371909574885493", + "21414777615318644939200434569971788107661288737252832241371433802590137831626", + "19926309906539946638451151936495517057597377615887396199326446649447508281702", + "5833294070005894544680949322571753681474561462111500624110195335953784263127", + "20316262126697618722223967532370347145297985363803056816800332573538115921683", + "20118897454905330779316757365327082825225674670546613715170828215358297124461", + "15893782176793316439240260419014348246083695310846638270933249304684265430800", + "12847296795001788271556697499714377689095182754228824085698104180563585670787", + "17258109440267943312537478894153608811927087776527641627893802618672319064807", + ], + vec![ + "3727185744255496747036491258134142468721926815259510264718979349995349167789", + "2377620008282598351802066487452475263179928244128123362464911386705146759528", + "20330733534745333298462159658402131849518313653717741882717272744687077336453", + "3063488930518144343621406800230347607891775381984489334408858649400823600099", + "11822391183098027641060542512210687183510613996100060945754635806285989372827", + "2697686870567304805976687716601580249659499813659634827192211658186812105269", + "19466890284409856892962357589067669895394760875472697889494886746493744150398", + "1006970646211395884475799222625896618366447925898943829426435645426534803620", + "18668143903499292595688863135570950175417970684200058312498191992564173409237", + "374118929819602952730503470915153310582862106749955863047933775501492632816", + "8098759627317959799834443934069068232617039455327629644555780572940389866941", + "5647931789489182000343586961287147762347200093731102535565999902997464444183", + ], + ], + vec![ + vec![ + "5891205978627836991071144083270417159015157070199928807771268303875194037650", + "19534191765629085451497649051014772157774065629075791332793195826681584551273", + "5154833515272483128294702820663628026710043323095920240638701304804298499578", + "7917593571945709638335150893778153193741477651398934233734658265336884279055", + "5133163238095742835090645087711007173805146496653007212988348307349716673728", + "19458003745533910239158707983408152209004063097952693956218574434126899070042", + "18880819400751577287416293176849355951596193714265500681157024197361640709188", + "11433257809059443065528679883569438998689217744131300496692049205047550090935", + "12190874701550908088290603272755607342096152398135156662834905887211629834704", + "3673886960353993252497154566539843726250940370616316569888448647738018022083", + "15676037835112699420746702265028664494892600184195941373794207326709270851677", + "5045635616511022726309482514512221209262777381751689684810362174166837266849", + "2127981970274354891783037834911068612842150500572698772082540184222710046966", + ], + vec![ + "20602209860969247631763456039704661822909928028055826436834118113792574371849", + "4493454930923344041221912772221535937546211498548091351164191172571413962846", + "9203273896007845628978022785284502260659411177801743914849998536940830966257", + "8789969715987458351416076625905723956294447007026950650495844175328857015476", + "11215930522605102963220331022508304708093835095809374254680583403572809751875", + "15586225257380986275249577321891268850474446160605827963612996219805386502932", + "10011841080310254678847831138830289501574615297642717372149423820287945856079", + "18753960408421275836614693391945489354883623026056573684871883182548779915774", + "19761678450406536764850099419131825403291221931719095841429266089353917202891", + "2072721799733232450097521331054430993573774079728747665078766019206508544636", + "10615297085623730419003352440151421233872764050371800287995783143303761635742", + "11342862179098306415193268089882628716478112844171129584590093902612740081938", + "19276661508999391381559540866976056457016683157876039262437370976462342153704", + ], + vec![ + "3487982371390545412669202630006964510064537478038100559383689697998101381695", + "18019611455528497754832260490613820837001396745087990928355304172772344827985", + "18731969803319425707048526160946688629598037600738751037795347491343115736279", + "18895516979789867152215547520753345684534505439453679476362827176682322481937", + "8262582236770254192527817333585360308520228688394271238254948357217070957179", + "20124670759706967221386035867404978156598790198778905768160002435038055915086", + "10113512603622787997151768792636528607526786828914243169997168136386113705095", + "915897670578586705347124681284501818659263249883815809455861196950322359631", + "4168315355477923626825760085789663510629998017921421169386695143739645254818", + "13671994112691093230470350971338683534216964112891455368255908216954091232088", + "16886224211742114996348237388698196253930997227482938831037908300450123060344", + "3345133367703042017339663005080189359441174937067366586009093723866269451347", + "21528583089657067992968569213666076092311468898762774519530397406988724032331", + ], + vec![ + "15876161034145690426475777675897218203065468785806228994483284137836054650127", + "11419482087592638487692501143058453465931464811952523437138882421550619359191", + "4688593371456663565609532492788107789533208004746508669973851893459273665535", + "1201806670097794327812047975382630669548999745861216990648173033237996826404", + "4317641195125807665177432835324854194367911827066332519796115671103402289320", + "7278153623621857829571838333149240184056003767208572498343141321166882833584", + "5645725384264681461759518050072125001915558414870126637288820197391715227313", + "7876944044995178879031614460771670730631140542631541219377182212657483769883", + "1817650660807237476840344988506407016951597837358142384730920692904089879519", + "5477456541807551375261337022497006230056471139241891672239301675658950367705", + "8048211508931499636316723219242009338536169470228918968914286373316199192147", + "11051780522682663717015921863167554166331060525740053284975151807269395431450", + "13621239165564266256293257623306520070257833884735472109300551735647149439281", + ], + vec![ + "2212937635665982737914958126511628913962157442295931340442990688391698941226", + "15995366165560744217392074544914614299200056620022955679432568011246760194348", + "8186603384193770310414376291688089375415922904100458138117461272757184852177", + "19591014640167727612037871145675698427320771791339346286884839214170680861630", + "6878084246380728147562027775456286883121281123291292075367753371891198993189", + "2177050211387664317673794964274713735596159455191994291603735988793477650579", + "19810792753868883549077084303872022596455081266982011682803771833184330522738", + "7185072414158632003497987061744951789947697798790757573674746012007540132625", + "12527008463897431318214816404214269326472255194708737027196205809865368523993", + "6934500447964393691613594947677420114877610140521721836488732706620555412923", + "9978011727059765171039296158502240318826874847845043732001483806732259491882", + "21367223873262404675887107131444254925505042466133465670356500592380419754092", + "12370989539828127569760369184182336850673006315469364895069603190681475159813", + ], + vec![ + "4771734208255151020750966033073146490824522462970752283771863328392876062708", + "13551343845317029011162863399460125746613189002552375164523344552618567494698", + "8714428409330855425634336943651573814895603648572558273360471650145732556280", + "1770920281347553432035718101936298919487500537138994976517066719980578590089", + "3110069281490803391353365800012007306367848815614936878141004366920256162421", + "587101336788172489216190547515347729725635829809300681040400739386253763168", + "2745547964008447408503376832407161412155228373377123760513770235899201269964", + "15688219884606649780982718944113231917978875761031663915052600865004707442286", + "18825709614401251798160680403375685428394659815656068061728877494732182115807", + "7344398268236623675422623600003139537460576229211381042555723883054380022043", + "14666515770263042245313469306170077834894759906373286169967918153150186862642", + "6262353441640473491135912890626291592970997790093308164286742582769628052614", + "16647307543328963728423591228360400112670150511841037950484862728187168155597", + ], + vec![ + "11187123547390829437191210933751439038277808812390863028310714957203862953416", + "13431586020033401007013925927716006954532655767977222332198563123215088393612", + "4290575536028694423523505804878297212249395907285796384174966179335089734293", + "9525030500997851642842588010076299538258273880797610368114449809143832950303", + "8240494019366037169932737683997756281590058122972608854062263901069681117554", + "20634410655079842888667296641045124414486057143740858179591482529433244800210", + "2021226937398532158458585055746155459624344885692396128118875161667614679890", + "13363628208058779432402710458326211021009444288989875416757721068391318188214", + "14662421589311461388753832631349921077594459767269924751258576584313288868105", + "18478701421001679788418312436274921897007545359172305786472370930338255515306", + "779644354087716689348274274240595541489283221242495213448957276905050464536", + "16071592196394048404777963063722475415819376772419538934537115615105548954438", + "5095096770582161819227893847981354649325178848130636101047350986634230116037", + ], + vec![ + "1243295118881144894548654933667320243992122811397983231810580344448403973343", + "11884934205846782297010667633102865650294795721122133935339824653150509106639", + "7458968983251027899406062962031140351528726088472453510482489928822496580100", + "19572605475586099575374380909719328911692508931262625802140140705257944509766", + "14705309817743162695613012501115465410697971407093611587529338557210155341093", + "21814585268359946040619047839768523980706543116273413618895661291550785045639", + "18720305501197276565912107809183977276090965285535057360911917408689742145019", + "2134301697439195186325742384937390317718398738774895777564128344393744278579", + "16999326242022117650983709520661797031983791094852258286603416430772587131676", + "17897483181215416614794986081059087805317610826416633427262022077916365348849", + "19707797946013555426424189263942163273279448488563211841018471715309464788783", + "14555678829341308540860562709255991938855501651550888461483653488337939676588", + "17257409408848021559108687223120061819076248102607600439065783833668882002860", + ], + vec![ + "3083159817330696927114122348973911210253613266522114299928693807761894470034", + "5736074496230638296274343498461296106748247754169694274901381380232637436330", + "8207744709591183622611260068351833593643143431375276434360211505091128037806", + "16073710603427960567922233549405442518423088068367439127980767364626490766482", + "16125801016656798988611163501719363451449395969542389751490084517803061425074", + "16681204974924630782971582682795720527615927905982945319130944791490607417696", + "4072675318306311800303326010748274698511258916524447342766005819081244518392", + "4639558473350853876171991553789446979158416238030006798108198266387155784407", + "21611752344375994669307116989730257280581712049771305171467376136735830835317", + "14260028812889714557229612460335412900866192266288236233734870104907234066106", + "10936007367915129326030460455513265303372033050181411601450223671258879981020", + "2935032369592212871409648743766195391225915910970902425024269583756879977136", + "6634946569637959135045435256486295750841113686207268069245788404426148269439", + ], + vec![ + "11165382706437522214793349607928919108508947068233467479625942161240196013032", + "11449774151698349588558943383567398137746718964787475388291636245211033594857", + "13380655477865684658511486065341626238240224687038830357479380314844874141318", + "1556090800260299290436338214947407050034615159120446561828975680392439133850", + "15775229412830292677008903643751483031582598935755329498528261914480871637362", + "8325948986690458545596228454116700887740572176003019243020371356605705227449", + "1530006957398320461897940887072398082651602436763497487522949690691142033613", + "12096799031117418724262752691656478204625211872574986459351576236141568686903", + "9795222686269696766812901618791046177266354705263547407732303299461050133927", + "6370027108686216641431817942352503637286925173249339052075608610090399016749", + "16881432515653361341795686702127944966732199129855247935726797972793170639701", + "15351738821101585856536273249878864428731819041333447251288973888111661451683", + "14781212701946742438784746658712056984412254191444809612525158953148214912100", + ], + vec![ + "8091550554023025707193058566806958042583606199181127012808071174695106343115", + "20490495862854672187041438553984493686275844004543178578410057111061213880755", + "17273768908086623408127314492263145283983205996943328025810362733169697859553", + "19294495315219029609698328900049261980545541811825479502505060031138576089112", + "21659964890056395567978937545379633715401240283937936880886676845913261767053", + "17982445074266408124204445317785538167802060185652418682509052997213396963809", + "5844393214733022541634389892381973171593154485646342141850588851005973351855", + "18594392739980641449638044568401634303115397702236811642652121212412266849233", + "8182160648431978634742268417253967926524505311200391645921674911687111696608", + "11615953679796573972512524871850400010935503185676276201708450621253942540441", + "21529769273680682324960067458105490598252059794192747768640910191459525561125", + "20132110404365493609540330952369583903023878161175287878881490874880122068662", + "3315629685104800403477925330007368560121731964009894641262206418774875779654", + ], + vec![ + "20760167657218552641600617661638902204729190730886821404831449731791027856388", + "1071944235595105837840075721866496337725631499557288435858051131598364359180", + "4555500238184379140256400675916267083899817767012366155141824954325999920862", + "12550383095401941417929336389002623533497339330808819244458405679309028814897", + "16242206659396631583090203067480642438705316999988728264056381863300603790054", + "9739751068073084582742270086389269366580429568378316378361121134559060472609", + "16116599372526867528428771037225046273420084760475362036998100512782645537424", + "1491702476187736766465155485454380021314952117101776564498935819251125640550", + "15487639992401023512588653485898591177260483949656489128772103961337143916568", + "271607781293753174262565976623664759968566553298322241892863651425822643900", + "19977950168772761551778240748644447848771422612925087742515420134526515662483", + "1983058998587842721058540590280805932874010898105761473162166721490888500174", + "16107724245058262116195892590046753877901405295502298275519986777803068771465", + ], + vec![ + "15598969808860428995774387003121151871293432204528482957206560135604646381090", + "10303850219152079194793643117738367623863138704730923818757626414538626924093", + "18042134015692222011976424010811982750427479608456170876100049843530175591980", + "11698490610713203406365860893495577403678312400711273827266244766764136895745", + "15094763799279956189651458728729592947034549021140257103479085265792492443257", + "430096731859098382312819496464652103427606481282347941948051379574546904741", + "13960717870097098006695192936395126361902142536383841733120824678277505383661", + "4016844697779547662080765040347308557842613782172342555085194018348439875647", + "21390612758814913695838461279472506230937295081984131634720827665924667792338", + "15210153052017283712229693210655704356560541803226219169450332030313730637768", + "1405020343631287949667260064722407285384539868534544303707265307694982887517", + "923081188208761071201163943024810005498690637139303388717205039798310044759", + "11733793144318360060340673323677375331041661345320756410073354731608712531433", + ], + ], + vec![ + vec![ + "6418344278839121997761558068555633277874924383297235060428040203550148460392", + "8184778893153361724502825398680471446632259194563037088246379746010351701224", + "16103007220917684483813510789286348056519805127857943417823167430289370248882", + "1664056765289606259902279533842090994664529145577166075019619213360544081038", + "8189267971733078428327714274800548471520787418839983750528172780925243373109", + "20200362011107872066803394413626937903139046091127740529060907959370256880701", + "20173744990845412211550008592188171997284132875646553363208340317991304601908", + "6100976759548353184263451545777211359935324012594548692714862113681123862281", + "18589557631793259794347972680714314322014920073994928130094285735070065431315", + "774191617468212021433032703138192471679212014828288788368078839883023639562", + "20846157077077136618808870082870348881758556999252332666235423866411633465885", + "179369838162302125553370844232732082673907343764950159722856079645852949296", + "11918621764929568738238867861947162353407822663185301208219638335882724852664", + "11892816132266551039449220442540498829071526084314780371963132054098089857199", + ], + vec![ + "8372083054789567618141127024727893763407629214826505908070525202510871464376", + "16053969929668995455194287538126250235271805322133461918670713060143921845064", + "2376335354846722834191665301267088635977856417140218787958824096999457654386", + "8473768787638105406889032631028566536048259675966039382891971280359035809717", + "21341339254986951084972542739281440827262568377712659704364142966685544694259", + "17589661461505870684154172724042456165609752814624225501433267429299923279866", + "15569410058783548405562051853887675489795673100717406458705097884992918832740", + "17136771849960699223133648686528512780844440317239376836819594891344485260741", + "6846911134505306255484642794298233221271816466086606204236441947984501580805", + "6155197242495613968880604860842601809115711093712877692064968695514089066009", + "19128637117872288039529749360298403408432890998345732113683578733270478526860", + "13897081066927368111242039741190751095002227555149381235627883407347099966197", + "15568696369607733677931070776042128239961624292553408918258251667007167792827", + "4872469898347155985390501972210222637651779849978701189099596399070936407527", + ], + vec![ + "15828528305683107589096901962734302130704783164486093165237194118210566499447", + "10871050448146531061933214597828428556087596885695624079458134367495768692641", + "12715634392841723603484337993449575047813504874083783048519693646075495401499", + "12461797447292058643518857326956961860724022201671935652194772338021756072767", + "7893478478385050482361413970069327892342442172936930424757486435631374257835", + "13837601157198138379089270567935062044589875195895421018230835162664685103799", + "2902437942374588332095798046833013227638771976745087670719351344275677524328", + "15243444720000907684100993103252257759861356192301299607302082943722781818072", + "13807772002950549127635782715429970069282430778664455286304012934232212216437", + "3330690659470353458591000516836956263198779643900957759109026769401030561064", + "9121369550752168091283493588432030055987339875340008013960004670271046125246", + "6735940425069065421825600687003545396352913420833444906054707032006918504438", + "10202597085447755780667010145999569476263425973948073114389503196598866401333", + "5502428069812047253902185475715178213199056100606031376530298129227524479345", + ], + vec![ + "16644412428296109976288812904703533310452124314681543539244699753829624611041", + "1464363769917768267070780357984170193149530071521363359029977430003041058712", + "6585015011013232733155666746473112211355795487658434466373981554623709413097", + "4801399347914387190330170488585953929447785020623558112311440395749307391856", + "4112082032915966312546686950757741516554419896344654985273514512341960565972", + "21751105223203500709422523072334627517145925318798961657655356624798705469220", + "5444707496026644829581718915396971639630623832272466243662913303978147462160", + "5176202236175268355216491019205403607765794084697970681965844070084688885903", + "9306814982626014542830328909308268815941037317672165275254890008767120833129", + "18534005003473959617581160840952814838772020918476059581821124675494141739706", + "6315286473605163880327953940950267064586093973100720152185112301408202766527", + "1300066066523772310953083996541204931152213396378337235691979863222476540925", + "5067928636570884230858740896001720688497565237268231912398775294420405805509", + "2247332707031505717534336329466562637772796336116582429349574314234357562544", + ], + vec![ + "20247738241445291688801934041857783193064556313940428743035963593744371184825", + "18242310613256758222060583109097399506427347785291803988073944852458874238653", + "5027570650503078047623512055050961603632044524914664539379503584245086748701", + "5171024028215683374631525538977404535998938725569475537905229563957776539718", + "14944506678316768982115249709678873125670683211913646815567280613875065136559", + "12712774808537003317921729077604843728755135888798177519726799585299850232729", + "9704917525949617705578174784620125192847872142914494686332094535178307346294", + "17607293541936391773356775760016259535976314844718033784671775110177131099056", + "2540563558441922228123722363766317044293335648393481767268774186670778379682", + "675334497725789143197575210295522345522852999942555409554666160997486976627", + "19406946394055111935833464724872576724279555593094398662505995447037826031689", + "8556081865419484437161415079267380957278647215963771014478927152338629734722", + "10894080579102201350420363592007910695129177124810422954037601558695648517625", + "11291505870061211613677608925985023369330908318817731122393740145823745286941", + ], + vec![ + "8111345701049008354923033690718534410144029586027127161268652239467844080405", + "274549942856353845785392053622966579448945647367587770109876363273061435394", + "4517253471300397604524436610993375842851058850598308811197142784491633632173", + "752091920065645178354359484693693144698238715470242001046768604290011335506", + "11638201273705718037686872429810169270152128290712392950719778292205581621583", + "347106139963113855692776969661437659762102942024424980057845079543094637582", + "1650860247124995235720423411540903470864782697117302631679816892337391491913", + "8807792864573824348524944774842835780541376663413796103538010142332713129239", + "10125706840777901469452374931444076850109537279493387272544675392762656076263", + "5796395502360267028606772833560634747652475684290748049574810918512176530377", + "14560087906294745333868694378307270211143393355422503000240374502300675908843", + "5122547319055760404581514483238475236540700695938727023605315976547513018758", + "18167310745991466802649320845690518166441060923615799361858509593334862918793", + "10537655786889979411145772153623144374332103428122704649668615330715372491734", + ], + vec![ + "11129217221221505592409814225825346415830118409870141057639470413354985898470", + "18539905463366886572265160849053355497514707079972544561033138019460289616722", + "2634262960559341073751666364573144239402802922297326052528609874036724778513", + "1017221748705774701515487698826243372900579204850532621904642815268966836932", + "17514607882958712820434977243901228312981191640605665595923587928949930664369", + "11518730158165740016295488007249832672982237183652963149460237152135828893220", + "5440554707898671641433725775650782970626686693839748603607869391440450924607", + "9147974268228093219515993293158817299424235911620662153289209094112083583914", + "2705991777416683673465834362340927783587369064825521543877658261521166181909", + "17763220243034576321349123132976335282008457858814906290437636386824597776861", + "8436811158855395670172615633911662578819734431001698203244903207934616540973", + "4138616319619099661442960332848564292509386403496259477312793995345740346110", + "9291305887217504987438822522544685995385674372729325355668719643387679293273", + "14247130176192495492949419984506575479387281081069847984799112525576996955413", + ], + vec![ + "12065917784915207956255864287163607339403293417584561373088222422519855200010", + "19602746430676351974790620090862300757670420200072717172839368523905726952498", + "5634613092261261536249683912838127927279222476821160426063089671641025921660", + "5043354945289735676261322233679902891599697472766188245131942353534121350357", + "10504830582720502950783794220870443419986496189960249704278002268186024527222", + "14773755085011014730752864609252484899166588993023999143755304644745097411069", + "15143011250372146369484566133567610168314155803929775363148643430052742055690", + "9296771043817098875687880718048618354875037373605143322099708291917193542563", + "6699308928121904151061270394393246387724926649022798867634982215175491150673", + "8773562200655600334022608356584787571874596251190350038742467377669146368640", + "14351113364159322541281738731216101468935785403577478179790191188832046493458", + "4850132968055386067280912095292570467587181719320385350609813213372507065742", + "4016458733475657342057293585429845015911706822034026494547861057484844319736", + "894041043510502707790816962794342134661931379046385946357294445947889380857", + ], + vec![ + "6102901509904208647404960172211548023168535043766794923435155807898706549347", + "10993930772305308408754972248679389846078570700140268311987303413959565163567", + "11304109937008720250639855591630423562437629896442798574433639679310105935745", + "2101572929952921976477335632619843501489349436900225186251308254022656908969", + "13379549674365217138865497711163371499213584223441782700932894243483907931587", + "14594340674649653462364863346385318403203482098904810919462071947226757935441", + "10647634642733631076053157841634424396589261258510712678605967658799137793311", + "16930068860033006574251855928288208559689461032565007963103701897524112820278", + "17170753006961827437085793102001841977757115057767702296590976802337127094191", + "1342928489123424058754093123906133488378103161461686346784407392402405815911", + "355084123756415397117817901422581736826549282147584490336790542339114994995", + "1823092998982212793902589678970070284970565355035702454684653415443223765731", + "8048898551230697881244098474076915607341905762324328681953519068200924158237", + "20856146965109368880184165603695312280799455722407374791306423939515039038196", + ], + vec![ + "3432336669019104452964292940253917813738689308738628946720655243686051453920", + "7245438991832359030357502486353272155562833935372540938834365796871211337300", + "3373527222057951116939099747150655397463396105930227294848620975346900604136", + "12572707687046208161448564696692430877391470466184921096766838283998472321208", + "12384937031493229201662431219560608524659573369707802134091983819133706078063", + "20462122407152292556380602425948480298757076111535435284512383591498863731385", + "6622785585380387259638611864923325325049433660844748518649420260935388464763", + "15605049021670133989806310757216368923392900885847286376362426319164543334301", + "14810486805240500676478878806350063220126440203863567013665308737654824897393", + "15365995118119349988306703047684161019037838200991362610041019844939301169864", + "6214064907470154787016516294417895267553707205697453843662274325114029144555", + "21692877109550228305351038417910800050989918141485758185602644390604582268196", + "7958034635501608074408556743462969750498597833797740641656952760348926890967", + "3158783210920420629823137370399691077131051120551658723733852791768119909942", + ], + vec![ + "18819671751132769006885776104610704012597692175792895108910904654781356258396", + "4790325452934584098003515312147561388538271922609950099121379835100064976005", + "20767398895337785696143628393192433940154143348446221959395714109953426025406", + "21100955173515864394919601160078443835098475047602620649008862351655083694950", + "8821416529246363042674973639302381036248538012124317741611227124444469290957", + "11914021995756592924439683950035443074155751236836696847913625157420325157778", + "12885168008148287764892654933214399102034605991806678959486007885552095995306", + "4425275709248360981167156075250953689813562430283330510520431316578062369047", + "18741351324856319300007832572892392651059227679283122747711533395263296376480", + "684786537545646459534722549414114801405982388707719975074226812265976115381", + "13474309605538568830758681457803561340228343878559353019653388997246494295147", + "3099805728977762711509412402176547022850118685798164610570416531465753528610", + "5600770441217208920894248156825173605916487562076226390167638135518086562704", + "20319458329312269677818220957673767032133429098791178878849925747919552800984", + ], + vec![ + "14900034607445803864669093098274289660238359682591825230729114910199159461945", + "8290880045417869099036293157381173265982569186829040493570954991061128070937", + "8462436298566072387124764625279911648753174589013128335782578822029407672023", + "21859976115370951722195695482147975930683297060804395404861331672395447064815", + "14955925753372802734448760267566451635799685206346879293698701403481312089613", + "4065981601972608953318716423564125873552717375448854762316325928896715511302", + "16523330234791629267329601436136683980570251446067965291433875700044665982992", + "4829516041540259457263557932051130738530942234255982016822863700315113125159", + "2798840870433591285859944008589519120500790120158397573735407833376475300691", + "13606709616318037902590277227595176470474803011913892797662975188860939656033", + "18594031070203820621139505381680984797252179318940256457018473319622484612714", + "301595919185954926511829797241004376609778538475902874115890821742528188064", + "13821866899865257052128875138353714301663063129600329885914206051645789155403", + "17834817495579398016717972195069792832976969906967797534885124084571666971093", + ], + vec![ + "4636638693344568063056245513561087534038777759740617868439601311740450428791", + "19461094558355290408553329139619939302588770167904059927666632659470708174566", + "21540578068459849172758651661944081878533817564350404794583097681041474789761", + "14656354515483866868842463518575118611017107704362419507056354562832024225447", + "343560765432454876700403976911389497782754407830340801930660700142029455668", + "11849359483183996767521484728570765305101196328390648813094255236259946055317", + "19924264286421201319107239943837407504286665549555105416180422159851955997381", + "17166797941774367858319514708612731492719223682833034509589752159955652705269", + "214236542688014950402766598120744795751883810511956979430464011049570919618", + "19396313302865541199110558245182888004201133198902041449761385765703954241755", + "12933296125492490585010683271492258153065303286717891025958189212827073719981", + "17844270568289843619005627429285364222068221173113885235581641198349558307966", + "10272636157097124940050637562161546985351212351240072791897429490752295849402", + "5635761628763643716582314870927054669889487541831762994265390120663404976213", + ], + vec![ + "1986614715532243876888195532508093896345436225715262830320266933005235142097", + "1081989267417492031457620947123272024310181199896785177670601875483149154563", + "4271010751492446271792663900865386872716262596127435260947966701647960153580", + "9432817034096362918055925335622289410018247097406473925807967486887418646785", + "8420506466048434939907866067225441403679080978454172178355845135440301602325", + "7476612931957868870953074766426582066042735713430654491467754928300129405389", + "7004218213610744415525118134908394594325377744218316765510260162880307132371", + "7571768879245095309014348656876595924444469219593531275918644914891061799835", + "4094897072683886403820666572344492999293005026807228977160362626188210714131", + "16648450944544843693538650431603587114533063135176857105384828552675810607958", + "12887042974366913908128212909540148628955501312789089034196573637089727885967", + "7707639242815954375609957852547086529799646052590213478924819089112009057667", + "8823950326593148986044014943552213420762296615584865468926316064433145020153", + "3784973153813546220636380916429273484146041804997715013288159720673291711004", + ], + ], + vec![ + vec![ + "1954546571818731885139861264947334230782822161673023234242993080695489129982", + "11606713580838194823093847718802359011098299538034455148401855555744041817997", + "21778217939341959600865514937973379081571132553754734185669152755967486158807", + "11867012199835162777599593543744285374463489953452947402200134749407575327780", + "20668200962959535110219664454556867828577715202494491079283962871771719016091", + "6796938349934826085352626361055311106987991567096993611616805270698773290279", + "15108030096316731537404525399718062561401239877230837132486990179727134215091", + "18618583058942935584876943765894457772128324732451762769633954204661267055617", + "7446958258820445329937058505234183740111199633995331064868799738609505041620", + "11019126795578266009911151080068316016022306110283709712693862589215772062237", + "17644961526468872013219663511656737898249108220341985100127433673616476030536", + "4959721361611533340499147366149623398780635086479466110353420780375692399477", + "18273613083607267201259595982191169247452947601227995394305633285537292365096", + "17816466502776842735116945485728134149282831962573761460376746436502757322332", + "7013781340485780306773324480395548266877763825891494701746512901494705653158", + ], + vec![ + "7793291197073594006386853421213450159077336220644997691715731402410704643042", + "3426005025972529257284910903433598760993095858232060658495826014698591260944", + "18530832095369567225742997294004908129637537286260217494648426754251364141983", + "16489909564793485793960504581924093693287639849995832883358263301067754354184", + "15126602449686250365534081130480301443166072957206187316416707568927051456663", + "13994979972845996867477162556668014834350043400046615820474382058282820089898", + "17892071176071030024436108339592708638442586845389229765280031864359446154887", + "7094246519433675669226318744483462997736245331529142696208838903133391196837", + "19739600791550679646703071148379836779124671330492407813260108679076122705926", + "16577895124793812992345966235533166228538388644746952252700985971901794098608", + "2190990407832635064016354900528055762572032133913345251583721394536626731922", + "10838969594099257399038118686024400001327577210604256394537002295046250661365", + "13742554186879139633322968994905507641568437399912823098239782636831322642395", + "10281113667801091149613944447670705624056560574926411753502305328318932013688", + "7661208680673970050246952651218127022141152720979640414729369551173790735959", + ], + vec![ + "14369836811580283035547554195559038793886958236571577920508487931208924192768", + "20603628573396476191496332378884772502350107937108583985752646932901407759112", + "19296916296835469264085474516279583782033370007674993417080564950885860980156", + "10983867448590555143664432588641225682254935452824608025544914671100236945380", + "5670198946055747149234813634846142209283829958947146164536023332201358566553", + "4904816432035963931263837796941455228547544800276020247096183162764093041386", + "928528370618860212551901809222389226336726628142306562102221490519648216649", + "8727385187994811157471310113729025912812882704232858255495018737569420129281", + "19909768217191699902186248006262494556099457367519802119733085801884256380544", + "10635786582281955931244778086998962127059196955758207056871772748744817883737", + "7140512340052162441606422433836236465795273624186668144911701254961330905493", + "16598081311443832517669265039250197623929992506944409626575335140315057620768", + "2339664320384903939910962081546057089170206846484766939921698239663651706239", + "425509623704802982425483674266195224640999670982140442030650575449074971057", + "7922384239142329258156226873732902413897900318612725000714450267548570680404", + ], + vec![ + "8178140403014386057685967488315772252114289881535707170540858306748328725322", + "12689293740944871195190670877158259851710828253354810002997981152414697198513", + "13670630626216376948528966598720909229691593992164633421606526176324419533442", + "20189490101967313329851160663874367593390331759675962821030507426149184002493", + "18209972608416650990264895614325602746017028678399567737887116829945804399280", + "20353660437114078502000672122042327871511027701339587880609263231648053792209", + "367135858451744056025051491593060073950844607000402056456474235270560576836", + "13355850760886700974133527239382497141869096511168824351814359808886023658462", + "7206193356029734986150290058613471641978817208643432940709861990432648635433", + "7885684183122679587266799938650213096329650494585142531776846669540995068168", + "19085115218990181267812208821832153255121894513890241319104580206329327134131", + "16305675470941528258170184941405206862153955794946952667798249341324791515500", + "15443626257895746936982356461453477742473783071787883800166168668037169561924", + "101832047855527584987088220264952346019960111874606050409415217845556024488", + "10576438072746903138917852030571732352003417543540340689583487864994727144138", + ], + vec![ + "5996861730264922256270512962050361669936822432104376503237345294243995854540", + "14877973900502178557219336745361213333854789301797456671201806787562463919326", + "14807962843542542498914061591358875654692300327506360640837865566998761281322", + "3133673265931719924452668737189159279894652423873815799856403146721022028744", + "2314426743898183021393131908284299082806555710249089245305873178073379019830", + "15353836455896084897563929713128028858175788390437902641700134508986437653318", + "5529981971838869469294842442312128910934708838384001405870007618574232226406", + "21863108219378799978996648633069571801923287451100447450849597846874069699478", + "6773528450923012634292634195479655092490402578779439394568805920957004744133", + "12150245180431051120309675366247495517352377611113958096501103925281912163211", + "11142442323884902255425165263749428309435092933107089893872191462936441527962", + "7030165611221942623542847326918501014233687676615371955108018311000952911338", + "14168907664945894221515023422776939138433274836712427741726190314020662482321", + "17973846874050037633502661848899122581090847479984048542694367819419188584711", + "6612448947387099268202244798863603173886774177350810153571814261011002084641", + ], + vec![ + "20765273984039816245168454930434370234220284726385931011063596091927027011108", + "12525833775624943075128880966259784896817535866921245846013552547690890352574", + "18082141488353658073882233625595393585704703572017716887747923068948432979709", + "12080205172928213829055364897249628749790838461826174687455161036760925324146", + "10426041417079669712788047630796875947997831039494087898318197116078426849054", + "18333367395418670733687742418586004501344157225223371831515062599898496336393", + "10666844346085567030848134043176991777319226647942683934134661343455999941894", + "20287202184725945519955164847740850432153598475156573942705745729215691030796", + "17942851314410450183054332374663618442963349517519641485232231950262698445043", + "17672277011389568686180232934337352157780343745417630591806280730146798908966", + "17416106918062278234521335281965623696779795380548750331807903068461573518054", + "15034390385078628923681181367678507353936042776187855843799428131823528700439", + "15016371809918204032764565101078018512566551812562861502095795302834732872947", + "8176229788878503959342848267153225222150151339952249831413185552792554528595", + "14202549166569309182319866775579092322766621157492056208423752359103429675445", + ], + vec![ + "6628758202046565882882491271332141326521031973243028104017889062740759748530", + "20267845326067450413789379016153439637066264448919236391605812427944953078755", + "2438946774028723892708023594952994993532105735189593503088246493623252811398", + "5327774123437518227973303235331602588839413198088244869937007412210139714640", + "16416517260868931624699960600760845305546648577328049221217547593071007584547", + "21691457642736313179352706050711464825492028639914839210493298427277168769684", + "17369736170805089474636304643282290719726533149056710536120639858255935606857", + "19460761623902421883797374762298555710671254062576987287084819867262496770119", + "19770570144034267396127078712986043464609355920552337517533085194608634179666", + "20904722049832148410244764905538463264520496768863784169293376826852051688706", + "6086931305514615639236334006857789100145606465861722355610937306339550862", + "5885641636117295888159072068000551173102681944020015073964039109891861226751", + "2197549059218467728366947205357858398035068309306368786367182781869500529089", + "18571065033075196607590252486530861399537536653506142751073877421696969841444", + "8754088881400442345643534933850221698544089985357770542959718766123813634810", + ], + vec![ + "13673463077059539437815915980077307152850526782227553623693374041649724049605", + "827897346385894242944663854685785871137033256170575635435612086616249561082", + "3256718342276213157296321691616951565542440296040693556287210483669841487973", + "18851058760089844863118102177423730353882359970674430675542303019981882750705", + "21009037983427297652279654800570889577926790955118786173230430780907667982896", + "19370673591737489444054265538393258291592098201577177240415593550679982651270", + "9712794467513451079466753095103777002587307366322848872714822737566534970868", + "2452976395290300719873209484043914405675637974162415011707015440506646332236", + "13967023770779438454858978860214792451127045212657472381516830319420403024355", + "18974770134907327058718913691556562240688992993972407935785848010954975834526", + "18005343276101020422248769804338953747590444642920980587346957205121649916277", + "5364199751574723768938730543610903684616886041855996090009230701935892264768", + "16915141432748433783158989990154900013143930156431056052460984382677436665679", + "9810457740455050658326943855759399108575402539560713791636000764640385927272", + "15711844859260073612371012979328688796642677336582424352155989490394966892994", + ], + vec![ + "2523486975208775388230636032695576725855997180931786065064150527465407276212", + "12939257203114853364537111886847673104871159136302860798643783368852456402126", + "18563343508729873190283517746040347988882591986176993103658794343898711153424", + "9139767925154848725661711816797304370425590863714334419686930754659416933343", + "15630326979358561783620795846763021145439701823262337294600523076394775855291", + "16709031855693747432049197217266634836607267701623669179857743766059308291076", + "11081746589259753124653594380015131650915622160245647873370327872758282529429", + "10263829532434991046602117509549967441368717347217075372460446855507340910410", + "923380097607272775621985864770232207712801803675470117823528453022294342573", + "7060362012752050086965449046391069479205357779670943566418766734533864254186", + "4238871118210220589598309748597547342336859622832314796736348778105851398663", + "17520061366255155846404852213753339526277619564174678951991892080505590972066", + "10513625869281904114245087023227471195681135249236672625281292889978751612829", + "8435396899666453466602702234562279601174578748121165208762270334814881381944", + "8088078454252433245088686773582075061475116946251984942060002979516553775360", + ], + vec![ + "2719987334353537600366656327639544587227927648913835976421439609622334518069", + "1024019320641379568207362641296952564452568447918936948059464814762366331275", + "13889412498086825909291896540147715217051215696137185869382874120375855480535", + "16901630530096437974516169843541514517956751790577876352535912471650304576719", + "10977539500432168331426791033212854950231005312634634920276038346698892818211", + "3226460659346175003896135924099734866664778523605375090881255390738403665617", + "9695421696482260394078309163365413177132015345359377667904366174083251929056", + "11269053203885423427900382169641426379373759430258387104391167619152696936070", + "5178750298399029508026924620615685679610392082191932524033828050686124044833", + "12845878982355860044505488053971997443594073436267126888909625977253125523142", + "7530981388399357124357431126610695946065175975095472306777796169245607001944", + "14058213441446348117716607452759269076404820355922112411973297063005877380478", + "825603012201903073682942337154197674883919500964784037574585377691889312422", + "10236993586198323123785803013039327931978354285685191819502917433935835639701", + "9438970111160688934509448828104684236155844660381402912193342504181825365420", + ], + vec![ + "11536612122721678319037657954738688943272460041908847457419482683492719528721", + "4048128893355211133472225346691072554088570917135573376272009214966234274059", + "17007960555125781716346334106074020327440615260084049810189285619997415816473", + "2935744638594795881476272405224480626548498961519135317809162650064622710267", + "6384493312721061401062408865799313573644091862395937725107886310975229942194", + "10507848115923740198082149097677194763453026968238422206438345199258995348681", + "18755782391252715265425541321566381935042481942506333926799033963914433188574", + "1622030934879728521636669415221999170954870898240640125007128754133416241951", + "19178897048453000979590659690957596324038669245300140765620928017217201486492", + "7668471074291870526245483897884601792626426080083647233981123797699500787980", + "17022938204221917796509718984925895198444138607270396412440297468084153383727", + "10938747411001421463106680010228586254730710894241856448408311145676137003709", + "1892143994611253681927160695719312882099525827316460372080933907151205825399", + "2626413664304179483436880400231214693597358131317388676910101259191110264005", + "10976814250018194880310517382191223839713741375476951941358522267556104616194", + ], + vec![ + "3267603976137604608815546917515683877598008503122930381370588099122094818035", + "18223585230504941070194267966378685287221743128395324427323638965512681791787", + "4055897021092860484143383650117982675609656498724344612791022670810747280835", + "18652001434191198724037217430343155151673545332667032591923572773249520166995", + "1179210983342192637294098069949454912191992256395734070923896011947222260627", + "4403412539347069757548448548289536146089860634393235869990028179479631393017", + "18208249577016536190404023195559477353692681610041814639755282640930299265764", + "5253459060178003600605009461295776576191151024266914967218417406063967602725", + "2110599375707753504956307604156004992055199034205281989577749327575131764193", + "16838175205667561737978735977781331049290662487428014461885404122880770700959", + "6473428079010461623647807107937762759737150259352991014918746820779470984847", + "8337370031139132243770630686523670334344396638842630818638144451253681713442", + "5852133535345551978469538570221409138541345120679970583582105205614182914641", + "7693908046708935218171096369565374697059710647347392990894755587360287791527", + "6754690467826351700887172852005234976131445583530162984365436173814346372178", + ], + vec![ + "4362899351088205531982963806583486557201252717995038448293398829823910923472", + "11518397041006514564038599401506526387562942749501723393674197214904315107893", + "20606341697536003623317613291213380804130123512962185582210369767659416485838", + "16897394754877405156789353426985842311670174197348619627467370676352261158652", + "11049995264887964858828368499123384474282091734658191426291499678845498016770", + "18903841016151023909305743424460730902070062204415137089939274033985227379247", + "14501632343069777665672565757138143573066425682965558756989443143462299059377", + "11936194426294671569251421865691095594275214629276606276242590758676139955663", + "8684782852463301178275527204056308121145836348455196441596832143888384190591", + "13840275015334112173632265864573045139112521216777064496416258170300441524371", + "8976112149735004651499648151657522459186187854485087924493254571044062238478", + "2557541446593153253492913007582627823644717754910327615163106890559828872362", + "17214289010670114093415697072867115184169717632618753776383803280726887828982", + "9277044732799923560347274951803854995664839245943597020159605756847120319168", + "8665104485244718969383349524127237156930430459852710098382428996861193438718", + ], + vec![ + "2966017993337369327831105148290320997881600321998609267125699685969931023637", + "20703140915572601301330743722461592884427012015286406537986295678495182028439", + "12415056396133226456247587270673507158810318408454307171336801889305959276558", + "14096884501745579659192341381525893498221351476730589189812251926483574089240", + "11380799045102603249740262962085086862746282867943901718244205293076495402152", + "11397463999860523006350413477163951990037313029979805067679629038011006323456", + "3989254560279764593104713297200294887781503399600736805260233256187721580128", + "12670526207690537332382598355517632246478654103765566238996179271593311461311", + "3183711571356392622250181639411196710255078687860623143026450897732409025180", + "5610846600257417510307213084599977483933519996901968341741797171439650039141", + "3280606490416179974005759319341626407586508917823138878220428529454629673364", + "1201982324417186063031536229293952219287079604094432487446300310977814955299", + "5320694228353869326806779260357179675523748849691821041819016623807887766991", + "1117900147109997141002482710095589298468009897854242933473562031850755107739", + "3423874914270570048663861326353594445564054387733265522218157141041404209456", + ], + vec![ + "15544724812507000325032356684034497915485954044805225704411638706645864153677", + "5773122431233952373926318394008190376724448498619094117536291976195311877322", + "6101823265492636176451963193766486622777300610881276372298697176417958756135", + "15795396300010870823125638802470599845845464744307444307899702370966825169519", + "1323789030194931509684647858838729962688902410898850967128356366290242773839", + "5751046064881673173633677922158261597917572500845688735304279201507132509829", + "1621252171583823353515150633750260236914561977816101468910968069661001399932", + "12193773521417435700759146251386454694422997324768376620870510312596267301181", + "6582582178277044206368630428791785430498389945338461249283089656681050213384", + "14215781677876725356925186332463972498447213260700771466349787162195918816425", + "5782842445406193701766362226063474843566378485612132749823957396025995938674", + "5452153017648783662501027666999013472879951066118424395570738985848450672673", + "54899108049022846277426184613878330780751769989719315816516065174629128493", + "8847320923102214377720246218239804718366581789144394009065472718259977212062", + "7818599458828105010909362503034203380908251186730393621503231459166558068065", + ], + ], + vec![ + vec![ + "19647061463337916460942375553072101475191437675089764130648797272059706835097", + "2987900412319695329324667493933426290750629320482434345012869808788189293747", + "14313117549814523542459271158255968194819696107203500245376504355915249564569", + "635066671179149779961724809079155342626591882143599249747638714005480456001", + "14160366375280976850992425663667859199067402849136919009370279834492741756927", + "6973916440684075662378599037972982797550158082488606172483341283171694141353", + "407790128607292443078618781455551950270304278197678311107891073846005921099", + "1875793830194257638983834574124736838833728874912304344706772047211830871895", + "101555677977911034029979807139724697918613026657646487138174278033141465909", + "13298961474358064737775518932222238976786587146906206646633234612439936576772", + "6675018665213382228528485041578965344759847379196981998842754547093440230230", + "5085649234634970209690321129917296688853246686378177913913323311616242468355", + "10058141944442728296289308385948277117189357184119821310668675797744136293133", + "20711981720256091912789603700019290285604375596717389895155646132584571552203", + "20115432152302860531854002084546199214679745925822431241410388037137709465378", + "19426738311039094155622173280735935805207149231732138766959497422037163547769", + ], + vec![ + "7740589787985988848427674257205602851899971532434369842038308874897481875095", + "11072265639503386933704945672016505140436978537584329931993329650203494086219", + "6167282302581750408390138662907316184354012779517813053982109604767767995057", + "14593714320140781629003483490890381863557111469157054599498274206519671343499", + "17959188687624917851017921366866983692604241271917787434145985166811823698158", + "10852786592684215415216400376119268936907433212885674472022333115957039052793", + "15899441678259173360040901233792251513972059637300348276334545233380063193689", + "3640175378514868793712597306483649195648235320181954901691448087453970656158", + "19498930515578230344335483600141550927765501643188753803487668144320311818295", + "4153883544158745158953668931089517690854504894896391299015592025101035411270", + "19024468701496237603291237797335586206588375930028220273546773163298357041151", + "7469727364011292433851252680653746774195189525727608179319902706399363717756", + "2372143841469285674441303263292066347817168610069150223765733476276718069613", + "2516526351266496289030890575774410993157441063594813081137075222758309555822", + "20958751338961200084885567700868871946051162714262967700193597995642229058459", + "9198209373895042225521605474867845062450002141670817279014351290187429107128", + ], + vec![ + "543785608759854122795367682791595958842618445464321379849398930724000250504", + "10214529630060513503750965897811894289300014475522844219670830726679857175601", + "11576753654045835303746511804171201194442330501175712221979130082457712862265", + "6214928611453392028562534794962748192402530967301618657847917468183855957477", + "6248903930557664471829331572570457764958370320737816568669654972084840708363", + "3521559114442643806761280511561190556015853803605505266866910604261521098953", + "14207749404758918058098136067805881181486166837455095244160881284733449919110", + "9959485107346230833915817969343930335833003289106263613217998567268111531500", + "17002458248120505483758089120825692383088865286608827557586088545674133219848", + "9310286746554253001882911152696415122865977191166769045081952245779941262056", + "833245639626789987010046903814146615257437312131003591772116076699143834195", + "8257332153195419962290907487481324519003765405123021230564312430389478396079", + "15127724347963527967475442670935452967842333763417615675896327776913208692165", + "15791631600664089304301903868070551535052107017766205491164731100213785544191", + "3248589614829341629004884091016822219853816257771914825780122055933452087513", + "17215199223989028745431952733663229031216291778213241728328297124270973463797", + ], + vec![ + "3857684745108028860654397149812523817069881299315264066597992653650257401551", + "4707785116452305555993924679316564589154347100943642537399862884483438576343", + "19430682328356065477111453488344441289467658065205729792227680437122893422861", + "8005988640968242998051528980068908390083328633663970547195021707967989536508", + "1972474227742829959658839187518313253567182690341134307491795498427960575880", + "6504813065413498635983080741406156525863657160083764580567056987831449046042", + "5823311218891803691266204716746992257279538141703406410574718561307174926795", + "9892303067707797586148875186586047934481214044907972144908705198351662761557", + "20467423831764780786043971286447965746242601887189594828393353559483921550575", + "5337137105639218811346004301122986797373254603744281473362301032791465429184", + "2653918865001450389595199059314513619487087198676481143857196098234024054997", + "20026090683375374670866007502511215153733777854247692013299401340222837331064", + "16088029123818655662676092939046004587731443682967462740467056646463545748825", + "9880178757459464201483861677712096813007025248923714154921858424834034903165", + "1227858189983101698453184059397045112686910656353893224019532173573557918655", + "6965709790321124552058584230424761849742693958580766537537673695015364525547", + ], + vec![ + "5275724511243540616354496187333612866929959836267482390875038898914899476257", + "697708336385781014957549769788950342363636191998726381071876409126144042559", + "7274584324261857876506709208086520820725839679509101845928052585127373751594", + "1101072498472320542658663987709974387416478403320298285132888772486638626384", + "17063249509595154712877503960715103016753273139274556931196815282616091591377", + "20468232842910222775240425801279694589286852891430236774476461428028768660386", + "10839957331597622631657614186340514237771754591887181416690281526344756522470", + "1833441125433983427564061829081424752522350755265858559398836992598910515884", + "18955730579934733484387457001397648556717991843841809299503396866826874046919", + "8193171082824386660318148864436464606096456472585333569988889002087311061541", + "6800731829409783994258949782115883803874917294598056504156236185152975271613", + "4551221506539437319374784319536342657448457365716669137274070321896962382201", + "3888700085587860510427705376785182344099574784427861867496328978292244934753", + "8086322087822351497126170321910559010882234382816099821864406027930561491554", + "13275797274085199955841117698566970822958536692349164078040808025934114965830", + "11798506987450083560046523556681776539473600393190500985018551824337777992733", + ], + vec![ + "2379081429050928317988088394722736405728459402480510127050576787799908525809", + "2158947553437093664557813698796314628878318098916390925037304154608297340081", + "11904049624504424229914369023060185670359894203980447724969113153014864088654", + "21129595246904679929428089867320350013514202309069019924095527072919847726344", + "10893562472341509760161513998095439702562664638408764329166649578524495942254", + "14633782125268548143403043594739012390811363821154748677494041549086652426818", + "18155420130909256009162482779733306385315875131491307204196352931575522168643", + "12073522950076264054413053294532869251854443128423131910399999522064467473027", + "1433592116103756425832298952472313408701354429203600638317025112329710147915", + "2210939565463298865317782595691956567659826882335372151952428383797077275627", + "17035360868359161456401993589512915729326589319922635525934508061308509305732", + "21403800287219776827894322644981677663016408317172756418765747341745060868637", + "13463317002652268594305080031749651114168039804631789430404782211764311412845", + "19738499492349409431828527491123847227085394983018723982858408988105307624104", + "2012548380220619299131832783872761872147153098580334235039922730491934764706", + "3325274441705326523449614352431988173829782789776117744919906973769657338996", + ], + vec![ + "5261611144921901341966147913919865209616390993972727644394713260572315512744", + "18987697050242894331980397947115962487019662790026980590641254086717180862945", + "8658141027857622941054124779019043605220504649377920644749538450450805414621", + "11298428708044619749095290390778425959792777464903586113463716315584533582828", + "6730200291399992595132121834599191803078178940321882359439272645986988925939", + "16058286461189478903573915480209402516073069688039571574175048313793344696582", + "9740895146643188739739241045620497326490653096157416163918867637699590812365", + "17328668678982472669285290349933801381460489699965770954259262923597437466085", + "21089229510079204828717685354260991995629733636903215847138008238449607565274", + "20640971546156771190021485453412235742638585574517108137718546522103899393969", + "14758279983387100491873648446401986574422791750180622274744397880182747812100", + "1331898546985028774480334813742156878861378216830516346949642945416964272379", + "6432287430987511826080726363315893796139259314225964668680871966245781390173", + "16771287021606049252082476128446106722127174299597407353702759915141825150750", + "8558856604643032676967156921137773032066151674912302830855999926475047747086", + "3441849687388033123111488396776112259878496892302987380166582753348946609870", + ], + vec![ + "20817116194964519717309108464421257788806753886196720998666047916921548668924", + "19363239836951813038374327912605477961457473367759250309818663552575087804364", + "8719722538679135055399244869855972116946451760806505569767286592823561841553", + "18664054074328463099250618543796241821469021451703648566147509976488389212302", + "14668897608285076749626150823646322752663015099871458303607991619920343960884", + "8824985320268620533295858061606775496359110158594681923758227994736311199135", + "10765520116421824752776648993191019870707037690612646148788741126433863060128", + "20754227554163810768271776561488490692278680037121708279136293739447289576147", + "8507072847563043340105426835824153184629689984787563844408253684598778757305", + "6766982373679017786884251724806484438649942596522690604198707242527640673411", + "3038766798814116247860373387571799940341461487105503437312437210868806237693", + "16132175023628563044043762398003871532172614031006064729051923614189729264142", + "15583173149116838843387513514855791665649616393679968646432984027900294981739", + "19200443718712964237956082975258333421930476944060656325774330146577168149713", + "14490821043935432280588585568226041328772039440696419883978899443298638245193", + "1261830229525183456874822855513761625054204680497477037321364189175040481068", + ], + vec![ + "6528746667003363057717101918351735481714469206031070610241614606650021871543", + "16147698956945808666133328464174436996026072559773234518262594815923002983587", + "9020387669972688980419006674825908656426016085797207362353154226605692909040", + "6727316761823910734900206867002954254557029243225097482815337322560175181198", + "10066421681146255853671223544720366622786875122426340101570461526567311479729", + "18114193263469715956238812322551819970497722041025850638963351240642707536449", + "13327552382593937204593701292574430198134175441510741573417228229955049364251", + "2372604211171385703747757710474646305749482500024237878826421281702483230858", + "13257727745849193909326785093877285673934675536283265665870765530981203548766", + "6028193081122651452411463574343231811776375151328081689399842891316362242212", + "8508301356193721985012355411615100178521599009635936162890863637274261948848", + "19464559199695905284994131173285166577427724356611906328878634139911049316349", + "11574946347736941315258330071986157639717219704847732435648573723449097294965", + "14316018291870434740761571976364226850140038868497601980741769481529398163257", + "10918196690875147279977362872452345319770767457845834002916792583407449275430", + "8977373069224380198540140180493576791843577554452269707469880849832228035023", + ], + vec![ + "748498829648879147053737200607377785638767247375633990031472844537260809404", + "7873158704115081877804196477528352958470140833786962209738121862287852609943", + "8630532424574483719830065132415752445222218233997041715460638881404278125797", + "8690582614704926771670051368117061261335922283383440650770249469863376973533", + "11095347717221488007795836937657301037546485308926406743891578760518489637433", + "14135401679286508502504277387212121656373093920904597158275723422439876100612", + "14738090907871182095556666808390406734966899260337679732930591106508814238308", + "69949271807030541733792162811562320986072778465870031251424993196153906266", + "1261108319753649612663311207745706802298135850234573661502446278242936235395", + "16817683438765699400477322528948826720336276287491287100775393652707943792575", + "18841362612982270174762542916999427955157117780377439797570032391179795654286", + "21870116979686159000730008975387147484906370787640570497473602061164852395071", + "2765949947644452455039725847864010340741814376903283748968022076584286340602", + "17243705140322781483942034937718263695017457618400778609034996357553437986248", + "13677914966377093417490296499705767815775553283213762175449591670735007344873", + "13297897273167025228957171745153893110275891317806768533464851402665750442708", + ], + vec![ + "13343269561671098171091946421541340634645677702710756455899883309946421878045", + "14317554923995329326292532110843156058636017277221221405605647959782965284991", + "2449835610256525707119222686954432076774548565002604197859382557987062142872", + "17311927259294224200654531686487034697399582221230204382526629700762752029323", + "15886029754147081563564215095016637219622964863827251334461319377673888336370", + "12975391569205596000382467418571211360327385366404855968892273321920864753986", + "5192224731376769981697271181929966876988577937843948018413420047649317448463", + "10676192139479409715075805869252336543157972214291179434959380291895052573000", + "11177450837775344504988539319102121281143354970746599512770721409890402968920", + "4593200667847399069176143966880767249193687931869738276411303724780636851859", + "11038090380551563944847929106606106685586830480239388947878234434263502089528", + "3953526418885419728011595573117200571065709475826662733812952860173033412620", + "11423581837569206292763368836201420979900393158634684009052097987935130296343", + "20821758092880168608657749212670937227806187953778513378055795779476865339010", + "18497750301637542715216545677959957759969933594321504330433834545748130561538", + "12908315310864070359072899712184126229744818024807969170422172983759986468742", + ], + vec![ + "18124554128224712379655197019948407579501104121202515283344405665022477997811", + "20982975342803604005070898815103511622812678185245827078739170834137855132820", + "269825514811016046965635325890713556615518696022373524499024558861784638050", + "3147172016143608266119085281262979524079358702373693860744797997889998689295", + "14386832245166477008833710911810567249931220515383598373556096298357174022469", + "4556487278328022691163443795787718624849832853076824895328263286768388362379", + "12261472135716169178595791281788338424856082203277018628926152780653238868197", + "3899423277681311798156637809536718065846612626667684730473026778811334914007", + "19506309861341587023369919042973949592579256277585657370274971571040135953685", + "1364959409282923580897524375843789492029158451437094417717346158650761726050", + "16825446178437335546349323854223244861262257417842514939476542139147191650927", + "8507209116997169365742612629060440573797814488088100151461758543065101868641", + "15267004752470933248572062004321128218304784520473623806984809921883550707694", + "676031704648473427598859615894926588607941948575683685792835248653139785855", + "5619669402121492986528563034254744932241765329516105050276049374453441613893", + "14704798323824402102639327448519792804756861685698966598005087155126928897024", + ], + vec![ + "16320067378138810368504584396122999292945236808095532790918287639367557973453", + "8146733655224190459272793912328710535983522769849572460217349885461291275505", + "1038180418056776651442944028459510265058633383281360520702142043667403503844", + "19104250152149288692194160087229108962380481983770051876357439473931889382526", + "7003760916474780870091321276888809099928758016210575511830123521067523691017", + "10460150809039904156668983747345347198316841366879181914914418086579576664491", + "15677112907432790716289265075133862681087874169637399306212310599581157175963", + "15326287388823547786897864243344800490244989953594543352512215080754525987008", + "3009920542142872962638960374061083879344081833005888204328736035225746795718", + "1804978488347291728619316877980070589260679228487014904460247370976082660690", + "1328483773995482788116589592947585572244503408960547493086897090179230375909", + "5730439196427856076422854580234519707227885379777920110477133774925338997125", + "12981431367443547352573507131765244291012615436617972351790438163822109185806", + "4832711978673748239567077367987729540684018769513731999388791063624971084279", + "19167638139894327951096186708600927728591679782746822664161578344690189946483", + "9333793061773227893961520586484148770892826436173136355616167263506645189532", + ], + vec![ + "21458443518750111068075382716496819469049134888053123021475459386077573760694", + "10205061553685164402371459751106832224694007401400200656551443744478399832956", + "9830442925198991171494436686328858756494894392913894165312258843542937207416", + "13609869649628867442619044498926584416410429910199812031508542862339177409898", + "842857359216662427573900948838829890161571314532391785590044951074431433210", + "6704851129269714864143856350805682503777715960622547687053167421313207852468", + "20114446898395957281817578351485444375476540075666338352767091837280210668931", + "6130491715603374999851365684496448519606340852139128448851532580625582546602", + "15813600594451539718733724931622603275717510629305297903420212550967482486778", + "21327142130781371825633810115678136219928778056926678460292750153897861437357", + "4961568602907543961625596532526706517274095072299784820035412496941108876522", + "9960714813540172203971946479714057278358565379915043327324100653488017320531", + "19766028424299726292403979387148081559608033800073407130824876437622345769610", + "19128679427621049663909949398415698465159247423858348746959133844645715231748", + "5570166864868188450021144960131468276106515498742461735810689530781856406802", + "11574972995586621052272541749980259251569951388173301707052886832340902170154", + ], + vec![ + "20877646438494519923058752260065237612204466401313282232221152388173388627982", + "5249150519585813956946898091205522450918428396100844955321690157312140444303", + "2017741632554727420098342601911590665808744692822556685407780092354922864904", + "1195760854074467363227832424961613965990883686618742557387108941759791735821", + "11466582138640916980683611003811079018804741425452823176665968956853901549307", + "10985903304141344987201754580174851046824447026961915755527591886735857840658", + "2130569969210610976943124127703960718576010294778156297713757734434872381369", + "10738808247531379378397673739325665568136689079862172683794674460448121540040", + "20614646033198180892625991863201166456931543657809805208583071176938057085966", + "565997125213498936861304726982380864240733960104507001725677609359585569840", + "10632097546602816816944445466416073366486654512740953706603375905768461201631", + "12929362833112356946255271584627031609627907912912902502403907291451582319157", + "1034235212357952436868793031480652544314617407845212103021627643626485031876", + "19390762319422155950976700977771604452581304443563816543281343170335005291057", + "10630153844633439282958810722979033212891017579520387012386923832074337305798", + "14190876500956732147461925775340768352695586121304109940717530850819588911999", + ], + vec![ + "18722506055380266423054060346625995391170752586033471909467419883841861306716", + "3056819986793075694786756176651004538794778835114033048299678024064951583754", + "4672570536584218848208255703572454924953635491594309524306431682544106754221", + "17351556719883029551473146382008643929874047263147825865359162665615894766393", + "15361589507494181649833267508353254535596250253216356073840778948791727807159", + "13693471199005207103868448237123737586403755421081623632730820742927025187060", + "17755277847125531485682000612777686738631414648862078678723432159826928724703", + "5078737090654746516628738054730387217943533822956354885634428155918832329055", + "12147601749747781924069337935019031145159705806951967042421913575214356549816", + "14365231440612787726412058658929032228572314258997026523542183583106877612565", + "7278303658563994843919131396912585917500535615791945995381401546353032136647", + "16203755920169126984249498560164803107868240707492521482933949021054510520315", + "6791925607504018751125155518211487306271141824074365658905258365090537532910", + "13823494237593720607868138054959291887740146822262268248432322209124930846096", + "11009501160902109690977091445438703229756969339078969536565574715162502634351", + "14720462490975063947234490477382491041961626472580003583159938559677559185952", + ], + ], + vec![ + vec![ + "11497693837059016825308731789443585196852778517742143582474723527597064448312", + "9160854578263429171202421862962594026987177464192712717562131193605088890171", + "16140003334191084124451468943070902129052879491017160345910048022420147165440", + "13954253824852759534031493316905992731351625718124698909948022659536770029356", + "21692459647877833789326815072729212414846887919903018341690717828718320112005", + "9941936267230985844518782624440910125063679135232844826673261884947459743883", + "13764916706054812213341909699290503443927147550756936312875016380348026052252", + "19102599208524798070012402067365820884265931087114568811319734727534891174260", + "2317229314815846955024814528087757341110607641360330608288042339421595574836", + "18416483069534816725178879766589878658686265350707575814092642317380006218736", + "1008780931278741447191637805167409477999443010661365677836100477728938308997", + "2545090804346934163783014162536858416885322260022963915511642447380970940906", + "20063061892576784746234714844937263854658165123147516223299128773175198821424", + "10515454963476061878165006305843100325941655508909556343534889232612007413255", + "14666046876279128708964624720946075308028756066224010008571298456055047416803", + "1019066804447509488848959767263827580870207313924599851872882869076383737080", + "12069305948801710705684828347680772060569105557945593846885134778987150642368", + ], + vec![ + "8621808318996908879998215444507382011199442894883948814246574848625262495021", + "2880093917191730817222149621749122107421670637456732204857760380124742164752", + "6542220672426887509675431131054437027921301676718161645952700116082427886835", + "5244163576308284656156828799646671881031097312780189786429450879353224489935", + "6473177356818363685990488940547034234726012773074061935352613261396549623686", + "21820960109882302233412313690073759484098924860992104203363318963429862834269", + "776485012941102583811996326817291151707768276005464663608504425462324969189", + "16008040671461692654565857317129301282959438973184407738728929267807682783872", + "11432621469217532329151110587073289606303873577842245393046608426744660231625", + "16529428848681190297024382986484471912959470051547049854507168832330777582917", + "21022141832384945339899318910964518260971220396886777286264903115370862218195", + "14654032557013894559632377088090851151746560959506805696507500157500224672350", + "21354688910115519251326230418165921377662159943573060390104844506964336391116", + "7752619774088278019865202458496784783724532278543560344508935599686049023521", + "9970654674664205783578937362441466301698784253920856829108173973533602978434", + "8766781005151378907464491478911534398597711022218593484510275842885145890118", + "427418044823104424459481289119034112585817787122063948281766019090147258009", + ], + vec![ + "20178400825368362428846659280983736786989246199446877730902913555280765010239", + "20090950791842994139452610863206653079874108855048809192426038290381481405709", + "20312256534186716461922298801781816516042265527822903054862895650988285562148", + "4909922693172591670749898596465752034935631291352031623980892388245203503556", + "14711873682305388680005649678171045910409841883308457847530167875811246495050", + "15958770417311022337941451962731041577578914593218560089308342731268531712920", + "14344571866504726216497643454572836820480317972842515429281824926793932325980", + "1769397319845168475613819781273574132774109508454697099778007579313922258588", + "258587389395146525547707547116022862705614858555176588203787762094788370646", + "7022463667655232121918607020344762877802128330181623732816227821885112788637", + "19845060031908398041047500648731073426448577406797522741285587091948148114644", + "8929070527835967284189505459276001504282186650781989399671939877306459603924", + "3547034362934554702033015610070536051926221082690581599667998618549114761320", + "4305372853651357563855521637099663044320680997112932657745973067738472275743", + "12214176886158725776791687535468106829515395407992835497927312162638766770078", + "11706938131011539306398383828922578377803816015869195385220014980091795489607", + "11651169608106469169652973769680487604343355235752770879131657116772958352610", + ], + vec![ + "13534129170664288430416028489502009030456779206590118389136628926201783734674", + "17992046595622622161177230469344714863802535922717313714552838880957499215981", + "10357743233228120740298650287569640748031608153491097653220067758242532130547", + "14517928487614900276301089165901434951535050275402697067248151083596639118376", + "8209182069608252268840455827520737704804441580266130568152978886513947166844", + "7640073974915695432060688995568585354936052110685953897202450273254409304465", + "6665784939677502535233010310165049782941185917893655988878440622136707103599", + "5556533518127592657586282330044128807869230494205809390140992474026365014562", + "408852126690043032125614061393097734033642541042497038832349288207856020928", + "12457107183372565990532088400464843754189254530767241274690121422398153743230", + "13376606774696045471509234199808902800235186345177397461276986609303176540711", + "5984750790245793264714000637029537436625478804219132653560918517073545131213", + "17088785934598552415567637681327922806463774512289686863420148678865116179843", + "20571346584413672249720180719864302959756306192295084730012069713189235458689", + "10563908856437897624492808163035753573292724651291788140893440219493498317425", + "8196914932493081300314276778567572383087801740102758457948867897900124032622", + "15351358433647454670571026954149706648025357095082471550411625465102412004635", + ], + vec![ + "4691549575923073479244290910433477874429633596189924034857922132851997509067", + "3824402350445187488499181297883462500055483166807595223590583390670577007868", + "3079709149206454455742256725666572935951085219797765997598190006202700553625", + "2933435916857570611285367519434342227981452027299670163614250683347221879812", + "11563256620627039928684054332499213986056102448749960304814905960272568400553", + "823982800920919472728462051408065816783743200056445901787723684868401502814", + "1426387027752743868839120378047531362360888408160839303820390910661721602405", + "5731997056751920185589748689260608921390513807500493745181552863591316000292", + "7720993839832885597994588400736035173902383878486875722645777844101744411689", + "9002464001048295091481045293483875664362475441902824699977741340020558338559", + "14092406920820325227424145554147519809410886190773484884502907926085878880530", + "17941121573263204671416572834368313711647830547916231506552438869447449377380", + "14129082317750218835304685520412323779932243077309313102580200435673568399019", + "12243264707350346605364305720479795822428336866265087370053925276897423448873", + "7359586883838006703688461248312304702229090925413466255532354254039350309505", + "3965462104895971860844762333128974051506845133345812196538391893357098668635", + "20413404046776512481224629835753522701244606906272947012890416028042446470875", + ], + vec![ + "14776939713848231582251698583594764736648030077584017202377553055729297756934", + "11733430377692682072369036122380634083002891720790435745639760545087660184574", + "11292503903510690808481905838273174776860675950299588217800551517281190922743", + "11420684871520848382159461165975179232780830769432469479502922603701278643559", + "508745459215920279034800716428719752357838311731203417677851933253804443780", + "13406614833603475717176189537057020401687589054028958458013676908546295198427", + "139498778461763532268768837474164379463641791532876816780917343085192436149", + "21267978943610728192699568453293742013765519272282009809779840168468272374983", + "13427075011225899058346282386775414644140701782157119231274761492276104178003", + "19408556542747941554117161987423225644412569826994655839704848575188694207985", + "4344748623903456736892311801593408666061687792073497811421388122550378864729", + "5249164170024197498338412206444867159273343488553715674803882832943638673489", + "2429321160057297680775462145791389342017255648014188548442207641044604477112", + "17633034583356484958669009250049760097861376611937917426871239574846566954163", + "8589358080626653532622087734866363152181033731195407703922685134228472217961", + "169706674245923892154983046053808641575955705892301306744269839968148939837", + "6930563539197568226045059034503291611681517204525149696500944995384070682620", + ], + vec![ + "7715180204730071272045176332163669182843407084675929323110162515552729145466", + "18820951479360410886797033460215573843065563410033088307349452904607006418510", + "13004571734426490172877737218587476803131320809388224624418159739666018276230", + "4221983194911935567520445796137770165826124860682996668925096525627918503987", + "4179458328609322852016864400883248432169808675570811233121213062838243996150", + "1974895668668582620251979849937803690939422905200315076249511583941627363923", + "6163089641798041487723906004413611225955533949531028903999813030048150618255", + "20798314848405704563578428763355242237484138146926032039199072579614133708498", + "7390763848541838674951447729994978703104555741288179669331719960925737939679", + "17595709281213749734228944927123024058411855966868873405155429088809356857614", + "2441295378821425129112278178696474169691086102217838124236465522719015824771", + "17849999656709233176331949642982487670074039937621948427273232063967495006615", + "5474039350730649299741439140946492457291098791158740216537321189511814004320", + "18047496680368319496003204418900793303422432417949101604087210129655489944730", + "13429638037186326961998474643589136448656547552567706898682870898125573018456", + "4946359485751570678621380009145072972898871622865400112232372288432448523287", + "3957822358540559545601980592836183988567881320017369675759228588453032530432", + ], + vec![ + "2400564715392273231728553914090327718988368277691346375047322875638060719294", + "11894303179337833272080120828007835602120013500741388580434416759507002392538", + "855198138664096487124576201178969627556714572486300747020353650684651888948", + "16178438790960495615141009256088177242852822581784859562011828222911238142141", + "10137977256085689928230181030370096331013301569857962720687404778057694535741", + "20515298296047282701750471139118794292164841708209011112551973440387123890479", + "7578927989884657210580284649728673292510875058413984297956940891614841867974", + "15739538064110791981043195085211350004676897766248503311173839899207608217532", + "8509314140684268376822128791119356380924264099968476717867240237437611156406", + "17940958334079989608306082569922896755033794405963583994940471979845228834401", + "6805144082112931458671099527607595264417843202533041916539891329055761729898", + "17799985834198911964923035884156388240555414131807336706586014102549806733421", + "1321093977038377443723007121319910985126631474786194145179087170534580192225", + "7212420993868874204016591911998823569295893131341249414736874789036080080431", + "16508732240056273863067547854850721821696261984346034975271352570374366672941", + "19365952558655609049762470933563228578883041309045062714419724629518231309330", + "7130642306151081144305696424018041594798737863203511582301667374235931034428", + ], + vec![ + "15515575256858646879456258778282520667217724217234033369846512585375869363745", + "9059306962111196078284859952597377729932287486280210246946890053176853282710", + "4280792737479805409403664514498090636723785279466801897971750554236293020515", + "597858655089478477234738420519139785711327216887739704481840585427214898574", + "12083197860096972935386715826619665719725493525449003092099143765304129114681", + "12386885105651539176174534724546204378869340659228252756723599802198672140462", + "16104345036536728728283892631581380860380214418375736872156768647788333934514", + "1354879242449295308208627398119082438254972230604954870747255418847391619159", + "4337243085646703769896498772355566596731636687256987520433742534413482763248", + "21848361732679483806572599977716282165817086425830260890883893707113846583427", + "19219603360817893268138318807528436572901663409914252265449565639651815377666", + "8493170221045553556330983879086435859112709446579471993956965243640551891229", + "14527043796091130553415865581526659472541220106744153300268446542314624889828", + "7937111786465239058321924657743649234315319708955946415168847089269341907969", + "6983241618969891267833664988834562719925952220932975709556427480787427509845", + "18465592203176453842524453315376851907098534348906659566313290009668304127477", + "12577134888425026368283611608059363450080978991251364658919457233283032077618", + ], + vec![ + "6518118228984271075704215109336988269949556606855830580887066004852197959585", + "10874313757780612756454383662805810655295466490423990025119534270332368723291", + "20050459982999023948350579488832955163413000705527333267976511300912322921717", + "21437528840108163281775491180120464654045652656938712316426352947376535924261", + "15993399358508475124653838111269121783855992741631266590611036066175654211556", + "10666318855988978766724392469406816805194254878125837815249460550109965279109", + "3728246313656508848829733684629154197051824118611903835393981369401120614690", + "15672779156766891637063280303644284349935441934769848438404666835306555334745", + "20385059098581842810391414884461531594818480948222590493458925664185735049630", + "17350905999329080753256083745369335770368978719306867594625495391694963226620", + "6853271030225637900654747228083647747830481256853322607732231926920221636638", + "16945013335725553221697952514726788415224349981949301561114619373330552300437", + "13958698773337419850196158271441769489630950623502500296722238460406723080346", + "10720560039907520317176701532328409797933913846226344831945799921818292744504", + "21881661898012375513399335499754096939362343783241122010305516740752509759219", + "8000150875389840177411621828177856485533123076967778453164076865957788973133", + "12006864256015504014085835403867962462497397391751885107034795661085483190829", + ], + vec![ + "11134943591559521791276826061536197135026619606969540608969613264694780202214", + "10460719249321273180939167776515335033037028258913526408809327052344611741158", + "15622824325483665989612723680117847052463187830077568039968213833454442303285", + "15410746446975895232352042328675463711076427815985485297730903996168105249910", + "2891625203408623652388062275887825901052275992286085168525244670232353662516", + "13945346776936592111645435552497641082181197055210791224507452108313960355724", + "9948029837520576958543862437428843878940881432043095435233721163595331057646", + "19462884070334019417166507501239463855294968830363882698967905077860898932836", + "5520136928844106525731832126160315598282989291025512087174203871698688956372", + "2261360495098633227748653797124151186175710067614537829940284371045336769476", + "18416255543912854662465760974639117371049579168655995592136361451572699752296", + "11463536517819692876416279640167390882172379596277754722588804736317693209031", + "14766312573746115666714530391277822821010415216258152152785909105008723593246", + "15974413313983607427146441647805917978765835122483385258268287590594890695726", + "7395768998784322986017026062700445312131764953109410144512127957624588026520", + "4984362060666297962621166548113407854009922708767986933509108968930963325593", + "7856069925664789206382562869453172139307214363990979700110954142509543954720", + ], + vec![ + "13392340056392075670742743235567931160245313927053798436913893965097578743589", + "14474853581934896987119860016624045909083123926480713073931600741515836999440", + "10281094117655562522718238098582121237113987849950975777935895552307296780258", + "15651874975250045926713763993802349605521485743295256479167713866101959393837", + "18837584337473843738351569365992574544592556787094567312567026738414350732486", + "5422285873429437751528536004788420694267218180077508101272917323525729776286", + "14483434861134394018133707852914799521209887335043004813883706597879855694501", + "20781651897373919207655051244184479648667009194959049646213659882342077548670", + "13257874746816536319517386553976680827294792028033921278407244416529098958298", + "11687595443717328453023567060513955787814475781716013805208754435947875015986", + "18166734702075655056906373297658555632769934998914813942424547912757194661408", + "21844245024899402789239043296889438934444349492098488274411135961103932824086", + "11576523423366505825808301659798885009429028155669986846400153307591026628871", + "1148929907457849288972409801053032039492913919668345502756020163532587226569", + "9833563661199700341560575887921549871699630430379479441644037429312353663854", + "3530072023449326955819177530541286351102246058096342120743109127661619847487", + "9578572618820421025088920868463234434017082077820664789170518853528041438058", + ], + vec![ + "7903274455513569732248555355877155817202990193424390098928646605922794954894", + "15657165496965632442135478212817895834512397576120080820666617370995124184076", + "20874620177603797416855214546759257732117862468392283423272496519554421621175", + "15225141515575386127960844403303707527145779098407107474807381929305493685913", + "18969726739742369826201131743053235807095357084892793088520738053078283905792", + "2273322388759602026766688787958421446004694547879317377854849512027808056744", + "5432511453110863684214390264939773007164867954188379597097897639266721931777", + "16573736113133010502307046980851924227669732687124639246608245168226674694938", + "14442377229880210285366376088368281053151209045775399428755468734230536622166", + "11507669956883827819118311484641373285663729813488745641363568933392081679659", + "17839532429606263422276008800408286253970022114288361307186094837217135904945", + "4014265539378515041529068347957642340321904838486974789543291691351911347680", + "13003411109936526663340169024379119873659186776343250211166084846025445488231", + "15306462549196010658398578543921194922332897913051564489815921371400376655492", + "15374920278441335059913374044144421033417626851032526544458034333565246831914", + "2323707387573936061377018731706151895551673463355017998838984100857163359959", + "8763825614293136074070194100490720958859071057754803255698169463529260604989", + ], + vec![ + "6460078619951263360498950096593765796985616066844041272594234887640757495947", + "7787749571541900537576366919010287855513576825542236245962970793208463720908", + "14155132064389382746286911268123763500605059883946776474093143918456417640061", + "5273412399684398435707041325417233001212071630727434066521150499193222197699", + "21782771343726562685905794523874393783448988772056027768682759741108535654588", + "2051311409953010480673263657665054154914165583084681916263406447692393737285", + "11875661765858469891704167021983258181748059479218144774825851054326067416765", + "21068403881106541076710977164706790160208140346094887767675202443752560686192", + "15758340092420689120259589661569467106735378390624556577895360824207644286190", + "6348044865909997285104441438862139025250301814988835887095459638724790173542", + "17932524652786058307278475190078972097828770914834668973535335630530452917847", + "3228816608788245618072625224844244257443056161113290604232896419878090903926", + "9551656383427589703749567396517141384933708835078892692797520811547619069163", + "9020946637763713728445703977273148745690000656147983844173946393849341364502", + "2943971707938849936044214925657955353752398302351355306608002652427585494465", + "13685248050363412502458409958081650237171619758299111626717716567774759779438", + "2868302441860272746035818268943730589208611466934920188905195434962368539498", + ], + vec![ + "12942754961762450702258615422302669970585456811961698112554744196254280397924", + "7774153016753223166594232439290374237307352882928845881491063153885238423849", + "6049199094489456460150873785624904803705527267070980629871787636829670320696", + "20845774618508072345571200270048422316984213457783733272821856328966613453129", + "893781271752145245996199566953390937322128434854461459523685354415462146991", + "18253521160421279022112163574719081086152283091051794425756555232523003389714", + "5830356839546054218884630677325272050228211705054095093735137780865196640272", + "465724850238100756077164119125411924005501292693566847128509233889037994810", + "16128424014911176575708362980592727342883849823936273728991741993116151256846", + "16294469866182032082852110477156803007474009685855833013024070339538767961172", + "16065302076211391028679793764673039756452069543133653373534107626161947643947", + "16884841668405833752583472630774457863811531316950980947099417366816649918715", + "6491186733352076588617073683591626846805330965399318749556699217006305107353", + "2905363039904017404089840194168764066155916074074586587258377722869451022895", + "12278525316798802696433043227895855967349302508482435733539545800309845622462", + "17314019937917464307399357201462393625411723762603710024519926241513301595993", + "17575148837153403621130441476206668397295797814863081080474688530411862819003", + ], + vec![ + "15471095648400918007606825575051718247465842587885842548618709676930106301461", + "12384318072593682092503177281279533170809948746241154089695346932340260672911", + "485123300957579906724434519828279717066415303115967428444773045074341666269", + "20783388705359037084238123327815399486599342641041862648140815340928809400500", + "782158089782171747510228803754257660300288627956397010472288943774913698434", + "3478639244366860518133129066736643188173227661380443503605881541719237123785", + "8117881660412001220282982503758841397343065193097821242261528946890929063115", + "35058721234476129650326366428341402515138917944396335645144933468879076532", + "16397938679668108498255642177027044558510343548921376375827648132906803583443", + "2433453154751221667718635947799318194191827076977739122814782387249355591958", + "10029257080729083671923349509739320383181145104464214096029001591053185820564", + "10656149073844062963681322014553075060763965763326275398946747079560564035850", + "16593959744982924702318121631183966060100893770061806448540495161947421822987", + "2068414540408163070577553011598011878961687148891918803076043034635892035594", + "7626934390068570344424013149524663454707020514194937543157326823804903603467", + "4554638618925430597188146675680715151198309496396245054063619103455584547039", + "13496396626587678250552586498039471308586222626132206600246125554024514007655", + ], + vec![ + "9386575758165199373292193827891788760863572720909068403298778509513288095168", + "7915179166050735066974287284338956896350001911949145779842510041370266325973", + "8167168351770465939827858686491835211463722244546741051891842055610292036496", + "2984855641991264563125921669666111398496570671860076360618117725463255935294", + "13345515131307851631101763161069239153529744919878592460694942948582652858318", + "1683899377816547722982571372970082226593844001913781056647398610840773630210", + "18082738493041031740109434905429676829872619916356658605260447196047710914008", + "3093627219579176143734483600677215286836843343902256951787659223143740127833", + "13711569243726999863512778161983125470458400981069655361767578939766971032706", + "2942999073656444791256166118886574877976523928599259988730412230442182398832", + "13741805436257583212406513632778404280140012772934699700695904335718144229392", + "2966496260001027437433299921197242880503083867421600405476235949236365134313", + "12214449406116917832159291589682410934022154504967698746834536840379875421993", + "14724028677081535225097028894192068729072210977241429998194582705094787450320", + "18532472576071611894578331306492326674151870705350144561012248059523768664658", + "12590949238667856614747230064159254583777806754508472794550424398198576585120", + "13228220894074693515947418568115512670466893414535562052872530653586084906533", + ], + ], + ]; + + (c_str, m_str) +} diff --git a/crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs b/crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs new file mode 100644 index 000000000..c67d4f817 --- /dev/null +++ b/crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs @@ -0,0 +1,272 @@ +//! Poseidon Hash implementation using ark-ff +//! +//! This module is vendored from at commit +//! 6d2487aa1308d9d3860a2b724c485d73095c1c68 with a few minor changes on top. + +#![allow(clippy::needless_range_loop)] + +use ark_bn254::Fr; +use ark_ff::fields::Field; +use ark_std::str::FromStr; +use ark_std::Zero; +use core::ops::{AddAssign, MulAssign}; + +mod constants; + +pub static POSEIDON: std::sync::LazyLock = std::sync::LazyLock::new(Poseidon::new); + +#[derive(Debug)] +struct Constants { + pub c: Vec>, + pub m: Vec>>, + pub n_rounds_f: usize, + pub n_rounds_p: Vec, +} + +fn load_constants() -> Constants { + let (c_str, m_str) = constants::constants(); + let mut c: Vec> = Vec::new(); + for i in 0..c_str.len() { + let mut cci: Vec = Vec::new(); + for j in 0..c_str[i].len() { + let b: Fr = Fr::from_str(c_str[i][j]).unwrap(); + cci.push(b); + } + c.push(cci); + } + let mut m: Vec>> = Vec::new(); + for i in 0..m_str.len() { + let mut mi: Vec> = Vec::new(); + for j in 0..m_str[i].len() { + let mut mij: Vec = Vec::new(); + for k in 0..m_str[i][j].len() { + let b: Fr = Fr::from_str(m_str[i][j][k]).unwrap(); + mij.push(b); + } + mi.push(mij); + } + m.push(mi); + } + Constants { + c, + m, + n_rounds_f: 8, + n_rounds_p: vec![ + 56, 57, 56, 60, 60, 63, 64, 63, 60, 66, 60, 65, 70, 60, 64, 68, + ], + } +} + +pub struct Poseidon { + constants: Constants, +} + +impl Poseidon { + pub fn new() -> Poseidon { + Poseidon { + constants: load_constants(), + } + } + + fn ark(&self, state: &mut [Fr], c: &[Fr], it: usize) { + for i in 0..state.len() { + state[i].add_assign(&c[it + i]); + } + } + + fn sbox(&self, n_rounds_f: usize, n_rounds_p: usize, state: &mut [Fr], i: usize) { + if i < n_rounds_f / 2 || i >= n_rounds_f / 2 + n_rounds_p { + for j in 0..state.len() { + let aux = state[j]; + state[j] = state[j].square(); + state[j] = state[j].square(); + state[j].mul_assign(&aux); + } + } else { + let aux = state[0]; + state[0] = state[0].square(); + state[0] = state[0].square(); + state[0].mul_assign(&aux); + } + } + + fn mix(&self, state: &[Fr], m: &[Vec]) -> Vec { + let mut new_state: Vec = Vec::new(); + for i in 0..state.len() { + new_state.push(Fr::zero()); + for j in 0..state.len() { + let mut mij = m[i][j]; + mij.mul_assign(&state[j]); + new_state[i].add_assign(&mij); + } + } + new_state.clone() + } + + pub fn hash(&self, inp: &[Fr]) -> Result { + let t = inp.len() + 1; + if inp.is_empty() || inp.len() > self.constants.n_rounds_p.len() { + return Err("Wrong inputs length".to_string()); + } + let n_rounds_f = self.constants.n_rounds_f; + let n_rounds_p = self.constants.n_rounds_p[t - 2]; + + let mut state = vec![Fr::zero(); t]; + state[1..].clone_from_slice(inp); + + for i in 0..(n_rounds_f + n_rounds_p) { + self.ark(&mut state, &self.constants.c[t - 2], i * t); + self.sbox(n_rounds_f, n_rounds_p, &mut state, i); + state = self.mix(&state, &self.constants.m[t - 2]); + } + + Ok(state[0]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(test)] + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + #[test] + fn test_load_constants() { + let cons = load_constants(); + assert_eq!( + cons.c[0][0].to_string(), + "4417881134626180770308697923359573201005643519861877412381846989312604493735" + ); + assert_eq!( + cons.c[cons.c.len() - 1][0].to_string(), + "21579410516734741630578831791708254656585702717204712919233299001262271512412" + ); + assert_eq!( + cons.m[0][0][0].to_string(), + "2910766817845651019878574839501801340070030115151021261302834310722729507541" + ); + assert_eq!( + cons.m[cons.m.len() - 1][0][0].to_string(), + "11497693837059016825308731789443585196852778517742143582474723527597064448312" + ); + } + + #[test] + fn test_hash() { + let b0: Fr = Fr::from_str("0").unwrap(); + let b1: Fr = Fr::from_str("1").unwrap(); + let b2: Fr = Fr::from_str("2").unwrap(); + let b3: Fr = Fr::from_str("3").unwrap(); + let b4: Fr = Fr::from_str("4").unwrap(); + let b5: Fr = Fr::from_str("5").unwrap(); + let b6: Fr = Fr::from_str("6").unwrap(); + let b7: Fr = Fr::from_str("7").unwrap(); + let b8: Fr = Fr::from_str("8").unwrap(); + let b9: Fr = Fr::from_str("9").unwrap(); + let b10: Fr = Fr::from_str("10").unwrap(); + let b11: Fr = Fr::from_str("11").unwrap(); + let b12: Fr = Fr::from_str("12").unwrap(); + let b13: Fr = Fr::from_str("13").unwrap(); + let b14: Fr = Fr::from_str("14").unwrap(); + let b15: Fr = Fr::from_str("15").unwrap(); + let b16: Fr = Fr::from_str("16").unwrap(); + + let poseidon = Poseidon::new(); + + let big_arr: Vec = vec![b1]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "18586133768512220936620570745912940619677854269274689475585506675881198879027" + ); + + let big_arr: Vec = vec![b1, b2]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "7853200120776062878684798364095072458815029376092732009249414926327459813530" + ); + + let big_arr: Vec = vec![b1, b2, b0, b0, b0]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "1018317224307729531995786483840663576608797660851238720571059489595066344487" + ); + + let big_arr: Vec = vec![b1, b2, b0, b0, b0, b0]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "15336558801450556532856248569924170992202208561737609669134139141992924267169" + ); + + let big_arr: Vec = vec![b3, b4, b0, b0, b0]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "5811595552068139067952687508729883632420015185677766880877743348592482390548" + ); + + let big_arr: Vec = vec![b3, b4, b0, b0, b0, b0]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "12263118664590987767234828103155242843640892839966517009184493198782366909018" + ); + + let big_arr: Vec = vec![b1, b2, b3, b4, b5, b6]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "20400040500897583745843009878988256314335038853985262692600694741116813247201" + ); + + let big_arr: Vec = vec![b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "8354478399926161176778659061636406690034081872658507739535256090879947077494" + ); + + let big_arr: Vec = vec![b1, b2, b3, b4, b5, b6, b7, b8, b9, b0, b0, b0, b0, b0]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "5540388656744764564518487011617040650780060800286365721923524861648744699539" + ); + + let big_arr: Vec = vec![ + b1, b2, b3, b4, b5, b6, b7, b8, b9, b0, b0, b0, b0, b0, b0, b0, + ]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "11882816200654282475720830292386643970958445617880627439994635298904836126497" + ); + + let big_arr: Vec = vec![ + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, + ]; + let h = poseidon.hash(&big_arr).unwrap(); + assert_eq!( + h.to_string(), + "9989051620750914585850546081941653841776809718687451684622678807385399211877" + ); + } + #[test] + fn test_wrong_inputs() { + let b0: Fr = Fr::from_str("0").unwrap(); + let b1: Fr = Fr::from_str("1").unwrap(); + let b2: Fr = Fr::from_str("2").unwrap(); + + let poseidon = Poseidon::new(); + + let big_arr: Vec = vec![ + b1, b2, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, b0, + ]; + poseidon.hash(&big_arr).expect_err("Wrong inputs length"); + } +} diff --git a/crates/sui-sdk-crypto/src/zklogin/tests.rs b/crates/sui-sdk-crypto/src/zklogin/tests.rs new file mode 100644 index 000000000..f1b9ccd35 --- /dev/null +++ b/crates/sui-sdk-crypto/src/zklogin/tests.rs @@ -0,0 +1,80 @@ +use signature::Signer; +use sui_sdk::types::PersonalMessage; + +use crate::ed25519::Ed25519PrivateKey; + +use super::*; + +/// Returns a valid zklogin material for testing only. +fn test_zklogin_material() -> (Jwk, JwkId, ZkLoginInputs, Ed25519PrivateKey, u64) { + let inputs = serde_json::json!({ + "proof_points": { + "a": [ + "17318089125952421736342263717932719437717844282410187957984751939942898251250", + "11373966645469122582074082295985388258840681618268593976697325892280915681207", + "1" + ], + "b": [ + [ + "5939871147348834997361720122238980177152303274311047249905942384915768690895", + "4533568271134785278731234570361482651996740791888285864966884032717049811708" + ], + [ + "10564387285071555469753990661410840118635925466597037018058770041347518461368", + "12597323547277579144698496372242615368085801313343155735511330003884767957854" + ], + ["1","0"] + ], + "c": [ + "15791589472556826263231644728873337629015269984699404073623603352537678813171", + "4547866499248881449676161158024748060485373250029423904113017422539037162527", + "1" + ] + }, + "iss_base64_details": { + "value": "wiaXNzIjoiaHR0cHM6Ly9pZC50d2l0Y2gudHYvb2F1dGgyIiw", + "index_mod_4": 2 + }, + "header_base64": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ", + "address_seed": "20794788559620669596206457022966176986688727876128223628113916380927502737911" + }); + + let zklogin_inputs: ZkLoginInputs = serde_json::from_value(inputs).unwrap(); + + let key = Ed25519PrivateKey::new([ + 155, 244, 154, 106, 7, 85, 249, 83, 129, 31, 206, 18, 95, 38, 131, 213, 4, 41, 195, 187, + 73, 224, 116, 20, 126, 0, 137, 165, 46, 174, 21, 95, + ]); + + let jwk: Jwk = serde_json::from_str(r#"{"alg":"RS256","e":"AQAB","kid":"1","kty":"RSA","n":"6lq9MQ-q6hcxr7kOUp-tHlHtdcDsVLwVIw13iXUCvuDOeCi0VSuxCCUY6UmMjy53dX00ih2E4Y4UvlrmmurK0eG26b-HMNNAvCGsVXHU3RcRhVoHDaOwHwU72j7bpHn9XbP3Q3jebX6KIfNbei2MiR0Wyb8RZHE-aZhRYO8_-k9G2GycTpvc-2GBsP8VHLUKKfAs2B6sW3q3ymU6M0L-cFXkZ9fHkn9ejs-sqZPhMJxtBPBxoUIUQFTgv4VXTSv914f_YkNw-EjuwbgwXMvpyr06EyfImxHoxsZkFYB-qBYHtaMxTnFsZBr6fn8Ha2JqT1hoP7Z5r5wxDu3GQhKkHw","use":"sig"}"#).unwrap(); + let jwk_id = JwkId { + iss: "https://id.twitch.tv/oauth2".to_string(), + kid: "1".to_string(), + }; + let max_epoch = 10; + (jwk, jwk_id, zklogin_inputs, key, max_epoch) +} + +#[test] +fn zklogin_sign_personal_message() { + let message = PersonalMessage(b"hello world".into()); + + let (jwk, jwk_id, inputs, key, max_epoch) = test_zklogin_material(); + let signature = key.sign(&message.signing_digest()); + let zklogin_authenticator = ZkLoginAuthenticator { + inputs, + max_epoch, + signature, + }; + let mut verifier = ZkloginVerifier::new_dev(); + verifier.jwks_mut().insert(jwk_id, jwk); + + verifier + .verify(&message.signing_digest(), &zklogin_authenticator) + .unwrap(); + + let user_signature = UserSignature::ZkLogin(zklogin_authenticator.into()); + verifier + .verify_personal_message(&message, &user_signature) + .unwrap(); +} diff --git a/crates/sui-sdk-crypto/src/zklogin/verify.rs b/crates/sui-sdk-crypto/src/zklogin/verify.rs new file mode 100644 index 000000000..d9264d4cd --- /dev/null +++ b/crates/sui-sdk-crypto/src/zklogin/verify.rs @@ -0,0 +1,681 @@ +use std::str::FromStr; + +use crate::SignatureError; +use ark_bn254::Fq; +use ark_bn254::Fq2; +use ark_bn254::Fr; +use ark_bn254::G1Affine; +use ark_bn254::G1Projective; +use ark_bn254::G2Affine; +use ark_bn254::G2Projective; +use ark_ff::PrimeField; +use ark_groth16::PreparedVerifyingKey; +use ark_groth16::Proof; +use sui_sdk::types::Bn254FieldElement; +use sui_sdk::types::CircomG1; +use sui_sdk::types::CircomG2; +use sui_sdk::types::Ed25519PublicKey; +use sui_sdk::types::Jwk; +use sui_sdk::types::Secp256k1PublicKey; +use sui_sdk::types::Secp256r1PublicKey; +use sui_sdk::types::SimpleSignature; +use sui_sdk::types::ZkLoginInputs; +use sui_sdk::types::ZkLoginProof; + +use super::POSEIDON; + +#[derive(Clone, Debug)] +pub struct VerifyingKey { + inner: PreparedVerifyingKey, +} + +const fn str_to_bn254(s: &str) -> Bn254FieldElement { + match Bn254FieldElement::from_str_radix_10(s) { + Ok(e) => e, + Err(_) => panic!("unable to convert bn254"), + } +} + +const fn build_circom_g1([e0, e1, e2]: [&str; 3]) -> CircomG1 { + CircomG1([str_to_bn254(e0), str_to_bn254(e1), str_to_bn254(e2)]) +} + +const fn build_circom_g2([[e00, e01], [e10, e11], [e20, e21]]: [[&str; 2]; 3]) -> CircomG2 { + CircomG2([ + [str_to_bn254(e00), str_to_bn254(e01)], + [str_to_bn254(e10), str_to_bn254(e11)], + [str_to_bn254(e20), str_to_bn254(e21)], + ]) +} + +fn circom_to_arkworks_g1(g1: &CircomG1) -> Result { + let CircomG1([f0, f1, f2]) = g1; + + let g1: G1Affine = + G1Projective::new_unchecked(bn254_to_fq(f0), bn254_to_fq(f1), bn254_to_fq(f2)).into(); + + if !g1.is_on_curve() || !g1.is_in_correct_subgroup_assuming_on_curve() { + return Err(SignatureError::from_source("invalid G1 input")); + } + + Ok(g1) +} + +fn circom_to_arkworks_g2(g2: &CircomG2) -> Result { + let CircomG2([[f00, f01], [f10, f11], [f20, f21]]) = g2; + + let g2: G2Affine = G2Projective::new_unchecked( + Fq2::new(bn254_to_fq(f00), bn254_to_fq(f01)), + Fq2::new(bn254_to_fq(f10), bn254_to_fq(f11)), + Fq2::new(bn254_to_fq(f20), bn254_to_fq(f21)), + ) + .into(); + + if !g2.is_on_curve() || !g2.is_in_correct_subgroup_assuming_on_curve() { + return Err(SignatureError::from_source("invalid G2 input")); + } + + Ok(g2) +} + +fn bn254_to_fq(f: &Bn254FieldElement) -> Fq { + Fq::from_be_bytes_mod_order(f.padded()) +} + +fn bn254_to_fr(f: &Bn254FieldElement) -> Fr { + Fr::from_be_bytes_mod_order(f.padded()) +} + +fn mainnet_verifying_key() -> VerifyingKey { + const CIRCOM_ALPHA_G1: CircomG1 = build_circom_g1([ + "21529901943976716921335152104180790524318946701278905588288070441048877064089", + "7775817982019986089115946956794180159548389285968353014325286374017358010641", + "1", + ]); + + const CIRCOM_BETA_G2: CircomG2 = build_circom_g2([ + [ + "6600437987682835329040464538375790690815756241121776438004683031791078085074", + "16207344858883952201936462217289725998755030546200154201671892670464461194903", + ], + [ + "17943105074568074607580970189766801116106680981075272363121544016828311544390", + "18339640667362802607939727433487930605412455701857832124655129852540230493587", + ], + ["1", "0"], + ]); + + const CIRCOM_GAMMA_G2: CircomG2 = build_circom_g2([ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ], + ["1", "0"], + ]); + + const CIRCOM_DELTA_G2: CircomG2 = build_circom_g2([ + [ + "19260309516619721648285279557078789954438346514188902804737557357941293711874", + "2480422554560175324649200374556411861037961022026590718777465211464278308900", + ], + [ + "14489104692423540990601374549557603533921811847080812036788172274404299703364", + "12564378633583954025611992187142343628816140907276948128970903673042690269191", + ], + ["1", "0"], + ]); + + const CIRCOM_GAMMA_ABC_G1: [CircomG1; 2] = [ + build_circom_g1([ + "1607694606386445293170795095076356565829000940041894770459712091642365695804", + "18066827569413962196795937356879694709963206118612267170825707780758040578649", + "1", + ]), + build_circom_g1([ + "20653794344898475822834426774542692225449366952113790098812854265588083247207", + "3296759704176575765409730962060698204792513807296274014163938591826372646699", + "1", + ]), + ]; + + let vk = ark_groth16::VerifyingKey { + alpha_g1: circom_to_arkworks_g1(&CIRCOM_ALPHA_G1).unwrap(), + beta_g2: circom_to_arkworks_g2(&CIRCOM_BETA_G2).unwrap(), + gamma_g2: circom_to_arkworks_g2(&CIRCOM_GAMMA_G2).unwrap(), + delta_g2: circom_to_arkworks_g2(&CIRCOM_DELTA_G2).unwrap(), + gamma_abc_g1: CIRCOM_GAMMA_ABC_G1 + .iter() + .map(circom_to_arkworks_g1) + .collect::>() + .unwrap(), + }; + + VerifyingKey { + inner: PreparedVerifyingKey::from(vk), + } +} + +/// Load a fixed verifying key from zkLogin.vkey output. This is based on a local setup and should not use in production. +fn dev_verifying_key() -> VerifyingKey { + const CIRCOM_ALPHA_G1: CircomG1 = build_circom_g1([ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1", + ]); + + const CIRCOM_BETA_G2: CircomG2 = build_circom_g2([ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132", + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679", + ], + ["1", "0"], + ]); + + const CIRCOM_GAMMA_G2: CircomG2 = build_circom_g2([ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ], + ["1", "0"], + ]); + + const CIRCOM_DELTA_G2: CircomG2 = build_circom_g2([ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ], + ["1", "0"], + ]); + + const CIRCOM_GAMMA_ABC_G1: [CircomG1; 2] = [ + build_circom_g1([ + "20701306374481714853949730154526815782802808896228594855451770849676897643964", + "2766989084754673216772682210231588284954002353414778477810174100808747060165", + "1", + ]), + build_circom_g1([ + "501195541410525737371980194958674422793469475773065719916327137354779402600", + "13527631693157515024233848630878973193664410306029731429350155106228769355415", + "1", + ]), + ]; + + let vk = ark_groth16::VerifyingKey { + alpha_g1: circom_to_arkworks_g1(&CIRCOM_ALPHA_G1).unwrap(), + beta_g2: circom_to_arkworks_g2(&CIRCOM_BETA_G2).unwrap(), + gamma_g2: circom_to_arkworks_g2(&CIRCOM_GAMMA_G2).unwrap(), + delta_g2: circom_to_arkworks_g2(&CIRCOM_DELTA_G2).unwrap(), + gamma_abc_g1: CIRCOM_GAMMA_ABC_G1 + .iter() + .map(circom_to_arkworks_g1) + .collect::>() + .unwrap(), + }; + + VerifyingKey::new(PreparedVerifyingKey::from(vk)) +} + +impl VerifyingKey { + fn new(inner: PreparedVerifyingKey) -> Self { + Self { inner } + } + + pub fn new_mainnet() -> Self { + mainnet_verifying_key() + } + + pub fn new_dev() -> Self { + dev_verifying_key() + } + + pub fn verify_zklogin( + &self, + jwk: &Jwk, + inputs: &ZkLoginInputs, + signature: &SimpleSignature, + max_epoch: u64, + ) -> Result<(), SignatureError> { + use base64ct::Base64UrlUnpadded; + use base64ct::Encoding; + // Decode modulus to bytes. + let modulus = Base64UrlUnpadded::decode_vec(&jwk.n) + .map_err(|e| SignatureError::from_source(e.to_string()))?; + + let proof = zklogin_proof_to_arkworks(&inputs.proof_points).unwrap(); + let input_hash = calculate_all_inputs_hash(inputs, signature, &modulus, max_epoch).unwrap(); + + self.verify_proof(&proof, &[input_hash]) + } + + fn verify_proof( + &self, + proof: &Proof, + public_inputs: &[ark_bn254::Fr], + ) -> Result<(), SignatureError> { + use ark_snark::SNARK; + + if ark_groth16::Groth16::::verify_with_processed_vk( + &self.inner, + public_inputs, + proof, + ) + .map_err(|e| SignatureError::from_source(e.to_string()))? + { + Ok(()) + } else { + Err(SignatureError::from_source("Groth16 proof verify failed")) + } + } +} + +fn zklogin_proof_to_arkworks( + proof: &ZkLoginProof, +) -> Result, SignatureError> { + Ok(Proof { + a: circom_to_arkworks_g1(&proof.a)?, + b: circom_to_arkworks_g2(&proof.b)?, + c: circom_to_arkworks_g1(&proof.c)?, + }) +} + +/// Given a SimpleSignature convert the corrisponding public key, prefixed with the signature +/// scheme flag, to two Bn254Frs +pub fn public_key_to_frs(signature: &SimpleSignature) -> (Fr, Fr) { + // buf length of the longest public key secp256r1/secp256k1 of 33 bytes plus 1 byte for the + // scheme + let mut buf = [0u8; 34]; + + buf[0] = signature.scheme().to_u8(); + + let buf = match signature { + SimpleSignature::Ed25519 { public_key, .. } => { + buf[1..Ed25519PublicKey::LENGTH + 1].copy_from_slice(public_key.inner()); + &buf[..Ed25519PublicKey::LENGTH + 1] + } + SimpleSignature::Secp256k1 { public_key, .. } => { + buf[1..Secp256k1PublicKey::LENGTH + 1].copy_from_slice(public_key.inner()); + &buf[..Secp256k1PublicKey::LENGTH + 1] + } + SimpleSignature::Secp256r1 { public_key, .. } => { + buf[1..Secp256r1PublicKey::LENGTH + 1].copy_from_slice(public_key.inner()); + &buf[..Secp256r1PublicKey::LENGTH + 1] + } + }; + + //TODO this comment is wrong... + // Split the bytes deterministically such that the first element contains the first 128 + // bits of the hash, and the second element contains the latter ones. + let (first_half, second_half) = buf.split_at(buf.len() - 16); + + let eph_public_key_0 = Fr::from_be_bytes_mod_order(first_half); + let eph_public_key_1 = Fr::from_be_bytes_mod_order(second_half); + (eph_public_key_0, eph_public_key_1) +} + +pub(crate) type U256 = bnum::BUintD8<32>; +pub(crate) type U2048 = bnum::BUintD8<256>; + +const MAX_HEADER_LEN: u8 = 248; +const PACK_WIDTH: u8 = 248; +#[allow(unused)] +const ISS: &str = "iss"; +#[allow(unused)] +const BASE64_URL_CHARSET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; +const MAX_EXT_ISS_LEN: u8 = 165; +const MAX_ISS_LEN_B64: u8 = 4 * (1 + MAX_EXT_ISS_LEN / 3); + +/// Pads a stream of bytes and maps it to a field element +pub fn hash_ascii_str_to_field(s: &str, max_size: u8) -> Result { + let str_padded = str_to_padded_char_codes(s, max_size)?; + hash_to_field(&str_padded, 8, PACK_WIDTH) +} + +fn str_to_padded_char_codes(s: &str, max_len: u8) -> Result, SignatureError> { + let arr: Vec = s.bytes().map(U256::from).collect(); + pad_with_zeroes(arr, max_len) +} + +fn pad_with_zeroes(in_arr: Vec, out_count: u8) -> Result, SignatureError> { + if in_arr.len() > out_count as usize { + return Err(SignatureError::from_source("in_arr too long")); + } + let mut padded = in_arr; + padded.resize(out_count as usize, U256::ZERO); + Ok(padded) +} + +/// Maps a stream of bigints to a single field element. First we convert the base from +/// inWidth to packWidth. Then we compute the poseidon hash of the "packed" input. +/// input is the input vector containing equal-width big ints. inWidth is the width of +/// each input element. +fn hash_to_field( + input: &[T], + in_width: u16, + pack_width: u8, +) -> Result { + let packed = convert_base(input, in_width, pack_width)?; + + POSEIDON.hash(&packed).map_err(SignatureError::from_source) +} + +/// Helper function to pack field elements from big ints. +fn convert_base( + in_arr: &[T], + in_width: u16, + out_width: u8, +) -> Result, SignatureError> { + if out_width == 0 { + return Err(SignatureError::from_source("invalid input")); + } + let bits = big_int_array_to_bits(in_arr, in_width as usize)?; + let mut packed: Vec = bits + .rchunks(out_width as usize) + .map(|chunk| { + Fr::from_be_bytes_mod_order(U256::from_radix_be(chunk, 2).unwrap().to_be().digits()) + }) + .collect(); + packed.reverse(); + match packed.len() != (in_arr.len() * in_width as usize).div_ceil(out_width as usize) { + true => Err(SignatureError::from_source("invalid input")), + false => Ok(packed), + } +} + +/// Convert a big int array to a bit array with 0 paddings. +fn big_int_array_to_bits( + integers: &[T], + intended_size: usize, +) -> Result, SignatureError> { + use itertools::Itertools; + use std::cmp::Ordering::{Equal, Greater, Less}; + + integers + .iter() + .map(|integer| { + let bits = integer.to_bits(); + match bits.len().cmp(&intended_size) { + Less => { + let extra_bits = intended_size - bits.len(); + let mut padded = vec![0; extra_bits]; + padded.extend(bits); + Ok(padded) + } + Equal => Ok(bits), + Greater => Err(SignatureError::from_source("invalid input")), + } + }) + .flatten_ok() + .collect() +} + +trait ToBits { + fn to_bits(&self) -> Vec; +} + +impl ToBits for U256 { + fn to_bits(&self) -> Vec { + self.to_radix_be(2) + } +} + +impl ToBits for U2048 { + fn to_bits(&self) -> Vec { + self.to_radix_be(2) + } +} + +/// Calculate the poseidon hash from selected fields from inputs, along with the ephemeral pubkey. +pub fn calculate_all_inputs_hash( + inputs: &ZkLoginInputs, + signature: &SimpleSignature, + modulus: &[u8], + max_epoch: u64, +) -> Result { + if inputs.header_base64.len() > MAX_HEADER_LEN as usize { + return Err(SignatureError::from_source("header too long")); + } + + let (first, second) = public_key_to_frs(signature); + + let address_seed = bn254_to_fr(&inputs.address_seed); + let max_epoch_f = Fr::from_be_bytes_mod_order(U256::from(max_epoch).to_be().digits()); + let index_mod_4_f = Fr::from_be_bytes_mod_order( + U256::from(inputs.iss_base64_details.index_mod_4) + .to_be() + .digits(), + ); + + let iss_base64_f = hash_ascii_str_to_field(&inputs.iss_base64_details.value, MAX_ISS_LEN_B64)?; + let header_f = hash_ascii_str_to_field(&inputs.header_base64, MAX_HEADER_LEN)?; + let modulus_f = hash_to_field(&[U2048::from_be_slice(modulus).unwrap()], 2048, PACK_WIDTH)?; + + POSEIDON + .hash(&[ + first, + second, + address_seed, + max_epoch_f, + iss_base64_f, + index_mod_4_f, + header_f, + modulus_f, + ]) + .map_err(SignatureError::from_source) +} + +/// Calculate the Sui address based on address seed and address params. +#[allow(unused)] +fn gen_address_seed( + salt: &str, + name: &str, // i.e. "sub" + value: &str, // i.e. the sub value + aud: &str, // i.e. the client ID +) -> Result { + let salt_hash = POSEIDON + .hash(&[bn254_to_fr( + &Bn254FieldElement::from_str(salt).map_err(SignatureError::from_source)?, + )]) + .map_err(SignatureError::from_source)?; + gen_address_seed_with_salt_hash(salt_hash, name, value, aud) +} + +const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32; +const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115; +const MAX_AUD_VALUE_LENGTH: u8 = 145; + +/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as input instead of the salt. +pub(crate) fn gen_address_seed_with_salt_hash( + salt_hash: Fr, + name: &str, // i.e. "sub" + value: &str, // i.e. the sub value + aud: &str, // i.e. the client ID +) -> Result { + Ok(POSEIDON + .hash(&[ + hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?, + hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?, + hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?, + salt_hash, + ]) + .map_err(SignatureError::from_source)? + .to_string()) +} + +#[cfg(test)] +mod test { + use super::*; + use sui_sdk::types::Ed25519Signature; + + #[cfg(test)] + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + #[test] + fn test_verify_zklogin_google() { + let user_salt = "206703048842351542647799591018316385612"; + + let pubkey = Ed25519PublicKey::new([ + 185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114, + 116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0, + ]); + let signature = SimpleSignature::Ed25519 { + signature: Ed25519Signature::new([0; 64]), + public_key: pubkey, + }; + + // Get the address seed. + let address_seed = gen_address_seed( + user_salt, + "sub", + "106294049240999307923", + "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com", + ) + .unwrap(); + + let inputs = serde_json::json!({ + "proof_points": { + "a": [ + "8247215875293406890829839156897863742504615191361518281091302475904551111016", + "6872980335748205979379321982220498484242209225765686471076081944034292159666", + "1" + ], + "b": [ + [ + "21419680064642047510915171723230639588631899775315750803416713283740137406807", + "21566716915562037737681888858382287035712341650647439119820808127161946325890" + ], + [ + "17867714710686394159919998503724240212517838710399045289784307078087926404555", + "21812769875502013113255155836896615164559280911997219958031852239645061854221" + ], + ["1","0"] + ], + "c": [ + "7530826803702928198368421787278524256623871560746240215547076095911132653214", + "16244547936249959771862454850485726883972969173921727256151991751860694123976", + "1" + ] + }, + "iss_base64_details": { + "value": "yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC", + "index_mod_4": 1 + }, + "header_base64": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ", + "address_seed": address_seed + }); + + let zklogin_inputs: ZkLoginInputs = serde_json::from_value(inputs).unwrap(); + + let jwk = Jwk { + kty: "RSA".to_string(), + e: "AQAB".to_string(), + n: "oUriU8GqbRw-avcMn95DGW1cpZR1IoM6L7krfrWvLSSCcSX6Ig117o25Yk7QWBiJpaPV0FbP7Y5-DmThZ3SaF0AXW-3BsKPEXfFfeKVc6vBqk3t5mKlNEowjdvNTSzoOXO5UIHwsXaxiJlbMRalaFEUm-2CKgmXl1ss_yGh1OHkfnBiGsfQUndKoHiZuDzBMGw8Sf67am_Ok-4FShK0NuR3-q33aB_3Z7obC71dejSLWFOEcKUVCaw6DGVuLog3x506h1QQ1r0FXKOQxnmqrRgpoHqGSouuG35oZve1vgCU4vLZ6EAgBAbC0KL35I7_0wUDSMpiAvf7iZxzJVbspkQ".to_string(), + alg: "RS256".to_string(), + }; + + VerifyingKey::new_mainnet() + .verify_zklogin(&jwk, &zklogin_inputs, &signature, 10) + .unwrap(); + } + + #[test] + fn test_public_key_to_frs() { + let pubkey = Ed25519PublicKey::new([ + 185, 198, 238, 22, 48, 239, 62, 113, 17, 68, 166, 72, 219, 6, 187, 178, 40, 79, 114, + 116, 207, 190, 229, 63, 252, 238, 80, 60, 193, 164, 146, 0, + ]); + let signature = SimpleSignature::Ed25519 { + signature: Ed25519Signature::new([0; 64]), + public_key: pubkey, + }; + let (actual_0, actual_1) = public_key_to_frs(&signature); + let expect_0 = Fr::from(ark_ff::BigInt([ + 1244302228903607218, + 13386648721483054705, + 0, + 0, + ])); + + let expect_1 = Fr::from(ark_ff::BigInt([ + 18225592963892023808, + 2904666130704426303, + 0, + 0, + ])); + assert_eq!(actual_0, expect_0); + assert_eq!(actual_1, expect_1); + } + + #[test] + fn test_hash_ascii_str_to_field() { + let actual = hash_ascii_str_to_field("sub", 32).unwrap(); + let expect = Fr::from(ark_ff::BigInt([ + 9420274050661827129, + 9736100402995345242, + 10431892319505233812, + 1450152190758097105, + ])); + assert_eq!(actual, expect); + + let actual = hash_ascii_str_to_field("106294049240999307923", 115).unwrap(); + let expect = Fr::from(ark_ff::BigInt([ + 1616959301818912987, + 17318965991705091209, + 15303466056770245354, + 1596136658728187659, + ])); + assert_eq!(actual, expect); + + let actual = hash_ascii_str_to_field( + "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com", + 145, + ) + .unwrap(); + let expect = Fr::from(ark_ff::BigInt([ + 5030944271044826582, + 8577618269522081956, + 6962871209781429610, + 2149811477348923117, + ])); + assert_eq!(actual, expect); + + let actual = + hash_ascii_str_to_field("yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC", 224) + .unwrap(); + let expect = Fr::from(ark_ff::BigInt([ + 6021918591354572765, + 14069258108381575504, + 1736509627917450843, + 2767185135515367512, + ])); + assert_eq!(actual, expect); + + let actual = hash_ascii_str_to_field( + "eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ", + 248, + ).unwrap(); + let expect = Fr::from(ark_ff::BigInt([ + 4239129243150064016, + 15469804315138207306, + 17534492051703966556, + 2100329545252322607, + ])); + assert_eq!(actual, expect); + } +} From e91e3745b3e9da4c0b3e6976acea94f0c09d5c9a Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 25 Sep 2024 15:48:09 -0500 Subject: [PATCH 017/107] chore: rename sui-sdk to sui-sdk-types --- Makefile | 4 ++-- crates/sui-graphql-client/Cargo.toml | 2 +- crates/sui-sdk-crypto/Cargo.toml | 12 +++++----- crates/sui-sdk-crypto/src/ed25519.rs | 12 +++++----- crates/sui-sdk-crypto/src/lib.rs | 6 ++--- crates/sui-sdk-crypto/src/secp256k1.rs | 12 +++++----- crates/sui-sdk-crypto/src/secp256r1.rs | 12 +++++----- crates/sui-sdk-crypto/src/simple.rs | 6 ++--- crates/sui-sdk-crypto/src/zklogin/mod.rs | 6 ++--- crates/sui-sdk-crypto/src/zklogin/tests.rs | 2 +- crates/sui-sdk-crypto/src/zklogin/verify.rs | 22 +++++++++---------- .../Cargo.toml | 2 +- .../{iota-rust-sdk => sui-sdk-types}/Makefile | 0 .../src/hash.rs | 0 .../src/lib.rs | 0 .../src/types/address.rs | 0 .../src/types/checkpoint.rs | 0 .../src/types/crypto/bls12381.rs | 0 .../src/types/crypto/ed25519.rs | 0 .../src/types/crypto/intent.rs | 0 .../src/types/crypto/mod.rs | 0 .../src/types/crypto/multisig.rs | 0 .../src/types/crypto/passkey.rs | 0 .../src/types/crypto/secp256k1.rs | 0 .../src/types/crypto/secp256r1.rs | 0 .../src/types/crypto/signature.rs | 0 .../src/types/crypto/validator.rs | 0 .../src/types/crypto/zklogin.rs | 0 .../src/types/digest.rs | 0 .../fixtures/genesis-transaction-effects | 0 .../types/effects/fixtures/sponsor-tx-effects | 0 .../src/types/effects/mod.rs | 0 .../src/types/effects/v1.rs | 0 .../src/types/events.rs | 0 .../src/types/execution_status.rs | 0 .../src/types/framework.rs | 0 .../src/types/gas.rs | 0 .../src/types/mod.rs | 0 .../src/types/object.rs | 0 .../src/types/object_id.rs | 0 .../src/types/serialization_proptests.rs | 0 .../types/transaction/fixtures/change-epoch | 0 .../fixtures/consensus-commit-prologue-v1 | 0 .../src/types/transaction/fixtures/genesis | 0 .../src/types/transaction/fixtures/ptb | 0 .../update-transaction-fixtures/.gitignore | 0 .../update-transaction-fixtures/Cargo.toml | 0 .../update-transaction-fixtures/src/main.rs | 0 .../src/types/transaction/mod.rs | 0 .../src/types/transaction/serialization.rs | 0 .../src/types/transaction/unresolved.rs | 0 .../src/types/type_tag/mod.rs | 0 .../src/types/type_tag/parse.rs | 0 .../src/types/type_tag/serialization.rs | 0 .../src/types/u256.rs | 0 55 files changed, 49 insertions(+), 49 deletions(-) rename crates/{iota-rust-sdk => sui-sdk-types}/Cargo.toml (99%) rename crates/{iota-rust-sdk => sui-sdk-types}/Makefile (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/hash.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/lib.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/address.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/checkpoint.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/bls12381.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/ed25519.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/intent.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/mod.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/multisig.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/passkey.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/secp256k1.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/secp256r1.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/signature.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/validator.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/crypto/zklogin.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/digest.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/effects/fixtures/genesis-transaction-effects (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/effects/fixtures/sponsor-tx-effects (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/effects/mod.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/effects/v1.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/events.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/execution_status.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/framework.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/gas.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/mod.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/object.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/object_id.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/serialization_proptests.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/change-epoch (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/consensus-commit-prologue-v1 (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/genesis (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/ptb (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/mod.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/serialization.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/transaction/unresolved.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/type_tag/mod.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/type_tag/parse.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/type_tag/serialization.rs (100%) rename crates/{iota-rust-sdk => sui-sdk-types}/src/types/u256.rs (100%) diff --git a/Makefile b/Makefile index 88af67e49..db7f630f6 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all:: ci .PHONY: check-features check-features: - $(MAKE) -C crates/iota-sdk check-features + $(MAKE) -C crates/iota-sdk-types check-features $(MAKE) -C crates/iota-sdk-crypto check-features .PHONY: check-fmt @@ -22,7 +22,7 @@ test: .PHONY: wasm wasm: - $(MAKE) -C crates/iota-sdk wasm + $(MAKE) -C crates/iota-sdk-types wasm $(MAKE) -C crates/iota-sdk-crypto wasm .PHONY: doc diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index b27c9e5d6..fccb23444 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -16,7 +16,7 @@ bcs = "0.1.6" chrono = { version = "0.4.38" } cynic = { version = "3.7.3" } reqwest = { version = "0.12", features = ["json"] } -sui-types = { package= "sui-sdk", path = "../sui-sdk", features = ["serde"] } +sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } tokio = { version = "1.39.2", features = ["full"] } [build-dependencies] diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-sdk-crypto/Cargo.toml index 35d05cdb4..0b76d211b 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-sdk-crypto/Cargo.toml @@ -21,9 +21,9 @@ rustdoc-args = [ [features] default = [] -ed25519 = ["dep:ed25519-dalek", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] -secp256r1 = ["dep:p256", "dep:rand_core", "sui-sdk/hash", "sui-sdk/serde"] -secp256k1 = ["dep:k256", "dep:rand_core", "signature/std", "sui-sdk/hash", "sui-sdk/serde"] +ed25519 = ["dep:ed25519-dalek", "dep:rand_core", "sui-sdk-types/hash", "sui-sdk-types/serde"] +secp256r1 = ["dep:p256", "dep:rand_core", "sui-sdk-types/hash", "sui-sdk-types/serde"] +secp256k1 = ["dep:k256", "dep:rand_core", "signature/std", "sui-sdk-types/hash", "sui-sdk-types/serde"] zklogin = [ "dep:ark-bn254", "dep:ark-ff", @@ -37,13 +37,13 @@ zklogin = [ "dep:serde_derive", "dep:serde_json", "signature/std", - "sui-sdk/hash", - "sui-sdk/serde", + "sui-sdk-types/hash", + "sui-sdk-types/serde", ] [dependencies] signature = "2.2" -sui-sdk = { version = "0.0.0", path = "../sui-sdk", default-features = false } +sui-sdk-types = { version = "0.0.0", path = "../sui-sdk-types", default-features = false } # RNG support rand_core = { version = "0.6.4", optional = true } diff --git a/crates/sui-sdk-crypto/src/ed25519.rs b/crates/sui-sdk-crypto/src/ed25519.rs index 3bf22c5fb..9be214dfd 100644 --- a/crates/sui-sdk-crypto/src/ed25519.rs +++ b/crates/sui-sdk-crypto/src/ed25519.rs @@ -3,12 +3,12 @@ use crate::Signer; use crate::SuiSigner; use crate::SuiVerifier; use crate::Verifier; -use sui_sdk::types::Ed25519PublicKey; -use sui_sdk::types::Ed25519Signature; -use sui_sdk::types::PersonalMessage; -use sui_sdk::types::SimpleSignature; -use sui_sdk::types::Transaction; -use sui_sdk::types::UserSignature; +use sui_sdk_types::types::Ed25519PublicKey; +use sui_sdk_types::types::Ed25519Signature; +use sui_sdk_types::types::PersonalMessage; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::Transaction; +use sui_sdk_types::types::UserSignature; pub struct Ed25519PrivateKey(ed25519_dalek::SigningKey); diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-sdk-crypto/src/lib.rs index cc153be96..1bdd086c4 100644 --- a/crates/sui-sdk-crypto/src/lib.rs +++ b/crates/sui-sdk-crypto/src/lib.rs @@ -1,8 +1,8 @@ #![cfg_attr(doc_cfg, feature(doc_cfg))] -use sui_sdk::types::PersonalMessage; -use sui_sdk::types::Transaction; -use sui_sdk::types::UserSignature; +use sui_sdk_types::types::PersonalMessage; +use sui_sdk_types::types::Transaction; +use sui_sdk_types::types::UserSignature; pub use signature::Error as SignatureError; pub use signature::Signer; diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-sdk-crypto/src/secp256k1.rs index d44ccb9f2..37f6984d6 100644 --- a/crates/sui-sdk-crypto/src/secp256k1.rs +++ b/crates/sui-sdk-crypto/src/secp256k1.rs @@ -6,12 +6,12 @@ use k256::ecdsa::VerifyingKey; use k256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk::types::PersonalMessage; -use sui_sdk::types::Secp256k1PublicKey; -use sui_sdk::types::Secp256k1Signature; -use sui_sdk::types::SimpleSignature; -use sui_sdk::types::Transaction; -use sui_sdk::types::UserSignature; +use sui_sdk_types::types::PersonalMessage; +use sui_sdk_types::types::Secp256k1PublicKey; +use sui_sdk_types::types::Secp256k1Signature; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::Transaction; +use sui_sdk_types::types::UserSignature; pub struct Secp256k1PrivateKey(SigningKey); diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-sdk-crypto/src/secp256r1.rs index 5219b8a73..f60d62b71 100644 --- a/crates/sui-sdk-crypto/src/secp256r1.rs +++ b/crates/sui-sdk-crypto/src/secp256r1.rs @@ -6,12 +6,12 @@ use p256::ecdsa::VerifyingKey; use p256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk::types::PersonalMessage; -use sui_sdk::types::Secp256r1PublicKey; -use sui_sdk::types::Secp256r1Signature; -use sui_sdk::types::SimpleSignature; -use sui_sdk::types::Transaction; -use sui_sdk::types::UserSignature; +use sui_sdk_types::types::PersonalMessage; +use sui_sdk_types::types::Secp256r1PublicKey; +use sui_sdk_types::types::Secp256r1Signature; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::Transaction; +use sui_sdk_types::types::UserSignature; pub struct Secp256r1PrivateKey(SigningKey); diff --git a/crates/sui-sdk-crypto/src/simple.rs b/crates/sui-sdk-crypto/src/simple.rs index 90f626498..f2ac61298 100644 --- a/crates/sui-sdk-crypto/src/simple.rs +++ b/crates/sui-sdk-crypto/src/simple.rs @@ -1,6 +1,6 @@ use crate::{SignatureError, SuiVerifier}; use signature::Verifier; -use sui_sdk::types::{SimpleSignature, UserSignature}; +use sui_sdk_types::types::{SimpleSignature, UserSignature}; pub struct SimpleVerifier; @@ -63,7 +63,7 @@ impl Verifier for SimpleVerifier { impl SuiVerifier for SimpleVerifier { fn verify_transaction( &self, - transaction: &sui_sdk::types::Transaction, + transaction: &sui_sdk_types::types::Transaction, signature: &UserSignature, ) -> Result<(), SignatureError> { let message = transaction.signing_digest(); @@ -72,7 +72,7 @@ impl SuiVerifier for SimpleVerifier { fn verify_personal_message( &self, - message: &sui_sdk::types::PersonalMessage<'_>, + message: &sui_sdk_types::types::PersonalMessage<'_>, signature: &UserSignature, ) -> Result<(), SignatureError> { let message = message.signing_digest(); diff --git a/crates/sui-sdk-crypto/src/zklogin/mod.rs b/crates/sui-sdk-crypto/src/zklogin/mod.rs index 9d23065f3..69586f7d4 100644 --- a/crates/sui-sdk-crypto/src/zklogin/mod.rs +++ b/crates/sui-sdk-crypto/src/zklogin/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use crate::{SignatureError, SuiVerifier}; use poseidon::POSEIDON; use signature::Verifier; -use sui_sdk::types::{Claim, Jwk, JwkId, UserSignature, ZkLoginAuthenticator, ZkLoginInputs}; +use sui_sdk_types::types::{Claim, Jwk, JwkId, UserSignature, ZkLoginAuthenticator, ZkLoginInputs}; mod poseidon; mod verify; @@ -82,7 +82,7 @@ impl Verifier for ZkloginVerifier { impl SuiVerifier for ZkloginVerifier { fn verify_transaction( &self, - transaction: &sui_sdk::types::Transaction, + transaction: &sui_sdk_types::types::Transaction, signature: &UserSignature, ) -> Result<(), SignatureError> { let message = transaction.signing_digest(); @@ -91,7 +91,7 @@ impl SuiVerifier for ZkloginVerifier { fn verify_personal_message( &self, - message: &sui_sdk::types::PersonalMessage<'_>, + message: &sui_sdk_types::types::PersonalMessage<'_>, signature: &UserSignature, ) -> Result<(), SignatureError> { let message = message.signing_digest(); diff --git a/crates/sui-sdk-crypto/src/zklogin/tests.rs b/crates/sui-sdk-crypto/src/zklogin/tests.rs index f1b9ccd35..d67c90a54 100644 --- a/crates/sui-sdk-crypto/src/zklogin/tests.rs +++ b/crates/sui-sdk-crypto/src/zklogin/tests.rs @@ -1,5 +1,5 @@ use signature::Signer; -use sui_sdk::types::PersonalMessage; +use sui_sdk_types::types::PersonalMessage; use crate::ed25519::Ed25519PrivateKey; diff --git a/crates/sui-sdk-crypto/src/zklogin/verify.rs b/crates/sui-sdk-crypto/src/zklogin/verify.rs index d9264d4cd..f958a2474 100644 --- a/crates/sui-sdk-crypto/src/zklogin/verify.rs +++ b/crates/sui-sdk-crypto/src/zklogin/verify.rs @@ -11,16 +11,16 @@ use ark_bn254::G2Projective; use ark_ff::PrimeField; use ark_groth16::PreparedVerifyingKey; use ark_groth16::Proof; -use sui_sdk::types::Bn254FieldElement; -use sui_sdk::types::CircomG1; -use sui_sdk::types::CircomG2; -use sui_sdk::types::Ed25519PublicKey; -use sui_sdk::types::Jwk; -use sui_sdk::types::Secp256k1PublicKey; -use sui_sdk::types::Secp256r1PublicKey; -use sui_sdk::types::SimpleSignature; -use sui_sdk::types::ZkLoginInputs; -use sui_sdk::types::ZkLoginProof; +use sui_sdk_types::types::Bn254FieldElement; +use sui_sdk_types::types::CircomG1; +use sui_sdk_types::types::CircomG2; +use sui_sdk_types::types::Ed25519PublicKey; +use sui_sdk_types::types::Jwk; +use sui_sdk_types::types::Secp256k1PublicKey; +use sui_sdk_types::types::Secp256r1PublicKey; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::ZkLoginInputs; +use sui_sdk_types::types::ZkLoginProof; use super::POSEIDON; @@ -520,7 +520,7 @@ pub(crate) fn gen_address_seed_with_salt_hash( #[cfg(test)] mod test { use super::*; - use sui_sdk::types::Ed25519Signature; + use sui_sdk_types::types::Ed25519Signature; #[cfg(test)] #[cfg(target_arch = "wasm32")] diff --git a/crates/iota-rust-sdk/Cargo.toml b/crates/sui-sdk-types/Cargo.toml similarity index 99% rename from crates/iota-rust-sdk/Cargo.toml rename to crates/sui-sdk-types/Cargo.toml index 5aff347f0..4ac193c06 100644 --- a/crates/iota-rust-sdk/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "iota-rust-sdk" +name = "iota-sdk-types" version = "0.0.0" authors = ["IOTA Foundation "] edition = "2021" diff --git a/crates/iota-rust-sdk/Makefile b/crates/sui-sdk-types/Makefile similarity index 100% rename from crates/iota-rust-sdk/Makefile rename to crates/sui-sdk-types/Makefile diff --git a/crates/iota-rust-sdk/src/hash.rs b/crates/sui-sdk-types/src/hash.rs similarity index 100% rename from crates/iota-rust-sdk/src/hash.rs rename to crates/sui-sdk-types/src/hash.rs diff --git a/crates/iota-rust-sdk/src/lib.rs b/crates/sui-sdk-types/src/lib.rs similarity index 100% rename from crates/iota-rust-sdk/src/lib.rs rename to crates/sui-sdk-types/src/lib.rs diff --git a/crates/iota-rust-sdk/src/types/address.rs b/crates/sui-sdk-types/src/types/address.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/address.rs rename to crates/sui-sdk-types/src/types/address.rs diff --git a/crates/iota-rust-sdk/src/types/checkpoint.rs b/crates/sui-sdk-types/src/types/checkpoint.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/checkpoint.rs rename to crates/sui-sdk-types/src/types/checkpoint.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/bls12381.rs b/crates/sui-sdk-types/src/types/crypto/bls12381.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/bls12381.rs rename to crates/sui-sdk-types/src/types/crypto/bls12381.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/ed25519.rs b/crates/sui-sdk-types/src/types/crypto/ed25519.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/ed25519.rs rename to crates/sui-sdk-types/src/types/crypto/ed25519.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/intent.rs b/crates/sui-sdk-types/src/types/crypto/intent.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/intent.rs rename to crates/sui-sdk-types/src/types/crypto/intent.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/mod.rs b/crates/sui-sdk-types/src/types/crypto/mod.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/mod.rs rename to crates/sui-sdk-types/src/types/crypto/mod.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/multisig.rs b/crates/sui-sdk-types/src/types/crypto/multisig.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/multisig.rs rename to crates/sui-sdk-types/src/types/crypto/multisig.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/passkey.rs rename to crates/sui-sdk-types/src/types/crypto/passkey.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/secp256k1.rs b/crates/sui-sdk-types/src/types/crypto/secp256k1.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/secp256k1.rs rename to crates/sui-sdk-types/src/types/crypto/secp256k1.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/secp256r1.rs b/crates/sui-sdk-types/src/types/crypto/secp256r1.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/secp256r1.rs rename to crates/sui-sdk-types/src/types/crypto/secp256r1.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/signature.rs b/crates/sui-sdk-types/src/types/crypto/signature.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/signature.rs rename to crates/sui-sdk-types/src/types/crypto/signature.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/validator.rs b/crates/sui-sdk-types/src/types/crypto/validator.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/validator.rs rename to crates/sui-sdk-types/src/types/crypto/validator.rs diff --git a/crates/iota-rust-sdk/src/types/crypto/zklogin.rs b/crates/sui-sdk-types/src/types/crypto/zklogin.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/crypto/zklogin.rs rename to crates/sui-sdk-types/src/types/crypto/zklogin.rs diff --git a/crates/iota-rust-sdk/src/types/digest.rs b/crates/sui-sdk-types/src/types/digest.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/digest.rs rename to crates/sui-sdk-types/src/types/digest.rs diff --git a/crates/iota-rust-sdk/src/types/effects/fixtures/genesis-transaction-effects b/crates/sui-sdk-types/src/types/effects/fixtures/genesis-transaction-effects similarity index 100% rename from crates/iota-rust-sdk/src/types/effects/fixtures/genesis-transaction-effects rename to crates/sui-sdk-types/src/types/effects/fixtures/genesis-transaction-effects diff --git a/crates/iota-rust-sdk/src/types/effects/fixtures/sponsor-tx-effects b/crates/sui-sdk-types/src/types/effects/fixtures/sponsor-tx-effects similarity index 100% rename from crates/iota-rust-sdk/src/types/effects/fixtures/sponsor-tx-effects rename to crates/sui-sdk-types/src/types/effects/fixtures/sponsor-tx-effects diff --git a/crates/iota-rust-sdk/src/types/effects/mod.rs b/crates/sui-sdk-types/src/types/effects/mod.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/effects/mod.rs rename to crates/sui-sdk-types/src/types/effects/mod.rs diff --git a/crates/iota-rust-sdk/src/types/effects/v1.rs b/crates/sui-sdk-types/src/types/effects/v1.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/effects/v1.rs rename to crates/sui-sdk-types/src/types/effects/v1.rs diff --git a/crates/iota-rust-sdk/src/types/events.rs b/crates/sui-sdk-types/src/types/events.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/events.rs rename to crates/sui-sdk-types/src/types/events.rs diff --git a/crates/iota-rust-sdk/src/types/execution_status.rs b/crates/sui-sdk-types/src/types/execution_status.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/execution_status.rs rename to crates/sui-sdk-types/src/types/execution_status.rs diff --git a/crates/iota-rust-sdk/src/types/framework.rs b/crates/sui-sdk-types/src/types/framework.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/framework.rs rename to crates/sui-sdk-types/src/types/framework.rs diff --git a/crates/iota-rust-sdk/src/types/gas.rs b/crates/sui-sdk-types/src/types/gas.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/gas.rs rename to crates/sui-sdk-types/src/types/gas.rs diff --git a/crates/iota-rust-sdk/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/mod.rs rename to crates/sui-sdk-types/src/types/mod.rs diff --git a/crates/iota-rust-sdk/src/types/object.rs b/crates/sui-sdk-types/src/types/object.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/object.rs rename to crates/sui-sdk-types/src/types/object.rs diff --git a/crates/iota-rust-sdk/src/types/object_id.rs b/crates/sui-sdk-types/src/types/object_id.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/object_id.rs rename to crates/sui-sdk-types/src/types/object_id.rs diff --git a/crates/iota-rust-sdk/src/types/serialization_proptests.rs b/crates/sui-sdk-types/src/types/serialization_proptests.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/serialization_proptests.rs rename to crates/sui-sdk-types/src/types/serialization_proptests.rs diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/change-epoch b/crates/sui-sdk-types/src/types/transaction/fixtures/change-epoch similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/change-epoch rename to crates/sui-sdk-types/src/types/transaction/fixtures/change-epoch diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/consensus-commit-prologue-v1 b/crates/sui-sdk-types/src/types/transaction/fixtures/consensus-commit-prologue-v1 similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/consensus-commit-prologue-v1 rename to crates/sui-sdk-types/src/types/transaction/fixtures/consensus-commit-prologue-v1 diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/genesis b/crates/sui-sdk-types/src/types/transaction/fixtures/genesis similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/genesis rename to crates/sui-sdk-types/src/types/transaction/fixtures/genesis diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/ptb b/crates/sui-sdk-types/src/types/transaction/fixtures/ptb similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/ptb rename to crates/sui-sdk-types/src/types/transaction/fixtures/ptb diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore b/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore rename to crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml b/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml rename to crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml diff --git a/crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs b/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs rename to crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs diff --git a/crates/iota-rust-sdk/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/mod.rs rename to crates/sui-sdk-types/src/types/transaction/mod.rs diff --git a/crates/iota-rust-sdk/src/types/transaction/serialization.rs b/crates/sui-sdk-types/src/types/transaction/serialization.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/serialization.rs rename to crates/sui-sdk-types/src/types/transaction/serialization.rs diff --git a/crates/iota-rust-sdk/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/transaction/unresolved.rs rename to crates/sui-sdk-types/src/types/transaction/unresolved.rs diff --git a/crates/iota-rust-sdk/src/types/type_tag/mod.rs b/crates/sui-sdk-types/src/types/type_tag/mod.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/type_tag/mod.rs rename to crates/sui-sdk-types/src/types/type_tag/mod.rs diff --git a/crates/iota-rust-sdk/src/types/type_tag/parse.rs b/crates/sui-sdk-types/src/types/type_tag/parse.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/type_tag/parse.rs rename to crates/sui-sdk-types/src/types/type_tag/parse.rs diff --git a/crates/iota-rust-sdk/src/types/type_tag/serialization.rs b/crates/sui-sdk-types/src/types/type_tag/serialization.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/type_tag/serialization.rs rename to crates/sui-sdk-types/src/types/type_tag/serialization.rs diff --git a/crates/iota-rust-sdk/src/types/u256.rs b/crates/sui-sdk-types/src/types/u256.rs similarity index 100% rename from crates/iota-rust-sdk/src/types/u256.rs rename to crates/sui-sdk-types/src/types/u256.rs From c6679d17f1b3cb778649a1b6ee3ba68f9b26d36a Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 25 Sep 2024 15:57:24 -0500 Subject: [PATCH 018/107] chore: rename sui-sdk-crypto to sui-crypto --- Makefile | 4 ++-- crates/{sui-sdk-crypto => sui-crypto}/Cargo.toml | 2 +- crates/{sui-sdk-crypto => sui-crypto}/Makefile | 0 crates/{sui-sdk-crypto => sui-crypto}/src/bls12381.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/ed25519.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/lib.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/secp256k1.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/secp256r1.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/simple.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/mod.rs | 0 .../src/zklogin/poseidon/constants.rs | 0 .../src/zklogin/poseidon/mod.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/tests.rs | 0 crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/verify.rs | 0 14 files changed, 3 insertions(+), 3 deletions(-) rename crates/{sui-sdk-crypto => sui-crypto}/Cargo.toml (99%) rename crates/{sui-sdk-crypto => sui-crypto}/Makefile (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/bls12381.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/ed25519.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/lib.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/secp256k1.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/secp256r1.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/simple.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/mod.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/poseidon/constants.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/poseidon/mod.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/tests.rs (100%) rename crates/{sui-sdk-crypto => sui-crypto}/src/zklogin/verify.rs (100%) diff --git a/Makefile b/Makefile index db7f630f6..f4719a44d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ all:: ci .PHONY: check-features check-features: $(MAKE) -C crates/iota-sdk-types check-features - $(MAKE) -C crates/iota-sdk-crypto check-features + $(MAKE) -C crates/iota-crypto check-features .PHONY: check-fmt check-fmt: @@ -23,7 +23,7 @@ test: .PHONY: wasm wasm: $(MAKE) -C crates/iota-sdk-types wasm - $(MAKE) -C crates/iota-sdk-crypto wasm + $(MAKE) -C crates/iota-crypto wasm .PHONY: doc doc: diff --git a/crates/sui-sdk-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml similarity index 99% rename from crates/sui-sdk-crypto/Cargo.toml rename to crates/sui-crypto/Cargo.toml index 0b76d211b..8e527341c 100644 --- a/crates/sui-sdk-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sui-sdk-crypto" +name = "sui-crypto" version = "0.0.0" authors = ["Brandon Williams "] license = "Apache-2.0" diff --git a/crates/sui-sdk-crypto/Makefile b/crates/sui-crypto/Makefile similarity index 100% rename from crates/sui-sdk-crypto/Makefile rename to crates/sui-crypto/Makefile diff --git a/crates/sui-sdk-crypto/src/bls12381.rs b/crates/sui-crypto/src/bls12381.rs similarity index 100% rename from crates/sui-sdk-crypto/src/bls12381.rs rename to crates/sui-crypto/src/bls12381.rs diff --git a/crates/sui-sdk-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs similarity index 100% rename from crates/sui-sdk-crypto/src/ed25519.rs rename to crates/sui-crypto/src/ed25519.rs diff --git a/crates/sui-sdk-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs similarity index 100% rename from crates/sui-sdk-crypto/src/lib.rs rename to crates/sui-crypto/src/lib.rs diff --git a/crates/sui-sdk-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs similarity index 100% rename from crates/sui-sdk-crypto/src/secp256k1.rs rename to crates/sui-crypto/src/secp256k1.rs diff --git a/crates/sui-sdk-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs similarity index 100% rename from crates/sui-sdk-crypto/src/secp256r1.rs rename to crates/sui-crypto/src/secp256r1.rs diff --git a/crates/sui-sdk-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs similarity index 100% rename from crates/sui-sdk-crypto/src/simple.rs rename to crates/sui-crypto/src/simple.rs diff --git a/crates/sui-sdk-crypto/src/zklogin/mod.rs b/crates/sui-crypto/src/zklogin/mod.rs similarity index 100% rename from crates/sui-sdk-crypto/src/zklogin/mod.rs rename to crates/sui-crypto/src/zklogin/mod.rs diff --git a/crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs b/crates/sui-crypto/src/zklogin/poseidon/constants.rs similarity index 100% rename from crates/sui-sdk-crypto/src/zklogin/poseidon/constants.rs rename to crates/sui-crypto/src/zklogin/poseidon/constants.rs diff --git a/crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs b/crates/sui-crypto/src/zklogin/poseidon/mod.rs similarity index 100% rename from crates/sui-sdk-crypto/src/zklogin/poseidon/mod.rs rename to crates/sui-crypto/src/zklogin/poseidon/mod.rs diff --git a/crates/sui-sdk-crypto/src/zklogin/tests.rs b/crates/sui-crypto/src/zklogin/tests.rs similarity index 100% rename from crates/sui-sdk-crypto/src/zklogin/tests.rs rename to crates/sui-crypto/src/zklogin/tests.rs diff --git a/crates/sui-sdk-crypto/src/zklogin/verify.rs b/crates/sui-crypto/src/zklogin/verify.rs similarity index 100% rename from crates/sui-sdk-crypto/src/zklogin/verify.rs rename to crates/sui-crypto/src/zklogin/verify.rs From 59cd22d71d501e9120d7c6640b72710394628a7a Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 25 Sep 2024 16:13:02 -0500 Subject: [PATCH 019/107] sui-graphql-client: ignore test_object_query test Ignore the sui-graphql-client::test_object_query test since its has been unreliable due to the dependency on an extrnal running network. --- crates/sui-graphql-client/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 6f3120823..fd7f417ff 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -716,6 +716,7 @@ mod tests { } #[tokio::test] + #[ignore] async fn test_object_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); From c77df1dbfe0a1896604da7069c2ac664b7aa5f86 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 25 Sep 2024 16:29:25 -0500 Subject: [PATCH 020/107] fix doc link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0aa4d298c..611d5e7d0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # IOTA Sdk -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk/) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) A WIP from-scratch rust sdk for the IOTA blockchain From 08e4901ac6bb5c5cf90c7c18a77a70e42334c4e3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 25 Sep 2024 19:51:36 -0500 Subject: [PATCH 021/107] prepare sui-sdk-types-0.0.1 and sui-crypto-0.0.1 --- README.md | 38 ++++++++++++++++++++++++-- crates/sui-crypto/CHANGELOG.md | 3 ++ crates/sui-crypto/Cargo.toml | 8 ++++-- crates/sui-crypto/README.md | 8 ++++++ crates/sui-graphql-client/CHANGELOG.md | 3 ++ crates/sui-graphql-client/README.md | 6 ++++ crates/sui-sdk-types/CHANGELOG.md | 3 ++ crates/sui-sdk-types/Cargo.toml | 4 +-- crates/sui-sdk-types/README.md | 8 ++++++ 9 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 crates/sui-crypto/CHANGELOG.md create mode 100644 crates/sui-crypto/README.md create mode 100644 crates/sui-graphql-client/CHANGELOG.md create mode 100644 crates/sui-sdk-types/CHANGELOG.md create mode 100644 crates/sui-sdk-types/README.md diff --git a/README.md b/README.md index 611d5e7d0..0947ab5d3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,39 @@ # IOTA Sdk -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) +A rust Sdk for integrating with the [Sui blockchain](https://docs.sui.io/). -A WIP from-scratch rust sdk for the IOTA blockchain +> [!NOTE] +> This is project is under development and many features may still be under +> development or missing. + +## Overview + +This repository contains a collection of libraries for integrating with the Sui blockchain. + +A few of the project's high-level goals are as follows: + +* **Be modular** - user's should only need to pay the cost (in terms of dependencies/compilation time) for the features that they use. +* **Be light** - strive to have a minimal dependency footprint. +* **Support developers** - provide all needed types, abstractions and APIs to enable developers to build robust applications on Sui. +* **Support wasm** - where possible, libraries should be usable in wasm environments. + +## Crates + +In an effort to be modular, functionality is split between a number of crates. + +* [`sui-sdk-types`](crates/sui-sdk-types) + [![sui-sdk-types on crates.io](https://img.shields.io/crates/v/sui-sdk-types)](https://crates.io/crates/sui-sdk-types) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-sdk-types) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_sdk_types/) +* [`sui-crypto`](crates/sui-crypto) + [![sui-crypto on crates.io](https://img.shields.io/crates/v/sui-crypto)](https://crates.io/crates/sui-crypto) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-crypto) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_crypto/) +* [`sui-graphql-client`](crates/sui-crypto) + [![sui-graphql-client on crates.io](https://img.shields.io/crates/v/sui-graphql-client)](https://crates.io/crates/sui-graphql-client) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-graphql-client) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui-graphql-client/) + +## License + +This project is available under the terms of the [Apache 2.0 license](LICENSE). diff --git a/crates/sui-crypto/CHANGELOG.md b/crates/sui-crypto/CHANGELOG.md new file mode 100644 index 000000000..31b830538 --- /dev/null +++ b/crates/sui-crypto/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.0.1 - 2024-09-25 + +Initial release diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index 8e527341c..ec33f9ab0 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "sui-crypto" -version = "0.0.0" +version = "0.0.1" authors = ["Brandon Williams "] +repository = "https://github.com/mystenlabs/sui-rust-sdk/" license = "Apache-2.0" edition = "2021" -publish = false +readme = "README.md" +description = "Defines the interface for signing and verying messages in the Sui ecosystem" [package.metadata.docs.rs] # To build locally: @@ -43,7 +45,7 @@ zklogin = [ [dependencies] signature = "2.2" -sui-sdk-types = { version = "0.0.0", path = "../sui-sdk-types", default-features = false } +sui-sdk-types = { version = "0.0.1", path = "../sui-sdk-types", default-features = false } # RNG support rand_core = { version = "0.6.4", optional = true } diff --git a/crates/sui-crypto/README.md b/crates/sui-crypto/README.md new file mode 100644 index 000000000..84a02d5b8 --- /dev/null +++ b/crates/sui-crypto/README.md @@ -0,0 +1,8 @@ +# sui-crypto + +[![sui-crypto on crates.io](https://img.shields.io/crates/v/sui-crypto)](https://crates.io/crates/sui-crypto) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-crypto) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_crypto/) + +The `sui-crypto` crate provides the interface for signing and verifying +transactions and messages in the Sui ecosystem. diff --git a/crates/sui-graphql-client/CHANGELOG.md b/crates/sui-graphql-client/CHANGELOG.md new file mode 100644 index 000000000..31b830538 --- /dev/null +++ b/crates/sui-graphql-client/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.0.1 - 2024-09-25 + +Initial release diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index 316b7ae7a..894c25b0e 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -1,3 +1,9 @@ +# sui-graphql-client + +[![sui-graphql-client on crates.io](https://img.shields.io/crates/v/sui-graphql-client)](https://crates.io/crates/sui-graphql-client) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-graphql-client) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui-graphql-client/) + The Sui GraphQL client is a client for interacting with the Sui blockchain via GraphQL. It provides a set of APIs for querying the blockchain for information such as chain identifier, reference gas price, protocol configuration, service configuration, checkpoint, epoch, diff --git a/crates/sui-sdk-types/CHANGELOG.md b/crates/sui-sdk-types/CHANGELOG.md new file mode 100644 index 000000000..31b830538 --- /dev/null +++ b/crates/sui-sdk-types/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.0.1 - 2024-09-25 + +Initial release diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 4ac193c06..570dbb6ae 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "iota-sdk-types" -version = "0.0.0" +version = "0.0.1" authors = ["IOTA Foundation "] edition = "2021" license = "Apache-2.0" publish = false readme = "README.md" -description = "Sdk for the IOTA Blockchain" +description = "Core types for the IOTA SDK" [package.metadata.docs.rs] # To build locally: diff --git a/crates/sui-sdk-types/README.md b/crates/sui-sdk-types/README.md new file mode 100644 index 000000000..8d4f97dee --- /dev/null +++ b/crates/sui-sdk-types/README.md @@ -0,0 +1,8 @@ +# sui-sdk-types + +[![sui-sdk-types on crates.io](https://img.shields.io/crates/v/sui-sdk-types)](https://crates.io/crates/sui-sdk-types) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-sdk-types) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_sdk_types/) + +The `sui-sdk-types` crate provides the definitions of the core types that are +part of the public API of the Sui blockchain. From 5b616d6a188e4e5b5a01c689e358bb1f67639dc4 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Sun, 29 Sep 2024 08:46:30 -0700 Subject: [PATCH 022/107] chore: update crates to latest versions (#19) --- crates/sui-crypto/Cargo.toml | 16 ++++++++-------- crates/sui-graphql-client/Cargo.toml | 10 +++++----- crates/sui-sdk-types/Cargo.toml | 14 +++++++------- .../src/types/serialization_proptests.rs | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index ec33f9ab0..24372bdb7 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -61,28 +61,28 @@ k256 = { version = "0.13.4", default-features = false, features = ["ecdsa"], opt # zklogin verification support ark-bn254 = { version = "0.4.0", optional = true } -ark-ff = { version = "0.4.1", features = ["asm"], optional = true } +ark-ff = { version = "0.4.2", features = ["asm"], optional = true } ark-groth16 = { version = "0.4.0", default-features = false, optional = true } ark-snark = { version = "0.4.0", optional = true } ark-std = { version = "0.4.0", optional = true } base64ct = { version = "1.6.0", features = ["alloc"], optional = true } -bnum = { version = "0.11.0", optional = true } -itertools = { version = "0.10.5", optional = true } -serde = { version = "1.0.190", optional = true } -serde_derive = { version = "1.0.190", optional = true } -serde_json = { version = "1.0.114", optional = true } +bnum = { version = "0.12.0", optional = true } +itertools = { version = "0.13.0", optional = true } +serde = { version = "1.0.210", optional = true } +serde_derive = { version = "1.0.210", optional = true } +serde_json = { version = "1.0.128", optional = true } [dev-dependencies] bcs = { version = "0.1.6" } hex = "0.4.3" -serde_json = { version = "1.0.114" } +serde_json = { version = "1.0.128" } # proptest support in tests # # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments # see https://github.com/proptest-rs/proptest/pull/270 for more info proptest = { git = "https://github.com/bmwill/proptest.git", rev = "bc36db126183bce18c8bc595f0c0cfeac48b870c", default-features = false, features = ["std"] } -test-strategy = "0.3.1" +test-strategy = "0.4.0" [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index fccb23444..453ec6316 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -9,16 +9,16 @@ readme = "README.md" description = "Sui GraphQL RPC Client for the Sui Blockchain" [dependencies] -anyhow = "1.0.86" -async-trait = "0.1.81" +anyhow = "1.0.8" +async-trait = "0.1.8" base64ct = { version = "1.6.0", features = ["alloc"] } bcs = "0.1.6" chrono = { version = "0.4.38" } -cynic = { version = "3.7.3" } +cynic = { version = "3.8.0" } reqwest = { version = "0.12", features = ["json"] } sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } -tokio = { version = "1.39.2", features = ["full"] } +tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] -cynic-codegen = { version = "3.7.3" } +cynic-codegen = { version = "3.8.0" } diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 570dbb6ae..1eddeae81 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -38,17 +38,17 @@ hash = ["dep:blake2"] [dependencies] base64ct = { version = "1.6.0", features = ["alloc"] } -bnum = "0.11.0" +bnum = "0.12.0" bs58 = "0.5.1" hex = "0.4.3" roaring = { version = "0.10.6", default-features = false } -winnow = "0.6.18" +winnow = "0.6.20" # Serialization and Deserialization support bcs = { version = "0.1.6", optional = true } serde = { version = "1.0.190", optional = true } serde_derive = { version = "1.0.190", optional = true } -serde_json = { version = "1.0.114", optional = true } +serde_json = { version = "1.0.128", optional = true } serde_with = { version = "3.9", default-features = false, features = ["alloc"], optional = true } # JsonSchema definitions for types, useful for generating an OpenAPI Specificaiton. @@ -62,17 +62,17 @@ blake2 = { version = "0.10.6", optional = true } [dev-dependencies] bcs = "0.1.6" -jsonschema = { version = "0.18", default-features = false } -num-bigint = "0.4.4" +jsonschema = { version = "0.20", default-features = false } +num-bigint = "0.4.6" paste = "1.0.15" -serde_json = "1.0.114" +serde_json = "1.0.128" # proptest support in tests # # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments # see https://github.com/proptest-rs/proptest/pull/270 for more info proptest = { git = "https://github.com/bmwill/proptest.git", rev = "bc36db126183bce18c8bc595f0c0cfeac48b870c", default-features = false, features = ["std"] } -test-strategy = "0.3.1" +test-strategy = "0.4" [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/crates/sui-sdk-types/src/types/serialization_proptests.rs b/crates/sui-sdk-types/src/types/serialization_proptests.rs index c6b50cefd..63227e5cc 100644 --- a/crates/sui-sdk-types/src/types/serialization_proptests.rs +++ b/crates/sui-sdk-types/src/types/serialization_proptests.rs @@ -39,7 +39,7 @@ where { let root_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); let schema = serde_json::json!(root_schema); - let compiled = jsonschema::JSONSchema::compile(&schema).unwrap(); + let compiled = jsonschema::Validator::new(&schema).unwrap(); let instance = serde_json::json!(instance); let result = compiled.validate(&instance); From 99abfffaa10a987cfec32e29fd0a3808f2e31cb5 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Sun, 29 Sep 2024 09:03:07 -0700 Subject: [PATCH 023/107] sui-graphql-rpc: add balance function (#13) --- crates/sui-graphql-client/src/lib.rs | 54 ++++++++++++++++--- .../src/query_types/balance.rs | 31 +++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 4 +- 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/balance.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index fd7f417ff..99d770902 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -7,12 +7,13 @@ pub mod query_types; use base64ct::Encoding; use query_types::{ - ChainIdentifierQuery, CheckpointArgs, CheckpointId, CheckpointQuery, CoinMetadata, - CoinMetadataArgs, CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, EventFilter, - EventsQuery, EventsQueryArgs, ObjectFilter, ObjectQuery, ObjectQueryArgs, ObjectsQuery, - ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs, - ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, TransactionBlockQuery, - TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, Uint53, + BalanceArgs, BalanceQuery, ChainIdentifierQuery, CheckpointArgs, CheckpointId, CheckpointQuery, + CoinMetadata, CoinMetadataArgs, CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, + EventFilter, EventsQuery, EventsQueryArgs, ObjectFilter, ObjectQuery, ObjectQueryArgs, + ObjectsQuery, ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, ProtocolConfigs, + ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, + TransactionBlockQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, + Uint53, }; use reqwest::Url; use sui_types::types::{ @@ -194,6 +195,38 @@ impl Client { .ok_or_else(|| Error::msg("No data in response")) } + // =========================================================================== + // Balance API + // =========================================================================== + + /// Get the balance of all the coins owned by address for the provided coin type. + /// Coin type will default to `0x2::coin::Coin<0x2::sui::SUI>` if not provided. + pub async fn balance( + &self, + address: Address, + coin_type: Option<&str>, + ) -> Result, Error> { + let operation = BalanceQuery::build(BalanceArgs { + address: address.into(), + coin_type: coin_type.map(|x| x.to_string()), + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + let total_balance = response + .data + .map(|b| b.owner.and_then(|o| o.balance.map(|b| b.total_balance))) + .ok_or_else(|| Error::msg("No data in response"))? + .flatten() + .map(|x| x.0.parse::()) + .transpose() + .map_err(|e| Error::msg(format!("Cannot parse balance into u128: {e}")))?; + Ok(total_balance) + } + // =========================================================================== // Coin API // =========================================================================== @@ -576,6 +609,15 @@ mod tests { assert!(client.set_rpc_server("9125/graphql").is_err()); } + #[tokio::test] + async fn test_balance_query() { + for (n, _) in NETWORKS.iter() { + let client = Client::new(n).unwrap(); + let balance = client.balance("0x1".parse().unwrap(), None).await; + assert!(balance.is_ok(), "Balance query failed for network: {n}"); + } + } + #[tokio::test] async fn test_chain_id() { for (n, id) in NETWORKS.iter() { diff --git a/crates/sui-graphql-client/src/query_types/balance.rs b/crates/sui-graphql-client/src/query_types/balance.rs new file mode 100644 index 000000000..02dfee4f1 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/balance.rs @@ -0,0 +1,31 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use super::SuiAddress; +use crate::query_types::{schema, BigInt}; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "BalanceArgs")] +pub struct BalanceQuery { + #[arguments(address: $address)] + pub owner: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Owner", variables = "BalanceArgs")] +pub struct Owner { + #[arguments(type: $coin_type)] + pub balance: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Balance")] +pub struct Balance { + pub total_balance: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct BalanceArgs { + pub address: SuiAddress, + pub coin_type: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index e45edfa28..34accfd64 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::str::FromStr; +mod balance; mod chain; mod checkpoint; mod coin; @@ -13,6 +14,7 @@ mod service_config; mod transaction; use anyhow::{anyhow, Error}; +pub use balance::{Balance, BalanceArgs, BalanceQuery, Owner}; pub use chain::ChainIdentifierQuery; pub use checkpoint::{CheckpointArgs, CheckpointId, CheckpointQuery}; pub use coin::{CoinMetadata, CoinMetadataArgs, CoinMetadataQuery}; @@ -48,7 +50,7 @@ pub struct BigInt(pub String); #[cynic(graphql_type = "DateTime")] pub struct DateTime(pub String); -#[derive(cynic::Scalar, Debug, Clone)] +#[derive(cynic::Scalar, Debug)] pub struct SuiAddress(pub String); #[derive(cynic::Scalar, Debug, Clone)] From 536efc00fc9128df4744fc31a0242f7e3f46770b Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 30 Sep 2024 07:17:38 -0700 Subject: [PATCH 024/107] sui-graphql-client: add coins function (#12) --- crates/sui-graphql-client/Cargo.toml | 9 ++- crates/sui-graphql-client/src/lib.rs | 115 ++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 3 deletions(-) diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 453ec6316..99d1ec4c3 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -10,13 +10,18 @@ description = "Sui GraphQL RPC Client for the Sui Blockchain" [dependencies] anyhow = "1.0.8" +async-stream = "0.3.5" async-trait = "0.1.8" base64ct = { version = "1.6.0", features = ["alloc"] } bcs = "0.1.6" -chrono = { version = "0.4.38" } -cynic = { version = "3.8.0" } +chrono = "0.4.38" +cynic = "3.8.0" +futures = "0.3.30" reqwest = { version = "0.12", features = ["json"] } sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } +tokio = "1.40.0" + +[dev-dependencies] tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 99d770902..e0449738b 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -17,11 +17,14 @@ use query_types::{ }; use reqwest::Url; use sui_types::types::{ - Address, CheckpointSequenceNumber, CheckpointSummary, Event, Object, SignedTransaction, + framework::Coin, Address, CheckpointSequenceNumber, CheckpointSummary, Event, Object, + SignedTransaction, }; use anyhow::{anyhow, ensure, Error, Result}; use cynic::{serde, GraphQlResponse, Operation, QueryBuilder}; +use futures::Stream; +use std::pin::Pin; const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; @@ -231,6 +234,87 @@ impl Client { // Coin API // =========================================================================== + /// Get the list of coins for the specified address. + /// + /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all + /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. + pub async fn coins( + &self, + owner: Address, + after: Option<&str>, + before: Option<&str>, + first: Option, + last: Option, + coin_type: Option<&str>, + ) -> Result>, Error> { + let response = self + .objects( + after, + before, + Some(ObjectFilter { + type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), + owner: Some(owner.into()), + object_ids: None, + object_keys: None, + }), + first, + last, + ) + .await?; + + Ok(response.map(|x| { + Page::new( + x.page_info, + x.data + .iter() + .flat_map(Coin::try_from_object) + .map(|c| c.into_owned()) + .collect::>(), + ) + })) + } + + /// Stream of coins for the specified address and coin type. + pub fn coins_stream<'a>( + &'a self, + owner: Address, + coin_type: Option<&'a str>, + ) -> Pin> + 'a>> { + Box::pin(async_stream::try_stream! { + let mut after = None; + loop { + let response = self.objects( + after.as_deref(), + None, + Some(ObjectFilter { + type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), + owner: Some(owner.into()), + object_ids: None, + object_keys: None, + }), + None, + None, + ).await?; + + if let Some(page) = response { + for object in page.data { + if let Some(coin) = Coin::try_from_object(&object) { + yield coin.into_owned(); + } + } + + if let Some(end_cursor) = page.page_info.end_cursor { + after = Some(end_cursor); + } else { + break; + } + } else { + break; + } + } + }) + } + pub async fn coin_metadata(&self, coin_type: &str) -> Result, Error> { let operation = CoinMetadataQuery::build(CoinMetadataArgs { coin_type }); let response = self.run_query(&operation).await?; @@ -591,6 +675,8 @@ impl Client { #[cfg(test)] mod tests { + use futures::StreamExt; + use crate::{Client, DEFAULT_LOCAL_HOST, DEVNET_HOST, MAINNET_HOST, TESTNET_HOST}; const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; @@ -783,4 +869,31 @@ mod tests { ); } } + + #[tokio::test] + async fn test_coins_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let coins = client + .coins("0x1".parse().unwrap(), None, None, None, None, None) + .await; + assert!( + coins.is_ok(), + "Coins query failed for network: {n}. Error: {}", + coins.unwrap_err() + ); + } + } + + #[tokio::test] + async fn test_coins_stream() { + let client = Client::new_testnet(); + let mut stream = client.coins_stream("0x1".parse().unwrap(), None); + let mut num_coins = 0; + while let Some(result) = stream.next().await { + assert!(result.is_ok()); + num_coins += 1; + } + assert!(num_coins > 0); + } } From 94d94fc56b04201303a4cb8efb9d293c4f6c85fc Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:13:34 -0700 Subject: [PATCH 025/107] sui-graphql-client: introduce a faucet client (#11) --- crates/sui-graphql-client/Cargo.toml | 3 + crates/sui-graphql-client/README.md | 55 ++++++ crates/sui-graphql-client/src/faucet.rs | 242 ++++++++++++++++++++++++ crates/sui-graphql-client/src/lib.rs | 11 +- 4 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 crates/sui-graphql-client/src/faucet.rs diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 99d1ec4c3..46b95d101 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -18,7 +18,10 @@ chrono = "0.4.38" cynic = "3.8.0" futures = "0.3.30" reqwest = { version = "0.12", features = ["json"] } +serde = { version = "1.0" } +serde_json = {version = "1.0"} sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } +tracing = "0.1.40" tokio = "1.40.0" [dev-dependencies] diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index 894c25b0e..1e6b5e0cb 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -36,6 +36,61 @@ async fn main() -> Result<()> { } ``` +## Requesting gas from the faucet +The client provides an API to request gas from the faucet. The `request_and_wait` function sends a request to the faucet and waits until the transaction is confirmed. The function returns the transaction details if the request is successful. + +### Example for standard devnet/testnet/local networks. +```rust, no_run +use sui_graphql_client::faucet::FaucetClient; +use sui_types::types::Address; + +use anyhow::Result; +use std::str::FromStr; + +#[tokio::main] +async fn main() -> Result<()> { + let address = Address::from_str("SUI_ADDRESS_HERE")?; + // Request gas from the faucet and wait until a coin is received + // As the client is set to devnet, faucet will use the devnet faucet. + let faucet = FaucetClient::devnet().request_and_wait(address).await?; + if let Some(resp) = faucet { + let coins = resp.sent; + for coin in coins { + println!("coin: {:?}", coin); + } + } + + // Request gas from the testnet faucet by explicitly setting the faucet to testnet + let faucet_testnet = FaucetClient::testnet().request_and_wait(address).await?; + Ok(()) +} +``` + +### Example for custom faucet service. +Note that this [`FaucetClient`] is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.sui.io`). +```rust, no_run +use sui_graphql_client::faucet::FaucetClient; +use sui_types::types::Address; + +use anyhow::Result; +use std::str::FromStr; + +#[tokio::main] +async fn main() -> Result<()> { + let address = Address::from_str("SUI_ADDRESS_HERE")?; + // Request gas from the faucet and wait until a coin is received + // As the client is set to devnet, faucet will use the devnet faucet. + let faucet = FaucetClient::new("https://myfaucet_testnet.com").request_and_wait(address).await?; + if let Some(resp) = faucet { + let coins = resp.sent; + for coin in coins { + println!("coin: {:?}", coin); + } + } + Ok(()) +} +``` + ## Custom Queries There are several options for running custom queries. 1) Use a GraphQL client library of your choosing. diff --git a/crates/sui-graphql-client/src/faucet.rs b/crates/sui-graphql-client/src/faucet.rs new file mode 100644 index 000000000..8e1610fdb --- /dev/null +++ b/crates/sui-graphql-client/src/faucet.rs @@ -0,0 +1,242 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use sui_types::types::{Address, ObjectId, TransactionDigest}; + +use anyhow::{anyhow, bail}; +use reqwest::{StatusCode, Url}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use std::time::Duration; +use tracing::{error, info}; + +pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.sui.io"; +pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.sui.io"; +pub const FAUCET_LOCAL_HOST: &str = "http://localhost:9123"; + +const FAUCET_REQUEST_TIMEOUT: Duration = Duration::from_secs(120); +const FAUCET_POLL_INTERVAL: Duration = Duration::from_secs(2); + +pub struct FaucetClient { + faucet_url: Url, + inner: reqwest::Client, +} + +#[derive(serde::Deserialize)] +struct FaucetResponse { + task: Option, + error: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +struct BatchStatusFaucetResponse { + pub status: Option, + pub error: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +#[serde(rename_all = "UPPERCASE")] +pub enum BatchSendStatusType { + Inprogress, + Succeeded, + Discarded, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct BatchSendStatus { + pub status: BatchSendStatusType, + pub transferred_gas_objects: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FaucetReceipt { + pub sent: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct BatchFaucetReceipt { + pub task: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "camelCase")] +pub struct CoinInfo { + pub amount: u64, + pub id: ObjectId, + pub transfer_tx_digest: TransactionDigest, +} + +impl FaucetClient { + /// Construct a new `FaucetClient` with the given faucet service URL. This [`FaucetClient`] + /// expects that the service provides two endpoints: /v1/gas and /v1/status. As such, do not + /// provide the request endpoint, just the top level service endpoint. + /// + /// - /v1/gas is used to request gas + /// - /v1/status/taks-uuid is used to check the status of the request + pub fn new(faucet_url: &str) -> Self { + let inner = reqwest::Client::new(); + let faucet_url = Url::parse(faucet_url).expect("Invalid faucet URL"); + FaucetClient { faucet_url, inner } + } + + /// Set to local faucet. + pub fn local() -> Self { + Self { + faucet_url: Url::parse(FAUCET_LOCAL_HOST).expect("Invalid faucet URL"), + inner: reqwest::Client::new(), + } + } + + /// Set to devnet faucet. + pub fn devnet() -> Self { + Self { + faucet_url: Url::parse(FAUCET_DEVNET_HOST).expect("Invalid faucet URL"), + inner: reqwest::Client::new(), + } + } + + /// Set to testnet faucet. + pub fn testnet() -> Self { + Self { + faucet_url: Url::parse(FAUCET_TESTNET_HOST).expect("Invalid faucet URL"), + inner: reqwest::Client::new(), + } + } + + /// Request gas from the faucet. Note that this will return the UUID of the request and not + /// wait until the token is received. Use `request_and_wait` to wait for the token. + pub async fn request(&self, address: Address) -> Result, anyhow::Error> { + self.request_impl(address).await + } + + /// Internal implementation of a faucet request. It returns the task Uuid as a String. + async fn request_impl(&self, address: Address) -> Result, anyhow::Error> { + let address = address.to_string(); + let json_body = json![{ + "FixedAmountRequest": { + "recipient": &address + } + }]; + let url = format!("{}v1/gas", self.faucet_url); + info!( + "Requesting gas from faucet for address {} : {}", + address, url + ); + let resp = self + .inner + .post(url) + .header("content-type", "application/json") + .json(&json_body) + .send() + .await?; + match resp.status() { + StatusCode::ACCEPTED | StatusCode::CREATED => { + let faucet_resp: FaucetResponse = resp.json().await?; + + if let Some(err) = faucet_resp.error { + error!("Faucet request was unsuccessful: {err}"); + bail!("Faucet request was unsuccessful: {err}") + } else { + info!("Request succesful: {:?}", faucet_resp.task); + Ok(faucet_resp.task) + } + } + StatusCode::TOO_MANY_REQUESTS => { + error!("Faucet service received too many requests from this IP address."); + bail!("Faucet service received too many requests from this IP address. Please try again after 60 minutes."); + } + StatusCode::SERVICE_UNAVAILABLE => { + error!("Faucet service is currently overloaded or unavailable."); + bail!("Faucet service is currently overloaded or unavailable. Please try again later."); + } + status_code => { + error!("Faucet request was unsuccessful: {status_code}"); + bail!("Faucet request was unsuccessful: {status_code}"); + } + } + } + + /// Request gas from the faucet and wait until the request is completed and token is + /// transferred. Returns `FaucetReceipt` if the request is successful, which contains the list + /// of tokens transferred, and the transaction digest. + /// + /// Note that the faucet is heavily rate-limited, so calling repeatedly the faucet would likely + /// result in a 429 code or 502 code. + pub async fn request_and_wait( + &self, + address: Address, + ) -> Result, anyhow::Error> { + let request_id = self.request(address).await?; + if let Some(request_id) = request_id { + let poll_response = tokio::time::timeout(FAUCET_REQUEST_TIMEOUT, async { + let mut interval = tokio::time::interval(FAUCET_POLL_INTERVAL); + loop { + interval.tick().await; + info!("Polling faucet request status: {request_id}"); + let req = self.request_status(request_id.clone()).await; + + if let Ok(Some(poll_response)) = req { + match poll_response.status { + BatchSendStatusType::Succeeded => { + info!("Faucet request {request_id} succeeded"); + break Ok(poll_response); + } + BatchSendStatusType::Discarded => { + break Ok(BatchSendStatus { + status: BatchSendStatusType::Discarded, + transferred_gas_objects: None, + }); + } + BatchSendStatusType::Inprogress => { + continue; + } + } + } else if let Some(err) = req.err() { + error!("Faucet request {request_id} failed. Error: {:?}", err); + break Err(anyhow!( + "Faucet request {request_id} failed. Error: {:?}", + err + )); + } + } + }) + .await + .map_err(|_| { + error!( + "Faucet request {request_id} timed out. Timeout set to {} seconds", + FAUCET_REQUEST_TIMEOUT.as_secs() + ); + anyhow!("Faucet request timed out") + })??; + Ok(poll_response.transferred_gas_objects) + } else { + Ok(None) + } + } + + /// Check the faucet request status. + /// + /// Possible statuses are defined in: [`BatchSendStatusType`] + pub async fn request_status( + &self, + id: String, + ) -> Result, anyhow::Error> { + let status_url = format!("{}v1/status/{}", self.faucet_url, id); + info!("Checking status of faucet request: {status_url}"); + let response = self.inner.get(&status_url).send().await?; + if response.status() == StatusCode::TOO_MANY_REQUESTS { + bail!("Cannot fetch request status due to too many requests from this IP address."); + } else if response.status() == StatusCode::BAD_GATEWAY { + bail!("Cannot fetch request status due to a bad gateway.") + } + let json = response + .json::() + .await + .map_err(|e| { + error!("Failed to parse faucet response: {:?}", e); + anyhow!("Failed to parse faucet response: {:?}", e) + })?; + Ok(json.status) + } +} diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index e0449738b..39dc1215a 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -3,6 +3,7 @@ #![doc = include_str!("../README.md")] +pub mod faucet; pub mod query_types; use base64ct::Encoding; @@ -29,7 +30,7 @@ use std::pin::Pin; const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; -const DEFAULT_LOCAL_HOST: &str = "http://localhost:9125/graphql"; +const LOCAL_HOST: &str = "http://localhost:9125/graphql"; static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); #[derive(Debug)] @@ -98,7 +99,7 @@ impl Client { /// Create a new GraphQL client connected to the `localhost` GraphQL server: /// {DEFAULT_LOCAL_HOST}. pub fn new_localhost() -> Self { - Self::new(DEFAULT_LOCAL_HOST).expect("Invalid localhost URL") + Self::new(LOCAL_HOST).expect("Invalid localhost URL") } /// Set the server address for the GraphQL GraphQL client. It should be a valid URL with a host and @@ -677,7 +678,7 @@ impl Client { mod tests { use futures::StreamExt; - use crate::{Client, DEFAULT_LOCAL_HOST, DEVNET_HOST, MAINNET_HOST, TESTNET_HOST}; + use crate::{Client, DEVNET_HOST, LOCAL_HOST, MAINNET_HOST, TESTNET_HOST}; const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; #[test] @@ -688,8 +689,8 @@ mod tests { assert_eq!(client.rpc_server(), TESTNET_HOST); client.set_rpc_server(DEVNET_HOST).unwrap(); assert_eq!(client.rpc_server(), DEVNET_HOST); - client.set_rpc_server(DEFAULT_LOCAL_HOST).unwrap(); - assert_eq!(client.rpc_server(), DEFAULT_LOCAL_HOST); + client.set_rpc_server(LOCAL_HOST).unwrap(); + assert_eq!(client.rpc_server(), LOCAL_HOST); assert!(client.set_rpc_server("localhost:9125/graphql").is_ok()); assert!(client.set_rpc_server("9125/graphql").is_err()); From f13e2868595ffcfe282f63a8f506db2b151436f6 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 06:39:27 -0700 Subject: [PATCH 026/107] sui-graphql-client: use 0x0 as sender for event if none (#20) --- crates/sui-graphql-client/src/lib.rs | 35 ++++--- .../src/query_types/events.rs | 98 +++++++++++++++++-- 2 files changed, 114 insertions(+), 19 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 39dc1215a..00bde3522 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -428,40 +428,34 @@ impl Client { pub async fn events( &self, + filter: Option, after: Option, before: Option, - filter: Option, first: Option, last: Option, ) -> Result>, Error> { let operation = EventsQuery::build(EventsQueryArgs { + filter, after, before, - filter, first, last, }); + let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { return Err(Error::msg(format!("{:?}", errors))); } - // TODO bcs from bytes into Event fails due to custom type parser error - // called `Result::unwrap()` on an `Err` value: Custom("TypeParseError") if let Some(events) = response.data { let ec = events.events; let page_info = ec.page_info; let nodes = ec .nodes - .iter() - .map(|e| base64ct::Base64::decode_vec(e.bcs.0.as_str())) - .collect::, base64ct::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode Base64 event bcs bytes: {e}",)))? - .iter() - .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Event: {e}",)))?; + .into_iter() + .map(Event::try_from) + .collect::, _>>()?; Ok(Some(Page::new(page_info, nodes))) } else { @@ -831,6 +825,23 @@ mod tests { } } + #[tokio::test] + async fn test_events_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let events = client.events(None, None, None, None, Some(10)).await; + assert!( + events.is_ok(), + "Events query failed for network: {n}. Error: {}", + events.unwrap_err() + ); + assert!( + events.unwrap().is_some(), + "Events query returned no data for network: {n}" + ); + } + } + #[tokio::test] async fn test_objects_query() { for (n, _) in NETWORKS { diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index 288cd27fc..93a6cc7dc 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -1,6 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + +use base64ct::Encoding; +use sui_types::types::{Identifier, ObjectId}; + use crate::query_types::{schema, Base64, PageInfo, SuiAddress}; // =========================================================================== @@ -20,9 +25,9 @@ pub struct EventsQuery { #[derive(cynic::QueryVariables, Debug)] pub struct EventsQueryArgs { + pub filter: Option, pub after: Option, pub before: Option, - pub filter: Option, pub first: Option, pub last: Option, } @@ -38,12 +43,6 @@ pub struct EventConnection { pub nodes: Vec, } -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Event")] -pub struct Event { - pub bcs: Base64, -} - #[derive(cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "EventFilter")] pub struct EventFilter { @@ -52,3 +51,88 @@ pub struct EventFilter { pub sender: Option, pub transaction_digest: Option, } + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Event")] +pub struct Event { + #[cynic(rename = "type")] + pub type_: MoveType, + pub sending_module: Option, + pub sender: Option
, + pub bcs: Base64, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveModule")] +pub struct MoveModule { + pub name: String, + pub package: MovePackage, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MovePackage")] +pub struct MovePackage { + pub address: SuiAddress, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveType")] +pub struct MoveType { + pub repr: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Address")] +pub struct Address { + pub address: SuiAddress, +} + +#[derive(cynic::Scalar, Debug, Clone)] +pub struct MoveTypeLayout(pub String); + +impl TryFrom for sui_types::types::Event { + type Error = anyhow::Error; + + fn try_from(value: Event) -> Result { + let Event { + type_, + sending_module, + sender, + bcs, + } = value; + + let type_ = if let Some(t) = type_ + .repr + .map(|layout| sui_types::types::StructTag::from_str(&layout)) + .transpose() + .map_err(|e| anyhow::anyhow!("Invalid struct tag in event: {}", e))? + { + t + } else { + return Err(anyhow::anyhow!("Missing struct tag in event")); + }; + + let (package_id, module) = sending_module + .map(|module| (module.package.address, module.name)) + .ok_or_else(|| anyhow::anyhow!("Missing sending module in event"))?; + let package_id = ObjectId::from_str(&package_id.0)?; + let module = Identifier::from_str(&module)?; + + let sender = sender + .map(|x| x.address) + .unwrap_or_else(|| SuiAddress("0x0".to_string())) + .try_into() + .map_err(|e| anyhow::anyhow!("Invalid sender address in event: {}", e))?; + + let contents = base64ct::Base64::decode_vec(&bcs.0) + .map_err(|_| anyhow::anyhow!("Invalid base64 in event"))?; + + Ok(Self { + package_id, + module, + sender, + type_, + contents, + }) + } +} From 3918631f150a38a87131b50129dfeb5322f750f6 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 06:40:15 -0700 Subject: [PATCH 027/107] sui-graphql-client: add method for querying total supply of a coin (#17) --- crates/sui-graphql-client/src/lib.rs | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 00bde3522..b1c3eb9b5 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -316,6 +316,7 @@ impl Client { }) } + /// Get the coin metadata for the coin type. pub async fn coin_metadata(&self, coin_type: &str) -> Result, Error> { let operation = CoinMetadataQuery::build(CoinMetadataArgs { coin_type }); let response = self.run_query(&operation).await?; @@ -327,6 +328,16 @@ impl Client { Ok(response.data.and_then(|x| x.coin_metadata)) } + /// Get total supply for the coin type. + pub async fn total_supply(&self, coin_type: &str) -> Result, Error> { + let coin_metadata = self.coin_metadata(coin_type).await?; + + coin_metadata + .and_then(|c| c.supply) + .map(|c| c.try_into()) + .transpose() + } + // =========================================================================== // Checkpoints API // =========================================================================== @@ -908,4 +919,22 @@ mod tests { } assert!(num_coins > 0); } + + #[tokio::test] + async fn test_total_supply() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let ts = client.total_supply("0x2::sui::SUI").await; + assert!( + ts.is_ok(), + "Total supply query failed for network: {n}. Error: {}", + ts.unwrap_err() + ); + assert_eq!( + ts.unwrap().unwrap(), + 10_000_000_000, + "Total supply mismatch for network: {n}" + ); + } + } } From 0466c3321ac7067324a4f2b992890f1a804f3a9e Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 06:48:36 -0700 Subject: [PATCH 028/107] sui-graphql-client: add execute transaction query (#22) --- crates/sui-graphql-client/src/lib.rs | 46 ++++++++++++++++--- .../src/query_types/execute_tx.rs | 34 ++++++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 2 + 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/execute_tx.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index b1c3eb9b5..3c08a90d4 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -10,20 +10,20 @@ use base64ct::Encoding; use query_types::{ BalanceArgs, BalanceQuery, ChainIdentifierQuery, CheckpointArgs, CheckpointId, CheckpointQuery, CoinMetadata, CoinMetadataArgs, CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, - EventFilter, EventsQuery, EventsQueryArgs, ObjectFilter, ObjectQuery, ObjectQueryArgs, - ObjectsQuery, ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, ProtocolConfigs, - ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, - TransactionBlockQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, - Uint53, + EventFilter, EventsQuery, EventsQueryArgs, ExecuteTransactionArgs, ExecuteTransactionQuery, + ObjectFilter, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, PageInfo, + ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, + TransactionBlockArgs, TransactionBlockQuery, TransactionBlocksQuery, + TransactionBlocksQueryArgs, TransactionsFilter, Uint53, }; use reqwest::Url; use sui_types::types::{ framework::Coin, Address, CheckpointSequenceNumber, CheckpointSummary, Event, Object, - SignedTransaction, + SignedTransaction, Transaction, TransactionEffects, UserSignature, }; use anyhow::{anyhow, ensure, Error, Result}; -use cynic::{serde, GraphQlResponse, Operation, QueryBuilder}; +use cynic::{serde, GraphQlResponse, MutationBuilder, Operation, QueryBuilder}; use futures::Stream; use std::pin::Pin; @@ -677,6 +677,38 @@ impl Client { Ok(None) } } + + /// Execute a transaction. + pub async fn execute_tx( + &self, + signatures: Vec, + tx: &Transaction, + ) -> Result, Error> { + let operation = ExecuteTransactionQuery::build(ExecuteTransactionArgs { + signatures: signatures.iter().map(|s| s.to_base64()).collect(), + tx_bytes: base64ct::Base64::encode_string(bcs::to_bytes(tx).unwrap().as_ref()), + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(data) = response.data { + let result = data.execute_transaction_block; + let bcs = + base64ct::Base64::decode_vec(result.effects.bcs.0.as_str()).map_err(|_| { + Error::msg("Cannot decode bcs bytes from Base64 for transaction effects") + })?; + let effects: TransactionEffects = bcs::from_bytes(&bcs) + .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; + + Ok(Some(effects)) + } else { + Ok(None) + } + } } #[cfg(test)] diff --git a/crates/sui-graphql-client/src/query_types/execute_tx.rs b/crates/sui-graphql-client/src/query_types/execute_tx.rs new file mode 100644 index 000000000..aa0f80bf3 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/execute_tx.rs @@ -0,0 +1,34 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::{schema, Base64}; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Mutation", + variables = "ExecuteTransactionArgs" +)] +pub struct ExecuteTransactionQuery { + #[arguments(signatures: $signatures, txBytes: $tx_bytes)] + pub execute_transaction_block: ExecutionResult, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct ExecuteTransactionArgs { + pub signatures: Vec, + pub tx_bytes: String, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ExecutionResult")] +pub struct ExecutionResult { + pub errors: Option>, + pub effects: TransactionBlockEffects, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlockEffects")] +pub struct TransactionBlockEffects { + pub bcs: Base64, +} diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 34accfd64..0dbcb8a24 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -8,6 +8,7 @@ mod checkpoint; mod coin; mod epoch; mod events; +mod execute_tx; mod object; mod protocol_config; mod service_config; @@ -20,6 +21,7 @@ pub use checkpoint::{CheckpointArgs, CheckpointId, CheckpointQuery}; pub use coin::{CoinMetadata, CoinMetadataArgs, CoinMetadataQuery}; pub use epoch::{Epoch, EpochSummaryArgs, EpochSummaryQuery}; pub use events::{Event, EventConnection, EventFilter, EventsQuery, EventsQueryArgs}; +pub use execute_tx::{ExecuteTransactionArgs, ExecuteTransactionQuery, ExecutionResult}; pub use object::{ ObjectFilter, ObjectKey, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, }; From 622eb29d0501cd32fb58c4c71617e361f4a416f7 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:40:30 -0700 Subject: [PATCH 029/107] sui-graphql-client: ignore events test due to graphql server failures (#26) --- crates/sui-graphql-client/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 3c08a90d4..f59ad0ac8 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -869,6 +869,7 @@ mod tests { } #[tokio::test] + #[ignore] async fn test_events_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); From 8c21ee28383b51f9afe7e9c6128f0723fe5a6e32 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:03:08 -0700 Subject: [PATCH 030/107] sui-graphql-client: add active validators query function (#21) --- crates/sui-graphql-client/src/lib.rs | 73 +++++++++- .../src/query_types/active_validators.rs | 127 ++++++++++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 31 ++++- 3 files changed, 219 insertions(+), 12 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/active_validators.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index f59ad0ac8..42ee2a133 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -8,13 +8,14 @@ pub mod query_types; use base64ct::Encoding; use query_types::{ - BalanceArgs, BalanceQuery, ChainIdentifierQuery, CheckpointArgs, CheckpointId, CheckpointQuery, - CoinMetadata, CoinMetadataArgs, CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, - EventFilter, EventsQuery, EventsQueryArgs, ExecuteTransactionArgs, ExecuteTransactionQuery, - ObjectFilter, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, PageInfo, - ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, - TransactionBlockArgs, TransactionBlockQuery, TransactionBlocksQuery, - TransactionBlocksQueryArgs, TransactionsFilter, Uint53, + ActiveValidatorsArgs, ActiveValidatorsQuery, BalanceArgs, BalanceQuery, ChainIdentifierQuery, + CheckpointArgs, CheckpointId, CheckpointQuery, CoinMetadata, CoinMetadataArgs, + CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, EventFilter, EventsQuery, + EventsQueryArgs, ExecuteTransactionArgs, ExecuteTransactionQuery, ObjectFilter, ObjectQuery, + ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, + ProtocolConfigs, ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, + TransactionBlockQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, + Uint53, Validator, }; use reqwest::Url; use sui_types::types::{ @@ -199,6 +200,46 @@ impl Client { .ok_or_else(|| Error::msg("No data in response")) } + /// Get the list of active validators for the provided epoch, including related metadata. + /// If no epoch is provided, it will return the active validators for the current epoch. + pub async fn active_validators( + &self, + epoch: Option, + after: Option, + before: Option, + first: Option, + last: Option, + ) -> Result>, Error> { + let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { + id: epoch.map(Uint53), + after, + before, + first, + last, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(validators) = response + .data + .and_then(|d| d.epoch) + .and_then(|v| v.validator_set) + { + let page_info = validators.active_validators.page_info; + let nodes = validators + .active_validators + .nodes + .into_iter() + .collect::>(); + Ok(Some(Page::new(page_info, nodes))) + } else { + Ok(None) + } + } + // =========================================================================== // Balance API // =========================================================================== @@ -794,6 +835,24 @@ mod tests { } } + #[tokio::test] + async fn test_active_validators() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let av = client.active_validators(None, None, None, None, None).await; + assert!( + av.is_ok(), + "Active validators query failed for network: {n}. Error: {}", + av.unwrap_err() + ); + + assert!( + av.unwrap().is_some(), + "Active validators query returned None for network: {n}" + ); + } + } + #[tokio::test] async fn test_coin_metadata_query() { for (n, _) in NETWORKS { diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs new file mode 100644 index 000000000..ebf2ac66a --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -0,0 +1,127 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::{ + schema, Address, Base64, BigInt, MoveObject, PageInfo, SuiAddress, Uint53, +}; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "ActiveValidatorsArgs" +)] +pub struct ActiveValidatorsQuery { + #[arguments(id: $id)] + pub epoch: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct ActiveValidatorsArgs { + pub id: Option, + pub after: Option, + pub before: Option, + pub first: Option, + pub last: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Epoch", + variables = "ActiveValidatorsArgs" +)] +pub struct EpochValidator { + pub validator_set: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "ValidatorSet", + variables = "ActiveValidatorsArgs" +)] +pub struct ValidatorSet { + #[arguments(after: $after, before: $before, first: $first, last: $last)] + pub active_validators: ValidatorConnection, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ValidatorConnection")] +pub struct ValidatorConnection { + pub page_info: PageInfo, + pub nodes: Vec, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Validator")] +pub struct Validator { + /// The APY of this validator in basis points. + /// To get the APY in percentage, divide by 100. + pub apy: Option, + /// The validator's address. + pub address: Address, + /// The fee charged by the validator for staking services. + pub commission_rate: Option, + /// Validator's credentials. + pub credentials: Option, + /// Validator's description. + pub description: Option, + /// Number of exchange rates in the table. + pub exchange_rates_size: Option, + /// The reference gas price for this epoch. + pub gas_price: Option, + /// Validator's name. + pub name: Option, + /// Validator's url containing their custom image. + pub image_url: Option, + /// The proposed next epoch fee for the validator's staking services. + pub next_epoch_commission_rate: Option, + /// Validator's credentials for the next epoch. + pub next_epoch_credentials: Option, + /// The validator's gas price quote for the next epoch. + pub next_epoch_gas_price: Option, + /// The total number of SUI tokens in this pool plus + /// the pending stake amount for this epoch. + pub next_epoch_stake: Option, + /// The validator's current valid `Cap` object. Validators can delegate + /// the operation ability to another address. The address holding this `Cap` object + /// can then update the reference gas price and tallying rule on behalf of the validator. + pub operation_cap: Option, + /// Pending pool token withdrawn during the current epoch, emptied at epoch boundaries. + pub pending_pool_token_withdraw: Option, + /// Pending stake amount for this epoch. + pub pending_stake: Option, + /// Pending stake withdrawn during the current epoch, emptied at epoch boundaries. + pub pending_total_sui_withdraw: Option, + /// Total number of pool tokens issued by the pool. + pub pool_token_balance: Option, + /// Validator's homepage URL. + pub project_url: Option, + /// The epoch stake rewards will be added here at the end of each epoch. + pub rewards_pool: Option, + /// The epoch at which this pool became active. + pub staking_pool_activation_epoch: Option, + /// The ID of this validator's `0x3::staking_pool::StakingPool`. + pub staking_pool_id: SuiAddress, + /// The total number of SUI tokens in this pool. + pub staking_pool_sui_balance: Option, + /// The voting power of this validator in basis points (e.g., 100 = 1% voting power). + pub voting_power: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "ValidatorCredentials")] +#[allow(non_snake_case)] +/// The credentials related fields associated with a validator. +pub struct ValidatorCredentials { + pub protocol_pub_key: Option, + pub network_pub_key: Option, + pub worker_pub_key: Option, + pub proof_of_possession: Option, + pub net_address: Option, + // TODO need to fix this in the graphQL schema ugh. p2P -> p2p + // pub p2P_address: Option, + pub primary_address: Option, + pub worker_address: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 0dbcb8a24..b3818a03b 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::str::FromStr; +mod active_validators; mod balance; mod chain; mod checkpoint; @@ -14,6 +15,10 @@ mod protocol_config; mod service_config; mod transaction; +pub use active_validators::{ + ActiveValidatorsArgs, ActiveValidatorsQuery, EpochValidator, Validator, ValidatorConnection, + ValidatorSet, +}; use anyhow::{anyhow, Error}; pub use balance::{Balance, BalanceArgs, BalanceQuery, Owner}; pub use chain::ChainIdentifierQuery; @@ -27,7 +32,7 @@ pub use object::{ }; pub use protocol_config::{ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs}; pub use service_config::{Feature, ServiceConfig, ServiceConfigQuery}; -use sui_types::types::Address; +use sui_types::types::Address as NativeAddress; pub use transaction::{ TransactionBlockArgs, TransactionBlockQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, @@ -59,6 +64,22 @@ pub struct SuiAddress(pub String); #[cynic(graphql_type = "UInt53")] pub struct Uint53(pub u64); +// =========================================================================== +// Types used in several queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Address")] +pub struct Address { + pub address: SuiAddress, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveObject")] +pub struct MoveObject { + pub bcs: Option, +} + // =========================================================================== // Utility Types // =========================================================================== @@ -94,17 +115,17 @@ impl TryFrom for u64 { } } -impl From
for SuiAddress { - fn from(value: Address) -> Self { +impl From for SuiAddress { + fn from(value: NativeAddress) -> Self { SuiAddress(value.to_string()) } } -impl TryFrom for Address { +impl TryFrom for NativeAddress { type Error = anyhow::Error; fn try_from(value: SuiAddress) -> Result { - Address::from_str(&value.0) + NativeAddress::from_str(&value.0) .map_err(|e| Error::msg(format!("Cannot convert SuiAddress into Address: {e}"))) } } From 29d771bcd5981ff8149cf0d359411e35cc9fcc99 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 1 Oct 2024 15:02:30 -0500 Subject: [PATCH 031/107] chore: format with imports_granularity=Item (#27) In an effort to reduce merge conflicts, run rustfmt with 'imports_granularity=Item' to have each imported item be on a separate line. --- .github/workflows/ci.yml | 2 +- Makefile | 6 +- crates/sui-crypto/src/simple.rs | 6 +- crates/sui-crypto/src/zklogin/mod.rs | 13 ++- crates/sui-crypto/src/zklogin/poseidon/mod.rs | 3 +- crates/sui-crypto/src/zklogin/verify.rs | 4 +- .../examples/custom_query.rs | 8 +- crates/sui-graphql-client/src/faucet.rs | 18 +++-- crates/sui-graphql-client/src/lib.rs | 79 ++++++++++++++----- .../src/query_types/active_validators.rs | 11 ++- .../src/query_types/balance.rs | 3 +- .../src/query_types/checkpoint.rs | 11 ++- .../src/query_types/coin.rs | 4 +- .../src/query_types/epoch.rs | 6 +- .../src/query_types/events.rs | 8 +- .../src/query_types/execute_tx.rs | 3 +- .../sui-graphql-client/src/query_types/mod.rs | 66 +++++++++++----- .../src/query_types/object.rs | 6 +- .../src/query_types/transaction.rs | 5 +- crates/sui-sdk-types/src/hash.rs | 18 +++-- 20 files changed, 205 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f9c43c99..e6fcbe477 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: run: make check-features - name: rustfmt - run: cargo +nightly fmt -- --check + run: make check-fmt - name: clippy run: make clippy diff --git a/Makefile b/Makefile index f4719a44d..dbce9180b 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,11 @@ check-features: .PHONY: check-fmt check-fmt: - cargo fmt -- --check + cargo fmt -- --config imports_granularity=Item --check + +.PHONY: fmt +fmt: + cargo fmt -- --config imports_granularity=Item .PHONY: clippy clippy: diff --git a/crates/sui-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs index f2ac61298..248e7eb68 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/sui-crypto/src/simple.rs @@ -1,6 +1,8 @@ -use crate::{SignatureError, SuiVerifier}; +use crate::SignatureError; +use crate::SuiVerifier; use signature::Verifier; -use sui_sdk_types::types::{SimpleSignature, UserSignature}; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::UserSignature; pub struct SimpleVerifier; diff --git a/crates/sui-crypto/src/zklogin/mod.rs b/crates/sui-crypto/src/zklogin/mod.rs index 69586f7d4..b0a5e77b7 100644 --- a/crates/sui-crypto/src/zklogin/mod.rs +++ b/crates/sui-crypto/src/zklogin/mod.rs @@ -1,9 +1,15 @@ use std::collections::HashMap; -use crate::{SignatureError, SuiVerifier}; +use crate::SignatureError; +use crate::SuiVerifier; use poseidon::POSEIDON; use signature::Verifier; -use sui_sdk_types::types::{Claim, Jwk, JwkId, UserSignature, ZkLoginAuthenticator, ZkLoginInputs}; +use sui_sdk_types::types::Claim; +use sui_sdk_types::types::Jwk; +use sui_sdk_types::types::JwkId; +use sui_sdk_types::types::UserSignature; +use sui_sdk_types::types::ZkLoginAuthenticator; +use sui_sdk_types::types::ZkLoginInputs; mod poseidon; mod verify; @@ -130,7 +136,8 @@ struct JwtHeader { impl JwtHeader { fn from_base64(s: &str) -> Result { - use base64ct::{Base64UrlUnpadded, Encoding}; + use base64ct::Base64UrlUnpadded; + use base64ct::Encoding; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] struct Header { diff --git a/crates/sui-crypto/src/zklogin/poseidon/mod.rs b/crates/sui-crypto/src/zklogin/poseidon/mod.rs index c67d4f817..dd42cec4c 100644 --- a/crates/sui-crypto/src/zklogin/poseidon/mod.rs +++ b/crates/sui-crypto/src/zklogin/poseidon/mod.rs @@ -9,7 +9,8 @@ use ark_bn254::Fr; use ark_ff::fields::Field; use ark_std::str::FromStr; use ark_std::Zero; -use core::ops::{AddAssign, MulAssign}; +use core::ops::AddAssign; +use core::ops::MulAssign; mod constants; diff --git a/crates/sui-crypto/src/zklogin/verify.rs b/crates/sui-crypto/src/zklogin/verify.rs index f958a2474..7fca3daaf 100644 --- a/crates/sui-crypto/src/zklogin/verify.rs +++ b/crates/sui-crypto/src/zklogin/verify.rs @@ -403,7 +403,9 @@ fn big_int_array_to_bits( intended_size: usize, ) -> Result, SignatureError> { use itertools::Itertools; - use std::cmp::Ordering::{Equal, Greater, Less}; + use std::cmp::Ordering::Equal; + use std::cmp::Ordering::Greater; + use std::cmp::Ordering::Less; integers .iter() diff --git a/crates/sui-graphql-client/examples/custom_query.rs b/crates/sui-graphql-client/examples/custom_query.rs index 06b7644a7..97cbf8be5 100644 --- a/crates/sui-graphql-client/examples/custom_query.rs +++ b/crates/sui-graphql-client/examples/custom_query.rs @@ -4,10 +4,10 @@ use anyhow::Result; use cynic::QueryBuilder; -use sui_graphql_client::{ - query_types::{schema, BigInt, Uint53}, - Client, -}; +use sui_graphql_client::query_types::schema; +use sui_graphql_client::query_types::BigInt; +use sui_graphql_client::query_types::Uint53; +use sui_graphql_client::Client; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] diff --git a/crates/sui-graphql-client/src/faucet.rs b/crates/sui-graphql-client/src/faucet.rs index 8e1610fdb..68df43c34 100644 --- a/crates/sui-graphql-client/src/faucet.rs +++ b/crates/sui-graphql-client/src/faucet.rs @@ -1,14 +1,20 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use sui_types::types::{Address, ObjectId, TransactionDigest}; - -use anyhow::{anyhow, bail}; -use reqwest::{StatusCode, Url}; -use serde::{Deserialize, Serialize}; +use sui_types::types::Address; +use sui_types::types::ObjectId; +use sui_types::types::TransactionDigest; + +use anyhow::anyhow; +use anyhow::bail; +use reqwest::StatusCode; +use reqwest::Url; +use serde::Deserialize; +use serde::Serialize; use serde_json::json; use std::time::Duration; -use tracing::{error, info}; +use tracing::error; +use tracing::info; pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.sui.io"; pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.sui.io"; diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 42ee2a133..619f8dbd8 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -7,24 +7,63 @@ pub mod faucet; pub mod query_types; use base64ct::Encoding; -use query_types::{ - ActiveValidatorsArgs, ActiveValidatorsQuery, BalanceArgs, BalanceQuery, ChainIdentifierQuery, - CheckpointArgs, CheckpointId, CheckpointQuery, CoinMetadata, CoinMetadataArgs, - CoinMetadataQuery, EpochSummaryArgs, EpochSummaryQuery, EventFilter, EventsQuery, - EventsQueryArgs, ExecuteTransactionArgs, ExecuteTransactionQuery, ObjectFilter, ObjectQuery, - ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, PageInfo, ProtocolConfigQuery, - ProtocolConfigs, ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, TransactionBlockArgs, - TransactionBlockQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, TransactionsFilter, - Uint53, Validator, -}; +use query_types::ActiveValidatorsArgs; +use query_types::ActiveValidatorsQuery; +use query_types::BalanceArgs; +use query_types::BalanceQuery; +use query_types::ChainIdentifierQuery; +use query_types::CheckpointArgs; +use query_types::CheckpointId; +use query_types::CheckpointQuery; +use query_types::CoinMetadata; +use query_types::CoinMetadataArgs; +use query_types::CoinMetadataQuery; +use query_types::EpochSummaryArgs; +use query_types::EpochSummaryQuery; +use query_types::EventFilter; +use query_types::EventsQuery; +use query_types::EventsQueryArgs; +use query_types::ExecuteTransactionArgs; +use query_types::ExecuteTransactionQuery; +use query_types::ObjectFilter; +use query_types::ObjectQuery; +use query_types::ObjectQueryArgs; +use query_types::ObjectsQuery; +use query_types::ObjectsQueryArgs; +use query_types::PageInfo; +use query_types::ProtocolConfigQuery; +use query_types::ProtocolConfigs; +use query_types::ProtocolVersionArgs; +use query_types::ServiceConfig; +use query_types::ServiceConfigQuery; +use query_types::TransactionBlockArgs; +use query_types::TransactionBlockQuery; +use query_types::TransactionBlocksQuery; +use query_types::TransactionBlocksQueryArgs; +use query_types::TransactionsFilter; +use query_types::Uint53; +use query_types::Validator; use reqwest::Url; -use sui_types::types::{ - framework::Coin, Address, CheckpointSequenceNumber, CheckpointSummary, Event, Object, - SignedTransaction, Transaction, TransactionEffects, UserSignature, -}; - -use anyhow::{anyhow, ensure, Error, Result}; -use cynic::{serde, GraphQlResponse, MutationBuilder, Operation, QueryBuilder}; +use sui_types::types::framework::Coin; +use sui_types::types::Address; +use sui_types::types::CheckpointSequenceNumber; +use sui_types::types::CheckpointSummary; +use sui_types::types::Event; +use sui_types::types::Object; +use sui_types::types::SignedTransaction; +use sui_types::types::Transaction; +use sui_types::types::TransactionEffects; +use sui_types::types::UserSignature; + +use anyhow::anyhow; +use anyhow::ensure; +use anyhow::Error; +use anyhow::Result; +use cynic::serde; +use cynic::GraphQlResponse; +use cynic::MutationBuilder; +use cynic::Operation; +use cynic::QueryBuilder; use futures::Stream; use std::pin::Pin; @@ -756,7 +795,11 @@ impl Client { mod tests { use futures::StreamExt; - use crate::{Client, DEVNET_HOST, LOCAL_HOST, MAINNET_HOST, TESTNET_HOST}; + use crate::Client; + use crate::DEVNET_HOST; + use crate::LOCAL_HOST; + use crate::MAINNET_HOST; + use crate::TESTNET_HOST; const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; #[test] diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs index ebf2ac66a..340cc055c 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -1,9 +1,14 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::{ - schema, Address, Base64, BigInt, MoveObject, PageInfo, SuiAddress, Uint53, -}; +use crate::query_types::schema; +use crate::query_types::Address; +use crate::query_types::Base64; +use crate::query_types::BigInt; +use crate::query_types::MoveObject; +use crate::query_types::PageInfo; +use crate::query_types::SuiAddress; +use crate::query_types::Uint53; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/sui-graphql-client/src/query_types/balance.rs b/crates/sui-graphql-client/src/query_types/balance.rs index 02dfee4f1..99d4d76ed 100644 --- a/crates/sui-graphql-client/src/query_types/balance.rs +++ b/crates/sui-graphql-client/src/query_types/balance.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use super::SuiAddress; -use crate::query_types::{schema, BigInt}; +use crate::query_types::schema; +use crate::query_types::BigInt; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "BalanceArgs")] diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index 380966ef2..9c53beff4 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -4,10 +4,17 @@ use std::str::FromStr; use anyhow::Error; use chrono::DateTime as ChronoDT; +use sui_types::types::CheckpointContentsDigest; +use sui_types::types::CheckpointDigest; +use sui_types::types::CheckpointSummary; use sui_types::types::GasCostSummary as NativeGasCostSummary; -use sui_types::types::{CheckpointContentsDigest, CheckpointDigest, CheckpointSummary}; -use crate::query_types::{schema, Base64, BigInt, DateTime, Epoch, Uint53}; +use crate::query_types::schema; +use crate::query_types::Base64; +use crate::query_types::BigInt; +use crate::query_types::DateTime; +use crate::query_types::Epoch; +use crate::query_types::Uint53; // =========================================================================== // Checkpoint Queries diff --git a/crates/sui-graphql-client/src/query_types/coin.rs b/crates/sui-graphql-client/src/query_types/coin.rs index c699afa3b..4d3e0ea5c 100644 --- a/crates/sui-graphql-client/src/query_types/coin.rs +++ b/crates/sui-graphql-client/src/query_types/coin.rs @@ -25,7 +25,9 @@ pub struct CoinMetadataArgs<'a> { // Types // =========================================================================== -use crate::query_types::{schema, BigInt, Uint53}; +use crate::query_types::schema; +use crate::query_types::BigInt; +use crate::query_types::Uint53; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "CoinMetadata")] diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/sui-graphql-client/src/query_types/epoch.rs index f87574c78..07aebc01a 100644 --- a/crates/sui-graphql-client/src/query_types/epoch.rs +++ b/crates/sui-graphql-client/src/query_types/epoch.rs @@ -1,7 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 // -use crate::query_types::{schema, BigInt, DateTime, SuiAddress, Uint53}; +use crate::query_types::schema; +use crate::query_types::BigInt; +use crate::query_types::DateTime; +use crate::query_types::SuiAddress; +use crate::query_types::Uint53; // =========================================================================== // Epoch Queries diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index 93a6cc7dc..c952a2089 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -4,9 +4,13 @@ use std::str::FromStr; use base64ct::Encoding; -use sui_types::types::{Identifier, ObjectId}; +use sui_types::types::Identifier; +use sui_types::types::ObjectId; -use crate::query_types::{schema, Base64, PageInfo, SuiAddress}; +use crate::query_types::schema; +use crate::query_types::Base64; +use crate::query_types::PageInfo; +use crate::query_types::SuiAddress; // =========================================================================== // Events Queries diff --git a/crates/sui-graphql-client/src/query_types/execute_tx.rs b/crates/sui-graphql-client/src/query_types/execute_tx.rs index aa0f80bf3..a0c4ac5b8 100644 --- a/crates/sui-graphql-client/src/query_types/execute_tx.rs +++ b/crates/sui-graphql-client/src/query_types/execute_tx.rs @@ -1,7 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::{schema, Base64}; +use crate::query_types::schema; +use crate::query_types::Base64; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index b3818a03b..790404316 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -15,28 +15,54 @@ mod protocol_config; mod service_config; mod transaction; -pub use active_validators::{ - ActiveValidatorsArgs, ActiveValidatorsQuery, EpochValidator, Validator, ValidatorConnection, - ValidatorSet, -}; -use anyhow::{anyhow, Error}; -pub use balance::{Balance, BalanceArgs, BalanceQuery, Owner}; +pub use active_validators::ActiveValidatorsArgs; +pub use active_validators::ActiveValidatorsQuery; +pub use active_validators::EpochValidator; +pub use active_validators::Validator; +pub use active_validators::ValidatorConnection; +pub use active_validators::ValidatorSet; +use anyhow::anyhow; +use anyhow::Error; +pub use balance::Balance; +pub use balance::BalanceArgs; +pub use balance::BalanceQuery; +pub use balance::Owner; pub use chain::ChainIdentifierQuery; -pub use checkpoint::{CheckpointArgs, CheckpointId, CheckpointQuery}; -pub use coin::{CoinMetadata, CoinMetadataArgs, CoinMetadataQuery}; -pub use epoch::{Epoch, EpochSummaryArgs, EpochSummaryQuery}; -pub use events::{Event, EventConnection, EventFilter, EventsQuery, EventsQueryArgs}; -pub use execute_tx::{ExecuteTransactionArgs, ExecuteTransactionQuery, ExecutionResult}; -pub use object::{ - ObjectFilter, ObjectKey, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, -}; -pub use protocol_config::{ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs}; -pub use service_config::{Feature, ServiceConfig, ServiceConfigQuery}; +pub use checkpoint::CheckpointArgs; +pub use checkpoint::CheckpointId; +pub use checkpoint::CheckpointQuery; +pub use coin::CoinMetadata; +pub use coin::CoinMetadataArgs; +pub use coin::CoinMetadataQuery; +pub use epoch::Epoch; +pub use epoch::EpochSummaryArgs; +pub use epoch::EpochSummaryQuery; +pub use events::Event; +pub use events::EventConnection; +pub use events::EventFilter; +pub use events::EventsQuery; +pub use events::EventsQueryArgs; +pub use execute_tx::ExecuteTransactionArgs; +pub use execute_tx::ExecuteTransactionQuery; +pub use execute_tx::ExecutionResult; +pub use object::ObjectFilter; +pub use object::ObjectKey; +pub use object::ObjectQuery; +pub use object::ObjectQueryArgs; +pub use object::ObjectsQuery; +pub use object::ObjectsQueryArgs; +pub use protocol_config::ProtocolConfigQuery; +pub use protocol_config::ProtocolConfigs; +pub use protocol_config::ProtocolVersionArgs; +pub use service_config::Feature; +pub use service_config::ServiceConfig; +pub use service_config::ServiceConfigQuery; use sui_types::types::Address as NativeAddress; -pub use transaction::{ - TransactionBlockArgs, TransactionBlockQuery, TransactionBlocksQuery, - TransactionBlocksQueryArgs, TransactionsFilter, -}; +pub use transaction::TransactionBlockArgs; +pub use transaction::TransactionBlockQuery; +pub use transaction::TransactionBlocksQuery; +pub use transaction::TransactionBlocksQueryArgs; +pub use transaction::TransactionsFilter; #[cynic::schema("rpc")] pub mod schema {} diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index 267f2fd85..17cb89e3c 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -1,7 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::{schema, Base64, PageInfo, SuiAddress, Uint53}; +use crate::query_types::schema; +use crate::query_types::Base64; +use crate::query_types::PageInfo; +use crate::query_types::SuiAddress; +use crate::query_types::Uint53; // =========================================================================== // Object(s) Queries diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index fef4c33f8..b26d33fb8 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -1,8 +1,11 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::query_types::schema; use crate::query_types::Base64; -use crate::query_types::{schema, PageInfo, SuiAddress, Uint53}; +use crate::query_types::PageInfo; +use crate::query_types::SuiAddress; +use crate::query_types::Uint53; // =========================================================================== // Transaction Block(s) Queries diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/sui-sdk-types/src/hash.rs index c8fb7412f..96f80b2a2 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/sui-sdk-types/src/hash.rs @@ -163,11 +163,19 @@ impl crate::types::MultisigCommittee { #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod type_digest { use super::Hasher; - use crate::types::{ - CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSummary, Digest, - Object, ObjectDigest, Transaction, TransactionDigest, TransactionEffects, - TransactionEffectsDigest, TransactionEvents, TransactionEventsDigest, - }; + use crate::types::CheckpointContents; + use crate::types::CheckpointContentsDigest; + use crate::types::CheckpointDigest; + use crate::types::CheckpointSummary; + use crate::types::Digest; + use crate::types::Object; + use crate::types::ObjectDigest; + use crate::types::Transaction; + use crate::types::TransactionDigest; + use crate::types::TransactionEffects; + use crate::types::TransactionEffectsDigest; + use crate::types::TransactionEvents; + use crate::types::TransactionEventsDigest; impl Object { pub fn digest(&self) -> ObjectDigest { From d1ddd4a1b25a5316125ea9a852da211f5a09b5b7 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 1 Oct 2024 14:35:36 -0700 Subject: [PATCH 032/107] sui-graphql-client: use Address as scalar for SuiAddress (#28) --- crates/sui-graphql-client/src/lib.rs | 10 +++---- .../src/query_types/active_validators.rs | 6 ++-- .../src/query_types/balance.rs | 4 +-- .../src/query_types/epoch.rs | 10 +++---- .../src/query_types/events.rs | 24 +++++---------- .../sui-graphql-client/src/query_types/mod.rs | 30 +++++-------------- .../src/query_types/object.rs | 10 +++---- .../src/query_types/transaction.rs | 8 ++--- 8 files changed, 38 insertions(+), 64 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 619f8dbd8..07cc610b6 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -291,7 +291,7 @@ impl Client { coin_type: Option<&str>, ) -> Result, Error> { let operation = BalanceQuery::build(BalanceArgs { - address: address.into(), + address, coin_type: coin_type.map(|x| x.to_string()), }); let response = self.run_query(&operation).await?; @@ -334,7 +334,7 @@ impl Client { before, Some(ObjectFilter { type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), - owner: Some(owner.into()), + owner: Some(owner), object_ids: None, object_keys: None, }), @@ -369,7 +369,7 @@ impl Client { None, Some(ObjectFilter { type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), - owner: Some(owner.into()), + owner: Some(owner), object_ids: None, object_keys: None, }), @@ -568,7 +568,7 @@ impl Client { version: Option, ) -> Result, Error> { let operation = ObjectQuery::build(ObjectQueryArgs { - address: address.into(), + address, version: version.map(Uint53), }); @@ -662,7 +662,7 @@ impl Client { /// Return the object's bcs content [`Vec`] based on the provided [`Address`]. pub async fn object_bcs(&self, object_id: Address) -> Result>, Error> { let operation = ObjectQuery::build(ObjectQueryArgs { - address: object_id.into(), + address: object_id, version: None, }); diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs index 340cc055c..396ef6aa6 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -5,9 +5,9 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::BigInt; +use crate::query_types::GQLAddress; use crate::query_types::MoveObject; use crate::query_types::PageInfo; -use crate::query_types::SuiAddress; use crate::query_types::Uint53; #[derive(cynic::QueryFragment, Debug)] @@ -65,7 +65,7 @@ pub struct Validator { /// To get the APY in percentage, divide by 100. pub apy: Option, /// The validator's address. - pub address: Address, + pub address: GQLAddress, /// The fee charged by the validator for staking services. pub commission_rate: Option, /// Validator's credentials. @@ -108,7 +108,7 @@ pub struct Validator { /// The epoch at which this pool became active. pub staking_pool_activation_epoch: Option, /// The ID of this validator's `0x3::staking_pool::StakingPool`. - pub staking_pool_id: SuiAddress, + pub staking_pool_id: Address, /// The total number of SUI tokens in this pool. pub staking_pool_sui_balance: Option, /// The voting power of this validator in basis points (e.g., 100 = 1% voting power). diff --git a/crates/sui-graphql-client/src/query_types/balance.rs b/crates/sui-graphql-client/src/query_types/balance.rs index 99d4d76ed..843e861ac 100644 --- a/crates/sui-graphql-client/src/query_types/balance.rs +++ b/crates/sui-graphql-client/src/query_types/balance.rs @@ -1,9 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use super::SuiAddress; use crate::query_types::schema; use crate::query_types::BigInt; +use crate::Address; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "BalanceArgs")] @@ -27,6 +27,6 @@ pub struct Balance { #[derive(cynic::QueryVariables, Debug)] pub struct BalanceArgs { - pub address: SuiAddress, + pub address: Address, pub coin_type: Option, } diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/sui-graphql-client/src/query_types/epoch.rs index 07aebc01a..bb9595cd3 100644 --- a/crates/sui-graphql-client/src/query_types/epoch.rs +++ b/crates/sui-graphql-client/src/query_types/epoch.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // use crate::query_types::schema; +use crate::query_types::Address; use crate::query_types::BigInt; use crate::query_types::DateTime; -use crate::query_types::SuiAddress; use crate::query_types::Uint53; // =========================================================================== @@ -64,14 +64,14 @@ pub struct Epoch { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ValidatorSet")] pub struct ValidatorSet { - pub inactive_pools_id: Option, + pub inactive_pools_id: Option
, pub inactive_pools_size: Option, - pub pending_active_validators_id: Option, + pub pending_active_validators_id: Option
, pub pending_active_validators_size: Option, pub pending_removals: Option>, - pub staking_pool_mappings_id: Option, + pub staking_pool_mappings_id: Option
, pub staking_pool_mappings_size: Option, pub total_stake: Option, pub validator_candidates_size: Option, - pub validator_candidates_id: Option, + pub validator_candidates_id: Option
, } diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index c952a2089..4bb70b6f6 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -5,12 +5,12 @@ use std::str::FromStr; use base64ct::Encoding; use sui_types::types::Identifier; -use sui_types::types::ObjectId; use crate::query_types::schema; +use crate::query_types::Address; use crate::query_types::Base64; +use crate::query_types::GQLAddress; use crate::query_types::PageInfo; -use crate::query_types::SuiAddress; // =========================================================================== // Events Queries @@ -52,7 +52,7 @@ pub struct EventConnection { pub struct EventFilter { pub emitting_module: Option, pub event_type: Option, - pub sender: Option, + pub sender: Option
, pub transaction_digest: Option, } @@ -62,7 +62,7 @@ pub struct Event { #[cynic(rename = "type")] pub type_: MoveType, pub sending_module: Option, - pub sender: Option
, + pub sender: Option, pub bcs: Base64, } @@ -76,7 +76,7 @@ pub struct MoveModule { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "MovePackage")] pub struct MovePackage { - pub address: SuiAddress, + pub address: Address, } #[derive(cynic::QueryFragment, Debug)] @@ -85,12 +85,6 @@ pub struct MoveType { pub repr: Option, } -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Address")] -pub struct Address { - pub address: SuiAddress, -} - #[derive(cynic::Scalar, Debug, Clone)] pub struct MoveTypeLayout(pub String); @@ -119,14 +113,10 @@ impl TryFrom for sui_types::types::Event { let (package_id, module) = sending_module .map(|module| (module.package.address, module.name)) .ok_or_else(|| anyhow::anyhow!("Missing sending module in event"))?; - let package_id = ObjectId::from_str(&package_id.0)?; + let package_id = package_id.into(); let module = Identifier::from_str(&module)?; - let sender = sender - .map(|x| x.address) - .unwrap_or_else(|| SuiAddress("0x0".to_string())) - .try_into() - .map_err(|e| anyhow::anyhow!("Invalid sender address in event: {}", e))?; + let sender = sender.map(|x| x.address).unwrap_or_else(|| Address::ZERO); let contents = base64ct::Base64::decode_vec(&bcs.0) .map_err(|_| anyhow::anyhow!("Invalid base64 in event"))?; diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 790404316..82b5ba683 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -1,6 +1,5 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; mod active_validators; mod balance; @@ -22,7 +21,6 @@ pub use active_validators::Validator; pub use active_validators::ValidatorConnection; pub use active_validators::ValidatorSet; use anyhow::anyhow; -use anyhow::Error; pub use balance::Balance; pub use balance::BalanceArgs; pub use balance::BalanceQuery; @@ -57,13 +55,15 @@ pub use protocol_config::ProtocolVersionArgs; pub use service_config::Feature; pub use service_config::ServiceConfig; pub use service_config::ServiceConfigQuery; -use sui_types::types::Address as NativeAddress; +use sui_types::types::Address; pub use transaction::TransactionBlockArgs; pub use transaction::TransactionBlockQuery; pub use transaction::TransactionBlocksQuery; pub use transaction::TransactionBlocksQueryArgs; pub use transaction::TransactionsFilter; +use cynic::impl_scalar; + #[cynic::schema("rpc")] pub mod schema {} @@ -71,6 +71,8 @@ pub mod schema {} // Scalars // =========================================================================== +impl_scalar!(Address, schema::SuiAddress); + #[derive(cynic::Scalar, Debug, Clone)] #[cynic(graphql_type = "Base64")] pub struct Base64(pub String); @@ -83,9 +85,6 @@ pub struct BigInt(pub String); #[cynic(graphql_type = "DateTime")] pub struct DateTime(pub String); -#[derive(cynic::Scalar, Debug)] -pub struct SuiAddress(pub String); - #[derive(cynic::Scalar, Debug, Clone)] #[cynic(graphql_type = "UInt53")] pub struct Uint53(pub u64); @@ -96,8 +95,8 @@ pub struct Uint53(pub u64); #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Address")] -pub struct Address { - pub address: SuiAddress, +pub struct GQLAddress { + pub address: Address, } #[derive(cynic::QueryFragment, Debug)] @@ -140,18 +139,3 @@ impl TryFrom for u64 { .map_err(|e| anyhow!("Cannot convert BigInt into u64: {e}")) } } - -impl From for SuiAddress { - fn from(value: NativeAddress) -> Self { - SuiAddress(value.to_string()) - } -} - -impl TryFrom for NativeAddress { - type Error = anyhow::Error; - - fn try_from(value: SuiAddress) -> Result { - NativeAddress::from_str(&value.0) - .map_err(|e| Error::msg(format!("Cannot convert SuiAddress into Address: {e}"))) - } -} diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index 17cb89e3c..6f8b99559 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; +use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::PageInfo; -use crate::query_types::SuiAddress; use crate::query_types::Uint53; // =========================================================================== @@ -31,7 +31,7 @@ pub struct ObjectsQuery { #[derive(cynic::QueryVariables, Debug)] pub struct ObjectQueryArgs { - pub address: SuiAddress, + pub address: Address, pub version: Option, } @@ -59,15 +59,15 @@ pub struct Object { pub struct ObjectFilter<'a> { #[cynic(rename = "type")] pub type_: Option<&'a str>, - pub owner: Option, - pub object_ids: Option>, + pub owner: Option
, + pub object_ids: Option>, pub object_keys: Option>, } #[derive(cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "ObjectKey")] pub struct ObjectKey { - pub object_id: SuiAddress, + pub object_id: Address, pub version: Uint53, } diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index b26d33fb8..2e71d253d 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; +use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::PageInfo; -use crate::query_types::SuiAddress; use crate::query_types::Uint53; // =========================================================================== @@ -79,9 +79,9 @@ pub struct TransactionsFilter { pub kind: Option, pub at_checkpoint: Option, pub before_checkpoint: Option, - pub changed_object: Option, - pub input_object: Option, - pub recv_address: Option, + pub changed_object: Option
, + pub input_object: Option
, + pub recv_address: Option
, } #[derive(cynic::QueryFragment, Debug)] From acab9c39955d69da528f5991776a3b61dee62496 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 1 Oct 2024 20:41:03 -0500 Subject: [PATCH 033/107] sui-crypto: introduce multisig verifier and aggregator (#25) --- crates/sui-crypto/src/lib.rs | 17 + crates/sui-crypto/src/multisig.rs | 442 ++++++++++++++++++ crates/sui-crypto/src/zklogin/mod.rs | 10 + .../src/types/crypto/multisig.rs | 55 ++- .../sui-sdk-types/src/types/crypto/zklogin.rs | 8 + 5 files changed, 526 insertions(+), 6 deletions(-) create mode 100644 crates/sui-crypto/src/multisig.rs diff --git a/crates/sui-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs index 1bdd086c4..81a4b854b 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/sui-crypto/src/lib.rs @@ -44,6 +44,23 @@ pub mod zklogin; )] pub mod simple; +#[cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" +))] +#[cfg_attr( + doc_cfg, + doc(cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" + ))) +)] +pub mod multisig; + pub trait SuiSigner { fn sign_transaction(&self, transaction: &Transaction) -> Result; fn sign_personal_message( diff --git a/crates/sui-crypto/src/multisig.rs b/crates/sui-crypto/src/multisig.rs new file mode 100644 index 000000000..03f068596 --- /dev/null +++ b/crates/sui-crypto/src/multisig.rs @@ -0,0 +1,442 @@ +use crate::SignatureError; +use crate::SuiVerifier; +use crate::Verifier; +use sui_sdk_types::types::MultisigAggregatedSignature; +use sui_sdk_types::types::MultisigCommittee; +use sui_sdk_types::types::MultisigMemberPublicKey; +use sui_sdk_types::types::MultisigMemberSignature; +use sui_sdk_types::types::UserSignature; + +#[derive(Default)] +pub struct MultisigVerifier { + #[cfg(feature = "zklogin")] + zklogin_verifier: Option, +} + +impl MultisigVerifier { + pub fn new() -> Self { + Default::default() + } + + fn verify_member_signature( + &self, + message: &[u8], + member_public_key: &MultisigMemberPublicKey, + signature: &MultisigMemberSignature, + ) -> Result<(), SignatureError> { + match (member_public_key, signature) { + #[cfg(not(feature = "ed25519"))] + (MultisigMemberPublicKey::Ed25519(_), MultisigMemberSignature::Ed25519(_)) => Err( + SignatureError::from_source("support for ed25519 is not enabled"), + ), + #[cfg(feature = "ed25519")] + ( + MultisigMemberPublicKey::Ed25519(ed25519_public_key), + MultisigMemberSignature::Ed25519(ed25519_signature), + ) => crate::ed25519::Ed25519VerifyingKey::new(ed25519_public_key)? + .verify(message, ed25519_signature), + + #[cfg(not(feature = "secp256k1"))] + (MultisigMemberPublicKey::Secp256k1(_), MultisigMemberSignature::Secp256k1(_)) => Err( + SignatureError::from_source("support for secp256k1 is not enabled"), + ), + #[cfg(feature = "secp256k1")] + ( + MultisigMemberPublicKey::Secp256k1(k1_public_key), + MultisigMemberSignature::Secp256k1(k1_signature), + ) => crate::secp256k1::Secp256k1VerifyingKey::new(k1_public_key)? + .verify(message, k1_signature), + + #[cfg(not(feature = "secp256r1"))] + (MultisigMemberPublicKey::Secp256r1(_), MultisigMemberSignature::Secp256r1(_)) => Err( + SignatureError::from_source("support for secp256r1 is not enabled"), + ), + #[cfg(feature = "secp256r1")] + ( + MultisigMemberPublicKey::Secp256r1(r1_public_key), + MultisigMemberSignature::Secp256r1(r1_signature), + ) => crate::secp256r1::Secp256r1VerifyingKey::new(r1_public_key)? + .verify(message, r1_signature), + + #[cfg(not(feature = "zklogin"))] + (MultisigMemberPublicKey::ZkLogin(_), MultisigMemberSignature::ZkLogin(_)) => Err( + SignatureError::from_source("support for zklogin is not enabled"), + ), + #[cfg(feature = "zklogin")] + ( + MultisigMemberPublicKey::ZkLogin(zklogin_identifier), + MultisigMemberSignature::ZkLogin(zklogin_authenticator), + ) => { + let zklogin_verifier = self + .zklogin_verifier() + .ok_or_else(|| SignatureError::from_source("no zklogin verifier provided"))?; + + // verify that the member identifier and the authenticator match + if zklogin_identifier + != &crate::zklogin::zklogin_identifier_from_inputs( + &zklogin_authenticator.inputs, + )? + { + return Err(SignatureError::from_source( + "member zklogin identifier does not match signature", + )); + } + + zklogin_verifier.verify(message, zklogin_authenticator.as_ref()) + } + + _ => Err(SignatureError::from_source( + "member and signature scheme do not match", + )), + } + } +} + +#[cfg(feature = "zklogin")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))] +impl MultisigVerifier { + pub fn with_zklogin_verifier(&mut self, zklogin_verifier: crate::zklogin::ZkloginVerifier) { + self.zklogin_verifier = Some(zklogin_verifier); + } + + pub fn zklogin_verifier(&self) -> Option<&crate::zklogin::ZkloginVerifier> { + self.zklogin_verifier.as_ref() + } + + pub fn zklogin_verifier_mut(&mut self) -> Option<&mut crate::zklogin::ZkloginVerifier> { + self.zklogin_verifier.as_mut() + } +} + +impl Verifier for MultisigVerifier { + fn verify( + &self, + message: &[u8], + signature: &MultisigAggregatedSignature, + ) -> Result<(), SignatureError> { + if !signature.committee().is_valid() { + return Err(SignatureError::from_source("invalid MultisigCommittee")); + } + + if signature.signatures().len() != signature.bitmap().count_ones() as usize { + return Err(SignatureError::from_source( + "number of signatures does not match bitmap", + )); + } + + if signature.signatures().len() > signature.committee().members().len() { + return Err(SignatureError::from_source( + "more signatures than committee members", + )); + } + + let weight = BitmapIndices::new(signature.bitmap()) + .map(|member_idx| { + signature + .committee() + .members() + .get(member_idx as usize) + .ok_or_else(|| SignatureError::from_source("invalid bitmap")) + }) + .zip(signature.signatures()) + .map(|(maybe_member, signature)| { + let member = maybe_member?; + self.verify_member_signature(message, member.public_key(), signature) + .map(|()| member.weight() as u16) + }) + .sum::>()?; + + if weight >= signature.committee().threshold() { + Ok(()) + } else { + Err(SignatureError::from_source( + "signature weight does not exceed threshold", + )) + } + } +} + +impl Verifier for MultisigVerifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Multisig(signature) = signature else { + return Err(SignatureError::from_source("not a multisig signature")); + }; + + self.verify(message, signature) + } +} + +impl SuiVerifier for MultisigVerifier { + fn verify_transaction( + &self, + transaction: &sui_sdk_types::types::Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &sui_sdk_types::types::PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +/// Interpret a bitmap of 01s as a list of indices that is set to 1s. +/// e.g. 22 = 0b10110, then the result is [1, 2, 4]. +struct BitmapIndices { + bitmap: u16, + range: std::ops::Range, +} + +impl BitmapIndices { + pub fn new(bitmap: u16) -> Self { + Self { + bitmap, + range: 0..(u16::BITS as u8), + } + } +} + +impl Iterator for BitmapIndices { + type Item = u8; + + fn next(&mut self) -> Option { + #[allow(clippy::while_let_on_iterator)] + while let Some(i) = self.range.next() { + if self.bitmap & (1 << i) != 0 { + return Some(i); + } + } + + None + } +} + +/// Verifier that will verify all UserSignature variants +#[derive(Default)] +pub struct UserSignatureVerifier { + inner: MultisigVerifier, +} + +impl UserSignatureVerifier { + pub fn new() -> Self { + Default::default() + } +} + +#[cfg(feature = "zklogin")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))] +impl UserSignatureVerifier { + pub fn with_zklogin_verifier(&mut self, zklogin_verifier: crate::zklogin::ZkloginVerifier) { + self.inner.with_zklogin_verifier(zklogin_verifier); + } + + pub fn zklogin_verifier(&self) -> Option<&crate::zklogin::ZkloginVerifier> { + self.inner.zklogin_verifier() + } + + pub fn zklogin_verifier_mut(&mut self) -> Option<&mut crate::zklogin::ZkloginVerifier> { + self.inner.zklogin_verifier_mut() + } +} + +impl Verifier for UserSignatureVerifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + match signature { + UserSignature::Simple(simple_signature) => { + crate::simple::SimpleVerifier.verify(message, simple_signature) + } + UserSignature::Multisig(multisig) => self.inner.verify(message, multisig), + + #[cfg(not(feature = "zklogin"))] + UserSignature::ZkLogin(_) => Err(SignatureError::from_source( + "support for zklogin is not enabled", + )), + #[cfg(feature = "zklogin")] + UserSignature::ZkLogin(zklogin_authenticator) => { + let zklogin_verifier = self + .zklogin_verifier() + .ok_or_else(|| SignatureError::from_source("no zklogin verifier provided"))?; + + zklogin_verifier.verify(message, zklogin_authenticator.as_ref()) + } + + UserSignature::Passkey(_) => Err(SignatureError::from_source( + "unsupported user signature scheme", + )), + } + } +} + +impl SuiVerifier for UserSignatureVerifier { + fn verify_transaction( + &self, + transaction: &sui_sdk_types::types::Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &sui_sdk_types::types::PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} + +pub struct MultisigAggregator { + committee: MultisigCommittee, + signatures: std::collections::BTreeMap, + signed_weight: u16, + message: Vec, + verifier: MultisigVerifier, +} + +impl MultisigAggregator { + pub fn new_with_transaction( + committee: MultisigCommittee, + transaction: &sui_sdk_types::types::Transaction, + ) -> Self { + Self { + committee, + signatures: Default::default(), + signed_weight: 0, + message: transaction.signing_digest().to_vec(), + verifier: Default::default(), + } + } + + pub fn new_with_message( + committee: MultisigCommittee, + message: &sui_sdk_types::types::PersonalMessage<'_>, + ) -> Self { + Self { + committee, + signatures: Default::default(), + signed_weight: 0, + message: message.signing_digest().to_vec(), + verifier: Default::default(), + } + } + + pub fn verifier(&self) -> &MultisigVerifier { + &self.verifier + } + + pub fn verifier_mut(&mut self) -> &mut MultisigVerifier { + &mut self.verifier + } + + pub fn add_signature(&mut self, signature: UserSignature) -> Result<(), SignatureError> { + use std::collections::btree_map::Entry; + + let (public_key, signature) = multisig_pubkey_and_signature_from_user_signature(signature)?; + let member_idx = self + .committee + .members() + .iter() + .position(|member| member.public_key() == &public_key) + .ok_or_else(|| { + SignatureError::from_source( + "provided signature does not belong to committee member", + ) + })?; + + self.verifier() + .verify_member_signature(&self.message, &public_key, &signature)?; + + match self.signatures.entry(member_idx) { + Entry::Vacant(v) => { + v.insert(signature); + } + Entry::Occupied(_) => { + return Err(SignatureError::from_source( + "duplicate signature from same committee member", + )) + } + } + + self.signed_weight += self.committee.members()[member_idx].weight() as u16; + + Ok(()) + } + + pub fn finish(&mut self) -> Result { + if self.signed_weight < self.committee.threshold() { + return Err(SignatureError::from_source( + "insufficient signature weight to reach threshold", + )); + } + + let (signatures, bitmap) = self.signatures.clone().into_iter().fold( + (Vec::new(), 0), + |(mut signatures, mut bitmap), (member_idx, signature)| { + bitmap |= 1 << member_idx; + signatures.push(signature); + (signatures, bitmap) + }, + ); + + Ok(MultisigAggregatedSignature::new( + self.committee.clone(), + signatures, + bitmap, + )) + } +} + +fn multisig_pubkey_and_signature_from_user_signature( + signature: UserSignature, +) -> Result<(MultisigMemberPublicKey, MultisigMemberSignature), SignatureError> { + use sui_sdk_types::types::SimpleSignature; + match signature { + UserSignature::Simple(SimpleSignature::Ed25519 { + signature, + public_key, + }) => Ok(( + MultisigMemberPublicKey::Ed25519(public_key), + MultisigMemberSignature::Ed25519(signature), + )), + UserSignature::Simple(SimpleSignature::Secp256k1 { + signature, + public_key, + }) => Ok(( + MultisigMemberPublicKey::Secp256k1(public_key), + MultisigMemberSignature::Secp256k1(signature), + )), + UserSignature::Simple(SimpleSignature::Secp256r1 { + signature, + public_key, + }) => Ok(( + MultisigMemberPublicKey::Secp256r1(public_key), + MultisigMemberSignature::Secp256r1(signature), + )), + + #[cfg(not(feature = "zklogin"))] + UserSignature::ZkLogin(_) => Err(SignatureError::from_source( + "support for zklogin is not enabled", + )), + #[cfg(feature = "zklogin")] + UserSignature::ZkLogin(zklogin_authenticator) => { + let zklogin_identifier = + crate::zklogin::zklogin_identifier_from_inputs(&zklogin_authenticator.inputs)?; + Ok(( + MultisigMemberPublicKey::ZkLogin(zklogin_identifier), + MultisigMemberSignature::ZkLogin(zklogin_authenticator), + )) + } + + UserSignature::Multisig(_) | UserSignature::Passkey(_) => { + Err(SignatureError::from_source("invalid siganture scheme")) + } + } +} diff --git a/crates/sui-crypto/src/zklogin/mod.rs b/crates/sui-crypto/src/zklogin/mod.rs index b0a5e77b7..aa28c268a 100644 --- a/crates/sui-crypto/src/zklogin/mod.rs +++ b/crates/sui-crypto/src/zklogin/mod.rs @@ -263,3 +263,13 @@ fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result Result { + const ISS: &str = "iss"; + + let iss = verify_extended_claim(&inputs.iss_base64_details, ISS)?; + sui_sdk_types::types::ZkLoginPublicIdentifier::new(iss, inputs.address_seed.clone()) + .ok_or_else(|| SignatureError::from_source("invalid iss")) +} diff --git a/crates/sui-sdk-types/src/types/crypto/multisig.rs b/crates/sui-sdk-types/src/types/crypto/multisig.rs index adf76bedd..ebd1ef4ea 100644 --- a/crates/sui-sdk-types/src/types/crypto/multisig.rs +++ b/crates/sui-sdk-types/src/types/crypto/multisig.rs @@ -8,6 +8,7 @@ pub type WeightUnit = u8; pub type ThresholdUnit = u16; pub type BitmapUnit = u16; +const MAX_COMMITTEE_SIZE: usize = 10; // TODO validate sigs // const MAX_BITMAP_VALUE: BitmapUnit = 0b1111111111; @@ -70,6 +71,35 @@ impl MultisigCommittee { pub fn scheme(&self) -> SignatureScheme { SignatureScheme::Multisig } + + /// Checks if the Committee is valid. + /// + /// A valid committee is one that: + /// - Has a nonzero threshold + /// - Has at least one member + /// - Has at most ten members + /// - No member has weight 0 + /// - the sum of the weights of all members must be larger than the + /// threshold + /// - contains no duplicate members + pub fn is_valid(&self) -> bool { + self.threshold != 0 + && !self.members.is_empty() + && self.members.len() <= MAX_COMMITTEE_SIZE + && !self.members.iter().any(|member| member.weight == 0) + && self + .members + .iter() + .map(|member| member.weight as ThresholdUnit) + .sum::() + >= self.threshold + && !self.members.iter().enumerate().any(|(i, member)| { + self.members + .iter() + .skip(i + 1) + .any(|m| member.public_key == m.public_key) + }) + } } /// The struct that contains signatures and public keys necessary for @@ -79,6 +109,9 @@ impl MultisigCommittee { #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub struct MultisigAggregatedSignature { /// The plain signature encoded with signature scheme. + /// + /// The signatures must be in the same order as they are listed in the + /// committee. #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] signatures: Vec, /// A bitmap that indicates the position of which public key the signature @@ -90,6 +123,19 @@ pub struct MultisigAggregatedSignature { } impl MultisigAggregatedSignature { + pub fn new( + committee: MultisigCommittee, + signatures: Vec, + bitmap: BitmapUnit, + ) -> Self { + Self { + signatures, + bitmap, + legacy_bitmap: None, + committee, + } + } + pub fn signatures(&self) -> &[MultisigMemberSignature] { &self.signatures } @@ -115,12 +161,11 @@ impl Eq for MultisigAggregatedSignature {} #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(test, derive(test_strategy::Arbitrary))] -#[allow(clippy::large_enum_variant)] pub enum MultisigMemberSignature { Ed25519(Ed25519Signature), Secp256k1(Secp256k1Signature), Secp256r1(Secp256r1Signature), - ZkLogin(ZkLoginAuthenticator), + ZkLogin(Box), } #[cfg(feature = "serde")] @@ -363,24 +408,22 @@ mod serialization { } #[derive(serde_derive::Serialize, serde_derive::Deserialize)] - #[allow(clippy::large_enum_variant)] enum MemberSignature { Ed25519(Ed25519Signature), Secp256k1(Secp256k1Signature), Secp256r1(Secp256r1Signature), - ZkLogin(ZkLoginAuthenticator), + ZkLogin(Box), } #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(tag = "scheme", rename_all = "lowercase")] #[serde(rename = "MultisigMemberSignature")] - #[allow(clippy::large_enum_variant)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] enum ReadableMemberSignature { Ed25519 { signature: Ed25519Signature }, Secp256k1 { signature: Secp256k1Signature }, Secp256r1 { signature: Secp256r1Signature }, - ZkLogin(ZkLoginAuthenticator), + ZkLogin(Box), } #[cfg(feature = "schemars")] diff --git a/crates/sui-sdk-types/src/types/crypto/zklogin.rs b/crates/sui-sdk-types/src/types/crypto/zklogin.rs index e1369dee1..f670faa19 100644 --- a/crates/sui-sdk-types/src/types/crypto/zklogin.rs +++ b/crates/sui-sdk-types/src/types/crypto/zklogin.rs @@ -82,6 +82,14 @@ pub struct ZkLoginPublicIdentifier { } impl ZkLoginPublicIdentifier { + pub fn new(iss: String, address_seed: Bn254FieldElement) -> Option { + if iss.len() > 255 { + None + } else { + Some(Self { iss, address_seed }) + } + } + pub fn iss(&self) -> &str { &self.iss } From c2fc061638466313cfb5984203fb0a5c7a02e34f Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 4 Oct 2024 09:08:31 -0700 Subject: [PATCH 034/107] sui-graphql-client: impl_scalar for u64 and Uint53 (#29) --- crates/sui-graphql-client/README.md | 25 +++++++------- .../examples/custom_query.rs | 11 +++---- crates/sui-graphql-client/src/lib.rs | 33 +++++++------------ .../src/query_types/active_validators.rs | 7 ++-- .../src/query_types/checkpoint.rs | 15 ++++----- .../src/query_types/coin.rs | 3 +- .../src/query_types/epoch.rs | 17 +++++----- .../sui-graphql-client/src/query_types/mod.rs | 11 +------ .../src/query_types/object.rs | 5 ++- .../src/query_types/protocol_config.rs | 5 ++- .../src/query_types/transaction.rs | 5 ++- 11 files changed, 52 insertions(+), 85 deletions(-) diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index 1e6b5e0cb..a8840d0b7 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -115,12 +115,13 @@ query CustomQuery($id: UInt53) { ``` The generated query types are defined below. Note that the `id` variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the `id` variable is not provided, the query will return the data for the last known epoch. +Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. ```rust,ignore #[derive(cynic::QueryVariables, Debug)] pub struct CustomQueryVariables { - pub id: Option, + pub id: Option, } #[derive(cynic::QueryFragment, Debug)] @@ -132,19 +133,15 @@ pub struct CustomQuery { #[derive(cynic::QueryFragment, Debug)] pub struct Epoch { - pub epoch_id: Uint53, + pub epoch_id: u64, pub reference_gas_price: Option, pub total_gas_fees: Option, - pub total_checkpoints: Option, - pub total_transactions: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, } #[derive(cynic::Scalar, Debug, Clone)] pub struct BigInt(pub String); - -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "UInt53")] -pub struct Uint53(pub u64); ``` The complete example is shown below: @@ -153,7 +150,7 @@ use anyhow::Result; use cynic::QueryBuilder; use sui_graphql_client::{ - query_types::{schema, BigInt, Uint53}, + query_types::{schema, BigInt}, Client, }; use sui_types::types::Address; @@ -162,11 +159,11 @@ use sui_types::types::Address; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Epoch")] pub struct EpochData { - pub epoch_id: Uint53, + pub epoch_id: u64, pub reference_gas_price: Option, pub total_gas_fees: Option, - pub total_checkpoints: Option, - pub total_transactions: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, } // The variables to pass to the custom query. @@ -174,7 +171,7 @@ pub struct EpochData { // Otherwise, the query will return the data for the last known epoch. #[derive(cynic::QueryVariables, Debug)] pub struct CustomVariables { - pub id: Option, + pub id: Option, } // The custom query. Note that the variables need to be explicitly declared. @@ -205,7 +202,7 @@ async fn main() -> Result<()> { println!("{:?}", response); // Query the data for epoch 1. - let epoch_id = Uint53(1); + let epoch_id = 1; let operation = CustomQuery::build(CustomVariables { id: Some(epoch_id) }); let response = client .run_query::(&operation) diff --git a/crates/sui-graphql-client/examples/custom_query.rs b/crates/sui-graphql-client/examples/custom_query.rs index 97cbf8be5..4c995205d 100644 --- a/crates/sui-graphql-client/examples/custom_query.rs +++ b/crates/sui-graphql-client/examples/custom_query.rs @@ -6,18 +6,17 @@ use cynic::QueryBuilder; use sui_graphql_client::query_types::schema; use sui_graphql_client::query_types::BigInt; -use sui_graphql_client::query_types::Uint53; use sui_graphql_client::Client; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Epoch")] pub struct EpochData { - pub epoch_id: Uint53, + pub epoch_id: u64, pub reference_gas_price: Option, pub total_gas_fees: Option, - pub total_checkpoints: Option, - pub total_transactions: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, } // The variables to pass to the custom query. @@ -25,7 +24,7 @@ pub struct EpochData { // Otherwise, the query will return the data for the last known epoch. #[derive(cynic::QueryVariables, Debug)] pub struct CustomVariables { - pub id: Option, + pub id: Option, } // The custom query. Note that the variables need to be explicitly declared. @@ -56,7 +55,7 @@ async fn main() -> Result<()> { println!("{:?}", response); // Query the data for epoch 1. - let epoch_id = Uint53(1); + let epoch_id = 1; let operation = CustomQuery::build(CustomVariables { id: Some(epoch_id) }); let response = client .run_query::(&operation) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 07cc610b6..9d890a069 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -41,7 +41,6 @@ use query_types::TransactionBlockQuery; use query_types::TransactionBlocksQuery; use query_types::TransactionBlocksQueryArgs; use query_types::TransactionsFilter; -use query_types::Uint53; use query_types::Validator; use reqwest::Url; use sui_types::types::framework::Coin; @@ -199,9 +198,7 @@ impl Client { /// This will return `Ok(None)` if the epoch requested is not available in the GraphQL service /// (e.g., due to prunning). pub async fn reference_gas_price(&self, epoch: Option) -> Result, Error> { - let operation = EpochSummaryQuery::build(EpochSummaryArgs { - id: epoch.map(Uint53), - }); + let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); let response = self.run_query(&operation).await?; if let Some(data) = response.data { @@ -220,9 +217,7 @@ impl Client { &self, version: Option, ) -> Result, Error> { - let operation = ProtocolConfigQuery::build(ProtocolVersionArgs { - id: version.map(Uint53), - }); + let operation = ProtocolConfigQuery::build(ProtocolVersionArgs { id: version }); let response = self.run_query(&operation).await?; Ok(response.data.map(|p| p.protocol_config)) } @@ -250,7 +245,7 @@ impl Client { last: Option, ) -> Result>, Error> { let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { - id: epoch.map(Uint53), + id: epoch, after, before, first, @@ -437,7 +432,7 @@ impl Client { let operation = CheckpointQuery::build(CheckpointArgs { id: CheckpointId { digest, - sequence_number: seq_num.map(Uint53), + sequence_number: seq_num, }, }); let response = self.run_query(&operation).await?; @@ -478,8 +473,7 @@ impl Client { Ok(response .data .and_then(|d| d.epoch) - .and_then(|e| e.total_checkpoints) - .map(|x| x.into())) + .and_then(|e| e.total_checkpoints)) } /// Return the number of transaction blocks in this epoch. This will return `Ok(None)` if the @@ -497,8 +491,7 @@ impl Client { Ok(response .data .and_then(|d| d.epoch) - .and_then(|e| e.total_transactions) - .map(|x| x.into())) + .and_then(|e| e.total_transactions)) } /// Internal method for getting the epoch summary that is called in a few other APIs for @@ -507,9 +500,7 @@ impl Client { &self, epoch: Option, ) -> Result, Error> { - let operation = EpochSummaryQuery::build(EpochSummaryArgs { - id: epoch.map(Uint53), - }); + let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); self.run_query(&operation).await } @@ -567,10 +558,7 @@ impl Client { address: Address, version: Option, ) -> Result, Error> { - let operation = ObjectQuery::build(ObjectQueryArgs { - address, - version: version.map(Uint53), - }); + let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); let response = self.run_query(&operation).await?; @@ -861,9 +849,9 @@ mod tests { let pc = pc.unwrap(); if let Some(pc) = pc { assert_eq!( - pc.protocol_version.0, 50, + pc.protocol_version, 50, "Protocol version query mismatch for network: {n}. Expected: 50, received: {}", - pc.protocol_version.0 + pc.protocol_version ); } } @@ -989,6 +977,7 @@ mod tests { } #[tokio::test] + #[ignore] async fn test_objects_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs index 396ef6aa6..a46df9f10 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -8,7 +8,6 @@ use crate::query_types::BigInt; use crate::query_types::GQLAddress; use crate::query_types::MoveObject; use crate::query_types::PageInfo; -use crate::query_types::Uint53; #[derive(cynic::QueryFragment, Debug)] #[cynic( @@ -23,7 +22,7 @@ pub struct ActiveValidatorsQuery { #[derive(cynic::QueryVariables, Debug)] pub struct ActiveValidatorsArgs { - pub id: Option, + pub id: Option, pub after: Option, pub before: Option, pub first: Option, @@ -73,7 +72,7 @@ pub struct Validator { /// Validator's description. pub description: Option, /// Number of exchange rates in the table. - pub exchange_rates_size: Option, + pub exchange_rates_size: Option, /// The reference gas price for this epoch. pub gas_price: Option, /// Validator's name. @@ -106,7 +105,7 @@ pub struct Validator { /// The epoch stake rewards will be added here at the end of each epoch. pub rewards_pool: Option, /// The epoch at which this pool became active. - pub staking_pool_activation_epoch: Option, + pub staking_pool_activation_epoch: Option, /// The ID of this validator's `0x3::staking_pool::StakingPool`. pub staking_pool_id: Address, /// The total number of SUI tokens in this pool. diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index 9c53beff4..57aa018bb 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -14,7 +14,6 @@ use crate::query_types::Base64; use crate::query_types::BigInt; use crate::query_types::DateTime; use crate::query_types::Epoch; -use crate::query_types::Uint53; // =========================================================================== // Checkpoint Queries @@ -40,7 +39,7 @@ pub struct CheckpointArgs { #[cynic(schema = "rpc", graphql_type = "CheckpointId")] pub struct CheckpointId { pub digest: Option, - pub sequence_number: Option, + pub sequence_number: Option, } // =========================================================================== // Checkpoint Types @@ -51,9 +50,9 @@ pub struct CheckpointId { pub struct Checkpoint { pub epoch: Option, pub digest: String, - pub network_total_transactions: Option, + pub network_total_transactions: Option, pub previous_checkpoint_digest: Option, - pub sequence_number: Uint53, + pub sequence_number: u64, pub timestamp: DateTime, pub validator_signatures: Base64, pub rolling_gas_summary: Option, @@ -76,13 +75,11 @@ impl TryInto for Checkpoint { let epoch = self .epoch .ok_or_else(|| Error::msg("Epoch is missing"))? - .epoch_id - .into(); + .epoch_id; let network_total_transactions = self .network_total_transactions - .ok_or_else(|| Error::msg("Network total transactions is missing"))? - .into(); - let sequence_number = self.sequence_number.into(); + .ok_or_else(|| Error::msg("Network total transactions is missing"))?; + let sequence_number = self.sequence_number; let timestamp_ms = ChronoDT::parse_from_rfc3339(&self.timestamp.0) .map_err(|e| Error::msg(format!("Cannot parse DateTime: {e}")))? .timestamp_millis() diff --git a/crates/sui-graphql-client/src/query_types/coin.rs b/crates/sui-graphql-client/src/query_types/coin.rs index 4d3e0ea5c..9f1dbce77 100644 --- a/crates/sui-graphql-client/src/query_types/coin.rs +++ b/crates/sui-graphql-client/src/query_types/coin.rs @@ -27,7 +27,6 @@ pub struct CoinMetadataArgs<'a> { use crate::query_types::schema; use crate::query_types::BigInt; -use crate::query_types::Uint53; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "CoinMetadata")] @@ -38,5 +37,5 @@ pub struct CoinMetadata { pub name: Option, pub symbol: Option, pub supply: Option, - pub version: Uint53, + pub version: u64, } diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/sui-graphql-client/src/query_types/epoch.rs index bb9595cd3..117aa607e 100644 --- a/crates/sui-graphql-client/src/query_types/epoch.rs +++ b/crates/sui-graphql-client/src/query_types/epoch.rs @@ -5,7 +5,6 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::BigInt; use crate::query_types::DateTime; -use crate::query_types::Uint53; // =========================================================================== // Epoch Queries @@ -24,16 +23,16 @@ pub struct EpochSummaryQuery { #[derive(cynic::QueryVariables, Debug)] pub struct EpochSummaryArgs { - pub id: Option, + pub id: Option, } #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Epoch")] pub struct EpochSummary { - pub epoch_id: Uint53, + pub epoch_id: u64, pub reference_gas_price: Option, - pub total_checkpoints: Option, - pub total_transactions: Option, + pub total_checkpoints: Option, + pub total_transactions: Option, } // =========================================================================== @@ -44,7 +43,7 @@ pub struct EpochSummary { #[cynic(schema = "rpc", graphql_type = "Epoch")] pub struct Epoch { pub end_timestamp: Option, - pub epoch_id: Uint53, + pub epoch_id: u64, pub fund_inflow: Option, pub fund_outflow: Option, pub fund_size: Option, @@ -52,12 +51,12 @@ pub struct Epoch { pub net_inflow: Option, pub reference_gas_price: Option, pub start_timestamp: DateTime, - pub system_state_version: Option, - pub total_checkpoints: Option, + pub system_state_version: Option, + pub total_checkpoints: Option, pub total_gas_fees: Option, pub total_stake_rewards: Option, pub total_stake_subsidies: Option, - pub total_transactions: Option, + pub total_transactions: Option, pub validator_set: Option, } diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 82b5ba683..fcc7e84a0 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -72,6 +72,7 @@ pub mod schema {} // =========================================================================== impl_scalar!(Address, schema::SuiAddress); +impl_scalar!(u64, schema::UInt53); #[derive(cynic::Scalar, Debug, Clone)] #[cynic(graphql_type = "Base64")] @@ -85,10 +86,6 @@ pub struct BigInt(pub String); #[cynic(graphql_type = "DateTime")] pub struct DateTime(pub String); -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "UInt53")] -pub struct Uint53(pub u64); - // =========================================================================== // Types used in several queries // =========================================================================== @@ -123,12 +120,6 @@ pub struct PageInfo { pub end_cursor: Option, } -impl From for u64 { - fn from(value: Uint53) -> Self { - value.0 - } -} - impl TryFrom for u64 { type Error = anyhow::Error; diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index 6f8b99559..10ea0e29b 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -5,7 +5,6 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::PageInfo; -use crate::query_types::Uint53; // =========================================================================== // Object(s) Queries @@ -32,7 +31,7 @@ pub struct ObjectsQuery { #[derive(cynic::QueryVariables, Debug)] pub struct ObjectQueryArgs { pub address: Address, - pub version: Option, + pub version: Option, } #[derive(cynic::QueryVariables, Debug)] @@ -68,7 +67,7 @@ pub struct ObjectFilter<'a> { #[cynic(schema = "rpc", graphql_type = "ObjectKey")] pub struct ObjectKey { pub object_id: Address, - pub version: Uint53, + pub version: u64, } #[derive(cynic::QueryFragment, Debug)] diff --git a/crates/sui-graphql-client/src/query_types/protocol_config.rs b/crates/sui-graphql-client/src/query_types/protocol_config.rs index 1299b4621..e46e72d12 100644 --- a/crates/sui-graphql-client/src/query_types/protocol_config.rs +++ b/crates/sui-graphql-client/src/query_types/protocol_config.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; -use crate::query_types::Uint53; // =========================================================================== // Protocol Config Queries @@ -25,7 +24,7 @@ pub struct ProtocolConfigQuery { #[derive(cynic::QueryVariables, Debug)] pub struct ProtocolVersionArgs { - pub id: Option, + pub id: Option, } // =========================================================================== @@ -36,7 +35,7 @@ pub struct ProtocolVersionArgs { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigs")] pub struct ProtocolConfigs { - pub protocol_version: Uint53, + pub protocol_version: u64, pub feature_flags: Vec, pub configs: Vec, } diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 2e71d253d..9b76ed77a 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -5,7 +5,6 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::PageInfo; -use crate::query_types::Uint53; // =========================================================================== // Transaction Block(s) Queries @@ -77,8 +76,8 @@ pub enum TransactionBlockKindInput { pub struct TransactionsFilter { pub function: Option, pub kind: Option, - pub at_checkpoint: Option, - pub before_checkpoint: Option, + pub at_checkpoint: Option, + pub before_checkpoint: Option, pub changed_object: Option
, pub input_object: Option
, pub recv_address: Option
, From 3cfa333ede6435466bf9e38d0e4ae4a98a27b1da Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:36:15 -0700 Subject: [PATCH 035/107] sui-graphql-client: add dry run query (#23) --- crates/sui-graphql-client/src/lib.rs | 105 +++++++++++++++++- .../src/query_types/dry_run.rs | 58 ++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 11 +- .../src/query_types/transaction.rs | 7 ++ 4 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/dry_run.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 9d890a069..16dbbfd4c 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -18,6 +18,8 @@ use query_types::CheckpointQuery; use query_types::CoinMetadata; use query_types::CoinMetadataArgs; use query_types::CoinMetadataQuery; +use query_types::DryRunArgs; +use query_types::DryRunQuery; use query_types::EpochSummaryArgs; use query_types::EpochSummaryQuery; use query_types::EventFilter; @@ -40,9 +42,10 @@ use query_types::TransactionBlockArgs; use query_types::TransactionBlockQuery; use query_types::TransactionBlocksQuery; use query_types::TransactionBlocksQueryArgs; +use query_types::TransactionMetadata; use query_types::TransactionsFilter; use query_types::Validator; -use reqwest::Url; + use sui_types::types::framework::Coin; use sui_types::types::Address; use sui_types::types::CheckpointSequenceNumber; @@ -52,6 +55,7 @@ use sui_types::types::Object; use sui_types::types::SignedTransaction; use sui_types::types::Transaction; use sui_types::types::TransactionEffects; +use sui_types::types::TransactionKind; use sui_types::types::UserSignature; use anyhow::anyhow; @@ -64,6 +68,7 @@ use cynic::MutationBuilder; use cynic::Operation; use cynic::QueryBuilder; use futures::Stream; +use reqwest::Url; use std::pin::Pin; const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; @@ -72,6 +77,12 @@ const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; const LOCAL_HOST: &str = "http://localhost:9125/graphql"; static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); +#[derive(Debug)] +pub struct DryRunResult { + pub effects: Option, + pub error: Option, +} + #[derive(Debug)] /// A page of items returned by the GraphQL server. pub struct Page { @@ -671,6 +682,87 @@ impl Client { } } + // =========================================================================== + // Dry Run API + // =========================================================================== + + /// Dry run a [`Transaction`] and return the transaction effects and dry run error (if any). + /// + /// `skipChecks` optional flag disables the usual verification checks that prevent access to + /// objects that are owned by addresses other than the sender, and calling non-public, + /// non-entry functions, and some other checks. Defaults to false. + pub async fn dry_run_tx( + &self, + tx: &Transaction, + skip_checks: Option, + ) -> Result { + let tx_bytes = base64ct::Base64::encode_string( + &bcs::to_bytes(&tx).map_err(|_| Error::msg("Cannot encode Transaction as BCS"))?, + ); + self.dry_run(tx_bytes, skip_checks, None).await + } + + /// Dry run a [`TransactionKind`] and return the transaction effects and dry run error (if any). + /// + /// `skipChecks` optional flag disables the usual verification checks that prevent access to + /// objects that are owned by addresses other than the sender, and calling non-public, + /// non-entry functions, and some other checks. Defaults to false. + /// + /// `tx_meta` is the transaction metadata. + pub async fn dry_run_tx_kind( + &self, + tx_kind: &TransactionKind, + skip_checks: Option, + tx_meta: TransactionMetadata, + ) -> Result { + let tx_bytes = base64ct::Base64::encode_string( + &bcs::to_bytes(&tx_kind).map_err(|_| Error::msg("Cannot encode Transaction as BCS"))?, + ); + self.dry_run(tx_bytes, skip_checks, Some(tx_meta)).await + } + + /// Internal implementation of the dry run API. + async fn dry_run( + &self, + tx_bytes: String, + skip_checks: Option, + tx_meta: Option, + ) -> Result { + let skip_checks = skip_checks.unwrap_or(false); + let operation = DryRunQuery::build(DryRunArgs { + tx_bytes, + skip_checks, + tx_meta, + }); + let response = self.run_query(&operation).await?; + + // Query errors + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + // Dry Run errors + let error = response + .data + .as_ref() + .and_then(|tx| tx.dry_run_transaction_block.error.clone()); + + let effects = response + .data + .map(|tx| tx.dry_run_transaction_block) + .and_then(|tx| tx.transaction) + .and_then(|tx| tx.effects) + .and_then(|bcs| bcs.bcs) + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|_| Error::msg("Cannot decode bcs bytes from Base64 for transaction effects"))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose() + .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; + + Ok(DryRunResult { effects, error }) + } + // =========================================================================== // Transaction API // =========================================================================== @@ -1061,4 +1153,15 @@ mod tests { ); } } + + #[tokio::test] + async fn test_dry_run() { + let client = Client::new_testnet(); + // this tx bytes works on testnet + let tx_bytes = "AAACAAiA8PoCAAAAAAAg7q6yDns6nPznaKLd9pUD2K6NFiiibC10pDVQHJKdP2kCAgABAQAAAQECAAABAQBGLuHCJ/xjZfhC4vTJt/Zrvq1gexKLaKf3aVzyIkxRaAFUHzz8ftiZdY25qP4f9zySuT1K/qyTWjbGiTu0i0Z1ZFA4gwUAAAAAILeG86EeQm3qY3ajat3iUnY2Gbrk/NbdwV/d9MZviAwwRi7hwif8Y2X4QuL0ybf2a76tYHsSi2in92lc8iJMUWjoAwAAAAAAAECrPAAAAAAAAA=="; + + let dry_run = client.dry_run(tx_bytes.to_string(), None, None).await; + + assert!(dry_run.is_ok()); + } } diff --git a/crates/sui-graphql-client/src/query_types/dry_run.rs b/crates/sui-graphql-client/src/query_types/dry_run.rs new file mode 100644 index 000000000..4e5641e1f --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/dry_run.rs @@ -0,0 +1,58 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use sui_types::types::ObjectReference; + +use crate::query_types::schema; +use crate::query_types::Address; +use crate::query_types::TransactionBlock; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "DryRunArgs")] +pub struct DryRunQuery { + #[arguments(txBytes: $tx_bytes, skipChecks: $skip_checks, txMeta: $tx_meta)] + pub dry_run_transaction_block: DryRunResult, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "DryRunResult")] +pub struct DryRunResult { + pub error: Option, + pub transaction: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct DryRunArgs { + pub tx_bytes: String, + pub skip_checks: bool, + pub tx_meta: Option, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionMetadata")] +pub struct TransactionMetadata { + pub gas_budget: Option, + pub gas_objects: Option>, + pub gas_price: Option, + pub gas_sponsor: Option
, + pub sender: Option
, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "ObjectRef")] +pub struct ObjectRef { + pub address: Address, + pub digest: String, + pub version: u64, +} + +impl From for ObjectRef { + fn from(value: ObjectReference) -> Self { + let address: Address = (*value.object_id()).into(); + ObjectRef { + address, + version: value.version(), + digest: value.digest().to_string(), + } + } +} diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index fcc7e84a0..824b6d65e 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -6,6 +6,7 @@ mod balance; mod chain; mod checkpoint; mod coin; +mod dry_run; mod epoch; mod events; mod execute_tx; @@ -20,7 +21,6 @@ pub use active_validators::EpochValidator; pub use active_validators::Validator; pub use active_validators::ValidatorConnection; pub use active_validators::ValidatorSet; -use anyhow::anyhow; pub use balance::Balance; pub use balance::BalanceArgs; pub use balance::BalanceQuery; @@ -32,6 +32,10 @@ pub use checkpoint::CheckpointQuery; pub use coin::CoinMetadata; pub use coin::CoinMetadataArgs; pub use coin::CoinMetadataQuery; +pub use dry_run::DryRunArgs; +pub use dry_run::DryRunQuery; +pub use dry_run::DryRunResult; +pub use dry_run::TransactionMetadata; pub use epoch::Epoch; pub use epoch::EpochSummaryArgs; pub use epoch::EpochSummaryQuery; @@ -55,13 +59,16 @@ pub use protocol_config::ProtocolVersionArgs; pub use service_config::Feature; pub use service_config::ServiceConfig; pub use service_config::ServiceConfigQuery; -use sui_types::types::Address; +pub use transaction::TransactionBlock; pub use transaction::TransactionBlockArgs; pub use transaction::TransactionBlockQuery; pub use transaction::TransactionBlocksQuery; pub use transaction::TransactionBlocksQueryArgs; pub use transaction::TransactionsFilter; +use sui_types::types::Address; + +use anyhow::anyhow; use cynic::impl_scalar; #[cynic::schema("rpc")] diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 9b76ed77a..5df1cb516 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -58,6 +58,13 @@ pub struct TransactionBlocksQueryArgs { #[cynic(schema = "rpc", graphql_type = "TransactionBlock")] pub struct TransactionBlock { pub bcs: Option, + pub effects: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlockEffects")] +pub struct TransactionBlockEffects { + pub bcs: Option, } #[derive(cynic::Enum, Clone, Copy, Debug)] From d3e28dfe84af056f25202f48b5568e876e2c711d Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:44:13 -0700 Subject: [PATCH 036/107] sui-graphql-client: avoid an extra RPC call by checking if first and last are not both set (#35) --- crates/sui-graphql-client/src/lib.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 16dbbfd4c..7416e1d73 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -255,6 +255,11 @@ impl Client { first: Option, last: Option, ) -> Result>, Error> { + ensure!( + !(first.is_some() && last.is_some()), + "Cannot pass both first and last" + ); + let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { id: epoch, after, @@ -334,6 +339,11 @@ impl Client { last: Option, coin_type: Option<&str>, ) -> Result>, Error> { + ensure!( + !(first.is_some() && last.is_some()), + "Cannot pass both first and last" + ); + let response = self .objects( after, @@ -527,6 +537,11 @@ impl Client { first: Option, last: Option, ) -> Result>, Error> { + ensure!( + !(first.is_some() && last.is_some()), + "Cannot pass both first and last" + ); + let operation = EventsQuery::build(EventsQueryArgs { filter, after, @@ -620,6 +635,11 @@ impl Client { first: Option, last: Option, ) -> Result>, Error> { + ensure!( + !(first.is_some() && last.is_some()), + "Cannot pass both first and last" + ); + let operation = ObjectsQuery::build(ObjectsQueryArgs { after, before, @@ -796,6 +816,11 @@ impl Client { last: Option, filter: Option, ) -> Result>, Error> { + ensure!( + !(first.is_some() && last.is_some()), + "Cannot pass both first and last" + ); + let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { after, before, From 58f537d22b6f949e8b0ecb39f9e1c9fdae9345d9 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:47:33 -0700 Subject: [PATCH 037/107] sui-graphql-client: fix transaction(s) query (#32) The TransactionBlock bcs in GraphQL now encodes the TransactionData. This PR fixes the logic to correctly deserialize the transaction bcs data and signatures into a SignedTransaction. As the service does not serve yet the new data, I tested it locally by starting a local network and GraphQL server. For now, I put an #ignore on the test. --- crates/sui-graphql-client/src/lib.rs | 60 +++++++++---------- .../src/query_types/transaction.rs | 51 ++++++++++++++-- 2 files changed, 73 insertions(+), 38 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 7416e1d73..b50f723e7 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -795,26 +795,22 @@ impl Client { }); let response = self.run_query(&operation).await?; - let signed_tx = response + response .data - .and_then(|tbq| tbq.transaction_block) - .and_then(|tb| tb.bcs) - .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .and_then(|d| d.transaction_block) + .map(|tx| tx.try_into()) .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 transaction bcs bytes: {e}")))? - .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose()?; - Ok(signed_tx) + .map_err(|e| Error::msg(format!("Cannot decode transaction: {e}"))) } /// Get a page of transactions based on the provided filters. - pub async fn transactions( + pub async fn transactions<'a>( &self, - after: Option, - before: Option, + after: Option<&str>, + before: Option<&str>, first: Option, last: Option, - filter: Option, + filter: Option>, ) -> Result>, Error> { ensure!( !(first.is_some() && last.is_some()), @@ -834,28 +830,12 @@ impl Client { if let Some(txb) = response.data { let txc = txb.transaction_blocks; let page_info = txc.page_info; - let bcs = txc - .nodes - .iter() - .map(|tx| &tx.bcs) - .filter_map(|b64| { - b64.as_ref() - .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) - }) - .collect::, base64ct::Error>>() - .map_err(|e| { - Error::msg(format!("Cannot decode Base64 transaction bcs bytes: {e}")) - })?; - let transactions = bcs - .iter() - .map(|tx| bcs::from_bytes::(tx)) - .collect::, bcs::Error>>() - .map_err(|e| { - Error::msg(format!( - "Cannot decode bcs bytes into SignedTransaction: {e}" - )) - })?; + let transactions = txc + .nodes + .into_iter() + .map(|n| n.try_into()) + .collect::>>()?; let page = Page::new(page_info, transactions); Ok(Some(page)) } else { @@ -1161,6 +1141,20 @@ mod tests { assert!(num_coins > 0); } + #[tokio::test] + #[ignore] + async fn test_transactions_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let transactions = client.transactions(None, None, None, Some(5), None).await; + assert!( + transactions.is_ok(), + "Transactions query failed for network: {n}. Error: {}", + transactions.unwrap_err() + ); + } + } + #[tokio::test] async fn test_total_supply() { for (n, _) in NETWORKS { diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 5df1cb516..43efe24aa 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -1,6 +1,12 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use anyhow::Error; +use base64ct::Encoding; +use sui_types::types::SignedTransaction; +use sui_types::types::Transaction; +use sui_types::types::UserSignature; + use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; @@ -42,12 +48,12 @@ pub struct TransactionBlockArgs { } #[derive(cynic::QueryVariables, Debug)] -pub struct TransactionBlocksQueryArgs { +pub struct TransactionBlocksQueryArgs<'a> { pub first: Option, - pub after: Option, + pub after: Option<&'a str>, pub last: Option, - pub before: Option, - pub filter: Option, + pub before: Option<&'a str>, + pub filter: Option>, } // =========================================================================== @@ -59,6 +65,7 @@ pub struct TransactionBlocksQueryArgs { pub struct TransactionBlock { pub bcs: Option, pub effects: Option, + pub signatures: Option>, } #[derive(cynic::QueryFragment, Debug)] @@ -80,7 +87,7 @@ pub enum TransactionBlockKindInput { #[derive(cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "TransactionBlockFilter")] -pub struct TransactionsFilter { +pub struct TransactionsFilter<'a> { pub function: Option, pub kind: Option, pub at_checkpoint: Option, @@ -88,6 +95,7 @@ pub struct TransactionsFilter { pub changed_object: Option
, pub input_object: Option
, pub recv_address: Option
, + pub transaction_ids: Option>, } #[derive(cynic::QueryFragment, Debug)] @@ -96,3 +104,36 @@ pub struct TransactionBlockConnection { pub nodes: Vec, pub page_info: PageInfo, } + +impl TryFrom for SignedTransaction { + type Error = anyhow::Error; + + fn try_from(value: TransactionBlock) -> Result { + let transaction = value + .bcs + .map(|tx| base64ct::Base64::decode_vec(tx.0.as_str())) + .transpose() + .map_err(|_| Error::msg("Cannot decode Base64 transaction bcs bytes"))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose() + .map_err(|_| Error::msg("Cannot decode bcs bytes into Transaction"))?; + + let signatures = if let Some(sigs) = value.signatures { + sigs.iter() + .map(|s| UserSignature::from_base64(&s.0)) + .collect::, _>>() + .map_err(|_| Error::msg("Cannot decode Base64 signature"))? + } else { + vec![] + }; + + if let Some(transaction) = transaction { + Ok(SignedTransaction { + transaction, + signatures, + }) + } else { + Err(Error::msg("Cannot decode transaction")) + } + } +} From 6fbaa8ae755624c804af4e59561f05fdd5c898ec Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 15 Oct 2024 20:04:09 -0500 Subject: [PATCH 038/107] type_tag: add StructTag parsing test case --- crates/sui-sdk-types/src/types/type_tag/parse.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/sui-sdk-types/src/types/type_tag/parse.rs b/crates/sui-sdk-types/src/types/type_tag/parse.rs index c55860347..a3386c18d 100644 --- a/crates/sui-sdk-types/src/types/type_tag/parse.rs +++ b/crates/sui-sdk-types/src/types/type_tag/parse.rs @@ -145,6 +145,7 @@ mod tests { "0x1::__::__", "0x1::_bar::_BAR<0x2::_____::______fooo______>", "0x1::__::__<0x2::_____::______fooo______, 0xff::Bar____::_______foo>", + "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::sui::SUI, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", ] { assert!(parse_type_tag(s).is_ok(), "Failed to parse tag {}", s); } @@ -180,6 +181,7 @@ mod tests { "0x1::__::__", "0x1::_bar::_BAR<0x2::_____::______fooo______>", "0x1::__::__<0x2::_____::______fooo______, 0xff::Bar____::_______foo>", + "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::sui::SUI, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", ]; for s in valid { let mut input = s; From 884e66f06fd80d51ea67957c2a212bf688f98c98 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 15 Oct 2024 10:27:30 -0500 Subject: [PATCH 039/107] types: improve format of UnresolvedTransaction --- crates/sui-sdk-types/src/types/mod.rs | 49 +----- .../src/types/transaction/mod.rs | 5 +- .../src/types/transaction/unresolved.rs | 153 ++++++++++++++---- 3 files changed, 136 insertions(+), 71 deletions(-) diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index d17121bfc..a2cac0469 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -13,53 +13,20 @@ pub mod transaction; pub mod type_tag; pub mod u256; -pub use address::Address; -pub use checkpoint::{ - CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber, - CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo, - EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit, -}; -pub use crypto::{ - Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, - Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, - JwkId, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, - MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PublicKey, - Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, - UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, - ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, -}; -pub use digest::{ - CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, - EffectsAuxiliaryDataDigest, ObjectDigest, SigningDigest, TransactionDigest, - TransactionEffectsDigest, TransactionEventsDigest, -}; -pub use effects::{ - ChangedObject, EffectsObjectChange, IdOperation, ObjectIn, ObjectOut, TransactionEffects, - TransactionEffectsV1, UnchangedSharedKind, UnchangedSharedObject, -}; -pub use events::{BalanceChange, Event, TransactionEvents}; -pub use execution_status::{ - CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError, - TypeArgumentError, -}; -pub use gas::GasCostSummary; -pub use object::{ - GenesisObject, Object, ObjectData, ObjectReference, ObjectType, Owner, TypeOrigin, UpgradeInfo, - Version, -}; -pub use object_id::ObjectId; #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub(crate) use transaction::SignedTransactionWithIntentMessage; pub use transaction::{ - ActiveJwk, Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, - CancelledTransaction, ChangeEpoch, Command, ConsensusCommitPrologueV1, - ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind, GasPayment, - GenesisTransaction, InputArgument, MakeMoveVector, MergeCoins, MoveCall, + ActiveJwk, Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, + AuthenticatorStateUpdateV1, CancelledTransaction, ChangeEpoch, Command, + ConsensusCommitPrologue, ConsensusCommitPrologueV1, ConsensusCommitPrologueV2, + ConsensusCommitPrologueV3, ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind, + GasPayment, GenesisTransaction, InputArgument, MakeMoveVector, MergeCoins, MoveCall, ProgrammableTransaction, Publish, RandomnessStateUpdate, SignedTransaction, SplitCoins, SystemPackage, Transaction, TransactionExpiration, TransactionKind, TransferObjects, - UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedObjectReference, - UnresolvedProgrammableTransaction, UnresolvedTransaction, Upgrade, VersionAssignment, + UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedInputArgumentKind, + UnresolvedObjectReference, UnresolvedProgrammableTransaction, UnresolvedTransaction, + UnresolvedValue, Upgrade, VersionAssignment, }; pub use type_tag::{Identifier, StructTag, TypeParseError, TypeTag}; diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs index dc98d6569..874917283 100644 --- a/crates/sui-sdk-types/src/types/transaction/mod.rs +++ b/crates/sui-sdk-types/src/types/transaction/mod.rs @@ -13,8 +13,9 @@ pub(crate) use serialization::SignedTransactionWithIntentMessage; mod unresolved; pub use unresolved::{ - UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedObjectReference, - UnresolvedProgrammableTransaction, UnresolvedTransaction, + UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedInputArgumentKind, + UnresolvedObjectReference, UnresolvedProgrammableTransaction, UnresolvedTransaction, + UnresolvedValue, }; #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs index c1c120030..33b601cb7 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-sdk-types/src/types/transaction/unresolved.rs @@ -6,10 +6,12 @@ use crate::types::{Address, ObjectDigest, ObjectId, object::Version}; feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct UnresolvedTransaction { #[cfg_attr(feature = "serde", serde(flatten))] pub ptb: UnresolvedProgrammableTransaction, pub sender: Address, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] pub gas_payment: Option, pub expiration: TransactionExpiration, } @@ -18,6 +20,7 @@ pub struct UnresolvedTransaction { feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct UnresolvedProgrammableTransaction { pub inputs: Vec, pub commands: Vec, @@ -27,18 +30,33 @@ pub struct UnresolvedProgrammableTransaction { feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct UnresolvedGasPayment { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Vec::is_empty") + )] pub objects: Vec, pub owner: Address, #[cfg_attr( feature = "serde", - serde(with = "crate::_serde::OptionReadableDisplay") + serde( + with = "crate::_serde::OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none", + ) )] + #[cfg_attr(feature = "schemars", schemars(with = "Option"))] pub price: Option, #[cfg_attr( feature = "serde", - serde(with = "crate::_serde::OptionReadableDisplay") + serde( + with = "crate::_serde::OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none", + ) )] + #[cfg_attr(feature = "schemars", schemars(with = "Option"))] pub budget: Option, } @@ -46,43 +64,122 @@ pub struct UnresolvedGasPayment { feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct UnresolvedObjectReference { pub object_id: ObjectId, #[cfg_attr( feature = "serde", - serde(with = "crate::_serde::OptionReadableDisplay") + serde( + with = "crate::_serde::OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none", + ) )] + #[cfg_attr(feature = "schemars", schemars(with = "Option"))] pub version: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] pub digest: Option, } +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename_all = "snake_case") +)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub enum UnresolvedInputArgumentKind { + Pure, + Shared, + Receiving, + ImmutableOrOwned, + Immutable, + Owned, + Literal, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] -pub enum UnresolvedInputArgument { - // contains no structs or objects - Pure { - #[cfg_attr( - feature = "serde", - serde(with = "::serde_with::As::<::serde_with::Bytes>") - )] - value: Vec, - }, - // A Move object, either immutable, or owned mutable. - ImmutableOrOwned(UnresolvedObjectReference), - // A Move object that's shared. - // SharedObject::mutable controls whether caller asks for a mutable reference to shared - // object. - Shared { - object_id: ObjectId, - #[cfg_attr( - feature = "serde", - serde(with = "crate::_serde::OptionReadableDisplay") - )] - initial_shared_version: Option, - mutable: Option, - }, - // A Move object that can be received in this transaction. - Receiving(UnresolvedObjectReference), +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct UnresolvedInputArgument { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + pub kind: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + pub value: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + pub object_id: Option, + /// Either the `initial_shared_version` if object is a shared object, or the + /// `version` if this is an owned object + #[cfg_attr( + feature = "serde", + serde( + with = "crate::_serde::OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none", + alias = "initial_shared_version", + ) + )] + #[cfg_attr(feature = "schemars", schemars(with = "Option"))] + pub version: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + pub digest: Option, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + pub mutable: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(try_from = "serde_json::Value", into = "serde_json::Value") +)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema), schemars(untagged))] +pub enum UnresolvedValue { + Null, + Bool(bool), + Number(u64), + String(String), + Array(Vec), +} + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +impl TryFrom for UnresolvedValue { + type Error = &'static str; + + fn try_from(value: serde_json::Value) -> Result { + let v = match value { + serde_json::Value::Null => Self::Null, + serde_json::Value::Bool(b) => Self::Bool(b), + serde_json::Value::Number(n) => { + Self::Number(n.as_u64().ok_or("expected unsigned integer")?) + } + serde_json::Value::String(s) => Self::String(s), + serde_json::Value::Array(a) => Self::Array( + a.into_iter() + .map(Self::try_from) + .collect::>()?, + ), + serde_json::Value::Object(_) => return Err("objects are not supported"), + }; + + Ok(v) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +impl From for serde_json::Value { + fn from(value: UnresolvedValue) -> Self { + match value { + UnresolvedValue::Null => Self::Null, + UnresolvedValue::Bool(b) => Self::Bool(b), + UnresolvedValue::Number(n) => Self::Number(n.into()), + UnresolvedValue::String(s) => Self::String(s), + UnresolvedValue::Array(a) => Self::Array(a.into_iter().map(Into::into).collect()), + } + } } From 011f48cc6f29af9f8235f722a64c7470f1a39669 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:27:10 -0700 Subject: [PATCH 040/107] sui-graphql-client: update GraphQL schema and fix events to use the new bcs field (#31) * Use the new schema - which fixed events' BCS and added packageBCS, among other things * Update schema --- .../schema/graphql_rpc.graphql | 131 ++++++++++-------- crates/sui-graphql-client/src/lib.rs | 12 +- .../src/query_types/events.rs | 75 ---------- .../src/query_types/transaction.rs | 6 +- 4 files changed, 85 insertions(+), 139 deletions(-) diff --git a/crates/sui-graphql-client/schema/graphql_rpc.graphql b/crates/sui-graphql-client/schema/graphql_rpc.graphql index 177346bbe..92330bc77 100644 --- a/crates/sui-graphql-client/schema/graphql_rpc.graphql +++ b/crates/sui-graphql-client/schema/graphql_rpc.graphql @@ -163,22 +163,15 @@ type AddressOwner { The possible relationship types for a transaction block: sent, or received. """ enum AddressTransactionBlockRelationship { - """ - Transactions this address has sent. NOTE: this input filter has been deprecated in favor of - `SENT` which behaves identically but is named more clearly. Both filters restrict - transactions by their sender, only, not signers in general. - - This filter will be removed after 1.36.0 (2024-10-14). - """ - SIGN """ Transactions this address has sent. """ SENT """ - Transactions that sent objects to this address. + Transactions that this address was involved in, either as the sender, sponsor, or as the + owner of some object that was created, modified or transfered. """ - RECV + AFFECTED } """ @@ -1196,7 +1189,42 @@ type Epoch { transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! } +type EpochConnection { + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! + """ + A list of edges. + """ + edges: [EpochEdge!]! + """ + A list of nodes. + """ + nodes: [Epoch!]! +} + +""" +An edge in a connection. +""" +type EpochEdge { + """ + The item at the end of the edge + """ + node: Epoch! + """ + A cursor for use in pagination + """ + cursor: String! +} + type Event { + """ + The transaction block that emitted this event. This information is only available for + events from indexed transactions, and not from transactions that have just been executed or + dry-run. + """ + transactionBlock: TransactionBlock """ The Move module containing some function that when called by a programmable transaction block (PTB) emitted this event. @@ -1214,32 +1242,13 @@ type Event { """ timestamp: DateTime """ - The value's Move type. + The event's contents as a Move value. """ - type: MoveType! + contents: MoveValue! """ - The BCS representation of this value, Base64 encoded. + The Base64 encoded BCS serialized bytes of the event. """ bcs: Base64! - """ - Structured contents of a Move value. - """ - data: MoveData! - """ - Representation of a Move value in JSON, where: - - - Addresses, IDs, and UIDs are represented in canonical form, as JSON strings. - - Bools are represented by JSON boolean literals. - - u8, u16, and u32 are represented as JSON numbers. - - u64, u128, and u256 are represented as JSON strings. - - Vectors are represented by JSON arrays. - - Structs are represented by JSON objects. - - Empty optional values are represented by `null`. - - This form is offered as a less verbose convenience in cases where the layout of the type is - known by the client. - """ - json: JSON! } type EventConnection { @@ -2350,6 +2359,10 @@ type MovePackage implements IObject & IOwner { """ typeOrigins: [TypeOrigin!] """ + BCS representation of the package itself, as a MovePackage. + """ + packageBcs: Base64 + """ BCS representation of the package's modules. Modules appear as a sequence of pairs (module name, followed by module bytes), in alphabetic order by module name. """ @@ -2493,13 +2506,14 @@ type MoveType { """ signature: MoveTypeSignature! """ - Structured representation of the "shape" of values that match this type. + Structured representation of the "shape" of values that match this type. May return no + layout if the type is invalid. """ - layout: MoveTypeLayout! + layout: MoveTypeLayout """ - The abilities this concrete type has. + The abilities this concrete type has. Returns no abilities if the type is invalid. """ - abilities: [MoveAbility!]! + abilities: [MoveAbility!] } """ @@ -2518,11 +2532,11 @@ type MoveTypeLayout = } | { enum: [{ type: string, - variants: [{ + variants: [{ name: string, fields: [{ name: string, layout: MoveTypeLayout }], }] - }] + }] } """ scalar MoveTypeLayout @@ -2886,11 +2900,6 @@ enum ObjectKind { The object is fetched from the index. """ INDEXED - """ - The object is deleted or wrapped and only partial information can be loaded from the - indexer. - """ - WRAPPED_OR_DELETED } """ @@ -3066,11 +3075,13 @@ type PageInfo { """ If the object's owner is a Parent, this object is part of a dynamic field (it is the value of -the dynamic field, or the intermediate Field object itself). Also note that if the owner -is a parent, then it's guaranteed to be an object. +the dynamic field, or the intermediate Field object itself), and it is owned by another object. + +Although its owner is guaranteed to be an object, it is exposed as an Owner, as the parent +object could be wrapped and therefore not directly accessible. """ type Parent { - parent: Object + parent: Owner } """ @@ -3301,6 +3312,7 @@ type Query { `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! + epochs(first: Int, after: String, last: Int, before: String): EpochConnection! """ The checkpoints that exist in the network. """ @@ -4198,7 +4210,7 @@ type TransactionBlock { """ expiration: Epoch """ - Serialized form of this transaction's `SenderSignedData`, BCS serialized and Base64 encoded. + Serialized form of this transaction's `TransactionData`, BCS serialized and Base64 encoded. """ bcs: Base64 } @@ -4321,27 +4333,28 @@ input TransactionBlockFilter { """ beforeCheckpoint: UInt53 """ - Limit to transactions that were sent by the given address. NOTE: this input filter has been - deprecated in favor of `sentAddress` which behaves identically but is named more clearly. - Both filters restrict transactions by their sender, only, not signers in general. - - This filter will be removed after 1.36.0 (2024-10-14). + Limit to transactions that interacted with the given address. The address could be a + sender, sponsor, or recipient of the transaction. """ - signAddress: SuiAddress + affectedAddress: SuiAddress """ Limit to transactions that were sent by the given address. """ sentAddress: SuiAddress """ - Limit to transactions that sent an object to the given address. - """ - recvAddress: SuiAddress - """ - Limit to transactions that accepted the given object as an input. + Limit to transactions that accepted the given object as an input. NOTE: this input filter + has been deprecated in favor of `affectedObject` which offers an easier to under behavior. + + This filter will be removed with 1.36.0 (2024-10-14), or at least one release after + `affectedObject` is introduced, whichever is later. """ inputObject: SuiAddress """ - Limit to transactions that output a versioon of this object. + Limit to transactions that output a versioon of this object. NOTE: this input filter has + been deprecated in favor of `affectedObject` which offers an easier to understand behavor. + + This filter will be removed with 1.36.0 (2024-10-14), or at least one release after + `affectedObject` is introduced, whichever is later. """ changedObject: SuiAddress """ diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index b50f723e7..10f132084 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -562,8 +562,14 @@ impl Client { let nodes = ec .nodes .into_iter() - .map(Event::try_from) - .collect::, _>>()?; + .map(|e| e.bcs.0) + .map(|b| base64ct::Base64::decode_vec(&b)) + .collect::, base64ct::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode Base64 event bcs bytes: {e}")))? + .iter() + .map(|b| bcs::from_bytes::(b)) + .collect::, bcs::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Event: {e}")))?; Ok(Some(Page::new(page_info, nodes))) } else { @@ -1056,7 +1062,7 @@ mod tests { } #[tokio::test] - #[ignore] + #[ignore] // schema was updated, but the service has not been released with the new schema async fn test_events_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index 4bb70b6f6..b281c1e8a 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -1,15 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; - -use base64ct::Encoding; -use sui_types::types::Identifier; - use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; -use crate::query_types::GQLAddress; use crate::query_types::PageInfo; // =========================================================================== @@ -59,74 +53,5 @@ pub struct EventFilter { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Event")] pub struct Event { - #[cynic(rename = "type")] - pub type_: MoveType, - pub sending_module: Option, - pub sender: Option, pub bcs: Base64, } - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveModule")] -pub struct MoveModule { - pub name: String, - pub package: MovePackage, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MovePackage")] -pub struct MovePackage { - pub address: Address, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveType")] -pub struct MoveType { - pub repr: Option, -} - -#[derive(cynic::Scalar, Debug, Clone)] -pub struct MoveTypeLayout(pub String); - -impl TryFrom for sui_types::types::Event { - type Error = anyhow::Error; - - fn try_from(value: Event) -> Result { - let Event { - type_, - sending_module, - sender, - bcs, - } = value; - - let type_ = if let Some(t) = type_ - .repr - .map(|layout| sui_types::types::StructTag::from_str(&layout)) - .transpose() - .map_err(|e| anyhow::anyhow!("Invalid struct tag in event: {}", e))? - { - t - } else { - return Err(anyhow::anyhow!("Missing struct tag in event")); - }; - - let (package_id, module) = sending_module - .map(|module| (module.package.address, module.name)) - .ok_or_else(|| anyhow::anyhow!("Missing sending module in event"))?; - let package_id = package_id.into(); - let module = Identifier::from_str(&module)?; - - let sender = sender.map(|x| x.address).unwrap_or_else(|| Address::ZERO); - - let contents = base64ct::Base64::decode_vec(&bcs.0) - .map_err(|_| anyhow::anyhow!("Invalid base64 in event"))?; - - Ok(Self { - package_id, - module, - sender, - type_, - contents, - }) - } -} diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 43efe24aa..0655ce2fd 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -90,11 +90,13 @@ pub enum TransactionBlockKindInput { pub struct TransactionsFilter<'a> { pub function: Option, pub kind: Option, + pub after_checkpoint: Option, pub at_checkpoint: Option, pub before_checkpoint: Option, - pub changed_object: Option
, + pub affected_address: Option
, + pub sent_address: Option
, pub input_object: Option
, - pub recv_address: Option
, + pub changed_object: Option
, pub transaction_ids: Option>, } From 02f0088e365201777ebbc060d897416d78672aa0 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:47:00 -0700 Subject: [PATCH 041/107] sui-graphql-client: add checkpoints query (#34) --- crates/sui-graphql-client/src/lib.rs | 52 ++++++++++++++++++- .../src/query_types/checkpoint.rs | 22 ++++++++ .../sui-graphql-client/src/query_types/mod.rs | 2 + 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 10f132084..15791556f 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -15,6 +15,8 @@ use query_types::ChainIdentifierQuery; use query_types::CheckpointArgs; use query_types::CheckpointId; use query_types::CheckpointQuery; +use query_types::CheckpointsArgs; +use query_types::CheckpointsQuery; use query_types::CoinMetadata; use query_types::CoinMetadataArgs; use query_types::CoinMetadataQuery; @@ -438,7 +440,7 @@ impl Client { // Checkpoints API // =========================================================================== - /// Get the `CheckpointSummary` for a given checkpoint digest or checkpoint id. If none is + /// Get the [`CheckpointSummary`] for a given checkpoint digest or checkpoint id. If none is /// provided, it will use the last known checkpoint id. pub async fn checkpoint( &self, @@ -468,6 +470,42 @@ impl Client { .ok_or_else(|| Error::msg("No data in response"))? } + /// Get a page of [`CheckpointSummary`] for the provided parameters. + pub async fn checkpoints( + &self, + after: Option<&str>, + before: Option<&str>, + first: Option, + last: Option, + ) -> Result>, Error> { + let operation = CheckpointsQuery::build(CheckpointsArgs { + after, + before, + first, + last, + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(checkpoints) = response.data { + let cc = checkpoints.checkpoints; + let page_info = cc.page_info; + let nodes = cc + .nodes + .into_iter() + .map(|c| c.try_into()) + .collect::, _>>()?; + + Ok(Some(Page::new(page_info, nodes))) + } else { + Ok(None) + } + } + /// Return the sequence number of the latest checkpoint that has been executed. pub async fn latest_checkpoint_sequence_number( &self, @@ -1008,6 +1046,18 @@ mod tests { ); } } + #[tokio::test] + async fn test_checkpoints_query() { + for (n, _) in NETWORKS { + let client = Client::new(n).unwrap(); + let c = client.checkpoints(None, None, None, Some(5)).await; + assert!( + c.is_ok(), + "Checkpoints query failed for network: {n}. Error: {}", + c.unwrap_err() + ); + } + } #[tokio::test] async fn test_latest_checkpoint_sequence_number_query() { diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index 57aa018bb..ecf3c68b6 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -14,6 +14,7 @@ use crate::query_types::Base64; use crate::query_types::BigInt; use crate::query_types::DateTime; use crate::query_types::Epoch; +use crate::query_types::PageInfo; // =========================================================================== // Checkpoint Queries @@ -26,6 +27,27 @@ pub struct CheckpointQuery { pub checkpoint: Option, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CheckpointsArgs")] +pub struct CheckpointsQuery { + pub checkpoints: CheckpointConnection, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "CheckpointConnection")] +pub struct CheckpointConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct CheckpointsArgs<'a> { + pub first: Option, + pub after: Option<&'a str>, + pub last: Option, + pub before: Option<&'a str>, +} + // =========================================================================== // Checkpoint Query Args // =========================================================================== diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 824b6d65e..1ac458c8c 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -29,6 +29,8 @@ pub use chain::ChainIdentifierQuery; pub use checkpoint::CheckpointArgs; pub use checkpoint::CheckpointId; pub use checkpoint::CheckpointQuery; +pub use checkpoint::CheckpointsArgs; +pub use checkpoint::CheckpointsQuery; pub use coin::CoinMetadata; pub use coin::CoinMetadataArgs; pub use coin::CoinMetadataQuery; From ddd70f90c061ab21a01c7401714ff0fcd6eaeff5 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:29:53 -0700 Subject: [PATCH 042/107] sui-graphql-client: rework the Page type to support empty pages (#36) --- crates/sui-graphql-client/src/lib.rs | 70 +++++++++++-------- .../sui-graphql-client/src/query_types/mod.rs | 2 +- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 15791556f..373a01c68 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -95,17 +95,30 @@ pub struct Page { } impl Page { + /// Return the page information. pub fn page_info(&self) -> &PageInfo { &self.page_info } + /// Return the data in the page. pub fn data(&self) -> &[T] { &self.data } + /// Internal function to create a new page with the provided data and page information. fn new(page_info: PageInfo, data: Vec) -> Self { Self { page_info, data } } + + /// Check if the page has data. + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// Internal function to create a page with no data. + fn new_empty() -> Self { + Self::new(PageInfo::default(), vec![]) + } } /// The GraphQL client for interacting with the Sui blockchain. @@ -256,7 +269,7 @@ impl Client { before: Option, first: Option, last: Option, - ) -> Result>, Error> { + ) -> Result, Error> { ensure!( !(first.is_some() && last.is_some()), "Cannot pass both first and last" @@ -286,9 +299,9 @@ impl Client { .nodes .into_iter() .collect::>(); - Ok(Some(Page::new(page_info, nodes))) + Ok(Page::new(page_info, nodes)) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -340,7 +353,7 @@ impl Client { first: Option, last: Option, coin_type: Option<&str>, - ) -> Result>, Error> { + ) -> Result, Error> { ensure!( !(first.is_some() && last.is_some()), "Cannot pass both first and last" @@ -361,16 +374,15 @@ impl Client { ) .await?; - Ok(response.map(|x| { - Page::new( - x.page_info, - x.data - .iter() - .flat_map(Coin::try_from_object) - .map(|c| c.into_owned()) - .collect::>(), - ) - })) + Ok(Page::new( + response.page_info, + response + .data + .iter() + .flat_map(Coin::try_from_object) + .map(|c| c.into_owned()) + .collect::>(), + )) } /// Stream of coins for the specified address and coin type. @@ -395,14 +407,14 @@ impl Client { None, ).await?; - if let Some(page) = response { - for object in page.data { - if let Some(coin) = Coin::try_from_object(&object) { + if !response.is_empty() { + for object in response.data() { + if let Some(coin) = Coin::try_from_object(object) { yield coin.into_owned(); } } - if let Some(end_cursor) = page.page_info.end_cursor { + if let Some(end_cursor) = response.page_info.end_cursor { after = Some(end_cursor); } else { break; @@ -574,7 +586,7 @@ impl Client { before: Option, first: Option, last: Option, - ) -> Result>, Error> { + ) -> Result, Error> { ensure!( !(first.is_some() && last.is_some()), "Cannot pass both first and last" @@ -609,9 +621,9 @@ impl Client { .collect::, bcs::Error>>() .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Event: {e}")))?; - Ok(Some(Page::new(page_info, nodes))) + Ok(Page::new(page_info, nodes)) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -678,7 +690,7 @@ impl Client { filter: Option>, first: Option, last: Option, - ) -> Result>, Error> { + ) -> Result, Error> { ensure!( !(first.is_some() && last.is_some()), "Cannot pass both first and last" @@ -716,9 +728,9 @@ impl Client { .collect::, bcs::Error>>() .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}")))?; - Ok(Some(Page::new(page_info, objects))) + Ok(Page::new(page_info, objects)) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -855,7 +867,7 @@ impl Client { first: Option, last: Option, filter: Option>, - ) -> Result>, Error> { + ) -> Result, Error> { ensure!( !(first.is_some() && last.is_some()), "Cannot pass both first and last" @@ -881,9 +893,9 @@ impl Client { .map(|n| n.try_into()) .collect::>>()?; let page = Page::new(page_info, transactions); - Ok(Some(page)) + Ok(page) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -1019,7 +1031,7 @@ mod tests { ); assert!( - av.unwrap().is_some(), + !av.unwrap().is_empty(), "Active validators query returned None for network: {n}" ); } @@ -1123,7 +1135,7 @@ mod tests { events.unwrap_err() ); assert!( - events.unwrap().is_some(), + !events.unwrap().is_empty(), "Events query returned no data for network: {n}" ); } diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 1ac458c8c..86eecfed6 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -115,7 +115,7 @@ pub struct MoveObject { // Utility Types // =========================================================================== -#[derive(cynic::QueryFragment, Debug)] +#[derive(Default, cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "PageInfo")] /// Information about pagination in a connection. pub struct PageInfo { From 9660c002f5855ef2fdd2144c9620c0707c02400c Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:05:16 -0700 Subject: [PATCH 043/107] sui-graphql-client: add a pagination filter (#37) * Add a pagination filter instead of explicitly requiring after/before/first/last, which does not fully make sense * Make it a constant * Remove the items per page limit and use the server's default. --- crates/sui-graphql-client/src/lib.rs | 119 +++++++++--------- .../src/query_types/active_validators.rs | 6 +- .../src/query_types/events.rs | 6 +- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 373a01c68..66a302bfc 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -121,6 +121,30 @@ impl Page { } } +/// Pagination direction. +pub enum Direction { + Forward, + Backward, +} + +/// Pagination options for querying the GraphQL server. It defaults to forward pagination with the +/// GraphQL server's default items per page limit. +pub struct PaginationFilter<'a> { + direction: Direction, + cursor: Option<&'a str>, + limit: Option, +} + +impl Default for PaginationFilter<'_> { + fn default() -> Self { + Self { + direction: Direction::Forward, + cursor: None, + limit: None, + } + } +} + /// The GraphQL client for interacting with the Sui blockchain. /// By default, it uses the `reqwest` crate as the HTTP client. pub struct Client { @@ -262,18 +286,16 @@ impl Client { /// Get the list of active validators for the provided epoch, including related metadata. /// If no epoch is provided, it will return the active validators for the current epoch. - pub async fn active_validators( + pub async fn active_validators<'a>( &self, epoch: Option, - after: Option, - before: Option, - first: Option, - last: Option, + pagination_filter: Option>, ) -> Result, Error> { - ensure!( - !(first.is_some() && last.is_some()), - "Cannot pass both first and last" - ); + let pagination = pagination_filter.unwrap_or_default(); + let (after, before, first, last) = match pagination.direction { + Direction::Forward => (pagination.cursor, None, pagination.limit, None), + Direction::Backward => (None, pagination.cursor, None, pagination.limit), + }; let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { id: epoch, @@ -345,32 +367,21 @@ impl Client { /// /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. - pub async fn coins( + pub async fn coins<'a>( &self, owner: Address, - after: Option<&str>, - before: Option<&str>, - first: Option, - last: Option, coin_type: Option<&str>, + pagination_filter: Option>, ) -> Result, Error> { - ensure!( - !(first.is_some() && last.is_some()), - "Cannot pass both first and last" - ); - let response = self .objects( - after, - before, Some(ObjectFilter { type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), owner: Some(owner), object_ids: None, object_keys: None, }), - first, - last, + pagination_filter, ) .await?; @@ -395,16 +406,16 @@ impl Client { let mut after = None; loop { let response = self.objects( - after.as_deref(), - None, Some(ObjectFilter { type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), owner: Some(owner), object_ids: None, object_keys: None, }), - None, - None, + Some(PaginationFilter { + cursor: after.as_deref(), + ..Default::default() + }), ).await?; if !response.is_empty() { @@ -582,15 +593,13 @@ impl Client { pub async fn events( &self, filter: Option, - after: Option, - before: Option, - first: Option, - last: Option, + pagination_filter: Option>, ) -> Result, Error> { - ensure!( - !(first.is_some() && last.is_some()), - "Cannot pass both first and last" - ); + let pagination = pagination_filter.unwrap_or_default(); + let (after, before, first, last) = match pagination.direction { + Direction::Forward => (pagination.cursor, None, pagination.limit, None), + Direction::Backward => (None, pagination.cursor, None, pagination.limit), + }; let operation = EventsQuery::build(EventsQueryArgs { filter, @@ -685,16 +694,14 @@ impl Client { /// ``` pub async fn objects( &self, - after: Option<&str>, - before: Option<&str>, filter: Option>, - first: Option, - last: Option, + pagination_filter: Option>, ) -> Result, Error> { - ensure!( - !(first.is_some() && last.is_some()), - "Cannot pass both first and last" - ); + let pagination = pagination_filter.unwrap_or_default(); + let (after, before, first, last) = match pagination.direction { + Direction::Forward => (pagination.cursor, None, pagination.limit, None), + Direction::Backward => (None, pagination.cursor, None, pagination.limit), + }; let operation = ObjectsQuery::build(ObjectsQueryArgs { after, @@ -862,16 +869,14 @@ impl Client { /// Get a page of transactions based on the provided filters. pub async fn transactions<'a>( &self, - after: Option<&str>, - before: Option<&str>, - first: Option, - last: Option, filter: Option>, + pagination_filter: Option>, ) -> Result, Error> { - ensure!( - !(first.is_some() && last.is_some()), - "Cannot pass both first and last" - ); + let pagination = pagination_filter.unwrap_or_default(); + let (after, before, first, last) = match pagination.direction { + Direction::Forward => (pagination.cursor, None, pagination.limit, None), + Direction::Backward => (None, pagination.cursor, None, pagination.limit), + }; let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { after, @@ -1023,7 +1028,7 @@ mod tests { async fn test_active_validators() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let av = client.active_validators(None, None, None, None, None).await; + let av = client.active_validators(None, None).await; assert!( av.is_ok(), "Active validators query failed for network: {n}. Error: {}", @@ -1128,7 +1133,7 @@ mod tests { async fn test_events_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let events = client.events(None, None, None, None, Some(10)).await; + let events = client.events(None, None).await; assert!( events.is_ok(), "Events query failed for network: {n}. Error: {}", @@ -1146,7 +1151,7 @@ mod tests { async fn test_objects_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let objects = client.objects(None, None, None, None, None).await; + let objects = client.objects(None, None).await; assert!( objects.is_ok(), "Objects query failed for network: {n}. Error: {}", @@ -1186,9 +1191,7 @@ mod tests { async fn test_coins_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let coins = client - .coins("0x1".parse().unwrap(), None, None, None, None, None) - .await; + let coins = client.coins("0x1".parse().unwrap(), None, None).await; assert!( coins.is_ok(), "Coins query failed for network: {n}. Error: {}", @@ -1214,7 +1217,7 @@ mod tests { async fn test_transactions_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let transactions = client.transactions(None, None, None, Some(5), None).await; + let transactions = client.transactions(None, None).await; assert!( transactions.is_ok(), "Transactions query failed for network: {n}. Error: {}", diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs index a46df9f10..925b70975 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -21,10 +21,10 @@ pub struct ActiveValidatorsQuery { } #[derive(cynic::QueryVariables, Debug)] -pub struct ActiveValidatorsArgs { +pub struct ActiveValidatorsArgs<'a> { pub id: Option, - pub after: Option, - pub before: Option, + pub after: Option<&'a str>, + pub before: Option<&'a str>, pub first: Option, pub last: Option, } diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index b281c1e8a..90781ffe2 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -22,10 +22,10 @@ pub struct EventsQuery { // =========================================================================== #[derive(cynic::QueryVariables, Debug)] -pub struct EventsQueryArgs { +pub struct EventsQueryArgs<'a> { pub filter: Option, - pub after: Option, - pub before: Option, + pub after: Option<&'a str>, + pub before: Option<&'a str>, pub first: Option, pub last: Option, } From 3add02837a133a818e64f7e3ba5484bc1b6dcd26 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 21 Oct 2024 12:54:33 -0500 Subject: [PATCH 044/107] passkey: update passkey challenge format Passkeys now sign the same signing message as other formats resulting in a slight tweak to the authenticator data shape. --- .../sui-sdk-types/src/types/crypto/passkey.rs | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs index 7df6b83a7..c1b76c429 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/types/crypto/passkey.rs @@ -1,5 +1,4 @@ use super::{Secp256r1PublicKey, Secp256r1Signature}; -use crate::types::Digest; /// An passkey authenticator with parsed fields. See field definition below. Can /// be initialized from [struct RawPasskeyAuthenticator]. @@ -13,13 +12,8 @@ pub struct PasskeyAuthenticator { /// Initialized from `user_signature` in `RawPasskeyAuthenticator`. signature: Secp256r1Signature, - /// Valid intent parsed from the first 3 bytes of - /// `client_data_json.challenge`. - intent: [u8; 3], - - /// Valid tx_digest parsed from the last 32 bytes of - /// `client_data_json.challenge`. - digest: Digest, + /// Parsed challenge bytes from `client_data_json.challenge`. + challenge: Vec, /// `authenticatorData` is a bytearray that encodes /// [Authenticator Data](https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data) @@ -145,14 +139,10 @@ mod serialization { origin: _, } = serde_json::from_str(&client_data_json).map_err(SignatureFromBytesError::new)?; - // challenge is 3 byte intent | 32 byte hash - let mut challenge_buf = [0; 3 + Digest::LENGTH]; - // decode unpadded url endoded base64 data per spec: // https://w3c.github.io/webauthn/#base64url-encoding - ::decode( - challenge, - &mut challenge_buf, + let challenge = ::decode_vec( + &challenge, ) .map_err(|e| { SignatureFromBytesError::new(format!( @@ -160,14 +150,10 @@ mod serialization { )) })?; - let intent = challenge_buf[..3].try_into().unwrap(); - let digest = challenge_buf[3..].try_into().unwrap(); - Ok(Self { public_key, signature, - intent, - digest: Digest::new(digest), + challenge, authenticator_data, client_data_json, }) @@ -303,18 +289,14 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { ( any::(), any::(), - any::(), - any::<[u8; 3]>(), + vec(any::(), 32), vec(any::(), 0..32), ) .prop_map( - |(public_key, signature, digest, intent, authenticator_data)| { - let mut challenge_buf = Vec::new(); - challenge_buf.extend_from_slice(&intent); - challenge_buf.extend_from_slice(digest.inner()); + |(public_key, signature, challenge_bytes, authenticator_data)| { let challenge = ::encode_string( - &challenge_buf, + &challenge_bytes, ); let client_data_json = serde_json::to_string(&CollectedClientData { ty: ClientDataType::Get, @@ -326,8 +308,7 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { Self { public_key, signature, - intent, - digest, + challenge: challenge_bytes, authenticator_data, client_data_json, } @@ -336,3 +317,16 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { .boxed() } } + +#[cfg(test)] +mod tests { + use crate::types::UserSignature; + + #[test] + fn base64_encoded_passkey_user_signature() { + let b64 = "BiVYDmenOnqS+thmz5m5SrZnWaKXZLVxgh+rri6LHXs25B0AAAAAnQF7InR5cGUiOiJ3ZWJhdXRobi5nZXQiLCAiY2hhbGxlbmdlIjoiQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQSIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6NTE3MyIsImNyb3NzT3JpZ2luIjpmYWxzZSwgInVua25vd24iOiAidW5rbm93biJ9YgJMwqcOmZI7F/N+K5SMe4DRYCb4/cDWW68SFneSHoD2GxKKhksbpZ5rZpdrjSYABTCsFQQBpLORzTvbj4edWKd/AsEBeovrGvHR9Ku7critg6k7qvfFlPUngujXfEzXd8Eg"; + + let sig = UserSignature::from_base64(b64).unwrap(); + assert!(matches!(sig, UserSignature::Passkey(_))); + } +} From 3d70541fa23fe8cf972d6b8112af678231512e85 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 22 Oct 2024 06:26:49 -0700 Subject: [PATCH 045/107] sui-graphql-client: simplify pagination filter usage (#39) --- crates/sui-graphql-client/src/lib.rs | 107 +++++++++++++-------------- 1 file changed, 52 insertions(+), 55 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 66a302bfc..8a53c97fe 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -110,7 +110,7 @@ impl Page { Self { page_info, data } } - /// Check if the page has data. + /// Check if the page has no data. pub fn is_empty(&self) -> bool { self.data.is_empty() } @@ -122,29 +122,22 @@ impl Page { } /// Pagination direction. +#[derive(Default)] pub enum Direction { + #[default] Forward, Backward, } /// Pagination options for querying the GraphQL server. It defaults to forward pagination with the /// GraphQL server's default items per page limit. +#[derive(Default)] pub struct PaginationFilter<'a> { direction: Direction, cursor: Option<&'a str>, limit: Option, } -impl Default for PaginationFilter<'_> { - fn default() -> Self { - Self { - direction: Direction::Forward, - cursor: None, - limit: None, - } - } -} - /// The GraphQL client for interacting with the Sui blockchain. /// By default, it uses the `reqwest` crate as the HTTP client. pub struct Client { @@ -204,6 +197,28 @@ impl Client { self.rpc.as_str() } + /// Internal function to handle pagination filters and return the appropriate values. + fn pagination_filter<'a>( + &self, + pagination_filter: PaginationFilter<'a>, + ) -> (Option<&'a str>, Option<&'a str>, Option, Option) { + let (after, before, first, last) = match pagination_filter.direction { + Direction::Forward => ( + pagination_filter.cursor, + None, + pagination_filter.limit, + None, + ), + Direction::Backward => ( + None, + pagination_filter.cursor, + None, + pagination_filter.limit, + ), + }; + (after, before, first, last) + } + /// Run a query on the GraphQL server and return the response. /// This method returns [`cynic::GraphQlResponse`] over the query type `T`, and it is /// intended to be used with custom queries. @@ -289,13 +304,9 @@ impl Client { pub async fn active_validators<'a>( &self, epoch: Option, - pagination_filter: Option>, + pagination_filter: PaginationFilter<'a>, ) -> Result, Error> { - let pagination = pagination_filter.unwrap_or_default(); - let (after, before, first, last) = match pagination.direction { - Direction::Forward => (pagination.cursor, None, pagination.limit, None), - Direction::Backward => (None, pagination.cursor, None, pagination.limit), - }; + let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { id: epoch, @@ -371,7 +382,7 @@ impl Client { &self, owner: Address, coin_type: Option<&str>, - pagination_filter: Option>, + pagination_filter: PaginationFilter<'a>, ) -> Result, Error> { let response = self .objects( @@ -412,13 +423,12 @@ impl Client { object_ids: None, object_keys: None, }), - Some(PaginationFilter { + PaginationFilter { cursor: after.as_deref(), ..Default::default() - }), + }, ).await?; - if !response.is_empty() { for object in response.data() { if let Some(coin) = Coin::try_from_object(object) { yield coin.into_owned(); @@ -430,9 +440,6 @@ impl Client { } else { break; } - } else { - break; - } } }) } @@ -494,20 +501,18 @@ impl Client { } /// Get a page of [`CheckpointSummary`] for the provided parameters. - pub async fn checkpoints( + pub async fn checkpoints<'a>( &self, - after: Option<&str>, - before: Option<&str>, - first: Option, - last: Option, + pagination_filter: PaginationFilter<'a>, ) -> Result>, Error> { + let (after, before, first, last) = self.pagination_filter(pagination_filter); + let operation = CheckpointsQuery::build(CheckpointsArgs { after, before, first, last, }); - let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { @@ -593,13 +598,9 @@ impl Client { pub async fn events( &self, filter: Option, - pagination_filter: Option>, + pagination_filter: PaginationFilter<'_>, ) -> Result, Error> { - let pagination = pagination_filter.unwrap_or_default(); - let (after, before, first, last) = match pagination.direction { - Direction::Forward => (pagination.cursor, None, pagination.limit, None), - Direction::Backward => (None, pagination.cursor, None, pagination.limit), - }; + let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = EventsQuery::build(EventsQueryArgs { filter, @@ -695,14 +696,9 @@ impl Client { pub async fn objects( &self, filter: Option>, - pagination_filter: Option>, + pagination_filter: PaginationFilter<'_>, ) -> Result, Error> { - let pagination = pagination_filter.unwrap_or_default(); - let (after, before, first, last) = match pagination.direction { - Direction::Forward => (pagination.cursor, None, pagination.limit, None), - Direction::Backward => (None, pagination.cursor, None, pagination.limit), - }; - + let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = ObjectsQuery::build(ObjectsQueryArgs { after, before, @@ -870,13 +866,9 @@ impl Client { pub async fn transactions<'a>( &self, filter: Option>, - pagination_filter: Option>, + pagination_filter: PaginationFilter<'a>, ) -> Result, Error> { - let pagination = pagination_filter.unwrap_or_default(); - let (after, before, first, last) = match pagination.direction { - Direction::Forward => (pagination.cursor, None, pagination.limit, None), - Direction::Backward => (None, pagination.cursor, None, pagination.limit), - }; + let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { after, @@ -942,6 +934,7 @@ mod tests { use futures::StreamExt; use crate::Client; + use crate::PaginationFilter; use crate::DEVNET_HOST; use crate::LOCAL_HOST; use crate::MAINNET_HOST; @@ -1028,7 +1021,9 @@ mod tests { async fn test_active_validators() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let av = client.active_validators(None, None).await; + let av = client + .active_validators(None, PaginationFilter::default()) + .await; assert!( av.is_ok(), "Active validators query failed for network: {n}. Error: {}", @@ -1067,7 +1062,7 @@ mod tests { async fn test_checkpoints_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let c = client.checkpoints(None, None, None, Some(5)).await; + let c = client.checkpoints(PaginationFilter::default()).await; assert!( c.is_ok(), "Checkpoints query failed for network: {n}. Error: {}", @@ -1133,7 +1128,7 @@ mod tests { async fn test_events_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let events = client.events(None, None).await; + let events = client.events(None, PaginationFilter::default()).await; assert!( events.is_ok(), "Events query failed for network: {n}. Error: {}", @@ -1151,7 +1146,7 @@ mod tests { async fn test_objects_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let objects = client.objects(None, None).await; + let objects = client.objects(None, PaginationFilter::default()).await; assert!( objects.is_ok(), "Objects query failed for network: {n}. Error: {}", @@ -1191,7 +1186,9 @@ mod tests { async fn test_coins_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let coins = client.coins("0x1".parse().unwrap(), None, None).await; + let coins = client + .coins("0x1".parse().unwrap(), None, PaginationFilter::default()) + .await; assert!( coins.is_ok(), "Coins query failed for network: {n}. Error: {}", @@ -1217,7 +1214,7 @@ mod tests { async fn test_transactions_query() { for (n, _) in NETWORKS { let client = Client::new(n).unwrap(); - let transactions = client.transactions(None, None).await; + let transactions = client.transactions(None, PaginationFilter::default()).await; assert!( transactions.is_ok(), "Transactions query failed for network: {n}. Error: {}", From 0ca4534cd51686cd0931aee09a0096ccdbe87632 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:52:15 -0700 Subject: [PATCH 046/107] ci: separate tests that require network and run localnet in ci (#38) --- .github/workflows/ci.yml | 54 ++++ Makefile | 6 +- crates/sui-graphql-client/README.md | 6 +- crates/sui-graphql-client/src/lib.rs | 380 ++++++++++++++------------- 4 files changed, 253 insertions(+), 193 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6fcbe477..4686d7640 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,3 +74,57 @@ jobs: - name: Run tests in wasm run: make wasm + + + run_tests_with_network: + runs-on: ubuntu-latest + env: + EPOCH_DURATION_MS: 10000 + services: + postgres: # we need this postgres instance for running a local network with indexer and graphql + image: postgres + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgrespw + POSTGRES_DB: sui_indexer_v2 + POSTGRES_HOST_AUTH_METHOD: trust + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: rust version + run: | + rustc --version + cargo --version + + - uses: taiki-e/install-action@cargo-nextest + + - name: Get the Sui testnet binary and start a local network + shell: bash + env: + SUI_BINARY_VERSION: "1.35.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests + SUI_NETWORK_RELEASE: "testnet" # which release to use + run: | + ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" + download_url="https://github.com/mystenlabs/sui/releases/download/$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION/$ASSET_NAME" + + echo "Downloading testnet binary from $download_url" + wget -q $download_url -O sui.tgz + tar -zxvf sui.tgz ./sui + echo "Starting local network with a faucet, an indexer (port 5432) and GraphQL. Epoch duration is set to $EPOCH_DURATION_MS ms" + ./sui start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name sui_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & + + - name: Run tests that require local network (GraphQL Client and Tx Builder) + env: + NETWORK: "local" # other expected options are mainnet, testnet, or devnet, or an actual URL to a GraphQL server: http://localhost:port + run: | + sleep $((EPOCH_DURATION_MS / 1000)) # wait for the network to get to epoch #2 + make test-with-localnet + diff --git a/Makefile b/Makefile index dbce9180b..64480bac6 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,13 @@ clippy: .PHONY: test test: - cargo nextest run --all-features + cargo nextest run --all-features -p sui-sdk-types -p sui-crypto cargo test --doc +.PHONY: test-with-localnet +test-with-localnet: + cargo nextest run -p sui-graphql-client + .PHONY: wasm wasm: $(MAKE) -C crates/iota-sdk-types wasm diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index a8840d0b7..9901fb414 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -20,7 +20,7 @@ executing transactions and more. ## Connecting to a GraphQL server Instantiate a client with [`Client::new(server: &str)`] or use one of the predefined functions for different networks [`Client`]. -```rust +```rust, no_run use sui_graphql_client::Client; use anyhow::Result; @@ -118,7 +118,7 @@ The generated query types are defined below. Note that the `id` variable is opti Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. -```rust,ignore +```rust, ignore #[derive(cynic::QueryVariables, Debug)] pub struct CustomQueryVariables { pub id: Option, @@ -145,7 +145,7 @@ pub struct BigInt(pub String); ``` The complete example is shown below: -```rust +```rust, ignore use anyhow::Result; use cynic::QueryBuilder; diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 8a53c97fe..d11b7ae43 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -929,6 +929,7 @@ impl Client { } } +// This function is used in tests to create a new client instance for the local server. #[cfg(test)] mod tests { use futures::StreamExt; @@ -939,7 +940,17 @@ mod tests { use crate::LOCAL_HOST; use crate::MAINNET_HOST; use crate::TESTNET_HOST; - const NETWORKS: [(&str, &str); 2] = [(MAINNET_HOST, "35834a8a"), (TESTNET_HOST, "4c78adac")]; + + fn test_client() -> Client { + let network = std::env::var("NETWORK").unwrap_or_else(|_| "local".to_string()); + match network.as_str() { + "mainnet" => Client::new_mainnet(), + "testnet" => Client::new_testnet(), + "devnet" => Client::new_devnet(), + "local" => Client::new_localhost(), + _ => Client::new(&network).expect("Invalid network URL: {network}"), + } + } #[test] fn test_rpc_server() { @@ -958,243 +969,235 @@ mod tests { #[tokio::test] async fn test_balance_query() { - for (n, _) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let balance = client.balance("0x1".parse().unwrap(), None).await; - assert!(balance.is_ok(), "Balance query failed for network: {n}"); - } + let client = test_client(); + let balance = client.balance("0x1".parse().unwrap(), None).await; + assert!( + balance.is_ok(), + "Balance query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_chain_id() { - for (n, id) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let chain_id = client.chain_id().await; - assert!(chain_id.is_ok()); - assert_eq!(&chain_id.unwrap(), id); - } + let client = test_client(); + let chain_id = client.chain_id().await; + assert!(chain_id.is_ok()); } #[tokio::test] async fn test_reference_gas_price_query() { - for (n, _) in NETWORKS.iter() { - let client = Client::new(n).unwrap(); - let rgp = client.reference_gas_price(None).await; - assert!( - rgp.is_ok(), - "Reference gas price query failed for network: {n}" - ); - } + let client = test_client(); + let rgp = client.reference_gas_price(None).await; + assert!( + rgp.is_ok(), + "Reference gas price query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_protocol_config_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let pc = client.protocol_config(None).await; - assert!(pc.is_ok()); - - // test specific version - let pc = client.protocol_config(Some(50)).await; - assert!(pc.is_ok()); - let pc = pc.unwrap(); - if let Some(pc) = pc { - assert_eq!( - pc.protocol_version, 50, - "Protocol version query mismatch for network: {n}. Expected: 50, received: {}", - pc.protocol_version - ); - } + let client = test_client(); + let pc = client.protocol_config(None).await; + assert!(pc.is_ok()); + + // test specific version + let pc = client.protocol_config(Some(50)).await; + assert!(pc.is_ok()); + let pc = pc.unwrap(); + if let Some(pc) = pc { + assert_eq!( + pc.protocol_version, + 50, + "Protocol version query mismatch for {} network. Expected: 50, received: {}", + client.rpc_server(), + pc.protocol_version + ); } } #[tokio::test] async fn test_service_config_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let sc = client.service_config().await; - assert!(sc.is_ok(), "Service config query failed for network: {n}"); - } + let client = test_client(); + let sc = client.service_config().await; + assert!( + sc.is_ok(), + "Service config query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_active_validators() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let av = client - .active_validators(None, PaginationFilter::default()) - .await; - assert!( - av.is_ok(), - "Active validators query failed for network: {n}. Error: {}", - av.unwrap_err() - ); + let client = test_client(); + let av = client + .active_validators(None, PaginationFilter::default()) + .await; + assert!( + av.is_ok(), + "Active validators query failed for {} network. Error: {}", + client.rpc_server(), + av.unwrap_err() + ); - assert!( - !av.unwrap().is_empty(), - "Active validators query returned None for network: {n}" - ); - } + assert!( + !av.unwrap().is_empty(), + "Active validators query returned None for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_coin_metadata_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let cm = client.coin_metadata("0x2::sui::SUI").await; - assert!(cm.is_ok(), "Coin metadata query failed for network: {n}"); - } + let client = test_client(); + let cm = client.coin_metadata("0x2::sui::SUI").await; + assert!( + cm.is_ok(), + "Coin metadata query failed for {} network", + client.rpc_server() + ); } #[tokio::test] async fn test_checkpoint_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let c = client.checkpoint(None, None).await; - assert!( - c.is_ok(), - "Checkpoint query failed for network: {n}. Error: {}", - c.unwrap_err() - ); - } + let client = test_client(); + let c = client.checkpoint(None, None).await; + assert!( + c.is_ok(), + "Checkpoint query failed for {} network. Error: {}", + client.rpc_server(), + c.unwrap_err() + ); } #[tokio::test] async fn test_checkpoints_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let c = client.checkpoints(PaginationFilter::default()).await; - assert!( - c.is_ok(), - "Checkpoints query failed for network: {n}. Error: {}", - c.unwrap_err() - ); - } + let client = test_client(); + let c = client.checkpoints(PaginationFilter::default()).await; + assert!( + c.is_ok(), + "Checkpoints query failed for {} network. Error: {}", + client.rpc_server(), + c.unwrap_err() + ); } #[tokio::test] async fn test_latest_checkpoint_sequence_number_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let last_checkpoint = client.latest_checkpoint_sequence_number().await; - assert!( - last_checkpoint.is_ok(), - "Latest checkpoint sequence number query failed for network: {n}. Error: {}", - last_checkpoint.unwrap_err() - ); - } + let client = test_client(); + let last_checkpoint = client.latest_checkpoint_sequence_number().await; + assert!( + last_checkpoint.is_ok(), + "Latest checkpoint sequence number query failed for {} network. Error: {}", + client.rpc_server(), + last_checkpoint.unwrap_err() + ); } #[tokio::test] async fn test_epoch_total_checkpoints_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_total_checkpoints(None).await; - assert!( - e.is_ok(), - "Epoch total checkpoints query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_total_checkpoints(None).await; + assert!( + e.is_ok(), + "Epoch total checkpoints query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] async fn test_epoch_total_transaction_blocks_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_total_transaction_blocks(None).await; - assert!( - e.is_ok(), - "Epoch total transaction blocks query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_total_transaction_blocks(None).await; + assert!( + e.is_ok(), + "Epoch total transaction blocks query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] async fn test_epoch_summary_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let e = client.epoch_summary(None).await; - assert!( - e.is_ok(), - "Epoch summary query failed for network: {n}. Error: {}", - e.unwrap_err() - ); - } + let client = test_client(); + let e = client.epoch_summary(None).await; + assert!( + e.is_ok(), + "Epoch summary query failed for {} network. Error: {}", + client.rpc_server(), + e.unwrap_err() + ); } #[tokio::test] #[ignore] // schema was updated, but the service has not been released with the new schema async fn test_events_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let events = client.events(None, PaginationFilter::default()).await; - assert!( - events.is_ok(), - "Events query failed for network: {n}. Error: {}", - events.unwrap_err() - ); - assert!( - !events.unwrap().is_empty(), - "Events query returned no data for network: {n}" - ); - } + let client = test_client(); + let events = client.events(None, PaginationFilter::default()).await; + assert!( + events.is_ok(), + "Events query failed for {} network. Error: {}", + client.rpc_server(), + events.unwrap_err() + ); + assert!( + !events.unwrap().is_empty(), + "Events query returned no data for {} network", + client.rpc_server() + ); } #[tokio::test] #[ignore] async fn test_objects_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let objects = client.objects(None, PaginationFilter::default()).await; - assert!( - objects.is_ok(), - "Objects query failed for network: {n}. Error: {}", - objects.unwrap_err() - ); - } + let client = test_client(); + let objects = client.objects(None, PaginationFilter::default()).await; + assert!( + objects.is_ok(), + "Objects query failed for {} network. Error: {}", + client.rpc_server(), + objects.unwrap_err() + ); } #[tokio::test] #[ignore] async fn test_object_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let object = client.object("0x5".parse().unwrap(), None).await; - assert!( - object.is_ok(), - "Object query failed for network: {n}. Error: {}", - object.unwrap_err() - ); - } + let client = test_client(); + let object = client.object("0x5".parse().unwrap(), None).await; + assert!( + object.is_ok(), + "Object query failed for {} network. Error: {}", + client.rpc_server(), + object.unwrap_err() + ); } #[tokio::test] async fn test_object_bcs_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let object_bcs = client.object_bcs("0x5".parse().unwrap()).await; - assert!( - object_bcs.is_ok(), - "Object bcs query failed for network: {n}. Error: {}", - object_bcs.unwrap_err() - ); - } + let client = test_client(); + let object_bcs = client.object_bcs("0x5".parse().unwrap()).await; + assert!( + object_bcs.is_ok(), + "Object bcs query failed for {} network. Error: {}", + client.rpc_server(), + object_bcs.unwrap_err() + ); } #[tokio::test] async fn test_coins_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let coins = client - .coins("0x1".parse().unwrap(), None, PaginationFilter::default()) - .await; - assert!( - coins.is_ok(), - "Coins query failed for network: {n}. Error: {}", - coins.unwrap_err() - ); - } + let client = test_client(); + let coins = client + .coins("0x1".parse().unwrap(), None, PaginationFilter::default()) + .await; + assert!( + coins.is_ok(), + "Coins query failed for {} network. Error: {}", + client.rpc_server(), + coins.unwrap_err() + ); } #[tokio::test] @@ -1212,33 +1215,32 @@ mod tests { #[tokio::test] #[ignore] async fn test_transactions_query() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let transactions = client.transactions(None, PaginationFilter::default()).await; - assert!( - transactions.is_ok(), - "Transactions query failed for network: {n}. Error: {}", - transactions.unwrap_err() - ); - } + let client = test_client(); + let transactions = client.transactions(None, PaginationFilter::default()).await; + assert!( + transactions.is_ok(), + "Transactions query failed for {} network. Error: {}", + client.rpc_server(), + transactions.unwrap_err() + ); } #[tokio::test] async fn test_total_supply() { - for (n, _) in NETWORKS { - let client = Client::new(n).unwrap(); - let ts = client.total_supply("0x2::sui::SUI").await; - assert!( - ts.is_ok(), - "Total supply query failed for network: {n}. Error: {}", - ts.unwrap_err() - ); - assert_eq!( - ts.unwrap().unwrap(), - 10_000_000_000, - "Total supply mismatch for network: {n}" - ); - } + let client = test_client(); + let ts = client.total_supply("0x2::sui::SUI").await; + assert!( + ts.is_ok(), + "Total supply query failed for {} network. Error: {}", + client.rpc_server(), + ts.unwrap_err() + ); + assert_eq!( + ts.unwrap().unwrap(), + 10_000_000_000, + "Total supply mismatch for {} network", + client.rpc_server() + ); } #[tokio::test] From c642db89ecb5c92333f9fa1015c5518030fa3463 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:20:27 -0700 Subject: [PATCH 047/107] sui-graphql-client: fix coin stream test (#42) --- crates/sui-graphql-client/Cargo.toml | 2 ++ crates/sui-graphql-client/src/lib.rs | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 46b95d101..d52aa145a 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -25,6 +25,8 @@ tracing = "0.1.40" tokio = "1.40.0" [dev-dependencies] +sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "rand", "hash"] } +rand = "0.8.5" tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index d11b7ae43..118ddb0f0 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -933,7 +933,9 @@ impl Client { #[cfg(test)] mod tests { use futures::StreamExt; + use sui_types::types::Ed25519PublicKey; + use crate::faucet::FaucetClient; use crate::Client; use crate::PaginationFilter; use crate::DEVNET_HOST; @@ -1202,8 +1204,17 @@ mod tests { #[tokio::test] async fn test_coins_stream() { - let client = Client::new_testnet(); - let mut stream = client.coins_stream("0x1".parse().unwrap(), None); + let client = test_client(); + let faucet = match client.rpc_server() { + LOCAL_HOST => FaucetClient::local(), + TESTNET_HOST => FaucetClient::testnet(), + DEVNET_HOST => FaucetClient::devnet(), + _ => return, + }; + let key = Ed25519PublicKey::generate(rand::thread_rng()); + let address = key.to_address(); + faucet.request_and_wait(address).await.unwrap(); + let mut stream = client.coins_stream(address, None); let mut num_coins = 0; while let Some(result) = stream.next().await { assert!(result.is_ok()); From fbaa407636470c01717e780c3a893b4490c0fb11 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:55:38 -0700 Subject: [PATCH 048/107] sui-graphql-client: add dynamic fields query (#18) --- crates/sui-graphql-client/src/lib.rs | 304 +++++++++++++++++- .../src/query_types/dynamic_fields.rs | 198 ++++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 28 ++ .../src/query_types/object.rs | 2 + 4 files changed, 527 insertions(+), 5 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/dynamic_fields.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 118ddb0f0..3b8fbd9ac 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -22,6 +22,11 @@ use query_types::CoinMetadataArgs; use query_types::CoinMetadataQuery; use query_types::DryRunArgs; use query_types::DryRunQuery; +use query_types::DynamicFieldArgs; +use query_types::DynamicFieldConnectionArgs; +use query_types::DynamicFieldQuery; +use query_types::DynamicFieldsOwnerQuery; +use query_types::DynamicObjectFieldQuery; use query_types::EpochSummaryArgs; use query_types::EpochSummaryQuery; use query_types::EventFilter; @@ -48,6 +53,8 @@ use query_types::TransactionMetadata; use query_types::TransactionsFilter; use query_types::Validator; +use serde::de::DeserializeOwned; +use serde::Serialize; use sui_types::types::framework::Coin; use sui_types::types::Address; use sui_types::types::CheckpointSequenceNumber; @@ -58,6 +65,7 @@ use sui_types::types::SignedTransaction; use sui_types::types::Transaction; use sui_types::types::TransactionEffects; use sui_types::types::TransactionKind; +use sui_types::types::TypeTag; use sui_types::types::UserSignature; use anyhow::anyhow; @@ -79,12 +87,44 @@ const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; const LOCAL_HOST: &str = "http://localhost:9125/graphql"; static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); +// =========================================================================== +// Output Types +// =========================================================================== + #[derive(Debug)] pub struct DryRunResult { pub effects: Option, pub error: Option, } +#[derive(Debug)] +pub struct DynamicFieldName { + /// The type name of this dynamic field name + pub type_: TypeTag, + /// The bcs bytes of this dynamic field name + pub bcs: Vec, + /// The json representation of the dynamic field name + pub json: Option, +} + +type Typename = String; + +#[derive(Debug)] +pub struct DynamicFieldOutput { + /// The name of the dynamic field + pub name: DynamicFieldName, + /// The dynamic field value typename and bcs + pub value: Option<(Typename, Vec)>, + /// The json representation of the dynamic field value object + pub value_as_json: Option, +} + +/// Helper struct for passing a value that has a type that implements Serialize, for the dynamic +/// fields API. +pub struct NameValue(Vec); +/// Helper struct for passing a raw bcs value. +pub struct BcsName(pub Vec); + #[derive(Debug)] /// A page of items returned by the GraphQL server. pub struct Page { @@ -138,6 +178,34 @@ pub struct PaginationFilter<'a> { limit: Option, } +impl From for NameValue { + fn from(value: T) -> Self { + NameValue(bcs::to_bytes(&value).unwrap()) + } +} + +impl From for NameValue { + fn from(value: BcsName) -> Self { + NameValue(value.0) + } +} + +impl DynamicFieldOutput { + pub fn deserialize( + &self, + expected_type: TypeTag, + ) -> Result { + assert_eq!( + expected_type, self.name.type_, + "Expected type {}, but got {}", + expected_type, self.name.type_ + ); + + let bcs = &self.name.bcs; + bcs::from_bytes::(bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) + } +} + /// The GraphQL client for interacting with the Sui blockchain. /// By default, it uses the `reqwest` crate as the HTTP client. pub struct Client { @@ -261,7 +329,7 @@ impl Client { /// provided. /// /// This will return `Ok(None)` if the epoch requested is not available in the GraphQL service - /// (e.g., due to prunning). + /// (e.g., due to pruning). pub async fn reference_gas_price(&self, epoch: Option) -> Result, Error> { let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); let response = self.run_query(&operation).await?; @@ -544,12 +612,145 @@ impl Client { .map(|c| c.sequence_number)) } + // =========================================================================== + // Dynamic Field(s) API + // =========================================================================== + + /// Access a dynamic field on an object using its name. Names are arbitrary Move values whose + /// type have copy, drop, and store, and are specified using their type, and their BCS + /// contents, Base64 encoded. + /// + /// The `name` argument can be either a [`BcsName`] for passing raw bcs bytes or a type that + /// implements Serialize. + /// + /// This returns [`DynamicFieldOutput`] which contains the name, the value as json, and object. + /// + /// # Example + /// ```rust,ignore + /// + /// let client = sui_graphql_client::Client::new_devnet(); + /// let address = Address::from_str("0x5").unwrap(); + /// let df = client.dynamic_field_with_name(address, "u64", 2u64).await.unwrap(); + /// + /// # alternatively, pass in the bcs bytes + /// let bcs = base64ct::Base64::decode_vec("AgAAAAAAAAA=").unwrap(); + /// let df = client.dynamic_field(address, "u64", BcsName(bcs)).await.unwrap(); + /// ``` + pub async fn dynamic_field( + &self, + address: Address, + type_: TypeTag, + name: impl Into, + ) -> Result, Error> { + let bcs = name.into().0; + let operation = DynamicFieldQuery::build(DynamicFieldArgs { + address, + name: crate::query_types::DynamicFieldName { + type_: type_.to_string(), + bcs: crate::query_types::Base64(base64ct::Base64::encode_string(&bcs)), + }, + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + let result = response + .data + .and_then(|d| d.object) + .and_then(|o| o.dynamic_field) + .map(|df| df.try_into()) + .transpose() + .map_err(|e| Error::msg(format!("{:?}", e)))?; + + Ok(result) + } + + /// Access a dynamic object field on an object using its name. Names are arbitrary Move values whose + /// type have copy, drop, and store, and are specified using their type, and their BCS + /// contents, Base64 encoded. + /// + /// The `name` argument can be either a [`BcsName`] for passing raw bcs bytes or a type that + /// implements Serialize. + /// + /// This returns [`DynamicFieldOutput`] which contains the name, the value as json, and object. + pub async fn dynamic_object_field( + &self, + address: Address, + type_: TypeTag, + name: impl Into, + ) -> Result, Error> { + let bcs = name.into().0; + let operation = DynamicObjectFieldQuery::build(DynamicFieldArgs { + address, + name: crate::query_types::DynamicFieldName { + type_: type_.to_string(), + bcs: crate::query_types::Base64(base64ct::Base64::encode_string(&bcs)), + }, + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + let result: Option = response + .data + .and_then(|d| d.object) + .and_then(|o| o.dynamic_object_field) + .map(|df| df.try_into()) + .transpose() + .map_err(|e| Error::msg(format!("{:?}", e)))?; + Ok(result) + } + + /// Get a page of dynamic fields for the provided address. Note that this will also fetch + /// dynamic fields on wrapped objects. + /// + /// This returns [`Page`] of [`DynamicFieldOutput`]s. + pub async fn dynamic_fields<'a>( + &self, + address: Address, + pagination_filter: PaginationFilter<'a>, + ) -> Result>, Error> { + let (after, before, first, last) = self.pagination_filter(pagination_filter); + let operation = DynamicFieldsOwnerQuery::build(DynamicFieldConnectionArgs { + address, + after, + before, + first, + last, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + let Some(DynamicFieldsOwnerQuery { owner: Some(dfs) }) = response.data else { + return Ok(Some(Page::new_empty())); + }; + + Ok(Some(Page::new( + dfs.dynamic_fields.page_info, + dfs.dynamic_fields + .nodes + .into_iter() + .map(TryInto::try_into) + .collect::, Error>>() + .map_err(|e| Error::msg(format!("{:?}", e)))?, + ))) + } + // =========================================================================== // Epoch API // =========================================================================== /// Return the number of checkpoints in this epoch. This will return `Ok(None)` if the epoch - /// requested is not available in the GraphQL service (e.g., due to prunning). + /// requested is not available in the GraphQL service (e.g., due to pruning). pub async fn epoch_total_checkpoints(&self, epoch: Option) -> Result, Error> { let response = self.epoch_summary(epoch).await?; @@ -564,7 +765,7 @@ impl Client { } /// Return the number of transaction blocks in this epoch. This will return `Ok(None)` if the - /// epoch requested is not available in the GraphQL service (e.g., due to prunning). + /// epoch requested is not available in the GraphQL service (e.g., due to pruning). pub async fn epoch_total_transaction_blocks( &self, epoch: Option, @@ -643,7 +844,7 @@ impl Client { /// Return an object based on the provided [`Address`]. /// - /// If the object does not exist (e.g., due to prunning), this will return `Ok(None)`. + /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. /// Similarly, if this is not an object but an address, it will return `Ok(None)`. pub async fn object( &self, @@ -761,6 +962,63 @@ impl Client { } } + /// Return the contents' JSON of an object that is a Move object. + /// + /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. + /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + pub async fn move_object_contents( + &self, + address: Address, + version: Option, + ) -> Result, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data { + Ok(object + .object + .and_then(|o| o.as_move_object) + .and_then(|o| o.contents) + .and_then(|mv| mv.json)) + } else { + Ok(None) + } + } + /// Return the BCS of an object that is a Move object. + /// + /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. + /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + pub async fn move_object_contents_bcs( + &self, + address: Address, + version: Option, + ) -> Result>, Error> { + let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(object) = response.data { + object + .object + .and_then(|o| o.as_move_object) + .and_then(|o| o.contents) + .map(|bcs| base64ct::Base64::decode_vec(bcs.bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}"))) + } else { + Ok(None) + } + } + // =========================================================================== // Dry Run API // =========================================================================== @@ -932,10 +1190,13 @@ impl Client { // This function is used in tests to create a new client instance for the local server. #[cfg(test)] mod tests { + use base64ct::Encoding; use futures::StreamExt; use sui_types::types::Ed25519PublicKey; + use sui_types::types::TypeTag; use crate::faucet::FaucetClient; + use crate::BcsName; use crate::Client; use crate::PaginationFilter; use crate::DEVNET_HOST; @@ -1218,7 +1479,7 @@ mod tests { let mut num_coins = 0; while let Some(result) = stream.next().await { assert!(result.is_ok()); - num_coins += 1; + num_coins = 1; } assert!(num_coins > 0); } @@ -1254,7 +1515,9 @@ mod tests { ); } + // This needs the tx builder to be able to be tested properly #[tokio::test] + #[ignore] async fn test_dry_run() { let client = Client::new_testnet(); // this tx bytes works on testnet @@ -1264,4 +1527,35 @@ mod tests { assert!(dry_run.is_ok()); } + + #[tokio::test] + async fn test_dynamic_field_query() { + let client = test_client(); + let bcs = base64ct::Base64::decode_vec("AgAAAAAAAAA=").unwrap(); + let dynamic_field = client + .dynamic_field("0x5".parse().unwrap(), TypeTag::U64, BcsName(bcs)) + .await; + + assert!(dynamic_field.is_ok()); + + let dynamic_field = client + .dynamic_field("0x5".parse().unwrap(), TypeTag::U64, 2u64) + .await; + + assert!(dynamic_field.is_ok()); + } + + #[tokio::test] + async fn test_dynamic_fields_query() { + let client = test_client(); + let dynamic_fields = client + .dynamic_fields("0x5".parse().unwrap(), PaginationFilter::default()) + .await; + assert!( + dynamic_fields.is_ok(), + "Dynamic fields query failed for {} network. Error: {}", + client.rpc_server(), + dynamic_fields.unwrap_err() + ); + } } diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs new file mode 100644 index 000000000..4600dcab7 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -0,0 +1,198 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use base64ct::Encoding; +use sui_types::types::TypeTag; + +use crate::query_types::schema; +use crate::query_types::Address; +use crate::query_types::Base64; +use crate::query_types::JsonValue; +use crate::query_types::MoveObjectContents; +use crate::query_types::MoveValue; +use crate::query_types::PageInfo; +use crate::DynamicFieldOutput; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "DynamicFieldConnectionArgs" +)] +pub struct DynamicFieldsOwnerQuery { + #[arguments(address: $address)] + pub owner: Option, +} +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Owner", + variables = "DynamicFieldConnectionArgs" +)] +pub struct ObjectOwner { + #[arguments(after: $after, before: $before, first: $first, last: $last)] + pub dynamic_fields: DynamicFieldConnection, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "DynamicFieldArgs")] +pub struct DynamicFieldQuery { + #[arguments(address: $address)] + pub object: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Object", + variables = "DynamicFieldArgs" +)] +pub struct ObjectField { + #[arguments(name: $name)] + pub dynamic_field: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct DynamicFieldArgs { + pub address: Address, + pub name: DynamicFieldName, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct DynamicFieldsQueryArgs { + pub address: Address, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct DynamicFieldConnectionArgs<'a> { + pub address: Address, + pub after: Option<&'a str>, + pub before: Option<&'a str>, + pub first: Option, + pub last: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "DynamicFieldConnection")] +pub struct DynamicFieldConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "DynamicField")] +pub struct DynamicField { + pub value: Option, + pub name: Option, +} + +#[derive(cynic::InlineFragments, Debug)] +#[cynic(schema = "rpc", graphql_type = "DynamicFieldValue")] +pub enum DynamicFieldValue { + MoveObject(MoveObjectContents), + MoveValue(MoveValue), + #[cynic(fallback)] + Unknown, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "DynamicFieldName")] +pub struct DynamicFieldName { + #[cynic(rename = "type")] + pub type_: String, + pub bcs: Base64, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Object", + variables = "DynamicFieldArgs" +)] +pub struct DynamicObjectField { + #[arguments(name: $name)] + pub dynamic_object_field: Option, +} +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "DynamicFieldArgs")] +pub struct DynamicObjectFieldQuery { + #[arguments(address: $address)] + pub object: Option, +} + +impl DynamicFieldValue { + /// Returns the JSON representation of the field value, if available. + pub fn field_value_json(&self) -> Option { + match self { + DynamicFieldValue::MoveObject(mo) => { + mo.contents.as_ref().and_then(|mv| mv.json.clone()) + } + DynamicFieldValue::MoveValue(mv) => mv.json.clone(), + _ => None, + } + } + + /// Return the typename and bcs of this dynamic field value. + pub fn type_bcs(&self) -> Option<(String, Vec)> { + match self { + DynamicFieldValue::MoveObject(mo) => mo + .contents + .as_ref() + .map(|o| (o.type_.repr.clone(), o.bcs.0.clone().into())), + DynamicFieldValue::MoveValue(mv) => { + Some((mv.type_.repr.clone(), mv.bcs.0.clone().into())) + } + _ => None, + } + } +} + +impl DynamicField { + /// Returns the JSON representation of the field value, if available. + pub fn field_value_json(&self) -> Option { + self.value.as_ref().and_then(|v| v.field_value_json()) + } +} + +impl TryFrom for DynamicFieldOutput { + type Error = anyhow::Error; + + fn try_from(val: DynamicField) -> Result { + let typetag = TypeTag::from_str( + val.name + .as_ref() + .expect("There should be a name in this dynamic field") + .type_ + .repr + .as_str(), + ) + .map_err(|_| anyhow::anyhow!("Invalid TypeTag"))?; + Ok(DynamicFieldOutput { + name: crate::DynamicFieldName { + type_: typetag, + bcs: base64ct::Base64::decode_vec(val.name.as_ref().unwrap().bcs.0.as_ref()) + .unwrap(), + json: val.name.as_ref().unwrap().json.clone(), + }, + value_as_json: val.field_value_json(), + value: val.value.and_then(|x| x.type_bcs()), + }) + } +} + +// impl From for DynamicFieldOutput { +// fn from(val: DynamicField) -> DynamicFieldOutput { +// DynamicFieldOutput { +// name: crate::DynamicFieldName { +// type_: TypeTag::from_str(val.name.as_ref().unwrap().type_.as_str()), +// bcs: base64ct::Base64::decode_vec(val.name.as_ref().unwrap().bcs.0.as_ref()) +// .unwrap(), +// json: val.name.as_ref().unwrap().json.clone(), +// }, +// value_as_json: val.field_value_json(), +// value: val.value.and_then(|x| x.type_bcs()), +// } +// } +// } diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 86eecfed6..1b3be8c8c 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -7,6 +7,7 @@ mod chain; mod checkpoint; mod coin; mod dry_run; +mod dynamic_fields; mod epoch; mod events; mod execute_tx; @@ -38,6 +39,12 @@ pub use dry_run::DryRunArgs; pub use dry_run::DryRunQuery; pub use dry_run::DryRunResult; pub use dry_run::TransactionMetadata; +pub use dynamic_fields::DynamicFieldArgs; +pub use dynamic_fields::DynamicFieldConnectionArgs; +pub use dynamic_fields::DynamicFieldName; +pub use dynamic_fields::DynamicFieldQuery; +pub use dynamic_fields::DynamicFieldsOwnerQuery; +pub use dynamic_fields::DynamicObjectFieldQuery; pub use epoch::Epoch; pub use epoch::EpochSummaryArgs; pub use epoch::EpochSummaryQuery; @@ -72,6 +79,7 @@ use sui_types::types::Address; use anyhow::anyhow; use cynic::impl_scalar; +use serde_json::Value as JsonValue; #[cynic::schema("rpc")] pub mod schema {} @@ -82,6 +90,7 @@ pub mod schema {} impl_scalar!(Address, schema::SuiAddress); impl_scalar!(u64, schema::UInt53); +impl_scalar!(JsonValue, schema::JSON); #[derive(cynic::Scalar, Debug, Clone)] #[cynic(graphql_type = "Base64")] @@ -111,6 +120,25 @@ pub struct MoveObject { pub bcs: Option, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveObject")] +pub struct MoveObjectContents { + pub contents: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveValue")] +pub struct MoveValue { + pub type_: MoveType, + pub bcs: Base64, + pub json: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveType")] +pub struct MoveType { + pub repr: String, +} // =========================================================================== // Utility Types // =========================================================================== diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index 10ea0e29b..deca2bca2 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -4,6 +4,7 @@ use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; +use crate::query_types::MoveObjectContents; use crate::query_types::PageInfo; // =========================================================================== @@ -50,6 +51,7 @@ pub struct ObjectsQueryArgs<'a> { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Object")] pub struct Object { + pub as_move_object: Option, pub bcs: Option, } From ec1101d99972a704abb020b79303313c26176163 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:35:27 -0700 Subject: [PATCH 049/107] sui-graphql-client: fix return of bcs and typetag in DynamicFieldOutput (#46) --- crates/sui-graphql-client/src/lib.rs | 34 +++++++++++++++---- .../src/query_types/dynamic_fields.rs | 19 ++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 3b8fbd9ac..6eae7b790 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -107,14 +107,12 @@ pub struct DynamicFieldName { pub json: Option, } -type Typename = String; - #[derive(Debug)] pub struct DynamicFieldOutput { /// The name of the dynamic field pub name: DynamicFieldName, /// The dynamic field value typename and bcs - pub value: Option<(Typename, Vec)>, + pub value: Option<(TypeTag, Vec)>, /// The json representation of the dynamic field value object pub value_as_json: Option, } @@ -191,19 +189,41 @@ impl From for NameValue { } impl DynamicFieldOutput { - pub fn deserialize( + /// Deserialize the name of the dynamic field into the specified type. + pub fn deserialize_name( &self, - expected_type: TypeTag, + expected_type: &TypeTag, ) -> Result { assert_eq!( - expected_type, self.name.type_, + expected_type, &self.name.type_, "Expected type {}, but got {}", - expected_type, self.name.type_ + expected_type, &self.name.type_ ); let bcs = &self.name.bcs; bcs::from_bytes::(bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) } + + /// Deserialize the value of the dynamic field into the specified type. + pub fn deserialize_value( + &self, + expected_type: &TypeTag, + ) -> Result { + let typetag = self.value.as_ref().map(|(typename, _)| typename); + assert_eq!( + Some(&expected_type), + typetag.as_ref(), + "Expected type {}, but got {:?}", + expected_type, + typetag + ); + + if let Some((_, bcs)) = &self.value { + bcs::from_bytes::(bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) + } else { + Err(anyhow!("No value found")) + } + } } /// The GraphQL client for interacting with the Sui blockchain. diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index 4600dcab7..e238c79be 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -135,15 +135,18 @@ impl DynamicFieldValue { } /// Return the typename and bcs of this dynamic field value. - pub fn type_bcs(&self) -> Option<(String, Vec)> { + pub fn type_bcs(&self) -> Option<(TypeTag, Vec)> { match self { - DynamicFieldValue::MoveObject(mo) => mo - .contents - .as_ref() - .map(|o| (o.type_.repr.clone(), o.bcs.0.clone().into())), - DynamicFieldValue::MoveValue(mv) => { - Some((mv.type_.repr.clone(), mv.bcs.0.clone().into())) - } + DynamicFieldValue::MoveObject(mo) => mo.contents.as_ref().map(|o| { + ( + TypeTag::from_str(&o.type_.repr.clone()).expect("Invalid TypeTag"), + base64ct::Base64::decode_vec(&o.bcs.0).expect("Invalid Base64"), + ) + }), + DynamicFieldValue::MoveValue(mv) => Some(( + TypeTag::from_str(&mv.type_.repr.clone()).expect("Invalid TypeTag"), + base64ct::Base64::decode_vec(&mv.bcs.0).expect("Invalid Base64"), + )), _ => None, } } From ab5583d0042256d1c387dcf956a8950f888f55b3 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:35:56 -0700 Subject: [PATCH 050/107] sui-graphql-client: return Result instead of Result> (#44) --- crates/sui-graphql-client/src/lib.rs | 8 ++++---- .../src/query_types/dynamic_fields.rs | 15 --------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 6eae7b790..3dc791120 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -735,7 +735,7 @@ impl Client { &self, address: Address, pagination_filter: PaginationFilter<'a>, - ) -> Result>, Error> { + ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = DynamicFieldsOwnerQuery::build(DynamicFieldConnectionArgs { address, @@ -751,10 +751,10 @@ impl Client { } let Some(DynamicFieldsOwnerQuery { owner: Some(dfs) }) = response.data else { - return Ok(Some(Page::new_empty())); + return Ok(Page::new_empty()); }; - Ok(Some(Page::new( + Ok(Page::new( dfs.dynamic_fields.page_info, dfs.dynamic_fields .nodes @@ -762,7 +762,7 @@ impl Client { .map(TryInto::try_into) .collect::, Error>>() .map_err(|e| Error::msg(format!("{:?}", e)))?, - ))) + )) } // =========================================================================== diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index e238c79be..7c4d59715 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -184,18 +184,3 @@ impl TryFrom for DynamicFieldOutput { }) } } - -// impl From for DynamicFieldOutput { -// fn from(val: DynamicField) -> DynamicFieldOutput { -// DynamicFieldOutput { -// name: crate::DynamicFieldName { -// type_: TypeTag::from_str(val.name.as_ref().unwrap().type_.as_str()), -// bcs: base64ct::Base64::decode_vec(val.name.as_ref().unwrap().bcs.0.as_ref()) -// .unwrap(), -// json: val.name.as_ref().unwrap().json.clone(), -// }, -// value_as_json: val.field_value_json(), -// value: val.value.and_then(|x| x.type_bcs()), -// } -// } -// } From 7b7ff5393dd4e390b58de551b9903ddc5e5e5007 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sat, 26 Oct 2024 13:29:55 -0500 Subject: [PATCH 051/107] crypto: add blanket implementation for SuiSigner and SuiVerifier --- crates/sui-crypto/Cargo.toml | 10 ++--- crates/sui-crypto/src/ed25519.rs | 62 ++------------------------ crates/sui-crypto/src/lib.rs | 51 +++++++++++++++++++++ crates/sui-crypto/src/multisig.rs | 41 ----------------- crates/sui-crypto/src/secp256k1.rs | 62 ++------------------------ crates/sui-crypto/src/secp256r1.rs | 62 ++------------------------ crates/sui-crypto/src/simple.rs | 21 --------- crates/sui-crypto/src/zklogin/mod.rs | 21 --------- crates/sui-crypto/src/zklogin/tests.rs | 1 + 9 files changed, 65 insertions(+), 266 deletions(-) diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index 24372bdb7..fed377a2d 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -23,9 +23,9 @@ rustdoc-args = [ [features] default = [] -ed25519 = ["dep:ed25519-dalek", "dep:rand_core", "sui-sdk-types/hash", "sui-sdk-types/serde"] -secp256r1 = ["dep:p256", "dep:rand_core", "sui-sdk-types/hash", "sui-sdk-types/serde"] -secp256k1 = ["dep:k256", "dep:rand_core", "signature/std", "sui-sdk-types/hash", "sui-sdk-types/serde"] +ed25519 = ["dep:ed25519-dalek", "dep:rand_core"] +secp256r1 = ["dep:p256", "dep:rand_core"] +secp256k1 = ["dep:k256", "dep:rand_core", "signature/std"] zklogin = [ "dep:ark-bn254", "dep:ark-ff", @@ -39,13 +39,11 @@ zklogin = [ "dep:serde_derive", "dep:serde_json", "signature/std", - "sui-sdk-types/hash", - "sui-sdk-types/serde", ] [dependencies] signature = "2.2" -sui-sdk-types = { version = "0.0.1", path = "../sui-sdk-types", default-features = false } +sui-sdk-types = { version = "0.0.1", path = "../sui-sdk-types", default-features = false, features = ["hash", "serde"] } # RNG support rand_core = { version = "0.6.4", optional = true } diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs index 9be214dfd..70127e802 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/sui-crypto/src/ed25519.rs @@ -1,13 +1,9 @@ use crate::SignatureError; use crate::Signer; -use crate::SuiSigner; -use crate::SuiVerifier; use crate::Verifier; use sui_sdk_types::types::Ed25519PublicKey; use sui_sdk_types::types::Ed25519Signature; -use sui_sdk_types::types::PersonalMessage; use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::Transaction; use sui_sdk_types::types::UserSignature; pub struct Ed25519PrivateKey(ed25519_dalek::SigningKey); @@ -85,21 +81,6 @@ impl Signer for Ed25519PrivateKey { } } -impl SuiSigner for Ed25519PrivateKey { - fn sign_transaction(&self, transaction: &Transaction) -> Result { - let msg = transaction.signing_digest(); - self.try_sign(&msg) - } - - fn sign_personal_message( - &self, - message: &PersonalMessage<'_>, - ) -> Result { - let msg = message.signing_digest(); - self.try_sign(&msg) - } -} - pub struct Ed25519VerifyingKey(ed25519_dalek::VerifyingKey); impl Ed25519VerifyingKey { @@ -149,26 +130,6 @@ impl Verifier for Ed25519VerifyingKey { } } -impl SuiVerifier for Ed25519VerifyingKey { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[derive(Default, Clone, Debug)] pub struct Ed25519Verifier {} @@ -204,29 +165,12 @@ impl Verifier for Ed25519Verifier { } } -impl SuiVerifier for Ed25519Verifier { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[cfg(test)] mod test { use super::*; + use crate::SuiSigner; + use crate::SuiVerifier; + use sui_sdk_types::types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs index 81a4b854b..32121725a 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/sui-crypto/src/lib.rs @@ -61,6 +61,14 @@ pub mod simple; )] pub mod multisig; +/// Interface for signing user transactions and messages in Sui +/// +/// # Note +/// +/// There is a blanket implementation of `SuiSigner` for all `T` where `T: +/// `[`Signer`]`<`[`UserSignature`]`>` so it is generally recommended for a signer to implement +/// `Signer` and rely on the blanket implementation which handles the proper +/// construction of the signing message. pub trait SuiSigner { fn sign_transaction(&self, transaction: &Transaction) -> Result; fn sign_personal_message( @@ -69,6 +77,29 @@ pub trait SuiSigner { ) -> Result; } +impl> SuiSigner for T { + fn sign_transaction(&self, transaction: &Transaction) -> Result { + let msg = transaction.signing_digest(); + self.try_sign(&msg) + } + + fn sign_personal_message( + &self, + message: &PersonalMessage<'_>, + ) -> Result { + let msg = message.signing_digest(); + self.try_sign(&msg) + } +} + +/// Interface for verifying user transactions and messages in Sui +/// +/// # Note +/// +/// There is a blanket implementation of `SuiVerifier` for all `T` where `T: +/// `[`Verifier`]`<`[`UserSignature`]`>` so it is generally recommended for a signer to implement +/// `Verifier` and rely on the blanket implementation which handles the proper +/// construction of the signing message. pub trait SuiVerifier { fn verify_transaction( &self, @@ -81,3 +112,23 @@ pub trait SuiVerifier { signature: &UserSignature, ) -> Result<(), SignatureError>; } + +impl> SuiVerifier for T { + fn verify_transaction( + &self, + transaction: &Transaction, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = transaction.signing_digest(); + self.verify(&message, signature) + } + + fn verify_personal_message( + &self, + message: &PersonalMessage<'_>, + signature: &UserSignature, + ) -> Result<(), SignatureError> { + let message = message.signing_digest(); + self.verify(&message, signature) + } +} diff --git a/crates/sui-crypto/src/multisig.rs b/crates/sui-crypto/src/multisig.rs index 03f068596..5a9dbd079 100644 --- a/crates/sui-crypto/src/multisig.rs +++ b/crates/sui-crypto/src/multisig.rs @@ -1,5 +1,4 @@ use crate::SignatureError; -use crate::SuiVerifier; use crate::Verifier; use sui_sdk_types::types::MultisigAggregatedSignature; use sui_sdk_types::types::MultisigCommittee; @@ -166,26 +165,6 @@ impl Verifier for MultisigVerifier { } } -impl SuiVerifier for MultisigVerifier { - fn verify_transaction( - &self, - transaction: &sui_sdk_types::types::Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &sui_sdk_types::types::PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - /// Interpret a bitmap of 01s as a list of indices that is set to 1s. /// e.g. 22 = 0b10110, then the result is [1, 2, 4]. struct BitmapIndices { @@ -273,26 +252,6 @@ impl Verifier for UserSignatureVerifier { } } -impl SuiVerifier for UserSignatureVerifier { - fn verify_transaction( - &self, - transaction: &sui_sdk_types::types::Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &sui_sdk_types::types::PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - pub struct MultisigAggregator { committee: MultisigCommittee, signatures: std::collections::BTreeMap, diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs index 37f6984d6..e9f3d52e6 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/sui-crypto/src/secp256k1.rs @@ -1,16 +1,12 @@ use crate::SignatureError; -use crate::SuiSigner; -use crate::SuiVerifier; use k256::ecdsa::SigningKey; use k256::ecdsa::VerifyingKey; use k256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk_types::types::PersonalMessage; use sui_sdk_types::types::Secp256k1PublicKey; use sui_sdk_types::types::Secp256k1Signature; use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::Transaction; use sui_sdk_types::types::UserSignature; pub struct Secp256k1PrivateKey(SigningKey); @@ -87,21 +83,6 @@ impl Signer for Secp256k1PrivateKey { } } -impl SuiSigner for Secp256k1PrivateKey { - fn sign_transaction(&self, transaction: &Transaction) -> Result { - let msg = transaction.signing_digest(); - self.try_sign(&msg) - } - - fn sign_personal_message( - &self, - message: &PersonalMessage<'_>, - ) -> Result { - let msg = message.signing_digest(); - self.try_sign(&msg) - } -} - pub struct Secp256k1VerifyingKey(VerifyingKey); impl Secp256k1VerifyingKey { @@ -151,26 +132,6 @@ impl Verifier for Secp256k1VerifyingKey { } } -impl SuiVerifier for Secp256k1VerifyingKey { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[derive(Default, Clone, Debug)] pub struct Secp256k1Verifier {} @@ -206,29 +167,12 @@ impl Verifier for Secp256k1Verifier { } } -impl SuiVerifier for Secp256k1Verifier { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[cfg(test)] mod test { use super::*; + use crate::SuiSigner; + use crate::SuiVerifier; + use sui_sdk_types::types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs index f60d62b71..8d5f3e181 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/sui-crypto/src/secp256r1.rs @@ -1,16 +1,12 @@ use crate::SignatureError; -use crate::SuiSigner; -use crate::SuiVerifier; use p256::ecdsa::SigningKey; use p256::ecdsa::VerifyingKey; use p256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk_types::types::PersonalMessage; use sui_sdk_types::types::Secp256r1PublicKey; use sui_sdk_types::types::Secp256r1Signature; use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::Transaction; use sui_sdk_types::types::UserSignature; pub struct Secp256r1PrivateKey(SigningKey); @@ -87,21 +83,6 @@ impl Signer for Secp256r1PrivateKey { } } -impl SuiSigner for Secp256r1PrivateKey { - fn sign_transaction(&self, transaction: &Transaction) -> Result { - let msg = transaction.signing_digest(); - self.try_sign(&msg) - } - - fn sign_personal_message( - &self, - message: &PersonalMessage<'_>, - ) -> Result { - let msg = message.signing_digest(); - self.try_sign(&msg) - } -} - pub struct Secp256r1VerifyingKey(VerifyingKey); impl Secp256r1VerifyingKey { @@ -151,26 +132,6 @@ impl Verifier for Secp256r1VerifyingKey { } } -impl SuiVerifier for Secp256r1VerifyingKey { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[derive(Default, Clone, Debug)] pub struct Secp256r1Verifier {} @@ -206,29 +167,12 @@ impl Verifier for Secp256r1Verifier { } } -impl SuiVerifier for Secp256r1Verifier { - fn verify_transaction( - &self, - transaction: &Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - #[cfg(test)] mod test { use super::*; + use crate::SuiSigner; + use crate::SuiVerifier; + use sui_sdk_types::types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs index 248e7eb68..28bb301a9 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/sui-crypto/src/simple.rs @@ -1,5 +1,4 @@ use crate::SignatureError; -use crate::SuiVerifier; use signature::Verifier; use sui_sdk_types::types::SimpleSignature; use sui_sdk_types::types::UserSignature; @@ -61,23 +60,3 @@ impl Verifier for SimpleVerifier { >::verify(self, message, signature) } } - -impl SuiVerifier for SimpleVerifier { - fn verify_transaction( - &self, - transaction: &sui_sdk_types::types::Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &sui_sdk_types::types::PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} diff --git a/crates/sui-crypto/src/zklogin/mod.rs b/crates/sui-crypto/src/zklogin/mod.rs index aa28c268a..929e3a021 100644 --- a/crates/sui-crypto/src/zklogin/mod.rs +++ b/crates/sui-crypto/src/zklogin/mod.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use crate::SignatureError; -use crate::SuiVerifier; use poseidon::POSEIDON; use signature::Verifier; use sui_sdk_types::types::Claim; @@ -85,26 +84,6 @@ impl Verifier for ZkloginVerifier { } } -impl SuiVerifier for ZkloginVerifier { - fn verify_transaction( - &self, - transaction: &sui_sdk_types::types::Transaction, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = transaction.signing_digest(); - self.verify(&message, signature) - } - - fn verify_personal_message( - &self, - message: &sui_sdk_types::types::PersonalMessage<'_>, - signature: &UserSignature, - ) -> Result<(), SignatureError> { - let message = message.signing_digest(); - self.verify(&message, signature) - } -} - /// A structed of parsed JWT details, consists of kid, header, iss. #[derive(Debug, Clone, PartialEq, Eq)] struct JwtDetails { diff --git a/crates/sui-crypto/src/zklogin/tests.rs b/crates/sui-crypto/src/zklogin/tests.rs index d67c90a54..66e3b1078 100644 --- a/crates/sui-crypto/src/zklogin/tests.rs +++ b/crates/sui-crypto/src/zklogin/tests.rs @@ -2,6 +2,7 @@ use signature::Signer; use sui_sdk_types::types::PersonalMessage; use crate::ed25519::Ed25519PrivateKey; +use crate::SuiVerifier; use super::*; From 5dadea83827022c82547775aadabfd85800e93f4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 27 Oct 2024 13:54:57 -0500 Subject: [PATCH 052/107] crypto: support der and pem format for pub/priv keys --- crates/sui-crypto/Cargo.toml | 6 ++ crates/sui-crypto/src/ed25519.rs | 93 ++++++++++++++++++++++++++++++ crates/sui-crypto/src/secp256k1.rs | 93 ++++++++++++++++++++++++++++++ crates/sui-crypto/src/secp256r1.rs | 93 ++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+) diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index fed377a2d..c4c594f01 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -40,6 +40,8 @@ zklogin = [ "dep:serde_json", "signature/std", ] +der = ["dep:pkcs8", "ed25519-dalek?/pkcs8", "p256?/pkcs8", "k256?/pkcs8"] +pem = ["der", "dep:pem-rfc7468", "ed25519-dalek?/pem", "p256?/pem", "k256?/pem"] [dependencies] signature = "2.2" @@ -70,6 +72,10 @@ serde = { version = "1.0.210", optional = true } serde_derive = { version = "1.0.210", optional = true } serde_json = { version = "1.0.128", optional = true } +# pkcs8 der and pem support +pkcs8 = { version = "0.10", optional = true, features = ["std"] } +pem-rfc7468 = { version = "0.7", optional = true, features = ["std"] } + [dev-dependencies] bcs = { version = "0.1.6" } hex = "0.4.3" diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs index 70127e802..b4fca284c 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/sui-crypto/src/ed25519.rs @@ -54,6 +54,53 @@ impl Ed25519PrivateKey { rng.fill_bytes(&mut buf); Self(buf.into()) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + ed25519_dalek::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this private key as DER-encoded PKCS#8 + pub fn to_der(&self) -> Result, SignatureError> { + use ed25519_dalek::pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_der() + .map_err(SignatureError::from_source) + .map(|der| der.as_bytes().to_owned()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize PKCS#8-encoded private key from PEM. + pub fn from_pem(s: &str) -> Result { + ed25519_dalek::pkcs8::DecodePrivateKey::from_pkcs8_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this private key as PEM-encoded PKCS#8 + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + .map(|pem| (*pem).to_owned()) + } + + #[cfg(feature = "der")] + pub(crate) fn from_dalek(private_key: ed25519_dalek::SigningKey) -> Self { + Self(private_key) + } } impl Signer for Ed25519PrivateKey { @@ -91,6 +138,52 @@ impl Ed25519VerifyingKey { pub fn public_key(&self) -> Ed25519PublicKey { Ed25519PublicKey::new(self.0.to_bytes()) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize public key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + ed25519_dalek::pkcs8::DecodePublicKey::from_public_key_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this public key as DER-encoded data + pub fn to_der(&self) -> Result, SignatureError> { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_der() + .map_err(SignatureError::from_source) + .map(|der| der.into_vec()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize public key from PEM. + pub fn from_pem(s: &str) -> Result { + ed25519_dalek::pkcs8::DecodePublicKey::from_public_key_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this public key into PEM format + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + pub(crate) fn from_dalek(verifying_key: ed25519_dalek::VerifyingKey) -> Self { + Self(verifying_key) + } } impl Verifier for Ed25519VerifyingKey { diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs index e9f3d52e6..1e3c16d17 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/sui-crypto/src/secp256k1.rs @@ -57,6 +57,53 @@ impl Secp256k1PrivateKey { { Self(SigningKey::random(&mut rng)) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + k256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this private key as DER-encoded PKCS#8 + pub fn to_der(&self) -> Result, SignatureError> { + use k256::pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_der() + .map_err(SignatureError::from_source) + .map(|der| der.as_bytes().to_owned()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize PKCS#8-encoded private key from PEM. + pub fn from_pem(s: &str) -> Result { + k256::pkcs8::DecodePrivateKey::from_pkcs8_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this private key as PEM-encoded PKCS#8 + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + .map(|pem| (*pem).to_owned()) + } + + #[cfg(feature = "der")] + pub(crate) fn from_k256(private_key: SigningKey) -> Self { + Self(private_key) + } } impl Signer for Secp256k1PrivateKey { @@ -93,6 +140,52 @@ impl Secp256k1VerifyingKey { pub fn public_key(&self) -> Secp256k1PublicKey { Secp256k1PublicKey::new(self.0.as_ref().to_bytes().into()) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize public key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + k256::pkcs8::DecodePublicKey::from_public_key_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this public key as DER-encoded data + pub fn to_der(&self) -> Result, SignatureError> { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_der() + .map_err(SignatureError::from_source) + .map(|der| der.into_vec()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize public key from PEM. + pub fn from_pem(s: &str) -> Result { + k256::pkcs8::DecodePublicKey::from_public_key_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this public key into PEM + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + pub(crate) fn from_k256(verifying_key: VerifyingKey) -> Self { + Self(verifying_key) + } } impl Verifier for Secp256k1VerifyingKey { diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs index 8d5f3e181..cfcc4d0b6 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/sui-crypto/src/secp256r1.rs @@ -57,6 +57,53 @@ impl Secp256r1PrivateKey { rng.fill_bytes(&mut buf); Self::new(buf) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + p256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this private key as DER-encoded PKCS#8 + pub fn to_der(&self) -> Result, SignatureError> { + use p256::pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_der() + .map_err(SignatureError::from_source) + .map(|der| der.as_bytes().to_owned()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize PKCS#8-encoded private key from PEM. + pub fn from_pem(s: &str) -> Result { + p256::pkcs8::DecodePrivateKey::from_pkcs8_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this private key as PEM-encoded PKCS#8 + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePrivateKey; + + self.0 + .to_pkcs8_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + .map(|pem| (*pem).to_owned()) + } + + #[cfg(feature = "der")] + pub(crate) fn from_p256(private_key: SigningKey) -> Self { + Self(private_key) + } } impl Signer for Secp256r1PrivateKey { @@ -93,6 +140,52 @@ impl Secp256r1VerifyingKey { pub fn public_key(&self) -> Secp256r1PublicKey { Secp256r1PublicKey::new(self.0.as_ref().to_bytes().into()) } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize public key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + p256::pkcs8::DecodePublicKey::from_public_key_der(bytes) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this public key as DER-encoded data + pub fn to_der(&self) -> Result, SignatureError> { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_der() + .map_err(SignatureError::from_source) + .map(|der| der.into_vec()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize public key from PEM. + pub fn from_pem(s: &str) -> Result { + p256::pkcs8::DecodePublicKey::from_public_key_pem(s) + .map(Self) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this public key into PEM + pub fn to_pem(&self) -> Result { + use pkcs8::EncodePublicKey; + + self.0 + .to_public_key_pem(pkcs8::LineEnding::default()) + .map_err(SignatureError::from_source) + } + + #[cfg(feature = "der")] + pub(crate) fn from_p256(verifying_key: VerifyingKey) -> Self { + Self(verifying_key) + } } impl Verifier for Secp256r1VerifyingKey { From 16cf4c3996417d4e93a6188a7032171235e884a7 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 27 Oct 2024 13:55:13 -0500 Subject: [PATCH 053/107] crypto: introduce SimpleKeypair type Introduce SimpleKeypair type which can contain either an ed25519, secp256k1, or secp256r1 keypair. --- crates/sui-crypto/src/ed25519.rs | 6 + crates/sui-crypto/src/secp256k1.rs | 6 + crates/sui-crypto/src/secp256r1.rs | 6 + crates/sui-crypto/src/simple.rs | 576 +++++++++++++++++++++++++++++ 4 files changed, 594 insertions(+) diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs index b4fca284c..ac6eddf53 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/sui-crypto/src/ed25519.rs @@ -3,6 +3,7 @@ use crate::Signer; use crate::Verifier; use sui_sdk_types::types::Ed25519PublicKey; use sui_sdk_types::types::Ed25519Signature; +use sui_sdk_types::types::SignatureScheme; use sui_sdk_types::types::SimpleSignature; use sui_sdk_types::types::UserSignature; @@ -37,6 +38,10 @@ impl Ed25519PrivateKey { Self(bytes.into()) } + pub fn scheme(&self) -> SignatureScheme { + SignatureScheme::Ed25519 + } + pub fn verifying_key(&self) -> Ed25519VerifyingKey { let verifying_key = self.0.verifying_key(); Ed25519VerifyingKey(verifying_key) @@ -128,6 +133,7 @@ impl Signer for Ed25519PrivateKey { } } +#[derive(Debug)] pub struct Ed25519VerifyingKey(ed25519_dalek::VerifyingKey); impl Ed25519VerifyingKey { diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs index 1e3c16d17..5e09f8340 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/sui-crypto/src/secp256k1.rs @@ -6,6 +6,7 @@ use signature::Signer; use signature::Verifier; use sui_sdk_types::types::Secp256k1PublicKey; use sui_sdk_types::types::Secp256k1Signature; +use sui_sdk_types::types::SignatureScheme; use sui_sdk_types::types::SimpleSignature; use sui_sdk_types::types::UserSignature; @@ -42,6 +43,10 @@ impl Secp256k1PrivateKey { SigningKey::from_bytes(&bytes.into()).map(Self) } + pub fn scheme(&self) -> SignatureScheme { + SignatureScheme::Secp256k1 + } + pub fn verifying_key(&self) -> Secp256k1VerifyingKey { let verifying_key = self.0.verifying_key(); Secp256k1VerifyingKey(*verifying_key) @@ -130,6 +135,7 @@ impl Signer for Secp256k1PrivateKey { } } +#[derive(Debug)] pub struct Secp256k1VerifyingKey(VerifyingKey); impl Secp256k1VerifyingKey { diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs index cfcc4d0b6..da53eeb0b 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/sui-crypto/src/secp256r1.rs @@ -6,6 +6,7 @@ use signature::Signer; use signature::Verifier; use sui_sdk_types::types::Secp256r1PublicKey; use sui_sdk_types::types::Secp256r1Signature; +use sui_sdk_types::types::SignatureScheme; use sui_sdk_types::types::SimpleSignature; use sui_sdk_types::types::UserSignature; @@ -40,6 +41,10 @@ impl Secp256r1PrivateKey { Self(SigningKey::from_bytes(&bytes.into()).unwrap()) } + pub fn scheme(&self) -> SignatureScheme { + SignatureScheme::Secp256r1 + } + pub fn verifying_key(&self) -> Secp256r1VerifyingKey { let verifying_key = self.0.verifying_key(); Secp256r1VerifyingKey(*verifying_key) @@ -130,6 +135,7 @@ impl Signer for Secp256r1PrivateKey { } } +#[derive(Debug)] pub struct Secp256r1VerifyingKey(VerifyingKey); impl Secp256r1VerifyingKey { diff --git a/crates/sui-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs index 28bb301a9..fd8c6fb7f 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/sui-crypto/src/simple.rs @@ -60,3 +60,579 @@ impl Verifier for SimpleVerifier { >::verify(self, message, signature) } } + +#[cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))] +#[cfg_attr( + doc_cfg, + doc(cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))) +)] +#[rustfmt::skip] +pub use keypair::{SimpleKeypair, SimpleVerifiyingKey}; + +#[cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))] +#[cfg_attr( + doc_cfg, + doc(cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))) +)] +mod keypair { + use crate::SignatureError; + use signature::Signer; + use signature::Verifier; + use sui_sdk_types::types::MultisigMemberPublicKey; + use sui_sdk_types::types::SignatureScheme; + use sui_sdk_types::types::SimpleSignature; + use sui_sdk_types::types::UserSignature; + + pub struct SimpleKeypair { + inner: InnerKeypair, + } + + enum InnerKeypair { + #[cfg(feature = "ed25519")] + Ed25519(crate::ed25519::Ed25519PrivateKey), + #[cfg(feature = "secp256k1")] + Secp256k1(crate::secp256k1::Secp256k1PrivateKey), + #[cfg(feature = "secp256r1")] + Secp256r1(crate::secp256r1::Secp256r1PrivateKey), + } + + impl SimpleKeypair { + pub fn scheme(&self) -> SignatureScheme { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerKeypair::Ed25519(private_key) => private_key.scheme(), + #[cfg(feature = "secp256k1")] + InnerKeypair::Secp256k1(private_key) => private_key.scheme(), + #[cfg(feature = "secp256r1")] + InnerKeypair::Secp256r1(private_key) => private_key.scheme(), + } + } + + pub fn verifying_key(&self) -> SimpleVerifiyingKey { + let verifying_key = match &self.inner { + #[cfg(feature = "ed25519")] + InnerKeypair::Ed25519(private_key) => { + InnerVerifyingKey::Ed25519(private_key.verifying_key()) + } + #[cfg(feature = "secp256k1")] + InnerKeypair::Secp256k1(private_key) => { + InnerVerifyingKey::Secp256k1(private_key.verifying_key()) + } + #[cfg(feature = "secp256r1")] + InnerKeypair::Secp256r1(private_key) => { + InnerVerifyingKey::Secp256r1(private_key.verifying_key()) + } + }; + + SimpleVerifiyingKey { + inner: verifying_key, + } + } + + pub fn public_key(&self) -> MultisigMemberPublicKey { + self.verifying_key().public_key() + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + let private_key = + pkcs8::PrivateKeyInfo::try_from(bytes).map_err(SignatureError::from_source)?; + + match private_key + .algorithm + .oids() + .map_err(SignatureError::from_source)? + { + #[cfg(feature = "ed25519")] + (ed25519_dalek::pkcs8::ALGORITHM_OID, None) => private_key + .try_into() + .map(crate::ed25519::Ed25519PrivateKey::from_dalek) + .map(InnerKeypair::Ed25519) + .map_err(SignatureError::from_source), + + #[cfg(feature = "secp256r1")] + ( + p256::elliptic_curve::ALGORITHM_OID, + Some(::OID), + ) => private_key + .try_into() + .map(crate::secp256r1::Secp256r1PrivateKey::from_p256) + .map(InnerKeypair::Secp256r1) + .map_err(SignatureError::from_source), + + #[cfg(feature = "secp256k1")] + ( + k256::elliptic_curve::ALGORITHM_OID, + Some(::OID), + ) => private_key + .try_into() + .map(crate::secp256k1::Secp256k1PrivateKey::from_k256) + .map(InnerKeypair::Secp256k1) + .map_err(SignatureError::from_source), + + _ => Err(SignatureError::from_source( + "unsupported or invalid private key type", + )), + } + .map(|inner| Self { inner }) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this private key as DER-encoded PKCS#8 + pub fn to_der(&self) -> Result, SignatureError> { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerKeypair::Ed25519(private_key) => private_key.to_der(), + #[cfg(feature = "secp256k1")] + InnerKeypair::Secp256k1(private_key) => private_key.to_der(), + #[cfg(feature = "secp256r1")] + InnerKeypair::Secp256r1(private_key) => private_key.to_der(), + } + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize PKCS#8-encoded private key from PEM. + pub fn from_pem(s: &str) -> Result { + use pkcs8::der::pem::PemLabel; + + let (label, doc) = + pkcs8::SecretDocument::from_pem(s).map_err(SignatureError::from_source)?; + pkcs8::PrivateKeyInfo::validate_pem_label(label) + .map_err(SignatureError::from_source)?; + Self::from_der(doc.as_bytes()) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this private key as DER-encoded PKCS#8 + pub fn to_pem(&self) -> Result { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerKeypair::Ed25519(private_key) => private_key.to_pem(), + #[cfg(feature = "secp256k1")] + InnerKeypair::Secp256k1(private_key) => private_key.to_pem(), + #[cfg(feature = "secp256r1")] + InnerKeypair::Secp256r1(private_key) => private_key.to_pem(), + } + } + } + + impl Signer for SimpleKeypair { + fn try_sign(&self, message: &[u8]) -> Result { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerKeypair::Ed25519(private_key) => private_key.try_sign(message), + #[cfg(feature = "secp256k1")] + InnerKeypair::Secp256k1(private_key) => private_key.try_sign(message), + #[cfg(feature = "secp256r1")] + InnerKeypair::Secp256r1(private_key) => private_key.try_sign(message), + } + } + } + + impl Signer for SimpleKeypair { + fn try_sign(&self, msg: &[u8]) -> Result { + >::try_sign(self, msg).map(UserSignature::Simple) + } + } + + #[cfg(feature = "ed25519")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))] + impl From for SimpleKeypair { + fn from(private_key: crate::ed25519::Ed25519PrivateKey) -> Self { + Self { + inner: InnerKeypair::Ed25519(private_key), + } + } + } + + #[cfg(feature = "secp256r1")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] + impl From for SimpleKeypair { + fn from(private_key: crate::secp256r1::Secp256r1PrivateKey) -> Self { + Self { + inner: InnerKeypair::Secp256r1(private_key), + } + } + } + + #[cfg(feature = "secp256k1")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))] + impl From for SimpleKeypair { + fn from(private_key: crate::secp256k1::Secp256k1PrivateKey) -> Self { + Self { + inner: InnerKeypair::Secp256k1(private_key), + } + } + } + + pub struct SimpleVerifiyingKey { + inner: InnerVerifyingKey, + } + + enum InnerVerifyingKey { + #[cfg(feature = "ed25519")] + Ed25519(crate::ed25519::Ed25519VerifyingKey), + #[cfg(feature = "secp256k1")] + Secp256k1(crate::secp256k1::Secp256k1VerifyingKey), + #[cfg(feature = "secp256r1")] + Secp256r1(crate::secp256r1::Secp256r1VerifyingKey), + } + + impl SimpleVerifiyingKey { + pub fn scheme(&self) -> SignatureScheme { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.public_key().scheme(), + #[cfg(feature = "secp256k1")] + InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.public_key().scheme(), + #[cfg(feature = "secp256r1")] + InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.public_key().scheme(), + } + } + + pub fn public_key(&self) -> MultisigMemberPublicKey { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerVerifyingKey::Ed25519(verifying_key) => { + MultisigMemberPublicKey::Ed25519(verifying_key.public_key()) + } + #[cfg(feature = "secp256k1")] + InnerVerifyingKey::Secp256k1(verifying_key) => { + MultisigMemberPublicKey::Secp256k1(verifying_key.public_key()) + } + #[cfg(feature = "secp256r1")] + InnerVerifyingKey::Secp256r1(verifying_key) => { + MultisigMemberPublicKey::Secp256r1(verifying_key.public_key()) + } + } + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Deserialize public key from ASN.1 DER-encoded data (binary format). + pub fn from_der(bytes: &[u8]) -> Result { + let public_key = pkcs8::SubjectPublicKeyInfoRef::try_from(bytes) + .map_err(SignatureError::from_source)?; + + match public_key + .algorithm + .oids() + .map_err(SignatureError::from_source)? + { + #[cfg(feature = "ed25519")] + (ed25519_dalek::pkcs8::ALGORITHM_OID, None) => public_key + .try_into() + .map(crate::ed25519::Ed25519VerifyingKey::from_dalek) + .map(InnerVerifyingKey::Ed25519) + .map_err(SignatureError::from_source), + + #[cfg(feature = "secp256r1")] + ( + p256::elliptic_curve::ALGORITHM_OID, + Some(::OID), + ) => public_key + .try_into() + .map(crate::secp256r1::Secp256r1VerifyingKey::from_p256) + .map(InnerVerifyingKey::Secp256r1) + .map_err(SignatureError::from_source), + + #[cfg(feature = "secp256k1")] + ( + k256::elliptic_curve::ALGORITHM_OID, + Some(::OID), + ) => public_key + .try_into() + .map(crate::secp256k1::Secp256k1VerifyingKey::from_k256) + .map(InnerVerifyingKey::Secp256k1) + .map_err(SignatureError::from_source), + + _ => Err(SignatureError::from_source( + "unsupported or invalid public key type", + )), + } + .map(|inner| Self { inner }) + } + + #[cfg(feature = "der")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + /// Serialize this public key as DER-encoded data + pub fn to_der(&self) -> Result, SignatureError> { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.to_der(), + #[cfg(feature = "secp256k1")] + InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.to_der(), + #[cfg(feature = "secp256r1")] + InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.to_der(), + } + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Deserialize public key from PEM. + pub fn from_pem(s: &str) -> Result { + use pkcs8::der::pem::PemLabel; + + let (label, doc) = pkcs8::Document::from_pem(s).map_err(SignatureError::from_source)?; + pkcs8::SubjectPublicKeyInfoRef::validate_pem_label(label) + .map_err(SignatureError::from_source)?; + Self::from_der(doc.as_bytes()) + } + + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] + /// Serialize this public key as PEM + pub fn to_pem(&self) -> Result { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerVerifyingKey::Ed25519(verifying_key) => verifying_key.to_pem(), + #[cfg(feature = "secp256k1")] + InnerVerifyingKey::Secp256k1(verifying_key) => verifying_key.to_pem(), + #[cfg(feature = "secp256r1")] + InnerVerifyingKey::Secp256r1(verifying_key) => verifying_key.to_pem(), + } + } + } + + impl Verifier for SimpleVerifiyingKey { + fn verify( + &self, + message: &[u8], + signature: &SimpleSignature, + ) -> Result<(), SignatureError> { + match &self.inner { + #[cfg(feature = "ed25519")] + InnerVerifyingKey::Ed25519(verifying_key) => { + verifying_key.verify(message, signature) + } + #[cfg(feature = "secp256k1")] + InnerVerifyingKey::Secp256k1(verifying_key) => { + verifying_key.verify(message, signature) + } + #[cfg(feature = "secp256r1")] + InnerVerifyingKey::Secp256r1(verifying_key) => { + verifying_key.verify(message, signature) + } + } + } + } + + impl Verifier for SimpleVerifiyingKey { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Simple(signature) = signature else { + return Err(SignatureError::from_source("not a simple signature")); + }; + + >::verify(self, message, signature) + } + } + + #[cfg(feature = "ed25519")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))] + impl From for SimpleVerifiyingKey { + fn from(verifying_key: crate::ed25519::Ed25519VerifyingKey) -> Self { + Self { + inner: InnerVerifyingKey::Ed25519(verifying_key), + } + } + } + + #[cfg(feature = "secp256r1")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] + impl From for SimpleVerifiyingKey { + fn from(verifying_key: crate::secp256r1::Secp256r1VerifyingKey) -> Self { + Self { + inner: InnerVerifyingKey::Secp256r1(verifying_key), + } + } + } + + #[cfg(feature = "secp256k1")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))] + impl From for SimpleVerifiyingKey { + fn from(verifying_key: crate::secp256k1::Secp256k1VerifyingKey) -> Self { + Self { + inner: InnerVerifyingKey::Secp256k1(verifying_key), + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::ed25519::Ed25519PrivateKey; + use crate::ed25519::Ed25519VerifyingKey; + use crate::secp256k1::Secp256k1PrivateKey; + use crate::secp256k1::Secp256k1VerifyingKey; + use crate::secp256r1::Secp256r1PrivateKey; + use crate::secp256r1::Secp256r1VerifyingKey; + use test_strategy::proptest; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + #[proptest] + fn ed25519_pem_der(signer: Ed25519PrivateKey) { + // + // Private Key + // + let public_key = signer.public_key(); + let ed25519_der = signer.to_der().unwrap(); + let ed25519_pem = signer.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Ed25519PrivateKey::from_der(&ed25519_der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Ed25519PrivateKey::from_pem(&ed25519_pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to secp256r1 or secp256k1 + Secp256r1PrivateKey::from_der(&ed25519_der).unwrap_err(); + Secp256r1PrivateKey::from_pem(&ed25519_pem).unwrap_err(); + Secp256k1PrivateKey::from_der(&ed25519_der).unwrap_err(); + Secp256k1PrivateKey::from_pem(&ed25519_pem).unwrap_err(); + + // SimpleKeypair parses + let keypair_from_der = SimpleKeypair::from_der(&ed25519_der).unwrap(); + assert_eq!(ed25519_der, keypair_from_der.to_der().unwrap()); + let keypair_from_pem = SimpleKeypair::from_pem(&ed25519_pem).unwrap(); + assert_eq!(ed25519_pem, keypair_from_pem.to_pem().unwrap()); + + // + // Verifying Key + // + let verifying_key = signer.verifying_key(); + let der = verifying_key.to_der().unwrap(); + let pem = verifying_key.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Ed25519VerifyingKey::from_der(&der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Ed25519VerifyingKey::from_pem(&pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to secp256r1 or secp256k1 + Secp256r1VerifyingKey::from_der(&der).unwrap_err(); + Secp256r1VerifyingKey::from_pem(&pem).unwrap_err(); + Secp256k1VerifyingKey::from_der(&der).unwrap_err(); + Secp256k1VerifyingKey::from_pem(&pem).unwrap_err(); + + // SimpleKeypair parses + let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + assert_eq!(der, from_der.to_der().unwrap()); + let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + assert_eq!(pem, from_pem.to_pem().unwrap()); + } + + #[proptest] + fn secp256r1_pem_der(signer: Secp256r1PrivateKey) { + // + // Private Key + // + let public_key = signer.public_key(); + let secp256r1_der = signer.to_der().unwrap(); + let secp256r1_pem = signer.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Secp256r1PrivateKey::from_der(&secp256r1_der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Secp256r1PrivateKey::from_pem(&secp256r1_pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to ed25519 or secp256k1 + Ed25519PrivateKey::from_der(&secp256r1_der).unwrap_err(); + Ed25519PrivateKey::from_pem(&secp256r1_pem).unwrap_err(); + Secp256k1PrivateKey::from_der(&secp256r1_der).unwrap_err(); + Secp256k1PrivateKey::from_pem(&secp256r1_pem).unwrap_err(); + + // SimpleKeypair parses + let keypair_from_der = SimpleKeypair::from_der(&secp256r1_der).unwrap(); + assert_eq!(secp256r1_der, keypair_from_der.to_der().unwrap()); + let keypair_from_pem = SimpleKeypair::from_pem(&secp256r1_pem).unwrap(); + assert_eq!(secp256r1_pem, keypair_from_pem.to_pem().unwrap()); + + // + // Verifying Key + // + let verifying_key = signer.verifying_key(); + let der = verifying_key.to_der().unwrap(); + let pem = verifying_key.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Secp256r1VerifyingKey::from_der(&der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Secp256r1VerifyingKey::from_pem(&pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to ed25519 or secp256k1 + Ed25519VerifyingKey::from_der(&der).unwrap_err(); + Ed25519VerifyingKey::from_pem(&pem).unwrap_err(); + Secp256k1VerifyingKey::from_der(&der).unwrap_err(); + Secp256k1VerifyingKey::from_pem(&pem).unwrap_err(); + + // SimpleKeypair parses + let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + assert_eq!(der, from_der.to_der().unwrap()); + let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + assert_eq!(pem, from_pem.to_pem().unwrap()); + } + + #[proptest] + fn secp256k1_pem_der(signer: Secp256k1PrivateKey) { + // + // Private Key + // + let public_key = signer.public_key(); + let secp256k1_der = signer.to_der().unwrap(); + let secp256k1_pem = signer.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Secp256k1PrivateKey::from_der(&secp256k1_der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Secp256k1PrivateKey::from_pem(&secp256k1_pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to secp256r1 or ed25519 + Ed25519PrivateKey::from_der(&secp256k1_der).unwrap_err(); + Ed25519PrivateKey::from_pem(&secp256k1_pem).unwrap_err(); + Secp256r1PrivateKey::from_der(&secp256k1_der).unwrap_err(); + Secp256r1PrivateKey::from_pem(&secp256k1_pem).unwrap_err(); + + // SimpleKeypair parses + let keypair_from_der = SimpleKeypair::from_der(&secp256k1_der).unwrap(); + assert_eq!(secp256k1_der, keypair_from_der.to_der().unwrap()); + let keypair_from_pem = SimpleKeypair::from_pem(&secp256k1_pem).unwrap(); + assert_eq!(secp256k1_pem, keypair_from_pem.to_pem().unwrap()); + + // + // Verifying Key + // + let verifying_key = signer.verifying_key(); + let der = verifying_key.to_der().unwrap(); + let pem = verifying_key.to_pem().unwrap(); + + // der and pem round-trip + let from_der = Secp256k1VerifyingKey::from_der(&der).unwrap(); + assert_eq!(from_der.public_key(), public_key); + let from_pem = Secp256k1VerifyingKey::from_pem(&pem).unwrap(); + assert_eq!(from_pem.public_key(), public_key); + + // der and pem bytes don't convert to ed25519 or secp256r1 + Ed25519VerifyingKey::from_der(&der).unwrap_err(); + Ed25519VerifyingKey::from_pem(&pem).unwrap_err(); + Secp256r1VerifyingKey::from_der(&der).unwrap_err(); + Secp256r1VerifyingKey::from_pem(&pem).unwrap_err(); + + // SimpleKeypair parses + let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + assert_eq!(der, from_der.to_der().unwrap()); + let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + assert_eq!(pem, from_pem.to_pem().unwrap()); + } +} From 4ebdf60736f3109894869ff5e52a9837fff01269 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 27 Oct 2024 13:55:39 -0500 Subject: [PATCH 054/107] crypto: rexpect UserSignatureVerifier from top-level --- crates/sui-crypto/src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/sui-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs index 32121725a..2be7cd122 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/sui-crypto/src/lib.rs @@ -61,6 +61,24 @@ pub mod simple; )] pub mod multisig; +#[cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" +))] +#[cfg_attr( + doc_cfg, + doc(cfg(any( + feature = "ed25519", + feature = "secp256r1", + feature = "secp256k1", + feature = "zklogin" + ))) +)] +#[doc(inline)] +pub use multisig::UserSignatureVerifier; + /// Interface for signing user transactions and messages in Sui /// /// # Note From 37b7ca31e88c5d3ef469cb7a959d75a75126c6fb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Sun, 27 Oct 2024 14:20:07 -0500 Subject: [PATCH 055/107] crypto: collapse 'der' feature into 'pem' Outputting some key types to 'der' format required their 'pem' feature to be enabled, so instead of having a separate 'der' and 'pem' feature lets just collapse them into a single feature to simplify things. --- crates/sui-crypto/Cargo.toml | 12 ++++++++++-- crates/sui-crypto/src/ed25519.rs | 20 ++++++++++---------- crates/sui-crypto/src/secp256k1.rs | 20 ++++++++++---------- crates/sui-crypto/src/secp256r1.rs | 20 ++++++++++---------- crates/sui-crypto/src/simple.rs | 20 ++++++++++---------- 5 files changed, 50 insertions(+), 42 deletions(-) diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index c4c594f01..e5c9974b2 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -40,8 +40,16 @@ zklogin = [ "dep:serde_json", "signature/std", ] -der = ["dep:pkcs8", "ed25519-dalek?/pkcs8", "p256?/pkcs8", "k256?/pkcs8"] -pem = ["der", "dep:pem-rfc7468", "ed25519-dalek?/pem", "p256?/pem", "k256?/pem"] +pem = [ + "dep:pkcs8", + "dep:pem-rfc7468", + "ed25519-dalek?/pkcs8", + "p256?/pkcs8", + "k256?/pkcs8", + "ed25519-dalek?/pem", + "p256?/pem", + "k256?/pem", +] [dependencies] signature = "2.2" diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs index ac6eddf53..b07c0fda5 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/sui-crypto/src/ed25519.rs @@ -60,8 +60,8 @@ impl Ed25519PrivateKey { Self(buf.into()) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { ed25519_dalek::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) @@ -69,8 +69,8 @@ impl Ed25519PrivateKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this private key as DER-encoded PKCS#8 pub fn to_der(&self) -> Result, SignatureError> { use ed25519_dalek::pkcs8::EncodePrivateKey; @@ -102,7 +102,7 @@ impl Ed25519PrivateKey { .map(|pem| (*pem).to_owned()) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_dalek(private_key: ed25519_dalek::SigningKey) -> Self { Self(private_key) } @@ -145,8 +145,8 @@ impl Ed25519VerifyingKey { Ed25519PublicKey::new(self.0.to_bytes()) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize public key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { ed25519_dalek::pkcs8::DecodePublicKey::from_public_key_der(bytes) @@ -154,8 +154,8 @@ impl Ed25519VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this public key as DER-encoded data pub fn to_der(&self) -> Result, SignatureError> { use pkcs8::EncodePublicKey; @@ -186,7 +186,7 @@ impl Ed25519VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_dalek(verifying_key: ed25519_dalek::VerifyingKey) -> Self { Self(verifying_key) } diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs index 5e09f8340..e1d2ca280 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/sui-crypto/src/secp256k1.rs @@ -63,8 +63,8 @@ impl Secp256k1PrivateKey { Self(SigningKey::random(&mut rng)) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { k256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) @@ -72,8 +72,8 @@ impl Secp256k1PrivateKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this private key as DER-encoded PKCS#8 pub fn to_der(&self) -> Result, SignatureError> { use k256::pkcs8::EncodePrivateKey; @@ -105,7 +105,7 @@ impl Secp256k1PrivateKey { .map(|pem| (*pem).to_owned()) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_k256(private_key: SigningKey) -> Self { Self(private_key) } @@ -147,8 +147,8 @@ impl Secp256k1VerifyingKey { Secp256k1PublicKey::new(self.0.as_ref().to_bytes().into()) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize public key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { k256::pkcs8::DecodePublicKey::from_public_key_der(bytes) @@ -156,8 +156,8 @@ impl Secp256k1VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this public key as DER-encoded data pub fn to_der(&self) -> Result, SignatureError> { use pkcs8::EncodePublicKey; @@ -188,7 +188,7 @@ impl Secp256k1VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_k256(verifying_key: VerifyingKey) -> Self { Self(verifying_key) } diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs index da53eeb0b..0f00b467c 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/sui-crypto/src/secp256r1.rs @@ -63,8 +63,8 @@ impl Secp256r1PrivateKey { Self::new(buf) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { p256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) @@ -72,8 +72,8 @@ impl Secp256r1PrivateKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this private key as DER-encoded PKCS#8 pub fn to_der(&self) -> Result, SignatureError> { use p256::pkcs8::EncodePrivateKey; @@ -105,7 +105,7 @@ impl Secp256r1PrivateKey { .map(|pem| (*pem).to_owned()) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_p256(private_key: SigningKey) -> Self { Self(private_key) } @@ -147,8 +147,8 @@ impl Secp256r1VerifyingKey { Secp256r1PublicKey::new(self.0.as_ref().to_bytes().into()) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize public key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { p256::pkcs8::DecodePublicKey::from_public_key_der(bytes) @@ -156,8 +156,8 @@ impl Secp256r1VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this public key as DER-encoded data pub fn to_der(&self) -> Result, SignatureError> { use pkcs8::EncodePublicKey; @@ -188,7 +188,7 @@ impl Secp256r1VerifyingKey { .map_err(SignatureError::from_source) } - #[cfg(feature = "der")] + #[cfg(feature = "pem")] pub(crate) fn from_p256(verifying_key: VerifyingKey) -> Self { Self(verifying_key) } diff --git a/crates/sui-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs index fd8c6fb7f..db1ec9f28 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/sui-crypto/src/simple.rs @@ -133,8 +133,8 @@ mod keypair { self.verifying_key().public_key() } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { let private_key = @@ -179,8 +179,8 @@ mod keypair { .map(|inner| Self { inner }) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this private key as DER-encoded PKCS#8 pub fn to_der(&self) -> Result, SignatureError> { match &self.inner { @@ -206,8 +206,8 @@ mod keypair { Self::from_der(doc.as_bytes()) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this private key as DER-encoded PKCS#8 pub fn to_pem(&self) -> Result { match &self.inner { @@ -312,8 +312,8 @@ mod keypair { } } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Deserialize public key from ASN.1 DER-encoded data (binary format). pub fn from_der(bytes: &[u8]) -> Result { let public_key = pkcs8::SubjectPublicKeyInfoRef::try_from(bytes) @@ -358,8 +358,8 @@ mod keypair { .map(|inner| Self { inner }) } - #[cfg(feature = "der")] - #[cfg_attr(doc_cfg, doc(cfg(feature = "der")))] + #[cfg(feature = "pem")] + #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] /// Serialize this public key as DER-encoded data pub fn to_der(&self) -> Result, SignatureError> { match &self.inner { From bc763fcbb9f1e0f91339712ccc397c8be14cb209 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 28 Oct 2024 06:49:27 -0700 Subject: [PATCH 056/107] sui-graphql-client: add some more doc comments (#50) --- crates/sui-graphql-client/src/lib.rs | 19 ++++++++++++++----- .../src/query_types/active_validators.rs | 3 ++- .../src/query_types/coin.rs | 8 ++++++++ .../src/query_types/epoch.rs | 5 +++++ .../src/query_types/protocol_config.rs | 12 ++++++++++++ 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 3dc791120..f4b87ee42 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -6,7 +6,6 @@ pub mod faucet; pub mod query_types; -use base64ct::Encoding; use query_types::ActiveValidatorsArgs; use query_types::ActiveValidatorsQuery; use query_types::BalanceArgs; @@ -53,12 +52,11 @@ use query_types::TransactionMetadata; use query_types::TransactionsFilter; use query_types::Validator; -use serde::de::DeserializeOwned; -use serde::Serialize; use sui_types::types::framework::Coin; use sui_types::types::Address; use sui_types::types::CheckpointSequenceNumber; use sui_types::types::CheckpointSummary; +use sui_types::types::Digest; use sui_types::types::Event; use sui_types::types::Object; use sui_types::types::SignedTransaction; @@ -72,6 +70,7 @@ use anyhow::anyhow; use anyhow::ensure; use anyhow::Error; use anyhow::Result; +use base64ct::Encoding; use cynic::serde; use cynic::GraphQlResponse; use cynic::MutationBuilder; @@ -79,6 +78,8 @@ use cynic::Operation; use cynic::QueryBuilder; use futures::Stream; use reqwest::Url; +use serde::de::DeserializeOwned; +use serde::Serialize; use std::pin::Pin; const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; @@ -91,12 +92,15 @@ static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_V // Output Types // =========================================================================== +/// The result of a dry run, which includes the effects of the transaction and any errors that may +/// have occurred. #[derive(Debug)] pub struct DryRunResult { pub effects: Option, pub error: Option, } +/// The name part of a dynamic field, including its type, bcs, and json representation. #[derive(Debug)] pub struct DynamicFieldName { /// The type name of this dynamic field name @@ -107,6 +111,8 @@ pub struct DynamicFieldName { pub json: Option, } +/// The output of a dynamic field query, that includes the name, value, and value's json +/// representation. #[derive(Debug)] pub struct DynamicFieldOutput { /// The name of the dynamic field @@ -171,8 +177,11 @@ pub enum Direction { /// GraphQL server's default items per page limit. #[derive(Default)] pub struct PaginationFilter<'a> { + /// The direction of pagination. direction: Direction, + /// An opaque cursor used for pagination. cursor: Option<&'a str>, + /// The maximum number of items to return. Use `service_config` to find the limit. limit: Option, } @@ -816,6 +825,7 @@ impl Client { // Events API // =========================================================================== + /// Return a page of events based on the provided filters. pub async fn events( &self, filter: Option, @@ -1124,9 +1134,8 @@ impl Client { // Transaction API // =========================================================================== - // TODO: From Brandon: this fails due to SignedTransaction in Sui core type being technically inaccurate but it is fixed in this SDK here. in particular core incorrectly appends the signing intent when it shouldn't so my guess is that's whats wrong /// Get a transaction by its digest. - pub async fn transaction(&self, digest: &str) -> Result, Error> { + pub async fn transaction(&self, digest: Digest) -> Result, Error> { let operation = TransactionBlockQuery::build(TransactionBlockArgs { digest: digest.to_string(), }); diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/sui-graphql-client/src/query_types/active_validators.rs index 925b70975..4842f7c2a 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/sui-graphql-client/src/query_types/active_validators.rs @@ -57,6 +57,7 @@ pub struct ValidatorConnection { pub nodes: Vec, } +/// Represents a validator in the system. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Validator")] pub struct Validator { @@ -114,10 +115,10 @@ pub struct Validator { pub voting_power: Option, } +/// The credentials related fields associated with a validator. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ValidatorCredentials")] #[allow(non_snake_case)] -/// The credentials related fields associated with a validator. pub struct ValidatorCredentials { pub protocol_pub_key: Option, pub network_pub_key: Option, diff --git a/crates/sui-graphql-client/src/query_types/coin.rs b/crates/sui-graphql-client/src/query_types/coin.rs index 9f1dbce77..10632ac9b 100644 --- a/crates/sui-graphql-client/src/query_types/coin.rs +++ b/crates/sui-graphql-client/src/query_types/coin.rs @@ -28,14 +28,22 @@ pub struct CoinMetadataArgs<'a> { use crate::query_types::schema; use crate::query_types::BigInt; +/// The coin metadata associated with the given coin type. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "CoinMetadata")] pub struct CoinMetadata { + /// The number of decimal places used to represent the token. pub decimals: Option, + /// Optional description of the token, provided by the creator of the token. pub description: Option, + /// Icon URL of the coin. pub icon_url: Option, + /// Full, official name of the token. pub name: Option, + /// The token's identifying abbreviation. pub symbol: Option, + /// The overall quantity of tokens that will be issued. pub supply: Option, + /// Version of the token. pub version: u64, } diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/sui-graphql-client/src/query_types/epoch.rs index 117aa607e..1ee12ba5e 100644 --- a/crates/sui-graphql-client/src/query_types/epoch.rs +++ b/crates/sui-graphql-client/src/query_types/epoch.rs @@ -26,12 +26,17 @@ pub struct EpochSummaryArgs { pub id: Option, } +/// A summary of the epoch. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Epoch")] pub struct EpochSummary { + /// The epoch number. pub epoch_id: u64, + /// The reference gas price throughout this epoch. pub reference_gas_price: Option, + /// The total number of checkpoints in this epoch. pub total_checkpoints: Option, + /// The total number of transactions in this epoch. pub total_transactions: Option, } diff --git a/crates/sui-graphql-client/src/query_types/protocol_config.rs b/crates/sui-graphql-client/src/query_types/protocol_config.rs index e46e72d12..1441ddaa2 100644 --- a/crates/sui-graphql-client/src/query_types/protocol_config.rs +++ b/crates/sui-graphql-client/src/query_types/protocol_config.rs @@ -32,14 +32,25 @@ pub struct ProtocolVersionArgs { // =========================================================================== /// Information about the configuration of the protocol. +/// Constants that control how the chain operates. +/// These can only change during protocol upgrades which happen on epoch boundaries. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigs")] pub struct ProtocolConfigs { + /// The protocol is not required to change on every epoch boundary, so the protocol version + /// tracks which change to the protocol these configs are from. pub protocol_version: u64, + /// List all available feature flags and their values. Feature flags are a form of boolean + /// configuration that are usually used to gate features while they are in development. Once a + /// flag has been enabled, it is rare for it to be disabled. pub feature_flags: Vec, + /// List all available configurations and their values. These configurations can take any value + /// (but they will all be represented in string form), and do not include feature flags. pub configs: Vec, } +/// Feature flags are a form of boolean configuration that are usually used to gate features while +/// they are in development. Once a lag has been enabled, it is rare for it to be disabled. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigFeatureFlag")] pub struct ProtocolConfigFeatureFlag { @@ -47,6 +58,7 @@ pub struct ProtocolConfigFeatureFlag { pub value: bool, } +/// A key-value protocol configuration attribute. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigAttr")] pub struct ProtocolConfigAttr { From 30b19e2aff0c8e3890982550d0579b68759e1d50 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:49:36 -0700 Subject: [PATCH 057/107] sui-graphql-client: add total transaction blocks query (#47) --- crates/sui-graphql-client/src/lib.rs | 97 ++++++++++++++++++- .../src/query_types/checkpoint.rs | 18 +++- .../sui-graphql-client/src/query_types/mod.rs | 1 + 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index f4b87ee42..73a9afcd6 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -82,6 +82,8 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::pin::Pin; +use crate::query_types::CheckpointTotalTxQuery; + const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; @@ -435,6 +437,62 @@ impl Client { } } + /// The total number of transaction blocks in the network by the end of the provided + /// checkpoint digest. + pub async fn total_transaction_blocks_by_digest( + &self, + digest: Digest, + ) -> Result, Error> { + self.internal_total_transaction_blocks(Some(digest.to_string()), None) + .await + } + + /// The total number of transaction blocks in the network by the end of the provided checkpoint + /// sequence number. + pub async fn total_transaction_blocks_by_seq_num( + &self, + seq_num: u64, + ) -> Result, Error> { + self.internal_total_transaction_blocks(None, Some(seq_num)) + .await + } + + /// The total number of transaction blocks in the network by the end of the last known + /// checkpoint. + pub async fn total_transaction_blocks(&self) -> Result, Error> { + self.internal_total_transaction_blocks(None, None).await + } + + /// Internal function to get the total number of transaction blocks based on the provided + /// checkpoint digest or sequence number. + async fn internal_total_transaction_blocks( + &self, + digest: Option, + seq_num: Option, + ) -> Result, Error> { + ensure!( + !(digest.is_some() && seq_num.is_some()), + "Cannot provide both digest and seq_num." + ); + + let operation = CheckpointTotalTxQuery::build(CheckpointArgs { + id: CheckpointId { + digest, + sequence_number: seq_num, + }, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response + .data + .and_then(|x| x.checkpoint) + .and_then(|c| c.network_total_transactions)) + } + // =========================================================================== // Balance API // =========================================================================== @@ -571,7 +629,7 @@ impl Client { /// provided, it will use the last known checkpoint id. pub async fn checkpoint( &self, - digest: Option, + digest: Option, seq_num: Option, ) -> Result, Error> { ensure!( @@ -581,7 +639,7 @@ impl Client { let operation = CheckpointQuery::build(CheckpointArgs { id: CheckpointId { - digest, + digest: digest.map(|d| d.to_string()), sequence_number: seq_num, }, }); @@ -1587,4 +1645,39 @@ mod tests { dynamic_fields.unwrap_err() ); } + + #[tokio::test] + async fn test_total_transaction_blocks() { + let client = test_client(); + let total_transaction_blocks = client.total_transaction_blocks().await; + assert!( + total_transaction_blocks + .as_ref() + .is_ok_and(|f| f.is_some_and(|tx| tx > 0)), + "Total transaction blocks query failed for {} network. Error: {}", + client.rpc_server(), + total_transaction_blocks.unwrap_err() + ); + + let chckp = client.latest_checkpoint_sequence_number().await; + assert!( + chckp.is_ok(), + "Latest checkpoint sequence number query failed for {} network. Error: {}", + client.rpc_server(), + chckp.unwrap_err() + ); + let chckp_id = chckp.unwrap().unwrap(); + let total_transaction_blocks = client.total_transaction_blocks_by_seq_num(chckp_id).await; + assert!(total_transaction_blocks.is_ok()); + assert!(total_transaction_blocks.unwrap().is_some_and(|tx| tx > 0)); + + let chckp = client.checkpoint(None, Some(chckp_id)).await; + assert!(chckp.is_ok()); + let digest = chckp.unwrap().unwrap().content_digest; + let total_transaction_blocks = client + .total_transaction_blocks_by_digest(digest.into()) + .await; + assert!(total_transaction_blocks.is_ok()); + assert!(total_transaction_blocks.unwrap().is_some_and(|tx| tx > 0)); + } } diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index ecf3c68b6..bdd01d436 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -1,6 +1,5 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; use anyhow::Error; use chrono::DateTime as ChronoDT; @@ -27,6 +26,19 @@ pub struct CheckpointQuery { pub checkpoint: Option, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "CheckpointArgs")] +pub struct CheckpointTotalTxQuery { + #[arguments(id: $id)] + pub checkpoint: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Checkpoint")] +pub struct CheckpointTotalTx { + pub network_total_transactions: Option, +} + #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "CheckpointsArgs")] pub struct CheckpointsQuery { @@ -106,10 +118,10 @@ impl TryInto for Checkpoint { .map_err(|e| Error::msg(format!("Cannot parse DateTime: {e}")))? .timestamp_millis() .try_into()?; - let content_digest = CheckpointContentsDigest::from_str(&self.digest)?; + let content_digest = CheckpointContentsDigest::from_base58(&self.digest)?; let previous_digest = self .previous_checkpoint_digest - .map(|d| CheckpointDigest::from_str(&d)) + .map(|d| CheckpointDigest::from_base58(&d)) .transpose()?; let epoch_rolling_gas_cost_summary = self .rolling_gas_summary diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 1b3be8c8c..d64d8ac5d 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -30,6 +30,7 @@ pub use chain::ChainIdentifierQuery; pub use checkpoint::CheckpointArgs; pub use checkpoint::CheckpointId; pub use checkpoint::CheckpointQuery; +pub use checkpoint::CheckpointTotalTxQuery; pub use checkpoint::CheckpointsArgs; pub use checkpoint::CheckpointsQuery; pub use coin::CoinMetadata; From 08b7196666bb05875c905ef28bedba0e6c5831b0 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:50:36 -0700 Subject: [PATCH 058/107] sui-graphql-client: change PaginationFilter to use a String vs &str (#43) --- crates/sui-graphql-client/src/lib.rs | 54 ++++++++++++++-------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 73a9afcd6..a547cc8c6 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -178,13 +178,13 @@ pub enum Direction { /// Pagination options for querying the GraphQL server. It defaults to forward pagination with the /// GraphQL server's default items per page limit. #[derive(Default)] -pub struct PaginationFilter<'a> { +pub struct PaginationFilter { /// The direction of pagination. - direction: Direction, + pub direction: Direction, /// An opaque cursor used for pagination. - cursor: Option<&'a str>, + pub cursor: Option, /// The maximum number of items to return. Use `service_config` to find the limit. - limit: Option, + pub limit: Option, } impl From for NameValue { @@ -297,10 +297,10 @@ impl Client { } /// Internal function to handle pagination filters and return the appropriate values. - fn pagination_filter<'a>( + fn pagination_filter( &self, - pagination_filter: PaginationFilter<'a>, - ) -> (Option<&'a str>, Option<&'a str>, Option, Option) { + pagination_filter: PaginationFilter, + ) -> (Option, Option, Option, Option) { let (after, before, first, last) = match pagination_filter.direction { Direction::Forward => ( pagination_filter.cursor, @@ -403,14 +403,14 @@ impl Client { pub async fn active_validators<'a>( &self, epoch: Option, - pagination_filter: PaginationFilter<'a>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { id: epoch, - after, - before, + after: after.as_deref(), + before: before.as_deref(), first, last, }); @@ -537,7 +537,7 @@ impl Client { &self, owner: Address, coin_type: Option<&str>, - pagination_filter: PaginationFilter<'a>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let response = self .objects( @@ -579,7 +579,7 @@ impl Client { object_keys: None, }), PaginationFilter { - cursor: after.as_deref(), + cursor: after, ..Default::default() }, ).await?; @@ -658,13 +658,13 @@ impl Client { /// Get a page of [`CheckpointSummary`] for the provided parameters. pub async fn checkpoints<'a>( &self, - pagination_filter: PaginationFilter<'a>, + pagination_filter: PaginationFilter, ) -> Result>, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = CheckpointsQuery::build(CheckpointsArgs { - after, - before, + after: after.as_deref(), + before: before.as_deref(), first, last, }); @@ -801,13 +801,13 @@ impl Client { pub async fn dynamic_fields<'a>( &self, address: Address, - pagination_filter: PaginationFilter<'a>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = DynamicFieldsOwnerQuery::build(DynamicFieldConnectionArgs { address, - after, - before, + after: after.as_deref(), + before: before.as_deref(), first, last, }); @@ -887,14 +887,14 @@ impl Client { pub async fn events( &self, filter: Option, - pagination_filter: PaginationFilter<'_>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = EventsQuery::build(EventsQueryArgs { filter, - after, - before, + after: after.as_deref(), + before: before.as_deref(), first, last, }); @@ -985,12 +985,12 @@ impl Client { pub async fn objects( &self, filter: Option>, - pagination_filter: PaginationFilter<'_>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = ObjectsQuery::build(ObjectsQueryArgs { - after, - before, + after: after.as_deref(), + before: before.as_deref(), filter, first, last, @@ -1211,13 +1211,13 @@ impl Client { pub async fn transactions<'a>( &self, filter: Option>, - pagination_filter: PaginationFilter<'a>, + pagination_filter: PaginationFilter, ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter); let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { - after, - before, + after: after.as_deref(), + before: before.as_deref(), filter, first, last, From 5d4129c4e3fbbc56e24b4bab7004ca0091d63c47 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:37:18 -0700 Subject: [PATCH 059/107] sui-graphql-client: add normalized move function and module queries (#49) --- crates/sui-graphql-client/src/lib.rs | 89 +++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 11 ++ .../query_types/normalized_move/function.rs | 47 ++++++ .../src/query_types/normalized_move/mod.rs | 54 +++++++ .../src/query_types/normalized_move/module.rs | 151 ++++++++++++++++++ 5 files changed, 352 insertions(+) create mode 100644 crates/sui-graphql-client/src/query_types/normalized_move/function.rs create mode 100644 crates/sui-graphql-client/src/query_types/normalized_move/mod.rs create mode 100644 crates/sui-graphql-client/src/query_types/normalized_move/module.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index a547cc8c6..84f953c75 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -33,6 +33,12 @@ use query_types::EventsQuery; use query_types::EventsQueryArgs; use query_types::ExecuteTransactionArgs; use query_types::ExecuteTransactionQuery; +use query_types::MoveFunction; +use query_types::MoveModule; +use query_types::NormalizedMoveFunctionQuery; +use query_types::NormalizedMoveFunctionQueryArgs; +use query_types::NormalizedMoveModuleQuery; +use query_types::NormalizedMoveModuleQueryArgs; use query_types::ObjectFilter; use query_types::ObjectQuery; use query_types::ObjectQueryArgs; @@ -81,6 +87,7 @@ use reqwest::Url; use serde::de::DeserializeOwned; use serde::Serialize; use std::pin::Pin; +use std::str::FromStr; use crate::query_types::CheckpointTotalTxQuery; @@ -1272,6 +1279,88 @@ impl Client { Ok(None) } } + + // =========================================================================== + // Normalized Move Package API + // =========================================================================== + /// Return the normalized Move function data for the provided package, module, and function. + pub async fn normalized_move_function( + &self, + package: &str, + module: &str, + function: &str, + version: Option, + ) -> Result, Error> { + let operation = NormalizedMoveFunctionQuery::build(NormalizedMoveFunctionQueryArgs { + address: Address::from_str(package)?, + module, + function, + version, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response + .data + .and_then(|p| p.package) + .and_then(|p| p.module) + .and_then(|m| m.function)) + } + + /// Return the normalized Move module data for the provided module. + // TODO: do we want to self paginate everything and return all the data, or keep pagination + // options? + #[allow(clippy::too_many_arguments)] + pub async fn normalized_move_module( + &self, + package: &str, + module: &str, + version: Option, + pagination_filter_enums: PaginationFilter, + pagination_filter_friends: PaginationFilter, + pagination_filter_functions: PaginationFilter, + pagination_filter_structs: PaginationFilter, + ) -> Result, Error> { + let (after_enums, before_enums, first_enums, last_enums) = + self.pagination_filter(pagination_filter_enums); + let (after_friends, before_friends, first_friends, last_friends) = + self.pagination_filter(pagination_filter_friends); + let (after_functions, before_functions, first_functions, last_functions) = + self.pagination_filter(pagination_filter_functions); + let (after_structs, before_structs, first_structs, last_structs) = + self.pagination_filter(pagination_filter_structs); + let operation = NormalizedMoveModuleQuery::build(NormalizedMoveModuleQueryArgs { + package: Address::from_str(package)?, + module, + version, + after_enums: after_enums.as_deref(), + after_functions: after_functions.as_deref(), + after_structs: after_structs.as_deref(), + after_friends: after_friends.as_deref(), + before_enums: before_enums.as_deref(), + before_functions: before_functions.as_deref(), + before_structs: before_structs.as_deref(), + before_friends: before_friends.as_deref(), + first_enums, + first_functions, + first_structs, + first_friends, + last_enums, + last_functions, + last_structs, + last_friends, + }); + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response.data.and_then(|p| p.package).and_then(|p| p.module)) + } } // This function is used in tests to create a new client instance for the local server. diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index d64d8ac5d..7aca87819 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -11,6 +11,7 @@ mod dynamic_fields; mod epoch; mod events; mod execute_tx; +mod normalized_move; mod object; mod protocol_config; mod service_config; @@ -57,6 +58,16 @@ pub use events::EventsQueryArgs; pub use execute_tx::ExecuteTransactionArgs; pub use execute_tx::ExecuteTransactionQuery; pub use execute_tx::ExecutionResult; +pub use normalized_move::MoveAbility; +pub use normalized_move::MoveFunction; +pub use normalized_move::MoveFunctionTypeParameter; +pub use normalized_move::MoveModule; +pub use normalized_move::MoveVisibility; +pub use normalized_move::NormalizedMoveFunctionQuery; +pub use normalized_move::NormalizedMoveFunctionQueryArgs; +pub use normalized_move::NormalizedMoveModuleQuery; +pub use normalized_move::NormalizedMoveModuleQueryArgs; +pub use normalized_move::OpenMoveType; pub use object::ObjectFilter; pub use object::ObjectKey; pub use object::ObjectQuery; diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/function.rs b/crates/sui-graphql-client/src/query_types/normalized_move/function.rs new file mode 100644 index 000000000..e60b5fda6 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/normalized_move/function.rs @@ -0,0 +1,47 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::schema; +use crate::query_types::Address; +use crate::query_types::MoveFunction; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "NormalizedMoveFunctionQueryArgs" +)] +pub struct NormalizedMoveFunctionQuery { + #[arguments(address: $address, version: $version)] + pub package: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct NormalizedMoveFunctionQueryArgs<'a> { + pub address: Address, + pub version: Option, + pub module: &'a str, + pub function: &'a str, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "MovePackage", + variables = "NormalizedMoveFunctionQueryArgs" +)] +pub struct MovePackage { + #[arguments(name: $module)] + pub module: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "MoveModule", + variables = "NormalizedMoveFunctionQueryArgs" +)] +pub struct MoveModule { + #[arguments(name: $function)] + pub function: Option, +} diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/mod.rs b/crates/sui-graphql-client/src/query_types/normalized_move/mod.rs new file mode 100644 index 000000000..053fde61b --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/normalized_move/mod.rs @@ -0,0 +1,54 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod function; +mod module; + +pub use function::NormalizedMoveFunctionQuery; +pub use function::NormalizedMoveFunctionQueryArgs; +pub use module::MoveModule; +pub use module::NormalizedMoveModuleQuery; +pub use module::NormalizedMoveModuleQueryArgs; + +use crate::query_types::schema; + +#[derive(cynic::Enum, Clone, Copy, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveAbility")] +pub enum MoveAbility { + Copy, + Drop, + Key, + Store, +} + +#[derive(cynic::Enum, Clone, Copy, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveVisibility")] +pub enum MoveVisibility { + Public, + Private, + Friend, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveFunction")] +pub struct MoveFunction { + pub is_entry: Option, + pub name: String, + pub parameters: Option>, + #[cynic(rename = "return")] + pub return_: Option>, + pub type_parameters: Option>, + pub visibility: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveFunctionTypeParameter")] +pub struct MoveFunctionTypeParameter { + pub constraints: Vec, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "OpenMoveType")] +pub struct OpenMoveType { + pub repr: String, +} diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/module.rs b/crates/sui-graphql-client/src/query_types/normalized_move/module.rs new file mode 100644 index 000000000..3448d4501 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/normalized_move/module.rs @@ -0,0 +1,151 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::schema; +use crate::query_types::Address; +use crate::query_types::MoveAbility; +use crate::query_types::MoveFunction; +use crate::query_types::PageInfo; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "NormalizedMoveModuleQueryArgs" +)] +pub struct NormalizedMoveModuleQuery { + #[arguments(address: $package, version: $version)] + pub package: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct NormalizedMoveModuleQueryArgs<'a> { + pub package: Address, + pub module: &'a str, + pub version: Option, + pub after_enums: Option<&'a str>, + pub after_functions: Option<&'a str>, + pub after_structs: Option<&'a str>, + pub after_friends: Option<&'a str>, + pub before_enums: Option<&'a str>, + pub before_functions: Option<&'a str>, + pub before_structs: Option<&'a str>, + pub before_friends: Option<&'a str>, + pub first_enums: Option, + pub first_functions: Option, + pub first_structs: Option, + pub first_friends: Option, + pub last_enums: Option, + pub last_functions: Option, + pub last_structs: Option, + pub last_friends: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "MovePackage", + variables = "NormalizedMoveModuleQueryArgs" +)] +pub struct MovePackage { + #[arguments(name: $module)] + pub module: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "MoveModule", + variables = "NormalizedMoveModuleQueryArgs" +)] +pub struct MoveModule { + pub file_format_version: i32, + #[arguments(after: $after_enums, before:$before_enums, first: $first_enums, last: $last_enums)] + pub enums: Option, + #[arguments(after: $after_friends, before: $before_friends, first: $first_friends, last: $last_friends)] + pub friends: MoveModuleConnection, + #[arguments(after: $after_functions, before: $before_functions, first: $first_functions, last: $last_functions)] + pub functions: Option, + #[arguments(after: $after_structs, before: $before_structs, first: $first_structs, last: $last_structs)] + pub structs: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveStructConnection")] +pub struct MoveStructConnection { + pub page_info: PageInfo, + pub nodes: Vec, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveStruct")] +pub struct MoveStruct { + pub abilities: Option>, + pub name: String, + pub fields: Option>, + pub type_parameters: Option>, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveModuleConnection")] +pub struct MoveModuleConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveModule")] +pub struct MoveModule2 { + pub name: String, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveFunctionConnection")] +pub struct MoveFunctionConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveEnumConnection")] +pub struct MoveEnumConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveEnum")] +pub struct MoveEnum { + pub abilities: Option>, + pub name: String, + pub type_parameters: Option>, + pub variants: Option>, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveEnumVariant")] +pub struct MoveEnumVariant { + pub fields: Option>, + pub name: String, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveField")] +pub struct MoveField { + pub name: String, + #[cynic(rename = "type")] + pub type_: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "OpenMoveType")] +pub struct OpenMoveType { + pub repr: String, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveStructTypeParameter")] +pub struct MoveStructTypeParameter { + pub constraints: Vec, + pub is_phantom: bool, +} From 910cf62898e7fcd2e6a6f0d4cde551ffab4175d8 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:45:55 -0700 Subject: [PATCH 060/107] sui-graphql-client: add suins queries (#48) --- crates/sui-graphql-client/src/lib.rs | 38 +++++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 5 ++ .../src/query_types/suins.rs | 53 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 crates/sui-graphql-client/src/query_types/suins.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 84f953c75..56248c18d 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -19,6 +19,8 @@ use query_types::CheckpointsQuery; use query_types::CoinMetadata; use query_types::CoinMetadataArgs; use query_types::CoinMetadataQuery; +use query_types::DefaultSuinsNameQuery; +use query_types::DefaultSuinsNameQueryArgs; use query_types::DryRunArgs; use query_types::DryRunQuery; use query_types::DynamicFieldArgs; @@ -48,6 +50,8 @@ use query_types::PageInfo; use query_types::ProtocolConfigQuery; use query_types::ProtocolConfigs; use query_types::ProtocolVersionArgs; +use query_types::ResolveSuinsQuery; +use query_types::ResolveSuinsQueryArgs; use query_types::ServiceConfig; use query_types::ServiceConfigQuery; use query_types::TransactionBlockArgs; @@ -1361,6 +1365,40 @@ impl Client { Ok(response.data.and_then(|p| p.package).and_then(|p| p.module)) } + + // =========================================================================== + // SuiNS + // =========================================================================== + + /// Get the address for the provided Suins domain name. + pub async fn resolve_suins_to_address(&self, domain: &str) -> Result, Error> { + let operation = ResolveSuinsQuery::build(ResolveSuinsQueryArgs { name: domain }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + Ok(response + .data + .and_then(|d| d.resolve_suins_address) + .map(|a| a.address)) + } + + /// Get the default Suins domain name for the provided address. + pub async fn default_suins_name(&self, address: Address) -> Result, Error> { + let operation = DefaultSuinsNameQuery::build(DefaultSuinsNameQueryArgs { address }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + Ok(response + .data + .and_then(|d| d.address) + .and_then(|a| a.default_suins_name)) + } } // This function is used in tests to create a new client instance for the local server. diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 7aca87819..a838a4fb6 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -15,6 +15,7 @@ mod normalized_move; mod object; mod protocol_config; mod service_config; +mod suins; mod transaction; pub use active_validators::ActiveValidatorsArgs; @@ -80,6 +81,10 @@ pub use protocol_config::ProtocolVersionArgs; pub use service_config::Feature; pub use service_config::ServiceConfig; pub use service_config::ServiceConfigQuery; +pub use suins::DefaultSuinsNameQuery; +pub use suins::DefaultSuinsNameQueryArgs; +pub use suins::ResolveSuinsQuery; +pub use suins::ResolveSuinsQueryArgs; pub use transaction::TransactionBlock; pub use transaction::TransactionBlockArgs; pub use transaction::TransactionBlockQuery; diff --git a/crates/sui-graphql-client/src/query_types/suins.rs b/crates/sui-graphql-client/src/query_types/suins.rs new file mode 100644 index 000000000..3c4b0b4a1 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/suins.rs @@ -0,0 +1,53 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// =========================================================================== +// Suins Queries +// =========================================================================== + +use crate::query_types::schema; +use crate::query_types::Address as SdkAddress; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "ResolveSuinsQueryArgs" +)] +pub struct ResolveSuinsQuery { + #[arguments(domain: $name)] + pub resolve_suins_address: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct ResolveSuinsQueryArgs<'a> { + pub name: &'a str, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Address")] +pub struct DomainAddress { + pub address: SdkAddress, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "DefaultSuinsNameQueryArgs" +)] +pub struct DefaultSuinsNameQuery { + #[arguments(address: $address)] + pub address: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct DefaultSuinsNameQueryArgs { + pub address: SdkAddress, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Address")] +pub struct AddressDefaultSuins { + pub default_suins_name: Option, +} From 96a676271eccef7bc56e189c9329a7555e60a6e4 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:50:22 -0700 Subject: [PATCH 061/107] sui-graphql-client: add generic stream impl (#51) --- crates/sui-graphql-client/src/lib.rs | 191 +++++++++++------- .../src/query_types/events.rs | 2 +- .../sui-graphql-client/src/query_types/mod.rs | 2 +- .../src/query_types/object.rs | 4 +- .../src/query_types/transaction.rs | 2 +- crates/sui-graphql-client/src/streams.rs | 178 ++++++++++++++++ 6 files changed, 303 insertions(+), 76 deletions(-) create mode 100644 crates/sui-graphql-client/src/streams.rs diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 56248c18d..179eb5fb9 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -5,6 +5,7 @@ pub mod faucet; pub mod query_types; +pub mod streams; use query_types::ActiveValidatorsArgs; use query_types::ActiveValidatorsQuery; @@ -61,6 +62,7 @@ use query_types::TransactionBlocksQueryArgs; use query_types::TransactionMetadata; use query_types::TransactionsFilter; use query_types::Validator; +use streams::stream_paginated_query; use sui_types::types::framework::Coin; use sui_types::types::Address; @@ -90,11 +92,12 @@ use futures::Stream; use reqwest::Url; use serde::de::DeserializeOwned; use serde::Serialize; -use std::pin::Pin; use std::str::FromStr; use crate::query_types::CheckpointTotalTxQuery; +const DEFAULT_ITEMS_PER_PAGE: i32 = 10; + const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; @@ -114,7 +117,7 @@ pub struct DryRunResult { } /// The name part of a dynamic field, including its type, bcs, and json representation. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DynamicFieldName { /// The type name of this dynamic field name pub type_: TypeTag, @@ -126,7 +129,7 @@ pub struct DynamicFieldName { /// The output of a dynamic field query, that includes the name, value, and value's json /// representation. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DynamicFieldOutput { /// The name of the dynamic field pub name: DynamicFieldName, @@ -142,7 +145,7 @@ pub struct NameValue(Vec); /// Helper struct for passing a raw bcs value. pub struct BcsName(pub Vec); -#[derive(Debug)] +#[derive(Clone, Debug)] /// A page of items returned by the GraphQL server. pub struct Page { /// Information about the page, such as the cursor and whether there are more pages. @@ -176,10 +179,14 @@ impl Page { fn new_empty() -> Self { Self::new(PageInfo::default(), vec![]) } + + pub fn into_parts(self) -> (PageInfo, Vec) { + (self.page_info, self.data) + } } /// Pagination direction. -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub enum Direction { #[default] Forward, @@ -188,7 +195,7 @@ pub enum Direction { /// Pagination options for querying the GraphQL server. It defaults to forward pagination with the /// GraphQL server's default items per page limit. -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct PaginationFilter { /// The direction of pagination. pub direction: Direction, @@ -308,23 +315,23 @@ impl Client { } /// Internal function to handle pagination filters and return the appropriate values. - fn pagination_filter( + async fn pagination_filter( &self, pagination_filter: PaginationFilter, ) -> (Option, Option, Option, Option) { + let limit = if let Some(limit) = pagination_filter.limit { + limit + } else { + let cfg = self.service_config().await; + if let Ok(cfg) = cfg { + cfg.max_page_size + } else { + DEFAULT_ITEMS_PER_PAGE + } + }; let (after, before, first, last) = match pagination_filter.direction { - Direction::Forward => ( - pagination_filter.cursor, - None, - pagination_filter.limit, - None, - ), - Direction::Backward => ( - None, - pagination_filter.cursor, - None, - pagination_filter.limit, - ), + Direction::Forward => (pagination_filter.cursor, None, Some(limit), None), + Direction::Backward => (None, pagination_filter.cursor, None, Some(limit)), }; (after, before, first, last) } @@ -416,7 +423,7 @@ impl Client { epoch: Option, pagination_filter: PaginationFilter, ) -> Result, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { id: epoch, @@ -573,41 +580,20 @@ impl Client { )) } - /// Stream of coins for the specified address and coin type. - pub fn coins_stream<'a>( - &'a self, - owner: Address, - coin_type: Option<&'a str>, - ) -> Pin> + 'a>> { - Box::pin(async_stream::try_stream! { - let mut after = None; - loop { - let response = self.objects( - Some(ObjectFilter { - type_: Some(coin_type.unwrap_or("0x2::coin::Coin")), - owner: Some(owner), - object_ids: None, - object_keys: None, - }), - PaginationFilter { - cursor: after, - ..Default::default() - }, - ).await?; - - for object in response.data() { - if let Some(coin) = Coin::try_from_object(object) { - yield coin.into_owned(); - } - } - - if let Some(end_cursor) = response.page_info.end_cursor { - after = Some(end_cursor); - } else { - break; - } - } - }) + /// Get the list of coins for the specified address as a stream. + /// + /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all + /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. + pub async fn coins_stream( + &self, + address: Address, + coin_type: Option<&'static str>, + streaming_direction: Direction, + ) -> impl Stream> { + stream_paginated_query( + move |filter| self.coins(address, coin_type, filter), + streaming_direction, + ) } /// Get the coin metadata for the coin type. @@ -670,8 +656,8 @@ impl Client { pub async fn checkpoints<'a>( &self, pagination_filter: PaginationFilter, - ) -> Result>, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + ) -> Result, Error> { + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = CheckpointsQuery::build(CheckpointsArgs { after: after.as_deref(), @@ -694,12 +680,21 @@ impl Client { .map(|c| c.try_into()) .collect::, _>>()?; - Ok(Some(Page::new(page_info, nodes))) + Ok(Page::new(page_info, nodes)) } else { - Ok(None) + Ok(Page::new_empty()) } } + /// Get a stream of [`CheckpointSummary`]. Note that this will fetch all checkpoints which may + /// trigger a lot of requests. + pub async fn checkpoints_stream( + &self, + streaming_direction: Direction, + ) -> impl Stream> + '_ { + stream_paginated_query(move |filter| self.checkpoints(filter), streaming_direction) + } + /// Return the sequence number of the latest checkpoint that has been executed. pub async fn latest_checkpoint_sequence_number( &self, @@ -814,7 +809,7 @@ impl Client { address: Address, pagination_filter: PaginationFilter, ) -> Result, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = DynamicFieldsOwnerQuery::build(DynamicFieldConnectionArgs { address, after: after.as_deref(), @@ -843,6 +838,19 @@ impl Client { )) } + /// Get a stream of dynamic fields for the provided address. Note that this will also fetch + /// dynamic fields on wrapped objects. + pub async fn dynamic_fields_stream( + &self, + address: Address, + streaming_direction: Direction, + ) -> impl Stream> + '_ { + stream_paginated_query( + move |filter| self.dynamic_fields(address, filter), + streaming_direction, + ) + } + // =========================================================================== // Epoch API // =========================================================================== @@ -894,13 +902,13 @@ impl Client { // Events API // =========================================================================== - /// Return a page of events based on the provided filters. + /// Return a page of events based on the (optional) event filter. pub async fn events( &self, filter: Option, pagination_filter: PaginationFilter, ) -> Result, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = EventsQuery::build(EventsQueryArgs { filter, @@ -937,6 +945,18 @@ impl Client { } } + /// Return a stream of events based on the (optional) event filter. + pub async fn events_stream( + &self, + filter: Option, + streaming_direction: Direction, + ) -> impl Stream> + '_ { + stream_paginated_query( + move |pag_filter| self.events(filter.clone(), pag_filter), + streaming_direction, + ) + } + // =========================================================================== // Objects API // =========================================================================== @@ -998,7 +1018,7 @@ impl Client { filter: Option>, pagination_filter: PaginationFilter, ) -> Result, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = ObjectsQuery::build(ObjectsQueryArgs { after: after.as_deref(), before: before.as_deref(), @@ -1037,6 +1057,18 @@ impl Client { } } + /// Return a stream of objects based on the (optional) object filter. + pub async fn objects_stream<'a>( + &'a self, + filter: Option>, + streaming_direction: Direction, + ) -> impl Stream> + 'a { + stream_paginated_query( + move |pag_filter| self.objects(filter.clone(), pag_filter), + streaming_direction, + ) + } + /// Return the object's bcs content [`Vec`] based on the provided [`Address`]. pub async fn object_bcs(&self, object_id: Address) -> Result>, Error> { let operation = ObjectQuery::build(ObjectQueryArgs { @@ -1224,7 +1256,7 @@ impl Client { filter: Option>, pagination_filter: PaginationFilter, ) -> Result, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { after: after.as_deref(), @@ -1252,6 +1284,18 @@ impl Client { } } + /// Get a stream of transactions based on the (optional) transaction filter. + pub async fn transactions_stream<'a>( + &'a self, + filter: Option>, + streaming_direction: Direction, + ) -> impl Stream> + 'a { + stream_paginated_query( + move |pag_filter| self.transactions(filter.clone(), pag_filter), + streaming_direction, + ) + } + /// Execute a transaction. pub async fn execute_tx( &self, @@ -1329,13 +1373,13 @@ impl Client { pagination_filter_structs: PaginationFilter, ) -> Result, Error> { let (after_enums, before_enums, first_enums, last_enums) = - self.pagination_filter(pagination_filter_enums); + self.pagination_filter(pagination_filter_enums).await; let (after_friends, before_friends, first_friends, last_friends) = - self.pagination_filter(pagination_filter_friends); + self.pagination_filter(pagination_filter_friends).await; let (after_functions, before_functions, first_functions, last_functions) = - self.pagination_filter(pagination_filter_functions); + self.pagination_filter(pagination_filter_functions).await; let (after_structs, before_structs, first_structs, last_structs) = - self.pagination_filter(pagination_filter_structs); + self.pagination_filter(pagination_filter_structs).await; let operation = NormalizedMoveModuleQuery::build(NormalizedMoveModuleQueryArgs { package: Address::from_str(package)?, module, @@ -1412,6 +1456,7 @@ mod tests { use crate::faucet::FaucetClient; use crate::BcsName; use crate::Client; + use crate::Direction; use crate::PaginationFilter; use crate::DEVNET_HOST; use crate::LOCAL_HOST; @@ -1679,6 +1724,7 @@ mod tests { #[tokio::test] async fn test_coins_stream() { + const NUM_COINS_FROM_FAUCET: usize = 5; let client = test_client(); let faucet = match client.rpc_server() { LOCAL_HOST => FaucetClient::local(), @@ -1689,13 +1735,16 @@ mod tests { let key = Ed25519PublicKey::generate(rand::thread_rng()); let address = key.to_address(); faucet.request_and_wait(address).await.unwrap(); - let mut stream = client.coins_stream(address, None); + let mut stream = client + .coins_stream(address, None, Direction::default()) + .await; let mut num_coins = 0; + while let Some(result) = stream.next().await { assert!(result.is_ok()); - num_coins = 1; + num_coins += 1; } - assert!(num_coins > 0); + assert!(num_coins == NUM_COINS_FROM_FAUCET); } #[tokio::test] diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index 90781ffe2..591a76ada 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -41,7 +41,7 @@ pub struct EventConnection { pub nodes: Vec, } -#[derive(cynic::InputObject, Debug)] +#[derive(Clone, cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "EventFilter")] pub struct EventFilter { pub emitting_module: Option, diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index a838a4fb6..7d2a2e2b7 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -160,7 +160,7 @@ pub struct MoveType { // Utility Types // =========================================================================== -#[derive(Default, cynic::QueryFragment, Debug)] +#[derive(Clone, Default, cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "PageInfo")] /// Information about pagination in a connection. pub struct PageInfo { diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index deca2bca2..f8b87677b 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -55,7 +55,7 @@ pub struct Object { pub bcs: Option, } -#[derive(cynic::InputObject, Debug)] +#[derive(Clone, cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "ObjectFilter")] pub struct ObjectFilter<'a> { #[cynic(rename = "type")] @@ -65,7 +65,7 @@ pub struct ObjectFilter<'a> { pub object_keys: Option>, } -#[derive(cynic::InputObject, Debug)] +#[derive(Clone, cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "ObjectKey")] pub struct ObjectKey { pub object_id: Address, diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 0655ce2fd..d8f5a4cb9 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -85,7 +85,7 @@ pub enum TransactionBlockKindInput { ProgrammableTx, } -#[derive(cynic::InputObject, Debug)] +#[derive(Clone, cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "TransactionBlockFilter")] pub struct TransactionsFilter<'a> { pub function: Option, diff --git a/crates/sui-graphql-client/src/streams.rs b/crates/sui-graphql-client/src/streams.rs new file mode 100644 index 000000000..5b0c96e92 --- /dev/null +++ b/crates/sui-graphql-client/src/streams.rs @@ -0,0 +1,178 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::query_types::PageInfo; +use crate::Direction; +use crate::Error; +use crate::Page; +use crate::PaginationFilter; + +use futures::Stream; +use std::future::Future; +use std::pin::Pin; +use std::task::Context; +use std::task::Poll; + +/// A stream that yields items from a paginated query with support for bidirectional pagination. +pub struct PageStream { + query_fn: F, + direction: Direction, + current_page: Option<(PageInfo, std::vec::IntoIter)>, + current_future: Option>>, + finished: bool, + is_first_page: bool, +} + +impl PageStream { + pub fn new(query_fn: F, direction: Direction) -> Self { + Self { + query_fn, + direction, + current_page: None, + current_future: None, + finished: false, + is_first_page: true, + } + } +} + +impl Stream for PageStream +where + T: Clone + Unpin, + F: Fn(PaginationFilter) -> Fut, + F: Unpin, + Fut: Future, Error>>, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.finished { + return Poll::Ready(None); + } + + loop { + let direction = self.direction.clone(); + // If we have a current page, return the next item + if let Some((page_info, iter)) = &mut self.current_page { + if let Some(item) = iter.next() { + return Poll::Ready(Some(Ok(item))); + } + + // For backward pagination, we check for previous page + // For the first page in backward pagination, we don't need to check has_previous_page + let should_continue = match direction { + Direction::Forward => page_info.has_next_page, + Direction::Backward => page_info.has_previous_page, + }; + if !should_continue { + self.finished = true; + return Poll::Ready(None); + } + } + + // Get cursor from current page + let current_cursor = self + .current_page + .as_ref() + .and_then(|(page_info, _iter)| { + match self.direction { + Direction::Forward => page_info + .has_next_page + .then(|| page_info.end_cursor.clone()), + Direction::Backward => { + // For the first page in backward pagination, we don't use a cursor + // This ensures we start from the last page + if self.is_first_page { + None + } else { + page_info + .has_previous_page + .then(|| page_info.start_cursor.clone()) + } + } + } + }) + .flatten(); + + // If there's no future yet, create one + if self.current_future.is_none() { + if self.is_first_page && current_cursor.is_some() { + self.is_first_page = false; + } + let filter = PaginationFilter { + direction: self.direction.clone(), + cursor: current_cursor, + limit: None, + }; + let future = (self.query_fn)(filter); + self.current_future = Some(Box::pin(future)); + } + + // Poll the future + match self.current_future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(Ok(page)) => { + self.current_future = None; + + if page.is_empty() { + self.finished = true; + return Poll::Ready(None); + } + + let (page_info, data) = page.into_parts(); + // For backward pagination, we need to reverse the items + let iter = match self.direction { + Direction::Forward => data.into_iter(), + Direction::Backward => { + let mut vec = data; + vec.reverse(); + vec.into_iter() + } + }; + self.current_page = Some((page_info, iter)); + + if self.is_first_page { + self.is_first_page = false; + } + } + Poll::Ready(Err(e)) => { + if self.is_first_page { + self.is_first_page = false; + } + self.finished = true; + self.current_future = None; + return Poll::Ready(Some(Err(e))); + } + Poll::Pending => return Poll::Pending, + } + } + } +} + +/// Creates a new `PageStream` for a paginated query. +/// +/// Examples +/// ```rust,ignore +/// use futures::StreamExt; +/// use sui_graphql_client::streams::stream_paginated_query; +/// use sui_graphql_client::Client; +/// use sui_graphql_client::PaginationFilter; +/// use sui_graphql_client::Direction; +/// +/// let client = Client::new_testnet(); +/// let stream = stream_paginated_query(|pagination_filter, Direction::Forward| { +/// client.coins(owner, coin_type, pagination_filter }) +/// }); +/// while let Some(result) = stream.next().await { +/// match result { +/// Ok(coin) => println!("Got coin: {:?}", coin), +/// Err(e) => eprintln!("Error: {}", e), +/// } +/// } +/// ``` +pub fn stream_paginated_query(query_fn: F, direction: Direction) -> PageStream +where + F: Fn(PaginationFilter) -> Fut, + Fut: Future, Error>>, +{ + PageStream::new(query_fn, direction) +} From bd57a3b4c31ea9f2800fcbc36ea4b3598f56027e Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:56:33 -0700 Subject: [PATCH 062/107] sui-graphql-client: add package queries (#33) --- .github/workflows/ci.yml | 8 +- crates/sui-graphql-client/Cargo.toml | 24 +- crates/sui-graphql-client/src/lib.rs | 256 ++++++++++++++++++ .../sui-graphql-client/src/query_types/mod.rs | 13 + .../src/query_types/packages.rs | 132 +++++++++ crates/sui-sdk-types/src/types/mod.rs | 48 +++- 6 files changed, 458 insertions(+), 23 deletions(-) create mode 100644 crates/sui-graphql-client/src/query_types/packages.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4686d7640..c767acd9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,7 +75,6 @@ jobs: - name: Run tests in wasm run: make wasm - run_tests_with_network: runs-on: ubuntu-latest env: @@ -103,18 +102,18 @@ jobs: run: | rustc --version cargo --version - + - uses: taiki-e/install-action@cargo-nextest - name: Get the Sui testnet binary and start a local network shell: bash env: - SUI_BINARY_VERSION: "1.35.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests + SUI_BINARY_VERSION: "1.36.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests SUI_NETWORK_RELEASE: "testnet" # which release to use run: | ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" download_url="https://github.com/mystenlabs/sui/releases/download/$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION/$ASSET_NAME" - + echo "Downloading testnet binary from $download_url" wget -q $download_url -O sui.tgz tar -zxvf sui.tgz ./sui @@ -127,4 +126,3 @@ jobs: run: | sleep $((EPOCH_DURATION_MS / 1000)) # wait for the network to get to epoch #2 make test-with-localnet - diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index d52aa145a..6d5dcd3e4 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -9,20 +9,20 @@ readme = "README.md" description = "Sui GraphQL RPC Client for the Sui Blockchain" [dependencies] -anyhow = "1.0.8" -async-stream = "0.3.5" -async-trait = "0.1.8" +anyhow = "1.0.71" +async-stream = "0.3.3" +async-trait = "0.1.61" base64ct = { version = "1.6.0", features = ["alloc"] } -bcs = "0.1.6" -chrono = "0.4.38" -cynic = "3.8.0" -futures = "0.3.30" +bcs = "0.1.4" +chrono = "0.4.26" +cynic = "3.7.3" +futures = "0.3.29" reqwest = { version = "0.12", features = ["json"] } -serde = { version = "1.0" } -serde_json = {version = "1.0"} +serde = { version = "1.0.144" } +serde_json = {version = "1.0.95"} sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } -tracing = "0.1.40" -tokio = "1.40.0" +tracing = "0.1.37" +tokio = "1.36.0" [dev-dependencies] sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "rand", "hash"] } @@ -30,5 +30,5 @@ rand = "0.8.5" tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] -cynic-codegen = { version = "3.8.0" } +cynic-codegen = { version = "3.7.3" } diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 179eb5fb9..3d0f1bf05 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -36,8 +36,10 @@ use query_types::EventsQuery; use query_types::EventsQueryArgs; use query_types::ExecuteTransactionArgs; use query_types::ExecuteTransactionQuery; +use query_types::LatestPackageQuery; use query_types::MoveFunction; use query_types::MoveModule; +use query_types::MovePackageVersionFilter; use query_types::NormalizedMoveFunctionQuery; use query_types::NormalizedMoveFunctionQueryArgs; use query_types::NormalizedMoveModuleQuery; @@ -47,6 +49,15 @@ use query_types::ObjectQuery; use query_types::ObjectQueryArgs; use query_types::ObjectsQuery; use query_types::ObjectsQueryArgs; +use query_types::PackageArgs; +use query_types::PackageByNameArgs; +use query_types::PackageByNameQuery; +use query_types::PackageCheckpointFilter; +use query_types::PackageQuery; +use query_types::PackageVersionsArgs; +use query_types::PackageVersionsQuery; +use query_types::PackagesQuery; +use query_types::PackagesQueryArgs; use query_types::PageInfo; use query_types::ProtocolConfigQuery; use query_types::ProtocolConfigs; @@ -70,6 +81,7 @@ use sui_types::types::CheckpointSequenceNumber; use sui_types::types::CheckpointSummary; use sui_types::types::Digest; use sui_types::types::Event; +use sui_types::types::MovePackage; use sui_types::types::Object; use sui_types::types::SignedTransaction; use sui_types::types::Transaction; @@ -1150,6 +1162,210 @@ impl Client { } } + // =========================================================================== + // Package API + // =========================================================================== + + /// The package corresponding to the given address (at the optionally given version). + /// When no version is given, the package is loaded directly from the address given. Otherwise, + /// the address is translated before loading to point to the package whose original ID matches + /// the package at address, but whose version is version. For non-system packages, this + /// might result in a different address than address because different versions of a package, + /// introduced by upgrades, exist at distinct addresses. + /// + /// Note that this interpretation of version is different from a historical object read (the + /// interpretation of version for the object query). + pub async fn package( + &self, + address: Address, + version: Option, + ) -> Result, Error> { + let operation = PackageQuery::build(PackageArgs { address, version }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + response + .data + .and_then(|x| x.package) + .and_then(|x| x.package_bcs) + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}"))) + } + + /// Fetch all versions of package at address (packages that share this package's original ID), + /// optionally bounding the versions exclusively from below with afterVersion, or from above + /// with beforeVersion. + pub async fn package_versions( + &self, + address: Address, + pagination_filter: PaginationFilter, + after_version: Option, + before_version: Option, + ) -> Result>, Error> { + let (after, before, first, last) = self.pagination_filter(pagination_filter); + let operation = PackageVersionsQuery::build(PackageVersionsArgs { + address, + after: after.as_deref(), + before: before.as_deref(), + first, + last, + filter: Some(MovePackageVersionFilter { + after_version, + before_version, + }), + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(packages) = response.data { + let pc = packages.package_versions; + let page_info = pc.page_info; + let bcs = pc + .nodes + .iter() + .map(|p| &p.package_bcs) + .filter_map(|b64| { + b64.as_ref() + .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) + }) + .collect::, base64ct::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))?; + let packages = bcs + .iter() + .map(|b| bcs::from_bytes::(b)) + .collect::, bcs::Error>>() + .map_err(|e| { + Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) + })?; + + Ok(Some(Page::new(page_info, packages))) + } else { + Ok(None) + } + } + + /// Fetch the latest version of the package at address. + /// This corresponds to the package with the highest version that shares its original ID with + /// the package at address. + pub async fn package_latest(&self, address: Address) -> Result, Error> { + let operation = LatestPackageQuery::build(PackageArgs { + address, + version: None, + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + let pkg = response + .data + .and_then(|x| x.latest_package) + .and_then(|x| x.package_bcs) + .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")))?; + + Ok(pkg) + } + + /// Fetch a package by its name (using Move Registry Service) + pub async fn package_by_name(&self, name: &str) -> Result, Error> { + let operation = PackageByNameQuery::build(PackageByNameArgs { name }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + println!("{:?}", errors); + return Err(Error::msg(format!("{:?}", errors))); + } + + Ok(response + .data + .and_then(|x| x.package_by_name) + .and_then(|x| x.package_bcs) + .and_then(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str()).ok()) + .and_then(|bcs| bcs::from_bytes::(&bcs).ok())) + } + + /// The Move packages that exist in the network, optionally filtered to be strictly before + /// beforeCheckpoint and/or strictly after afterCheckpoint. + /// + /// This query returns all versions of a given user package that appear between the specified + /// checkpoints, but only records the latest versions of system packages. + pub async fn packages( + &self, + after: Option<&str>, + before: Option<&str>, + first: Option, + last: Option, + after_checkpoint: Option, + before_checkpoint: Option, + ) -> Result>, Error> { + if first.is_some() && last.is_some() { + return Err(Error::msg("Cannot specify both first and last")); + } + + let operation = PackagesQuery::build(PackagesQueryArgs { + after, + before, + first, + last, + filter: Some(PackageCheckpointFilter { + after_checkpoint, + before_checkpoint, + }), + }); + + let response = self.run_query(&operation).await?; + + if let Some(errors) = response.errors { + return Err(Error::msg(format!("{:?}", errors))); + } + + if let Some(packages) = response.data { + let pc = packages.packages; + let page_info = pc.page_info; + let bcs = pc + .nodes + .iter() + .map(|p| &p.package_bcs) + .filter_map(|b64| { + b64.as_ref() + .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) + }) + .collect::, base64ct::Error>>() + .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))?; + let packages = bcs + .iter() + .map(|b| bcs::from_bytes::(b)) + .collect::, bcs::Error>>() + .map_err(|e| { + Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) + })?; + + Ok(Some(Page::new(page_info, packages))) + } else { + Ok(None) + } + } + // =========================================================================== // Dry Run API // =========================================================================== @@ -1856,4 +2072,44 @@ mod tests { assert!(total_transaction_blocks.is_ok()); assert!(total_transaction_blocks.unwrap().is_some_and(|tx| tx > 0)); } + + #[tokio::test] + async fn test_package() { + let client = test_client(); + let package = client.package("0x2".parse().unwrap(), None).await; + assert!(package.is_ok()); + } + + #[tokio::test] + #[ignore] // don't know which name is not malformed + async fn test_package_by_name() { + let client = Client::new_testnet(); + let package = client.package_by_name("sui@sui").await; + assert!(package.is_ok()); + } + + #[tokio::test] + async fn test_latest_package_query() { + let client = test_client(); + let package = client.package_latest("0x2".parse().unwrap()).await; + assert!( + package.is_ok(), + "Latest package query failed for {} network. Error: {}", + client.rpc_server(), + package.unwrap_err() + ); + } + + #[tokio::test] + #[ignore] // TIMES OUT FOR NOW + async fn test_packages_query() { + let client = test_client(); + let packages = client.packages(None, None, None, None, None, None).await; + assert!( + packages.is_ok(), + "Packages query failed for {} network. Error: {}", + client.rpc_server(), + packages.unwrap_err() + ); + } } diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 7d2a2e2b7..0bd15de65 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -13,6 +13,7 @@ mod events; mod execute_tx; mod normalized_move; mod object; +mod packages; mod protocol_config; mod service_config; mod suins; @@ -75,6 +76,18 @@ pub use object::ObjectQuery; pub use object::ObjectQueryArgs; pub use object::ObjectsQuery; pub use object::ObjectsQueryArgs; +pub use packages::LatestPackageQuery; +pub use packages::MovePackage; +pub use packages::MovePackageVersionFilter; +pub use packages::PackageArgs; +pub use packages::PackageByNameArgs; +pub use packages::PackageByNameQuery; +pub use packages::PackageCheckpointFilter; +pub use packages::PackageQuery; +pub use packages::PackageVersionsArgs; +pub use packages::PackageVersionsQuery; +pub use packages::PackagesQuery; +pub use packages::PackagesQueryArgs; pub use protocol_config::ProtocolConfigQuery; pub use protocol_config::ProtocolConfigs; pub use protocol_config::ProtocolVersionArgs; diff --git a/crates/sui-graphql-client/src/query_types/packages.rs b/crates/sui-graphql-client/src/query_types/packages.rs new file mode 100644 index 000000000..0f86100f5 --- /dev/null +++ b/crates/sui-graphql-client/src/query_types/packages.rs @@ -0,0 +1,132 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use sui_types::types::Address; + +use crate::query_types::schema; +use crate::query_types::Base64; +use crate::query_types::PageInfo; + +// =========================================================================== +// Package by address (and optional version) +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "PackageArgs")] +pub struct PackageQuery { + #[arguments(address: $address, version: $version)] + pub package: Option, +} + +// =========================================================================== +// Latest Package +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Query", variables = "PackageArgs")] +pub struct LatestPackageQuery { + #[arguments(address: $address)] + pub latest_package: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct PackageArgs { + pub address: Address, + pub version: Option, +} + +// =========================================================================== +// Package By Name +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "PackageByNameArgs" +)] +pub struct PackageByNameQuery { + #[arguments(name: "")] + pub package_by_name: Option, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct PackageByNameArgs<'a> { + pub name: &'a str, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MovePackage")] +pub struct MovePackage { + pub package_bcs: Option, +} + +// =========================================================================== +// Packages +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "PackagesQueryArgs" +)] +pub struct PackagesQuery { + #[arguments(after: $after, before: $before, filter: $filter, first: $first, last: $last)] + pub packages: MovePackageConnection, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct PackagesQueryArgs<'a> { + pub after: Option<&'a str>, + pub before: Option<&'a str>, + pub filter: Option, + pub first: Option, + pub last: Option, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "MovePackageCheckpointFilter")] +pub struct PackageCheckpointFilter { + pub after_checkpoint: Option, + pub before_checkpoint: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MovePackageConnection")] +pub struct MovePackageConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + +// =========================================================================== +// PackagesVersions +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "PackageVersionsArgs" +)] +pub struct PackageVersionsQuery { + #[arguments(address: $address, after: $after, first: $first, last: $last, before: $before, filter:$filter)] + pub package_versions: MovePackageConnection, +} + +#[derive(cynic::QueryVariables, Debug)] +pub struct PackageVersionsArgs<'a> { + pub address: Address, + pub after: Option<&'a str>, + pub first: Option, + pub last: Option, + pub before: Option<&'a str>, + pub filter: Option, +} + +#[derive(cynic::InputObject, Debug)] +#[cynic(schema = "rpc", graphql_type = "MovePackageVersionFilter")] +pub struct MovePackageVersionFilter { + pub after_version: Option, + pub before_version: Option, +} diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index a2cac0469..eb33cb5f7 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -6,13 +6,49 @@ pub mod effects; pub mod events; pub mod execution_status; pub mod framework; -pub mod gas; -pub mod object; -pub mod object_id; -pub mod transaction; -pub mod type_tag; -pub mod u256; +mod gas; +mod object; +mod object_id; +mod transaction; +mod type_tag; +mod u256; +pub use address::Address; +pub use checkpoint::{ + CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber, + CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo, + EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit, +}; +pub use crypto::{ + Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, + Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, + JwkId, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, + MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PublicKey, + Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, + UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, + ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, +}; +pub use digest::{ + CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, + EffectsAuxiliaryDataDigest, ObjectDigest, SigningDigest, TransactionDigest, + TransactionEffectsDigest, TransactionEventsDigest, +}; +pub use effects::{ + ChangedObject, EffectsObjectChange, IdOperation, ModifiedAtVersion, ObjectIn, ObjectOut, + ObjectReferenceWithOwner, TransactionEffects, TransactionEffectsV1, TransactionEffectsV2, + UnchangedSharedKind, UnchangedSharedObject, +}; +pub use events::{BalanceChange, Event, TransactionEvents}; +pub use execution_status::{ + CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError, + TypeArgumentError, +}; +pub use gas::GasCostSummary; +pub use object::{ + GenesisObject, MovePackage, Object, ObjectData, ObjectReference, ObjectType, Owner, TypeOrigin, + UpgradeInfo, Version, +}; +pub use object_id::ObjectId; #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub(crate) use transaction::SignedTransactionWithIntentMessage; From 08d0569503d5b2f4972cc79f21b835c82887344d Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:28:55 -0700 Subject: [PATCH 063/107] sui-graphql-client: properly await pagination_filter query (#52) --- crates/sui-graphql-client/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 3d0f1bf05..fc96158df 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -1210,7 +1210,7 @@ impl Client { after_version: Option, before_version: Option, ) -> Result>, Error> { - let (after, before, first, last) = self.pagination_filter(pagination_filter); + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = PackageVersionsQuery::build(PackageVersionsArgs { address, after: after.as_deref(), From aa191e9c2960d663834e1f76a3b7dbdfcbb93f94 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 1 Nov 2024 07:06:51 -0700 Subject: [PATCH 064/107] sui-graphql-client: make dynamic field queries using owner (#54) --- crates/sui-graphql-client/src/lib.rs | 2 +- .../src/query_types/dynamic_fields.rs | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index fc96158df..bbf54a065 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -764,7 +764,7 @@ impl Client { let result = response .data - .and_then(|d| d.object) + .and_then(|d| d.owner) .and_then(|o| o.dynamic_field) .map(|df| df.try_into()) .transpose() diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index 7c4d59715..4773ba3b4 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -40,16 +40,12 @@ pub struct ObjectOwner { #[cynic(schema = "rpc", graphql_type = "Query", variables = "DynamicFieldArgs")] pub struct DynamicFieldQuery { #[arguments(address: $address)] - pub object: Option, + pub owner: Option, } #[derive(cynic::QueryFragment, Debug)] -#[cynic( - schema = "rpc", - graphql_type = "Object", - variables = "DynamicFieldArgs" -)] -pub struct ObjectField { +#[cynic(schema = "rpc", graphql_type = "Owner", variables = "DynamicFieldArgs")] +pub struct OwnerField { #[arguments(name: $name)] pub dynamic_field: Option, } From c1cadd81a0c10ce8ba10a4320f84e84454edf337 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 11 Nov 2024 07:40:39 -0800 Subject: [PATCH 065/107] sui-graphql-client: cleanup of Option and add tests back (#56) --- crates/sui-graphql-client/src/lib.rs | 43 +++++++++++++++++++--------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index bbf54a065..6013a724d 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -1209,7 +1209,7 @@ impl Client { pagination_filter: PaginationFilter, after_version: Option, before_version: Option, - ) -> Result>, Error> { + ) -> Result, Error> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = PackageVersionsQuery::build(PackageVersionsArgs { address, @@ -1250,9 +1250,9 @@ impl Client { Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) })?; - Ok(Some(Page::new(page_info, packages))) + Ok(Page::new(page_info, packages)) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -1317,7 +1317,7 @@ impl Client { last: Option, after_checkpoint: Option, before_checkpoint: Option, - ) -> Result>, Error> { + ) -> Result, Error> { if first.is_some() && last.is_some() { return Err(Error::msg("Cannot specify both first and last")); } @@ -1360,9 +1360,9 @@ impl Client { Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) })?; - Ok(Some(Page::new(page_info, packages))) + Ok(Page::new(page_info, packages)) } else { - Ok(None) + Ok(Page::new_empty()) } } @@ -1869,7 +1869,6 @@ mod tests { } #[tokio::test] - #[ignore] // schema was updated, but the service has not been released with the new schema async fn test_events_query() { let client = test_client(); let events = client.events(None, PaginationFilter::default()).await; @@ -1887,7 +1886,6 @@ mod tests { } #[tokio::test] - #[ignore] async fn test_objects_query() { let client = test_client(); let objects = client.objects(None, PaginationFilter::default()).await; @@ -1900,7 +1898,6 @@ mod tests { } #[tokio::test] - #[ignore] async fn test_object_query() { let client = test_client(); let object = client.object("0x5".parse().unwrap(), None).await; @@ -1964,7 +1961,6 @@ mod tests { } #[tokio::test] - #[ignore] async fn test_transactions_query() { let client = test_client(); let transactions = client.transactions(None, PaginationFilter::default()).await; @@ -1996,7 +1992,6 @@ mod tests { // This needs the tx builder to be able to be tested properly #[tokio::test] - #[ignore] async fn test_dry_run() { let client = Client::new_testnet(); // this tx bytes works on testnet @@ -2077,7 +2072,18 @@ mod tests { async fn test_package() { let client = test_client(); let package = client.package("0x2".parse().unwrap(), None).await; - assert!(package.is_ok()); + assert!( + package.is_ok(), + "Package query failed for {} network. Error: {}", + client.rpc_server(), + package.unwrap_err() + ); + + assert!( + package.unwrap().is_some(), + "Package query returned None for {} network", + client.rpc_server() + ); } #[tokio::test] @@ -2098,10 +2104,15 @@ mod tests { client.rpc_server(), package.unwrap_err() ); + + assert!( + package.unwrap().is_some(), + "Latest package for 0x2 query returned None for {} network", + client.rpc_server() + ); } #[tokio::test] - #[ignore] // TIMES OUT FOR NOW async fn test_packages_query() { let client = test_client(); let packages = client.packages(None, None, None, None, None, None).await; @@ -2111,5 +2122,11 @@ mod tests { client.rpc_server(), packages.unwrap_err() ); + + assert!( + !packages.unwrap().is_empty(), + "Packages query returned no data for {} network", + client.rpc_server() + ); } } From b2014dee6e95d3a3d876cbefbdab5011acbfc368 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Nov 2024 10:33:17 -0600 Subject: [PATCH 066/107] ci: set toolchain to 1.81.0 for wasm builds There's a change in how rust 1.82.0 compiles for wasm targets that wasm-bindgen needs to account for. Till a new release of that project, and other dependencies, lets pin to our rust version to 1.81 for wasm tests. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c767acd9a..258dfbf6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: - name: rust version run: | + rustup default 1.81.0 rustc --version cargo --version From aa68191d34b6feee4102452d3abee9dee1c5c1a4 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:44:51 -0800 Subject: [PATCH 067/107] sui-graphql-client: add a build crate to allow registering schema in other crates (#57) --- crates/sui-graphql-client-build/Cargo.toml | 8 +++ crates/sui-graphql-client-build/README.md | 39 ++++++++++++++ .../schema.graphql} | 0 crates/sui-graphql-client-build/src/lib.rs | 51 +++++++++++++++++++ crates/sui-graphql-client/Cargo.toml | 2 +- crates/sui-graphql-client/README.md | 10 ++-- crates/sui-graphql-client/build.rs | 4 +- 7 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 crates/sui-graphql-client-build/Cargo.toml create mode 100644 crates/sui-graphql-client-build/README.md rename crates/{sui-graphql-client/schema/graphql_rpc.graphql => sui-graphql-client-build/schema.graphql} (100%) create mode 100644 crates/sui-graphql-client-build/src/lib.rs diff --git a/crates/sui-graphql-client-build/Cargo.toml b/crates/sui-graphql-client-build/Cargo.toml new file mode 100644 index 000000000..583763ea3 --- /dev/null +++ b/crates/sui-graphql-client-build/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sui-graphql-client-build" +version = "0.1.0" +edition = "2021" + +[dependencies] +cynic-codegen = "3.8.0" + diff --git a/crates/sui-graphql-client-build/README.md b/crates/sui-graphql-client-build/README.md new file mode 100644 index 000000000..357b4b4ab --- /dev/null +++ b/crates/sui-graphql-client-build/README.md @@ -0,0 +1,39 @@ +### Description +This crate provides a function to register a schema to enable building custom queries using cynic derive macros queries. Call +this function in a `build.rs` file in your crate if you need to build custom queries. + +### Example +```rust,ignore +// build.rs file +fn main() { + let schema_name = "MYSCHEMA" + sui_graphql_client_build::register_schema(schema_name); +} + +// Cargo.toml +... +[dependencies] +cynic = "3.8.0" +... +[build-dependencies] +sui_graphql_client_build = "VERSION_HERE" + +// lib.rs +// Custom query +use cynic::QueryBuilder; +use sui_graphql_client::{query_types::schema, Client}; + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "MYSCHEMA", graphql_type = "Query")] +pub struct MyQuery { + pub chain_identifier: String, +} + +#[tokio::main] +async fn main() { + let client = Client::new_mainnet(); + let operation = MyQuery::build(()); + let q = client.run_query(&operation).await.unwrap(); + println!("{:?}", q); +} +``` diff --git a/crates/sui-graphql-client/schema/graphql_rpc.graphql b/crates/sui-graphql-client-build/schema.graphql similarity index 100% rename from crates/sui-graphql-client/schema/graphql_rpc.graphql rename to crates/sui-graphql-client-build/schema.graphql diff --git a/crates/sui-graphql-client-build/src/lib.rs b/crates/sui-graphql-client-build/src/lib.rs new file mode 100644 index 000000000..272a7b01f --- /dev/null +++ b/crates/sui-graphql-client-build/src/lib.rs @@ -0,0 +1,51 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#![doc = include_str!("../README.md")] + +/// Register the schema to enable building custom queries using cynic derive macros queries. Call +/// this function in a `build.rs` file in your crate if you need to build custom queries. +/// +/// Examples +/// ```rust,ignore +/// // build.rs file +/// fn main() { +/// let schema_name = "MYSCHEMA" +/// sui_graphql_client_build::register_schema(schema_name); +/// } +/// +/// // Cargo.toml +/// ... +/// [dependencies] +/// cynic = "3.8.0" +/// ... +/// [build-dependencies] +/// sui_graphql_client_build = "VERSION_HERE" +/// +/// // lib.rs +/// // Custom query +/// use cynic::QueryBuilder; +/// use sui_graphql_client::{query_types::schema, Client}; +/// +/// #[derive(cynic::QueryFragment, Debug)] +/// #[cynic(schema = "MYSCHEMA", graphql_type = "Query")] +/// pub struct MyQuery { +/// pub chain_identifier: String, +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// let client = Client::new_mainnet(); +/// let operation = MyQuery::build(()); +/// let q = client.run_query(&operation).await.unwrap(); +/// println!("{:?}", q); +/// } +/// ``` +pub fn register_schema(schema_name: &str) { + let sdl = include_str!("../schema.graphql"); + cynic_codegen::register_schema(schema_name) + .from_sdl(sdl) + .expect("Failed to find GraphQL Schema") + .as_default() + .unwrap(); +} diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 6d5dcd3e4..0f48749e3 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -30,5 +30,5 @@ rand = "0.8.5" tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] -cynic-codegen = { version = "3.7.3" } +sui_graphql_client_build = { package = "sui-graphql-client-build", path = "../sui-graphql-client-build" } diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index 9901fb414..efe439704 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -114,6 +114,8 @@ query CustomQuery($id: UInt53) { } ``` +When using `cynic` and `sui-graphql-client`, you will need to register the schema by calling `sui-graphql-client-build::register_schema` in a `build.rs` file. See [sui-graphql-client-build](https://github.com/MystenLabs/sui-rust-sdk/tree/master/crates/sui-graphql-client-build) for more information. + The generated query types are defined below. Note that the `id` variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the `id` variable is not provided, the query will return the data for the last known epoch. Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. @@ -125,7 +127,7 @@ pub struct CustomQueryVariables { } #[derive(cynic::QueryFragment, Debug)] -#[cynic(graphql_type = "Query", variables = "CustomQueryVariables")] +#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query", variables = "CustomQueryVariables")] pub struct CustomQuery { #[arguments(id: $id)] pub epoch: Option, @@ -157,7 +159,7 @@ use sui_types::types::Address; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Epoch")] +#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Epoch")] pub struct EpochData { pub epoch_id: u64, pub reference_gas_price: Option, @@ -176,7 +178,7 @@ pub struct CustomVariables { // The custom query. Note that the variables need to be explicitly declared. #[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Query", variables = "CustomVariables")] +#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query", variables = "CustomVariables")] pub struct CustomQuery { #[arguments(id: $id)] pub epoch: Option, @@ -184,7 +186,7 @@ pub struct CustomQuery { // Custom query with no variables. #[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Query")] +#[cynic(schema = "SCHEMA_NAME_HERE", graphql_type = "Query")] pub struct ChainIdQuery { chain_identifier: String, } diff --git a/crates/sui-graphql-client/build.rs b/crates/sui-graphql-client/build.rs index 293b6dd38..1ad185a9b 100644 --- a/crates/sui-graphql-client/build.rs +++ b/crates/sui-graphql-client/build.rs @@ -1,6 +1,4 @@ /// Register Sui RPC schema for creating structs for queries fn main() { - cynic_codegen::register_schema("rpc") - .from_sdl_file("schema/graphql_rpc.graphql") - .expect("Failed to find GraphQL Schema"); + sui_graphql_client_build::register_schema("rpc"); } From f54460d3df0fd9ba8079cbef154a598b6b152e32 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:54:42 -0800 Subject: [PATCH 068/107] sui-graphql-client: initialize service config and max page size lazily (#53) --- crates/sui-graphql-client/src/lib.rs | 56 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 6013a724d..bba974252 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -206,14 +206,15 @@ pub enum Direction { } /// Pagination options for querying the GraphQL server. It defaults to forward pagination with the -/// GraphQL server's default items per page limit. +/// GraphQL server's max page size. #[derive(Clone, Debug, Default)] pub struct PaginationFilter { /// The direction of pagination. pub direction: Direction, /// An opaque cursor used for pagination. pub cursor: Option, - /// The maximum number of items to return. Use `service_config` to find the limit. + /// The maximum number of items to return. If this is ommitted, it will lazily query the + /// service configuration for the max page size. pub limit: Option, } @@ -274,6 +275,8 @@ pub struct Client { rpc: Url, /// The reqwest client. inner: reqwest::Client, + + service_config: std::sync::OnceLock, } impl Client { @@ -288,6 +291,7 @@ impl Client { let client = Client { rpc, inner: reqwest::Client::builder().user_agent(USER_AGENT).build()?, + service_config: Default::default(), }; Ok(client) } @@ -327,20 +331,15 @@ impl Client { } /// Internal function to handle pagination filters and return the appropriate values. + /// If limit is omitted, it will use the max page size from the service config. async fn pagination_filter( &self, pagination_filter: PaginationFilter, ) -> (Option, Option, Option, Option) { - let limit = if let Some(limit) = pagination_filter.limit { - limit - } else { - let cfg = self.service_config().await; - if let Ok(cfg) = cfg { - cfg.max_page_size - } else { - DEFAULT_ITEMS_PER_PAGE - } - }; + let limit = pagination_filter + .limit + .unwrap_or(self.max_page_size().await.unwrap_or(DEFAULT_ITEMS_PER_PAGE)); + let (after, before, first, last) = match pagination_filter.direction { Direction::Forward => (pagination_filter.cursor, None, Some(limit), None), Direction::Backward => (None, pagination_filter.cursor, None, Some(limit)), @@ -348,6 +347,11 @@ impl Client { (after, before, first, last) } + /// Lazily fetch the max page size + pub async fn max_page_size(&self) -> Result { + self.service_config().await.map(|cfg| cfg.max_page_size) + } + /// Run a query on the GraphQL server and return the response. /// This method returns [`cynic::GraphQlResponse`] over the query type `T`, and it is /// intended to be used with custom queries. @@ -418,14 +422,26 @@ impl Client { /// Get the GraphQL service configuration, including complexity limits, read and mutation limits, /// supported versions, and others. - pub async fn service_config(&self) -> Result { - let operation = ServiceConfigQuery::build(()); - let response = self.run_query(&operation).await?; + pub async fn service_config(&self) -> Result<&ServiceConfig, Error> { + // If the value is already initialized, return it + if let Some(service_config) = self.service_config.get() { + return Ok(service_config); + } - response - .data - .map(|s| s.service_config) - .ok_or_else(|| Error::msg("No data in response")) + // Otherwise, fetch and initialize it + let service_config = { + let operation = ServiceConfigQuery::build(()); + let response = self.run_query(&operation).await?; + + response + .data + .map(|s| s.service_config) + .ok_or_else(|| Error::msg("No data in response"))? + }; + + let service_config = self.service_config.get_or_init(move || service_config); + + Ok(service_config) } /// Get the list of active validators for the provided epoch, including related metadata. @@ -707,7 +723,7 @@ impl Client { stream_paginated_query(move |filter| self.checkpoints(filter), streaming_direction) } - /// Return the sequence number of the latest checkpoint that has been executed. + /// Return the sequence number of the latest checkpoint that has been executed. pub async fn latest_checkpoint_sequence_number( &self, ) -> Result, Error> { From 43693e5222f6b22eee4fa1f3fe956f202649f220 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:26:41 -0800 Subject: [PATCH 069/107] sui-graphql-client: add transaction effects query (#55) --- crates/sui-graphql-client/src/lib.rs | 100 +++++++++++++++++- .../src/query_types/dry_run.rs | 5 +- .../sui-graphql-client/src/query_types/mod.rs | 2 + .../src/query_types/transaction.rs | 52 ++++++++- 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index bba974252..bc2dc64ed 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -67,7 +67,9 @@ use query_types::ResolveSuinsQueryArgs; use query_types::ServiceConfig; use query_types::ServiceConfigQuery; use query_types::TransactionBlockArgs; +use query_types::TransactionBlockEffectsQuery; use query_types::TransactionBlockQuery; +use query_types::TransactionBlocksEffectsQuery; use query_types::TransactionBlocksQuery; use query_types::TransactionBlocksQueryArgs; use query_types::TransactionMetadata; @@ -85,6 +87,7 @@ use sui_types::types::MovePackage; use sui_types::types::Object; use sui_types::types::SignedTransaction; use sui_types::types::Transaction; +use sui_types::types::TransactionDigest; use sui_types::types::TransactionEffects; use sui_types::types::TransactionKind; use sui_types::types::TypeTag; @@ -1482,6 +1485,24 @@ impl Client { .map_err(|e| Error::msg(format!("Cannot decode transaction: {e}"))) } + /// Get a transaction's effects by its digest. + pub async fn transaction_effects( + &self, + digest: TransactionDigest, + ) -> Result, Error> { + let operation = TransactionBlockEffectsQuery::build(TransactionBlockArgs { + digest: digest.to_string(), + }); + let response = self.run_query(&operation).await?; + + response + .data + .and_then(|d| d.transaction_block) + .map(|tx| tx.try_into()) + .transpose() + .map_err(|e| Error::msg(format!("Cannot decode transaction: {e}"))) + } + /// Get a page of transactions based on the provided filters. pub async fn transactions<'a>( &self, @@ -1516,6 +1537,40 @@ impl Client { } } + /// Get a page of transactions' effects based on the provided filters. + pub async fn transactions_effects<'a>( + &self, + filter: Option>, + pagination_filter: PaginationFilter, + ) -> Result, Error> { + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; + + let operation = TransactionBlocksEffectsQuery::build(TransactionBlocksQueryArgs { + after: after.as_deref(), + before: before.as_deref(), + filter, + first, + last, + }); + + let response = self.run_query(&operation).await?; + + if let Some(txb) = response.data { + let txc = txb.transaction_blocks; + let page_info = txc.page_info; + + let transactions = txc + .nodes + .into_iter() + .map(|n| n.try_into()) + .collect::>>()?; + let page = Page::new(page_info, transactions); + Ok(page) + } else { + Ok(Page::new_empty()) + } + } + /// Get a stream of transactions based on the (optional) transaction filter. pub async fn transactions_stream<'a>( &'a self, @@ -1528,6 +1583,18 @@ impl Client { ) } + /// Get a stream of transactions' effects based on the (optional) transaction filter. + pub async fn transactions_effects_stream<'a>( + &'a self, + filter: Option>, + streaming_direction: Direction, + ) -> impl Stream> + 'a { + stream_paginated_query( + move |pag_filter| self.transactions_effects(filter.clone(), pag_filter), + streaming_direction, + ) + } + /// Execute a transaction. pub async fn execute_tx( &self, @@ -1695,6 +1762,8 @@ mod tests { use crate::MAINNET_HOST; use crate::TESTNET_HOST; + const NUM_COINS_FROM_FAUCET: usize = 5; + fn test_client() -> Client { let network = std::env::var("NETWORK").unwrap_or_else(|_| "local".to_string()); match network.as_str() { @@ -1953,7 +2022,6 @@ mod tests { #[tokio::test] async fn test_coins_stream() { - const NUM_COINS_FROM_FAUCET: usize = 5; let client = test_client(); let faucet = match client.rpc_server() { LOCAL_HOST => FaucetClient::local(), @@ -1976,6 +2044,36 @@ mod tests { assert!(num_coins == NUM_COINS_FROM_FAUCET); } + #[tokio::test] + async fn test_transaction_effects_query() { + let client = test_client(); + let transactions = client + .transactions(None, PaginationFilter::default()) + .await + .unwrap(); + let tx_digest = transactions.data()[0].transaction.digest(); + let effects = client.transaction_effects(tx_digest).await.unwrap(); + assert!( + effects.is_some(), + "Transaction effects query failed for {} network.", + client.rpc_server(), + ); + } + + #[tokio::test] + async fn test_transactions_effects_query() { + let client = test_client(); + let txs_effects = client + .transactions_effects(None, PaginationFilter::default()) + .await; + assert!( + txs_effects.is_ok(), + "Transactions effects query failed for {} network. Error: {}", + client.rpc_server(), + txs_effects.unwrap_err() + ); + } + #[tokio::test] async fn test_transactions_query() { let client = test_client(); diff --git a/crates/sui-graphql-client/src/query_types/dry_run.rs b/crates/sui-graphql-client/src/query_types/dry_run.rs index 4e5641e1f..9d01d44f6 100644 --- a/crates/sui-graphql-client/src/query_types/dry_run.rs +++ b/crates/sui-graphql-client/src/query_types/dry_run.rs @@ -5,7 +5,8 @@ use sui_types::types::ObjectReference; use crate::query_types::schema; use crate::query_types::Address; -use crate::query_types::TransactionBlock; + +use super::transaction::TxBlockEffects; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "DryRunArgs")] @@ -18,7 +19,7 @@ pub struct DryRunQuery { #[cynic(schema = "rpc", graphql_type = "DryRunResult")] pub struct DryRunResult { pub error: Option, - pub transaction: Option, + pub transaction: Option, } #[derive(cynic::QueryVariables, Debug)] diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 0bd15de65..2bf88140f 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -100,7 +100,9 @@ pub use suins::ResolveSuinsQuery; pub use suins::ResolveSuinsQueryArgs; pub use transaction::TransactionBlock; pub use transaction::TransactionBlockArgs; +pub use transaction::TransactionBlockEffectsQuery; pub use transaction::TransactionBlockQuery; +pub use transaction::TransactionBlocksEffectsQuery; pub use transaction::TransactionBlocksQuery; pub use transaction::TransactionBlocksQueryArgs; pub use transaction::TransactionsFilter; diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index d8f5a4cb9..e95b4c6e5 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -5,6 +5,7 @@ use anyhow::Error; use base64ct::Encoding; use sui_types::types::SignedTransaction; use sui_types::types::Transaction; +use sui_types::types::TransactionEffects; use sui_types::types::UserSignature; use crate::query_types::schema; @@ -27,6 +28,17 @@ pub struct TransactionBlockQuery { pub transaction_block: Option, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "TransactionBlockArgs" +)] +pub struct TransactionBlockEffectsQuery { + #[arguments(digest: $digest)] + pub transaction_block: Option, +} + #[derive(cynic::QueryFragment, Debug)] #[cynic( schema = "rpc", @@ -38,6 +50,16 @@ pub struct TransactionBlocksQuery { pub transaction_blocks: TransactionBlockConnection, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic( + schema = "rpc", + graphql_type = "Query", + variables = "TransactionBlocksQueryArgs" +)] +pub struct TransactionBlocksEffectsQuery { + #[arguments(first: $first, after: $after, last: $last, before: $before, filter: $filter)] + pub transaction_blocks: TransactionBlockEffectsConnection, +} // =========================================================================== // Transaction Block(s) Query Args // =========================================================================== @@ -64,10 +86,15 @@ pub struct TransactionBlocksQueryArgs<'a> { #[cynic(schema = "rpc", graphql_type = "TransactionBlock")] pub struct TransactionBlock { pub bcs: Option, - pub effects: Option, pub signatures: Option>, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlock")] +pub struct TxBlockEffects { + pub effects: Option, +} + #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "TransactionBlockEffects")] pub struct TransactionBlockEffects { @@ -107,6 +134,13 @@ pub struct TransactionBlockConnection { pub page_info: PageInfo, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlockConnection")] +pub struct TransactionBlockEffectsConnection { + pub nodes: Vec, + pub page_info: PageInfo, +} + impl TryFrom for SignedTransaction { type Error = anyhow::Error; @@ -139,3 +173,19 @@ impl TryFrom for SignedTransaction { } } } + +impl TryFrom for TransactionEffects { + type Error = anyhow::Error; + + fn try_from(value: TxBlockEffects) -> Result { + let effects = value + .effects + .map(|fx| base64ct::Base64::decode_vec(fx.bcs.unwrap().0.as_str())) + .transpose() + .map_err(|_| Error::msg("Cannot decode Base64 effects bcs bytes"))? + .map(|bcs| bcs::from_bytes::(&bcs)) + .transpose() + .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; + effects.ok_or_else(|| Error::msg("Cannot decode effects")) + } +} From 7c95d7eadf7da85e5960599c5fb05fe91f7caa9a Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:56:59 -0800 Subject: [PATCH 070/107] sui-graphql-client: rework errors (#58) Implement a custom error type instead of relying on `anyhow` for the graphql client. --- crates/sui-graphql-client/Cargo.toml | 3 +- crates/sui-graphql-client/src/error.rs | 179 +++++++++ crates/sui-graphql-client/src/lib.rs | 370 ++++++++---------- .../src/query_types/checkpoint.rs | 39 +- .../src/query_types/dynamic_fields.rs | 6 +- .../sui-graphql-client/src/query_types/mod.rs | 10 +- .../src/query_types/transaction.rs | 35 +- crates/sui-graphql-client/src/streams.rs | 8 +- crates/sui-sdk-types/src/types/mod.rs | 2 +- 9 files changed, 402 insertions(+), 250 deletions(-) create mode 100644 crates/sui-graphql-client/src/error.rs diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 0f48749e3..1104c5254 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -12,7 +12,7 @@ description = "Sui GraphQL RPC Client for the Sui Blockchain" anyhow = "1.0.71" async-stream = "0.3.3" async-trait = "0.1.61" -base64ct = { version = "1.6.0", features = ["alloc"] } +base64ct = { version = "1.6.0", features = ["alloc", "std"] } bcs = "0.1.4" chrono = "0.4.26" cynic = "3.7.3" @@ -23,6 +23,7 @@ serde_json = {version = "1.0.95"} sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } tracing = "0.1.37" tokio = "1.36.0" +url = "2.5.3" [dev-dependencies] sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "rand", "hash"] } diff --git a/crates/sui-graphql-client/src/error.rs b/crates/sui-graphql-client/src/error.rs new file mode 100644 index 000000000..c79873b2a --- /dev/null +++ b/crates/sui-graphql-client/src/error.rs @@ -0,0 +1,179 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::num::ParseIntError; +use std::num::TryFromIntError; + +use cynic::GraphQlError; + +use sui_types::types::AddressParseError; +use sui_types::types::DigestParseError; +use sui_types::types::TypeParseError; + +type BoxError = Box; + +pub type Result = std::result::Result; + +/// General error type for the client. It is used to wrap all the possible errors that can occur. +#[derive(Debug)] +pub struct Error { + inner: Box, +} + +/// Error type for the client. It is split into multiple fields to allow for more granular error +/// handling. The `source` field is used to store the original error. +#[derive(Debug)] +struct InnerError { + /// Error kind. + kind: Kind, + /// Errors returned by the GraphQL server. + query_errors: Option>, + /// The original error. + source: Option, +} + +#[derive(Debug)] +#[non_exhaustive] +pub enum Kind { + Deserialization, + Parse, + Query, + Other, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.source.as_deref().map(|e| e as _) + } +} + +impl Error { + // Public accessors + + /// Returns the kind of error. + pub fn kind(&self) -> &Kind { + &self.inner.kind + } + + /// Original GraphQL query errors. + pub fn graphql_errors(&self) -> Option<&[GraphQlError]> { + self.inner.query_errors.as_deref() + } + + // Private constructors + + /// Convert the given error into a generic error. + pub(crate) fn from_error>(kind: Kind, error: E) -> Self { + Self { + inner: Box::new(InnerError { + kind, + source: Some(error.into()), + query_errors: None, + }), + } + } + + /// Special constructor for queries that expect to return data but it's none. + pub(crate) fn empty_response_error() -> Self { + Self { + inner: Box::new(InnerError { + kind: Kind::Query, + source: Some("Expected a non-empty response data from query".into()), + query_errors: None, + }), + } + } + + /// Create a Query kind of error with the original graphql errors. + pub(crate) fn graphql_error(errors: Vec) -> Self { + Self { + inner: Box::new(InnerError { + kind: Kind::Query, + source: None, + query_errors: Some(errors), + }), + } + } +} + +impl std::fmt::Display for Kind { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Kind::Deserialization => write!(f, "Deserialization error:"), + Kind::Parse => write!(f, "Parse error:"), + Kind::Query => write!(f, "Query error:"), + Kind::Other => write!(f, "Error:"), + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.inner.kind)?; + + if let Some(source) = &self.inner.source { + writeln!(f, " {}", source)?; + } + Ok(()) + } +} + +impl From for Error { + fn from(error: bcs::Error) -> Self { + Self::from_error(Kind::Deserialization, error) + } +} + +impl From for Error { + fn from(error: reqwest::Error) -> Self { + Self::from_error(Kind::Other, error) + } +} + +impl From for Error { + fn from(error: url::ParseError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: ParseIntError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: AddressParseError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: base64ct::Error) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: chrono::ParseError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: DigestParseError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: TryFromIntError) -> Self { + Self::from_error(Kind::Parse, error) + } +} + +impl From for Error { + fn from(error: TypeParseError) -> Self { + Self::from_error(Kind::Parse, error) + } +} diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index bc2dc64ed..a3196af6d 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -3,10 +3,12 @@ #![doc = include_str!("../README.md")] +pub mod error; pub mod faucet; pub mod query_types; pub mod streams; +use error::Error; use query_types::ActiveValidatorsArgs; use query_types::ActiveValidatorsQuery; use query_types::BalanceArgs; @@ -93,10 +95,6 @@ use sui_types::types::TransactionKind; use sui_types::types::TypeTag; use sui_types::types::UserSignature; -use anyhow::anyhow; -use anyhow::ensure; -use anyhow::Error; -use anyhow::Result; use base64ct::Encoding; use cynic::serde; use cynic::GraphQlResponse; @@ -109,10 +107,11 @@ use serde::de::DeserializeOwned; use serde::Serialize; use std::str::FromStr; +use crate::error::Kind; +use crate::error::Result; use crate::query_types::CheckpointTotalTxQuery; const DEFAULT_ITEMS_PER_PAGE: i32 = 10; - const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; @@ -235,10 +234,7 @@ impl From for NameValue { impl DynamicFieldOutput { /// Deserialize the name of the dynamic field into the specified type. - pub fn deserialize_name( - &self, - expected_type: &TypeTag, - ) -> Result { + pub fn deserialize_name(&self, expected_type: &TypeTag) -> Result { assert_eq!( expected_type, &self.name.type_, "Expected type {}, but got {}", @@ -246,14 +242,11 @@ impl DynamicFieldOutput { ); let bcs = &self.name.bcs; - bcs::from_bytes::(bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) + bcs::from_bytes::(bcs).map_err(Into::into) } /// Deserialize the value of the dynamic field into the specified type. - pub fn deserialize_value( - &self, - expected_type: &TypeTag, - ) -> Result { + pub fn deserialize_value(&self, expected_type: &TypeTag) -> Result { let typetag = self.value.as_ref().map(|(typename, _)| typename); assert_eq!( Some(&expected_type), @@ -264,9 +257,9 @@ impl DynamicFieldOutput { ); if let Some((_, bcs)) = &self.value { - bcs::from_bytes::(bcs).map_err(|_| anyhow!("Cannot decode BCS bytes")) + bcs::from_bytes::(bcs).map_err(Into::into) } else { - Err(anyhow!("No value found")) + Err(Error::from_error(Kind::Deserialization, "Value is missing")) } } } @@ -288,8 +281,8 @@ impl Client { // =========================================================================== /// Create a new GraphQL client with the provided server address. - pub fn new(server: &str) -> Result { - let rpc = reqwest::Url::parse(server).map_err(|_| anyhow!("Invalid URL: {}", server))?; + pub fn new(server: &str) -> Result { + let rpc = reqwest::Url::parse(server)?; let client = Client { rpc, @@ -322,7 +315,7 @@ impl Client { /// Set the server address for the GraphQL GraphQL client. It should be a valid URL with a host and /// optionally a port number. - pub fn set_rpc_server(&mut self, server: &str) -> Result<(), Error> { + pub fn set_rpc_server(&mut self, server: &str) -> Result<()> { let rpc = reqwest::Url::parse(server)?; self.rpc = rpc; Ok(()) @@ -379,18 +372,18 @@ impl Client { // =========================================================================== /// Get the chain identifier. - pub async fn chain_id(&self) -> Result { + pub async fn chain_id(&self) -> Result { let operation = ChainIdentifierQuery::build(()); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } response .data .map(|e| e.chain_identifier) - .ok_or_else(|| Error::msg("No data in response")) + .ok_or_else(Error::empty_response_error) } /// Get the reference gas price for the provided epoch or the last known one if no epoch is @@ -398,26 +391,24 @@ impl Client { /// /// This will return `Ok(None)` if the epoch requested is not available in the GraphQL service /// (e.g., due to pruning). - pub async fn reference_gas_price(&self, epoch: Option) -> Result, Error> { + pub async fn reference_gas_price(&self, epoch: Option) -> Result> { let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); let response = self.run_query(&operation).await?; - if let Some(data) = response.data { - data.epoch - .and_then(|e| e.reference_gas_price.map(|x| x.try_into())) - .transpose() - } else if let Some(errors) = response.errors { - Err(Error::msg(format!("{:?}", errors))) - } else { - Err(Error::msg("No data in response")) + if let Some(errors) = response.errors { + return Err(Error::graphql_error(errors)); } + + response + .data + .and_then(|e| e.epoch) + .and_then(|e| e.reference_gas_price) + .map(|x| x.try_into()) + .transpose() } /// Get the protocol configuration. - pub async fn protocol_config( - &self, - version: Option, - ) -> Result, Error> { + pub async fn protocol_config(&self, version: Option) -> Result> { let operation = ProtocolConfigQuery::build(ProtocolVersionArgs { id: version }); let response = self.run_query(&operation).await?; Ok(response.data.map(|p| p.protocol_config)) @@ -425,7 +416,7 @@ impl Client { /// Get the GraphQL service configuration, including complexity limits, read and mutation limits, /// supported versions, and others. - pub async fn service_config(&self) -> Result<&ServiceConfig, Error> { + pub async fn service_config(&self) -> Result<&ServiceConfig> { // If the value is already initialized, return it if let Some(service_config) = self.service_config.get() { return Ok(service_config); @@ -436,10 +427,14 @@ impl Client { let operation = ServiceConfigQuery::build(()); let response = self.run_query(&operation).await?; + if let Some(errors) = response.errors { + return Err(Error::graphql_error(errors)); + } + response .data .map(|s| s.service_config) - .ok_or_else(|| Error::msg("No data in response"))? + .ok_or_else(Error::empty_response_error)? }; let service_config = self.service_config.get_or_init(move || service_config); @@ -453,7 +448,7 @@ impl Client { &self, epoch: Option, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = ActiveValidatorsQuery::build(ActiveValidatorsArgs { @@ -466,7 +461,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(validators) = response @@ -488,27 +483,21 @@ impl Client { /// The total number of transaction blocks in the network by the end of the provided /// checkpoint digest. - pub async fn total_transaction_blocks_by_digest( - &self, - digest: Digest, - ) -> Result, Error> { + pub async fn total_transaction_blocks_by_digest(&self, digest: Digest) -> Result> { self.internal_total_transaction_blocks(Some(digest.to_string()), None) .await } /// The total number of transaction blocks in the network by the end of the provided checkpoint /// sequence number. - pub async fn total_transaction_blocks_by_seq_num( - &self, - seq_num: u64, - ) -> Result, Error> { + pub async fn total_transaction_blocks_by_seq_num(&self, seq_num: u64) -> Result> { self.internal_total_transaction_blocks(None, Some(seq_num)) .await } /// The total number of transaction blocks in the network by the end of the last known /// checkpoint. - pub async fn total_transaction_blocks(&self) -> Result, Error> { + pub async fn total_transaction_blocks(&self) -> Result> { self.internal_total_transaction_blocks(None, None).await } @@ -518,11 +507,13 @@ impl Client { &self, digest: Option, seq_num: Option, - ) -> Result, Error> { - ensure!( - !(digest.is_some() && seq_num.is_some()), - "Cannot provide both digest and seq_num." - ); + ) -> Result> { + if digest.is_some() && seq_num.is_some() { + return Err(Error::from_error( + Kind::Other, + "Conflicting arguments: either digest or seq_num can be provided, but not both.", + )); + } let operation = CheckpointTotalTxQuery::build(CheckpointArgs { id: CheckpointId { @@ -533,7 +524,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response @@ -548,11 +539,7 @@ impl Client { /// Get the balance of all the coins owned by address for the provided coin type. /// Coin type will default to `0x2::coin::Coin<0x2::sui::SUI>` if not provided. - pub async fn balance( - &self, - address: Address, - coin_type: Option<&str>, - ) -> Result, Error> { + pub async fn balance(&self, address: Address, coin_type: Option<&str>) -> Result> { let operation = BalanceQuery::build(BalanceArgs { address, coin_type: coin_type.map(|x| x.to_string()), @@ -560,17 +547,16 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } let total_balance = response .data .map(|b| b.owner.and_then(|o| o.balance.map(|b| b.total_balance))) - .ok_or_else(|| Error::msg("No data in response"))? + .ok_or_else(Error::empty_response_error)? .flatten() .map(|x| x.0.parse::()) - .transpose() - .map_err(|e| Error::msg(format!("Cannot parse balance into u128: {e}")))?; + .transpose()?; Ok(total_balance) } @@ -587,7 +573,7 @@ impl Client { owner: Address, coin_type: Option<&str>, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let response = self .objects( Some(ObjectFilter { @@ -620,7 +606,7 @@ impl Client { address: Address, coin_type: Option<&'static str>, streaming_direction: Direction, - ) -> impl Stream> { + ) -> impl Stream> { stream_paginated_query( move |filter| self.coins(address, coin_type, filter), streaming_direction, @@ -628,19 +614,19 @@ impl Client { } /// Get the coin metadata for the coin type. - pub async fn coin_metadata(&self, coin_type: &str) -> Result, Error> { + pub async fn coin_metadata(&self, coin_type: &str) -> Result> { let operation = CoinMetadataQuery::build(CoinMetadataArgs { coin_type }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response.data.and_then(|x| x.coin_metadata)) } /// Get total supply for the coin type. - pub async fn total_supply(&self, coin_type: &str) -> Result, Error> { + pub async fn total_supply(&self, coin_type: &str) -> Result> { let coin_metadata = self.coin_metadata(coin_type).await?; coin_metadata @@ -659,11 +645,13 @@ impl Client { &self, digest: Option, seq_num: Option, - ) -> Result, Error> { - ensure!( - !(digest.is_some() && seq_num.is_some()), - "Either digest or seq_num must be provided" - ); + ) -> Result> { + if digest.is_some() && seq_num.is_some() { + return Err(Error::from_error( + Kind::Other, + "either digest or seq_num must be provided", + )); + } let operation = CheckpointQuery::build(CheckpointArgs { id: CheckpointId { @@ -674,20 +662,20 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } response .data .map(|c| c.checkpoint.map(|c| c.try_into()).transpose()) - .ok_or_else(|| Error::msg("No data in response"))? + .ok_or(Error::empty_response_error())? } /// Get a page of [`CheckpointSummary`] for the provided parameters. pub async fn checkpoints<'a>( &self, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = CheckpointsQuery::build(CheckpointsArgs { @@ -699,7 +687,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(checkpoints) = response.data { @@ -722,14 +710,14 @@ impl Client { pub async fn checkpoints_stream( &self, streaming_direction: Direction, - ) -> impl Stream> + '_ { + ) -> impl Stream> + '_ { stream_paginated_query(move |filter| self.checkpoints(filter), streaming_direction) } /// Return the sequence number of the latest checkpoint that has been executed. pub async fn latest_checkpoint_sequence_number( &self, - ) -> Result, Error> { + ) -> Result> { Ok(self .checkpoint(None, None) .await? @@ -765,7 +753,7 @@ impl Client { address: Address, type_: TypeTag, name: impl Into, - ) -> Result, Error> { + ) -> Result> { let bcs = name.into().0; let operation = DynamicFieldQuery::build(DynamicFieldArgs { address, @@ -778,7 +766,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } let result = response @@ -786,8 +774,7 @@ impl Client { .and_then(|d| d.owner) .and_then(|o| o.dynamic_field) .map(|df| df.try_into()) - .transpose() - .map_err(|e| Error::msg(format!("{:?}", e)))?; + .transpose()?; Ok(result) } @@ -805,7 +792,7 @@ impl Client { address: Address, type_: TypeTag, name: impl Into, - ) -> Result, Error> { + ) -> Result> { let bcs = name.into().0; let operation = DynamicObjectFieldQuery::build(DynamicFieldArgs { address, @@ -818,7 +805,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } let result: Option = response @@ -826,8 +813,7 @@ impl Client { .and_then(|d| d.object) .and_then(|o| o.dynamic_object_field) .map(|df| df.try_into()) - .transpose() - .map_err(|e| Error::msg(format!("{:?}", e)))?; + .transpose()?; Ok(result) } @@ -839,7 +825,7 @@ impl Client { &self, address: Address, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = DynamicFieldsOwnerQuery::build(DynamicFieldConnectionArgs { address, @@ -851,7 +837,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } let Some(DynamicFieldsOwnerQuery { owner: Some(dfs) }) = response.data else { @@ -864,8 +850,7 @@ impl Client { .nodes .into_iter() .map(TryInto::try_into) - .collect::, Error>>() - .map_err(|e| Error::msg(format!("{:?}", e)))?, + .collect::>>()?, )) } @@ -875,7 +860,7 @@ impl Client { &self, address: Address, streaming_direction: Direction, - ) -> impl Stream> + '_ { + ) -> impl Stream> + '_ { stream_paginated_query( move |filter| self.dynamic_fields(address, filter), streaming_direction, @@ -888,11 +873,11 @@ impl Client { /// Return the number of checkpoints in this epoch. This will return `Ok(None)` if the epoch /// requested is not available in the GraphQL service (e.g., due to pruning). - pub async fn epoch_total_checkpoints(&self, epoch: Option) -> Result, Error> { + pub async fn epoch_total_checkpoints(&self, epoch: Option) -> Result> { let response = self.epoch_summary(epoch).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response @@ -903,14 +888,11 @@ impl Client { /// Return the number of transaction blocks in this epoch. This will return `Ok(None)` if the /// epoch requested is not available in the GraphQL service (e.g., due to pruning). - pub async fn epoch_total_transaction_blocks( - &self, - epoch: Option, - ) -> Result, Error> { + pub async fn epoch_total_transaction_blocks(&self, epoch: Option) -> Result> { let response = self.epoch_summary(epoch).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response @@ -924,7 +906,7 @@ impl Client { async fn epoch_summary( &self, epoch: Option, - ) -> Result, Error> { + ) -> Result> { let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); self.run_query(&operation).await } @@ -938,7 +920,7 @@ impl Client { &self, filter: Option, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = EventsQuery::build(EventsQueryArgs { @@ -952,7 +934,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(events) = response.data { @@ -963,12 +945,10 @@ impl Client { .into_iter() .map(|e| e.bcs.0) .map(|b| base64ct::Base64::decode_vec(&b)) - .collect::, base64ct::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode Base64 event bcs bytes: {e}")))? + .collect::, base64ct::Error>>()? .iter() .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Event: {e}")))?; + .collect::, bcs::Error>>()?; Ok(Page::new(page_info, nodes)) } else { @@ -981,7 +961,7 @@ impl Client { &self, filter: Option, streaming_direction: Direction, - ) -> impl Stream> + '_ { + ) -> impl Stream> + '_ { stream_paginated_query( move |pag_filter| self.events(filter.clone(), pag_filter), streaming_direction, @@ -996,17 +976,13 @@ impl Client { /// /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. /// Similarly, if this is not an object but an address, it will return `Ok(None)`. - pub async fn object( - &self, - address: Address, - version: Option, - ) -> Result, Error> { + pub async fn object(&self, address: Address, version: Option) -> Result> { let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(object) = response.data { @@ -1014,12 +990,11 @@ impl Client { let bcs = obj .and_then(|o| o.bcs) .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}",)))?; + .transpose()?; + let object = bcs .map(|b| bcs::from_bytes::(&b)) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}",)))?; + .transpose()?; Ok(object) } else { @@ -1048,7 +1023,7 @@ impl Client { &self, filter: Option>, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = ObjectsQuery::build(ObjectsQueryArgs { after: after.as_deref(), @@ -1060,7 +1035,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(objects) = response.data { @@ -1074,13 +1049,11 @@ impl Client { b64.as_ref() .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) }) - .collect::, base64ct::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}")))?; + .collect::, base64ct::Error>>()?; let objects = bcs .iter() .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into Object: {e}")))?; + .collect::, bcs::Error>>()?; Ok(Page::new(page_info, objects)) } else { @@ -1093,7 +1066,7 @@ impl Client { &'a self, filter: Option>, streaming_direction: Direction, - ) -> impl Stream> + 'a { + ) -> impl Stream> + 'a { stream_paginated_query( move |pag_filter| self.objects(filter.clone(), pag_filter), streaming_direction, @@ -1101,7 +1074,7 @@ impl Client { } /// Return the object's bcs content [`Vec`] based on the provided [`Address`]. - pub async fn object_bcs(&self, object_id: Address) -> Result>, Error> { + pub async fn object_bcs(&self, object_id: Address) -> Result>> { let operation = ObjectQuery::build(ObjectQueryArgs { address: object_id, version: None, @@ -1110,15 +1083,14 @@ impl Client { let response = self.run_query(&operation).await.unwrap(); if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(object) = response.data.map(|d| d.object) { - object + Ok(object .and_then(|o| o.bcs) .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}"))) + .transpose()?) } else { Ok(None) } @@ -1132,13 +1104,13 @@ impl Client { &self, address: Address, version: Option, - ) -> Result, Error> { + ) -> Result> { let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(object) = response.data { @@ -1159,23 +1131,22 @@ impl Client { &self, address: Address, version: Option, - ) -> Result>, Error> { + ) -> Result>> { let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(object) = response.data { - object + Ok(object .object .and_then(|o| o.as_move_object) .and_then(|o| o.contents) .map(|bcs| base64ct::Base64::decode_vec(bcs.bcs.0.as_str())) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 object bcs bytes: {e}"))) + .transpose()?) } else { Ok(None) } @@ -1198,25 +1169,23 @@ impl Client { &self, address: Address, version: Option, - ) -> Result, Error> { + ) -> Result> { let operation = PackageQuery::build(PackageArgs { address, version }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } - response + Ok(response .data .and_then(|x| x.package) .and_then(|x| x.package_bcs) .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))? + .transpose()? .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}"))) + .transpose()?) } /// Fetch all versions of package at address (packages that share this package's original ID), @@ -1228,7 +1197,7 @@ impl Client { pagination_filter: PaginationFilter, after_version: Option, before_version: Option, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = PackageVersionsQuery::build(PackageVersionsArgs { address, @@ -1245,7 +1214,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(packages) = response.data { @@ -1259,15 +1228,11 @@ impl Client { b64.as_ref() .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) }) - .collect::, base64ct::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))?; + .collect::, base64ct::Error>>()?; let packages = bcs .iter() .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>() - .map_err(|e| { - Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) - })?; + .collect::, bcs::Error>>()?; Ok(Page::new(page_info, packages)) } else { @@ -1278,7 +1243,7 @@ impl Client { /// Fetch the latest version of the package at address. /// This corresponds to the package with the highest version that shares its original ID with /// the package at address. - pub async fn package_latest(&self, address: Address) -> Result, Error> { + pub async fn package_latest(&self, address: Address) -> Result> { let operation = LatestPackageQuery::build(PackageArgs { address, version: None, @@ -1287,7 +1252,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } let pkg = response @@ -1295,24 +1260,22 @@ impl Client { .and_then(|x| x.latest_package) .and_then(|x| x.package_bcs) .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))? + .transpose()? .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose() - .map_err(|e| Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")))?; + .transpose()?; Ok(pkg) } /// Fetch a package by its name (using Move Registry Service) - pub async fn package_by_name(&self, name: &str) -> Result, Error> { + pub async fn package_by_name(&self, name: &str) -> Result> { let operation = PackageByNameQuery::build(PackageByNameArgs { name }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { println!("{:?}", errors); - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response @@ -1336,9 +1299,12 @@ impl Client { last: Option, after_checkpoint: Option, before_checkpoint: Option, - ) -> Result, Error> { + ) -> Result> { if first.is_some() && last.is_some() { - return Err(Error::msg("Cannot specify both first and last")); + return Err(Error::from_error( + Kind::Other, + "Conflicting arguments: either first or last can be provided, but not both.", + )); } let operation = PackagesQuery::build(PackagesQueryArgs { @@ -1355,7 +1321,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(packages) = response.data { @@ -1369,15 +1335,11 @@ impl Client { b64.as_ref() .map(|b| base64ct::Base64::decode_vec(b.0.as_str())) }) - .collect::, base64ct::Error>>() - .map_err(|e| Error::msg(format!("Cannot decode Base64 package bcs bytes: {e}")))?; + .collect::, base64ct::Error>>()?; let packages = bcs .iter() .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>() - .map_err(|e| { - Error::msg(format!("Cannot decode bcs bytes into MovePackage: {e}")) - })?; + .collect::, bcs::Error>>()?; Ok(Page::new(page_info, packages)) } else { @@ -1398,10 +1360,8 @@ impl Client { &self, tx: &Transaction, skip_checks: Option, - ) -> Result { - let tx_bytes = base64ct::Base64::encode_string( - &bcs::to_bytes(&tx).map_err(|_| Error::msg("Cannot encode Transaction as BCS"))?, - ); + ) -> Result { + let tx_bytes = base64ct::Base64::encode_string(&bcs::to_bytes(&tx)?); self.dry_run(tx_bytes, skip_checks, None).await } @@ -1417,10 +1377,8 @@ impl Client { tx_kind: &TransactionKind, skip_checks: Option, tx_meta: TransactionMetadata, - ) -> Result { - let tx_bytes = base64ct::Base64::encode_string( - &bcs::to_bytes(&tx_kind).map_err(|_| Error::msg("Cannot encode Transaction as BCS"))?, - ); + ) -> Result { + let tx_bytes = base64ct::Base64::encode_string(&bcs::to_bytes(&tx_kind)?); self.dry_run(tx_bytes, skip_checks, Some(tx_meta)).await } @@ -1430,7 +1388,7 @@ impl Client { tx_bytes: String, skip_checks: Option, tx_meta: Option, - ) -> Result { + ) -> Result { let skip_checks = skip_checks.unwrap_or(false); let operation = DryRunQuery::build(DryRunArgs { tx_bytes, @@ -1441,7 +1399,7 @@ impl Client { // Query errors if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } // Dry Run errors @@ -1457,11 +1415,9 @@ impl Client { .and_then(|tx| tx.effects) .and_then(|bcs| bcs.bcs) .map(|bcs| base64ct::Base64::decode_vec(bcs.0.as_str())) - .transpose() - .map_err(|_| Error::msg("Cannot decode bcs bytes from Base64 for transaction effects"))? + .transpose()? .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose() - .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; + .transpose()?; Ok(DryRunResult { effects, error }) } @@ -1471,25 +1427,28 @@ impl Client { // =========================================================================== /// Get a transaction by its digest. - pub async fn transaction(&self, digest: Digest) -> Result, Error> { + pub async fn transaction(&self, digest: Digest) -> Result> { let operation = TransactionBlockQuery::build(TransactionBlockArgs { digest: digest.to_string(), }); let response = self.run_query(&operation).await?; + if let Some(errors) = response.errors { + return Err(Error::graphql_error(errors)); + } + response .data .and_then(|d| d.transaction_block) .map(|tx| tx.try_into()) .transpose() - .map_err(|e| Error::msg(format!("Cannot decode transaction: {e}"))) } /// Get a transaction's effects by its digest. pub async fn transaction_effects( &self, digest: TransactionDigest, - ) -> Result, Error> { + ) -> Result> { let operation = TransactionBlockEffectsQuery::build(TransactionBlockArgs { digest: digest.to_string(), }); @@ -1500,7 +1459,6 @@ impl Client { .and_then(|d| d.transaction_block) .map(|tx| tx.try_into()) .transpose() - .map_err(|e| Error::msg(format!("Cannot decode transaction: {e}"))) } /// Get a page of transactions based on the provided filters. @@ -1508,7 +1466,7 @@ impl Client { &self, filter: Option>, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = TransactionBlocksQuery::build(TransactionBlocksQueryArgs { @@ -1521,6 +1479,10 @@ impl Client { let response = self.run_query(&operation).await?; + if let Some(errors) = response.errors { + return Err(Error::graphql_error(errors)); + } + if let Some(txb) = response.data { let txc = txb.transaction_blocks; let page_info = txc.page_info; @@ -1542,7 +1504,7 @@ impl Client { &self, filter: Option>, pagination_filter: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = TransactionBlocksEffectsQuery::build(TransactionBlocksQueryArgs { @@ -1576,7 +1538,7 @@ impl Client { &'a self, filter: Option>, streaming_direction: Direction, - ) -> impl Stream> + 'a { + ) -> impl Stream> + 'a { stream_paginated_query( move |pag_filter| self.transactions(filter.clone(), pag_filter), streaming_direction, @@ -1588,7 +1550,7 @@ impl Client { &'a self, filter: Option>, streaming_direction: Direction, - ) -> impl Stream> + 'a { + ) -> impl Stream> + 'a { stream_paginated_query( move |pag_filter| self.transactions_effects(filter.clone(), pag_filter), streaming_direction, @@ -1600,7 +1562,7 @@ impl Client { &self, signatures: Vec, tx: &Transaction, - ) -> Result, Error> { + ) -> Result> { let operation = ExecuteTransactionQuery::build(ExecuteTransactionArgs { signatures: signatures.iter().map(|s| s.to_base64()).collect(), tx_bytes: base64ct::Base64::encode_string(bcs::to_bytes(tx).unwrap().as_ref()), @@ -1609,17 +1571,13 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } if let Some(data) = response.data { let result = data.execute_transaction_block; - let bcs = - base64ct::Base64::decode_vec(result.effects.bcs.0.as_str()).map_err(|_| { - Error::msg("Cannot decode bcs bytes from Base64 for transaction effects") - })?; - let effects: TransactionEffects = bcs::from_bytes(&bcs) - .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; + let bcs = base64ct::Base64::decode_vec(result.effects.bcs.0.as_str())?; + let effects: TransactionEffects = bcs::from_bytes(&bcs)?; Ok(Some(effects)) } else { @@ -1637,7 +1595,7 @@ impl Client { module: &str, function: &str, version: Option, - ) -> Result, Error> { + ) -> Result> { let operation = NormalizedMoveFunctionQuery::build(NormalizedMoveFunctionQueryArgs { address: Address::from_str(package)?, module, @@ -1647,7 +1605,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response @@ -1670,7 +1628,7 @@ impl Client { pagination_filter_friends: PaginationFilter, pagination_filter_functions: PaginationFilter, pagination_filter_structs: PaginationFilter, - ) -> Result, Error> { + ) -> Result> { let (after_enums, before_enums, first_enums, last_enums) = self.pagination_filter(pagination_filter_enums).await; let (after_friends, before_friends, first_friends, last_friends) = @@ -1703,7 +1661,7 @@ impl Client { let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response.data.and_then(|p| p.package).and_then(|p| p.module)) @@ -1714,13 +1672,13 @@ impl Client { // =========================================================================== /// Get the address for the provided Suins domain name. - pub async fn resolve_suins_to_address(&self, domain: &str) -> Result, Error> { + pub async fn resolve_suins_to_address(&self, domain: &str) -> Result> { let operation = ResolveSuinsQuery::build(ResolveSuinsQueryArgs { name: domain }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response .data @@ -1729,13 +1687,13 @@ impl Client { } /// Get the default Suins domain name for the provided address. - pub async fn default_suins_name(&self, address: Address) -> Result, Error> { + pub async fn default_suins_name(&self, address: Address) -> Result> { let operation = DefaultSuinsNameQuery::build(DefaultSuinsNameQueryArgs { address }); let response = self.run_query(&operation).await?; if let Some(errors) = response.errors { - return Err(Error::msg(format!("{:?}", errors))); + return Err(Error::graphql_error(errors)); } Ok(response .data diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index bdd01d436..bc4b77237 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -1,13 +1,15 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::Error; use chrono::DateTime as ChronoDT; use sui_types::types::CheckpointContentsDigest; use sui_types::types::CheckpointDigest; use sui_types::types::CheckpointSummary; use sui_types::types::GasCostSummary as NativeGasCostSummary; +use crate::error; +use crate::error::Error; +use crate::error::Kind; use crate::query_types::schema; use crate::query_types::Base64; use crate::query_types::BigInt; @@ -103,19 +105,23 @@ pub struct GasCostSummary { // TODO need bcs in GraphQL Checkpoint to avoid this conversion impl TryInto for Checkpoint { - type Error = anyhow::Error; + type Error = error::Error; fn try_into(self) -> Result { let epoch = self .epoch - .ok_or_else(|| Error::msg("Epoch is missing"))? + .ok_or_else(|| { + Error::from_error(Kind::Other, "Epoch is checkpoint summary is missing") + })? .epoch_id; - let network_total_transactions = self - .network_total_transactions - .ok_or_else(|| Error::msg("Network total transactions is missing"))?; + let network_total_transactions = self.network_total_transactions.ok_or_else(|| { + Error::from_error( + Kind::Other, + "Network total transactions in checkpoint summary is missing", + ) + })?; let sequence_number = self.sequence_number; - let timestamp_ms = ChronoDT::parse_from_rfc3339(&self.timestamp.0) - .map_err(|e| Error::msg(format!("Cannot parse DateTime: {e}")))? + let timestamp_ms = ChronoDT::parse_from_rfc3339(&self.timestamp.0)? .timestamp_millis() .try_into()?; let content_digest = CheckpointContentsDigest::from_base58(&self.digest)?; @@ -125,7 +131,12 @@ impl TryInto for Checkpoint { .transpose()?; let epoch_rolling_gas_cost_summary = self .rolling_gas_summary - .ok_or_else(|| Error::msg("Rolling gas summary is missing"))? + .ok_or_else(|| { + Error::from_error( + Kind::Other, + "Gas cost summary in checkpoint summary is missing", + ) + })? .try_into()?; Ok(CheckpointSummary { epoch, @@ -143,23 +154,23 @@ impl TryInto for Checkpoint { } impl TryInto for GasCostSummary { - type Error = anyhow::Error; + type Error = error::Error; fn try_into(self) -> Result { let computation_cost = self .computation_cost - .ok_or_else(|| Error::msg("Computation cost is missing"))? + .ok_or_else(|| Error::from_error(Kind::Other, "Computation cost is missing"))? .try_into()?; let non_refundable_storage_fee = self .non_refundable_storage_fee - .ok_or_else(|| Error::msg("Non-refundable storage fee is missing"))? + .ok_or_else(|| Error::from_error(Kind::Other, "Non-refundable storage fee is missing"))? .try_into()?; let storage_cost = self .storage_cost - .ok_or_else(|| Error::msg("Storage cost is missing"))? + .ok_or_else(|| Error::from_error(Kind::Other, "Storage cost is missing"))? .try_into()?; let storage_rebate = self .storage_rebate - .ok_or_else(|| Error::msg("Storage rebate is missing"))? + .ok_or_else(|| Error::from_error(Kind::Other, "Storage rebate is missing"))? .try_into()?; Ok(NativeGasCostSummary { computation_cost, diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index 4773ba3b4..3d19c1aff 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -6,6 +6,7 @@ use std::str::FromStr; use base64ct::Encoding; use sui_types::types::TypeTag; +use crate::error; use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; @@ -156,7 +157,7 @@ impl DynamicField { } impl TryFrom for DynamicFieldOutput { - type Error = anyhow::Error; + type Error = error::Error; fn try_from(val: DynamicField) -> Result { let typetag = TypeTag::from_str( @@ -166,8 +167,7 @@ impl TryFrom for DynamicFieldOutput { .type_ .repr .as_str(), - ) - .map_err(|_| anyhow::anyhow!("Invalid TypeTag"))?; + )?; Ok(DynamicFieldOutput { name: crate::DynamicFieldName { type_: typetag, diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index 2bf88140f..d58d2fbca 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -109,10 +109,11 @@ pub use transaction::TransactionsFilter; use sui_types::types::Address; -use anyhow::anyhow; use cynic::impl_scalar; use serde_json::Value as JsonValue; +use crate::error; + #[cynic::schema("rpc")] pub mod schema {} @@ -190,12 +191,9 @@ pub struct PageInfo { } impl TryFrom for u64 { - type Error = anyhow::Error; + type Error = error::Error; fn try_from(value: BigInt) -> Result { - value - .0 - .parse::() - .map_err(|e| anyhow!("Cannot convert BigInt into u64: {e}")) + Ok(value.0.parse::()?) } } diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index e95b4c6e5..38be0490f 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -1,13 +1,15 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::Error; use base64ct::Encoding; use sui_types::types::SignedTransaction; use sui_types::types::Transaction; use sui_types::types::TransactionEffects; use sui_types::types::UserSignature; +use crate::error; +use crate::error::Error; +use crate::error::Kind; use crate::query_types::schema; use crate::query_types::Address; use crate::query_types::Base64; @@ -142,23 +144,20 @@ pub struct TransactionBlockEffectsConnection { } impl TryFrom for SignedTransaction { - type Error = anyhow::Error; + type Error = error::Error; fn try_from(value: TransactionBlock) -> Result { let transaction = value .bcs .map(|tx| base64ct::Base64::decode_vec(tx.0.as_str())) - .transpose() - .map_err(|_| Error::msg("Cannot decode Base64 transaction bcs bytes"))? + .transpose()? .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose() - .map_err(|_| Error::msg("Cannot decode bcs bytes into Transaction"))?; + .transpose()?; let signatures = if let Some(sigs) = value.signatures { sigs.iter() .map(|s| UserSignature::from_base64(&s.0)) - .collect::, _>>() - .map_err(|_| Error::msg("Cannot decode Base64 signature"))? + .collect::, _>>()? } else { vec![] }; @@ -169,23 +168,29 @@ impl TryFrom for SignedTransaction { signatures, }) } else { - Err(Error::msg("Cannot decode transaction")) + Err(Error::from_error( + Kind::Other, + "Expected a deserialized transaction but got None", + )) } } } impl TryFrom for TransactionEffects { - type Error = anyhow::Error; + type Error = error::Error; fn try_from(value: TxBlockEffects) -> Result { let effects = value .effects .map(|fx| base64ct::Base64::decode_vec(fx.bcs.unwrap().0.as_str())) - .transpose() - .map_err(|_| Error::msg("Cannot decode Base64 effects bcs bytes"))? + .transpose()? .map(|bcs| bcs::from_bytes::(&bcs)) - .transpose() - .map_err(|_| Error::msg("Cannot decode bcs bytes into TransactionEffects"))?; - effects.ok_or_else(|| Error::msg("Cannot decode effects")) + .transpose()?; + effects.ok_or_else(|| { + Error::from_error( + Kind::Other, + "Cannot convert GraphQL TxBlockEffects into TransactionEffects", + ) + }) } } diff --git a/crates/sui-graphql-client/src/streams.rs b/crates/sui-graphql-client/src/streams.rs index 5b0c96e92..f16985e71 100644 --- a/crates/sui-graphql-client/src/streams.rs +++ b/crates/sui-graphql-client/src/streams.rs @@ -1,9 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::error; use crate::query_types::PageInfo; use crate::Direction; -use crate::Error; use crate::Page; use crate::PaginationFilter; @@ -41,9 +41,9 @@ where T: Clone + Unpin, F: Fn(PaginationFilter) -> Fut, F: Unpin, - Fut: Future, Error>>, + Fut: Future, error::Error>>, { - type Item = Result; + type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.finished { @@ -172,7 +172,7 @@ where pub fn stream_paginated_query(query_fn: F, direction: Direction) -> PageStream where F: Fn(PaginationFilter) -> Fut, - Fut: Future, Error>>, + Fut: Future, error::Error>>, { PageStream::new(query_fn, direction) } diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index eb33cb5f7..3fd0dc55a 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -13,7 +13,7 @@ mod transaction; mod type_tag; mod u256; -pub use address::Address; +pub use address::{Address, AddressParseError}; pub use checkpoint::{ CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber, CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo, From bc937e08abcaf5a862d4011a4ccf3d6dbc7ccf98 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:55:33 -0800 Subject: [PATCH 071/107] sui-sdk-types: rename types and add constructors for unresolved::Value (#60) --- crates/sui-sdk-types/src/types/address.rs | 2 +- crates/sui-sdk-types/src/types/effects/mod.rs | 28 +++ crates/sui-sdk-types/src/types/effects/v1.rs | 23 ++- crates/sui-sdk-types/src/types/mod.rs | 4 +- .../src/types/serialization_proptests.rs | 2 +- .../src/types/transaction/mod.rs | 25 ++- .../src/types/transaction/serialization.rs | 68 +++---- .../src/types/transaction/unresolved.rs | 185 +++++++++++++++--- 8 files changed, 257 insertions(+), 80 deletions(-) diff --git a/crates/sui-sdk-types/src/types/address.rs b/crates/sui-sdk-types/src/types/address.rs index b7a966b16..028620d08 100644 --- a/crates/sui-sdk-types/src/types/address.rs +++ b/crates/sui-sdk-types/src/types/address.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) diff --git a/crates/sui-sdk-types/src/types/effects/mod.rs b/crates/sui-sdk-types/src/types/effects/mod.rs index 0fcad88ba..01ba3ee44 100644 --- a/crates/sui-sdk-types/src/types/effects/mod.rs +++ b/crates/sui-sdk-types/src/types/effects/mod.rs @@ -5,6 +5,8 @@ pub use v1::{ UnchangedSharedKind, UnchangedSharedObject, }; +use crate::types::execution_status::ExecutionStatus; + /// The response from processing a transaction or a certified transaction #[derive(Eq, PartialEq, Clone, Debug)] #[cfg_attr( @@ -18,6 +20,32 @@ pub enum TransactionEffects { V1(Box), } +impl TransactionEffects { + /// Return the status of the transaction. + pub fn status(&self) -> &ExecutionStatus { + match self { + TransactionEffects::V1(e) => e.status(), + TransactionEffects::V2(e) => e.status(), + } + } + + /// Return the epoch in which this transaction was executed. + pub fn epoch(&self) -> u64 { + match self { + TransactionEffects::V1(e) => e.epoch(), + TransactionEffects::V2(e) => e.epoch(), + } + } + + /// Return the gas cost summary of the transaction. + pub fn gas_summary(&self) -> &crate::types::gas::GasCostSummary { + match self { + TransactionEffects::V1(e) => e.gas_summary(), + TransactionEffects::V2(e) => e.gas_summary(), + } + } +} + #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod serialization { diff --git a/crates/sui-sdk-types/src/types/effects/v1.rs b/crates/sui-sdk-types/src/types/effects/v1.rs index 1e8245b02..605eaa06f 100644 --- a/crates/sui-sdk-types/src/types/effects/v1.rs +++ b/crates/sui-sdk-types/src/types/effects/v1.rs @@ -12,11 +12,11 @@ use crate::types::{ pub struct TransactionEffectsV1 { /// The status of the execution #[cfg_attr(feature = "schemars", schemars(flatten))] - pub status: ExecutionStatus, + status: ExecutionStatus, /// The epoch when this transaction was executed. #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] - pub epoch: EpochId, - pub gas_used: GasCostSummary, + epoch: EpochId, + gas_used: GasCostSummary, /// The transaction digest pub transaction_digest: TransactionDigest, /// The updated gas object reference, as an index into the `changed_objects` @@ -186,6 +186,23 @@ pub enum IdOperation { Deleted, } +impl TransactionEffectsV1 { + /// The status of the execution + pub fn status(&self) -> &ExecutionStatus { + &self.status + } + + /// The epoch when this transaction was executed. + pub fn epoch(&self) -> EpochId { + self.epoch + } + + /// The gas used in this transaction. + pub fn gas_summary(&self) -> &GasCostSummary { + &self.gas_used + } +} + #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod serialization { diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index 3fd0dc55a..5ef35b30d 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -57,12 +57,12 @@ pub use transaction::{ AuthenticatorStateUpdateV1, CancelledTransaction, ChangeEpoch, Command, ConsensusCommitPrologue, ConsensusCommitPrologueV1, ConsensusCommitPrologueV2, ConsensusCommitPrologueV3, ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind, - GasPayment, GenesisTransaction, InputArgument, MakeMoveVector, MergeCoins, MoveCall, + GasPayment, GenesisTransaction, Input, InputArgument, MakeMoveVector, MergeCoins, MoveCall, ProgrammableTransaction, Publish, RandomnessStateUpdate, SignedTransaction, SplitCoins, SystemPackage, Transaction, TransactionExpiration, TransactionKind, TransferObjects, UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedInputArgumentKind, UnresolvedObjectReference, UnresolvedProgrammableTransaction, UnresolvedTransaction, - UnresolvedValue, Upgrade, VersionAssignment, + UnresolvedValue, Upgrade, VersionAssignment, unresolved, }; pub use type_tag::{Identifier, StructTag, TypeParseError, TypeTag}; diff --git a/crates/sui-sdk-types/src/types/serialization_proptests.rs b/crates/sui-sdk-types/src/types/serialization_proptests.rs index 63227e5cc..459af5eee 100644 --- a/crates/sui-sdk-types/src/types/serialization_proptests.rs +++ b/crates/sui-sdk-types/src/types/serialization_proptests.rs @@ -158,7 +158,7 @@ serialization_test!(VersionAssignment); serialization_test!(EndOfEpochTransactionKind); serialization_test!(GasPayment); serialization_test!(GenesisTransaction); -serialization_test!(InputArgument); +serialization_test!(Input); serialization_test!(MakeMoveVector); serialization_test!(MergeCoins); serialization_test!(MoveCall); diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs index 874917283..d7ca5cde9 100644 --- a/crates/sui-sdk-types/src/types/transaction/mod.rs +++ b/crates/sui-sdk-types/src/types/transaction/mod.rs @@ -11,12 +11,7 @@ mod serialization; #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub(crate) use serialization::SignedTransactionWithIntentMessage; -mod unresolved; -pub use unresolved::{ - UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedInputArgumentKind, - UnresolvedObjectReference, UnresolvedProgrammableTransaction, UnresolvedTransaction, - UnresolvedValue, -}; +pub mod unresolved; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(test, derive(test_strategy::Arbitrary))] @@ -39,10 +34,11 @@ pub struct SignedTransaction { pub signatures: Vec, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(test_strategy::Arbitrary))] pub enum TransactionExpiration { /// The transaction has no expiration + #[default] None, /// Validators wont sign a transaction unless the expiration Epoch /// is greater than or equal to the current epoch @@ -427,7 +423,7 @@ pub struct GenesisTransaction { pub struct ProgrammableTransaction { /// Input objects or primitive values #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] - pub inputs: Vec, + pub inputs: Vec, /// The commands to be executed sequentially. A failure in any command will /// result in the failure of the entire transaction. #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] @@ -441,7 +437,7 @@ pub struct ProgrammableTransaction { schemars(tag = "type", rename_all = "snake_case") )] #[cfg_attr(test, derive(test_strategy::Arbitrary))] -pub enum InputArgument { +pub enum Input { // contains no structs or objects Pure { #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))] @@ -614,6 +610,17 @@ pub enum Argument { NestedResult(u16, u16), } +impl Argument { + /// Turn a Result into a NestedResult. If the argument is not a Result, + /// returns None. + pub fn nested(&self, ix: u16) -> Option { + match self { + Argument::Result(i) => Some(Argument::NestedResult(*i, ix)), + _ => None, + } + } +} + /// The command for calling a Move function, either an entry function or a /// public function (which cannot return references). #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/sui-sdk-types/src/types/transaction/serialization.rs b/crates/sui-sdk-types/src/types/transaction/serialization.rs index 34afa58c5..425f670e4 100644 --- a/crates/sui-sdk-types/src/types/transaction/serialization.rs +++ b/crates/sui-sdk-types/src/types/transaction/serialization.rs @@ -536,11 +536,11 @@ mod version_assignments { mod input_argument { use super::*; - use crate::types::transaction::InputArgument; + use crate::types::transaction::{Input, InputArgument}; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] - enum ReadableInputArgument { + enum ReadableInput { Pure { #[serde(with = "::serde_with::As::")] value: Vec, @@ -572,38 +572,36 @@ mod input_argument { Receiving(ObjectReference), } - impl Serialize for InputArgument { + impl Serialize for Input { fn serialize(&self, serializer: S) -> Result where S: Serializer, { if serializer.is_human_readable() { let readable = match self.clone() { - InputArgument::Pure { value } => ReadableInputArgument::Pure { value }, - InputArgument::ImmutableOrOwned(object_ref) => { - ReadableInputArgument::ImmutableOrOwned(object_ref) + Input::Pure { value } => ReadableInput::Pure { value }, + Input::ImmutableOrOwned(object_ref) => { + ReadableInput::ImmutableOrOwned(object_ref) } - InputArgument::Shared { + Input::Shared { object_id, initial_shared_version, mutable, - } => ReadableInputArgument::Shared { + } => ReadableInput::Shared { object_id, initial_shared_version, mutable, }, - InputArgument::Receiving(object_ref) => { - ReadableInputArgument::Receiving(object_ref) - } + Input::Receiving(object_ref) => ReadableInput::Receiving(object_ref), }; readable.serialize(serializer) } else { let binary = match self.clone() { - InputArgument::Pure { value } => CallArg::Pure(value), - InputArgument::ImmutableOrOwned(object_ref) => { + Input::Pure { value } => CallArg::Pure(value), + Input::ImmutableOrOwned(object_ref) => { CallArg::Object(ObjectArg::ImmutableOrOwned(object_ref)) } - InputArgument::Shared { + Input::Shared { object_id, initial_shared_version, mutable, @@ -612,7 +610,7 @@ mod input_argument { initial_shared_version, mutable, }), - InputArgument::Receiving(object_ref) => { + Input::Receiving(object_ref) => { CallArg::Object(ObjectArg::Receiving(object_ref)) } }; @@ -621,47 +619,45 @@ mod input_argument { } } - impl<'de> Deserialize<'de> for InputArgument { + impl<'de> Deserialize<'de> for Input { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { if deserializer.is_human_readable() { - ReadableInputArgument::deserialize(deserializer).map(|readable| match readable { - ReadableInputArgument::Pure { value } => InputArgument::Pure { value }, - ReadableInputArgument::ImmutableOrOwned(object_ref) => { - InputArgument::ImmutableOrOwned(object_ref) + ReadableInput::deserialize(deserializer).map(|readable| match readable { + ReadableInput::Pure { value } => Input::Pure { value }, + ReadableInput::ImmutableOrOwned(object_ref) => { + Input::ImmutableOrOwned(object_ref) } - ReadableInputArgument::Shared { + ReadableInput::Shared { object_id, initial_shared_version, mutable, - } => InputArgument::Shared { + } => Input::Shared { object_id, initial_shared_version, mutable, }, - ReadableInputArgument::Receiving(object_ref) => { - InputArgument::Receiving(object_ref) - } + ReadableInput::Receiving(object_ref) => Input::Receiving(object_ref), }) } else { CallArg::deserialize(deserializer).map(|binary| match binary { - CallArg::Pure(value) => InputArgument::Pure { value }, + CallArg::Pure(value) => Input::Pure { value }, CallArg::Object(ObjectArg::ImmutableOrOwned(object_ref)) => { - InputArgument::ImmutableOrOwned(object_ref) + Input::ImmutableOrOwned(object_ref) } CallArg::Object(ObjectArg::Shared { object_id, initial_shared_version, mutable, - }) => InputArgument::Shared { + }) => Input::Shared { object_id, initial_shared_version, mutable, }, CallArg::Object(ObjectArg::Receiving(object_ref)) => { - InputArgument::Receiving(object_ref) + Input::Receiving(object_ref) } }) } @@ -1148,7 +1144,7 @@ mod test { use crate::types::{ ObjectDigest, ObjectId, ObjectReference, - transaction::{Argument, InputArgument, Transaction}, + transaction::{Argument, Input, InputArgument, Transaction}, }; #[test] @@ -1177,7 +1173,7 @@ mod test { fn input_argument() { let test_cases = [ ( - InputArgument::Pure { + Input::Pure { value: vec![1, 2, 3, 4], }, serde_json::json!({ @@ -1186,7 +1182,7 @@ mod test { }), ), ( - InputArgument::ImmutableOrOwned(ObjectReference::new( + Input::ImmutableOrOwned(ObjectReference::new( ObjectId::ZERO, 1, ObjectDigest::ZERO, @@ -1199,7 +1195,7 @@ mod test { }), ), ( - InputArgument::Shared { + Input::Shared { object_id: ObjectId::ZERO, initial_shared_version: 1, mutable: true, @@ -1212,11 +1208,7 @@ mod test { }), ), ( - InputArgument::Receiving(ObjectReference::new( - ObjectId::ZERO, - 1, - ObjectDigest::ZERO, - )), + Input::Receiving(ObjectReference::new(ObjectId::ZERO, 1, ObjectDigest::ZERO)), serde_json::json!({ "type": "receiving", "object_id": "0x0000000000000000000000000000000000000000000000000000000000000000", diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs index 33b601cb7..570d0e62e 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-sdk-types/src/types/transaction/unresolved.rs @@ -1,18 +1,20 @@ use super::{Command, TransactionExpiration}; use crate::types::{Address, ObjectDigest, ObjectId, object::Version}; -// A potentially Unresolved user transaction +// A potentially unresolved user transaction. Note that one can construct a +// fully resolved transaction using this type by providing all the required +// data. #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct UnresolvedTransaction { +pub struct Transaction { #[cfg_attr(feature = "serde", serde(flatten))] - pub ptb: UnresolvedProgrammableTransaction, + pub ptb: ProgrammableTransaction, pub sender: Address, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] - pub gas_payment: Option, + pub gas_payment: Option, pub expiration: TransactionExpiration, } @@ -21,8 +23,8 @@ pub struct UnresolvedTransaction { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct UnresolvedProgrammableTransaction { - pub inputs: Vec, +pub struct ProgrammableTransaction { + pub inputs: Vec, pub commands: Vec, } @@ -31,12 +33,12 @@ pub struct UnresolvedProgrammableTransaction { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct UnresolvedGasPayment { +pub struct GasPayment { #[cfg_attr( feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty") )] - pub objects: Vec, + pub objects: Vec, pub owner: Address, #[cfg_attr( feature = "serde", @@ -60,12 +62,13 @@ pub struct UnresolvedGasPayment { pub budget: Option, } +#[derive(Clone, Debug)] #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct UnresolvedObjectReference { +pub struct ObjectReference { pub object_id: ObjectId, #[cfg_attr( feature = "serde", @@ -88,31 +91,41 @@ pub struct UnresolvedObjectReference { serde(rename_all = "snake_case") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub enum UnresolvedInputArgumentKind { +pub enum InputKind { Pure, Shared, Receiving, ImmutableOrOwned, - Immutable, - Owned, Literal, } +/// A potentially unresolved transaction input. Note that one can construct a +/// fully resolved input using the provided constructors, but this struct is +/// also useful when the input data is not complete. +/// +/// If used in the context of transaction builder, make sure to call +/// `tx.resolve` function on the transaction builder to resolve all unresolved +/// inputs. #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -pub struct UnresolvedInputArgument { +pub struct Input { #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] - pub kind: Option, + pub kind: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] - pub value: Option, + pub value: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + /// Unique identifier for this object. pub object_id: Option, /// Either the `initial_shared_version` if object is a shared object, or the - /// `version` if this is an owned object + /// `version` if this is an owned object. + /// The semantics of version can change depending on whether the object is + /// shared or not. For shared objects, this is the initial version the + /// object was shared at. For all other objects, this is the version of + /// the object. #[cfg_attr( feature = "serde", serde( @@ -124,8 +137,12 @@ pub struct UnresolvedInputArgument { )] #[cfg_attr(feature = "schemars", schemars(with = "Option"))] pub version: Option, + /// The digest of this object. This field is only relevant for + /// owned/immutable/receiving inputs. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] pub digest: Option, + /// Whether this object is mutable. This field is only relevant for shared + /// objects. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] pub mutable: Option, } @@ -137,17 +154,133 @@ pub struct UnresolvedInputArgument { serde(try_from = "serde_json::Value", into = "serde_json::Value") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema), schemars(untagged))] -pub enum UnresolvedValue { +pub enum Value { Null, Bool(bool), Number(u64), String(String), - Array(Vec), + Array(Vec), +} + +impl Input { + /// Return an owned kind of object with all required fields. + pub fn owned(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self { + Self { + kind: Some(InputKind::ImmutableOrOwned), + object_id: Some(object_id), + version: Some(version), + digest: Some(digest), + ..Default::default() + } + } + + /// Return an immutable kind of object with all required fields. + pub fn immutable(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self { + Self { + kind: Some(InputKind::ImmutableOrOwned), + object_id: Some(object_id), + version: Some(version), + digest: Some(digest), + ..Default::default() + } + } + + /// Return a receiving kind of object with all required fields. + pub fn receiving(object_id: ObjectId, version: u64, digest: ObjectDigest) -> Self { + Self { + kind: Some(InputKind::Receiving), + object_id: Some(object_id), + version: Some(version), + digest: Some(digest), + ..Default::default() + } + } + + /// Return a shared object. + /// - `mutable` controls whether a command can accept the object by value or + /// mutable reference. + /// - `initial_shared_version` is the first version the object was shared + /// at. + pub fn shared(object_id: ObjectId, initial_shared_version: u64, mutable: bool) -> Self { + Self { + kind: Some(InputKind::Shared), + object_id: Some(object_id), + version: Some(initial_shared_version), + mutable: Some(mutable), + ..Default::default() + } + } + + /// Return an object with only its unique identifier. + pub fn by_id(object_id: ObjectId) -> Self { + Self { + object_id: Some(object_id), + ..Default::default() + } + } + + /// Set the object kind to immutable. + pub fn with_immutable_kind(&mut self) { + self.kind = Some(InputKind::ImmutableOrOwned); + } + + /// Set the object kind to owned. + pub fn with_owned_kind(&mut self) { + self.kind = Some(InputKind::ImmutableOrOwned); + } + + /// Set the object kind to receiving. + pub fn with_receiving_kind(&mut self) { + self.kind = Some(InputKind::Receiving); + } + + /// Set the object kind to shared. + pub fn with_shared_kind(&mut self) { + self.kind = Some(InputKind::Shared); + } + + /// Set the specified version. + pub fn with_version(&mut self, version: u64) { + self.version = Some(version); + } + + /// Set the specified digest. + pub fn with_digest(&mut self, digest: ObjectDigest) { + self.digest = Some(digest); + } + + // Shared fields + + /// Set the initial shared version. + pub fn with_initial_shared_version(&mut self, initial: u64) { + self.version = Some(initial); + } + + /// Make the object shared and set `mutable` to true when the input is used + /// by value. + pub fn by_val(&mut self) { + self.kind = Some(InputKind::Shared); + self.mutable = Some(true); + } + + /// Make the object shared and set `mutable` to false when the input is used + /// by reference. + pub fn by_ref(&mut self) { + self.kind = Some(InputKind::Shared); + self.mutable = Some(false); + } + + /// Make the object shared and set `mutable` to true when the input is used + /// by mutable reference. + pub fn by_mut(&mut self) { + self.kind = Some(InputKind::Shared); + self.mutable = Some(true); + } } #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] -impl TryFrom for UnresolvedValue { +impl TryFrom for Value { type Error = &'static str; fn try_from(value: serde_json::Value) -> Result { @@ -172,14 +305,14 @@ impl TryFrom for UnresolvedValue { #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] -impl From for serde_json::Value { - fn from(value: UnresolvedValue) -> Self { +impl From for serde_json::Value { + fn from(value: Value) -> Self { match value { - UnresolvedValue::Null => Self::Null, - UnresolvedValue::Bool(b) => Self::Bool(b), - UnresolvedValue::Number(n) => Self::Number(n.into()), - UnresolvedValue::String(s) => Self::String(s), - UnresolvedValue::Array(a) => Self::Array(a.into_iter().map(Into::into).collect()), + Value::Null => Self::Null, + Value::Bool(b) => Self::Bool(b), + Value::Number(n) => Self::Number(n.into()), + Value::String(s) => Self::String(s), + Value::Array(a) => Self::Array(a.into_iter().map(Into::into).collect()), } } } From f3eafe1ca50d07815fe33885405169c88664f3fb Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 20 Nov 2024 11:42:45 -0800 Subject: [PATCH 072/107] types: improve construction and accessing of inner fields --- crates/sui-sdk-types/src/types/checkpoint.rs | 14 ++++ .../src/types/crypto/multisig.rs | 9 ++- crates/sui-sdk-types/src/types/effects/v1.rs | 6 +- crates/sui-sdk-types/src/types/mod.rs | 4 +- crates/sui-sdk-types/src/types/object.rs | 75 +++++++++++++++++++ .../src/types/transaction/mod.rs | 2 +- 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/crates/sui-sdk-types/src/types/checkpoint.rs b/crates/sui-sdk-types/src/types/checkpoint.rs index 99caada7c..0fbb53916 100644 --- a/crates/sui-sdk-types/src/types/checkpoint.rs +++ b/crates/sui-sdk-types/src/types/checkpoint.rs @@ -122,6 +122,20 @@ pub struct CheckpointContents( pub Vec, ); +impl CheckpointContents { + pub fn new(transactions: Vec) -> Self { + Self(transactions) + } + + pub fn transactions(&self) -> &[CheckpointTransactionInfo] { + &self.0 + } + + pub fn into_v1(self) -> Vec { + self.0 + } +} + #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/crypto/multisig.rs b/crates/sui-sdk-types/src/types/crypto/multisig.rs index ebd1ef4ea..1e662751d 100644 --- a/crates/sui-sdk-types/src/types/crypto/multisig.rs +++ b/crates/sui-sdk-types/src/types/crypto/multisig.rs @@ -34,6 +34,10 @@ pub struct MultisigMember { } impl MultisigMember { + pub fn new(public_key: MultisigMemberPublicKey, weight: WeightUnit) -> Self { + Self { public_key, weight } + } + pub fn public_key(&self) -> &MultisigMemberPublicKey { &self.public_key } @@ -60,6 +64,10 @@ pub struct MultisigCommittee { } impl MultisigCommittee { + pub fn new(members: Vec, threshold: ThresholdUnit) -> Self { + Self { members, threshold } + } + pub fn members(&self) -> &[MultisigMember] { &self.members } @@ -131,7 +139,6 @@ impl MultisigAggregatedSignature { Self { signatures, bitmap, - legacy_bitmap: None, committee, } } diff --git a/crates/sui-sdk-types/src/types/effects/v1.rs b/crates/sui-sdk-types/src/types/effects/v1.rs index 605eaa06f..4ea8195f5 100644 --- a/crates/sui-sdk-types/src/types/effects/v1.rs +++ b/crates/sui-sdk-types/src/types/effects/v1.rs @@ -12,11 +12,11 @@ use crate::types::{ pub struct TransactionEffectsV1 { /// The status of the execution #[cfg_attr(feature = "schemars", schemars(flatten))] - status: ExecutionStatus, + pub status: ExecutionStatus, /// The epoch when this transaction was executed. #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] - epoch: EpochId, - gas_used: GasCostSummary, + pub epoch: EpochId, + pub gas_used: GasCostSummary, /// The transaction digest pub transaction_digest: TransactionDigest, /// The updated gas object reference, as an index into the `changed_objects` diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index 5ef35b30d..7042802b8 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -45,8 +45,8 @@ pub use execution_status::{ }; pub use gas::GasCostSummary; pub use object::{ - GenesisObject, MovePackage, Object, ObjectData, ObjectReference, ObjectType, Owner, TypeOrigin, - UpgradeInfo, Version, + GenesisObject, MovePackage, MoveStruct, Object, ObjectData, ObjectReference, ObjectType, Owner, + TypeOrigin, UpgradeInfo, Version, }; pub use object_id::ObjectId; #[cfg(feature = "serde")] diff --git a/crates/sui-sdk-types/src/types/object.rs b/crates/sui-sdk-types/src/types/object.rs index 531aeefcd..5d794fa8d 100644 --- a/crates/sui-sdk-types/src/types/object.rs +++ b/crates/sui-sdk-types/src/types/object.rs @@ -178,6 +178,7 @@ pub struct UpgradeInfo { } #[derive(Eq, PartialEq, Debug, Clone, Hash)] +// TODO hand-roll a Deserialize impl to enforce that an objectid is present #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) @@ -204,6 +205,42 @@ pub struct MoveStruct { pub contents: Vec, } +impl MoveStruct { + pub fn new( + type_: StructTag, + has_public_transfer: bool, + version: Version, + contents: Vec, + ) -> Option { + id_opt(&contents).map(|_| Self { + type_, + has_public_transfer, + version, + contents, + }) + } + + pub fn object_type(&self) -> &StructTag { + &self.type_ + } + + pub fn has_public_transfer(&self) -> bool { + self.has_public_transfer + } + + pub fn version(&self) -> Version { + self.version + } + + pub fn contents(&self) -> &[u8] { + &self.contents + } + + pub fn object_id(&self) -> ObjectId { + id_opt(self.contents()).unwrap() + } +} + /// Type of an IOTA object #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] pub enum ObjectType { @@ -229,6 +266,20 @@ pub struct Object { } impl Object { + pub fn new( + data: ObjectData, + owner: Owner, + previous_transaction: TransactionDigest, + storage_rebate: u64, + ) -> Self { + Self { + data, + owner, + previous_transaction, + storage_rebate, + } + } + pub fn object_id(&self) -> ObjectId { match &self.data { ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(), @@ -253,6 +304,18 @@ impl Object { pub fn owner(&self) -> &Owner { &self.owner } + + pub fn data(&self) -> &ObjectData { + &self.data + } + + pub fn previous_transaction(&self) -> TransactionDigest { + self.previous_transaction + } + + pub fn storage_rebate(&self) -> u64 { + self.storage_rebate + } } fn id_opt(contents: &[u8]) -> Option { @@ -273,6 +336,10 @@ pub struct GenesisObject { } impl GenesisObject { + pub fn new(data: ObjectData, owner: Owner) -> Self { + Self { data, owner } + } + pub fn object_id(&self) -> ObjectId { match &self.data { ObjectData::Struct(struct_) => id_opt(&struct_.contents).unwrap(), @@ -293,6 +360,14 @@ impl GenesisObject { ObjectData::Package(_) => ObjectType::Package, } } + + pub fn owner(&self) -> &Owner { + &self.owner + } + + pub fn data(&self) -> &ObjectData { + &self.data + } } // TODO improve ser/de to do borrowing to avoid clones where possible diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs index d7ca5cde9..7b430d3ef 100644 --- a/crates/sui-sdk-types/src/types/transaction/mod.rs +++ b/crates/sui-sdk-types/src/types/transaction/mod.rs @@ -157,7 +157,7 @@ pub struct AuthenticatorStateExpire { /// The initial version of the authenticator object that it was shared at. #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] - pub authenticator_obj_initial_shared_version: u64, + pub authenticator_object_initial_shared_version: u64, } #[derive(Clone, Debug, PartialEq, Eq)] From 0e8c4adc9ec2747f9a891a69bfc09530a75c64dd Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 20 Nov 2024 15:14:13 -0800 Subject: [PATCH 073/107] types: fix schema names for unresolved types --- .../src/types/transaction/unresolved.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs index 570d0e62e..f19cc0e3f 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-sdk-types/src/types/transaction/unresolved.rs @@ -6,7 +6,8 @@ use crate::types::{Address, ObjectDigest, ObjectId, object::Version}; // data. #[cfg_attr( feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedTransaction") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Transaction { @@ -20,7 +21,8 @@ pub struct Transaction { #[cfg_attr( feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedProgrammableTransaction") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ProgrammableTransaction { @@ -30,7 +32,8 @@ pub struct ProgrammableTransaction { #[cfg_attr( feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedGasPayment") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct GasPayment { @@ -65,7 +68,8 @@ pub struct GasPayment { #[derive(Clone, Debug)] #[cfg_attr( feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedObjectReference") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ObjectReference { @@ -88,6 +92,7 @@ pub struct ObjectReference { #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedInputKind"), serde(rename_all = "snake_case") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -109,7 +114,8 @@ pub enum InputKind { #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr( feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) + derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedInput"), )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Input { @@ -151,6 +157,7 @@ pub struct Input { #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize), + serde(rename = "UnresolvedValue"), serde(try_from = "serde_json::Value", into = "serde_json::Value") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema), schemars(untagged))] From cfec53d046bc50c6d44b74ba087a0c9725967d18 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:04:45 -0800 Subject: [PATCH 074/107] Fix issue with serde renaming (#63) --- crates/sui-sdk-types/src/types/transaction/unresolved.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs index f19cc0e3f..e64812881 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-sdk-types/src/types/transaction/unresolved.rs @@ -115,7 +115,7 @@ pub enum InputKind { #[cfg_attr( feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedInput"), + serde(rename = "UnresolvedInput") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Input { From 9f88b5a129fbf617f0297ffe90cd560fb11189f3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 3 Dec 2024 20:26:57 -0600 Subject: [PATCH 075/107] types: enable proptest::Arbitrary impls via the 'proptest' feature --- crates/sui-sdk-types/Cargo.toml | 12 ++- crates/sui-sdk-types/Makefile | 12 ++- crates/sui-sdk-types/src/hash.rs | 2 +- crates/sui-sdk-types/src/types/address.rs | 2 +- crates/sui-sdk-types/src/types/checkpoint.rs | 26 +++--- .../src/types/crypto/bls12381.rs | 4 +- .../sui-sdk-types/src/types/crypto/ed25519.rs | 4 +- crates/sui-sdk-types/src/types/crypto/mod.rs | 2 +- .../src/types/crypto/multisig.rs | 14 ++-- .../sui-sdk-types/src/types/crypto/passkey.rs | 2 +- .../src/types/crypto/secp256k1.rs | 4 +- .../src/types/crypto/secp256r1.rs | 4 +- .../src/types/crypto/signature.rs | 6 +- .../src/types/crypto/validator.rs | 10 +-- .../sui-sdk-types/src/types/crypto/zklogin.rs | 20 ++--- crates/sui-sdk-types/src/types/digest.rs | 4 +- crates/sui-sdk-types/src/types/effects/mod.rs | 2 +- crates/sui-sdk-types/src/types/effects/v1.rs | 22 ++--- crates/sui-sdk-types/src/types/events.rs | 6 +- .../src/types/execution_status.rs | 14 ++-- crates/sui-sdk-types/src/types/gas.rs | 2 +- crates/sui-sdk-types/src/types/object.rs | 20 ++--- crates/sui-sdk-types/src/types/object_id.rs | 2 +- .../src/types/transaction/mod.rs | 84 ++++++++++--------- .../sui-sdk-types/src/types/type_tag/mod.rs | 10 +-- 25 files changed, 149 insertions(+), 141 deletions(-) diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 1eddeae81..39328b8cc 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -35,6 +35,7 @@ serde = [ schemars = ["serde", "dep:schemars", "dep:serde_json"] rand = ["dep:rand_core"] hash = ["dep:blake2"] +proptest = ["dep:proptest", "dep:test-strategy", "serde"] [dependencies] base64ct = { version = "1.6.0", features = ["alloc"] } @@ -60,6 +61,10 @@ rand_core = { version = "0.6.4", optional = true } # Hash support blake2 = { version = "0.10.6", optional = true } +# proptest support +proptest = { version = "1.5.0", default-features = false, features = ["std"], optional = true } +test-strategy = { version = "0.4", optional = true } + [dev-dependencies] bcs = "0.1.6" jsonschema = { version = "0.20", default-features = false } @@ -67,13 +72,6 @@ num-bigint = "0.4.6" paste = "1.0.15" serde_json = "1.0.128" -# proptest support in tests -# -# Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments -# see https://github.com/proptest-rs/proptest/pull/270 for more info -proptest = { git = "https://github.com/bmwill/proptest.git", rev = "bc36db126183bce18c8bc595f0c0cfeac48b870c", default-features = false, features = ["std"] } -test-strategy = "0.4" - [target.wasm32-unknown-unknown.dev-dependencies] wasm-bindgen-test = "0.3" getrandom = { version = "0.2", features = ["js"] } diff --git a/crates/sui-sdk-types/Makefile b/crates/sui-sdk-types/Makefile index acc0c2322..ff15371b7 100644 --- a/crates/sui-sdk-types/Makefile +++ b/crates/sui-sdk-types/Makefile @@ -12,12 +12,20 @@ clippy: .PHONY: test test: - cargo nextest run --all-features + # Note on proptest: + # + # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments + # see https://github.com/proptest-rs/proptest/pull/270 for more info + cargo nextest run --all-features --config 'patch.crates-io.proptest.git="https://github.com/bmwill/proptest.git"' --config 'patch.crates-io.proptest.rev="0a140789d25f4999632b8b88aedb1e2ba056151d"' cargo test --doc .PHONY: wasm wasm: - CC=clang wasm-pack test --node --all-features + # Note on proptest: + # + # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments + # see https://github.com/proptest-rs/proptest/pull/270 for more info + CC=clang wasm-pack test --node --all-features --config 'patch.crates-io.proptest.git="https://github.com/bmwill/proptest.git"' --config 'patch.crates-io.proptest.rev="0a140789d25f4999632b8b88aedb1e2ba056151d"' %: $(MAKE) -C ../.. $@ diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/sui-sdk-types/src/hash.rs index 96f80b2a2..f31f9dc6a 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/sui-sdk-types/src/hash.rs @@ -280,7 +280,7 @@ mod signing_message { /// 0xf0 to ensure no hashing collision for any ObjectId vs Address which is /// derived as the hash of `flag || pubkey`. #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] #[repr(u8)] enum HashingIntent { #[cfg(feature = "serde")] diff --git a/crates/sui-sdk-types/src/types/address.rs b/crates/sui-sdk-types/src/types/address.rs index 028620d08..b17ea4376 100644 --- a/crates/sui-sdk-types/src/types/address.rs +++ b/crates/sui-sdk-types/src/types/address.rs @@ -3,7 +3,7 @@ feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Address( #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/checkpoint.rs b/crates/sui-sdk-types/src/types/checkpoint.rs index 0fbb53916..6525d45b9 100644 --- a/crates/sui-sdk-types/src/types/checkpoint.rs +++ b/crates/sui-sdk-types/src/types/checkpoint.rs @@ -16,7 +16,7 @@ pub type ProtocolVersion = u64; derive(schemars::JsonSchema), schemars(tag = "type", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum CheckpointCommitment { EcmhLiveObjectSet { digest: Digest }, // Other commitment types (e.g. merkle roots) go here. @@ -28,7 +28,7 @@ pub enum CheckpointCommitment { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct EndOfEpochData { /// next_epoch_committee is `Some` if and only if the current checkpoint is /// the last checkpoint of an epoch. @@ -56,7 +56,7 @@ pub struct EndOfEpochData { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CheckpointSummary { #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] pub epoch: EpochId, @@ -108,7 +108,7 @@ pub struct CheckpointSummary { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct SignedCheckpointSummary { pub checkpoint: CheckpointSummary, pub signature: ValidatorAggregatedSignature, @@ -116,9 +116,9 @@ pub struct SignedCheckpointSummary { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CheckpointContents( - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub Vec, ); @@ -142,11 +142,11 @@ impl CheckpointContents { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CheckpointTransactionInfo { pub transaction: TransactionDigest, pub effects: TransactionEffectsDigest, - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub signatures: Vec, } @@ -156,11 +156,11 @@ pub struct CheckpointTransactionInfo { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CheckpointData { pub checkpoint_summary: SignedCheckpointSummary, pub checkpoint_contents: CheckpointContents, - #[cfg_attr(test, any(proptest::collection::size_range(0..=1).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=1).lift()))] pub transactions: Vec, } @@ -170,7 +170,7 @@ pub struct CheckpointData { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CheckpointTransaction { /// The input Transaction #[cfg_attr( @@ -185,10 +185,10 @@ pub struct CheckpointTransaction { pub events: Option, /// The state of all inputs to this transaction as they were prior to /// execution. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub input_objects: Vec, /// The state of all output objects created or mutated by this transaction. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub output_objects: Vec, } diff --git a/crates/sui-sdk-types/src/types/crypto/bls12381.rs b/crates/sui-sdk-types/src/types/crypto/bls12381.rs index 5da099849..4f2dcff94 100644 --- a/crates/sui-sdk-types/src/types/crypto/bls12381.rs +++ b/crates/sui-sdk-types/src/types/crypto/bls12381.rs @@ -6,7 +6,7 @@ derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Bls12381PublicKey( #[cfg_attr( feature = "serde", @@ -107,7 +107,7 @@ impl std::fmt::Debug for Bls12381PublicKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Bls12381Signature( #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/crypto/ed25519.rs b/crates/sui-sdk-types/src/types/crypto/ed25519.rs index 9d42f868a..8a6c9e669 100644 --- a/crates/sui-sdk-types/src/types/crypto/ed25519.rs +++ b/crates/sui-sdk-types/src/types/crypto/ed25519.rs @@ -6,7 +6,7 @@ derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Ed25519PublicKey( #[cfg_attr( feature = "serde", @@ -105,7 +105,7 @@ impl std::fmt::Debug for Ed25519PublicKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Ed25519Signature( #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/crypto/mod.rs b/crates/sui-sdk-types/src/types/crypto/mod.rs index 65323093e..e909337ad 100644 --- a/crates/sui-sdk-types/src/types/crypto/mod.rs +++ b/crates/sui-sdk-types/src/types/crypto/mod.rs @@ -88,7 +88,7 @@ macro_rules! impl_base64_helper { #[allow(unused)] #[derive(Debug, PartialEq)] - #[cfg_attr(test, derive(test_strategy::Arbitrary))] + #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] struct $fromstr([u8; $base::LENGTH]); impl std::str::FromStr for $fromstr { diff --git a/crates/sui-sdk-types/src/types/crypto/multisig.rs b/crates/sui-sdk-types/src/types/crypto/multisig.rs index 1e662751d..c679a2e21 100644 --- a/crates/sui-sdk-types/src/types/crypto/multisig.rs +++ b/crates/sui-sdk-types/src/types/crypto/multisig.rs @@ -13,7 +13,7 @@ const MAX_COMMITTEE_SIZE: usize = 10; // const MAX_BITMAP_VALUE: BitmapUnit = 0b1111111111; #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum MultisigMemberPublicKey { Ed25519(Ed25519PublicKey), Secp256k1(Secp256k1PublicKey), @@ -27,7 +27,7 @@ pub enum MultisigMemberPublicKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MultisigMember { public_key: MultisigMemberPublicKey, weight: WeightUnit, @@ -53,10 +53,10 @@ impl MultisigMember { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MultisigCommittee { /// A list of committee members and their corresponding weight. - #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))] members: Vec, /// If the total weight of the public keys corresponding to verified /// signatures is larger than threshold, the Multisig is verified. @@ -114,13 +114,13 @@ impl MultisigCommittee { /// authenticating a Multisig. #[derive(Debug, Clone)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MultisigAggregatedSignature { /// The plain signature encoded with signature scheme. /// /// The signatures must be in the same order as they are listed in the /// committee. - #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))] signatures: Vec, /// A bitmap that indicates the position of which public key the signature /// should be authenticated with. @@ -167,7 +167,7 @@ impl PartialEq for MultisigAggregatedSignature { impl Eq for MultisigAggregatedSignature {} #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum MultisigMemberSignature { Ed25519(Ed25519Signature), Secp256k1(Secp256k1Signature), diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs index c1b76c429..92f81487f 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/types/crypto/passkey.rs @@ -277,7 +277,7 @@ mod serialization { } } -#[cfg(test)] +#[cfg(feature = "proptest")] impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { type Parameters = (); type Strategy = proptest::strategy::BoxedStrategy; diff --git a/crates/sui-sdk-types/src/types/crypto/secp256k1.rs b/crates/sui-sdk-types/src/types/crypto/secp256k1.rs index 928ffff70..94f556192 100644 --- a/crates/sui-sdk-types/src/types/crypto/secp256k1.rs +++ b/crates/sui-sdk-types/src/types/crypto/secp256k1.rs @@ -6,7 +6,7 @@ derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Secp256k1PublicKey( #[cfg_attr( feature = "serde", @@ -107,7 +107,7 @@ impl std::fmt::Debug for Secp256k1PublicKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Secp256k1Signature( #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/crypto/secp256r1.rs b/crates/sui-sdk-types/src/types/crypto/secp256r1.rs index 90d4baaa5..0e01918fc 100644 --- a/crates/sui-sdk-types/src/types/crypto/secp256r1.rs +++ b/crates/sui-sdk-types/src/types/crypto/secp256r1.rs @@ -24,7 +24,7 @@ impl Secp256r1PrivateKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Secp256r1PublicKey( #[cfg_attr( feature = "serde", @@ -125,7 +125,7 @@ impl std::fmt::Debug for Secp256r1PublicKey { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Secp256r1Signature( #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/types/crypto/signature.rs b/crates/sui-sdk-types/src/types/crypto/signature.rs index 966328cfa..b1eddc074 100644 --- a/crates/sui-sdk-types/src/types/crypto/signature.rs +++ b/crates/sui-sdk-types/src/types/crypto/signature.rs @@ -10,7 +10,7 @@ use super::{ derive(schemars::JsonSchema), schemars(tag = "scheme", rename_all = "lowercase") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum SimpleSignature { Ed25519 { signature: Ed25519Signature, @@ -37,7 +37,7 @@ impl SimpleSignature { } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] #[repr(u8)] pub enum SignatureScheme { Ed25519 = 0x00, @@ -120,7 +120,7 @@ impl std::fmt::Display for InvalidSignatureScheme { } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum UserSignature { Simple(SimpleSignature), Multisig(MultisigAggregatedSignature), diff --git a/crates/sui-sdk-types/src/types/crypto/validator.rs b/crates/sui-sdk-types/src/types/crypto/validator.rs index 12a36da24..a0916bd16 100644 --- a/crates/sui-sdk-types/src/types/crypto/validator.rs +++ b/crates/sui-sdk-types/src/types/crypto/validator.rs @@ -7,7 +7,7 @@ use crate::types::checkpoint::{EpochId, StakeUnit}; derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ValidatorCommittee { #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] @@ -21,7 +21,7 @@ pub struct ValidatorCommittee { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ValidatorCommitteeMember { #[cfg_attr(feature = "serde", serde(with = "ValidatorPublicKeySerialization"))] #[cfg_attr(feature = "schemars", schemars(with = "Bls12381PublicKey"))] @@ -37,7 +37,7 @@ pub struct ValidatorCommitteeMember { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ValidatorAggregatedSignature { #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] @@ -46,7 +46,7 @@ pub struct ValidatorAggregatedSignature { #[cfg_attr(feature = "serde", serde(with = "RoaringBitMapSerialization"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base64"))] #[cfg_attr( - test, + feature = "proptest", strategy(proptest::strategy::Just(roaring::RoaringBitmap::default())) )] pub bitmap: roaring::RoaringBitmap, @@ -98,7 +98,7 @@ impl<'de> serde_with::DeserializeAs<'de, Bls12381PublicKey> for BinaryValidatorP derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ValidatorSignature { #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] diff --git a/crates/sui-sdk-types/src/types/crypto/zklogin.rs b/crates/sui-sdk-types/src/types/crypto/zklogin.rs index f670faa19..06229c067 100644 --- a/crates/sui-sdk-types/src/types/crypto/zklogin.rs +++ b/crates/sui-sdk-types/src/types/crypto/zklogin.rs @@ -4,7 +4,7 @@ use crate::types::{checkpoint::EpochId, u256::U256}; /// An zk login authenticator with all the necessary fields. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ZkLoginAuthenticator { pub inputs: ZkLoginInputs, #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] @@ -20,7 +20,7 @@ pub struct ZkLoginAuthenticator { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ZkLoginInputs { pub proof_points: ZkLoginProof, pub iss_base64_details: Claim, @@ -35,7 +35,7 @@ pub struct ZkLoginInputs { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Claim { pub value: String, pub index_mod_4: u8, @@ -48,7 +48,7 @@ pub struct Claim { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ZkLoginProof { pub a: CircomG1, pub b: CircomG2, @@ -59,7 +59,7 @@ pub struct ZkLoginProof { /// canonical decimal representation of the projective coordinates in Fq. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CircomG1(pub [Bn254FieldElement; 3]); /// A G2 point in BN254 serialized as a vector of three vectors each being a @@ -67,14 +67,14 @@ pub struct CircomG1(pub [Bn254FieldElement; 3]); /// coefficients of the projective coordinates in Fq2. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CircomG2(pub [[Bn254FieldElement; 2]; 3]); /// A wrapper struct to retrofit in [enum PublicKey] for zkLogin. /// Useful to construct [struct MultiSigPublicKey]. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] // TODO ensure iss is less than 255 bytes long pub struct ZkLoginPublicIdentifier { iss: String, @@ -108,7 +108,7 @@ impl ZkLoginPublicIdentifier { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Jwk { /// Key type parameter, pub kty: String, @@ -127,7 +127,7 @@ pub struct Jwk { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct JwkId { /// iss string that identifies the OIDC provider. pub iss: String, @@ -137,7 +137,7 @@ pub struct JwkId { #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Bn254FieldElement( #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U256"))] [u8; 32], ); diff --git a/crates/sui-sdk-types/src/types/digest.rs b/crates/sui-sdk-types/src/types/digest.rs index 58383b5cb..963686ee8 100644 --- a/crates/sui-sdk-types/src/types/digest.rs +++ b/crates/sui-sdk-types/src/types/digest.rs @@ -5,7 +5,7 @@ derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Digest( #[cfg_attr(feature = "serde", serde(with = "DigestSerialization"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::Base58"))] @@ -194,7 +194,7 @@ macro_rules! impl_digest { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] - #[cfg_attr(test, derive(test_strategy::Arbitrary))] + #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct $t(Digest); impl $t { diff --git a/crates/sui-sdk-types/src/types/effects/mod.rs b/crates/sui-sdk-types/src/types/effects/mod.rs index 01ba3ee44..1e3cd753d 100644 --- a/crates/sui-sdk-types/src/types/effects/mod.rs +++ b/crates/sui-sdk-types/src/types/effects/mod.rs @@ -14,7 +14,7 @@ use crate::types::execution_status::ExecutionStatus; derive(schemars::JsonSchema), schemars(tag = "version") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum TransactionEffects { #[cfg_attr(feature = "schemars", schemars(rename = "1"))] V1(Box), diff --git a/crates/sui-sdk-types/src/types/effects/v1.rs b/crates/sui-sdk-types/src/types/effects/v1.rs index 4ea8195f5..962020eac 100644 --- a/crates/sui-sdk-types/src/types/effects/v1.rs +++ b/crates/sui-sdk-types/src/types/effects/v1.rs @@ -8,7 +8,7 @@ use crate::types::{ /// The response from processing a transaction or a certified transaction #[derive(Eq, PartialEq, Clone, Debug)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct TransactionEffectsV1 { /// The status of the execution #[cfg_attr(feature = "schemars", schemars(flatten))] @@ -27,21 +27,21 @@ pub struct TransactionEffectsV1 { /// can be None if the transaction does not emit any event. pub events_digest: Option, /// The set of transaction digests this transaction depends on. - #[cfg_attr(test, any(proptest::collection::size_range(0..=5).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=5).lift()))] pub dependencies: Vec, /// The version number of all the written Move objects by this transaction. #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] pub lamport_version: Version, /// Objects whose state are changed in the object store. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub changed_objects: Vec, /// Shared objects that are not mutated in this transaction. Unlike owned /// objects, read-only shared objects' version are not committed in the /// transaction, and in order for a node to catch up and execute it /// without consensus sequencing, the version needs to be committed in /// the effects. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub unchanged_shared_objects: Vec, /// Auxiliary data that are not protocol-critical, generated as part of the /// effects but are stored separately. Storing it separately allows us @@ -53,7 +53,7 @@ pub struct TransactionEffectsV1 { // XXX Do we maybe want to just fold "EffectsObjectChange" into this struct? #[derive(Eq, PartialEq, Clone, Debug)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ChangedObject { pub object_id: ObjectId, #[cfg_attr(feature = "schemars", schemars(flatten))] @@ -66,7 +66,7 @@ pub struct ChangedObject { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct UnchangedSharedObject { pub object_id: ObjectId, pub kind: UnchangedSharedKind, @@ -78,7 +78,7 @@ pub struct UnchangedSharedObject { derive(schemars::JsonSchema), schemars(tag = "kind", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum UnchangedSharedKind { /// Read-only shared objects from the input. We don't really need /// ObjectDigest for protocol correctness, but it will make it easier to @@ -115,7 +115,7 @@ pub enum UnchangedSharedKind { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct EffectsObjectChange { // input_state and output_state are the core fields that's required by // the protocol as it tells how an object changes on-chain. @@ -139,7 +139,7 @@ pub struct EffectsObjectChange { derive(schemars::JsonSchema), schemars(tag = "state", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum ObjectIn { NotExist, /// The old version, digest and owner. @@ -157,7 +157,7 @@ pub enum ObjectIn { derive(schemars::JsonSchema), schemars(tag = "state", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum ObjectOut { /// Same definition as in ObjectIn. NotExist, @@ -179,7 +179,7 @@ pub enum ObjectOut { serde(rename_all = "lowercase") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum IdOperation { None, Created, diff --git a/crates/sui-sdk-types/src/types/events.rs b/crates/sui-sdk-types/src/types/events.rs index 41cb8588b..398d0bf0b 100644 --- a/crates/sui-sdk-types/src/types/events.rs +++ b/crates/sui-sdk-types/src/types/events.rs @@ -6,7 +6,7 @@ use super::{Address, Identifier, ObjectId, StructTag, TypeTag}; derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct TransactionEvents(pub Vec); /// Specific type of event @@ -16,7 +16,7 @@ pub struct TransactionEvents(pub Vec); derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Event { pub package_id: ObjectId, pub module: Identifier, @@ -37,7 +37,7 @@ pub struct Event { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct BalanceChange { /// Owner of the balance change pub address: Address, diff --git a/crates/sui-sdk-types/src/types/execution_status.rs b/crates/sui-sdk-types/src/types/execution_status.rs index 3c54e103c..22bce2cc3 100644 --- a/crates/sui-sdk-types/src/types/execution_status.rs +++ b/crates/sui-sdk-types/src/types/execution_status.rs @@ -1,7 +1,7 @@ use super::{Address, Digest, Identifier, ObjectId}; #[derive(Eq, PartialEq, Clone, Debug)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum ExecutionStatus { Success, /// Gas used in the failed case, and the error. @@ -9,7 +9,7 @@ pub enum ExecutionStatus { /// The error error: ExecutionError, /// Which command the error occurred - #[cfg_attr(test, map(|x: Option| x.map(Into::into)))] + #[cfg_attr(feature = "proptest", map(|x: Option| x.map(Into::into)))] command: Option, }, } @@ -25,7 +25,7 @@ pub type TypeParameterIndex = u16; derive(schemars::JsonSchema), schemars(tag = "error", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum ExecutionError { // General transaction errors /// Insufficient Gas @@ -175,7 +175,7 @@ pub enum ExecutionError { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MoveLocation { pub package: ObjectId, pub module: Identifier, @@ -192,7 +192,7 @@ pub struct MoveLocation { derive(schemars::JsonSchema), schemars(tag = "kind", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum CommandArgumentError { /// The type of the value does not match the expected type TypeMismatch, @@ -233,7 +233,7 @@ pub enum CommandArgumentError { derive(schemars::JsonSchema), schemars(tag = "kind", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum PackageUpgradeError { /// Unable to fetch package UnableToFetchPackage { package_id: ObjectId }, @@ -263,7 +263,7 @@ pub enum PackageUpgradeError { derive(schemars::JsonSchema), schemars(rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum TypeArgumentError { /// A type was not found in the module specified TypeNotFound, diff --git a/crates/sui-sdk-types/src/types/gas.rs b/crates/sui-sdk-types/src/types/gas.rs index e810d7df6..b41e1e5fc 100644 --- a/crates/sui-sdk-types/src/types/gas.rs +++ b/crates/sui-sdk-types/src/types/gas.rs @@ -29,7 +29,7 @@ derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct GasCostSummary { /// Cost of computation/execution #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] diff --git a/crates/sui-sdk-types/src/types/object.rs b/crates/sui-sdk-types/src/types/object.rs index 5d794fa8d..d1f2617cc 100644 --- a/crates/sui-sdk-types/src/types/object.rs +++ b/crates/sui-sdk-types/src/types/object.rs @@ -10,7 +10,7 @@ pub type Version = u64; derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ObjectReference { object_id: ObjectId, #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -58,7 +58,7 @@ impl ObjectReference { serde(rename_all = "lowercase") )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum Owner { /// # Address Owned /// Object is exclusively owned by a single address, and is mutable. @@ -85,7 +85,7 @@ pub enum Owner { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[allow(clippy::large_enum_variant)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] // TODO think about hiding this type and not exposing it pub enum ObjectData { /// An object whose governing logic lives in a published Move module @@ -102,7 +102,7 @@ pub enum ObjectData { feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MovePackage { pub id: ObjectId, /// Most move packages are uniquely identified by their ID (i.e. there is @@ -153,7 +153,7 @@ pub struct MovePackage { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct TypeOrigin { pub module_name: Identifier, pub struct_name: Identifier, @@ -167,7 +167,7 @@ pub struct TypeOrigin { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct UpgradeInfo { /// Id of the upgraded packages pub upgraded_id: ObjectId, @@ -183,7 +183,7 @@ pub struct UpgradeInfo { feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize) )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MoveStruct { /// The type of this object. Immutable #[cfg_attr( @@ -201,7 +201,7 @@ pub struct MoveStruct { feature = "serde", serde(with = "::serde_with::As::<::serde_with::Bytes>") )] - #[cfg_attr(test, any(proptest::collection::size_range(32..=1024).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(32..=1024).lift()))] pub contents: Vec, } @@ -251,7 +251,7 @@ pub enum ObjectType { } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Object { /// The meat of the object pub data: ObjectData, @@ -329,7 +329,7 @@ fn id_opt(contents: &[u8]) -> Option { } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct GenesisObject { pub data: ObjectData, pub owner: Owner, diff --git a/crates/sui-sdk-types/src/types/object_id.rs b/crates/sui-sdk-types/src/types/object_id.rs index dedc5874f..89c8288c0 100644 --- a/crates/sui-sdk-types/src/types/object_id.rs +++ b/crates/sui-sdk-types/src/types/object_id.rs @@ -6,7 +6,7 @@ use super::Address; derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ObjectId(Address); impl ObjectId { diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs index 7b430d3ef..09e2349b7 100644 --- a/crates/sui-sdk-types/src/types/transaction/mod.rs +++ b/crates/sui-sdk-types/src/types/transaction/mod.rs @@ -14,7 +14,7 @@ pub(crate) use serialization::SignedTransactionWithIntentMessage; pub mod unresolved; #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Transaction { pub kind: TransactionKind, pub sender: Address, @@ -28,14 +28,14 @@ pub struct Transaction { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct SignedTransaction { pub transaction: Transaction, pub signatures: Vec, } #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum TransactionExpiration { /// The transaction has no expiration #[default] @@ -51,7 +51,7 @@ pub enum TransactionExpiration { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct GasPayment { pub objects: Vec, pub owner: Address, @@ -69,7 +69,7 @@ pub struct GasPayment { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct RandomnessStateUpdate { /// Epoch of the randomness state update transaction #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -95,7 +95,7 @@ pub struct RandomnessStateUpdate { } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum TransactionKind { /// A transaction that allows the interleaving of native commands and Move /// calls @@ -127,7 +127,7 @@ pub enum TransactionKind { derive(schemars::JsonSchema), schemars(tag = "kind", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum EndOfEpochTransactionKind { ChangeEpoch(ChangeEpoch), ChangeEpochV2(ChangeEpochV2), @@ -148,7 +148,7 @@ pub enum EndOfEpochTransactionKind { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct AuthenticatorStateExpire { /// expire JWKs that have a lower epoch than this #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -166,7 +166,7 @@ pub struct AuthenticatorStateExpire { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct AuthenticatorStateUpdateV1 { /// Epoch of the authenticator state update transaction #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -192,7 +192,7 @@ pub struct AuthenticatorStateUpdateV1 { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ActiveJwk { pub jwk_id: JwkId, pub jwk: Jwk, @@ -208,11 +208,11 @@ pub struct ActiveJwk { derive(schemars::JsonSchema), schemars(tag = "kind", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum ConsensusDeterminedVersionAssignments { /// Cancelled transaction version assignment. CancelledTransactions { - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] cancelled_transactions: Vec, }, } @@ -223,10 +223,10 @@ pub enum ConsensusDeterminedVersionAssignments { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct CancelledTransaction { pub digest: TransactionDigest, - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub version_assignments: Vec, } @@ -236,7 +236,7 @@ pub struct CancelledTransaction { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct VersionAssignment { pub object_id: ObjectId, #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -250,7 +250,7 @@ pub struct VersionAssignment { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ConsensusCommitPrologueV1 { /// Epoch of the commit prologue transaction #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -284,7 +284,7 @@ pub struct ConsensusCommitPrologueV1 { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ChangeEpoch { /// The next (to become) epoch ID. #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -320,7 +320,7 @@ pub struct ChangeEpoch { /// write out the modules below. Modules are provided with the version they /// will be upgraded to, their modules in serialized form (which include /// their package ID), and a list of their transitive dependencies. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub system_packages: Vec, } @@ -331,6 +331,7 @@ pub struct ChangeEpoch { )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ChangeEpochV2 { /// The next (to become) epoch ID. #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] @@ -381,6 +382,7 @@ pub struct ChangeEpochV2 { )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct SystemPackage { #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] @@ -392,7 +394,7 @@ pub struct SystemPackage { ) )] #[cfg_attr(feature = "schemars", schemars(with = "Vec"))] - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub modules: Vec>, pub dependencies: Vec, } @@ -403,11 +405,11 @@ pub struct SystemPackage { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct GenesisTransaction { - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub objects: Vec, - #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))] pub events: Vec, } @@ -419,14 +421,14 @@ pub struct GenesisTransaction { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ProgrammableTransaction { /// Input objects or primitive values - #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))] pub inputs: Vec, /// The commands to be executed sequentially. A failure in any command will /// result in the failure of the entire transaction. - #[cfg_attr(test, any(proptest::collection::size_range(0..=10).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=10).lift()))] pub commands: Vec, } @@ -436,7 +438,7 @@ pub struct ProgrammableTransaction { derive(schemars::JsonSchema), schemars(tag = "type", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum Input { // contains no structs or objects Pure { @@ -465,7 +467,7 @@ pub enum Input { derive(schemars::JsonSchema), schemars(tag = "command", rename_all = "snake_case") )] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum Command { /// A call to either an entry or a public Move function MoveCall(MoveCall), @@ -504,9 +506,9 @@ pub enum Command { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct TransferObjects { - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub objects: Vec, pub address: Argument, } @@ -517,10 +519,10 @@ pub struct TransferObjects { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct SplitCoins { pub coin: Argument, - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub amounts: Vec, } @@ -530,10 +532,10 @@ pub struct SplitCoins { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MergeCoins { pub coin: Argument, - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub coins_to_merge: Vec, } @@ -543,7 +545,7 @@ pub struct MergeCoins { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Publish { #[cfg_attr( feature = "serde", @@ -562,11 +564,11 @@ pub struct Publish { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MakeMoveVector { #[cfg_attr(feature = "serde", serde(rename = "type"))] pub type_: Option, - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub elements: Vec, } @@ -576,7 +578,7 @@ pub struct MakeMoveVector { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Upgrade { #[cfg_attr( feature = "serde", @@ -593,7 +595,7 @@ pub struct Upgrade { /// An argument to a programmable transaction command #[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum Argument { /// The gas coin. The gas coin can only be used by-ref, except for with /// `TransferObjects`, which can use it by-value. @@ -629,7 +631,7 @@ impl Argument { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct MoveCall { /// The package containing the module and function. pub package: ObjectId, @@ -638,9 +640,9 @@ pub struct MoveCall { /// The function to be called. pub function: Identifier, /// The type arguments to the function. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub type_arguments: Vec, /// The arguments to the function. - #[cfg_attr(test, any(proptest::collection::size_range(0..=2).lift()))] + #[cfg_attr(feature = "proptest", any(proptest::collection::size_range(0..=2).lift()))] pub arguments: Vec, } diff --git a/crates/sui-sdk-types/src/types/type_tag/mod.rs b/crates/sui-sdk-types/src/types/type_tag/mod.rs index 97d244da9..3f50f64c8 100644 --- a/crates/sui-sdk-types/src/types/type_tag/mod.rs +++ b/crates/sui-sdk-types/src/types/type_tag/mod.rs @@ -7,7 +7,7 @@ mod serialization; use super::Address; #[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Hash)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub enum TypeTag { U8, U16, @@ -18,7 +18,7 @@ pub enum TypeTag { Bool, Address, Signer, - #[cfg_attr(test, weight(0))] + #[cfg_attr(feature = "proptest", weight(0))] Vector(Box), Struct(Box), } @@ -65,7 +65,7 @@ impl std::fmt::Display for TypeParseError { impl std::error::Error for TypeParseError {} #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Identifier( #[cfg_attr( test, @@ -118,12 +118,12 @@ impl PartialEq for Identifier { } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] +#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct StructTag { pub address: Address, pub module: Identifier, pub name: Identifier, - #[cfg_attr(test, strategy(proptest::strategy::Just(Vec::new())))] + #[cfg_attr(feature = "proptest", strategy(proptest::strategy::Just(Vec::new())))] pub type_params: Vec, } From 4a1aed06abd755f6b12a7053876fe48374ecbb8f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 3 Dec 2024 21:17:00 -0600 Subject: [PATCH 076/107] types: fix Arbitrary impl for Identifier --- crates/sui-sdk-types/src/types/object.rs | 4 ++-- crates/sui-sdk-types/src/types/type_tag/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/sui-sdk-types/src/types/object.rs b/crates/sui-sdk-types/src/types/object.rs index d1f2617cc..00195c0db 100644 --- a/crates/sui-sdk-types/src/types/object.rs +++ b/crates/sui-sdk-types/src/types/object.rs @@ -124,7 +124,7 @@ pub struct MovePackage { serde(with = "::serde_with::As::>") )] #[cfg_attr( - test, + feature = "proptest", strategy( proptest::collection::btree_map(proptest::arbitrary::any::(), proptest::collection::vec(proptest::arbitrary::any::(), 0..=1024), 0..=5) ) @@ -138,7 +138,7 @@ pub struct MovePackage { // For each dependency, maps original package ID to the info about the (upgraded) dependency // version that this package is using #[cfg_attr( - test, + feature = "proptest", strategy( proptest::collection::btree_map(proptest::arbitrary::any::(), proptest::arbitrary::any::(), 0..=5) ) diff --git a/crates/sui-sdk-types/src/types/type_tag/mod.rs b/crates/sui-sdk-types/src/types/type_tag/mod.rs index 3f50f64c8..b397098fa 100644 --- a/crates/sui-sdk-types/src/types/type_tag/mod.rs +++ b/crates/sui-sdk-types/src/types/type_tag/mod.rs @@ -68,7 +68,7 @@ impl std::error::Error for TypeParseError {} #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Identifier( #[cfg_attr( - test, + feature = "proptest", strategy(proptest::strategy::Strategy::prop_map( "[a-zA-Z][a-zA-Z0-9_]{0,127}", Into::into From 1a6b0ae8bb755b6b114e798f4eaef7e7acc0d9b2 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 5 Dec 2024 06:37:53 -0800 Subject: [PATCH 077/107] sui-sdk-types: address rust 1.82 clippy lints (#65) --- crates/sui-sdk-types/src/hash.rs | 2 +- crates/sui-sdk-types/src/types/crypto/zklogin.rs | 2 +- crates/sui-sdk-types/src/types/object.rs | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/sui-sdk-types/src/hash.rs index f31f9dc6a..fcef27d14 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/sui-sdk-types/src/hash.rs @@ -263,7 +263,7 @@ mod signing_message { hasher.finalize() } - impl<'a> PersonalMessage<'a> { + impl PersonalMessage<'_> { pub fn signing_digest(&self) -> SigningDigest { const INTENT: Intent = Intent { scope: IntentScope::PersonalMessage, diff --git a/crates/sui-sdk-types/src/types/crypto/zklogin.rs b/crates/sui-sdk-types/src/types/crypto/zklogin.rs index 06229c067..c0cd029fe 100644 --- a/crates/sui-sdk-types/src/types/crypto/zklogin.rs +++ b/crates/sui-sdk-types/src/types/crypto/zklogin.rs @@ -485,7 +485,7 @@ mod serialization { struct Inner<'a>(&'a [Bn254FieldElement; 2]); - impl<'a> Serialize for Inner<'a> { + impl Serialize for Inner<'_> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, diff --git a/crates/sui-sdk-types/src/types/object.rs b/crates/sui-sdk-types/src/types/object.rs index 00195c0db..28184fefa 100644 --- a/crates/sui-sdk-types/src/types/object.rs +++ b/crates/sui-sdk-types/src/types/object.rs @@ -720,6 +720,9 @@ mod serialization { ReadableObjectData::Move(ReadableMoveStruct { contents }), ) => { // check id matches in contents + // switch to if id_opt(&contents).is_none_or(|id| id != object_id) when the + // API of is_none_or is stabilized as now this would fail in wasm tests + #[allow(clippy::nonminimal_bool)] if !id_opt(&contents).is_some_and(|id| id == object_id) { return Err(serde::de::Error::custom("id from contents doesn't match")); } @@ -874,6 +877,9 @@ mod serialization { ReadableObjectData::Move(ReadableMoveStruct { contents }), ) => { // check id matches in contents + // switch to if id_opt(&contents).is_none_or(|id| id != object_id) when the + // API of is_none_or is stabilized as now this would fail in wasm tests + #[allow(clippy::nonminimal_bool)] if !id_opt(&contents).is_some_and(|id| id == object_id) { return Err(serde::de::Error::custom("id from contents doesn't match")); } From c6cbb510ca4412d2ca7d040994c87622dc7ce20b Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 5 Dec 2024 11:48:08 -0800 Subject: [PATCH 078/107] sui-transaction-builder: introduce a crate for building transaction (#41) --- .github/workflows/ci.yml | 18 + Makefile | 7 +- .../src/query_types/object.rs | 2 +- .../src/types/transaction/unresolved.rs | 100 +- crates/sui-transaction-builder/.gitignore | 1 + crates/sui-transaction-builder/Cargo.toml | 26 + crates/sui-transaction-builder/src/error.rs | 40 + crates/sui-transaction-builder/src/lib.rs | 944 ++++++++++++++++++ .../tests/test_example_v1/Move.toml | 37 + .../test_example_v1/sources/test_example.move | 16 + .../tests/test_example_v2/Move.toml | 37 + .../test_example_v2/sources/test_example.move | 25 + 12 files changed, 1227 insertions(+), 26 deletions(-) create mode 100644 crates/sui-transaction-builder/.gitignore create mode 100644 crates/sui-transaction-builder/Cargo.toml create mode 100644 crates/sui-transaction-builder/src/error.rs create mode 100644 crates/sui-transaction-builder/src/lib.rs create mode 100644 crates/sui-transaction-builder/tests/test_example_v1/Move.toml create mode 100644 crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move create mode 100644 crates/sui-transaction-builder/tests/test_example_v2/Move.toml create mode 100644 crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 258dfbf6c..e650a836c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,9 +118,27 @@ jobs: echo "Downloading testnet binary from $download_url" wget -q $download_url -O sui.tgz tar -zxvf sui.tgz ./sui + chmod +x ./sui echo "Starting local network with a faucet, an indexer (port 5432) and GraphQL. Epoch duration is set to $EPOCH_DURATION_MS ms" + echo "$(pwd)" >> $GITHUB_PATH # we need it on the path for calling sui move build for some tests ./sui start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name sui_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & + - name: Set up the CLI environment (need a client.yaml for calling some Sui commands) + shell: bash + run: | + mkdir -p $HOME/.sui/sui_config + tee $HOME/.sui/sui_config/client.yaml < ../../$@ + .PHONY: test-with-localnet -test-with-localnet: - cargo nextest run -p sui-graphql-client +test-with-localnet: package_test_example_v1.json package_test_example_v2.json + cargo nextest run -p sui-graphql-client -p sui-transaction-builder .PHONY: wasm wasm: diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/sui-graphql-client/src/query_types/object.rs index f8b87677b..e708ec058 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/sui-graphql-client/src/query_types/object.rs @@ -55,7 +55,7 @@ pub struct Object { pub bcs: Option, } -#[derive(Clone, cynic::InputObject, Debug)] +#[derive(Clone, Default, cynic::InputObject, Debug)] #[cynic(schema = "rpc", graphql_type = "ObjectFilter")] pub struct ObjectFilter<'a> { #[cynic(rename = "type")] diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-sdk-types/src/types/transaction/unresolved.rs index e64812881..af3e71782 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-sdk-types/src/types/transaction/unresolved.rs @@ -227,61 +227,92 @@ impl Input { } /// Set the object kind to immutable. - pub fn with_immutable_kind(&mut self) { - self.kind = Some(InputKind::ImmutableOrOwned); + pub fn with_immutable_kind(self) -> Self { + Self { + kind: Some(InputKind::ImmutableOrOwned), + ..self + } } /// Set the object kind to owned. - pub fn with_owned_kind(&mut self) { - self.kind = Some(InputKind::ImmutableOrOwned); + pub fn with_owned_kind(self) -> Self { + Self { + kind: Some(InputKind::ImmutableOrOwned), + ..self + } } /// Set the object kind to receiving. - pub fn with_receiving_kind(&mut self) { - self.kind = Some(InputKind::Receiving); + pub fn with_receiving_kind(self) -> Self { + Self { + kind: Some(InputKind::Receiving), + ..self + } } /// Set the object kind to shared. - pub fn with_shared_kind(&mut self) { - self.kind = Some(InputKind::Shared); + pub fn with_shared_kind(self) -> Self { + Self { + kind: Some(InputKind::Shared), + ..self + } } /// Set the specified version. - pub fn with_version(&mut self, version: u64) { - self.version = Some(version); + pub fn with_version(self, version: u64) -> Self { + Self { + version: Some(version), + ..self + } } /// Set the specified digest. - pub fn with_digest(&mut self, digest: ObjectDigest) { - self.digest = Some(digest); + pub fn with_digest(self, digest: ObjectDigest) -> Self { + Self { + digest: Some(digest), + ..self + } } // Shared fields /// Set the initial shared version. - pub fn with_initial_shared_version(&mut self, initial: u64) { - self.version = Some(initial); + pub fn with_initial_shared_version(self, initial_version: u64) -> Self { + Self { + kind: Some(InputKind::Shared), + version: Some(initial_version), + ..self + } } /// Make the object shared and set `mutable` to true when the input is used /// by value. - pub fn by_val(&mut self) { - self.kind = Some(InputKind::Shared); - self.mutable = Some(true); + pub fn by_val(self) -> Self { + Self { + kind: Some(InputKind::Shared), + mutable: Some(true), + ..self + } } /// Make the object shared and set `mutable` to false when the input is used /// by reference. - pub fn by_ref(&mut self) { - self.kind = Some(InputKind::Shared); - self.mutable = Some(false); + pub fn by_ref(self) -> Self { + Self { + kind: Some(InputKind::Shared), + mutable: Some(false), + ..self + } } /// Make the object shared and set `mutable` to true when the input is used /// by mutable reference. - pub fn by_mut(&mut self) { - self.kind = Some(InputKind::Shared); - self.mutable = Some(true); + pub fn by_mut(self) -> Self { + Self { + kind: Some(InputKind::Shared), + mutable: Some(true), + ..self + } } } @@ -323,3 +354,26 @@ impl From for serde_json::Value { } } } + +#[cfg(all(feature = "serde", feature = "hash"))] +impl From<&crate::types::Object> for Input { + fn from(object: &crate::types::Object) -> Self { + use crate::types::object::Owner; + + let input = Input::by_id(object.object_id()) + .with_digest(object.digest()) + .with_version(object.version()); + match object.owner() { + Owner::Address(_) => input, + Owner::Object(_) => input, + Owner::Shared(at_version) => input.with_initial_shared_version(*at_version), + Owner::Immutable => input.with_immutable_kind(), + } + } +} + +impl From for Input { + fn from(object_id: ObjectId) -> Self { + Input::by_id(object_id) + } +} diff --git a/crates/sui-transaction-builder/.gitignore b/crates/sui-transaction-builder/.gitignore new file mode 100644 index 000000000..1697b39d4 --- /dev/null +++ b/crates/sui-transaction-builder/.gitignore @@ -0,0 +1 @@ +package_test_example*.json diff --git a/crates/sui-transaction-builder/Cargo.toml b/crates/sui-transaction-builder/Cargo.toml new file mode 100644 index 000000000..bd0948206 --- /dev/null +++ b/crates/sui-transaction-builder/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "sui-transaction-builder" +version = "0.1.0" +authors = ["Stefan Stanciulescu ", "Brandon Williams "] +license = "Apache-2.0" +edition = "2021" +publish = false +readme = "README.md" +description = "Transaction API for the Rust SDK for the Sui Blockchain" + +[dependencies] +base64ct = "1.6" +bcs = "0.1.6" +serde = { version = "1.0", features = ["derive"] } +sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "hash"] } +sui-graphql-client = { package = "sui-graphql-client", path = "../sui-graphql-client" } +thiserror = "2.0" + +[dev-dependencies] +anyhow = "1.0" +rand = "0.8" +serde_json = "1.0" +sui-crypto = { package = "sui-crypto", path = "../sui-crypto" , features = ["ed25519"] } +sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["rand"] } +tokio = { version = "1.0", features = ["full"] } + diff --git a/crates/sui-transaction-builder/src/error.rs b/crates/sui-transaction-builder/src/error.rs new file mode 100644 index 000000000..bc98d635e --- /dev/null +++ b/crates/sui-transaction-builder/src/error.rs @@ -0,0 +1,40 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use base64ct::Error as Base64Error; +use sui_types::types::ObjectId; + +#[derive(thiserror::Error, Debug, Clone)] +#[non_exhaustive] +pub enum Error { + #[error("Conversion error due to input issue: {0}")] + Input(String), + #[error("Gas object should be an immutable or owned object")] + WrongGasObject, + #[error("Decoding error: {0}")] + Decoding(#[from] Base64Error), + #[error("Missing object id")] + MissingObjectId, + #[error("Missing version for object {0}")] + MissingVersion(ObjectId), + #[error("Missing digest for object {0}")] + MissingDigest(ObjectId), + #[error("Missing sender")] + MissingSender, + #[error("Missing gas objects")] + MissingGasObjects, + #[error("Missing gas budget")] + MissingGasBudget, + #[error("Missing gas price")] + MissingGasPrice, + #[error("Missing object kind for object {0}")] + MissingObjectKind(ObjectId), + #[error("Missing initial shared version for object {0}")] + MissingInitialSharedVersion(ObjectId), + #[error("Missing pure value")] + MissingPureValue, + #[error("Unknown shared object mutability for object {0}")] + SharedObjectMutability(ObjectId), + #[error("Unsupported literal")] + UnsupportedLiteral, +} diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs new file mode 100644 index 000000000..281e812bc --- /dev/null +++ b/crates/sui-transaction-builder/src/lib.rs @@ -0,0 +1,944 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +mod error; + +use error::Error; +use sui_types::types::unresolved; +use sui_types::types::Address; +use sui_types::types::Argument; +use sui_types::types::Command; +use sui_types::types::GasPayment; +use sui_types::types::Identifier; +use sui_types::types::Input; +use sui_types::types::MakeMoveVector; +use sui_types::types::MergeCoins; +use sui_types::types::MoveCall; +use sui_types::types::ObjectId; +use sui_types::types::ObjectReference; +use sui_types::types::Publish; +use sui_types::types::SplitCoins; +use sui_types::types::Transaction; +use sui_types::types::TransactionExpiration; +use sui_types::types::TransferObjects; +use sui_types::types::TypeTag; +use sui_types::types::Upgrade; + +use base64ct::Encoding; +use serde::Serialize; + +/// A builder for creating transactions. Use [`resolve`] to finalize the transaction data. +#[derive(Clone, Default, Debug)] +pub struct TransactionBuilder { + /// The inputs to the transaction. + inputs: Vec, + /// The list of commands in the transaction. A command is a single operation in a programmable + /// transaction. + commands: Vec, + /// The gas objects that will be used to pay for the transaction. The most common way is to + /// use [`unresolved::Input::owned`] function to create a gas object and use the [`add_gas`] + /// method to set the gas objects. + gas: Vec, + /// The gas budget for the transaction. + gas_budget: Option, + /// The gas price for the transaction. + gas_price: Option, + /// The sender of the transaction. + sender: Option
, + /// The sponsor of the transaction. If None, the sender is also the sponsor. + sponsor: Option
, + /// The expiration of the transaction. The default value of this type is no expiration. + expiration: TransactionExpiration, +} + +/// A transaction input that bypasses serialization. The input contents is already BCS serialized +/// and is put verbatim into the transaction. +struct RawBytes(Vec); + +/// A transaction input that will be serialized from BCS. +pub struct Serialized<'a, T: Serialize>(pub &'a T); + +/// A separate type to support denoting a function by a more structured representation. +pub struct Function { + /// The package that contains the module with the function. + package: Address, + /// The module that contains the function. + module: Identifier, + /// The function name. + function: Identifier, + /// The type arguments for the function. + type_args: Vec, +} + +/// A transaction builder to build transactions. +impl TransactionBuilder { + /// Create a new transaction builder and initialize its elements to default. + pub fn new() -> Self { + Self::default() + } + + // Transaction Inputs + + /// Make a value available to the transaction as an input. + pub fn input(&mut self, i: impl Into) -> Argument { + let input = i.into(); + self.inputs.push(input); + Argument::Input((self.inputs.len() - 1) as u16) + } + + /// Return the argument to be the gas object. + pub fn gas(&self) -> Argument { + Argument::Gas + } + + // Metadata + + /// Add one or more gas objects to use to pay for the transaction. + /// + /// Most commonly the gas can be passed as a reference to an owned/immutable [`Object`], + /// or can created using one of the of the constructors of the [`unresolved::Input`] enum, + /// e.g., [`unresolved::Input::owned`]. + pub fn add_gas_objects(&mut self, gas: I) + where + O: Into, + I: IntoIterator, + { + self.gas.extend(gas.into_iter().map(|x| x.into())); + } + + /// Set the gas budget for the transaction. + pub fn set_gas_budget(&mut self, budget: u64) { + self.gas_budget = Some(budget); + } + + /// Set the gas price for the transaction. + pub fn set_gas_price(&mut self, price: u64) { + self.gas_price = Some(price); + } + + /// Set the sender of the transaction. + pub fn set_sender(&mut self, sender: Address) { + self.sender = Some(sender); + } + + /// Set the sponsor of the transaction. + pub fn set_sponsor(&mut self, sponsor: Address) { + self.sponsor = Some(sponsor); + } + + /// Set the expiration of the transaction to be a specific epoch. + pub fn set_expiration(&mut self, epoch: u64) { + self.expiration = TransactionExpiration::Epoch(epoch); + } + + // Commands + + /// Call a Move function with the given arguments. + /// + /// - `function` is a structured representation of a package::module::function argument, + /// optionally with type arguments. + /// + /// The return value is a result argument that can be used in subsequent commands. + /// If the move call returns multiple results, you can access them using the + /// [`Argument::nested`] method. + pub fn move_call(&mut self, function: Function, arguments: Vec) -> Argument { + let cmd = Command::MoveCall(MoveCall { + package: function.package.into(), + module: function.module, + function: function.function, + type_arguments: function.type_args, + arguments, + }); + self.commands.push(cmd); + Argument::Result(self.commands.len() as u16 - 1) + } + + /// Transfer a list of objects to the given address, without producing any result. + pub fn transfer_objects(&mut self, objects: Vec, address: Argument) { + let cmd = Command::TransferObjects(TransferObjects { objects, address }); + self.commands.push(cmd); + } + + /// Split a coin by the provided amounts, returning multiple results (as many as there are + /// amounts). To access the results, use the [`Argument::nested`] method to access the desired + /// coin by its index. + pub fn split_coins(&mut self, coin: Argument, amounts: Vec) -> Argument { + let cmd = Command::SplitCoins(SplitCoins { coin, amounts }); + self.commands.push(cmd); + Argument::Result(self.commands.len() as u16 - 1) + } + + /// Merge a list of coins into a single coin, without producing any result. + pub fn merge_coins(&mut self, coin: Argument, coins_to_merge: Vec) { + let cmd = Command::MergeCoins(MergeCoins { + coin, + coins_to_merge, + }); + self.commands.push(cmd); + } + + /// Make a move vector from a list of elements. If the elements are not objects, or the vector + /// is empty, a type must be supplied. + /// It returns the Move vector as an argument, that can be used in subsequent commands. + pub fn make_move_vec(&mut self, type_: Option, elements: Vec) -> Argument { + let cmd = Command::MakeMoveVector(MakeMoveVector { type_, elements }); + self.commands.push(cmd); + Argument::Result(self.commands.len() as u16 - 1) + } + + /// Publish a list of modules with the given dependencies. The result is the + /// `0x2::package::UpgradeCap` Move type. Note that the upgrade capability needs to be handled + /// after this call: + /// - transfer it to the transaction sender or another address + /// - burn it + /// - wrap it for access control + /// - discard the it to make a package immutable + /// + /// The arguments required for this command are: + /// - `modules`: is the modules' bytecode to be published + /// - `dependencies`: is the list of IDs of the transitive dependencies of the package + pub fn publish(&mut self, modules: Vec>, dependencies: Vec) -> Argument { + let cmd = Command::Publish(Publish { + modules, + dependencies, + }); + self.commands.push(cmd); + Argument::Result(self.commands.len() as u16 - 1) + } + + /// Upgrade a Move package. + /// + /// - `modules`: is the modules' bytecode for the modules to be published + /// - `dependencies`: is the list of IDs of the transitive dependencies of the package to be + /// upgraded + /// - `package`: is the ID of the current package being upgraded + /// - `ticket`: is the upgrade ticket + /// + /// To get the ticket, you have to call the `0x2::package::authorize_upgrade` function, + /// and pass the package ID, the upgrade policy, and package digest. + /// + /// Examples: + /// ### Upgrade a package with some pre-known data. + /// ```rust,ignore + /// use sui_graphql_client::Client; + /// use sui_sdk_types::types::unresolved; + /// use sui_transaction_builder::TransactionBuilder; + /// use sui_transaction_builder::Function; + /// + /// let mut tx = TransactionBuilder::new(); + /// let package_id = "0x...".parse().unwrap(); + /// let upgrade_cap = tx.input(unresolved::Input::by_id("0x...".parse().unwrap()); + /// let upgrade_policy = tx.input(Serialized(&0u8)); + /// // the digest of the new package that was compiled + /// let package_digest: &[u8] = &[ + /// 68, 89, 156, 51, 190, 35, 155, 216, 248, 49, 135, 170, 106, 42, 190, 4, 208, 59, 155, + /// 89, 74, 63, 70, 95, 207, 78, 227, 22, 136, 146, 57, 79, + /// ]; + /// let digest_arg = tx.input(Serialized(&package_digest)); + /// + /// // we need this ticket to authorize the upgrade + /// let upgrade_ticket = tx.move_call( + /// Function::new( + /// "0x2".parse().unwrap(), + /// "package".parse().unwrap(), + /// "authorize_upgrade".parse().unwrap(), + /// vec![], + /// ), + /// vec![upgrade_cap, upgrade_policy, digest_arg], + /// ); + /// // now we can upgrade the package + /// let upgrade_receipt = tx.upgrade( + /// updated_modules, + /// deps, + /// package_id, + /// upgrade_ticket, + /// ); + /// + /// // commit the upgrade + /// tx.move_call( + /// Function::new( + /// "0x2".parse().unwrap(), + /// "package".parse().unwrap(), + /// "commit_upgrade".parse().unwrap(), + /// vec![], + /// ), + /// vec![upgrade_cap, upgrade_receipt], + /// ); + /// + /// let client = Client::new_mainnet(); + /// let tx = tx.resolve(&client)?; + /// ``` + pub fn upgrade( + &mut self, + modules: Vec>, + dependencies: Vec, + package: ObjectId, + ticket: Argument, + ) -> Argument { + let cmd = Command::Upgrade(Upgrade { + modules, + dependencies, + package, + ticket, + }); + self.commands.push(cmd); + Argument::Result(self.commands.len() as u16 - 1) + } + + /// Assuming everything is resolved, convert this transaction into the + /// resolved form. Returns a [`Transaction`] if successful, or an [`Error`] if not. + pub fn finish(self) -> Result { + let Some(sender) = self.sender else { + return Err(Error::MissingSender); + }; + if self.gas.is_empty() { + return Err(Error::MissingGasObjects); + } + let Some(budget) = self.gas_budget else { + return Err(Error::MissingGasBudget); + }; + let Some(price) = self.gas_price else { + return Err(Error::MissingGasPrice); + }; + + Ok(Transaction { + kind: sui_types::types::TransactionKind::ProgrammableTransaction( + sui_types::types::ProgrammableTransaction { + inputs: self + .inputs + .into_iter() + .map(try_from_unresolved_input_arg) + .collect::, _>>()?, + commands: self.commands, + }, + ), + sender, + gas_payment: { + GasPayment { + objects: self + .gas + .into_iter() + .map(try_from_gas_unresolved_input_to_unresolved_obj_ref) + .collect::, _>>()? + .into_iter() + .map(try_from_unresolved_obj_ref) + .collect::, _>>()?, + owner: self.sponsor.unwrap_or(sender), + price, + budget, + } + }, + expiration: self.expiration, + }) + } +} + +impl Function { + /// Constructor for the function type. + pub fn new( + package: Address, + module: Identifier, + function: Identifier, + type_args: Vec, + ) -> Self { + Self { + package, + module, + function, + type_args, + } + } +} + +impl From for unresolved::Input { + fn from(raw: RawBytes) -> Self { + Self { + kind: Some(unresolved::InputKind::Pure), + value: Some(unresolved::Value::String(base64ct::Base64::encode_string( + &raw.0, + ))), + object_id: None, + version: None, + digest: None, + mutable: None, + } + } +} + +impl<'a, T: Serialize> From> for unresolved::Input { + fn from(value: Serialized<'a, T>) -> Self { + Self::from(RawBytes(bcs::to_bytes(value.0).unwrap())) + } +} + +/// Convert from an [`unresolved::Input`] to a [`unresolved::ObjectReference`]. This is used to +/// convert gas objects into unresolved object references. +fn try_from_gas_unresolved_input_to_unresolved_obj_ref( + input: unresolved::Input, +) -> Result { + match input.kind { + Some(unresolved::InputKind::ImmutableOrOwned) => { + let object_id = input.object_id.ok_or(Error::MissingObjectId)?; + let version = input.version; + let digest = input.digest; + Ok(unresolved::ObjectReference { + object_id, + version, + digest, + }) + } + _ => Err(Error::WrongGasObject), + } +} + +/// Convert from an [`unresolved::ObjectReference`] to a [`ObjectReference`]. +fn try_from_unresolved_obj_ref(obj: unresolved::ObjectReference) -> Result { + let obj_id = obj.object_id; + let version = obj.version.ok_or(Error::MissingVersion(obj_id))?; + let digest = obj.digest.ok_or(Error::MissingDigest(obj_id))?; + Ok(ObjectReference::new(obj_id, version, digest)) +} + +/// Convert from an [`unresolved::Input`] into an [`Input`] for resolving the +/// transaction. +fn try_from_unresolved_input_arg(value: unresolved::Input) -> Result { + if let Some(kind) = value.kind { + match kind { + unresolved::InputKind::Pure => { + let Some(value) = value.value else { + return Err(Error::MissingPureValue); + }; + + match value { + unresolved::Value::String(v) => { + let bytes = base64ct::Base64::decode_vec(&v).map_err(Error::Decoding)?; + Ok(Input::Pure { value: bytes }) + } + _ => Err(Error::Input( + "expected a base64 string value for the Pure input argument".to_string(), + )), + } + } + unresolved::InputKind::ImmutableOrOwned => { + let Some(object_id) = value.object_id else { + return Err(Error::MissingObjectId); + }; + let Some(version) = value.version else { + return Err(Error::MissingVersion(object_id)); + }; + let Some(digest) = value.digest else { + return Err(Error::MissingDigest(object_id)); + }; + Ok(Input::ImmutableOrOwned(ObjectReference::new( + object_id, version, digest, + ))) + } + unresolved::InputKind::Shared => { + let Some(object_id) = value.object_id else { + return Err(Error::MissingObjectId); + }; + let Some(initial_shared_version) = value.version else { + return Err(Error::MissingInitialSharedVersion(object_id)); + }; + let Some(mutable) = value.mutable else { + return Err(Error::SharedObjectMutability(object_id)); + }; + + Ok(Input::Shared { + object_id, + initial_shared_version, + mutable, + }) + } + unresolved::InputKind::Receiving => { + let Some(object_id) = value.object_id else { + return Err(Error::MissingObjectId); + }; + let Some(version) = value.version else { + return Err(Error::MissingVersion(object_id)); + }; + let Some(digest) = value.digest else { + return Err(Error::MissingDigest(object_id)); + }; + Ok(Input::Receiving(ObjectReference::new( + object_id, version, digest, + ))) + } + unresolved::InputKind::Literal => Err(Error::UnsupportedLiteral), + } + } else { + Err(Error::Input( + "unresolved::Input must have a kind that is not None".to_string(), + )) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use anyhow::Context; + use base64ct::Encoding; + use serde::de; + use serde::Deserialize; + use serde::Deserializer; + use sui_crypto::ed25519::Ed25519PrivateKey; + use sui_crypto::SuiSigner; + use sui_graphql_client::faucet::CoinInfo; + use sui_graphql_client::faucet::FaucetClient; + use sui_graphql_client::Client; + use sui_graphql_client::PaginationFilter; + use sui_types::types::Address; + use sui_types::types::ExecutionStatus; + use sui_types::types::IdOperation; + use sui_types::types::ObjectId; + use sui_types::types::ObjectType; + use sui_types::types::TransactionEffects; + use sui_types::types::TypeTag; + + use crate::unresolved::Input; + use crate::Function; + use crate::Serialized; + use crate::TransactionBuilder; + use sui_types::types::Digest; + + /// Type corresponding to the output of `sui move build --dump-bytecode-as-base64` + #[derive(serde::Deserialize, Debug)] + struct MovePackageData { + #[serde(deserialize_with = "bcs_from_str")] + modules: Vec>, + #[serde(deserialize_with = "deps_from_str")] + dependencies: Vec, + digest: Vec, + } + + fn bcs_from_str<'de, D>(deserializer: D) -> Result>, D::Error> + where + D: Deserializer<'de>, + { + let bcs = Vec::::deserialize(deserializer)?; + bcs.into_iter() + .map(|s| base64ct::Base64::decode_vec(&s).map_err(de::Error::custom)) + .collect() + } + + fn deps_from_str<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let deps = Vec::::deserialize(deserializer)?; + deps.into_iter() + .map(|s| ObjectId::from_str(&s).map_err(de::Error::custom)) + .collect() + } + + /// This is used to read the json file that contains the modules/deps/digest generated with sui + /// move build --dump-bytecode-as-base64 on the `test_example_v1 and test_example_v2` projects + /// in the tests directory. + /// The json files are generated automatically when running `make test-with-localnet` in the + /// root of the sui-transaction-builder crate. + fn move_package_data(file: &str) -> MovePackageData { + let data = std::fs::read_to_string(file) + .with_context(|| { + format!( + "Failed to read {file}. \ + Run `make test-with-localnet` from the root of the repository that will \ + generate the right json files with the package data and then run the tests." + ) + }) + .unwrap(); + serde_json::from_str(&data).unwrap() + } + + /// Generate a random private key and its corresponding address + fn helper_address_pk() -> (Address, Ed25519PrivateKey) { + let pk = Ed25519PrivateKey::generate(rand::thread_rng()); + let address = pk.public_key().to_address(); + (address, pk) + } + + /// Helper to: + /// - generate a private key and its corresponding address + /// - set the sender for the tx to this newly created address + /// - set gas price + /// - set gas budget + /// - call faucet which returns 5 coin objects + /// - set the gas object (last coin from the list of the 5 objects returned by faucet) + /// - return the address, private key, and coins. + /// + /// NB! This assumes that these tests run on a network whose faucet returns 5 coins per + /// each faucet request. + async fn helper_setup( + tx: &mut TransactionBuilder, + client: &Client, + ) -> (Address, Ed25519PrivateKey, Vec) { + let (address, pk) = helper_address_pk(); + let coins = FaucetClient::local() + .request_and_wait(address) + .await + .unwrap() + .unwrap() + .sent; + let tx_digest = coins.first().unwrap().transfer_tx_digest; + wait_for_tx(client, tx_digest.into()).await; + + let gas = coins.last().unwrap().id; + // TODO when we have tx resolution, we can just pass an ObjectId + let gas_obj: Input = (&client.object(gas.into(), None).await.unwrap().unwrap()).into(); + tx.add_gas_objects(vec![gas_obj.with_owned_kind()]); + tx.set_gas_budget(500000000); + tx.set_gas_price(1000); + tx.set_sender(address); + + (address, pk, coins) + } + + /// Wait for the transaction to be finalized and indexed. This queries the GraphQL server until + /// it retrieves the requested transaction. + async fn wait_for_tx(client: &Client, digest: Digest) { + while client.transaction(digest).await.unwrap().is_none() { + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + } + } + + /// Wait for the transaction to be finalized and indexed, and check the effects' to ensure the + /// transaction was successfully executed. + async fn wait_for_tx_and_check_effects_status_success( + client: &Client, + digest: Digest, + effects: Result, sui_graphql_client::error::Error>, + ) { + assert!(effects.is_ok(), "Execution failed. Effects: {:?}", effects); + // wait for the transaction to be finalized + wait_for_tx(client, digest).await; + // check that it succeeded + let status = effects.unwrap(); + let expected_status = ExecutionStatus::Success; + assert_eq!(&expected_status, status.as_ref().unwrap().status()); + } + + #[tokio::test] + async fn test_finish() { + let mut tx = TransactionBuilder::new(); + let coin_obj_id = "0x19406ea4d9609cd9422b85e6bf2486908f790b778c757aff805241f3f609f9b4"; + let coin_digest = "7opR9rFUYivSTqoJHvFb9p6p54THyHTatMG6id4JKZR9"; + let coin_version = 2; + let coin = tx.input(Input::owned( + coin_obj_id.parse().unwrap(), + coin_version, + coin_digest.parse().unwrap(), + )); + + let addr = Address::generate(rand::thread_rng()); + let recipient = tx.input(Serialized(&addr)); + + let result = tx.clone().finish(); + assert!(result.is_err()); + + tx.transfer_objects(vec![coin], recipient); + tx.set_gas_budget(500000000); + tx.set_gas_price(1000); + tx.add_gas_objects(vec![Input::immutable( + "0xd8792bce2743e002673752902c0e7348dfffd78638cb5367b0b85857bceb9821" + .parse() + .unwrap(), + 2, + "2ZigdvsZn5BMeszscPQZq9z8ebnS2FpmAuRbAi9ednCk" + .parse() + .unwrap(), + )]); + tx.set_sender( + "0xc574ea804d9c1a27c886312e96c0e2c9cfd71923ebaeb3000d04b5e65fca2793" + .parse() + .unwrap(), + ); + + let tx = tx.finish(); + assert!(tx.is_ok()); + } + + #[tokio::test] + async fn test_transfer_obj_execution() { + let mut tx = TransactionBuilder::new(); + let (_, pk, coins) = helper_setup(&mut tx, &Client::new_localhost()).await; + + // get the object information from the client + let client = Client::new_localhost(); + let first = coins.first().unwrap().id; + let coin: Input = (&client.object(first.into(), None).await.unwrap().unwrap()).into(); + let coin_input = tx.input(coin.with_owned_kind()); + let recipient = Address::generate(rand::thread_rng()); + let recipient_input = tx.input(Serialized(&recipient)); + tx.transfer_objects(vec![coin_input], recipient_input); + + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + + // check that recipient has 1 coin + let recipient_coins = client + .coins(recipient, None, PaginationFilter::default()) + .await + .unwrap(); + assert_eq!(recipient_coins.data().len(), 1); + } + + #[tokio::test] + async fn test_move_call() { + // Check that `0x1::option::is_none` move call works when passing `1` + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + // set up the sender, gas object, gas budget, and gas price and return the pk to sign + let (_, pk, _) = helper_setup(&mut tx, &client).await; + let function = Function::new( + "0x1".parse().unwrap(), + "option".parse().unwrap(), + "is_none".parse().unwrap(), + vec![TypeTag::U64], + ); + let input = tx.input(Serialized(&vec![1u64])); + tx.move_call(function, vec![input]); + + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + } + + #[tokio::test] + async fn test_split_transfer() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (_, pk, _) = helper_setup(&mut tx, &client).await; + + // transfer 1 SUI from Gas coin + let amount = tx.input(Serialized(&1_000_000_000u64)); + let result = tx.split_coins(tx.gas(), vec![amount]); + let recipient_address = Address::generate(rand::thread_rng()); + let recipient = tx.input(Serialized(&recipient_address)); + tx.transfer_objects(vec![result], recipient); + + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + + // check that recipient has 1 coin + let recipient_coins = client + .coins(recipient_address, None, PaginationFilter::default()) + .await + .unwrap(); + assert_eq!(recipient_coins.data().len(), 1); + } + + #[tokio::test] + async fn test_split_without_transfer_should_fail() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (_, pk, coins) = helper_setup(&mut tx, &client).await; + + let coin = coins.first().unwrap().id; + let coin_obj: Input = (&client.object(coin.into(), None).await.unwrap().unwrap()).into(); + let coin_input = tx.input(coin_obj.with_owned_kind()); + + // transfer 1 SUI + let amount = tx.input(Serialized(&1_000_000_000u64)); + tx.split_coins(coin_input, vec![amount]); + + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + + let effects = client.execute_tx(vec![sig], &tx).await; + assert!(effects.is_ok()); + + // wait for the transaction to be finalized + loop { + let tx_digest = client.transaction(tx.digest().into()).await.unwrap(); + if tx_digest.is_some() { + break; + } + } + assert!(effects.is_ok()); + let status = effects.unwrap(); + let expected_status = ExecutionStatus::Success; + // The tx failed, so we expect Failure instead of Success + assert_ne!(&expected_status, status.as_ref().unwrap().status()); + } + + #[tokio::test] + async fn test_merge_coins() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (address, pk, coins) = helper_setup(&mut tx, &client).await; + + let coin1 = coins.first().unwrap().id; + let coin1_obj: Input = (&client.object(coin1.into(), None).await.unwrap().unwrap()).into(); + let coin_to_merge = tx.input(coin1_obj.with_owned_kind()); + + let mut coins_to_merge = vec![]; + // last coin is used for gas, first coin is the one we merge into + for c in coins[1..&coins.len() - 1].iter() { + let coin: Input = (&client.object(c.id.into(), None).await.unwrap().unwrap()).into(); + coins_to_merge.push(tx.input(coin.with_owned_kind())); + } + + tx.merge_coins(coin_to_merge, coins_to_merge); + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + + // check that there are two coins + let coins_after = client + .coins(address, None, PaginationFilter::default()) + .await + .unwrap(); + assert_eq!(coins_after.data().len(), 2); + } + + #[tokio::test] + async fn test_make_move_vec() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (_, pk, _) = helper_setup(&mut tx, &client).await; + + let input = tx.input(Serialized(&1u64)); + tx.make_move_vec(Some(TypeTag::U64), vec![input]); + + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + } + + #[tokio::test] + async fn test_publish() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (address, pk, _) = helper_setup(&mut tx, &client).await; + + let package = move_package_data("package_test_example_v1.json"); + let sender = tx.input(Serialized(&address)); + let upgrade_cap = tx.publish(package.modules, package.dependencies); + tx.transfer_objects(vec![upgrade_cap], sender); + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + } + + #[tokio::test] + async fn test_upgrade() { + let client = Client::new_localhost(); + let mut tx = TransactionBuilder::new(); + let (address, pk, coins) = helper_setup(&mut tx, &client).await; + + let package = move_package_data("package_test_example_v2.json"); + let sender = tx.input(Serialized(&address)); + let upgrade_cap = tx.publish(package.modules, package.dependencies); + tx.transfer_objects(vec![upgrade_cap], sender); + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + let effects = client.execute_tx(vec![sig], &tx).await; + let mut package_id: Option = None; + let mut created_objs = vec![]; + if let Ok(Some(ref effects)) = effects { + match effects { + TransactionEffects::V2(e) => { + for obj in e.changed_objects.clone() { + if obj.change.id_operation == IdOperation::Created { + let change = obj.change.output_state; + match change { + sui_types::types::ObjectOut::PackageWrite { .. } => { + package_id = Some(obj.object_id); + } + sui_types::types::ObjectOut::ObjectWrite { .. } => { + created_objs.push(obj.object_id); + } + _ => {} + } + } + } + } + _ => panic!("Expected V2 effects"), + } + } + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + + let mut tx = TransactionBuilder::new(); + let mut upgrade_cap = None; + for o in created_objs { + let obj = client.object(*o.as_address(), None).await.unwrap().unwrap(); + match obj.object_type() { + ObjectType::Struct(x) if x.name.to_string() == "UpgradeCap" => { + match obj.owner() { + sui_types::types::Owner::Address(_) => { + let obj: Input = (&obj).into(); + upgrade_cap = Some(tx.input(obj.with_owned_kind())) + } + sui_types::types::Owner::Shared(_) => { + upgrade_cap = Some(tx.input(&obj)) + } + // If the capability is owned by an object, then the module defining the owning + // object gets to decide how the upgrade capability should be used. + sui_types::types::Owner::Object(_) => { + panic!("Upgrade capability controlled by object") + } + sui_types::types::Owner::Immutable => panic!("Upgrade capability is stored immutably and cannot be used for upgrades"), + }; + break; + } + _ => {} + }; + } + + let upgrade_policy = tx.input(Serialized(&0u8)); + let updated_package = move_package_data("package_test_example_v2.json"); + let digest_arg = tx.input(Serialized(&updated_package.digest)); + + // we need this ticket to authorize the upgrade + let upgrade_ticket = tx.move_call( + Function::new( + "0x2".parse().unwrap(), + "package".parse().unwrap(), + "authorize_upgrade".parse().unwrap(), + vec![], + ), + vec![upgrade_cap.unwrap(), upgrade_policy, digest_arg], + ); + // now we can upgrade the package + let upgrade_receipt = tx.upgrade( + updated_package.modules, + updated_package.dependencies, + package_id.unwrap(), + upgrade_ticket, + ); + + // commit the upgrade + tx.move_call( + Function::new( + "0x2".parse().unwrap(), + "package".parse().unwrap(), + "commit_upgrade".parse().unwrap(), + vec![], + ), + vec![upgrade_cap.unwrap(), upgrade_receipt], + ); + + let gas = coins.last().unwrap().id; + let gas_obj: Input = (&client.object(gas.into(), None).await.unwrap().unwrap()).into(); + tx.add_gas_objects(vec![gas_obj.with_owned_kind()]); + tx.set_gas_budget(500000000); + tx.set_gas_price(1000); + tx.set_sender(address); + let tx = tx.finish().unwrap(); + let sig = pk.sign_transaction(&tx).unwrap(); + let effects = client.execute_tx(vec![sig], &tx).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + } +} diff --git a/crates/sui-transaction-builder/tests/test_example_v1/Move.toml b/crates/sui-transaction-builder/tests/test_example_v1/Move.toml new file mode 100644 index 000000000..d69022f4f --- /dev/null +++ b/crates/sui-transaction-builder/tests/test_example_v1/Move.toml @@ -0,0 +1,37 @@ +[package] +name = "test_example" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +test_example = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move b/crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move new file mode 100644 index 000000000..a45aa4a2b --- /dev/null +++ b/crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move @@ -0,0 +1,16 @@ +module test_example::test_example { +use std::string::String; + public struct Object has key { + id: UID, // required + name: String, + } + + /// Creates a new Object with a Unique ID + public fun new(name: String, ctx: &mut TxContext): Object { + Object { + id: object::new(ctx), // creates a new UID + name, + } + } +} + diff --git a/crates/sui-transaction-builder/tests/test_example_v2/Move.toml b/crates/sui-transaction-builder/tests/test_example_v2/Move.toml new file mode 100644 index 000000000..d69022f4f --- /dev/null +++ b/crates/sui-transaction-builder/tests/test_example_v2/Move.toml @@ -0,0 +1,37 @@ +[package] +name = "test_example" +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move +# license = "" # e.g., "MIT", "GPL", "Apache 2.0" +# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] + +[dependencies] +Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } + +# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. +# Revision can be a branch, a tag, and a commit hash. +# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" } + +# For local dependencies use `local = path`. Path is relative to the package root +# Local = { local = "../path/to" } + +# To resolve a version conflict and force a specific version for dependency +# override use `override = true` +# Override = { local = "../conflicting/version", override = true } + +[addresses] +test_example = "0x0" + +# Named addresses will be accessible in Move as `@name`. They're also exported: +# for example, `std = "0x1"` is exported by the Standard Library. +# alice = "0xA11CE" + +[dev-dependencies] +# The dev-dependencies section allows overriding dependencies for `--test` and +# `--dev` modes. You can introduce test-only dependencies here. +# Local = { local = "../path/to/dev-build" } + +[dev-addresses] +# The dev-addresses section allows overwriting named addresses for the `--test` +# and `--dev` modes. +# alice = "0xB0B" + diff --git a/crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move b/crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move new file mode 100644 index 000000000..5a323dd02 --- /dev/null +++ b/crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move @@ -0,0 +1,25 @@ +module test_example::test_example { +use std::string::{Self, String}; + + public struct Object has key { + id: UID, // required + name: String, + } + + /// Creates a new Object with a Unique ID + public fun new(name: String, ctx: &mut TxContext): Object { + Object { + id: object::new(ctx), // creates a new UID + name, + } + } + + public fun default_name(ctx: &mut TxContext): Object { + let default: String = string::utf8(b"default"); + Object { + id: object::new(ctx), + name: default, + } + } +} + From 4d1c2e30c7018ee03050ee7a851a3dc6b1f3e738 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Fri, 6 Dec 2024 10:52:49 -0600 Subject: [PATCH 079/107] types: add accessors and constructor for PasskeyAuthenticator --- .../sui-sdk-types/src/types/crypto/passkey.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs index 92f81487f..e66fe4109 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/types/crypto/passkey.rs @@ -1,4 +1,4 @@ -use super::{Secp256r1PublicKey, Secp256r1Signature}; +use super::{Secp256r1PublicKey, Secp256r1Signature, SimpleSignature}; /// An passkey authenticator with parsed fields. See field definition below. Can /// be initialized from [struct RawPasskeyAuthenticator]. @@ -29,6 +29,23 @@ pub struct PasskeyAuthenticator { client_data_json: String, } +impl PasskeyAuthenticator { + pub fn authenticator_data(&self) -> &[u8] { + &self.authenticator_data + } + + pub fn client_data_json(&self) -> &str { + &self.client_data_json + } + + pub fn signature(&self) -> SimpleSignature { + SimpleSignature::Secp256r1 { + signature: self.signature, + public_key: self.public_key, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct PasskeyPublicKey(Secp256r1PublicKey); @@ -116,6 +133,19 @@ mod serialization { } impl PasskeyAuthenticator { + pub fn new( + authenticator_data: Vec, + client_data_json: String, + signature: SimpleSignature, + ) -> Option { + Self::try_from_raw::(Authenticator { + authenticator_data, + client_data_json, + signature, + }) + .ok() + } + fn try_from_raw( Authenticator { authenticator_data, From 61b4dd56d57d39d63f9d5fbe4ff5bb3254b77a41 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 11 Dec 2024 06:21:07 -0800 Subject: [PATCH 080/107] sui-graphql-client: make pagination consistent across queries (#66) --- crates/sui-graphql-client/src/lib.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index a3196af6d..0b01e4dc1 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -1293,23 +1293,15 @@ impl Client { /// checkpoints, but only records the latest versions of system packages. pub async fn packages( &self, - after: Option<&str>, - before: Option<&str>, - first: Option, - last: Option, + pagination_filter: PaginationFilter, after_checkpoint: Option, before_checkpoint: Option, ) -> Result> { - if first.is_some() && last.is_some() { - return Err(Error::from_error( - Kind::Other, - "Conflicting arguments: either first or last can be provided, but not both.", - )); - } + let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = PackagesQuery::build(PackagesQueryArgs { - after, - before, + after: after.as_deref(), + before: before.as_deref(), first, last, filter: Some(PackageCheckpointFilter { @@ -2187,7 +2179,9 @@ mod tests { #[tokio::test] async fn test_packages_query() { let client = test_client(); - let packages = client.packages(None, None, None, None, None, None).await; + let packages = client + .packages(PaginationFilter::default(), None, None) + .await; assert!( packages.is_ok(), "Packages query failed for {} network. Error: {}", From e1e7ad816653439fb8f1387ed79ee4931a38a322 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 11 Dec 2024 14:10:42 -0600 Subject: [PATCH 081/107] roaring: pin to 0.10.6 to work around no-std breakage Pin the roaring dependency to 0.10.6 in order to work around a breakage with roaring building without the `std` feature enabled. See https://github.com/RoaringBitmap/roaring-rs/issues/304 for more info. --- crates/sui-sdk-types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 39328b8cc..bf00768b1 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -42,7 +42,7 @@ base64ct = { version = "1.6.0", features = ["alloc"] } bnum = "0.12.0" bs58 = "0.5.1" hex = "0.4.3" -roaring = { version = "0.10.6", default-features = false } +roaring = { version = "=0.10.6", default-features = false } winnow = "0.6.20" # Serialization and Deserialization support From dd6d262345af12b6c57df95b0baeb16252185e15 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:44:57 -0800 Subject: [PATCH 082/107] sui-graphql-client: return a tuple of Event, TxDigest when querying events (#68) --- crates/sui-graphql-client/src/lib.rs | 37 +++++++++++++------ .../src/query_types/events.rs | 2 + .../src/query_types/transaction.rs | 6 +++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 0b01e4dc1..55809590e 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -915,12 +915,12 @@ impl Client { // Events API // =========================================================================== - /// Return a page of events based on the (optional) event filter. + /// Return a page of tuple (event, transaction digest) based on the (optional) event filter. pub async fn events( &self, filter: Option, pagination_filter: PaginationFilter, - ) -> Result> { + ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; let operation = EventsQuery::build(EventsQueryArgs { @@ -940,17 +940,32 @@ impl Client { if let Some(events) = response.data { let ec = events.events; let page_info = ec.page_info; - let nodes = ec + + let events_with_digests = ec .nodes .into_iter() - .map(|e| e.bcs.0) - .map(|b| base64ct::Base64::decode_vec(&b)) - .collect::, base64ct::Error>>()? - .iter() - .map(|b| bcs::from_bytes::(b)) - .collect::, bcs::Error>>()?; + .map(|node| -> Result<(Event, TransactionDigest)> { + let event = + bcs::from_bytes::(&base64ct::Base64::decode_vec(&node.bcs.0)?)?; + + let tx_digest = node + .transaction_block + .ok_or_else(Error::empty_response_error)? + .digest + .ok_or_else(|| { + Error::from_error( + Kind::Deserialization, + "Expected a transaction digest for this event, but it is missing.", + ) + })?; + + let tx_digest = TransactionDigest::from_base58(&tx_digest)?; + + Ok((event, tx_digest)) + }) + .collect::>>()?; - Ok(Page::new(page_info, nodes)) + Ok(Page::new(page_info, events_with_digests)) } else { Ok(Page::new_empty()) } @@ -961,7 +976,7 @@ impl Client { &self, filter: Option, streaming_direction: Direction, - ) -> impl Stream> + '_ { + ) -> impl Stream> + '_ { stream_paginated_query( move |pag_filter| self.events(filter.clone(), pag_filter), streaming_direction, diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/sui-graphql-client/src/query_types/events.rs index 591a76ada..1d3f4b8ed 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/sui-graphql-client/src/query_types/events.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; +use crate::query_types::transaction::TransactionBlockDigest; use crate::query_types::Address; use crate::query_types::Base64; use crate::query_types::PageInfo; @@ -54,4 +55,5 @@ pub struct EventFilter { #[cynic(schema = "rpc", graphql_type = "Event")] pub struct Event { pub bcs: Base64, + pub transaction_block: Option, } diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index 38be0490f..a384c2238 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -91,6 +91,12 @@ pub struct TransactionBlock { pub signatures: Option>, } +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "TransactionBlock")] +pub struct TransactionBlockDigest { + pub digest: Option, +} + #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "TransactionBlock")] pub struct TxBlockEffects { From 87492b35089a1477acd305654d30ba198ca8249a Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:11:21 -0800 Subject: [PATCH 083/107] Update GraphQL schema to v1.39.0 --- .../sui-graphql-client-build/schema.graphql | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/sui-graphql-client-build/schema.graphql b/crates/sui-graphql-client-build/schema.graphql index 92330bc77..bdeadd932 100644 --- a/crates/sui-graphql-client-build/schema.graphql +++ b/crates/sui-graphql-client-build/schema.graphql @@ -174,6 +174,11 @@ enum AddressTransactionBlockRelationship { AFFECTED } +""" +An Authenticator represents the access control rules for a ConsensusV2 object. +""" +union Authenticator = Address + """ System transaction for creating the on-chain state used by zkLogin. """ @@ -448,6 +453,10 @@ type Checkpoint { By default, the scanning range consists of all transactions in this checkpoint. """ transactionBlocks(first: Int, after: String, last: Int, before: String, filter: TransactionBlockFilter, scanLimit: Int): TransactionBlockConnection! + """ + The Base64 serialized BCS bytes of CheckpointSummary for this checkpoint. + """ + bcs: Base64 } type CheckpointConnection { @@ -848,6 +857,16 @@ type ConsensusCommitPrologueTransaction { consensusCommitDigest: String } +""" +A ConsensusV2 object is an object that is automatically versioned by the consensus protocol +and allows different authentication modes based on the chosen authenticator. +(Initially, only single-owner authentication is supported.) +""" +type ConsensusV2 { + startVersion: UInt53! + authenticator: Authenticator +} + """ ISO-8601 Date and Time: RFC3339 in UTC with format: YYYY-MM-DDTHH:MM:SS.mmmZ. Note that the milliseconds part is optional, and it may be omitted if its value is 0. """ @@ -2905,7 +2924,7 @@ enum ObjectKind { """ The object's owner type: Immutable, Shared, Parent, or Address. """ -union ObjectOwner = Immutable | Shared | Parent | AddressOwner +union ObjectOwner = Immutable | Shared | Parent | AddressOwner | ConsensusV2 input ObjectRef { """ @@ -3382,7 +3401,8 @@ type Query { """ typeByName(name: String!): MoveType! """ - The coin metadata associated with the given coin type. + The coin metadata associated with the given coin type. Note that if the latest version of + the coin's metadata is wrapped or deleted, it will not be found. """ coinMetadata(coinType: String!): CoinMetadata """ From 96823e978941dfc29f7bc7fabd604e24b65a213f Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:12:14 -0800 Subject: [PATCH 084/107] Use Sui CLI from version 1.39.1 that matches the GraphQL schema --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e650a836c..b4980d60d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: - name: Get the Sui testnet binary and start a local network shell: bash env: - SUI_BINARY_VERSION: "1.36.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests + SUI_BINARY_VERSION: "1.39.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests SUI_NETWORK_RELEASE: "testnet" # which release to use run: | ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" From f40dc53725ba0a54e746a18ef5173f76643b33ed Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 12 Dec 2024 06:38:11 -0800 Subject: [PATCH 085/107] sui-graphql-client: use `bcs` from `Checkpoint` instead of manual conversion (#62) --- crates/sui-graphql-client/src/lib.rs | 28 +++-- .../src/query_types/checkpoint.rs | 111 +++--------------- 2 files changed, 35 insertions(+), 104 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 55809590e..dec94cbdf 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -2133,18 +2133,28 @@ mod tests { chckp.unwrap_err() ); let chckp_id = chckp.unwrap().unwrap(); - let total_transaction_blocks = client.total_transaction_blocks_by_seq_num(chckp_id).await; - assert!(total_transaction_blocks.is_ok()); - assert!(total_transaction_blocks.unwrap().is_some_and(|tx| tx > 0)); - - let chckp = client.checkpoint(None, Some(chckp_id)).await; - assert!(chckp.is_ok()); - let digest = chckp.unwrap().unwrap().content_digest; let total_transaction_blocks = client + .total_transaction_blocks_by_seq_num(chckp_id) + .await + .unwrap() + .unwrap(); + assert!(total_transaction_blocks > 0); + + let chckp = client + .checkpoint(None, Some(chckp_id)) + .await + .unwrap() + .unwrap(); + + let digest = chckp.digest(); + let total_transaction_blocks_by_digest = client .total_transaction_blocks_by_digest(digest.into()) .await; - assert!(total_transaction_blocks.is_ok()); - assert!(total_transaction_blocks.unwrap().is_some_and(|tx| tx > 0)); + assert!(total_transaction_blocks_by_digest.is_ok()); + assert_eq!( + total_transaction_blocks_by_digest.unwrap().unwrap(), + total_transaction_blocks + ); } #[tokio::test] diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index bc4b77237..3e99e56f8 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -1,20 +1,14 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use chrono::DateTime as ChronoDT; -use sui_types::types::CheckpointContentsDigest; -use sui_types::types::CheckpointDigest; +use base64ct::Encoding; use sui_types::types::CheckpointSummary; -use sui_types::types::GasCostSummary as NativeGasCostSummary; use crate::error; use crate::error::Error; use crate::error::Kind; use crate::query_types::schema; use crate::query_types::Base64; -use crate::query_types::BigInt; -use crate::query_types::DateTime; -use crate::query_types::Epoch; use crate::query_types::PageInfo; // =========================================================================== @@ -84,99 +78,26 @@ pub struct CheckpointId { #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Checkpoint")] pub struct Checkpoint { - pub epoch: Option, - pub digest: String, - pub network_total_transactions: Option, - pub previous_checkpoint_digest: Option, - pub sequence_number: u64, - pub timestamp: DateTime, - pub validator_signatures: Base64, - pub rolling_gas_summary: Option, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "GasCostSummary")] -pub struct GasCostSummary { - pub computation_cost: Option, - pub non_refundable_storage_fee: Option, - pub storage_cost: Option, - pub storage_rebate: Option, + pub bcs: Option, } -// TODO need bcs in GraphQL Checkpoint to avoid this conversion impl TryInto for Checkpoint { type Error = error::Error; - fn try_into(self) -> Result { - let epoch = self - .epoch - .ok_or_else(|| { - Error::from_error(Kind::Other, "Epoch is checkpoint summary is missing") - })? - .epoch_id; - let network_total_transactions = self.network_total_transactions.ok_or_else(|| { - Error::from_error( - Kind::Other, - "Network total transactions in checkpoint summary is missing", - ) - })?; - let sequence_number = self.sequence_number; - let timestamp_ms = ChronoDT::parse_from_rfc3339(&self.timestamp.0)? - .timestamp_millis() - .try_into()?; - let content_digest = CheckpointContentsDigest::from_base58(&self.digest)?; - let previous_digest = self - .previous_checkpoint_digest - .map(|d| CheckpointDigest::from_base58(&d)) + fn try_into(self) -> Result { + let checkpoint = self + .bcs + .map(|x| base64ct::Base64::decode_vec(&x.0)) + .transpose()? + .map(|bcs| { + bcs::from_bytes::(&bcs).map_err(|e| { + Error::from_error( + Kind::Other, + format!("Failed to deserialize checkpoint summary: {}", e), + ) + }) + }) .transpose()?; - let epoch_rolling_gas_cost_summary = self - .rolling_gas_summary - .ok_or_else(|| { - Error::from_error( - Kind::Other, - "Gas cost summary in checkpoint summary is missing", - ) - })? - .try_into()?; - Ok(CheckpointSummary { - epoch, - sequence_number, - network_total_transactions, - timestamp_ms, - content_digest, - previous_digest, - epoch_rolling_gas_cost_summary, - checkpoint_commitments: vec![], - end_of_epoch_data: None, - version_specific_data: vec![], - }) - } -} - -impl TryInto for GasCostSummary { - type Error = error::Error; - fn try_into(self) -> Result { - let computation_cost = self - .computation_cost - .ok_or_else(|| Error::from_error(Kind::Other, "Computation cost is missing"))? - .try_into()?; - let non_refundable_storage_fee = self - .non_refundable_storage_fee - .ok_or_else(|| Error::from_error(Kind::Other, "Non-refundable storage fee is missing"))? - .try_into()?; - let storage_cost = self - .storage_cost - .ok_or_else(|| Error::from_error(Kind::Other, "Storage cost is missing"))? - .try_into()?; - let storage_rebate = self - .storage_rebate - .ok_or_else(|| Error::from_error(Kind::Other, "Storage rebate is missing"))? - .try_into()?; - Ok(NativeGasCostSummary { - computation_cost, - non_refundable_storage_fee, - storage_cost, - storage_rebate, - }) + checkpoint.ok_or_else(|| Error::from_error(Kind::Other, "Checkpoint summary is missing")) } } From 7bb8d1f60671ee7d9663e1d2acd47c38dcb18e43 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:21:00 -0800 Subject: [PATCH 086/107] sui-graphql-client: use typed digests for queries (#59) --- crates/sui-graphql-client/src/lib.rs | 19 +++++++++++------ crates/sui-transaction-builder/src/lib.rs | 26 +++++++++++------------ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index dec94cbdf..0919550db 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -81,9 +81,9 @@ use streams::stream_paginated_query; use sui_types::types::framework::Coin; use sui_types::types::Address; +use sui_types::types::CheckpointDigest; use sui_types::types::CheckpointSequenceNumber; use sui_types::types::CheckpointSummary; -use sui_types::types::Digest; use sui_types::types::Event; use sui_types::types::MovePackage; use sui_types::types::Object; @@ -483,7 +483,10 @@ impl Client { /// The total number of transaction blocks in the network by the end of the provided /// checkpoint digest. - pub async fn total_transaction_blocks_by_digest(&self, digest: Digest) -> Result> { + pub async fn total_transaction_blocks_by_digest( + &self, + digest: CheckpointDigest, + ) -> Result> { self.internal_total_transaction_blocks(Some(digest.to_string()), None) .await } @@ -643,7 +646,7 @@ impl Client { /// provided, it will use the last known checkpoint id. pub async fn checkpoint( &self, - digest: Option, + digest: Option, seq_num: Option, ) -> Result> { if digest.is_some() && seq_num.is_some() { @@ -1434,7 +1437,10 @@ impl Client { // =========================================================================== /// Get a transaction by its digest. - pub async fn transaction(&self, digest: Digest) -> Result> { + pub async fn transaction( + &self, + digest: TransactionDigest, + ) -> Result> { let operation = TransactionBlockQuery::build(TransactionBlockArgs { digest: digest.to_string(), }); @@ -2147,9 +2153,8 @@ mod tests { .unwrap(); let digest = chckp.digest(); - let total_transaction_blocks_by_digest = client - .total_transaction_blocks_by_digest(digest.into()) - .await; + let total_transaction_blocks_by_digest = + client.total_transaction_blocks_by_digest(digest).await; assert!(total_transaction_blocks_by_digest.is_ok()); assert_eq!( total_transaction_blocks_by_digest.unwrap().unwrap(), diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index 281e812bc..ab7a288d3 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -500,7 +500,7 @@ mod tests { use crate::Function; use crate::Serialized; use crate::TransactionBuilder; - use sui_types::types::Digest; + use sui_types::types::TransactionDigest; /// Type corresponding to the output of `sui move build --dump-bytecode-as-base64` #[derive(serde::Deserialize, Debug)] @@ -580,7 +580,7 @@ mod tests { .unwrap() .sent; let tx_digest = coins.first().unwrap().transfer_tx_digest; - wait_for_tx(client, tx_digest.into()).await; + wait_for_tx(client, tx_digest).await; let gas = coins.last().unwrap().id; // TODO when we have tx resolution, we can just pass an ObjectId @@ -595,7 +595,7 @@ mod tests { /// Wait for the transaction to be finalized and indexed. This queries the GraphQL server until /// it retrieves the requested transaction. - async fn wait_for_tx(client: &Client, digest: Digest) { + async fn wait_for_tx(client: &Client, digest: TransactionDigest) { while client.transaction(digest).await.unwrap().is_none() { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } @@ -605,7 +605,7 @@ mod tests { /// transaction was successfully executed. async fn wait_for_tx_and_check_effects_status_success( client: &Client, - digest: Digest, + digest: TransactionDigest, effects: Result, sui_graphql_client::error::Error>, ) { assert!(effects.is_ok(), "Execution failed. Effects: {:?}", effects); @@ -675,7 +675,7 @@ mod tests { let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; // check that recipient has 1 coin let recipient_coins = client @@ -704,7 +704,7 @@ mod tests { let tx = tx.finish().unwrap(); let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; } #[tokio::test] @@ -724,7 +724,7 @@ mod tests { let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; // check that recipient has 1 coin let recipient_coins = client @@ -756,7 +756,7 @@ mod tests { // wait for the transaction to be finalized loop { - let tx_digest = client.transaction(tx.digest().into()).await.unwrap(); + let tx_digest = client.transaction(tx.digest()).await.unwrap(); if tx_digest.is_some() { break; } @@ -790,7 +790,7 @@ mod tests { let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; // check that there are two coins let coins_after = client @@ -813,7 +813,7 @@ mod tests { let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; } #[tokio::test] @@ -829,7 +829,7 @@ mod tests { let tx = tx.finish().unwrap(); let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; } #[tokio::test] @@ -868,7 +868,7 @@ mod tests { _ => panic!("Expected V2 effects"), } } - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; let mut tx = TransactionBuilder::new(); let mut upgrade_cap = None; @@ -939,6 +939,6 @@ mod tests { let tx = tx.finish().unwrap(); let sig = pk.sign_transaction(&tx).unwrap(); let effects = client.execute_tx(vec![sig], &tx).await; - wait_for_tx_and_check_effects_status_success(&client, tx.digest().into(), effects).await; + wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; } } From a12f7b1eae082a262eb39197db6a74adbd04aa9b Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Wed, 18 Dec 2024 15:01:47 -0800 Subject: [PATCH 087/107] ci: update to sui v1.39.3 and use --ignore-chain in sui move build (#75) --- .github/workflows/ci.yml | 18 +----------------- Makefile | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4980d60d..d446dca4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: - name: Get the Sui testnet binary and start a local network shell: bash env: - SUI_BINARY_VERSION: "1.39.1" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests + SUI_BINARY_VERSION: "1.39.3" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests SUI_NETWORK_RELEASE: "testnet" # which release to use run: | ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" @@ -123,22 +123,6 @@ jobs: echo "$(pwd)" >> $GITHUB_PATH # we need it on the path for calling sui move build for some tests ./sui start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name sui_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & - - name: Set up the CLI environment (need a client.yaml for calling some Sui commands) - shell: bash - run: | - mkdir -p $HOME/.sui/sui_config - tee $HOME/.sui/sui_config/client.yaml < ../../$@ + cd crates/sui-transaction-builder/tests/$(*F) && sui move build --ignore-chain --dump-bytecode-as-base64 > ../../$@ .PHONY: test-with-localnet test-with-localnet: package_test_example_v1.json package_test_example_v2.json From 92791834099ddd00a4fdbb7c3af1aaa8fb026de5 Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:05:06 -0800 Subject: [PATCH 088/107] sui-graphql-client: update docs for using custom queries (#76) --- crates/sui-graphql-client-build/README.md | 35 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/crates/sui-graphql-client-build/README.md b/crates/sui-graphql-client-build/README.md index 357b4b4ab..e6f20fbec 100644 --- a/crates/sui-graphql-client-build/README.md +++ b/crates/sui-graphql-client-build/README.md @@ -2,22 +2,41 @@ This crate provides a function to register a schema to enable building custom queries using cynic derive macros queries. Call this function in a `build.rs` file in your crate if you need to build custom queries. -### Example +### Usage +1. Add this crate as a build dependency in your `Cargo.toml` file. +```toml +[build-dependencies] +sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client-build", branch = "master" } +``` + +2. Add a `build.rs` file in your crate root directory and call the `register_schema` function in it. ```rust,ignore // build.rs file fn main() { - let schema_name = "MYSCHEMA" + let schema_name = "MYSCHEMA"; sui_graphql_client_build::register_schema(schema_name); } +``` -// Cargo.toml -... +3. Add the `cynic` and `sui-graphql-client` dependencies in your `Cargo.toml` file. You should have something like this. +```toml +# Cargo.toml +# ... [dependencies] cynic = "3.8.0" -... +sui-graphql-client = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client", branch = "master" } + [build-dependencies] -sui_graphql_client_build = "VERSION_HERE" +sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client-build", branch = "master" } +``` +4. If using `cynic`, use the cynic generator to generate the Rust types from the GraphQL schema. \ + Go to https://generator.cynic-rs.dev/ and paste the URL to the GraphQL service or manually copy paste the schema. \ + Then you can select the fields in the query you want to have, and the generator will generate the Rust types for you. + +5. In your Rust code, you can now use the custom query types generated by `cynic`. + +```rust,ignore // lib.rs // Custom query use cynic::QueryBuilder; @@ -37,3 +56,7 @@ async fn main() { println!("{:?}", q); } ``` + +6. For `UInt53`, you can use `u64` type directly as the `sui-graphql-client`'s schema implements the `impl_scalar`. Similarly for other types (Base64, DateTime). See more available types here: https://github.com/MystenLabs/sui-rust-sdk/blob/02639f6b09375fe03fa2243868be17bec1dfa33c/crates/sui-graphql-client/src/query_types/mod.rs?plain=1#L124-L126 + +7. Read the `cynic` [documentation](https://cynic-rs.dev/) to learn how to work with it, particularly when it comes to passing arguments to the query. From 52d18aa099091357634a9876a11b16a1e90872e7 Mon Sep 17 00:00:00 2001 From: UnMaykr <98741738+unmaykr-aftermath@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:16:48 -0300 Subject: [PATCH 089/107] feat(sui-sdk-types): add From for TypeTag (#77) Closes #73 --- crates/sui-sdk-types/src/types/type_tag/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/sui-sdk-types/src/types/type_tag/mod.rs b/crates/sui-sdk-types/src/types/type_tag/mod.rs index b397098fa..dd01a0cea 100644 --- a/crates/sui-sdk-types/src/types/type_tag/mod.rs +++ b/crates/sui-sdk-types/src/types/type_tag/mod.rs @@ -51,6 +51,12 @@ impl std::str::FromStr for TypeTag { } } +impl From for TypeTag { + fn from(value: StructTag) -> Self { + Self::Struct(Box::new(value)) + } +} + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TypeParseError { source: String, From c83ab6f9df67e5ea527d4543b5af6276d6706072 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 19 Dec 2024 11:32:42 -0600 Subject: [PATCH 090/107] roaring: update to v0.10.9 (#79) --- crates/sui-sdk-types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index bf00768b1..9c38ef82e 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -42,7 +42,7 @@ base64ct = { version = "1.6.0", features = ["alloc"] } bnum = "0.12.0" bs58 = "0.5.1" hex = "0.4.3" -roaring = { version = "=0.10.6", default-features = false } +roaring = { version = "0.10.9", default-features = false } winnow = "0.6.20" # Serialization and Deserialization support From 6381c5eb595181aec94fca03eca8d6e98ebe18ee Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:33:37 -0800 Subject: [PATCH 091/107] sui-graphql-client: use rustls instead of openssl for reqwest (#80) --- crates/sui-graphql-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/sui-graphql-client/Cargo.toml index 1104c5254..fd410b478 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/sui-graphql-client/Cargo.toml @@ -17,7 +17,7 @@ bcs = "0.1.4" chrono = "0.4.26" cynic = "3.7.3" futures = "0.3.29" -reqwest = { version = "0.12", features = ["json"] } +reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } serde = { version = "1.0.144" } serde_json = {version = "1.0.95"} sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } From f2a4f42552a48ed07499befa2e43f5984d23164b Mon Sep 17 00:00:00 2001 From: stefan-mysten <135084671+stefan-mysten@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:25:36 -0800 Subject: [PATCH 092/107] sui-graphql-client: add retry to getting coins from faucet to fix flaky test (#82) --- crates/sui-graphql-client/src/lib.rs | 32 +++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 0919550db..9be1e70c4 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -1732,6 +1732,7 @@ mod tests { use crate::LOCAL_HOST; use crate::MAINNET_HOST; use crate::TESTNET_HOST; + use tokio::time; const NUM_COINS_FROM_FAUCET: usize = 5; @@ -2003,16 +2004,31 @@ mod tests { let key = Ed25519PublicKey::generate(rand::thread_rng()); let address = key.to_address(); faucet.request_and_wait(address).await.unwrap(); - let mut stream = client - .coins_stream(address, None, Direction::default()) - .await; - let mut num_coins = 0; - while let Some(result) = stream.next().await { - assert!(result.is_ok()); - num_coins += 1; + const MAX_RETRIES: u32 = 10; + const RETRY_DELAY: time::Duration = time::Duration::from_secs(1); + + let mut num_coins = 0; + for attempt in 0..MAX_RETRIES { + let mut stream = client + .coins_stream(address, None, Direction::default()) + .await; + + while let Some(result) = stream.next().await { + match result { + Ok(_) => num_coins += 1, + Err(_) => { + if attempt < MAX_RETRIES - 1 { + time::sleep(RETRY_DELAY).await; + num_coins = 0; + break; + } + } + } + } } - assert!(num_coins == NUM_COINS_FROM_FAUCET); + + assert!(num_coins >= NUM_COINS_FROM_FAUCET); } #[tokio::test] From f5a37b11f9eaec4ae343472d8c59492950da2cb2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 19 Dec 2024 12:43:53 -0600 Subject: [PATCH 093/107] sui-crypto: add support for verifying passkey authenticators (#81) --- crates/sui-crypto/Cargo.toml | 4 + crates/sui-crypto/src/lib.rs | 4 + crates/sui-crypto/src/multisig.rs | 8 +- crates/sui-crypto/src/passkey.rs | 90 +++++++++++++++++++ .../sui-sdk-types/src/types/crypto/passkey.rs | 4 + 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 crates/sui-crypto/src/passkey.rs diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index e5c9974b2..c5cec4e40 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -25,6 +25,7 @@ rustdoc-args = [ default = [] ed25519 = ["dep:ed25519-dalek", "dep:rand_core"] secp256r1 = ["dep:p256", "dep:rand_core"] +passkey = ["secp256r1", "dep:sha2"] secp256k1 = ["dep:k256", "dep:rand_core", "signature/std"] zklogin = [ "dep:ark-bn254", @@ -64,6 +65,9 @@ ed25519-dalek = { version = "2.1.1", optional = true } # secp256r1 support p256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "std"], optional = true } +# passkey verification support +sha2 = { version = "0.10.8", optional = true } + # secp256k1 support k256 = { version = "0.13.4", default-features = false, features = ["ecdsa"], optional = true } diff --git a/crates/sui-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs index 2be7cd122..fd77be2ad 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/sui-crypto/src/lib.rs @@ -23,6 +23,10 @@ pub mod secp256k1; #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] pub mod secp256r1; +#[cfg(feature = "passkey")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "passkey")))] +pub mod passkey; + #[cfg(feature = "zklogin")] #[cfg_attr(doc_cfg, doc(cfg(feature = "zklogin")))] pub mod zklogin; diff --git a/crates/sui-crypto/src/multisig.rs b/crates/sui-crypto/src/multisig.rs index 5a9dbd079..cb03a140f 100644 --- a/crates/sui-crypto/src/multisig.rs +++ b/crates/sui-crypto/src/multisig.rs @@ -244,10 +244,14 @@ impl Verifier for UserSignatureVerifier { zklogin_verifier.verify(message, zklogin_authenticator.as_ref()) } - + #[cfg(not(feature = "passkey"))] UserSignature::Passkey(_) => Err(SignatureError::from_source( - "unsupported user signature scheme", + "support for passkey is not enabled", )), + #[cfg(feature = "passkey")] + UserSignature::Passkey(passkey_authenticator) => { + crate::passkey::PasskeyVerifier::default().verify(message, passkey_authenticator) + } } } } diff --git a/crates/sui-crypto/src/passkey.rs b/crates/sui-crypto/src/passkey.rs new file mode 100644 index 000000000..dde00bd05 --- /dev/null +++ b/crates/sui-crypto/src/passkey.rs @@ -0,0 +1,90 @@ +use crate::secp256r1::Secp256r1VerifyingKey; +use crate::SignatureError; +use signature::Verifier; +use sui_sdk_types::types::PasskeyAuthenticator; +use sui_sdk_types::types::SimpleSignature; +use sui_sdk_types::types::UserSignature; + +#[derive(Default, Clone, Debug)] +pub struct PasskeyVerifier {} + +impl PasskeyVerifier { + pub fn new() -> Self { + Self {} + } +} + +impl Verifier for PasskeyVerifier { + fn verify( + &self, + message: &[u8], + authenticator: &PasskeyAuthenticator, + ) -> Result<(), SignatureError> { + let SimpleSignature::Secp256r1 { + signature, + public_key, + } = authenticator.signature() + else { + return Err(SignatureError::from_source("not a secp256r1 signature")); + }; + + if message != authenticator.challenge() { + return Err(SignatureError::from_source( + "passkey challenge does not match expected message", + )); + } + + // Construct passkey signing message = authenticator_data || sha256(client_data_json). + let mut message = authenticator.authenticator_data().to_owned(); + let client_data_hash = { + use sha2::Digest; + + let mut hasher = sha2::Sha256::new(); + hasher.update(authenticator.client_data_json().as_bytes()); + hasher.finalize() + }; + message.extend_from_slice(&client_data_hash); + + let verifying_key = Secp256r1VerifyingKey::new(&public_key)?; + + verifying_key.verify(&message, &signature) + } +} + +impl Verifier for PasskeyVerifier { + fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { + let UserSignature::Passkey(authenticator) = signature else { + return Err(SignatureError::from_source("not a passkey authenticator")); + }; + + >::verify(self, message, authenticator) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::SuiVerifier; + use sui_sdk_types::types::Transaction; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::wasm_bindgen_test as test; + + #[test] + fn transaction_signing_fixture() { + let transaction = "AAAAACdZawPnpJRjmVcwDu6xrIumtq5NLO+6GHbs0iGdCoD7AQ0T0TolicYERdSvyCRjSSduDZLbSpBsZBoib+lF48EBcgAAAAAAAAAgpQr/Mudl9BdzyBdkbqTlqBw4/aJ21kAD/jpJKa05im4nWWsD56SUY5lXMA7usayLprauTSzvuhh27NIhnQqA++gDAAAAAAAAgIQeAAAAAAAA"; + let signature = "BiVJlg3liA6MaHQ0Fw9kdmBbj+SuuaKGMseZXPO6gx2XYx0AAAAAhgF7InR5cGUiOiJ3ZWJhdXRobi5nZXQiLCJjaGFsbGVuZ2UiOiJXellBZmVvbHcweU15bEFheDRvbzNjVC1rdEVaM0xmenZXcURqakxKZVRvIiwib3JpZ2luIjoiaHR0cDovL2xvY2FsaG9zdDo1MTczIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfWICfOgpQ38QYao9Gj0/bqmWYNkuxvbuN3lz4uzFcXeVMEVivX41eC9H+tk+UnvUvKzThtf+uMLFzerU0zZLi8le4QJJsAUcyjsP/1UPAesax8UOC14M62FjAqtqaR46wR7jCg=="; + + let transaction: Transaction = { + use base64ct::Encoding; + let bytes = base64ct::Base64::decode_vec(transaction).unwrap(); + bcs::from_bytes(&bytes).unwrap() + }; + let signature = UserSignature::from_base64(signature).unwrap(); + + let verifier = PasskeyVerifier::default(); + verifier + .verify_transaction(&transaction, &signature) + .unwrap(); + } +} diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/types/crypto/passkey.rs index e66fe4109..88d566c8f 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/types/crypto/passkey.rs @@ -38,6 +38,10 @@ impl PasskeyAuthenticator { &self.client_data_json } + pub fn challenge(&self) -> &[u8] { + &self.challenge + } + pub fn signature(&self) -> SimpleSignature { SimpleSignature::Secp256r1 { signature: self.signature, From bdc31ca522ebcb9968c12c5c534c7fdeb8005ccc Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 19 Dec 2024 15:34:31 -0600 Subject: [PATCH 094/107] types: move unresolved types to sui-transaction-builder --- .../src/types/transaction/mod.rs | 2 - crates/sui-transaction-builder/Cargo.toml | 12 +- crates/sui-transaction-builder/src/lib.rs | 2 +- .../src}/unresolved.rs | 178 +++++++++--------- 4 files changed, 97 insertions(+), 97 deletions(-) rename crates/{sui-sdk-types/src/types/transaction => sui-transaction-builder/src}/unresolved.rs (70%) diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/types/transaction/mod.rs index 09e2349b7..9ef1a617a 100644 --- a/crates/sui-sdk-types/src/types/transaction/mod.rs +++ b/crates/sui-sdk-types/src/types/transaction/mod.rs @@ -11,8 +11,6 @@ mod serialization; #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub(crate) use serialization::SignedTransactionWithIntentMessage; -pub mod unresolved; - #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct Transaction { diff --git a/crates/sui-transaction-builder/Cargo.toml b/crates/sui-transaction-builder/Cargo.toml index bd0948206..c88c02cde 100644 --- a/crates/sui-transaction-builder/Cargo.toml +++ b/crates/sui-transaction-builder/Cargo.toml @@ -8,19 +8,27 @@ publish = false readme = "README.md" description = "Transaction API for the Rust SDK for the Sui Blockchain" +[features] +default = [] +schemars = ["dep:schemars", "sui-types/schemars"] + [dependencies] base64ct = "1.6" bcs = "0.1.6" serde = { version = "1.0", features = ["derive"] } +serde_with = { version = "3.9", default-features = false, features = ["alloc"] } sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "hash"] } -sui-graphql-client = { package = "sui-graphql-client", path = "../sui-graphql-client" } thiserror = "2.0" +serde_json = { version = "1.0.128" } + +# JsonSchema definitions for types, useful for generating an OpenAPI Specificaiton. +schemars = { version = "0.8.21", optional = true } [dev-dependencies] anyhow = "1.0" rand = "0.8" serde_json = "1.0" sui-crypto = { package = "sui-crypto", path = "../sui-crypto" , features = ["ed25519"] } +sui-graphql-client = { package = "sui-graphql-client", path = "../sui-graphql-client" } sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["rand"] } tokio = { version = "1.0", features = ["full"] } - diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index ab7a288d3..c617811c9 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 mod error; +pub mod unresolved; use error::Error; -use sui_types::types::unresolved; use sui_types::types::Address; use sui_types::types::Argument; use sui_types::types::Command; diff --git a/crates/sui-sdk-types/src/types/transaction/unresolved.rs b/crates/sui-transaction-builder/src/unresolved.rs similarity index 70% rename from crates/sui-sdk-types/src/types/transaction/unresolved.rs rename to crates/sui-transaction-builder/src/unresolved.rs index af3e71782..50f1417ed 100644 --- a/crates/sui-sdk-types/src/types/transaction/unresolved.rs +++ b/crates/sui-transaction-builder/src/unresolved.rs @@ -1,101 +1,73 @@ -use super::{Command, TransactionExpiration}; -use crate::types::{Address, ObjectDigest, ObjectId, object::Version}; +use sui_types::types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; // A potentially unresolved user transaction. Note that one can construct a // fully resolved transaction using this type by providing all the required // data. -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedTransaction") -)] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedTransaction")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Transaction { - #[cfg_attr(feature = "serde", serde(flatten))] + #[serde(flatten)] pub ptb: ProgrammableTransaction, pub sender: Address, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub gas_payment: Option, pub expiration: TransactionExpiration, } -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedProgrammableTransaction") -)] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedProgrammableTransaction")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct ProgrammableTransaction { pub inputs: Vec, pub commands: Vec, } -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedGasPayment") -)] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedGasPayment")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct GasPayment { - #[cfg_attr( - feature = "serde", - serde(default, skip_serializing_if = "Vec::is_empty") - )] + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub objects: Vec, pub owner: Address, - #[cfg_attr( - feature = "serde", - serde( - with = "crate::_serde::OptionReadableDisplay", - default, - skip_serializing_if = "Option::is_none", - ) + #[serde( + with = "OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none" )] - #[cfg_attr(feature = "schemars", schemars(with = "Option"))] + #[cfg_attr(feature = "schemars", schemars(with = "Option<_schemars::U64>"))] pub price: Option, - #[cfg_attr( - feature = "serde", - serde( - with = "crate::_serde::OptionReadableDisplay", - default, - skip_serializing_if = "Option::is_none", - ) + #[serde( + with = "OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none" )] - #[cfg_attr(feature = "schemars", schemars(with = "Option"))] + #[cfg_attr(feature = "schemars", schemars(with = "Option<_schemars::U64>"))] pub budget: Option, } #[derive(Clone, Debug)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedObjectReference") -)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedObjectReference")] pub struct ObjectReference { pub object_id: ObjectId, - #[cfg_attr( - feature = "serde", - serde( - with = "crate::_serde::OptionReadableDisplay", - default, - skip_serializing_if = "Option::is_none", - ) + #[serde( + with = "OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none" )] - #[cfg_attr(feature = "schemars", schemars(with = "Option"))] + #[cfg_attr(feature = "schemars", schemars(with = "Option<_schemars::U64>"))] pub version: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub digest: Option, } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedInputKind"), - serde(rename_all = "snake_case") -)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedInputKind")] +#[serde(rename_all = "snake_case")] pub enum InputKind { Pure, Shared, @@ -112,18 +84,15 @@ pub enum InputKind { /// `tx.resolve` function on the transaction builder to resolve all unresolved /// inputs. #[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedInput") -)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedInput")] pub struct Input { - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub kind: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub value: Option, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] /// Unique identifier for this object. pub object_id: Option, /// Either the `initial_shared_version` if object is a shared object, or the @@ -132,35 +101,29 @@ pub struct Input { /// shared or not. For shared objects, this is the initial version the /// object was shared at. For all other objects, this is the version of /// the object. - #[cfg_attr( - feature = "serde", - serde( - with = "crate::_serde::OptionReadableDisplay", - default, - skip_serializing_if = "Option::is_none", - alias = "initial_shared_version", - ) + #[serde( + with = "OptionReadableDisplay", + default, + skip_serializing_if = "Option::is_none", + alias = "initial_shared_version" )] - #[cfg_attr(feature = "schemars", schemars(with = "Option"))] + #[cfg_attr(feature = "schemars", schemars(with = "Option<_schemars::U64>"))] pub version: Option, /// The digest of this object. This field is only relevant for /// owned/immutable/receiving inputs. - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub digest: Option, /// Whether this object is mutable. This field is only relevant for shared /// objects. - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none",))] + #[serde(skip_serializing_if = "Option::is_none")] pub mutable: Option, } #[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize), - serde(rename = "UnresolvedValue"), - serde(try_from = "serde_json::Value", into = "serde_json::Value") -)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema), schemars(untagged))] +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename = "UnresolvedValue")] +#[serde(try_from = "serde_json::Value", into = "serde_json::Value")] pub enum Value { Null, Bool(bool), @@ -316,8 +279,6 @@ impl Input { } } -#[cfg(feature = "serde")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] impl TryFrom for Value { type Error = &'static str; @@ -341,8 +302,6 @@ impl TryFrom for Value { } } -#[cfg(feature = "serde")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] impl From for serde_json::Value { fn from(value: Value) -> Self { match value { @@ -355,10 +314,9 @@ impl From for serde_json::Value { } } -#[cfg(all(feature = "serde", feature = "hash"))] -impl From<&crate::types::Object> for Input { - fn from(object: &crate::types::Object) -> Self { - use crate::types::object::Owner; +impl From<&sui_types::types::Object> for Input { + fn from(object: &sui_types::types::Object) -> Self { + use sui_types::types::Owner; let input = Input::by_id(object.object_id()) .with_digest(object.digest()) @@ -377,3 +335,39 @@ impl From for Input { Input::by_id(object_id) } } + +pub(crate) type OptionReadableDisplay = + ::serde_with::As>>; + +#[cfg(feature = "schemars")] +mod _schemars { + use schemars::{ + JsonSchema, + schema::{InstanceType, Metadata, SchemaObject}, + }; + + pub(crate) struct U64; + + impl JsonSchema for U64 { + fn schema_name() -> String { + "u64".to_owned() + } + + fn json_schema(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + SchemaObject { + metadata: Some(Box::new(Metadata { + description: Some("Radix-10 encoded 64-bit unsigned integer".to_owned()), + ..Default::default() + })), + instance_type: Some(InstanceType::String.into()), + format: Some("u64".to_owned()), + ..Default::default() + } + .into() + } + + fn is_referenceable() -> bool { + false + } + } +} From da97d3c8b1f1d63317dc310f512fad851dc235c4 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 19 Dec 2024 15:46:07 -0600 Subject: [PATCH 095/107] types: fold EffectsObjectChange into ChangedObject struct --- crates/sui-sdk-types/src/types/effects/mod.rs | 4 +- crates/sui-sdk-types/src/types/effects/v1.rs | 82 +++---------------- crates/sui-sdk-types/src/types/mod.rs | 6 +- .../src/types/serialization_proptests.rs | 1 - crates/sui-transaction-builder/src/lib.rs | 4 +- 5 files changed, 20 insertions(+), 77 deletions(-) diff --git a/crates/sui-sdk-types/src/types/effects/mod.rs b/crates/sui-sdk-types/src/types/effects/mod.rs index 1e3cd753d..79f93bd3b 100644 --- a/crates/sui-sdk-types/src/types/effects/mod.rs +++ b/crates/sui-sdk-types/src/types/effects/mod.rs @@ -1,8 +1,8 @@ mod v1; pub use v1::{ - ChangedObject, EffectsObjectChange, IdOperation, ObjectIn, ObjectOut, TransactionEffectsV1, - UnchangedSharedKind, UnchangedSharedObject, + ChangedObject, IdOperation, ObjectIn, ObjectOut, TransactionEffectsV1, UnchangedSharedKind, + UnchangedSharedObject, }; use crate::types::execution_status::ExecutionStatus; diff --git a/crates/sui-sdk-types/src/types/effects/v1.rs b/crates/sui-sdk-types/src/types/effects/v1.rs index 962020eac..cb9be7f1b 100644 --- a/crates/sui-sdk-types/src/types/effects/v1.rs +++ b/crates/sui-sdk-types/src/types/effects/v1.rs @@ -52,12 +52,23 @@ pub struct TransactionEffectsV1 { // XXX Do we maybe want to just fold "EffectsObjectChange" into this struct? #[derive(Eq, PartialEq, Clone, Debug)] +#[cfg_attr( + feature = "serde", + derive(serde_derive::Serialize, serde_derive::Deserialize) +)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ChangedObject { pub object_id: ObjectId, - #[cfg_attr(feature = "schemars", schemars(flatten))] - pub change: EffectsObjectChange, + /// State of the object in the store prior to this transaction. + pub input_state: ObjectIn, + /// State of the object in the store after this transaction. + pub output_state: ObjectOut, + + /// Whether this object ID is created or deleted in this transaction. + /// This information isn't required by the protocol but is useful for + /// providing more detailed semantics on object changes. + pub id_operation: IdOperation, } #[derive(Eq, PartialEq, Clone, Debug)] @@ -109,27 +120,6 @@ pub enum UnchangedSharedKind { PerEpochConfig, } -#[derive(Eq, PartialEq, Clone, Debug)] -#[cfg_attr( - feature = "serde", - derive(serde_derive::Serialize, serde_derive::Deserialize) -)] -#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] -pub struct EffectsObjectChange { - // input_state and output_state are the core fields that's required by - // the protocol as it tells how an object changes on-chain. - /// State of the object in the store prior to this transaction. - pub input_state: ObjectIn, - /// State of the object in the store after this transaction. - pub output_state: ObjectOut, - - /// Whether this object ID is created or deleted in this transaction. - /// This information isn't required by the protocol but is useful for - /// providing more detailed semantics on object changes. - pub id_operation: IdOperation, -} - /// If an object exists (at root-level) in the store prior to this transaction, /// it should be Exist, otherwise it's NonExist, e.g. wrapped objects should be /// NonExist. @@ -391,52 +381,6 @@ mod serialization { } } - #[derive(serde_derive::Serialize, serde_derive::Deserialize)] - struct ReadableChangedObject { - object_id: ObjectId, - #[serde(flatten)] - change: EffectsObjectChange, - } - - #[derive(serde_derive::Serialize, serde_derive::Deserialize)] - struct BinaryChangedObject { - object_id: ObjectId, - change: EffectsObjectChange, - } - - impl Serialize for ChangedObject { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let Self { object_id, change } = self.clone(); - if serializer.is_human_readable() { - let readable = ReadableChangedObject { object_id, change }; - readable.serialize(serializer) - } else { - let binary = BinaryChangedObject { object_id, change }; - binary.serialize(serializer) - } - } - } - - impl<'de> Deserialize<'de> for ChangedObject { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - if deserializer.is_human_readable() { - let ReadableChangedObject { object_id, change } = - Deserialize::deserialize(deserializer)?; - Ok(Self { object_id, change }) - } else { - let BinaryChangedObject { object_id, change } = - Deserialize::deserialize(deserializer)?; - Ok(Self { object_id, change }) - } - } - } - #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] enum ReadableUnchangedSharedKind { diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs index 7042802b8..fc094b703 100644 --- a/crates/sui-sdk-types/src/types/mod.rs +++ b/crates/sui-sdk-types/src/types/mod.rs @@ -34,9 +34,9 @@ pub use digest::{ TransactionEffectsDigest, TransactionEventsDigest, }; pub use effects::{ - ChangedObject, EffectsObjectChange, IdOperation, ModifiedAtVersion, ObjectIn, ObjectOut, - ObjectReferenceWithOwner, TransactionEffects, TransactionEffectsV1, TransactionEffectsV2, - UnchangedSharedKind, UnchangedSharedObject, + ChangedObject, IdOperation, ModifiedAtVersion, ObjectIn, ObjectOut, ObjectReferenceWithOwner, + TransactionEffects, TransactionEffectsV1, TransactionEffectsV2, UnchangedSharedKind, + UnchangedSharedObject, }; pub use events::{BalanceChange, Event, TransactionEvents}; pub use execution_status::{ diff --git a/crates/sui-sdk-types/src/types/serialization_proptests.rs b/crates/sui-sdk-types/src/types/serialization_proptests.rs index 459af5eee..29bbebf5f 100644 --- a/crates/sui-sdk-types/src/types/serialization_proptests.rs +++ b/crates/sui-sdk-types/src/types/serialization_proptests.rs @@ -120,7 +120,6 @@ serialization_test!(TransactionDigest); serialization_test!(TransactionEffectsDigest); serialization_test!(TransactionEventsDigest); serialization_test!(ChangedObject); -serialization_test!(EffectsObjectChange); serialization_test!(IdOperation); serialization_test!(ObjectIn); serialization_test!(ObjectOut); diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index c617811c9..346f3f01d 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -851,8 +851,8 @@ mod tests { match effects { TransactionEffects::V2(e) => { for obj in e.changed_objects.clone() { - if obj.change.id_operation == IdOperation::Created { - let change = obj.change.output_state; + if obj.id_operation == IdOperation::Created { + let change = obj.output_state; match change { sui_types::types::ObjectOut::PackageWrite { .. } => { package_id = Some(obj.object_id); From 638a85ad9c15a7b080af3ecf0eb09113d7829830 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Thu, 19 Dec 2024 15:51:07 -0600 Subject: [PATCH 096/107] sui-sdk-types: flatten the types module --- crates/sui-crypto/src/ed25519.rs | 12 +- crates/sui-crypto/src/lib.rs | 6 +- crates/sui-crypto/src/multisig.rs | 16 +- crates/sui-crypto/src/passkey.rs | 8 +- crates/sui-crypto/src/secp256k1.rs | 12 +- crates/sui-crypto/src/secp256r1.rs | 12 +- crates/sui-crypto/src/simple.rs | 12 +- crates/sui-crypto/src/zklogin/mod.rs | 16 +- crates/sui-crypto/src/zklogin/tests.rs | 2 +- crates/sui-crypto/src/zklogin/verify.rs | 22 +-- crates/sui-graphql-client/README.md | 6 +- crates/sui-graphql-client/src/error.rs | 6 +- crates/sui-graphql-client/src/faucet.rs | 6 +- crates/sui-graphql-client/src/lib.rs | 38 ++--- .../src/query_types/checkpoint.rs | 2 +- .../src/query_types/dry_run.rs | 2 +- .../src/query_types/dynamic_fields.rs | 2 +- .../sui-graphql-client/src/query_types/mod.rs | 2 +- .../src/query_types/packages.rs | 2 +- .../src/query_types/transaction.rs | 8 +- .../sui-sdk-types/src/{types => }/address.rs | 0 .../src/{types => }/checkpoint.rs | 0 .../src/{types => }/crypto/bls12381.rs | 0 .../src/{types => }/crypto/ed25519.rs | 0 .../src/{types => }/crypto/intent.rs | 0 .../src/{types => }/crypto/mod.rs | 0 .../src/{types => }/crypto/multisig.rs | 2 +- .../src/{types => }/crypto/passkey.rs | 4 +- .../src/{types => }/crypto/secp256k1.rs | 0 .../src/{types => }/crypto/secp256r1.rs | 0 .../src/{types => }/crypto/signature.rs | 0 .../src/{types => }/crypto/validator.rs | 2 +- .../src/{types => }/crypto/zklogin.rs | 4 +- .../sui-sdk-types/src/{types => }/digest.rs | 0 .../fixtures/genesis-transaction-effects | 0 .../effects/fixtures/sponsor-tx-effects | 0 .../src/{types => }/effects/mod.rs | 4 +- .../src/{types => }/effects/v1.rs | 2 +- .../sui-sdk-types/src/{types => }/events.rs | 0 .../src/{types => }/execution_status.rs | 0 .../src/{types => }/framework.rs | 0 crates/sui-sdk-types/src/{types => }/gas.rs | 0 crates/sui-sdk-types/src/hash.rs | 53 +++--- crates/sui-sdk-types/src/lib.rs | 159 +++++++++++++++++- .../sui-sdk-types/src/{types => }/object.rs | 4 +- .../src/{types => }/object_id.rs | 0 .../{types => }/serialization_proptests.rs | 2 +- .../transaction/fixtures/change-epoch | 0 .../fixtures/consensus-commit-prologue-v1 | 0 .../{types => }/transaction/fixtures/genesis | 0 .../src/{types => }/transaction/fixtures/ptb | 0 .../update-transaction-fixtures/.gitignore | 0 .../update-transaction-fixtures/Cargo.toml | 0 .../update-transaction-fixtures/src/main.rs | 0 .../src/{types => }/transaction/mod.rs | 0 .../{types => }/transaction/serialization.rs | 20 +-- .../src/{types => }/type_tag/mod.rs | 0 .../src/{types => }/type_tag/parse.rs | 0 .../src/{types => }/type_tag/serialization.rs | 0 crates/sui-sdk-types/src/types/mod.rs | 73 -------- crates/sui-sdk-types/src/{types => }/u256.rs | 0 crates/sui-transaction-builder/src/error.rs | 2 +- crates/sui-transaction-builder/src/lib.rs | 70 ++++---- .../sui-transaction-builder/src/unresolved.rs | 8 +- 64 files changed, 333 insertions(+), 268 deletions(-) rename crates/sui-sdk-types/src/{types => }/address.rs (100%) rename crates/sui-sdk-types/src/{types => }/checkpoint.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/bls12381.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/ed25519.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/intent.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/mod.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/multisig.rs (99%) rename crates/sui-sdk-types/src/{types => }/crypto/passkey.rs (99%) rename crates/sui-sdk-types/src/{types => }/crypto/secp256k1.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/secp256r1.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/signature.rs (100%) rename crates/sui-sdk-types/src/{types => }/crypto/validator.rs (98%) rename crates/sui-sdk-types/src/{types => }/crypto/zklogin.rs (99%) rename crates/sui-sdk-types/src/{types => }/digest.rs (100%) rename crates/sui-sdk-types/src/{types => }/effects/fixtures/genesis-transaction-effects (100%) rename crates/sui-sdk-types/src/{types => }/effects/fixtures/sponsor-tx-effects (100%) rename crates/sui-sdk-types/src/{types => }/effects/mod.rs (97%) rename crates/sui-sdk-types/src/{types => }/effects/v1.rs (99%) rename crates/sui-sdk-types/src/{types => }/events.rs (100%) rename crates/sui-sdk-types/src/{types => }/execution_status.rs (100%) rename crates/sui-sdk-types/src/{types => }/framework.rs (100%) rename crates/sui-sdk-types/src/{types => }/gas.rs (100%) rename crates/sui-sdk-types/src/{types => }/object.rs (99%) rename crates/sui-sdk-types/src/{types => }/object_id.rs (100%) rename crates/sui-sdk-types/src/{types => }/serialization_proptests.rs (99%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/change-epoch (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/consensus-commit-prologue-v1 (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/genesis (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/ptb (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/update-transaction-fixtures/.gitignore (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/update-transaction-fixtures/Cargo.toml (100%) rename crates/sui-sdk-types/src/{types => }/transaction/fixtures/update-transaction-fixtures/src/main.rs (100%) rename crates/sui-sdk-types/src/{types => }/transaction/mod.rs (100%) rename crates/sui-sdk-types/src/{types => }/transaction/serialization.rs (99%) rename crates/sui-sdk-types/src/{types => }/type_tag/mod.rs (100%) rename crates/sui-sdk-types/src/{types => }/type_tag/parse.rs (100%) rename crates/sui-sdk-types/src/{types => }/type_tag/serialization.rs (100%) delete mode 100644 crates/sui-sdk-types/src/types/mod.rs rename crates/sui-sdk-types/src/{types => }/u256.rs (100%) diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/sui-crypto/src/ed25519.rs index b07c0fda5..a3856a44d 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/sui-crypto/src/ed25519.rs @@ -1,11 +1,11 @@ use crate::SignatureError; use crate::Signer; use crate::Verifier; -use sui_sdk_types::types::Ed25519PublicKey; -use sui_sdk_types::types::Ed25519Signature; -use sui_sdk_types::types::SignatureScheme; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::Ed25519PublicKey; +use sui_sdk_types::Ed25519Signature; +use sui_sdk_types::SignatureScheme; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::UserSignature; pub struct Ed25519PrivateKey(ed25519_dalek::SigningKey); @@ -269,7 +269,7 @@ mod test { use super::*; use crate::SuiSigner; use crate::SuiVerifier; - use sui_sdk_types::types::PersonalMessage; + use sui_sdk_types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/lib.rs b/crates/sui-crypto/src/lib.rs index fd77be2ad..ca20b0cbe 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/sui-crypto/src/lib.rs @@ -1,8 +1,8 @@ #![cfg_attr(doc_cfg, feature(doc_cfg))] -use sui_sdk_types::types::PersonalMessage; -use sui_sdk_types::types::Transaction; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::PersonalMessage; +use sui_sdk_types::Transaction; +use sui_sdk_types::UserSignature; pub use signature::Error as SignatureError; pub use signature::Signer; diff --git a/crates/sui-crypto/src/multisig.rs b/crates/sui-crypto/src/multisig.rs index cb03a140f..0afa933cd 100644 --- a/crates/sui-crypto/src/multisig.rs +++ b/crates/sui-crypto/src/multisig.rs @@ -1,10 +1,10 @@ use crate::SignatureError; use crate::Verifier; -use sui_sdk_types::types::MultisigAggregatedSignature; -use sui_sdk_types::types::MultisigCommittee; -use sui_sdk_types::types::MultisigMemberPublicKey; -use sui_sdk_types::types::MultisigMemberSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::MultisigAggregatedSignature; +use sui_sdk_types::MultisigCommittee; +use sui_sdk_types::MultisigMemberPublicKey; +use sui_sdk_types::MultisigMemberSignature; +use sui_sdk_types::UserSignature; #[derive(Default)] pub struct MultisigVerifier { @@ -267,7 +267,7 @@ pub struct MultisigAggregator { impl MultisigAggregator { pub fn new_with_transaction( committee: MultisigCommittee, - transaction: &sui_sdk_types::types::Transaction, + transaction: &sui_sdk_types::Transaction, ) -> Self { Self { committee, @@ -280,7 +280,7 @@ impl MultisigAggregator { pub fn new_with_message( committee: MultisigCommittee, - message: &sui_sdk_types::types::PersonalMessage<'_>, + message: &sui_sdk_types::PersonalMessage<'_>, ) -> Self { Self { committee, @@ -360,7 +360,7 @@ impl MultisigAggregator { fn multisig_pubkey_and_signature_from_user_signature( signature: UserSignature, ) -> Result<(MultisigMemberPublicKey, MultisigMemberSignature), SignatureError> { - use sui_sdk_types::types::SimpleSignature; + use sui_sdk_types::SimpleSignature; match signature { UserSignature::Simple(SimpleSignature::Ed25519 { signature, diff --git a/crates/sui-crypto/src/passkey.rs b/crates/sui-crypto/src/passkey.rs index dde00bd05..ddaf774b1 100644 --- a/crates/sui-crypto/src/passkey.rs +++ b/crates/sui-crypto/src/passkey.rs @@ -1,9 +1,9 @@ use crate::secp256r1::Secp256r1VerifyingKey; use crate::SignatureError; use signature::Verifier; -use sui_sdk_types::types::PasskeyAuthenticator; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::PasskeyAuthenticator; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::UserSignature; #[derive(Default, Clone, Debug)] pub struct PasskeyVerifier {} @@ -65,7 +65,7 @@ impl Verifier for PasskeyVerifier { mod test { use super::*; use crate::SuiVerifier; - use sui_sdk_types::types::Transaction; + use sui_sdk_types::Transaction; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/sui-crypto/src/secp256k1.rs index e1d2ca280..385a67d15 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/sui-crypto/src/secp256k1.rs @@ -4,11 +4,11 @@ use k256::ecdsa::VerifyingKey; use k256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk_types::types::Secp256k1PublicKey; -use sui_sdk_types::types::Secp256k1Signature; -use sui_sdk_types::types::SignatureScheme; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::Secp256k1PublicKey; +use sui_sdk_types::Secp256k1Signature; +use sui_sdk_types::SignatureScheme; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::UserSignature; pub struct Secp256k1PrivateKey(SigningKey); @@ -271,7 +271,7 @@ mod test { use super::*; use crate::SuiSigner; use crate::SuiVerifier; - use sui_sdk_types::types::PersonalMessage; + use sui_sdk_types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/sui-crypto/src/secp256r1.rs index 0f00b467c..b3fae1680 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/sui-crypto/src/secp256r1.rs @@ -4,11 +4,11 @@ use p256::ecdsa::VerifyingKey; use p256::elliptic_curve::group::GroupEncoding; use signature::Signer; use signature::Verifier; -use sui_sdk_types::types::Secp256r1PublicKey; -use sui_sdk_types::types::Secp256r1Signature; -use sui_sdk_types::types::SignatureScheme; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::Secp256r1PublicKey; +use sui_sdk_types::Secp256r1Signature; +use sui_sdk_types::SignatureScheme; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::UserSignature; pub struct Secp256r1PrivateKey(SigningKey); @@ -271,7 +271,7 @@ mod test { use super::*; use crate::SuiSigner; use crate::SuiVerifier; - use sui_sdk_types::types::PersonalMessage; + use sui_sdk_types::PersonalMessage; use test_strategy::proptest; #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-crypto/src/simple.rs b/crates/sui-crypto/src/simple.rs index db1ec9f28..0a9b8d95e 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/sui-crypto/src/simple.rs @@ -1,7 +1,7 @@ use crate::SignatureError; use signature::Verifier; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::UserSignature; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::UserSignature; pub struct SimpleVerifier; @@ -78,10 +78,10 @@ mod keypair { use crate::SignatureError; use signature::Signer; use signature::Verifier; - use sui_sdk_types::types::MultisigMemberPublicKey; - use sui_sdk_types::types::SignatureScheme; - use sui_sdk_types::types::SimpleSignature; - use sui_sdk_types::types::UserSignature; + use sui_sdk_types::MultisigMemberPublicKey; + use sui_sdk_types::SignatureScheme; + use sui_sdk_types::SimpleSignature; + use sui_sdk_types::UserSignature; pub struct SimpleKeypair { inner: InnerKeypair, diff --git a/crates/sui-crypto/src/zklogin/mod.rs b/crates/sui-crypto/src/zklogin/mod.rs index 929e3a021..2af4c38f4 100644 --- a/crates/sui-crypto/src/zklogin/mod.rs +++ b/crates/sui-crypto/src/zklogin/mod.rs @@ -3,12 +3,12 @@ use std::collections::HashMap; use crate::SignatureError; use poseidon::POSEIDON; use signature::Verifier; -use sui_sdk_types::types::Claim; -use sui_sdk_types::types::Jwk; -use sui_sdk_types::types::JwkId; -use sui_sdk_types::types::UserSignature; -use sui_sdk_types::types::ZkLoginAuthenticator; -use sui_sdk_types::types::ZkLoginInputs; +use sui_sdk_types::Claim; +use sui_sdk_types::Jwk; +use sui_sdk_types::JwkId; +use sui_sdk_types::UserSignature; +use sui_sdk_types::ZkLoginAuthenticator; +use sui_sdk_types::ZkLoginInputs; mod poseidon; mod verify; @@ -245,10 +245,10 @@ fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result Result { +) -> Result { const ISS: &str = "iss"; let iss = verify_extended_claim(&inputs.iss_base64_details, ISS)?; - sui_sdk_types::types::ZkLoginPublicIdentifier::new(iss, inputs.address_seed.clone()) + sui_sdk_types::ZkLoginPublicIdentifier::new(iss, inputs.address_seed.clone()) .ok_or_else(|| SignatureError::from_source("invalid iss")) } diff --git a/crates/sui-crypto/src/zklogin/tests.rs b/crates/sui-crypto/src/zklogin/tests.rs index 66e3b1078..0f9b25c65 100644 --- a/crates/sui-crypto/src/zklogin/tests.rs +++ b/crates/sui-crypto/src/zklogin/tests.rs @@ -1,5 +1,5 @@ use signature::Signer; -use sui_sdk_types::types::PersonalMessage; +use sui_sdk_types::PersonalMessage; use crate::ed25519::Ed25519PrivateKey; use crate::SuiVerifier; diff --git a/crates/sui-crypto/src/zklogin/verify.rs b/crates/sui-crypto/src/zklogin/verify.rs index 7fca3daaf..1af55fed0 100644 --- a/crates/sui-crypto/src/zklogin/verify.rs +++ b/crates/sui-crypto/src/zklogin/verify.rs @@ -11,16 +11,16 @@ use ark_bn254::G2Projective; use ark_ff::PrimeField; use ark_groth16::PreparedVerifyingKey; use ark_groth16::Proof; -use sui_sdk_types::types::Bn254FieldElement; -use sui_sdk_types::types::CircomG1; -use sui_sdk_types::types::CircomG2; -use sui_sdk_types::types::Ed25519PublicKey; -use sui_sdk_types::types::Jwk; -use sui_sdk_types::types::Secp256k1PublicKey; -use sui_sdk_types::types::Secp256r1PublicKey; -use sui_sdk_types::types::SimpleSignature; -use sui_sdk_types::types::ZkLoginInputs; -use sui_sdk_types::types::ZkLoginProof; +use sui_sdk_types::Bn254FieldElement; +use sui_sdk_types::CircomG1; +use sui_sdk_types::CircomG2; +use sui_sdk_types::Ed25519PublicKey; +use sui_sdk_types::Jwk; +use sui_sdk_types::Secp256k1PublicKey; +use sui_sdk_types::Secp256r1PublicKey; +use sui_sdk_types::SimpleSignature; +use sui_sdk_types::ZkLoginInputs; +use sui_sdk_types::ZkLoginProof; use super::POSEIDON; @@ -522,7 +522,7 @@ pub(crate) fn gen_address_seed_with_salt_hash( #[cfg(test)] mod test { use super::*; - use sui_sdk_types::types::Ed25519Signature; + use sui_sdk_types::Ed25519Signature; #[cfg(test)] #[cfg(target_arch = "wasm32")] diff --git a/crates/sui-graphql-client/README.md b/crates/sui-graphql-client/README.md index efe439704..808e7467b 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/sui-graphql-client/README.md @@ -42,7 +42,7 @@ The client provides an API to request gas from the faucet. The `request_and_wait ### Example for standard devnet/testnet/local networks. ```rust, no_run use sui_graphql_client::faucet::FaucetClient; -use sui_types::types::Address; +use sui_types::Address; use anyhow::Result; use std::str::FromStr; @@ -70,7 +70,7 @@ async fn main() -> Result<()> { Note that this [`FaucetClient`] is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.sui.io`). ```rust, no_run use sui_graphql_client::faucet::FaucetClient; -use sui_types::types::Address; +use sui_types::Address; use anyhow::Result; use std::str::FromStr; @@ -155,7 +155,7 @@ use sui_graphql_client::{ query_types::{schema, BigInt}, Client, }; -use sui_types::types::Address; +use sui_types::Address; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] diff --git a/crates/sui-graphql-client/src/error.rs b/crates/sui-graphql-client/src/error.rs index c79873b2a..2d04a84c2 100644 --- a/crates/sui-graphql-client/src/error.rs +++ b/crates/sui-graphql-client/src/error.rs @@ -6,9 +6,9 @@ use std::num::TryFromIntError; use cynic::GraphQlError; -use sui_types::types::AddressParseError; -use sui_types::types::DigestParseError; -use sui_types::types::TypeParseError; +use sui_types::AddressParseError; +use sui_types::DigestParseError; +use sui_types::TypeParseError; type BoxError = Box; diff --git a/crates/sui-graphql-client/src/faucet.rs b/crates/sui-graphql-client/src/faucet.rs index 68df43c34..dd0b23962 100644 --- a/crates/sui-graphql-client/src/faucet.rs +++ b/crates/sui-graphql-client/src/faucet.rs @@ -1,9 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use sui_types::types::Address; -use sui_types::types::ObjectId; -use sui_types::types::TransactionDigest; +use sui_types::Address; +use sui_types::ObjectId; +use sui_types::TransactionDigest; use anyhow::anyhow; use anyhow::bail; diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/sui-graphql-client/src/lib.rs index 9be1e70c4..d1c7c0f81 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/sui-graphql-client/src/lib.rs @@ -79,21 +79,21 @@ use query_types::TransactionsFilter; use query_types::Validator; use streams::stream_paginated_query; -use sui_types::types::framework::Coin; -use sui_types::types::Address; -use sui_types::types::CheckpointDigest; -use sui_types::types::CheckpointSequenceNumber; -use sui_types::types::CheckpointSummary; -use sui_types::types::Event; -use sui_types::types::MovePackage; -use sui_types::types::Object; -use sui_types::types::SignedTransaction; -use sui_types::types::Transaction; -use sui_types::types::TransactionDigest; -use sui_types::types::TransactionEffects; -use sui_types::types::TransactionKind; -use sui_types::types::TypeTag; -use sui_types::types::UserSignature; +use sui_types::framework::Coin; +use sui_types::Address; +use sui_types::CheckpointDigest; +use sui_types::CheckpointSequenceNumber; +use sui_types::CheckpointSummary; +use sui_types::Event; +use sui_types::MovePackage; +use sui_types::Object; +use sui_types::SignedTransaction; +use sui_types::Transaction; +use sui_types::TransactionDigest; +use sui_types::TransactionEffects; +use sui_types::TransactionKind; +use sui_types::TypeTag; +use sui_types::UserSignature; use base64ct::Encoding; use cynic::serde; @@ -1011,7 +1011,7 @@ impl Client { .transpose()?; let object = bcs - .map(|b| bcs::from_bytes::(&b)) + .map(|b| bcs::from_bytes::(&b)) .transpose()?; Ok(object) @@ -1070,7 +1070,7 @@ impl Client { .collect::, base64ct::Error>>()?; let objects = bcs .iter() - .map(|b| bcs::from_bytes::(b)) + .map(|b| bcs::from_bytes::(b)) .collect::, bcs::Error>>()?; Ok(Page::new(page_info, objects)) @@ -1720,8 +1720,8 @@ impl Client { mod tests { use base64ct::Encoding; use futures::StreamExt; - use sui_types::types::Ed25519PublicKey; - use sui_types::types::TypeTag; + use sui_types::Ed25519PublicKey; + use sui_types::TypeTag; use crate::faucet::FaucetClient; use crate::BcsName; diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/sui-graphql-client/src/query_types/checkpoint.rs index 3e99e56f8..3e6cf886a 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/sui-graphql-client/src/query_types/checkpoint.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use base64ct::Encoding; -use sui_types::types::CheckpointSummary; +use sui_types::CheckpointSummary; use crate::error; use crate::error::Error; diff --git a/crates/sui-graphql-client/src/query_types/dry_run.rs b/crates/sui-graphql-client/src/query_types/dry_run.rs index 9d01d44f6..5d6c26cb6 100644 --- a/crates/sui-graphql-client/src/query_types/dry_run.rs +++ b/crates/sui-graphql-client/src/query_types/dry_run.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use sui_types::types::ObjectReference; +use sui_types::ObjectReference; use crate::query_types::schema; use crate::query_types::Address; diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs index 3d19c1aff..61f683065 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/sui-graphql-client/src/query_types/dynamic_fields.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use base64ct::Encoding; -use sui_types::types::TypeTag; +use sui_types::TypeTag; use crate::error; use crate::query_types::schema; diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs index d58d2fbca..080800e1e 100644 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ b/crates/sui-graphql-client/src/query_types/mod.rs @@ -107,7 +107,7 @@ pub use transaction::TransactionBlocksQuery; pub use transaction::TransactionBlocksQueryArgs; pub use transaction::TransactionsFilter; -use sui_types::types::Address; +use sui_types::Address; use cynic::impl_scalar; use serde_json::Value as JsonValue; diff --git a/crates/sui-graphql-client/src/query_types/packages.rs b/crates/sui-graphql-client/src/query_types/packages.rs index 0f86100f5..347317fc6 100644 --- a/crates/sui-graphql-client/src/query_types/packages.rs +++ b/crates/sui-graphql-client/src/query_types/packages.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use sui_types::types::Address; +use sui_types::Address; use crate::query_types::schema; use crate::query_types::Base64; diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/sui-graphql-client/src/query_types/transaction.rs index a384c2238..29b888014 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/sui-graphql-client/src/query_types/transaction.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use base64ct::Encoding; -use sui_types::types::SignedTransaction; -use sui_types::types::Transaction; -use sui_types::types::TransactionEffects; -use sui_types::types::UserSignature; +use sui_types::SignedTransaction; +use sui_types::Transaction; +use sui_types::TransactionEffects; +use sui_types::UserSignature; use crate::error; use crate::error::Error; diff --git a/crates/sui-sdk-types/src/types/address.rs b/crates/sui-sdk-types/src/address.rs similarity index 100% rename from crates/sui-sdk-types/src/types/address.rs rename to crates/sui-sdk-types/src/address.rs diff --git a/crates/sui-sdk-types/src/types/checkpoint.rs b/crates/sui-sdk-types/src/checkpoint.rs similarity index 100% rename from crates/sui-sdk-types/src/types/checkpoint.rs rename to crates/sui-sdk-types/src/checkpoint.rs diff --git a/crates/sui-sdk-types/src/types/crypto/bls12381.rs b/crates/sui-sdk-types/src/crypto/bls12381.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/bls12381.rs rename to crates/sui-sdk-types/src/crypto/bls12381.rs diff --git a/crates/sui-sdk-types/src/types/crypto/ed25519.rs b/crates/sui-sdk-types/src/crypto/ed25519.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/ed25519.rs rename to crates/sui-sdk-types/src/crypto/ed25519.rs diff --git a/crates/sui-sdk-types/src/types/crypto/intent.rs b/crates/sui-sdk-types/src/crypto/intent.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/intent.rs rename to crates/sui-sdk-types/src/crypto/intent.rs diff --git a/crates/sui-sdk-types/src/types/crypto/mod.rs b/crates/sui-sdk-types/src/crypto/mod.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/mod.rs rename to crates/sui-sdk-types/src/crypto/mod.rs diff --git a/crates/sui-sdk-types/src/types/crypto/multisig.rs b/crates/sui-sdk-types/src/crypto/multisig.rs similarity index 99% rename from crates/sui-sdk-types/src/types/crypto/multisig.rs rename to crates/sui-sdk-types/src/crypto/multisig.rs index c679a2e21..c232649a3 100644 --- a/crates/sui-sdk-types/src/types/crypto/multisig.rs +++ b/crates/sui-sdk-types/src/crypto/multisig.rs @@ -184,7 +184,7 @@ mod serialization { use serde_with::{Bytes, DeserializeAs}; use super::*; - use crate::types::{ + use crate::{ Ed25519PublicKey, Secp256k1PublicKey, Secp256r1PublicKey, SignatureScheme, crypto::SignatureFromBytesError, }; diff --git a/crates/sui-sdk-types/src/types/crypto/passkey.rs b/crates/sui-sdk-types/src/crypto/passkey.rs similarity index 99% rename from crates/sui-sdk-types/src/types/crypto/passkey.rs rename to crates/sui-sdk-types/src/crypto/passkey.rs index 88d566c8f..a19f131c8 100644 --- a/crates/sui-sdk-types/src/types/crypto/passkey.rs +++ b/crates/sui-sdk-types/src/crypto/passkey.rs @@ -68,7 +68,7 @@ mod serialization { use serde_with::{Bytes, DeserializeAs}; use super::*; - use crate::types::{SignatureScheme, SimpleSignature, crypto::SignatureFromBytesError}; + use crate::{SignatureScheme, SimpleSignature, crypto::SignatureFromBytesError}; #[derive(serde::Serialize)] struct AuthenticatorRef<'a> { @@ -354,7 +354,7 @@ impl proptest::arbitrary::Arbitrary for PasskeyAuthenticator { #[cfg(test)] mod tests { - use crate::types::UserSignature; + use crate::UserSignature; #[test] fn base64_encoded_passkey_user_signature() { diff --git a/crates/sui-sdk-types/src/types/crypto/secp256k1.rs b/crates/sui-sdk-types/src/crypto/secp256k1.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/secp256k1.rs rename to crates/sui-sdk-types/src/crypto/secp256k1.rs diff --git a/crates/sui-sdk-types/src/types/crypto/secp256r1.rs b/crates/sui-sdk-types/src/crypto/secp256r1.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/secp256r1.rs rename to crates/sui-sdk-types/src/crypto/secp256r1.rs diff --git a/crates/sui-sdk-types/src/types/crypto/signature.rs b/crates/sui-sdk-types/src/crypto/signature.rs similarity index 100% rename from crates/sui-sdk-types/src/types/crypto/signature.rs rename to crates/sui-sdk-types/src/crypto/signature.rs diff --git a/crates/sui-sdk-types/src/types/crypto/validator.rs b/crates/sui-sdk-types/src/crypto/validator.rs similarity index 98% rename from crates/sui-sdk-types/src/types/crypto/validator.rs rename to crates/sui-sdk-types/src/crypto/validator.rs index a0916bd16..4cb499652 100644 --- a/crates/sui-sdk-types/src/types/crypto/validator.rs +++ b/crates/sui-sdk-types/src/crypto/validator.rs @@ -1,5 +1,5 @@ use super::{Bls12381PublicKey, Bls12381Signature}; -use crate::types::checkpoint::{EpochId, StakeUnit}; +use crate::checkpoint::{EpochId, StakeUnit}; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr( diff --git a/crates/sui-sdk-types/src/types/crypto/zklogin.rs b/crates/sui-sdk-types/src/crypto/zklogin.rs similarity index 99% rename from crates/sui-sdk-types/src/types/crypto/zklogin.rs rename to crates/sui-sdk-types/src/crypto/zklogin.rs index c0cd029fe..27c74f632 100644 --- a/crates/sui-sdk-types/src/types/crypto/zklogin.rs +++ b/crates/sui-sdk-types/src/crypto/zklogin.rs @@ -1,5 +1,5 @@ use super::SimpleSignature; -use crate::types::{checkpoint::EpochId, u256::U256}; +use crate::{checkpoint::EpochId, u256::U256}; /// An zk login authenticator with all the necessary fields. #[derive(Debug, Clone, PartialEq, Eq)] @@ -258,7 +258,7 @@ mod serialization { use serde_with::{Bytes, DeserializeAs, SerializeAs}; use super::*; - use crate::types::{SignatureScheme, crypto::SignatureFromBytesError}; + use crate::{SignatureScheme, crypto::SignatureFromBytesError}; // Serialized format is: iss_bytes_len || iss_bytes || // padded_32_byte_address_seed. diff --git a/crates/sui-sdk-types/src/types/digest.rs b/crates/sui-sdk-types/src/digest.rs similarity index 100% rename from crates/sui-sdk-types/src/types/digest.rs rename to crates/sui-sdk-types/src/digest.rs diff --git a/crates/sui-sdk-types/src/types/effects/fixtures/genesis-transaction-effects b/crates/sui-sdk-types/src/effects/fixtures/genesis-transaction-effects similarity index 100% rename from crates/sui-sdk-types/src/types/effects/fixtures/genesis-transaction-effects rename to crates/sui-sdk-types/src/effects/fixtures/genesis-transaction-effects diff --git a/crates/sui-sdk-types/src/types/effects/fixtures/sponsor-tx-effects b/crates/sui-sdk-types/src/effects/fixtures/sponsor-tx-effects similarity index 100% rename from crates/sui-sdk-types/src/types/effects/fixtures/sponsor-tx-effects rename to crates/sui-sdk-types/src/effects/fixtures/sponsor-tx-effects diff --git a/crates/sui-sdk-types/src/types/effects/mod.rs b/crates/sui-sdk-types/src/effects/mod.rs similarity index 97% rename from crates/sui-sdk-types/src/types/effects/mod.rs rename to crates/sui-sdk-types/src/effects/mod.rs index 79f93bd3b..579b89dae 100644 --- a/crates/sui-sdk-types/src/types/effects/mod.rs +++ b/crates/sui-sdk-types/src/effects/mod.rs @@ -5,7 +5,7 @@ pub use v1::{ UnchangedSharedObject, }; -use crate::types::execution_status::ExecutionStatus; +use crate::execution_status::ExecutionStatus; /// The response from processing a transaction or a certified transaction #[derive(Eq, PartialEq, Clone, Debug)] @@ -38,7 +38,7 @@ impl TransactionEffects { } /// Return the gas cost summary of the transaction. - pub fn gas_summary(&self) -> &crate::types::gas::GasCostSummary { + pub fn gas_summary(&self) -> &crate::gas::GasCostSummary { match self { TransactionEffects::V1(e) => e.gas_summary(), TransactionEffects::V2(e) => e.gas_summary(), diff --git a/crates/sui-sdk-types/src/types/effects/v1.rs b/crates/sui-sdk-types/src/effects/v1.rs similarity index 99% rename from crates/sui-sdk-types/src/types/effects/v1.rs rename to crates/sui-sdk-types/src/effects/v1.rs index cb9be7f1b..b7e9657b9 100644 --- a/crates/sui-sdk-types/src/types/effects/v1.rs +++ b/crates/sui-sdk-types/src/effects/v1.rs @@ -1,4 +1,4 @@ -use crate::types::{ +use crate::{ EpochId, GasCostSummary, ObjectDigest, ObjectId, TransactionDigest, TransactionEventsDigest, digest::EffectsAuxiliaryDataDigest, execution_status::ExecutionStatus, diff --git a/crates/sui-sdk-types/src/types/events.rs b/crates/sui-sdk-types/src/events.rs similarity index 100% rename from crates/sui-sdk-types/src/types/events.rs rename to crates/sui-sdk-types/src/events.rs diff --git a/crates/sui-sdk-types/src/types/execution_status.rs b/crates/sui-sdk-types/src/execution_status.rs similarity index 100% rename from crates/sui-sdk-types/src/types/execution_status.rs rename to crates/sui-sdk-types/src/execution_status.rs diff --git a/crates/sui-sdk-types/src/types/framework.rs b/crates/sui-sdk-types/src/framework.rs similarity index 100% rename from crates/sui-sdk-types/src/types/framework.rs rename to crates/sui-sdk-types/src/framework.rs diff --git a/crates/sui-sdk-types/src/types/gas.rs b/crates/sui-sdk-types/src/gas.rs similarity index 100% rename from crates/sui-sdk-types/src/types/gas.rs rename to crates/sui-sdk-types/src/gas.rs diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/sui-sdk-types/src/hash.rs index fcef27d14..d796fe108 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/sui-sdk-types/src/hash.rs @@ -1,6 +1,6 @@ use blake2::Digest as DigestTrait; -use crate::types::{Address, Digest}; +use crate::{Address, Digest}; type Blake2b256 = blake2::Blake2b; @@ -43,7 +43,7 @@ impl std::io::Write for Hasher { } } -impl crate::types::Ed25519PublicKey { +impl crate::Ed25519PublicKey { pub fn to_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); @@ -57,7 +57,7 @@ impl crate::types::Ed25519PublicKey { } } -impl crate::types::Secp256k1PublicKey { +impl crate::Secp256k1PublicKey { pub fn to_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); @@ -71,7 +71,7 @@ impl crate::types::Secp256k1PublicKey { } } -impl crate::types::Secp256r1PublicKey { +impl crate::Secp256r1PublicKey { pub fn to_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); @@ -85,7 +85,7 @@ impl crate::types::Secp256r1PublicKey { } } -impl crate::types::ZkLoginPublicIdentifier { +impl crate::ZkLoginPublicIdentifier { /// Define as iss_bytes_len || iss_bytes || padded_32_byte_address_seed. pub fn to_address_padded(&self) -> Address { let mut hasher = Hasher::new(); @@ -113,7 +113,7 @@ impl crate::types::ZkLoginPublicIdentifier { } } -impl crate::types::PasskeyPublicKey { +impl crate::PasskeyPublicKey { pub fn to_address(&self) -> Address { let mut hasher = Hasher::new(); self.write_into_hasher(&mut hasher); @@ -127,7 +127,7 @@ impl crate::types::PasskeyPublicKey { } } -impl crate::types::MultisigCommittee { +impl crate::MultisigCommittee { /// Derive an Address from a MultisigCommittee. A MultiSig address /// is defined as the 32-byte Blake2b hash of serializing the flag, the /// threshold, concatenation of all n flag, public keys and @@ -137,7 +137,7 @@ impl crate::types::MultisigCommittee { /// When flag_i is ZkLogin, pk_i refers to [struct ZkLoginPublicIdentifier] /// derived from padded address seed in bytes and iss. pub fn to_address(&self) -> Address { - use crate::types::MultisigMemberPublicKey::*; + use crate::MultisigMemberPublicKey::*; let mut hasher = Hasher::new(); hasher.update([self.scheme().to_u8()]); @@ -163,19 +163,11 @@ impl crate::types::MultisigCommittee { #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod type_digest { use super::Hasher; - use crate::types::CheckpointContents; - use crate::types::CheckpointContentsDigest; - use crate::types::CheckpointDigest; - use crate::types::CheckpointSummary; - use crate::types::Digest; - use crate::types::Object; - use crate::types::ObjectDigest; - use crate::types::Transaction; - use crate::types::TransactionDigest; - use crate::types::TransactionEffects; - use crate::types::TransactionEffectsDigest; - use crate::types::TransactionEvents; - use crate::types::TransactionEventsDigest; + use crate::{ + CheckpointContents, CheckpointContentsDigest, CheckpointDigest, CheckpointSummary, Digest, + Object, ObjectDigest, Transaction, TransactionDigest, TransactionEffects, + TransactionEffectsDigest, TransactionEvents, TransactionEventsDigest, + }; impl Object { pub fn digest(&self) -> ObjectDigest { @@ -237,11 +229,8 @@ mod type_digest { #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod signing_message { use crate::{ - hash::Hasher, - types::{ - Digest, Intent, IntentAppId, IntentScope, IntentVersion, PersonalMessage, - SigningDigest, Transaction, - }, + Digest, Intent, IntentAppId, IntentScope, IntentVersion, PersonalMessage, SigningDigest, + Transaction, hash::Hasher, }; impl Transaction { @@ -288,12 +277,12 @@ enum HashingIntent { RegularObjectId = 0xf1, } -impl crate::types::ObjectId { +impl crate::ObjectId { /// Create an ObjectId from `TransactionDigest` and `count`. /// /// `count` is the number of objects that have been created during a /// transactions. - pub fn derive_id(digest: crate::types::TransactionDigest, count: u64) -> Self { + pub fn derive_id(digest: crate::TransactionDigest, count: u64) -> Self { let mut hasher = Hasher::new(); hasher.update([HashingIntent::RegularObjectId as u8]); hasher.update(digest); @@ -307,11 +296,7 @@ impl crate::types::ObjectId { /// hash(parent || len(key) || key || key_type_tag) #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] - pub fn derive_dynamic_child_id( - &self, - key_type_tag: &crate::types::TypeTag, - key_bytes: &[u8], - ) -> Self { + pub fn derive_dynamic_child_id(&self, key_type_tag: &crate::TypeTag, key_bytes: &[u8]) -> Self { let mut hasher = Hasher::new(); hasher.update([HashingIntent::ChildObjectId as u8]); hasher.update(self); @@ -336,7 +321,7 @@ mod test { use wasm_bindgen_test::wasm_bindgen_test as test; use super::HashingIntent; - use crate::types::SignatureScheme; + use crate::SignatureScheme; impl HashingIntent { fn from_byte(byte: u8) -> Result { diff --git a/crates/sui-sdk-types/src/lib.rs b/crates/sui-sdk-types/src/lib.rs index 71944ccea..bc16f9405 100644 --- a/crates/sui-sdk-types/src/lib.rs +++ b/crates/sui-sdk-types/src/lib.rs @@ -1,11 +1,164 @@ #![cfg_attr(doc_cfg, feature(doc_cfg))] -pub mod types; - #[cfg(feature = "hash")] #[cfg_attr(doc_cfg, doc(cfg(feature = "hash")))] pub mod hash; +mod address; +mod checkpoint; +mod crypto; +mod digest; +mod effects; +mod events; +mod execution_status; +pub mod framework; +mod gas; +mod object; +mod object_id; +mod transaction; +mod type_tag; +mod u256; + +pub use address::Address; +pub use address::AddressParseError; +pub use checkpoint::CheckpointCommitment; +pub use checkpoint::CheckpointContents; +pub use checkpoint::CheckpointData; +pub use checkpoint::CheckpointSequenceNumber; +pub use checkpoint::CheckpointSummary; +pub use checkpoint::CheckpointTimestamp; +pub use checkpoint::CheckpointTransaction; +pub use checkpoint::CheckpointTransactionInfo; +pub use checkpoint::EndOfEpochData; +pub use checkpoint::EpochId; +pub use checkpoint::ProtocolVersion; +pub use checkpoint::SignedCheckpointSummary; +pub use checkpoint::StakeUnit; +pub use crypto::Bls12381PublicKey; +pub use crypto::Bls12381Signature; +pub use crypto::Bn254FieldElement; +pub use crypto::CircomG1; +pub use crypto::CircomG2; +pub use crypto::Claim; +pub use crypto::Ed25519PublicKey; +pub use crypto::Ed25519Signature; +pub use crypto::Intent; +pub use crypto::IntentAppId; +pub use crypto::IntentScope; +pub use crypto::IntentVersion; +pub use crypto::Jwk; +pub use crypto::JwkId; +pub use crypto::MultisigAggregatedSignature; +pub use crypto::MultisigCommittee; +pub use crypto::MultisigMember; +pub use crypto::MultisigMemberPublicKey; +pub use crypto::MultisigMemberSignature; +pub use crypto::PasskeyAuthenticator; +pub use crypto::PasskeyPublicKey; +pub use crypto::Secp256k1PublicKey; +pub use crypto::Secp256k1Signature; +pub use crypto::Secp256r1PublicKey; +pub use crypto::Secp256r1Signature; +pub use crypto::SignatureScheme; +pub use crypto::SimpleSignature; +pub use crypto::UserSignature; +pub use crypto::ValidatorAggregatedSignature; +pub use crypto::ValidatorCommittee; +pub use crypto::ValidatorCommitteeMember; +pub use crypto::ValidatorSignature; +pub use crypto::ZkLoginAuthenticator; +pub use crypto::ZkLoginInputs; +pub use crypto::ZkLoginProof; +pub use crypto::ZkLoginPublicIdentifier; +pub use digest::CheckpointContentsDigest; +pub use digest::CheckpointDigest; +pub use digest::ConsensusCommitDigest; +pub use digest::Digest; +pub use digest::DigestParseError; +pub use digest::EffectsAuxiliaryDataDigest; +pub use digest::ObjectDigest; +pub use digest::SigningDigest; +pub use digest::TransactionDigest; +pub use digest::TransactionEffectsDigest; +pub use digest::TransactionEventsDigest; +pub use effects::ChangedObject; +pub use effects::IdOperation; +pub use effects::ModifiedAtVersion; +pub use effects::ObjectIn; +pub use effects::ObjectOut; +pub use effects::ObjectReferenceWithOwner; +pub use effects::TransactionEffects; +pub use effects::TransactionEffectsV1; +pub use effects::TransactionEffectsV2; +pub use effects::UnchangedSharedKind; +pub use effects::UnchangedSharedObject; +pub use events::BalanceChange; +pub use events::Event; +pub use events::TransactionEvents; +pub use execution_status::CommandArgumentError; +pub use execution_status::ExecutionError; +pub use execution_status::ExecutionStatus; +pub use execution_status::MoveLocation; +pub use execution_status::PackageUpgradeError; +pub use execution_status::TypeArgumentError; +pub use gas::GasCostSummary; +pub use object::GenesisObject; +pub use object::MovePackage; +pub use object::MoveStruct; +pub use object::Object; +pub use object::ObjectData; +pub use object::ObjectReference; +pub use object::ObjectType; +pub use object::Owner; +pub use object::TypeOrigin; +pub use object::UpgradeInfo; +pub use object::Version; +pub use object_id::ObjectId; +pub use transaction::ActiveJwk; +pub use transaction::Argument; +pub use transaction::AuthenticatorStateExpire; +pub use transaction::AuthenticatorStateUpdate; +pub use transaction::CancelledTransaction; +pub use transaction::ChangeEpoch; +pub use transaction::Command; +pub use transaction::ConsensusCommitPrologue; +pub use transaction::ConsensusCommitPrologueV2; +pub use transaction::ConsensusCommitPrologueV3; +pub use transaction::ConsensusDeterminedVersionAssignments; +pub use transaction::EndOfEpochTransactionKind; +pub use transaction::GasPayment; +pub use transaction::GenesisTransaction; +pub use transaction::Input; +pub use transaction::MakeMoveVector; +pub use transaction::MergeCoins; +pub use transaction::MoveCall; +pub use transaction::ProgrammableTransaction; +pub use transaction::Publish; +pub use transaction::RandomnessStateUpdate; +pub use transaction::SignedTransaction; +pub use transaction::SplitCoins; +pub use transaction::SystemPackage; +pub use transaction::Transaction; +pub use transaction::TransactionExpiration; +pub use transaction::TransactionKind; +pub use transaction::TransferObjects; +pub use transaction::Upgrade; +pub use transaction::VersionAssignment; +pub use type_tag::Identifier; +pub use type_tag::StructTag; +pub use type_tag::TypeParseError; +pub use type_tag::TypeTag; + +#[cfg(feature = "serde")] +#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] +pub(crate) use transaction::SignedTransactionWithIntentMessage; + +#[cfg(test)] +mod serialization_proptests; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PersonalMessage<'a>(pub std::borrow::Cow<'a, [u8]>); + #[cfg(feature = "serde")] mod _serde { use std::borrow::Cow; @@ -114,7 +267,7 @@ mod _serde { } } - pub(crate) use super::types::SignedTransactionWithIntentMessage; + pub(crate) use super::SignedTransactionWithIntentMessage; } #[cfg(feature = "schemars")] diff --git a/crates/sui-sdk-types/src/types/object.rs b/crates/sui-sdk-types/src/object.rs similarity index 99% rename from crates/sui-sdk-types/src/types/object.rs rename to crates/sui-sdk-types/src/object.rs index 28184fefa..bb4b08d92 100644 --- a/crates/sui-sdk-types/src/types/object.rs +++ b/crates/sui-sdk-types/src/object.rs @@ -380,7 +380,7 @@ mod serialization { use serde_with::{DeserializeAs, SerializeAs}; use super::*; - use crate::types::TypeTag; + use crate::TypeTag; #[test] fn obj() { @@ -908,7 +908,7 @@ mod serialization { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; - use crate::types::object::Object; + use crate::object::Object; #[test] fn object_fixture() { diff --git a/crates/sui-sdk-types/src/types/object_id.rs b/crates/sui-sdk-types/src/object_id.rs similarity index 100% rename from crates/sui-sdk-types/src/types/object_id.rs rename to crates/sui-sdk-types/src/object_id.rs diff --git a/crates/sui-sdk-types/src/types/serialization_proptests.rs b/crates/sui-sdk-types/src/serialization_proptests.rs similarity index 99% rename from crates/sui-sdk-types/src/types/serialization_proptests.rs rename to crates/sui-sdk-types/src/serialization_proptests.rs index 29bbebf5f..da7ace404 100644 --- a/crates/sui-sdk-types/src/types/serialization_proptests.rs +++ b/crates/sui-sdk-types/src/serialization_proptests.rs @@ -2,7 +2,7 @@ use test_strategy::proptest; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; -use crate::types::*; +use crate::*; macro_rules! serialization_test { ($type:ident) => { diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/change-epoch b/crates/sui-sdk-types/src/transaction/fixtures/change-epoch similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/change-epoch rename to crates/sui-sdk-types/src/transaction/fixtures/change-epoch diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/consensus-commit-prologue-v1 b/crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/consensus-commit-prologue-v1 rename to crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/genesis b/crates/sui-sdk-types/src/transaction/fixtures/genesis similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/genesis rename to crates/sui-sdk-types/src/transaction/fixtures/genesis diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/ptb b/crates/sui-sdk-types/src/transaction/fixtures/ptb similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/ptb rename to crates/sui-sdk-types/src/transaction/fixtures/ptb diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore b/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/.gitignore rename to crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml b/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/Cargo.toml rename to crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml diff --git a/crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs b/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/fixtures/update-transaction-fixtures/src/main.rs rename to crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs diff --git a/crates/sui-sdk-types/src/types/transaction/mod.rs b/crates/sui-sdk-types/src/transaction/mod.rs similarity index 100% rename from crates/sui-sdk-types/src/types/transaction/mod.rs rename to crates/sui-sdk-types/src/transaction/mod.rs diff --git a/crates/sui-sdk-types/src/types/transaction/serialization.rs b/crates/sui-sdk-types/src/transaction/serialization.rs similarity index 99% rename from crates/sui-sdk-types/src/types/transaction/serialization.rs rename to crates/sui-sdk-types/src/transaction/serialization.rs index 425f670e4..8d628463b 100644 --- a/crates/sui-sdk-types/src/types/transaction/serialization.rs +++ b/crates/sui-sdk-types/src/transaction/serialization.rs @@ -2,11 +2,11 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; use super::Argument; -use crate::types::{ObjectId, ObjectReference}; +use crate::{ObjectId, ObjectReference}; mod transaction { use super::*; - use crate::types::{ + use crate::{ Address, transaction::{GasPayment, Transaction, TransactionExpiration, TransactionKind}, }; @@ -123,7 +123,7 @@ mod transaction { mod transaction_kind { use super::*; - use crate::types::transaction::{ + use crate::transaction::{ AuthenticatorStateUpdateV1, ConsensusCommitPrologueV1, EndOfEpochTransactionKind, GenesisTransaction, ProgrammableTransaction, RandomnessStateUpdate, TransactionKind, }; @@ -279,7 +279,7 @@ mod transaction_kind { mod end_of_epoch { use super::*; - use crate::types::{ + use crate::{ CheckpointDigest, transaction::{ AuthenticatorStateExpire, ChangeEpoch, ChangeEpochV2, EndOfEpochTransactionKind, @@ -445,7 +445,7 @@ mod end_of_epoch { mod version_assignments { use super::*; - use crate::types::transaction::{CancelledTransaction, ConsensusDeterminedVersionAssignments}; + use crate::transaction::{CancelledTransaction, ConsensusDeterminedVersionAssignments}; #[derive(serde_derive::Serialize)] #[serde(tag = "kind", rename_all = "snake_case")] @@ -536,7 +536,7 @@ mod version_assignments { mod input_argument { use super::*; - use crate::types::transaction::{Input, InputArgument}; + use crate::transaction::{Input, InputArgument}; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] @@ -785,7 +785,7 @@ mod argument { mod command { use super::*; - use crate::types::transaction::{ + use crate::transaction::{ Command, MakeMoveVector, MergeCoins, MoveCall, Publish, SplitCoins, TransferObjects, Upgrade, }; @@ -903,7 +903,7 @@ mod signed_transaction { use serde::ser::SerializeSeq; use super::*; - use crate::types::{ + use crate::{ UserSignature, transaction::{SignedTransaction, Transaction}, }; @@ -1041,7 +1041,7 @@ mod signed_transaction { mod transaction_expiration { use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use crate::types::{EpochId, TransactionExpiration}; + use crate::{EpochId, TransactionExpiration}; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(rename = "TransactionExpiration")] @@ -1142,7 +1142,7 @@ mod test { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; - use crate::types::{ + use crate::{ ObjectDigest, ObjectId, ObjectReference, transaction::{Argument, Input, InputArgument, Transaction}, }; diff --git a/crates/sui-sdk-types/src/types/type_tag/mod.rs b/crates/sui-sdk-types/src/type_tag/mod.rs similarity index 100% rename from crates/sui-sdk-types/src/types/type_tag/mod.rs rename to crates/sui-sdk-types/src/type_tag/mod.rs diff --git a/crates/sui-sdk-types/src/types/type_tag/parse.rs b/crates/sui-sdk-types/src/type_tag/parse.rs similarity index 100% rename from crates/sui-sdk-types/src/types/type_tag/parse.rs rename to crates/sui-sdk-types/src/type_tag/parse.rs diff --git a/crates/sui-sdk-types/src/types/type_tag/serialization.rs b/crates/sui-sdk-types/src/type_tag/serialization.rs similarity index 100% rename from crates/sui-sdk-types/src/types/type_tag/serialization.rs rename to crates/sui-sdk-types/src/type_tag/serialization.rs diff --git a/crates/sui-sdk-types/src/types/mod.rs b/crates/sui-sdk-types/src/types/mod.rs deleted file mode 100644 index fc094b703..000000000 --- a/crates/sui-sdk-types/src/types/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -pub mod address; -pub mod checkpoint; -pub mod crypto; -pub mod digest; -pub mod effects; -pub mod events; -pub mod execution_status; -pub mod framework; -mod gas; -mod object; -mod object_id; -mod transaction; -mod type_tag; -mod u256; - -pub use address::{Address, AddressParseError}; -pub use checkpoint::{ - CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber, - CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo, - EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit, -}; -pub use crypto::{ - Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, - Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, - JwkId, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, - MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PublicKey, - Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, - UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, - ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, -}; -pub use digest::{ - CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, - EffectsAuxiliaryDataDigest, ObjectDigest, SigningDigest, TransactionDigest, - TransactionEffectsDigest, TransactionEventsDigest, -}; -pub use effects::{ - ChangedObject, IdOperation, ModifiedAtVersion, ObjectIn, ObjectOut, ObjectReferenceWithOwner, - TransactionEffects, TransactionEffectsV1, TransactionEffectsV2, UnchangedSharedKind, - UnchangedSharedObject, -}; -pub use events::{BalanceChange, Event, TransactionEvents}; -pub use execution_status::{ - CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError, - TypeArgumentError, -}; -pub use gas::GasCostSummary; -pub use object::{ - GenesisObject, MovePackage, MoveStruct, Object, ObjectData, ObjectReference, ObjectType, Owner, - TypeOrigin, UpgradeInfo, Version, -}; -pub use object_id::ObjectId; -#[cfg(feature = "serde")] -#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] -pub(crate) use transaction::SignedTransactionWithIntentMessage; -pub use transaction::{ - ActiveJwk, Argument, AuthenticatorStateExpire, AuthenticatorStateUpdate, - AuthenticatorStateUpdateV1, CancelledTransaction, ChangeEpoch, Command, - ConsensusCommitPrologue, ConsensusCommitPrologueV1, ConsensusCommitPrologueV2, - ConsensusCommitPrologueV3, ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind, - GasPayment, GenesisTransaction, Input, InputArgument, MakeMoveVector, MergeCoins, MoveCall, - ProgrammableTransaction, Publish, RandomnessStateUpdate, SignedTransaction, SplitCoins, - SystemPackage, Transaction, TransactionExpiration, TransactionKind, TransferObjects, - UnresolvedGasPayment, UnresolvedInputArgument, UnresolvedInputArgumentKind, - UnresolvedObjectReference, UnresolvedProgrammableTransaction, UnresolvedTransaction, - UnresolvedValue, Upgrade, VersionAssignment, unresolved, -}; -pub use type_tag::{Identifier, StructTag, TypeParseError, TypeTag}; - -#[cfg(test)] -mod serialization_proptests; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PersonalMessage<'a>(pub std::borrow::Cow<'a, [u8]>); diff --git a/crates/sui-sdk-types/src/types/u256.rs b/crates/sui-sdk-types/src/u256.rs similarity index 100% rename from crates/sui-sdk-types/src/types/u256.rs rename to crates/sui-sdk-types/src/u256.rs diff --git a/crates/sui-transaction-builder/src/error.rs b/crates/sui-transaction-builder/src/error.rs index bc98d635e..8ef4721c6 100644 --- a/crates/sui-transaction-builder/src/error.rs +++ b/crates/sui-transaction-builder/src/error.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use base64ct::Error as Base64Error; -use sui_types::types::ObjectId; +use sui_types::ObjectId; #[derive(thiserror::Error, Debug, Clone)] #[non_exhaustive] diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/sui-transaction-builder/src/lib.rs index 346f3f01d..050648d8f 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/sui-transaction-builder/src/lib.rs @@ -5,24 +5,24 @@ mod error; pub mod unresolved; use error::Error; -use sui_types::types::Address; -use sui_types::types::Argument; -use sui_types::types::Command; -use sui_types::types::GasPayment; -use sui_types::types::Identifier; -use sui_types::types::Input; -use sui_types::types::MakeMoveVector; -use sui_types::types::MergeCoins; -use sui_types::types::MoveCall; -use sui_types::types::ObjectId; -use sui_types::types::ObjectReference; -use sui_types::types::Publish; -use sui_types::types::SplitCoins; -use sui_types::types::Transaction; -use sui_types::types::TransactionExpiration; -use sui_types::types::TransferObjects; -use sui_types::types::TypeTag; -use sui_types::types::Upgrade; +use sui_types::Address; +use sui_types::Argument; +use sui_types::Command; +use sui_types::GasPayment; +use sui_types::Identifier; +use sui_types::Input; +use sui_types::MakeMoveVector; +use sui_types::MergeCoins; +use sui_types::MoveCall; +use sui_types::ObjectId; +use sui_types::ObjectReference; +use sui_types::Publish; +use sui_types::SplitCoins; +use sui_types::Transaction; +use sui_types::TransactionExpiration; +use sui_types::TransferObjects; +use sui_types::TypeTag; +use sui_types::Upgrade; use base64ct::Encoding; use serde::Serialize; @@ -221,7 +221,7 @@ impl TransactionBuilder { /// ### Upgrade a package with some pre-known data. /// ```rust,ignore /// use sui_graphql_client::Client; - /// use sui_sdk_types::types::unresolved; + /// use sui_sdk_types::unresolved; /// use sui_transaction_builder::TransactionBuilder; /// use sui_transaction_builder::Function; /// @@ -302,8 +302,8 @@ impl TransactionBuilder { }; Ok(Transaction { - kind: sui_types::types::TransactionKind::ProgrammableTransaction( - sui_types::types::ProgrammableTransaction { + kind: sui_types::TransactionKind::ProgrammableTransaction( + sui_types::ProgrammableTransaction { inputs: self .inputs .into_iter() @@ -488,19 +488,19 @@ mod tests { use sui_graphql_client::faucet::FaucetClient; use sui_graphql_client::Client; use sui_graphql_client::PaginationFilter; - use sui_types::types::Address; - use sui_types::types::ExecutionStatus; - use sui_types::types::IdOperation; - use sui_types::types::ObjectId; - use sui_types::types::ObjectType; - use sui_types::types::TransactionEffects; - use sui_types::types::TypeTag; + use sui_types::Address; + use sui_types::ExecutionStatus; + use sui_types::IdOperation; + use sui_types::ObjectId; + use sui_types::ObjectType; + use sui_types::TransactionEffects; + use sui_types::TypeTag; use crate::unresolved::Input; use crate::Function; use crate::Serialized; use crate::TransactionBuilder; - use sui_types::types::TransactionDigest; + use sui_types::TransactionDigest; /// Type corresponding to the output of `sui move build --dump-bytecode-as-base64` #[derive(serde::Deserialize, Debug)] @@ -854,10 +854,10 @@ mod tests { if obj.id_operation == IdOperation::Created { let change = obj.output_state; match change { - sui_types::types::ObjectOut::PackageWrite { .. } => { + sui_types::ObjectOut::PackageWrite { .. } => { package_id = Some(obj.object_id); } - sui_types::types::ObjectOut::ObjectWrite { .. } => { + sui_types::ObjectOut::ObjectWrite { .. } => { created_objs.push(obj.object_id); } _ => {} @@ -877,19 +877,19 @@ mod tests { match obj.object_type() { ObjectType::Struct(x) if x.name.to_string() == "UpgradeCap" => { match obj.owner() { - sui_types::types::Owner::Address(_) => { + sui_types::Owner::Address(_) => { let obj: Input = (&obj).into(); upgrade_cap = Some(tx.input(obj.with_owned_kind())) } - sui_types::types::Owner::Shared(_) => { + sui_types::Owner::Shared(_) => { upgrade_cap = Some(tx.input(&obj)) } // If the capability is owned by an object, then the module defining the owning // object gets to decide how the upgrade capability should be used. - sui_types::types::Owner::Object(_) => { + sui_types::Owner::Object(_) => { panic!("Upgrade capability controlled by object") } - sui_types::types::Owner::Immutable => panic!("Upgrade capability is stored immutably and cannot be used for upgrades"), + sui_types::Owner::Immutable => panic!("Upgrade capability is stored immutably and cannot be used for upgrades"), }; break; } diff --git a/crates/sui-transaction-builder/src/unresolved.rs b/crates/sui-transaction-builder/src/unresolved.rs index 50f1417ed..2149257b6 100644 --- a/crates/sui-transaction-builder/src/unresolved.rs +++ b/crates/sui-transaction-builder/src/unresolved.rs @@ -1,4 +1,4 @@ -use sui_types::types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; +use sui_types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; // A potentially unresolved user transaction. Note that one can construct a // fully resolved transaction using this type by providing all the required @@ -314,9 +314,9 @@ impl From for serde_json::Value { } } -impl From<&sui_types::types::Object> for Input { - fn from(object: &sui_types::types::Object) -> Self { - use sui_types::types::Owner; +impl From<&sui_types::Object> for Input { + fn from(object: &sui_types::Object) -> Self { + use sui_types::Owner; let input = Input::by_id(object.object_id()) .with_digest(object.digest()) From ca68846aad4a216b9c574e5f2736cda7d81bf670 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 23 Dec 2024 11:21:40 -0600 Subject: [PATCH 097/107] chore: update proptest to 1.6.0 (#85) Update proptest to 1.6.0 which addresses the various bugs that were causing us to pin to a patched version. --- crates/sui-crypto/Cargo.toml | 5 +---- crates/sui-sdk-types/Cargo.toml | 2 +- crates/sui-sdk-types/Makefile | 12 ++---------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index c5cec4e40..18be4a4cf 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -94,10 +94,7 @@ hex = "0.4.3" serde_json = { version = "1.0.128" } # proptest support in tests -# -# Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments -# see https://github.com/proptest-rs/proptest/pull/270 for more info -proptest = { git = "https://github.com/bmwill/proptest.git", rev = "bc36db126183bce18c8bc595f0c0cfeac48b870c", default-features = false, features = ["std"] } +proptest = { version = "1.6.0", default-features = false, features = ["std"] } test-strategy = "0.4.0" [target.wasm32-unknown-unknown.dev-dependencies] diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 9c38ef82e..7d327107c 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -62,7 +62,7 @@ rand_core = { version = "0.6.4", optional = true } blake2 = { version = "0.10.6", optional = true } # proptest support -proptest = { version = "1.5.0", default-features = false, features = ["std"], optional = true } +proptest = { version = "1.6.0", default-features = false, features = ["std"], optional = true } test-strategy = { version = "0.4", optional = true } [dev-dependencies] diff --git a/crates/sui-sdk-types/Makefile b/crates/sui-sdk-types/Makefile index ff15371b7..acc0c2322 100644 --- a/crates/sui-sdk-types/Makefile +++ b/crates/sui-sdk-types/Makefile @@ -12,20 +12,12 @@ clippy: .PHONY: test test: - # Note on proptest: - # - # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments - # see https://github.com/proptest-rs/proptest/pull/270 for more info - cargo nextest run --all-features --config 'patch.crates-io.proptest.git="https://github.com/bmwill/proptest.git"' --config 'patch.crates-io.proptest.rev="0a140789d25f4999632b8b88aedb1e2ba056151d"' + cargo nextest run --all-features cargo test --doc .PHONY: wasm wasm: - # Note on proptest: - # - # Pin to this specific commit in order to work around an issue where proptest doesn't build properly in wasm environments - # see https://github.com/proptest-rs/proptest/pull/270 for more info - CC=clang wasm-pack test --node --all-features --config 'patch.crates-io.proptest.git="https://github.com/bmwill/proptest.git"' --config 'patch.crates-io.proptest.rev="0a140789d25f4999632b8b88aedb1e2ba056151d"' + CC=clang wasm-pack test --node --all-features %: $(MAKE) -C ../.. $@ From 6cff0659a5030f367741ba1b0c4cc6ff27c292d2 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 23 Dec 2024 13:03:28 -0600 Subject: [PATCH 098/107] sui-transaction-builder: enable base64ct feature 'std' --- crates/sui-transaction-builder/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sui-transaction-builder/Cargo.toml b/crates/sui-transaction-builder/Cargo.toml index c88c02cde..61e20b2ce 100644 --- a/crates/sui-transaction-builder/Cargo.toml +++ b/crates/sui-transaction-builder/Cargo.toml @@ -13,7 +13,7 @@ default = [] schemars = ["dep:schemars", "sui-types/schemars"] [dependencies] -base64ct = "1.6" +base64ct = { version = "1.6", features = ["std"] } bcs = "0.1.6" serde = { version = "1.0", features = ["derive"] } serde_with = { version = "3.9", default-features = false, features = ["alloc"] } From ed0f4ef6efb23927f255c4c2706a40e40d92a2ae Mon Sep 17 00:00:00 2001 From: caseylove Date: Wed, 1 Jan 2025 00:16:00 +0800 Subject: [PATCH 099/107] chore: add help target to Makefile to display the usage of all targets (#86) --- Makefile | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 9974c6aa7..aba8f2b0a 100644 --- a/Makefile +++ b/Makefile @@ -1,59 +1,64 @@ # Set the default target of this Makefile .PHONY: all -all:: ci +all:: ci ## Default target, runs the CI process .PHONY: check-features -check-features: +check-features: ## Check feature flags for crates $(MAKE) -C crates/iota-sdk-types check-features $(MAKE) -C crates/iota-crypto check-features .PHONY: check-fmt -check-fmt: +check-fmt: ## Check code formatting cargo fmt -- --config imports_granularity=Item --check .PHONY: fmt -fmt: +fmt: ## Format code cargo fmt -- --config imports_granularity=Item .PHONY: clippy -clippy: +clippy: ## Run Clippy linter cargo clippy --all-features --all-targets .PHONY: test -test: +test: ## Run unit tests cargo nextest run --all-features -p sui-sdk-types -p sui-crypto cargo test --doc -package_%.json: crates/sui-transaction-builder/tests/%/Move.toml crates/sui-transaction-builder/tests/%/sources/*.move +package_%.json: crates/sui-transaction-builder/tests/%/Move.toml crates/sui-transaction-builder/tests/%/sources/*.move ## Generate JSON files for tests cd crates/sui-transaction-builder/tests/$(*F) && sui move build --ignore-chain --dump-bytecode-as-base64 > ../../$@ .PHONY: test-with-localnet -test-with-localnet: package_test_example_v1.json package_test_example_v2.json +test-with-localnet: package_test_example_v1.json package_test_example_v2.json ## Run tests with localnet cargo nextest run -p sui-graphql-client -p sui-transaction-builder .PHONY: wasm -wasm: +wasm: ## Build WASM modules $(MAKE) -C crates/iota-sdk-types wasm $(MAKE) -C crates/iota-crypto wasm .PHONY: doc -doc: +doc: ## Generate documentation RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps .PHONY: doc-open -doc-open: +doc-open: ## Generate and open documentation RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps --open .PHONY: ci -ci: check-features check-fmt test wasm +ci: check-features check-fmt test wasm ## Run the full CI process .PHONY: ci-full -ci-full: ci doc +ci-full: ci doc ## Run the full CI process and generate documentation .PHONY: clean -clean: +clean: ## Clean build artifacts cargo clean .PHONY: clean-all -clean-all: clean - git clean -dX +clean-all: clean ## Clean all generated files, including those ignored by Git. Force removal. + git clean -dXf + +.PHONY: help +help: ## Show this help + @echo "Available targets:" + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) From 640a4087f1e6a867fc4225535027b006f9dac721 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 6 Jan 2025 09:55:07 -0600 Subject: [PATCH 100/107] release sui-sdk-types-0.0.2 and sui-crypto-0.0.2 --- crates/sui-crypto/CHANGELOG.md | 21 ++++++++++++++++++++- crates/sui-crypto/Cargo.toml | 4 ++-- crates/sui-sdk-types/CHANGELOG.md | 31 ++++++++++++++++++++++++++++++- crates/sui-sdk-types/Cargo.toml | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/crates/sui-crypto/CHANGELOG.md b/crates/sui-crypto/CHANGELOG.md index 31b830538..17b00dece 100644 --- a/crates/sui-crypto/CHANGELOG.md +++ b/crates/sui-crypto/CHANGELOG.md @@ -1,3 +1,22 @@ -# 0.0.1 - 2024-09-25 +# [0.0.2] - 2025-01-06 + +## Added + +- Added support for multisig verification and aggregation ([#25]) +- Added blanket implementation for SuiSigner and SuiVerifier ([`bc481a1`]) +- Added support for der and pem format for public and private keys ([`df32a46`]) +- Added a `SimpleKeypair` type which could be either an ed25519, secp256k1, or secp256r1 keypair ([`8d64c06`]) +- Added support for verifying passkey authenticators ([#81]) + +[#25]: https://github.com/mystenlabs/sui-rust-sdk/pull/25 +[`bc481a1`]: https://github.com/mystenlabs/sui-rust-sdk/commit/bc481a1ea156e6ccb528b5b49e62a511be5ba60a +[`df32a46`]: https://github.com/mystenlabs/sui-rust-sdk/commit/df32a46bfbecbbbf4ec7e9c1974eef0916ccd359 +[`8d64c06`]: https://github.com/mystenlabs/sui-rust-sdk/commit/8d64c06628b9494c674c27158ce74036fe45080e +[#81]: https://github.com/MystenLabs/sui-rust-sdk/pull/81 + +# [0.0.1] - 2024-09-25 Initial release + +[0.0.2]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-crypto-0.0.2 +[0.0.1]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-crypto-0.0.1 diff --git a/crates/sui-crypto/Cargo.toml b/crates/sui-crypto/Cargo.toml index 18be4a4cf..854c482f1 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/sui-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sui-crypto" -version = "0.0.1" +version = "0.0.2" authors = ["Brandon Williams "] repository = "https://github.com/mystenlabs/sui-rust-sdk/" license = "Apache-2.0" @@ -54,7 +54,7 @@ pem = [ [dependencies] signature = "2.2" -sui-sdk-types = { version = "0.0.1", path = "../sui-sdk-types", default-features = false, features = ["hash", "serde"] } +sui-sdk-types = { version = "0.0.2", path = "../sui-sdk-types", default-features = false, features = ["hash", "serde"] } # RNG support rand_core = { version = "0.6.4", optional = true } diff --git a/crates/sui-sdk-types/CHANGELOG.md b/crates/sui-sdk-types/CHANGELOG.md index 31b830538..2dca6b50b 100644 --- a/crates/sui-sdk-types/CHANGELOG.md +++ b/crates/sui-sdk-types/CHANGELOG.md @@ -1,3 +1,32 @@ -# 0.0.1 - 2024-09-25 +# [0.0.2] - 2025-01-06 + +## Added + +- Added `proptest::Arbitrary` impls via the `proptest` feature [`6918fd8`] +- Added From impl for TypeTag [#77] + +## Changed + +- Update the passkey challenge format to use the same signing message as other key types ([`c5a25ce`]) +- Flattened the `types` module into the top-level ([`dc54c46`]) +- Folded the `EffectsObjectChange` type into the `ChangedObject` struct ([`aa546ca`]) + +## Removed + +- Removed the `unresolved` module and moved it to the `sui-transaction-builder` crate ([`d965897`]) +- Removed the `schemars` feature ([`bc6dd37`]) + +[`c5a25ce`]: https://github.com/mystenlabs/sui-rust-sdk/commit/c5a25ce356a8cbe42ddcc6ec6bab380007790b44 +[`6918fd8`]: https://github.com/mystenlabs/sui-rust-sdk/commit/6918fd88d40734b8c15fb5c519e9a40aec53eb74 +[#77]: https://github.com/mystenlabs/sui-rust-sdk/pull/77 +[`d965897`]: https://github.com/mystenlabs/sui-rust-sdk/commit/d9658978a4c6e928d036fbedaab9326d5e28de87 +[`dc54c46`]: https://github.com/mystenlabs/sui-rust-sdk/commit/dc54c469f9d006f02d82ec5781d73e8e09ae26ae +[`aa546ca`]: https://github.com/mystenlabs/sui-rust-sdk/commit/aa546ca91249932da3f8e3d55ba6e52e40cd8929 +[`bc6dd37`]: https://github.com/mystenlabs/sui-rust-sdk/commit/bc6dd3732973ed3c1c3ae811a818fc8504a99f0b + +# [0.0.1] - 2024-09-25 Initial release + +[0.0.2]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-sdk-types-0.0.2 +[0.0.1]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-sdk-types-0.0.1 diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/sui-sdk-types/Cargo.toml index 7d327107c..9648d5811 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/sui-sdk-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iota-sdk-types" -version = "0.0.1" +version = "0.0.2" authors = ["IOTA Foundation "] edition = "2021" license = "Apache-2.0" From 95918e900817c05a7ec5ae2e218897468fd2327f Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Mon, 24 Feb 2025 10:23:40 -0500 Subject: [PATCH 101/107] chore: Apply renames and clean upstream changes --- .github/workflows/ci.yml | 29 +- .github/workflows/rustdoc.yml | 2 +- Makefile | 12 +- README.md | 36 +- crates/{sui-crypto => iota-crypto}/Cargo.toml | 67 +-- crates/{sui-crypto => iota-crypto}/Makefile | 0 crates/iota-crypto/README.md | 8 + .../src/bls12381.rs | 4 + .../src/ed25519.rs | 29 +- crates/{sui-crypto => iota-crypto}/src/lib.rs | 41 +- .../src/multisig.rs | 25 +- .../src/passkey.rs | 23 +- .../src/secp256k1.rs | 39 +- .../src/secp256r1.rs | 39 +- .../{sui-crypto => iota-crypto}/src/simple.rs | 77 ++- .../src/zklogin/mod.rs | 37 +- .../src/zklogin/poseidon/constants.rs | 4 + .../src/zklogin/poseidon/mod.rs | 15 +- .../src/zklogin/tests.rs | 10 +- .../src/zklogin/verify.rs | 79 ++- crates/iota-graphql-client-build/Cargo.toml | 8 + .../README.md | 23 +- .../schema.graphql | 386 +++++++------- .../src/lib.rs | 12 +- .../CHANGELOG.md | 0 .../Cargo.toml | 21 +- .../README.md | 50 +- crates/iota-graphql-client/build.rs | 8 + .../examples/custom_query.rs | 13 +- .../queries/coin_metadata.graphql | 0 .../queries/custom_query.graphql | 0 .../queries/epoch_total_checkpoints.graphql | 2 +- .../queries/object.graphql | 3 +- .../queries/objects.graphql | 25 + .../src/error.rs | 20 +- .../src/faucet.rs | 57 ++- .../src/lib.rs | 470 ++++++++---------- .../src/query_types/active_validators.rs | 31 +- .../src/query_types/balance.rs | 8 +- .../src/query_types/chain.rs | 1 + .../src/query_types/checkpoint.rs | 14 +- .../src/query_types/coin.rs | 4 +- .../src/query_types/dry_run.rs | 7 +- .../src/query_types/dynamic_fields.rs | 18 +- .../src/query_types/epoch.rs | 6 +- .../src/query_types/events.rs | 7 +- .../src/query_types/execute_tx.rs | 4 +- .../src/query_types/mod.rs | 149 ++++++ .../query_types/normalized_move/function.rs | 5 +- .../src/query_types/normalized_move/mod.rs | 8 +- .../src/query_types/normalized_move/module.rs | 7 +- .../src/query_types/object.rs | 7 +- .../src/query_types/packages.rs | 7 +- .../src/query_types/protocol_config.rs | 26 +- .../src/query_types/service_config.rs | 49 +- .../src/query_types/transaction.rs | 20 +- .../src/streams.rs | 36 +- crates/iota-sdk-types/CHANGELOG.md | 32 ++ .../Cargo.toml | 8 +- .../Makefile | 0 crates/iota-sdk-types/README.md | 8 + .../src/address.rs | 4 + .../src/checkpoint.rs | 8 +- .../src/crypto/bls12381.rs | 4 + .../src/crypto/ed25519.rs | 4 + .../src/crypto/intent.rs | 18 +- .../src/crypto/mod.rs | 4 + .../src/crypto/multisig.rs | 4 + .../src/crypto/passkey.rs | 4 + .../src/crypto/secp256k1.rs | 4 + .../src/crypto/secp256r1.rs | 4 + .../src/crypto/signature.rs | 15 +- .../src/crypto/validator.rs | 4 + .../src/crypto/zklogin.rs | 4 + .../src/digest.rs | 7 +- .../fixtures/genesis-transaction-effects | 0 .../src/effects/fixtures/sponsor-tx-effects | 0 .../src/effects/mod.rs | 7 +- .../src/effects/v1.rs | 4 + .../src/events.rs | 4 + .../src/execution_status.rs | 4 + .../src/framework.rs | 4 + .../src/gas.rs | 4 + .../src/hash.rs | 8 +- .../src/lib.rs | 174 ++----- .../src/object.rs | 40 +- .../src/object_id.rs | 4 + .../src/serialization_proptests.rs | 8 +- .../update-transaction-fixtures/Cargo.toml | 4 +- .../update-transaction-fixtures/src/main.rs | 4 +- .../src/transaction/mod.rs | 17 +- .../src/transaction/serialization.rs | 6 +- .../src/type_tag/mod.rs | 4 + .../src/type_tag/parse.rs | 8 +- .../src/type_tag/serialization.rs | 4 + .../src/u256.rs | 4 + .../.gitignore | 0 crates/iota-transaction-builder/Cargo.toml | 34 ++ .../src/error.rs | 3 +- .../src/lib.rs | 224 ++++----- .../src/unresolved.rs | 11 +- .../tests/test_example_v1}/Move.toml | 3 +- .../test_example_v1/sources/test_example.move | 0 .../tests/test_example_v2}/Move.toml | 3 +- .../test_example_v2/sources/test_example.move | 0 crates/sui-crypto/CHANGELOG.md | 22 - crates/sui-crypto/README.md | 8 - crates/sui-graphql-client-build/Cargo.toml | 8 - crates/sui-graphql-client/build.rs | 4 - .../queries/objects.graphql | 20 - .../sui-graphql-client/src/query_types/mod.rs | 199 -------- .../src/query_types/suins.rs | 53 -- crates/sui-sdk-types/CHANGELOG.md | 32 -- crates/sui-sdk-types/README.md | 8 - .../src/transaction/fixtures/change-epoch | 1 - .../fixtures/consensus-commit-prologue-v1 | 1 - .../src/transaction/fixtures/genesis | 1 - .../src/transaction/fixtures/ptb | 1 - .../update-transaction-fixtures/.gitignore | 1 - crates/sui-transaction-builder/Cargo.toml | 34 -- dprint.json | 10 +- 121 files changed, 1520 insertions(+), 1697 deletions(-) rename crates/{sui-crypto => iota-crypto}/Cargo.toml (71%) rename crates/{sui-crypto => iota-crypto}/Makefile (100%) create mode 100644 crates/iota-crypto/README.md rename crates/{sui-crypto => iota-crypto}/src/bls12381.rs (64%) rename crates/{sui-crypto => iota-crypto}/src/ed25519.rs (95%) rename crates/{sui-crypto => iota-crypto}/src/lib.rs (75%) rename crates/{sui-crypto => iota-crypto}/src/multisig.rs (96%) rename crates/{sui-crypto => iota-crypto}/src/passkey.rs (88%) rename crates/{sui-crypto => iota-crypto}/src/secp256k1.rs (94%) rename crates/{sui-crypto => iota-crypto}/src/secp256r1.rs (94%) rename crates/{sui-crypto => iota-crypto}/src/simple.rs (94%) rename crates/{sui-crypto => iota-crypto}/src/zklogin/mod.rs (90%) rename crates/{sui-crypto => iota-crypto}/src/zklogin/poseidon/constants.rs (99%) rename crates/{sui-crypto => iota-crypto}/src/zklogin/poseidon/mod.rs (97%) rename crates/{sui-crypto => iota-crypto}/src/zklogin/tests.rs (93%) rename crates/{sui-crypto => iota-crypto}/src/zklogin/verify.rs (93%) create mode 100644 crates/iota-graphql-client-build/Cargo.toml rename crates/{sui-graphql-client-build => iota-graphql-client-build}/README.md (55%) rename crates/{sui-graphql-client-build => iota-graphql-client-build}/schema.graphql (92%) rename crates/{sui-graphql-client-build => iota-graphql-client-build}/src/lib.rs (76%) rename crates/{sui-graphql-client => iota-graphql-client}/CHANGELOG.md (100%) rename crates/{sui-graphql-client => iota-graphql-client}/Cargo.toml (51%) rename crates/{sui-graphql-client => iota-graphql-client}/README.md (82%) create mode 100644 crates/iota-graphql-client/build.rs rename crates/{sui-graphql-client => iota-graphql-client}/examples/custom_query.rs (92%) rename crates/{sui-graphql-client => iota-graphql-client}/queries/coin_metadata.graphql (100%) rename crates/{sui-graphql-client => iota-graphql-client}/queries/custom_query.graphql (100%) rename crates/{sui-graphql-client => iota-graphql-client}/queries/epoch_total_checkpoints.graphql (51%) rename crates/{sui-graphql-client => iota-graphql-client}/queries/object.graphql (50%) create mode 100644 crates/iota-graphql-client/queries/objects.graphql rename crates/{sui-graphql-client => iota-graphql-client}/src/error.rs (92%) rename crates/{sui-graphql-client => iota-graphql-client}/src/faucet.rs (85%) rename crates/{sui-graphql-client => iota-graphql-client}/src/lib.rs (86%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/active_validators.rs (86%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/balance.rs (87%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/chain.rs (90%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/checkpoint.rs (93%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/coin.rs (94%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/dry_run.rs (92%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/dynamic_fields.rs (94%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/epoch.rs (95%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/events.rs (90%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/execute_tx.rs (90%) create mode 100644 crates/iota-graphql-client/src/query_types/mod.rs rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/normalized_move/function.rs (90%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/normalized_move/mod.rs (84%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/normalized_move/module.rs (96%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/object.rs (93%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/packages.rs (96%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/protocol_config.rs (76%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/service_config.rs (70%) rename crates/{sui-graphql-client => iota-graphql-client}/src/query_types/transaction.rs (94%) rename crates/{sui-graphql-client => iota-graphql-client}/src/streams.rs (91%) create mode 100644 crates/iota-sdk-types/CHANGELOG.md rename crates/{sui-sdk-types => iota-sdk-types}/Cargo.toml (92%) rename crates/{sui-sdk-types => iota-sdk-types}/Makefile (100%) create mode 100644 crates/iota-sdk-types/README.md rename crates/{sui-sdk-types => iota-sdk-types}/src/address.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/checkpoint.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/bls12381.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/ed25519.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/intent.rs (80%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/mod.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/multisig.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/passkey.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/secp256k1.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/secp256r1.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/signature.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/validator.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/crypto/zklogin.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/digest.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/effects/fixtures/genesis-transaction-effects (100%) rename crates/{sui-sdk-types => iota-sdk-types}/src/effects/fixtures/sponsor-tx-effects (100%) rename crates/{sui-sdk-types => iota-sdk-types}/src/effects/mod.rs (96%) rename crates/{sui-sdk-types => iota-sdk-types}/src/effects/v1.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/events.rs (93%) rename crates/{sui-sdk-types => iota-sdk-types}/src/execution_status.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/framework.rs (92%) rename crates/{sui-sdk-types => iota-sdk-types}/src/gas.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/hash.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/lib.rs (64%) rename crates/{sui-sdk-types => iota-sdk-types}/src/object.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/object_id.rs (93%) rename crates/{sui-sdk-types => iota-sdk-types}/src/serialization_proptests.rs (96%) rename crates/{sui-sdk-types => iota-sdk-types}/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml (100%) rename crates/{sui-sdk-types => iota-sdk-types}/src/transaction/fixtures/update-transaction-fixtures/src/main.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/transaction/mod.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/transaction/serialization.rs (99%) rename crates/{sui-sdk-types => iota-sdk-types}/src/type_tag/mod.rs (97%) rename crates/{sui-sdk-types => iota-sdk-types}/src/type_tag/parse.rs (94%) rename crates/{sui-sdk-types => iota-sdk-types}/src/type_tag/serialization.rs (98%) rename crates/{sui-sdk-types => iota-sdk-types}/src/u256.rs (96%) rename crates/{sui-transaction-builder => iota-transaction-builder}/.gitignore (100%) create mode 100644 crates/iota-transaction-builder/Cargo.toml rename crates/{sui-transaction-builder => iota-transaction-builder}/src/error.rs (94%) rename crates/{sui-transaction-builder => iota-transaction-builder}/src/lib.rs (86%) rename crates/{sui-transaction-builder => iota-transaction-builder}/src/unresolved.rs (97%) rename crates/{sui-transaction-builder/tests/test_example_v2 => iota-transaction-builder/tests/test_example_v1}/Move.toml (90%) rename crates/{sui-transaction-builder => iota-transaction-builder}/tests/test_example_v1/sources/test_example.move (100%) rename crates/{sui-transaction-builder/tests/test_example_v1 => iota-transaction-builder/tests/test_example_v2}/Move.toml (90%) rename crates/{sui-transaction-builder => iota-transaction-builder}/tests/test_example_v2/sources/test_example.move (100%) delete mode 100644 crates/sui-crypto/CHANGELOG.md delete mode 100644 crates/sui-crypto/README.md delete mode 100644 crates/sui-graphql-client-build/Cargo.toml delete mode 100644 crates/sui-graphql-client/build.rs delete mode 100644 crates/sui-graphql-client/queries/objects.graphql delete mode 100644 crates/sui-graphql-client/src/query_types/mod.rs delete mode 100644 crates/sui-graphql-client/src/query_types/suins.rs delete mode 100644 crates/sui-sdk-types/CHANGELOG.md delete mode 100644 crates/sui-sdk-types/README.md delete mode 100644 crates/sui-sdk-types/src/transaction/fixtures/change-epoch delete mode 100644 crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 delete mode 100644 crates/sui-sdk-types/src/transaction/fixtures/genesis delete mode 100644 crates/sui-sdk-types/src/transaction/fixtures/ptb delete mode 100644 crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore delete mode 100644 crates/sui-transaction-builder/Cargo.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d446dca4e..f2693f6ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: [main] pull_request: + types: [opened, synchronize, reopened, ready_for_review] workflow_dispatch: schedule: [cron: "40 1 * * *"] @@ -43,9 +44,6 @@ jobs: - name: Install latest nightly run: rustup toolchain install nightly --component rustfmt --allow-downgrade - - name: feature compatibility - run: make check-features - - name: rustfmt run: make check-fmt @@ -55,6 +53,9 @@ jobs: - name: rustdoc run: make doc + - name: feature compatibility + run: make check-features + wasm: runs-on: ubuntu-latest @@ -86,7 +87,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgrespw - POSTGRES_DB: sui_indexer_v2 + POSTGRES_DB: iota_indexer_v2 POSTGRES_HOST_AUTH_METHOD: trust options: >- --health-cmd pg_isready @@ -106,22 +107,22 @@ jobs: - uses: taiki-e/install-action@cargo-nextest - - name: Get the Sui testnet binary and start a local network + - name: Get the IOTA testnet binary and start a local network shell: bash env: - SUI_BINARY_VERSION: "1.39.3" # used for downloading a specific Sui binary versions that matches the GraphQL schema for local network tests - SUI_NETWORK_RELEASE: "testnet" # which release to use + IOTA_BINARY_VERSION: "1.39.3" # used for downloading a specific IOTA binary versions that matches the GraphQL schema for local network tests + IOTA_NETWORK_RELEASE: "testnet" # which release to use run: | - ASSET_NAME="sui-$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION-ubuntu-x86_64.tgz" - download_url="https://github.com/mystenlabs/sui/releases/download/$SUI_NETWORK_RELEASE-v$SUI_BINARY_VERSION/$ASSET_NAME" + ASSET_NAME="iota-$IOTA_NETWORK_RELEASE-v$IOTA_BINARY_VERSION-ubuntu-x86_64.tgz" + download_url="https://github.com/iotaledger/iota/releases/download/$IOTA_NETWORK_RELEASE-v$IOTA_BINARY_VERSION/$ASSET_NAME" echo "Downloading testnet binary from $download_url" - wget -q $download_url -O sui.tgz - tar -zxvf sui.tgz ./sui - chmod +x ./sui + wget -q $download_url -O iota.tgz + tar -zxvf iota.tgz ./iota + chmod +x ./iota echo "Starting local network with a faucet, an indexer (port 5432) and GraphQL. Epoch duration is set to $EPOCH_DURATION_MS ms" - echo "$(pwd)" >> $GITHUB_PATH # we need it on the path for calling sui move build for some tests - ./sui start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name sui_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & + echo "$(pwd)" >> $GITHUB_PATH # we need it on the path for calling iota move build for some tests + ./iota start --force-regenesis --with-faucet --with-indexer --with-graphql --pg-port 5432 --pg-db-name iota_indexer_v2 --epoch-duration-ms $EPOCH_DURATION_MS & - name: Run tests that require local network (GraphQL Client and Tx Builder) env: diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index a755c1d09..cb04ed952 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -2,7 +2,7 @@ name: rustdoc on: push: - branches: [main] + branches: [develop] workflow_dispatch: env: diff --git a/Makefile b/Makefile index aba8f2b0a..66ddd3c79 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,11 @@ check-features: ## Check feature flags for crates .PHONY: check-fmt check-fmt: ## Check code formatting - cargo fmt -- --config imports_granularity=Item --check + cargo +nightly fmt -- --check .PHONY: fmt fmt: ## Format code - cargo fmt -- --config imports_granularity=Item + cargo +nightly fmt .PHONY: clippy clippy: ## Run Clippy linter @@ -21,15 +21,15 @@ clippy: ## Run Clippy linter .PHONY: test test: ## Run unit tests - cargo nextest run --all-features -p sui-sdk-types -p sui-crypto + cargo nextest run --all-features -p iota-sdk-types -p iota-crypto cargo test --doc -package_%.json: crates/sui-transaction-builder/tests/%/Move.toml crates/sui-transaction-builder/tests/%/sources/*.move ## Generate JSON files for tests - cd crates/sui-transaction-builder/tests/$(*F) && sui move build --ignore-chain --dump-bytecode-as-base64 > ../../$@ +package_%.json: crates/iota-transaction-builder/tests/%/Move.toml crates/iota-transaction-builder/tests/%/sources/*.move ## Generate JSON files for tests + cd crates/iota-transaction-builder/tests/$(*F) && iota move build --ignore-chain --dump-bytecode-as-base64 > ../../$@ .PHONY: test-with-localnet test-with-localnet: package_test_example_v1.json package_test_example_v2.json ## Run tests with localnet - cargo nextest run -p sui-graphql-client -p sui-transaction-builder + cargo nextest run -p iota-graphql-client -p iota-transaction-builder .PHONY: wasm wasm: ## Build WASM modules diff --git a/README.md b/README.md index 0947ab5d3..48abaacc2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # IOTA Sdk -A rust Sdk for integrating with the [Sui blockchain](https://docs.sui.io/). +A Rust SDK for integrating with the [IOTA blockchain](https://docs.iota.org/). > [!NOTE] > This is project is under development and many features may still be under @@ -8,31 +8,31 @@ A rust Sdk for integrating with the [Sui blockchain](https://docs.sui.io/). ## Overview -This repository contains a collection of libraries for integrating with the Sui blockchain. +This repository contains a collection of libraries for integrating with the IOTA blockchain. A few of the project's high-level goals are as follows: -* **Be modular** - user's should only need to pay the cost (in terms of dependencies/compilation time) for the features that they use. -* **Be light** - strive to have a minimal dependency footprint. -* **Support developers** - provide all needed types, abstractions and APIs to enable developers to build robust applications on Sui. -* **Support wasm** - where possible, libraries should be usable in wasm environments. +- **Be modular** - user's should only need to pay the cost (in terms of dependencies/compilation time) for the features that they use. +- **Be light** - strive to have a minimal dependency footprint. +- **Support developers** - provide all needed types, abstractions and APIs to enable developers to build robust applications on IOTA. +- **Support wasm** - where possible, libraries should be usable in wasm environments. ## Crates In an effort to be modular, functionality is split between a number of crates. -* [`sui-sdk-types`](crates/sui-sdk-types) - [![sui-sdk-types on crates.io](https://img.shields.io/crates/v/sui-sdk-types)](https://crates.io/crates/sui-sdk-types) - [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-sdk-types) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_sdk_types/) -* [`sui-crypto`](crates/sui-crypto) - [![sui-crypto on crates.io](https://img.shields.io/crates/v/sui-crypto)](https://crates.io/crates/sui-crypto) - [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-crypto) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_crypto/) -* [`sui-graphql-client`](crates/sui-crypto) - [![sui-graphql-client on crates.io](https://img.shields.io/crates/v/sui-graphql-client)](https://crates.io/crates/sui-graphql-client) - [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-graphql-client) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui-graphql-client/) +- [`iota-sdk-types`](crates/iota-sdk-types) + [![iota-sdk-types on crates.io](https://img.shields.io/crates/v/iota-sdk-types)](https://crates.io/crates/iota-sdk-types) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-sdk-types) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) +- [`iota-crypto`](crates/iota-crypto) + [![iota-crypto on crates.io](https://img.shields.io/crates/v/iota-crypto)](https://crates.io/crates/iota-crypto) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-crypto) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) +- [`iota-graphql-client`](crates/iota-crypto) + [![iota-graphql-client on crates.io](https://img.shields.io/crates/v/iota-graphql-client)](https://crates.io/crates/iota-graphql-client) + [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-graphql-client) + [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) ## License diff --git a/crates/sui-crypto/Cargo.toml b/crates/iota-crypto/Cargo.toml similarity index 71% rename from crates/sui-crypto/Cargo.toml rename to crates/iota-crypto/Cargo.toml index 854c482f1..316ca1286 100644 --- a/crates/sui-crypto/Cargo.toml +++ b/crates/iota-crypto/Cargo.toml @@ -1,24 +1,25 @@ [package] -name = "sui-crypto" -version = "0.0.2" -authors = ["Brandon Williams "] -repository = "https://github.com/mystenlabs/sui-rust-sdk/" -license = "Apache-2.0" +name = "iota-crypto" +version = "0.0.0" +authors = ["IOTA Foundation "] edition = "2021" +license = "Apache-2.0" readme = "README.md" -description = "Defines the interface for signing and verying messages in the Sui ecosystem" +repository = "https://github.com/iotaledger/iota-rust-sdk/" +description = "Defines the interface for signing and verying messages in the IOTA ecosystem" [package.metadata.docs.rs] # To build locally: # RUSTDOCFLAGS="--cfg=doc_cfg -Zunstable-options --generate-link-to-definition" RUSTC_BOOTSTRAP=1 cargo doc --all-features --no-deps --open all-features = true rustdoc-args = [ - # Enable doc_cfg showing the required features. - "--cfg=doc_cfg", + # Enable doc_cfg showing the required features. + "--cfg=doc_cfg", - # Generate links to definition in rustdoc source code pages - # https://github.com/rust-lang/rust/pull/84176 - "-Zunstable-options", "--generate-link-to-definition" + # Generate links to definition in rustdoc source code pages + # https://github.com/rust-lang/rust/pull/84176 + "-Zunstable-options", + "--generate-link-to-definition", ] [features] @@ -28,33 +29,33 @@ secp256r1 = ["dep:p256", "dep:rand_core"] passkey = ["secp256r1", "dep:sha2"] secp256k1 = ["dep:k256", "dep:rand_core", "signature/std"] zklogin = [ - "dep:ark-bn254", - "dep:ark-ff", - "dep:ark-groth16", - "dep:ark-snark", - "dep:ark-std", - "dep:base64ct", - "dep:bnum", - "dep:itertools", - "dep:serde", - "dep:serde_derive", - "dep:serde_json", - "signature/std", + "dep:ark-bn254", + "dep:ark-ff", + "dep:ark-groth16", + "dep:ark-snark", + "dep:ark-std", + "dep:base64ct", + "dep:bnum", + "dep:itertools", + "dep:serde", + "dep:serde_derive", + "dep:serde_json", + "signature/std", ] pem = [ - "dep:pkcs8", - "dep:pem-rfc7468", - "ed25519-dalek?/pkcs8", - "p256?/pkcs8", - "k256?/pkcs8", - "ed25519-dalek?/pem", - "p256?/pem", - "k256?/pem", + "dep:pkcs8", + "dep:pem-rfc7468", + "ed25519-dalek?/pkcs8", + "p256?/pkcs8", + "k256?/pkcs8", + "ed25519-dalek?/pem", + "p256?/pem", + "k256?/pem", ] [dependencies] +iota-sdk-types = { version = "0.0.0", path = "../iota-sdk-types", default-features = false, features = ["hash", "serde"] } signature = "2.2" -sui-sdk-types = { version = "0.0.2", path = "../sui-sdk-types", default-features = false, features = ["hash", "serde"] } # RNG support rand_core = { version = "0.6.4", optional = true } @@ -85,8 +86,8 @@ serde_derive = { version = "1.0.210", optional = true } serde_json = { version = "1.0.128", optional = true } # pkcs8 der and pem support -pkcs8 = { version = "0.10", optional = true, features = ["std"] } pem-rfc7468 = { version = "0.7", optional = true, features = ["std"] } +pkcs8 = { version = "0.10", optional = true, features = ["std"] } [dev-dependencies] bcs = { version = "0.1.6" } diff --git a/crates/sui-crypto/Makefile b/crates/iota-crypto/Makefile similarity index 100% rename from crates/sui-crypto/Makefile rename to crates/iota-crypto/Makefile diff --git a/crates/iota-crypto/README.md b/crates/iota-crypto/README.md new file mode 100644 index 000000000..42f5813bc --- /dev/null +++ b/crates/iota-crypto/README.md @@ -0,0 +1,8 @@ +# iota-crypto + +[![iota-crypto on crates.io](https://img.shields.io/crates/v/iota-crypto)](https://crates.io/crates/iota-crypto) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-crypto) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) + +The `iota-crypto` crate provides the interface for signing and verifying +transactions and messages in the IOTA ecosystem. diff --git a/crates/sui-crypto/src/bls12381.rs b/crates/iota-crypto/src/bls12381.rs similarity index 64% rename from crates/sui-crypto/src/bls12381.rs rename to crates/iota-crypto/src/bls12381.rs index 4999432a2..73d15c29a 100644 --- a/crates/sui-crypto/src/bls12381.rs +++ b/crates/iota-crypto/src/bls12381.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Bls12381PrivateKey([u8; Self::LENGTH]); diff --git a/crates/sui-crypto/src/ed25519.rs b/crates/iota-crypto/src/ed25519.rs similarity index 95% rename from crates/sui-crypto/src/ed25519.rs rename to crates/iota-crypto/src/ed25519.rs index a3856a44d..a2cbc5f04 100644 --- a/crates/sui-crypto/src/ed25519.rs +++ b/crates/iota-crypto/src/ed25519.rs @@ -1,11 +1,12 @@ -use crate::SignatureError; -use crate::Signer; -use crate::Verifier; -use sui_sdk_types::Ed25519PublicKey; -use sui_sdk_types::Ed25519Signature; -use sui_sdk_types::SignatureScheme; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::UserSignature; +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{ + Ed25519PublicKey, Ed25519Signature, SignatureScheme, SimpleSignature, UserSignature, +}; + +use crate::{SignatureError, Signer, Verifier}; pub struct Ed25519PrivateKey(ed25519_dalek::SigningKey); @@ -62,7 +63,8 @@ impl Ed25519PrivateKey { #[cfg(feature = "pem")] #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] - /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary + /// format). pub fn from_der(bytes: &[u8]) -> Result { ed25519_dalek::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) .map(Self) @@ -266,15 +268,14 @@ impl Verifier for Ed25519Verifier { #[cfg(test)] mod test { - use super::*; - use crate::SuiSigner; - use crate::SuiVerifier; - use sui_sdk_types::PersonalMessage; + use iota_sdk_types::PersonalMessage; use test_strategy::proptest; - #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + use crate::{IotaSigner, IotaVerifier}; + // TODO need to export proptest impl from core crate // #[proptest] // fn transaction_signing(signer: Ed25519PrivateKey, transaction: Transaction) { diff --git a/crates/sui-crypto/src/lib.rs b/crates/iota-crypto/src/lib.rs similarity index 75% rename from crates/sui-crypto/src/lib.rs rename to crates/iota-crypto/src/lib.rs index ca20b0cbe..9a36f2231 100644 --- a/crates/sui-crypto/src/lib.rs +++ b/crates/iota-crypto/src/lib.rs @@ -1,12 +1,11 @@ -#![cfg_attr(doc_cfg, feature(doc_cfg))] +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 -use sui_sdk_types::PersonalMessage; -use sui_sdk_types::Transaction; -use sui_sdk_types::UserSignature; +#![cfg_attr(doc_cfg, feature(doc_cfg))] -pub use signature::Error as SignatureError; -pub use signature::Signer; -pub use signature::Verifier; +use iota_sdk_types::{PersonalMessage, Transaction, UserSignature}; +pub use signature::{Error as SignatureError, Signer, Verifier}; #[cfg(feature = "ed25519")] #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))] @@ -83,15 +82,15 @@ pub mod multisig; #[doc(inline)] pub use multisig::UserSignatureVerifier; -/// Interface for signing user transactions and messages in Sui +/// Interface for signing user transactions and messages in IOTA /// /// # Note /// -/// There is a blanket implementation of `SuiSigner` for all `T` where `T: -/// `[`Signer`]`<`[`UserSignature`]`>` so it is generally recommended for a signer to implement -/// `Signer` and rely on the blanket implementation which handles the proper -/// construction of the signing message. -pub trait SuiSigner { +/// There is a blanket implementation of `IotaSigner` for all `T` where `T: +/// `[`Signer`]`<`[`UserSignature`]`>` so it is generally recommended for a +/// signer to implement `Signer` and rely on the blanket +/// implementation which handles the proper construction of the signing message. +pub trait IotaSigner { fn sign_transaction(&self, transaction: &Transaction) -> Result; fn sign_personal_message( &self, @@ -99,7 +98,7 @@ pub trait SuiSigner { ) -> Result; } -impl> SuiSigner for T { +impl> IotaSigner for T { fn sign_transaction(&self, transaction: &Transaction) -> Result { let msg = transaction.signing_digest(); self.try_sign(&msg) @@ -114,15 +113,15 @@ impl> SuiSigner for T { } } -/// Interface for verifying user transactions and messages in Sui +/// Interface for verifying user transactions and messages in IOTA /// /// # Note /// -/// There is a blanket implementation of `SuiVerifier` for all `T` where `T: -/// `[`Verifier`]`<`[`UserSignature`]`>` so it is generally recommended for a signer to implement -/// `Verifier` and rely on the blanket implementation which handles the proper -/// construction of the signing message. -pub trait SuiVerifier { +/// There is a blanket implementation of `IotaVerifier` for all `T` where `T: +/// `[`Verifier`]`<`[`UserSignature`]`>` so it is generally recommended for a +/// signer to implement `Verifier` and rely on the blanket +/// implementation which handles the proper construction of the signing message. +pub trait IotaVerifier { fn verify_transaction( &self, transaction: &Transaction, @@ -135,7 +134,7 @@ pub trait SuiVerifier { ) -> Result<(), SignatureError>; } -impl> SuiVerifier for T { +impl> IotaVerifier for T { fn verify_transaction( &self, transaction: &Transaction, diff --git a/crates/sui-crypto/src/multisig.rs b/crates/iota-crypto/src/multisig.rs similarity index 96% rename from crates/sui-crypto/src/multisig.rs rename to crates/iota-crypto/src/multisig.rs index 0afa933cd..2fd85d8f1 100644 --- a/crates/sui-crypto/src/multisig.rs +++ b/crates/iota-crypto/src/multisig.rs @@ -1,10 +1,13 @@ -use crate::SignatureError; -use crate::Verifier; -use sui_sdk_types::MultisigAggregatedSignature; -use sui_sdk_types::MultisigCommittee; -use sui_sdk_types::MultisigMemberPublicKey; -use sui_sdk_types::MultisigMemberSignature; -use sui_sdk_types::UserSignature; +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{ + MultisigAggregatedSignature, MultisigCommittee, MultisigMemberPublicKey, + MultisigMemberSignature, UserSignature, +}; + +use crate::{SignatureError, Verifier}; #[derive(Default)] pub struct MultisigVerifier { @@ -267,7 +270,7 @@ pub struct MultisigAggregator { impl MultisigAggregator { pub fn new_with_transaction( committee: MultisigCommittee, - transaction: &sui_sdk_types::Transaction, + transaction: &iota_sdk_types::Transaction, ) -> Self { Self { committee, @@ -280,7 +283,7 @@ impl MultisigAggregator { pub fn new_with_message( committee: MultisigCommittee, - message: &sui_sdk_types::PersonalMessage<'_>, + message: &iota_sdk_types::PersonalMessage<'_>, ) -> Self { Self { committee, @@ -324,7 +327,7 @@ impl MultisigAggregator { Entry::Occupied(_) => { return Err(SignatureError::from_source( "duplicate signature from same committee member", - )) + )); } } @@ -360,7 +363,7 @@ impl MultisigAggregator { fn multisig_pubkey_and_signature_from_user_signature( signature: UserSignature, ) -> Result<(MultisigMemberPublicKey, MultisigMemberSignature), SignatureError> { - use sui_sdk_types::SimpleSignature; + use iota_sdk_types::SimpleSignature; match signature { UserSignature::Simple(SimpleSignature::Ed25519 { signature, diff --git a/crates/sui-crypto/src/passkey.rs b/crates/iota-crypto/src/passkey.rs similarity index 88% rename from crates/sui-crypto/src/passkey.rs rename to crates/iota-crypto/src/passkey.rs index ddaf774b1..58b712a2f 100644 --- a/crates/sui-crypto/src/passkey.rs +++ b/crates/iota-crypto/src/passkey.rs @@ -1,9 +1,11 @@ -use crate::secp256r1::Secp256r1VerifyingKey; -use crate::SignatureError; +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{PasskeyAuthenticator, SimpleSignature, UserSignature}; use signature::Verifier; -use sui_sdk_types::PasskeyAuthenticator; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::UserSignature; + +use crate::{SignatureError, secp256r1::Secp256r1VerifyingKey}; #[derive(Default, Clone, Debug)] pub struct PasskeyVerifier {} @@ -34,7 +36,8 @@ impl Verifier for PasskeyVerifier { )); } - // Construct passkey signing message = authenticator_data || sha256(client_data_json). + // Construct passkey signing message = authenticator_data || + // sha256(client_data_json). let mut message = authenticator.authenticator_data().to_owned(); let client_data_hash = { use sha2::Digest; @@ -63,13 +66,13 @@ impl Verifier for PasskeyVerifier { #[cfg(test)] mod test { - use super::*; - use crate::SuiVerifier; - use sui_sdk_types::Transaction; - + use iota_sdk_types::Transaction; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + use crate::IotaVerifier; + #[test] fn transaction_signing_fixture() { let transaction = "AAAAACdZawPnpJRjmVcwDu6xrIumtq5NLO+6GHbs0iGdCoD7AQ0T0TolicYERdSvyCRjSSduDZLbSpBsZBoib+lF48EBcgAAAAAAAAAgpQr/Mudl9BdzyBdkbqTlqBw4/aJ21kAD/jpJKa05im4nWWsD56SUY5lXMA7usayLprauTSzvuhh27NIhnQqA++gDAAAAAAAAgIQeAAAAAAAA"; diff --git a/crates/sui-crypto/src/secp256k1.rs b/crates/iota-crypto/src/secp256k1.rs similarity index 94% rename from crates/sui-crypto/src/secp256k1.rs rename to crates/iota-crypto/src/secp256k1.rs index 385a67d15..1e9edd1ca 100644 --- a/crates/sui-crypto/src/secp256k1.rs +++ b/crates/iota-crypto/src/secp256k1.rs @@ -1,14 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{ + Secp256k1PublicKey, Secp256k1Signature, SignatureScheme, SimpleSignature, UserSignature, +}; +use k256::{ + ecdsa::{SigningKey, VerifyingKey}, + elliptic_curve::group::GroupEncoding, +}; +use signature::{Signer, Verifier}; + use crate::SignatureError; -use k256::ecdsa::SigningKey; -use k256::ecdsa::VerifyingKey; -use k256::elliptic_curve::group::GroupEncoding; -use signature::Signer; -use signature::Verifier; -use sui_sdk_types::Secp256k1PublicKey; -use sui_sdk_types::Secp256k1Signature; -use sui_sdk_types::SignatureScheme; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::UserSignature; pub struct Secp256k1PrivateKey(SigningKey); @@ -65,7 +68,8 @@ impl Secp256k1PrivateKey { #[cfg(feature = "pem")] #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] - /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary + /// format). pub fn from_der(bytes: &[u8]) -> Result { k256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) .map(Self) @@ -268,19 +272,18 @@ impl Verifier for Secp256k1Verifier { #[cfg(test)] mod test { - use super::*; - use crate::SuiSigner; - use crate::SuiVerifier; - use sui_sdk_types::PersonalMessage; + use iota_sdk_types::PersonalMessage; use test_strategy::proptest; - #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + use crate::{IotaSigner, IotaVerifier}; + // TODO need to export proptest impl from core crate // #[proptest] - // fn transaction_signing(signer: Secp256k1PrivateKey, transaction: Transaction) { - // let signature = signer.sign_transaction(&transaction).unwrap(); + // fn transaction_signing(signer: Secp256k1PrivateKey, transaction: Transaction) + // { let signature = signer.sign_transaction(&transaction).unwrap(); // let verifier = signer.public_key(); // verifier // .verify_transaction(&transaction, &signature) diff --git a/crates/sui-crypto/src/secp256r1.rs b/crates/iota-crypto/src/secp256r1.rs similarity index 94% rename from crates/sui-crypto/src/secp256r1.rs rename to crates/iota-crypto/src/secp256r1.rs index b3fae1680..1870f7f53 100644 --- a/crates/sui-crypto/src/secp256r1.rs +++ b/crates/iota-crypto/src/secp256r1.rs @@ -1,14 +1,17 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{ + Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, UserSignature, +}; +use p256::{ + ecdsa::{SigningKey, VerifyingKey}, + elliptic_curve::group::GroupEncoding, +}; +use signature::{Signer, Verifier}; + use crate::SignatureError; -use p256::ecdsa::SigningKey; -use p256::ecdsa::VerifyingKey; -use p256::elliptic_curve::group::GroupEncoding; -use signature::Signer; -use signature::Verifier; -use sui_sdk_types::Secp256r1PublicKey; -use sui_sdk_types::Secp256r1Signature; -use sui_sdk_types::SignatureScheme; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::UserSignature; pub struct Secp256r1PrivateKey(SigningKey); @@ -65,7 +68,8 @@ impl Secp256r1PrivateKey { #[cfg(feature = "pem")] #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] - /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary + /// format). pub fn from_der(bytes: &[u8]) -> Result { p256::pkcs8::DecodePrivateKey::from_pkcs8_der(bytes) .map(Self) @@ -268,19 +272,18 @@ impl Verifier for Secp256r1Verifier { #[cfg(test)] mod test { - use super::*; - use crate::SuiSigner; - use crate::SuiVerifier; - use sui_sdk_types::PersonalMessage; + use iota_sdk_types::PersonalMessage; use test_strategy::proptest; - #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + use crate::{IotaSigner, IotaVerifier}; + // TODO need to export proptest impl from core crate // #[proptest] - // fn transaction_signing(signer: Secp256r1PrivateKey, transaction: Transaction) { - // let signature = signer.sign_transaction(&transaction).unwrap(); + // fn transaction_signing(signer: Secp256r1PrivateKey, transaction: Transaction) + // { let signature = signer.sign_transaction(&transaction).unwrap(); // let verifier = signer.public_key(); // verifier // .verify_transaction(&transaction, &signature) diff --git a/crates/sui-crypto/src/simple.rs b/crates/iota-crypto/src/simple.rs similarity index 94% rename from crates/sui-crypto/src/simple.rs rename to crates/iota-crypto/src/simple.rs index 0a9b8d95e..fcb97ff96 100644 --- a/crates/sui-crypto/src/simple.rs +++ b/crates/iota-crypto/src/simple.rs @@ -1,7 +1,11 @@ -use crate::SignatureError; +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use iota_sdk_types::{SimpleSignature, UserSignature}; use signature::Verifier; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::UserSignature; + +use crate::SignatureError; pub struct SimpleVerifier; @@ -67,7 +71,7 @@ impl Verifier for SimpleVerifier { doc(cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))) )] #[rustfmt::skip] -pub use keypair::{SimpleKeypair, SimpleVerifiyingKey}; +pub use keypair::{SimpleKeypair, SimpleVerifyingKey}; #[cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))] #[cfg_attr( @@ -75,13 +79,12 @@ pub use keypair::{SimpleKeypair, SimpleVerifiyingKey}; doc(cfg(any(feature = "ed25519", feature = "secp256r1", feature = "secp256k1",))) )] mod keypair { + use iota_sdk_types::{ + MultisigMemberPublicKey, SignatureScheme, SimpleSignature, UserSignature, + }; + use signature::{Signer, Verifier}; + use crate::SignatureError; - use signature::Signer; - use signature::Verifier; - use sui_sdk_types::MultisigMemberPublicKey; - use sui_sdk_types::SignatureScheme; - use sui_sdk_types::SimpleSignature; - use sui_sdk_types::UserSignature; pub struct SimpleKeypair { inner: InnerKeypair, @@ -108,7 +111,7 @@ mod keypair { } } - pub fn verifying_key(&self) -> SimpleVerifiyingKey { + pub fn verifying_key(&self) -> SimpleVerifyingKey { let verifying_key = match &self.inner { #[cfg(feature = "ed25519")] InnerKeypair::Ed25519(private_key) => { @@ -124,7 +127,7 @@ mod keypair { } }; - SimpleVerifiyingKey { + SimpleVerifyingKey { inner: verifying_key, } } @@ -135,7 +138,8 @@ mod keypair { #[cfg(feature = "pem")] #[cfg_attr(doc_cfg, doc(cfg(feature = "pem")))] - /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary format). + /// Deserialize PKCS#8 private key from ASN.1 DER-encoded data (binary + /// format). pub fn from_der(bytes: &[u8]) -> Result { let private_key = pkcs8::PrivateKeyInfo::try_from(bytes).map_err(SignatureError::from_source)?; @@ -270,7 +274,7 @@ mod keypair { } } - pub struct SimpleVerifiyingKey { + pub struct SimpleVerifyingKey { inner: InnerVerifyingKey, } @@ -283,7 +287,7 @@ mod keypair { Secp256r1(crate::secp256r1::Secp256r1VerifyingKey), } - impl SimpleVerifiyingKey { + impl SimpleVerifyingKey { pub fn scheme(&self) -> SignatureScheme { match &self.inner { #[cfg(feature = "ed25519")] @@ -399,7 +403,7 @@ mod keypair { } } - impl Verifier for SimpleVerifiyingKey { + impl Verifier for SimpleVerifyingKey { fn verify( &self, message: &[u8], @@ -422,7 +426,7 @@ mod keypair { } } - impl Verifier for SimpleVerifiyingKey { + impl Verifier for SimpleVerifyingKey { fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> { let UserSignature::Simple(signature) = signature else { return Err(SignatureError::from_source("not a simple signature")); @@ -434,7 +438,7 @@ mod keypair { #[cfg(feature = "ed25519")] #[cfg_attr(doc_cfg, doc(cfg(feature = "ed25519")))] - impl From for SimpleVerifiyingKey { + impl From for SimpleVerifyingKey { fn from(verifying_key: crate::ed25519::Ed25519VerifyingKey) -> Self { Self { inner: InnerVerifyingKey::Ed25519(verifying_key), @@ -444,7 +448,7 @@ mod keypair { #[cfg(feature = "secp256r1")] #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256r1")))] - impl From for SimpleVerifiyingKey { + impl From for SimpleVerifyingKey { fn from(verifying_key: crate::secp256r1::Secp256r1VerifyingKey) -> Self { Self { inner: InnerVerifyingKey::Secp256r1(verifying_key), @@ -454,7 +458,7 @@ mod keypair { #[cfg(feature = "secp256k1")] #[cfg_attr(doc_cfg, doc(cfg(feature = "secp256k1")))] - impl From for SimpleVerifiyingKey { + impl From for SimpleVerifyingKey { fn from(verifying_key: crate::secp256k1::Secp256k1VerifyingKey) -> Self { Self { inner: InnerVerifyingKey::Secp256k1(verifying_key), @@ -465,21 +469,19 @@ mod keypair { #[cfg(test)] mod test { - use super::*; - use crate::ed25519::Ed25519PrivateKey; - use crate::ed25519::Ed25519VerifyingKey; - use crate::secp256k1::Secp256k1PrivateKey; - use crate::secp256k1::Secp256k1VerifyingKey; - use crate::secp256r1::Secp256r1PrivateKey; - use crate::secp256r1::Secp256r1VerifyingKey; use test_strategy::proptest; - #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + use crate::{ + ed25519::{Ed25519PrivateKey, Ed25519VerifyingKey}, + secp256k1::{Secp256k1PrivateKey, Secp256k1VerifyingKey}, + secp256r1::{Secp256r1PrivateKey, Secp256r1VerifyingKey}, + }; + #[proptest] fn ed25519_pem_der(signer: Ed25519PrivateKey) { - // // Private Key // let public_key = signer.public_key(); @@ -504,7 +506,6 @@ mod test { let keypair_from_pem = SimpleKeypair::from_pem(&ed25519_pem).unwrap(); assert_eq!(ed25519_pem, keypair_from_pem.to_pem().unwrap()); - // // Verifying Key // let verifying_key = signer.verifying_key(); @@ -524,15 +525,14 @@ mod test { Secp256k1VerifyingKey::from_pem(&pem).unwrap_err(); // SimpleKeypair parses - let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + let from_der = SimpleVerifyingKey::from_der(&der).unwrap(); assert_eq!(der, from_der.to_der().unwrap()); - let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + let from_pem = SimpleVerifyingKey::from_pem(&pem).unwrap(); assert_eq!(pem, from_pem.to_pem().unwrap()); } #[proptest] fn secp256r1_pem_der(signer: Secp256r1PrivateKey) { - // // Private Key // let public_key = signer.public_key(); @@ -557,7 +557,6 @@ mod test { let keypair_from_pem = SimpleKeypair::from_pem(&secp256r1_pem).unwrap(); assert_eq!(secp256r1_pem, keypair_from_pem.to_pem().unwrap()); - // // Verifying Key // let verifying_key = signer.verifying_key(); @@ -577,15 +576,14 @@ mod test { Secp256k1VerifyingKey::from_pem(&pem).unwrap_err(); // SimpleKeypair parses - let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + let from_der = SimpleVerifyingKey::from_der(&der).unwrap(); assert_eq!(der, from_der.to_der().unwrap()); - let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + let from_pem = SimpleVerifyingKey::from_pem(&pem).unwrap(); assert_eq!(pem, from_pem.to_pem().unwrap()); } #[proptest] fn secp256k1_pem_der(signer: Secp256k1PrivateKey) { - // // Private Key // let public_key = signer.public_key(); @@ -610,7 +608,6 @@ mod test { let keypair_from_pem = SimpleKeypair::from_pem(&secp256k1_pem).unwrap(); assert_eq!(secp256k1_pem, keypair_from_pem.to_pem().unwrap()); - // // Verifying Key // let verifying_key = signer.verifying_key(); @@ -630,9 +627,9 @@ mod test { Secp256r1VerifyingKey::from_pem(&pem).unwrap_err(); // SimpleKeypair parses - let from_der = SimpleVerifiyingKey::from_der(&der).unwrap(); + let from_der = SimpleVerifyingKey::from_der(&der).unwrap(); assert_eq!(der, from_der.to_der().unwrap()); - let from_pem = SimpleVerifiyingKey::from_pem(&pem).unwrap(); + let from_pem = SimpleVerifyingKey::from_pem(&pem).unwrap(); assert_eq!(pem, from_pem.to_pem().unwrap()); } } diff --git a/crates/sui-crypto/src/zklogin/mod.rs b/crates/iota-crypto/src/zklogin/mod.rs similarity index 90% rename from crates/sui-crypto/src/zklogin/mod.rs rename to crates/iota-crypto/src/zklogin/mod.rs index 2af4c38f4..f286ecd47 100644 --- a/crates/sui-crypto/src/zklogin/mod.rs +++ b/crates/iota-crypto/src/zklogin/mod.rs @@ -1,14 +1,14 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use std::collections::HashMap; -use crate::SignatureError; +use iota_sdk_types::{Claim, Jwk, JwkId, UserSignature, ZkLoginAuthenticator, ZkLoginInputs}; use poseidon::POSEIDON; use signature::Verifier; -use sui_sdk_types::Claim; -use sui_sdk_types::Jwk; -use sui_sdk_types::JwkId; -use sui_sdk_types::UserSignature; -use sui_sdk_types::ZkLoginAuthenticator; -use sui_sdk_types::ZkLoginInputs; + +use crate::SignatureError; mod poseidon; mod verify; @@ -115,8 +115,7 @@ struct JwtHeader { impl JwtHeader { fn from_base64(s: &str) -> Result { - use base64ct::Base64UrlUnpadded; - use base64ct::Encoding; + use base64ct::{Base64UrlUnpadded, Encoding}; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] struct Header { @@ -137,10 +136,13 @@ impl JwtHeader { } } -/// Parse the extended claim json value to its claim value, using the expected claim key. +/// Parse the extended claim json value to its claim value, using the expected +/// claim key. fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result { - /// Map a base64 string to a bit array by taking each char's index and convert it to binary form with one bit per u8 - /// element in the output. Returns SignatureError if one of the characters is not in the base64 charset. + /// Map a base64 string to a bit array by taking each char's index and + /// convert it to binary form with one bit per u8 element in the output. + /// Returns SignatureError if one of the characters is not in the base64 + /// charset. fn base64_to_bitarray(input: &str) -> Result, SignatureError> { use itertools::Itertools; @@ -160,8 +162,8 @@ fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result Result, SignatureError> { if bits.len() % 8 != 0 { return Err(SignatureError::from_source( @@ -180,7 +182,8 @@ fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result Result { if s.len() < 2 { return Err(SignatureError::from_source("Base64 string smaller than 2")); @@ -245,10 +248,10 @@ fn verify_extended_claim(claim: &Claim, expected_key: &str) -> Result Result { +) -> Result { const ISS: &str = "iss"; let iss = verify_extended_claim(&inputs.iss_base64_details, ISS)?; - sui_sdk_types::ZkLoginPublicIdentifier::new(iss, inputs.address_seed.clone()) + iota_sdk_types::ZkLoginPublicIdentifier::new(iss, inputs.address_seed.clone()) .ok_or_else(|| SignatureError::from_source("invalid iss")) } diff --git a/crates/sui-crypto/src/zklogin/poseidon/constants.rs b/crates/iota-crypto/src/zklogin/poseidon/constants.rs similarity index 99% rename from crates/sui-crypto/src/zklogin/poseidon/constants.rs rename to crates/iota-crypto/src/zklogin/poseidon/constants.rs index 34b4332ba..cb8cf355c 100644 --- a/crates/sui-crypto/src/zklogin/poseidon/constants.rs +++ b/crates/iota-crypto/src/zklogin/poseidon/constants.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + pub fn constants() -> (Vec>, Vec>>) { let c_str: Vec> = vec![ vec![ diff --git a/crates/sui-crypto/src/zklogin/poseidon/mod.rs b/crates/iota-crypto/src/zklogin/poseidon/mod.rs similarity index 97% rename from crates/sui-crypto/src/zklogin/poseidon/mod.rs rename to crates/iota-crypto/src/zklogin/poseidon/mod.rs index dd42cec4c..ffe8885a8 100644 --- a/crates/sui-crypto/src/zklogin/poseidon/mod.rs +++ b/crates/iota-crypto/src/zklogin/poseidon/mod.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Poseidon Hash implementation using ark-ff //! //! This module is vendored from at commit @@ -5,12 +9,11 @@ #![allow(clippy::needless_range_loop)] +use core::ops::{AddAssign, MulAssign}; + use ark_bn254::Fr; use ark_ff::fields::Field; -use ark_std::str::FromStr; -use ark_std::Zero; -use core::ops::AddAssign; -use core::ops::MulAssign; +use ark_std::{Zero, str::FromStr}; mod constants; @@ -127,12 +130,12 @@ impl Poseidon { #[cfg(test)] mod tests { - use super::*; - #[cfg(test)] #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + #[test] fn test_load_constants() { let cons = load_constants(); diff --git a/crates/sui-crypto/src/zklogin/tests.rs b/crates/iota-crypto/src/zklogin/tests.rs similarity index 93% rename from crates/sui-crypto/src/zklogin/tests.rs rename to crates/iota-crypto/src/zklogin/tests.rs index 0f9b25c65..9fdb09f5f 100644 --- a/crates/sui-crypto/src/zklogin/tests.rs +++ b/crates/iota-crypto/src/zklogin/tests.rs @@ -1,10 +1,12 @@ -use signature::Signer; -use sui_sdk_types::PersonalMessage; +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 -use crate::ed25519::Ed25519PrivateKey; -use crate::SuiVerifier; +use iota_sdk_types::PersonalMessage; +use signature::Signer; use super::*; +use crate::{IotaVerifier, ed25519::Ed25519PrivateKey}; /// Returns a valid zklogin material for testing only. fn test_zklogin_material() -> (Jwk, JwkId, ZkLoginInputs, Ed25519PrivateKey, u64) { diff --git a/crates/sui-crypto/src/zklogin/verify.rs b/crates/iota-crypto/src/zklogin/verify.rs similarity index 93% rename from crates/sui-crypto/src/zklogin/verify.rs rename to crates/iota-crypto/src/zklogin/verify.rs index 1af55fed0..63aa0a63a 100644 --- a/crates/sui-crypto/src/zklogin/verify.rs +++ b/crates/iota-crypto/src/zklogin/verify.rs @@ -1,28 +1,19 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use std::str::FromStr; -use crate::SignatureError; -use ark_bn254::Fq; -use ark_bn254::Fq2; -use ark_bn254::Fr; -use ark_bn254::G1Affine; -use ark_bn254::G1Projective; -use ark_bn254::G2Affine; -use ark_bn254::G2Projective; +use ark_bn254::{Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; use ark_ff::PrimeField; -use ark_groth16::PreparedVerifyingKey; -use ark_groth16::Proof; -use sui_sdk_types::Bn254FieldElement; -use sui_sdk_types::CircomG1; -use sui_sdk_types::CircomG2; -use sui_sdk_types::Ed25519PublicKey; -use sui_sdk_types::Jwk; -use sui_sdk_types::Secp256k1PublicKey; -use sui_sdk_types::Secp256r1PublicKey; -use sui_sdk_types::SimpleSignature; -use sui_sdk_types::ZkLoginInputs; -use sui_sdk_types::ZkLoginProof; +use ark_groth16::{PreparedVerifyingKey, Proof}; +use iota_sdk_types::{ + Bn254FieldElement, CircomG1, CircomG2, Ed25519PublicKey, Jwk, Secp256k1PublicKey, + Secp256r1PublicKey, SimpleSignature, ZkLoginInputs, ZkLoginProof, +}; use super::POSEIDON; +use crate::SignatureError; #[derive(Clone, Debug)] pub struct VerifyingKey { @@ -159,7 +150,8 @@ fn mainnet_verifying_key() -> VerifyingKey { } } -/// Load a fixed verifying key from zkLogin.vkey output. This is based on a local setup and should not use in production. +/// Load a fixed verifying key from zkLogin.vkey output. This is based on a +/// local setup and should not use in production. fn dev_verifying_key() -> VerifyingKey { const CIRCOM_ALPHA_G1: CircomG1 = build_circom_g1([ "20491192805390485299153009773594534940189261866228447918068658471970481763042", @@ -251,8 +243,7 @@ impl VerifyingKey { signature: &SimpleSignature, max_epoch: u64, ) -> Result<(), SignatureError> { - use base64ct::Base64UrlUnpadded; - use base64ct::Encoding; + use base64ct::{Base64UrlUnpadded, Encoding}; // Decode modulus to bytes. let modulus = Base64UrlUnpadded::decode_vec(&jwk.n) .map_err(|e| SignatureError::from_source(e.to_string()))?; @@ -294,11 +285,11 @@ fn zklogin_proof_to_arkworks( }) } -/// Given a SimpleSignature convert the corrisponding public key, prefixed with the signature -/// scheme flag, to two Bn254Frs +/// Given a SimpleSignature convert the corrisponding public key, prefixed with +/// the signature scheme flag, to two Bn254Frs pub fn public_key_to_frs(signature: &SimpleSignature) -> (Fr, Fr) { - // buf length of the longest public key secp256r1/secp256k1 of 33 bytes plus 1 byte for the - // scheme + // buf length of the longest public key secp256r1/secp256k1 of 33 bytes plus 1 + // byte for the scheme let mut buf = [0u8; 34]; buf[0] = signature.scheme().to_u8(); @@ -318,9 +309,10 @@ pub fn public_key_to_frs(signature: &SimpleSignature) -> (Fr, Fr) { } }; - //TODO this comment is wrong... - // Split the bytes deterministically such that the first element contains the first 128 - // bits of the hash, and the second element contains the latter ones. + // TODO this comment is wrong... + // Split the bytes deterministically such that the first element contains the + // first 128 bits of the hash, and the second element contains the latter + // ones. let (first_half, second_half) = buf.split_at(buf.len() - 16); let eph_public_key_0 = Fr::from_be_bytes_mod_order(first_half); @@ -360,10 +352,10 @@ fn pad_with_zeroes(in_arr: Vec, out_count: u8) -> Result, Signat Ok(padded) } -/// Maps a stream of bigints to a single field element. First we convert the base from -/// inWidth to packWidth. Then we compute the poseidon hash of the "packed" input. -/// input is the input vector containing equal-width big ints. inWidth is the width of -/// each input element. +/// Maps a stream of bigints to a single field element. First we convert the +/// base from inWidth to packWidth. Then we compute the poseidon hash of the +/// "packed" input. input is the input vector containing equal-width big ints. +/// inWidth is the width of each input element. fn hash_to_field( input: &[T], in_width: u16, @@ -402,10 +394,9 @@ fn big_int_array_to_bits( integers: &[T], intended_size: usize, ) -> Result, SignatureError> { + use std::cmp::Ordering::{Equal, Greater, Less}; + use itertools::Itertools; - use std::cmp::Ordering::Equal; - use std::cmp::Ordering::Greater; - use std::cmp::Ordering::Less; integers .iter() @@ -442,7 +433,8 @@ impl ToBits for U2048 { } } -/// Calculate the poseidon hash from selected fields from inputs, along with the ephemeral pubkey. +/// Calculate the poseidon hash from selected fields from inputs, along with the +/// ephemeral pubkey. pub fn calculate_all_inputs_hash( inputs: &ZkLoginInputs, signature: &SimpleSignature, @@ -481,7 +473,7 @@ pub fn calculate_all_inputs_hash( .map_err(SignatureError::from_source) } -/// Calculate the Sui address based on address seed and address params. +/// Calculate the IOTA address based on address seed and address params. #[allow(unused)] fn gen_address_seed( salt: &str, @@ -501,7 +493,8 @@ const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32; const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115; const MAX_AUD_VALUE_LENGTH: u8 = 145; -/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as input instead of the salt. +/// Same as [`gen_address_seed`] but takes the poseidon hash of the salt as +/// input instead of the salt. pub(crate) fn gen_address_seed_with_salt_hash( salt_hash: Fr, name: &str, // i.e. "sub" @@ -521,13 +514,13 @@ pub(crate) fn gen_address_seed_with_salt_hash( #[cfg(test)] mod test { - use super::*; - use sui_sdk_types::Ed25519Signature; - + use iota_sdk_types::Ed25519Signature; #[cfg(test)] #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; + use super::*; + #[test] fn test_verify_zklogin_google() { let user_salt = "206703048842351542647799591018316385612"; diff --git a/crates/iota-graphql-client-build/Cargo.toml b/crates/iota-graphql-client-build/Cargo.toml new file mode 100644 index 000000000..393856e7c --- /dev/null +++ b/crates/iota-graphql-client-build/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "iota-graphql-client-build" +version = "0.0.0" +authors = ["IOTA Foundation "] +edition = "2021" + +[dependencies] +cynic-codegen = "3.8.0" diff --git a/crates/sui-graphql-client-build/README.md b/crates/iota-graphql-client-build/README.md similarity index 55% rename from crates/sui-graphql-client-build/README.md rename to crates/iota-graphql-client-build/README.md index e6f20fbec..629700f04 100644 --- a/crates/sui-graphql-client-build/README.md +++ b/crates/iota-graphql-client-build/README.md @@ -1,37 +1,42 @@ ### Description + This crate provides a function to register a schema to enable building custom queries using cynic derive macros queries. Call this function in a `build.rs` file in your crate if you need to build custom queries. ### Usage + 1. Add this crate as a build dependency in your `Cargo.toml` file. + ```toml [build-dependencies] -sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client-build", branch = "master" } +iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "master" } ``` 2. Add a `build.rs` file in your crate root directory and call the `register_schema` function in it. + ```rust,ignore // build.rs file fn main() { let schema_name = "MYSCHEMA"; - sui_graphql_client_build::register_schema(schema_name); + iota_graphql_client_build::register_schema(schema_name); } ``` -3. Add the `cynic` and `sui-graphql-client` dependencies in your `Cargo.toml` file. You should have something like this. +3. Add the `cynic` and `iota-graphql-client` dependencies in your `Cargo.toml` file. You should have something like this. + ```toml # Cargo.toml # ... [dependencies] cynic = "3.8.0" -sui-graphql-client = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client", branch = "master" } +iota-graphql-client = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client", branch = "master" } [build-dependencies] -sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", package = "sui-graphql-client-build", branch = "master" } +iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "master" } ``` -4. If using `cynic`, use the cynic generator to generate the Rust types from the GraphQL schema. \ - Go to https://generator.cynic-rs.dev/ and paste the URL to the GraphQL service or manually copy paste the schema. \ +4. If using `cynic`, use the cynic generator to generate the Rust types from the GraphQL schema.\ + Go to and paste the URL to the GraphQL service or manually copy paste the schema.\ Then you can select the fields in the query you want to have, and the generator will generate the Rust types for you. 5. In your Rust code, you can now use the custom query types generated by `cynic`. @@ -40,7 +45,7 @@ sui-graphql-client-build = { git = "https://github.com/mystenlabs/sui-rust-sdk", // lib.rs // Custom query use cynic::QueryBuilder; -use sui_graphql_client::{query_types::schema, Client}; +use iota_graphql_client::{query_types::schema, Client}; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "MYSCHEMA", graphql_type = "Query")] @@ -57,6 +62,6 @@ async fn main() { } ``` -6. For `UInt53`, you can use `u64` type directly as the `sui-graphql-client`'s schema implements the `impl_scalar`. Similarly for other types (Base64, DateTime). See more available types here: https://github.com/MystenLabs/sui-rust-sdk/blob/02639f6b09375fe03fa2243868be17bec1dfa33c/crates/sui-graphql-client/src/query_types/mod.rs?plain=1#L124-L126 +6. For `UInt53`, you can use `u64` type directly as the `iota-graphql-client`'s schema implements the `impl_scalar`. Similarly for other types (Base64, DateTime). See more available types here: 7. Read the `cynic` [documentation](https://cynic-rs.dev/) to learn how to work with it, particularly when it comes to passing arguments to the query. diff --git a/crates/sui-graphql-client-build/schema.graphql b/crates/iota-graphql-client-build/schema.graphql similarity index 92% rename from crates/sui-graphql-client-build/schema.graphql rename to crates/iota-graphql-client-build/schema.graphql index bdeadd932..c1861be31 100644 --- a/crates/sui-graphql-client-build/schema.graphql +++ b/crates/iota-graphql-client-build/schema.graphql @@ -62,14 +62,14 @@ type ActiveJwkEdge { The 32-byte address that is an account address (corresponding to a public key). """ type Address implements IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this address, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this address. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -79,22 +79,22 @@ type Address implements IOwner { """ The coin objects for this address. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this address. + The `0x3::staking_pool::StakedIota` objects owned by this address. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this address. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this address. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this address. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! """ Similar behavior to the `transactionBlocks` in Query but supporting the additional `AddressTransactionBlockRelationship` filter, which defaults to `SENT`. @@ -235,7 +235,7 @@ The total balance for a particular coin type. """ type Balance { """ - Coin type for the balance, such as 0x2::sui::SUI + Coin type for the balance, such as 0x2::iota::IOTA """ coinType: MoveType! """ @@ -257,7 +257,7 @@ type BalanceChange { """ owner: Owner """ - The inner type of the coin whose balance has changed (e.g. `0x2::sui::SUI`). + The inner type of the coin whose balance has changed (e.g. `0x2::iota::IOTA`). """ coinType: MoveType """ @@ -360,20 +360,20 @@ type ChangeEpochTransaction { """ protocolVersion: UInt53! """ - The total amount of gas charged for storage during the previous epoch (in MIST). + The total amount of gas charged for storage during the previous epoch (in NANOS). """ storageCharge: BigInt! """ - The total amount of gas charged for computation during the previous epoch (in MIST). + The total amount of gas charged for computation during the previous epoch (in NANOS). """ computationCharge: BigInt! """ - The SUI returned to transaction senders for cleaning up objects (in MIST). + The IOTA returned to transaction senders for cleaning up objects (in NANOS). """ storageRebate: BigInt! """ The total gas retained from storage fees, that will not be returned by storage rebates when - the relevant objects are cleaned up (in MIST). + the relevant objects are cleaned up (in NANOS). """ nonRefundableStorageFee: BigInt! """ @@ -500,14 +500,14 @@ input CheckpointId { Some 0x2::coin::Coin Move object. """ type Coin implements IMoveObject & IObject & IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -517,22 +517,22 @@ type Coin implements IMoveObject & IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -557,7 +557,7 @@ type Coin implements IMoveObject & IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -596,7 +596,7 @@ type Coin implements IMoveObject & IObject & IOwner { contents: MoveValue """ Determines whether a transaction can transfer this object, using the TransferObjects - transaction command or `sui::transfer::public_transfer`, both of which require the object to + transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! @@ -678,14 +678,14 @@ type CoinEdge { The metadata for a coin type. """ type CoinMetadata implements IMoveObject & IObject & IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -695,22 +695,22 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -735,7 +735,7 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -774,7 +774,7 @@ type CoinMetadata implements IMoveObject & IObject & IOwner { contents: MoveValue """ Determines whether a transaction can transfer this object, using the TransferObjects - transaction command or `sui::transfer::public_transfer`, both of which require the object to + transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! @@ -972,7 +972,7 @@ fields: 1) Dynamic Fields can store any value that has the `store` ability, however an object stored in this kind of field will be considered wrapped and will not be accessible directly via its ID by external tools (explorers, wallets, etc) accessing storage. -2) Dynamic Object Fields values must be Sui objects (have the `key` and `store` +2) Dynamic Object Fields values must be Iota objects (have the `key` and `store` abilities, and id: UID as the first field), but will still be directly accessible off-chain via their object ID after being attached. """ @@ -1078,7 +1078,7 @@ type EndOfEpochTransactionKindEdge { } """ -Operation of the Sui network is temporally partitioned into non-overlapping epochs, +Operation of the Iota network is temporally partitioned into non-overlapping epochs, and the network aims to keep epochs roughly the same duration as each other. During a particular epoch the following data is fixed: @@ -1116,11 +1116,11 @@ type Epoch { """ totalTransactions: UInt53 """ - The total amount of gas fees (in MIST) that were paid in this epoch. + The total amount of gas fees (in NANOS) that were paid in this epoch. """ totalGasFees: BigInt """ - The total MIST rewarded as stake. + The total NANOS rewarded as stake. """ totalStakeRewards: BigInt """ @@ -1153,7 +1153,7 @@ type Epoch { """ protocolConfigs: ProtocolConfigs! """ - SUI set aside to account for objects stored on-chain, at the start of the epoch. + IOTA set aside to account for objects stored on-chain, at the start of the epoch. This is also used for storage rebates. """ storageFund: StorageFund @@ -1163,7 +1163,7 @@ type Epoch { """ safeMode: SafeMode """ - The value of the `version` field of `0x5`, the `0x3::sui::SuiSystemState` object. This + The value of the `version` field of `0x5`, the `0x3::iota::IotaSystemState` object. This version changes whenever the fields contained in the system state object (held in a dynamic field attached to `0x5`) change. """ @@ -1303,7 +1303,7 @@ input EventFilter { """ Filter down to events from transactions sent by this address. """ - sender: SuiAddress + sender: IotaAddress """ Filter down to the events from this transaction (given by its transaction digest). """ @@ -1326,7 +1326,7 @@ input EventFilter { Generic types can be queried by either the generic type name, e.g. `0x2::coin::Coin`, or by the full type name, such as - `0x2::coin::Coin<0x2::sui::SUI>`. + `0x2::coin::Coin<0x2::iota::IOTA>`. """ eventType: String } @@ -1379,7 +1379,7 @@ enum Feature { """ DYNAMIC_FIELDS """ - SuiNS name and reverse name look-up. + IotaNS name and reverse name look-up. """ NAME_SERVICE """ @@ -1414,22 +1414,22 @@ Breakdown of gas costs in effects. """ type GasCostSummary { """ - Gas paid for executing this transaction (in MIST). + Gas paid for executing this transaction (in NANOS). """ computationCost: BigInt """ - Gas paid for the data stored on-chain by this transaction (in MIST). + Gas paid for the data stored on-chain by this transaction (in NANOS). """ storageCost: BigInt """ Part of storage cost that can be reclaimed by cleaning up data created by this transaction (when objects are deleted or an object is modified, which is treated as a deletion followed - by a creation) (in MIST). + by a creation) (in NANOS). """ storageRebate: BigInt """ Part of storage cost that is not reclaimed when data created by this transaction is cleaned - up (in MIST). + up (in NANOS). """ nonRefundableStorageFee: BigInt } @@ -1456,7 +1456,7 @@ type GasInput { gasPayment(first: Int, after: String, last: Int, before: String): ObjectConnection! """ An unsigned integer specifying the number of native tokens per gas unit this transaction - will pay (in MIST). + will pay (in NANOS). """ gasPrice: BigInt """ @@ -1511,7 +1511,7 @@ interface IMoveObject { """ contents: MoveValue """ - Determines whether a transaction can transfer this object, using the TransferObjects transaction command or `sui::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. + Determines whether a transaction can transfer this object, using the TransferObjects transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! """ @@ -1582,13 +1582,13 @@ object. The same address can only refer to an account or an object, never both, possible to know which up-front. """ interface IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object or address, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ - Total balance of all coins with marker type owned by this object or address. If type is not supplied, it defaults to `0x2::sui::SUI`. + Total balance of all coins with marker type owned by this object or address. If type is not supplied, it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -1598,21 +1598,21 @@ interface IOwner { """ The coin objects for this object or address. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object or address. + The `0x3::staking_pool::StakedIota` objects owned by this object or address. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object or address. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object or address. These grant the owner the capability to manage the associated domain. + The IotaNSRegistration NFTs owned by this object or address. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! } """ @@ -1646,11 +1646,11 @@ type Linkage { """ The ID on-chain of the first version of the dependency. """ - originalId: SuiAddress! + originalId: IotaAddress! """ The ID on-chain of the version of the dependency that this package depends on. """ - upgradedId: SuiAddress! + upgradedId: IotaAddress! """ The version of the dependency that this package depends on. """ @@ -1686,7 +1686,7 @@ type MergeCoinsTransaction { } """ -Abilities are keywords in Sui Move that define how types behave at the compiler level. +Abilities are keywords in Iota Move that define how types behave at the compiler level. """ enum MoveAbility { """ @@ -1714,7 +1714,7 @@ type MoveCallTransaction { """ The storage ID of the package the function being called is defined in. """ - package: SuiAddress! + package: IotaAddress! """ The name of the module the function being called is defined in. """ @@ -1741,9 +1741,9 @@ type MoveCallTransaction { The contents of a Move Value, corresponding to the following recursive type: type MoveData = - { Address: SuiAddress } - | { UID: SuiAddress } - | { ID: SuiAddress } + { Address: IotaAddress } + | { UID: IotaAddress } + | { ID: IotaAddress } | { Bool: bool } | { Number: BigInt } | { String: string } @@ -2048,14 +2048,14 @@ The representation of an object as a Move Object, which exposes additional infor (content, module that governs it, version, is transferrable, etc.) about this object. """ type MoveObject implements IMoveObject & IObject & IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -2065,22 +2065,22 @@ type MoveObject implements IMoveObject & IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -2105,7 +2105,7 @@ type MoveObject implements IMoveObject & IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -2144,7 +2144,7 @@ type MoveObject implements IMoveObject & IObject & IOwner { contents: MoveValue """ Determines whether a transaction can transfer this object, using the TransferObjects - transaction command or `sui::transfer::public_transfer`, both of which require the object to + transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! @@ -2185,17 +2185,17 @@ type MoveObject implements IMoveObject & IObject & IOwner { """ asCoin: Coin """ - Attempts to convert the Move object into a `0x3::staking_pool::StakedSui`. + Attempts to convert the Move object into a `0x3::staking_pool::StakedIota`. """ - asStakedSui: StakedSui + asStakedIota: StakedIota """ Attempts to convert the Move object into a `0x2::coin::CoinMetadata`. """ asCoinMetadata: CoinMetadata """ - Attempts to convert the Move object into a `SuinsRegistration` object. + Attempts to convert the Move object into a `IotaNSRegistration` object. """ - asSuinsRegistration: SuinsRegistration + asIotaNSRegistration: IotaNSRegistration } type MoveObjectConnection { @@ -2232,7 +2232,7 @@ A MovePackage is a kind of Move object that represents code that has been publis It exposes information about its modules, type definitions, functions, and dependencies. """ type MovePackage implements IObject & IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this package, optionally `filter`-ed. @@ -2242,7 +2242,7 @@ type MovePackage implements IObject & IOwner { objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this package. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. Note that coins owned by a package are inaccessible, because packages are immutable and cannot be owned by an address. @@ -2258,31 +2258,31 @@ type MovePackage implements IObject & IOwner { """ The coin objects owned by this package. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. Note that coins owned by a package are inaccessible, because packages are immutable and cannot be owned by an address. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this package. + The `0x3::staking_pool::StakedIota` objects owned by this package. Note that objects owned by a package are inaccessible, because packages are immutable and cannot be owned by an address. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this package. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this package. These grant the owner the capability to manage the associated domain. Note that objects owned by a package are inaccessible, because packages are immutable and cannot be owned by an address. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -2308,7 +2308,7 @@ type MovePackage implements IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. Note that packages cannot be deleted or mutated, so this number is provided purely for @@ -2630,7 +2630,7 @@ enum MoveVisibility { } """ -Mutations are used to write to the Sui network. +Mutations are used to write to the Iota network. """ type Mutation { """ @@ -2653,19 +2653,19 @@ type Mutation { } """ -An object in Sui is a package (set of Move bytecode modules) or object (typed data structure +An object in Iota is a package (set of Move bytecode modules) or object (typed data structure with fields) with additional metadata detailing its id, version, transaction digest, owner field indicating how this object can be accessed. """ type Object implements IObject & IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -2675,22 +2675,22 @@ type Object implements IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -2716,7 +2716,7 @@ type Object implements IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -2796,7 +2796,7 @@ type ObjectChange { """ The address of the object that has changed. """ - address: SuiAddress! + address: IotaAddress! """ The contents of the object immediately before the transaction. """ @@ -2887,17 +2887,17 @@ input ObjectFilter { name. Generic types can be queried by either the generic type name, e.g. `0x2::coin::Coin`, or by - the full type name, such as `0x2::coin::Coin<0x2::sui::SUI>`. + the full type name, such as `0x2::coin::Coin<0x2::iota::IOTA>`. """ type: String """ Filter for live objects by their current owners. """ - owner: SuiAddress + owner: IotaAddress """ Filter for live objects by their IDs. """ - objectIds: [SuiAddress!] + objectIds: [IotaAddress!] """ Filter for live or potentially historical objects by their ID and version. """ @@ -2905,7 +2905,7 @@ input ObjectFilter { } input ObjectKey { - objectId: SuiAddress! + objectId: IotaAddress! version: UInt53! } @@ -2930,7 +2930,7 @@ input ObjectRef { """ ID of the object. """ - address: SuiAddress! + address: IotaAddress! """ Version or sequence number of the object. """ @@ -2988,7 +2988,7 @@ type OwnedOrImmutable { """ ID of the object being read. """ - address: SuiAddress! + address: IotaAddress! """ Version of the object being read. """ @@ -3005,19 +3005,19 @@ type OwnedOrImmutable { } """ -An Owner is an entity that can own an object. Each Owner is identified by a SuiAddress which +An Owner is an entity that can own an object. Each Owner is identified by a IotaAddress which represents either an Address (corresponding to a public key of an account) or an Object, but never both (it is not known up-front whether a given Owner is an Address or an Object). """ type Owner implements IOwner { - address: SuiAddress! + address: IotaAddress! """ Objects owned by this object or address, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object or address. If type is not - supplied, it defaults to `0x2::sui::SUI`. + supplied, it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -3027,22 +3027,22 @@ type Owner implements IOwner { """ The coin objects for this object or address. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object or address. + The `0x3::staking_pool::StakedIota` objects owned by this object or address. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object or address. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object or address. These grant the owner the + The IotaNSRegistration NFTs owned by this object or address. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! asAddress: Address asObject: Object """ @@ -3211,7 +3211,7 @@ type PublishTransaction { """ IDs of the transitive dependencies of the package to be published. """ - dependencies: [SuiAddress!]! + dependencies: [IotaAddress!]! } """ @@ -3260,7 +3260,7 @@ type Query { """ dryRunTransactionBlock(txBytes: String!, txMeta: TransactionMetadata, skipChecks: Boolean): DryRunResult! """ - Look up an Owner by its SuiAddress. + Look up an Owner by its IotaAddress. `rootVersion` represents the version of the root object in some nested chain of dynamic fields. It allows consistent historical queries for the case of wrapped objects, which don't @@ -3272,16 +3272,16 @@ type Query { from above when querying `Owner.asObject`. This can be used, for example, to get the contents of a dynamic object field when its parent was at `rootVersion`. - If `rootVersion` is omitted, dynamic fields will be from a consistent snapshot of the Sui + If `rootVersion` is omitted, dynamic fields will be from a consistent snapshot of the Iota state at the latest checkpoint known to the GraphQL RPC. Similarly, `Owner.asObject` will return the object's version at the latest checkpoint. """ - owner(address: SuiAddress!, rootVersion: UInt53): Owner + owner(address: IotaAddress!, rootVersion: UInt53): Owner """ The object corresponding to the given address at the (optionally) given version. When no version is given, the latest version is returned. """ - object(address: SuiAddress!, version: UInt53): Object + object(address: IotaAddress!, version: UInt53): Object """ The package corresponding to the given address (at the optionally given version). @@ -3294,18 +3294,18 @@ type Query { Note that this interpretation of `version` is different from a historical object read (the interpretation of `version` for the `object` query). """ - package(address: SuiAddress!, version: UInt53): MovePackage + package(address: IotaAddress!, version: UInt53): MovePackage """ The latest version of the package at `address`. This corresponds to the package with the highest `version` that shares its original ID with the package at `address`. """ - latestPackage(address: SuiAddress!): MovePackage + latestPackage(address: IotaAddress!): MovePackage """ - Look-up an Account by its SuiAddress. + Look-up an Account by its IotaAddress. """ - address(address: SuiAddress!): Address + address(address: IotaAddress!): Address """ Fetch a structured representation of a concrete type, including its layout information. Fails if the type is malformed. @@ -3328,7 +3328,7 @@ type Query { The coin objects that exist in the network. The type field is a string of the inner type of the coin by which to filter (e.g. - `0x2::sui::SUI`). If no type is provided, it will default to `0x2::sui::SUI`. + `0x2::iota::IOTA`). If no type is provided, it will default to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! epochs(first: Int, after: String, last: Int, before: String): EpochConnection! @@ -3382,16 +3382,16 @@ type Query { optionally bounding the versions exclusively from below with `afterVersion`, or from above with `beforeVersion`. """ - packageVersions(first: Int, after: String, last: Int, before: String, address: SuiAddress!, filter: MovePackageVersionFilter): MovePackageConnection! + packageVersions(first: Int, after: String, last: Int, before: String, address: IotaAddress!, filter: MovePackageVersionFilter): MovePackageConnection! """ Fetch the protocol config by protocol version (defaults to the latest protocol version known to the GraphQL service). """ protocolConfig(protocolVersion: UInt53): ProtocolConfigs! """ - Resolves a SuiNS `domain` name to an address, if it has been bound. + Resolves a IotaNS `domain` name to an address, if it has been bound. """ - resolveSuinsAddress(domain: String!): Address + resolveIotaNSAddress(domain: String!): Address """ Fetch a package by its name (using dot move service) """ @@ -3418,7 +3418,7 @@ type Query { - `intentScope` is an enum that specifies the intent scope to be used to parse bytes. - `author` is the address of the signer of the transaction or personal msg. """ - verifyZkloginSignature(bytes: Base64!, signature: Base64!, intentScope: ZkLoginIntentScope!, author: SuiAddress!): ZkLoginVerifyResult! + verifyZkloginSignature(bytes: Base64!, signature: Base64!, intentScope: ZkLoginIntentScope!, author: IotaAddress!): ZkLoginVerifyResult! } type RandomnessStateCreateTransaction { @@ -3457,7 +3457,7 @@ type Receiving { """ ID of the object being read. """ - address: SuiAddress! + address: IotaAddress! """ Version of the object being read. """ @@ -3616,7 +3616,7 @@ type Shared { A Move object that's shared. """ type SharedInput { - address: SuiAddress! + address: IotaAddress! """ The version that this this object was shared at. """ @@ -3638,7 +3638,7 @@ type SharedObjectCancelled { """ ID of the shared object. """ - address: SuiAddress! + address: IotaAddress! """ The assigned shared object version. It is a special version indicating transaction cancellation reason. """ @@ -3653,7 +3653,7 @@ type SharedObjectDelete { """ ID of the shared object. """ - address: SuiAddress! + address: IotaAddress! """ The version of the shared object that was assigned to this transaction during by consensus, during sequencing. @@ -3673,7 +3673,7 @@ type SharedObjectRead { """ ID of the object being read. """ - address: SuiAddress! + address: IotaAddress! """ Version of the object being read. """ @@ -3727,7 +3727,7 @@ Parameters that control the distribution of the stake subsidy. """ type StakeSubsidy { """ - SUI set aside for stake subsidies -- reduces over time as stake subsidies are paid out over + IOTA set aside for stake subsidies -- reduces over time as stake subsidies are paid out over time. """ balance: BigInt @@ -3753,17 +3753,17 @@ type StakeSubsidy { } """ -Represents a `0x3::staking_pool::StakedSui` Move object on-chain. +Represents a `0x3::staking_pool::StakedIota` Move object on-chain. """ -type StakedSui implements IMoveObject & IObject & IOwner { - address: SuiAddress! +type StakedIota implements IMoveObject & IObject & IOwner { + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -3773,22 +3773,22 @@ type StakedSui implements IMoveObject & IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -3813,7 +3813,7 @@ type StakedSui implements IMoveObject & IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -3852,7 +3852,7 @@ type StakedSui implements IMoveObject & IObject & IOwner { contents: MoveValue """ Determines whether a transaction can transfer this object, using the TransferObjects - transaction command or `sui::transfer::public_transfer`, both of which require the object to + transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! @@ -3903,9 +3903,9 @@ type StakedSui implements IMoveObject & IObject & IOwner { """ The object id of the validator staking pool this stake belongs to. """ - poolId: SuiAddress + poolId: IotaAddress """ - The SUI that was initially staked. + The IOTA that was initially staked. """ principal: BigInt """ @@ -3923,7 +3923,7 @@ type StakedSui implements IMoveObject & IObject & IOwner { estimatedReward: BigInt } -type StakedSuiConnection { +type StakedIotaConnection { """ Information to aid in pagination. """ @@ -3931,21 +3931,21 @@ type StakedSuiConnection { """ A list of edges. """ - edges: [StakedSuiEdge!]! + edges: [StakedIotaEdge!]! """ A list of nodes. """ - nodes: [StakedSui!]! + nodes: [StakedIota!]! } """ An edge in a connection. """ -type StakedSuiEdge { +type StakedIotaEdge { """ The item at the end of the edge """ - node: StakedSui! + node: StakedIota! """ A cursor for use in pagination """ @@ -3953,7 +3953,7 @@ type StakedSuiEdge { } """ -SUI set aside to account for objects stored on-chain. +IOTA set aside to account for objects stored on-chain. """ type StorageFund { """ @@ -3972,19 +3972,19 @@ type StorageFund { """ -String containing 32B hex-encoded address, with a leading "0x". Leading zeroes can be omitted on input but will always appear in outputs (SuiAddress in output is guaranteed to be 66 characters long). +String containing 32B hex-encoded address, with a leading "0x". Leading zeroes can be omitted on input but will always appear in outputs (IotaAddress in output is guaranteed to be 66 characters long). """ -scalar SuiAddress +scalar IotaAddress -type SuinsRegistration implements IMoveObject & IObject & IOwner { - address: SuiAddress! +type IotaNSRegistration implements IMoveObject & IObject & IOwner { + address: IotaAddress! """ Objects owned by this object, optionally `filter`-ed. """ objects(first: Int, after: String, last: Int, before: String, filter: ObjectFilter): MoveObjectConnection! """ Total balance of all coins with marker type owned by this object. If type is not supplied, - it defaults to `0x2::sui::SUI`. + it defaults to `0x2::iota::IOTA`. """ balance(type: String): Balance """ @@ -3994,22 +3994,22 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { """ The coin objects for this object. - `type` is a filter on the coin's type parameter, defaulting to `0x2::sui::SUI`. + `type` is a filter on the coin's type parameter, defaulting to `0x2::iota::IOTA`. """ coins(first: Int, after: String, last: Int, before: String, type: String): CoinConnection! """ - The `0x3::staking_pool::StakedSui` objects owned by this object. + The `0x3::staking_pool::StakedIota` objects owned by this object. """ - stakedSuis(first: Int, after: String, last: Int, before: String): StakedSuiConnection! + stakedIotas(first: Int, after: String, last: Int, before: String): StakedIotaConnection! """ The domain explicitly configured as the default domain pointing to this object. """ - defaultSuinsName(format: DomainFormat): String + defaultIotaNSName(format: DomainFormat): String """ - The SuinsRegistration NFTs owned by this object. These grant the owner the capability to + The IotaNSRegistration NFTs owned by this object. These grant the owner the capability to manage the associated domain. """ - suinsRegistrations(first: Int, after: String, last: Int, before: String): SuinsRegistrationConnection! + iotansRegistrations(first: Int, after: String, last: Int, before: String): IotaNSRegistrationConnection! version: UInt53! """ The current status of the object as read from the off-chain store. The possible states are: @@ -4034,7 +4034,7 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { """ previousTransactionBlock: TransactionBlock """ - The amount of SUI we would rebate if this object gets deleted or mutated. This number is + The amount of IOTA we would rebate if this object gets deleted or mutated. This number is recalculated based on the present storage gas price. """ storageRebate: BigInt @@ -4073,7 +4073,7 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { contents: MoveValue """ Determines whether a transaction can transfer this object, using the TransferObjects - transaction command or `sui::transfer::public_transfer`, both of which require the object to + transaction command or `iota::transfer::public_transfer`, both of which require the object to have the `key` and `store` abilities. """ hasPublicTransfer: Boolean! @@ -4110,12 +4110,12 @@ type SuinsRegistration implements IMoveObject & IObject & IOwner { """ dynamicFields(first: Int, after: String, last: Int, before: String): DynamicFieldConnection! """ - Domain name of the SuinsRegistration object + Domain name of the IotaNSRegistration object """ domain: String! } -type SuinsRegistrationConnection { +type IotaNSRegistrationConnection { """ Information to aid in pagination. """ @@ -4123,21 +4123,21 @@ type SuinsRegistrationConnection { """ A list of edges. """ - edges: [SuinsRegistrationEdge!]! + edges: [IotaNSRegistrationEdge!]! """ A list of nodes. """ - nodes: [SuinsRegistration!]! + nodes: [IotaNSRegistration!]! } """ An edge in a connection. """ -type SuinsRegistrationEdge { +type IotaNSRegistrationEdge { """ The item at the end of the edge """ - node: SuinsRegistration! + node: IotaNSRegistration! """ A cursor for use in pagination """ @@ -4356,11 +4356,11 @@ input TransactionBlockFilter { Limit to transactions that interacted with the given address. The address could be a sender, sponsor, or recipient of the transaction. """ - affectedAddress: SuiAddress + affectedAddress: IotaAddress """ Limit to transactions that were sent by the given address. """ - sentAddress: SuiAddress + sentAddress: IotaAddress """ Limit to transactions that accepted the given object as an input. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to under behavior. @@ -4368,7 +4368,7 @@ input TransactionBlockFilter { This filter will be removed with 1.36.0 (2024-10-14), or at least one release after `affectedObject` is introduced, whichever is later. """ - inputObject: SuiAddress + inputObject: IotaAddress """ Limit to transactions that output a versioon of this object. NOTE: this input filter has been deprecated in favor of `affectedObject` which offers an easier to understand behavor. @@ -4376,7 +4376,7 @@ input TransactionBlockFilter { This filter will be removed with 1.36.0 (2024-10-14), or at least one release after `affectedObject` is introduced, whichever is later. """ - changedObject: SuiAddress + changedObject: IotaAddress """ Select transactions by their digest. """ @@ -4442,11 +4442,11 @@ gas price, `gasBudget` defaults to the max gas budget and `gasSponsor` defaults to the sender. """ input TransactionMetadata { - sender: SuiAddress + sender: IotaAddress gasPrice: UInt53 gasObjects: [ObjectRef!] gasBudget: UInt53 - gasSponsor: SuiAddress + gasSponsor: IotaAddress } """ @@ -4479,7 +4479,7 @@ type TypeOrigin { """ The storage ID of the package that first defined this type. """ - definingId: SuiAddress! + definingId: IotaAddress! } """ @@ -4536,11 +4536,11 @@ type UpgradeTransaction { """ IDs of the transitive dependencies of the package to be published. """ - dependencies: [SuiAddress!]! + dependencies: [IotaAddress!]! """ ID of the package being upgraded. """ - currentPackage: SuiAddress! + currentPackage: IotaAddress! """ The `UpgradeTicket` authorizing the upgrade. """ @@ -4590,16 +4590,16 @@ type Validator { """ The ID of this validator's `0x3::staking_pool::StakingPool`. """ - stakingPoolId: SuiAddress! + stakingPoolId: IotaAddress! """ The validator's current exchange object. The exchange rate is used to determine - the amount of SUI tokens that each past SUI staker can withdraw in the future. + the amount of IOTA tokens that each past IOTA staker can withdraw in the future. """ exchangeRates: MoveObject @deprecated(reason: "The exchange object is a wrapped object. Access its dynamic fields through the `exchangeRatesTable` query.") """ A wrapped object containing the validator's exchange rates. This is a table from epoch number to `PoolTokenExchangeRate` value. The exchange rate is used to determine the amount - of SUI tokens that each past SUI staker can withdraw in the future. + of IOTA tokens that each past IOTA staker can withdraw in the future. """ exchangeRatesTable: Owner """ @@ -4611,9 +4611,9 @@ type Validator { """ stakingPoolActivationEpoch: UInt53 """ - The total number of SUI tokens in this pool. + The total number of IOTA tokens in this pool. """ - stakingPoolSuiBalance: BigInt + stakingPoolIotaBalance: BigInt """ The epoch stake rewards will be added here at the end of each epoch. """ @@ -4629,7 +4629,7 @@ type Validator { """ Pending stake withdrawn during the current epoch, emptied at epoch boundaries. """ - pendingTotalSuiWithdraw: BigInt + pendingTotalIotaWithdraw: BigInt """ Pending pool token withdrawn during the current epoch, emptied at epoch boundaries. """ @@ -4647,7 +4647,7 @@ type Validator { """ commissionRate: Int """ - The total number of SUI tokens in this pool plus + The total number of IOTA tokens in this pool plus the pending stake amount for this epoch. """ nextEpochStake: BigInt @@ -4734,7 +4734,7 @@ type ValidatorSet { """ Object ID of the wrapped object `TableVec` storing the pending active validators. """ - pendingActiveValidatorsId: SuiAddress + pendingActiveValidatorsId: IotaAddress """ Size of the pending active validators table. """ @@ -4744,7 +4744,7 @@ type ValidatorSet { of the corresponding validators. This is needed because a validator's address can potentially change but the object ID of its pool will not. """ - stakingPoolMappingsId: SuiAddress + stakingPoolMappingsId: IotaAddress """ Size of the stake pool mappings `Table`. """ @@ -4752,7 +4752,7 @@ type ValidatorSet { """ Object ID of the `Table` storing the inactive staking pools. """ - inactivePoolsId: SuiAddress + inactivePoolsId: IotaAddress """ Size of the inactive pools `Table`. """ @@ -4760,7 +4760,7 @@ type ValidatorSet { """ Object ID of the `Table` storing the validator candidates. """ - validatorCandidatesId: SuiAddress + validatorCandidatesId: IotaAddress """ Size of the validator candidates `Table`. """ diff --git a/crates/sui-graphql-client-build/src/lib.rs b/crates/iota-graphql-client-build/src/lib.rs similarity index 76% rename from crates/sui-graphql-client-build/src/lib.rs rename to crates/iota-graphql-client-build/src/lib.rs index 272a7b01f..37ea3fac5 100644 --- a/crates/sui-graphql-client-build/src/lib.rs +++ b/crates/iota-graphql-client-build/src/lib.rs @@ -1,17 +1,19 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 #![doc = include_str!("../README.md")] -/// Register the schema to enable building custom queries using cynic derive macros queries. Call -/// this function in a `build.rs` file in your crate if you need to build custom queries. +/// Register the schema to enable building custom queries using cynic derive +/// macros queries. Call this function in a `build.rs` file in your crate if you +/// need to build custom queries. /// /// Examples /// ```rust,ignore /// // build.rs file /// fn main() { /// let schema_name = "MYSCHEMA" -/// sui_graphql_client_build::register_schema(schema_name); +/// iota_graphql_client_build::register_schema(schema_name); /// } /// /// // Cargo.toml @@ -20,12 +22,12 @@ /// cynic = "3.8.0" /// ... /// [build-dependencies] -/// sui_graphql_client_build = "VERSION_HERE" +/// iota_graphql_client_build = "VERSION_HERE" /// /// // lib.rs /// // Custom query /// use cynic::QueryBuilder; -/// use sui_graphql_client::{query_types::schema, Client}; +/// use iota_graphql_client::{query_types::schema, Client}; /// /// #[derive(cynic::QueryFragment, Debug)] /// #[cynic(schema = "MYSCHEMA", graphql_type = "Query")] diff --git a/crates/sui-graphql-client/CHANGELOG.md b/crates/iota-graphql-client/CHANGELOG.md similarity index 100% rename from crates/sui-graphql-client/CHANGELOG.md rename to crates/iota-graphql-client/CHANGELOG.md diff --git a/crates/sui-graphql-client/Cargo.toml b/crates/iota-graphql-client/Cargo.toml similarity index 51% rename from crates/sui-graphql-client/Cargo.toml rename to crates/iota-graphql-client/Cargo.toml index fd410b478..54429c26e 100644 --- a/crates/sui-graphql-client/Cargo.toml +++ b/crates/iota-graphql-client/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "sui-graphql-client" -version = "0.1.0" -authors = ["Stefan Stanciulescu ", "Brandon Williams "] -license = "Apache-2.0" +name = "iota-graphql-client" +version = "0.0.0" +authors = ["IOTA Foundation "] edition = "2021" +license = "Apache-2.0" publish = false readme = "README.md" -description = "Sui GraphQL RPC Client for the Sui Blockchain" +description = "GraphQL RPC Client for the IOTA Blockchain" [dependencies] anyhow = "1.0.71" @@ -17,19 +17,18 @@ bcs = "0.1.4" chrono = "0.4.26" cynic = "3.7.3" futures = "0.3.29" +iota-types = { package = "iota-sdk-types", path = "../iota-sdk-types", features = ["serde"] } reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } serde = { version = "1.0.144" } -serde_json = {version = "1.0.95"} -sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde"] } -tracing = "0.1.37" +serde_json = { version = "1.0.95" } tokio = "1.36.0" +tracing = "0.1.37" url = "2.5.3" [dev-dependencies] -sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "rand", "hash"] } +iota-types = { package = "iota-sdk-types", path = "../iota-sdk-types", features = ["serde", "rand", "hash"] } rand = "0.8.5" tokio = { version = "1.40.0", features = ["full"] } [build-dependencies] -sui_graphql_client_build = { package = "sui-graphql-client-build", path = "../sui-graphql-client-build" } - +iota_graphql_client_build = { package = "iota-graphql-client-build", path = "../iota-graphql-client-build" } diff --git a/crates/sui-graphql-client/README.md b/crates/iota-graphql-client/README.md similarity index 82% rename from crates/sui-graphql-client/README.md rename to crates/iota-graphql-client/README.md index 808e7467b..27f776fa5 100644 --- a/crates/sui-graphql-client/README.md +++ b/crates/iota-graphql-client/README.md @@ -1,10 +1,10 @@ -# sui-graphql-client +# iota-graphql-client -[![sui-graphql-client on crates.io](https://img.shields.io/crates/v/sui-graphql-client)](https://crates.io/crates/sui-graphql-client) -[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-graphql-client) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui-graphql-client/) +[![iota-graphql-client on crates.io](https://img.shields.io/crates/v/iota-graphql-client)](https://crates.io/crates/iota-graphql-client) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-graphql-client) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) -The Sui GraphQL client is a client for interacting with the Sui blockchain via GraphQL. +The IOTA GraphQL client is a client for interacting with the IOTA blockchain via GraphQL. It provides a set of APIs for querying the blockchain for information such as chain identifier, reference gas price, protocol configuration, service configuration, checkpoint, epoch, executing transactions and more. @@ -18,10 +18,11 @@ executing transactions and more. # Usage ## Connecting to a GraphQL server + Instantiate a client with [`Client::new(server: &str)`] or use one of the predefined functions for different networks [`Client`]. ```rust, no_run -use sui_graphql_client::Client; +use iota_graphql_client::Client; use anyhow::Result; #[tokio::main] @@ -37,19 +38,21 @@ async fn main() -> Result<()> { ``` ## Requesting gas from the faucet + The client provides an API to request gas from the faucet. The `request_and_wait` function sends a request to the faucet and waits until the transaction is confirmed. The function returns the transaction details if the request is successful. ### Example for standard devnet/testnet/local networks. + ```rust, no_run -use sui_graphql_client::faucet::FaucetClient; -use sui_types::Address; +use iota_graphql_client::faucet::FaucetClient; +use iota_types::Address; use anyhow::Result; use std::str::FromStr; #[tokio::main] async fn main() -> Result<()> { - let address = Address::from_str("SUI_ADDRESS_HERE")?; + let address = Address::from_str("IOTA_ADDRESS_HERE")?; // Request gas from the faucet and wait until a coin is received // As the client is set to devnet, faucet will use the devnet faucet. let faucet = FaucetClient::devnet().request_and_wait(address).await?; @@ -67,17 +70,19 @@ async fn main() -> Result<()> { ``` ### Example for custom faucet service. -Note that this [`FaucetClient`] is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.sui.io`). + +Note that this `FaucetClient` is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.iota.io`). + ```rust, no_run -use sui_graphql_client::faucet::FaucetClient; -use sui_types::Address; +use iota_graphql_client::faucet::FaucetClient; +use iota_types::Address; use anyhow::Result; use std::str::FromStr; #[tokio::main] async fn main() -> Result<()> { - let address = Address::from_str("SUI_ADDRESS_HERE")?; + let address = Address::from_str("IOTA_ADDRESS_HERE")?; // Request gas from the faucet and wait until a coin is received // As the client is set to devnet, faucet will use the devnet faucet. let faucet = FaucetClient::new("https://myfaucet_testnet.com").request_and_wait(address).await?; @@ -92,15 +97,19 @@ async fn main() -> Result<()> { ``` ## Custom Queries + There are several options for running custom queries. -1) Use a GraphQL client library of your choosing. -2) Use the [cynic's web generator](https://generator.cynic-rs.dev/) that accepts as input the schema and generates the query types. -3) Use the [cynic's CLI](https://github.com/obmarg/cynic/tree/main/cynic-cli) and use the `cynic querygen` command to generate the query types. + +1. Use a GraphQL client library of your choosing. +2. Use the [cynic's web generator](https://generator.cynic-rs.dev/) that accepts as input the schema and generates the query types. +3. Use the [cynic's CLI](https://github.com/obmarg/cynic/tree/main/cynic-cli) and use the `cynic querygen` command to generate the query types. Below is an example that uses the `cynic querygen` CLI to generate the query types from the schema and the following query: + ```bash cynic querygen --schema rpc.graphql --query custom_query.graphql ``` + where `custom_query.graphql` contains the following query: ```graphql @@ -114,12 +123,11 @@ query CustomQuery($id: UInt53) { } ``` -When using `cynic` and `sui-graphql-client`, you will need to register the schema by calling `sui-graphql-client-build::register_schema` in a `build.rs` file. See [sui-graphql-client-build](https://github.com/MystenLabs/sui-rust-sdk/tree/master/crates/sui-graphql-client-build) for more information. +When using `cynic` and `iota-graphql-client`, you will need to register the schema by calling `iota-graphql-client-build::register_schema` in a `build.rs` file. See [iota-graphql-client-build](https://github.com/iotaledger/iota-rust-sdk/tree/master/crates/iota-graphql-client-build) for more information. The generated query types are defined below. Note that the `id` variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the `id` variable is not provided, the query will return the data for the last known epoch. Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. - ```rust, ignore #[derive(cynic::QueryVariables, Debug)] pub struct CustomQueryVariables { @@ -147,15 +155,16 @@ pub struct BigInt(pub String); ``` The complete example is shown below: + ```rust, ignore use anyhow::Result; use cynic::QueryBuilder; -use sui_graphql_client::{ +use iota_graphql_client::{ query_types::{schema, BigInt}, Client, }; -use sui_types::Address; +use iota_types::Address; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] @@ -221,4 +230,3 @@ async fn main() -> Result<()> { Ok(()) } ``` - diff --git a/crates/iota-graphql-client/build.rs b/crates/iota-graphql-client/build.rs new file mode 100644 index 000000000..ee5fef8a1 --- /dev/null +++ b/crates/iota-graphql-client/build.rs @@ -0,0 +1,8 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +/// Register IOTA RPC schema for creating structs for queries +fn main() { + iota_graphql_client_build::register_schema("rpc"); +} diff --git a/crates/sui-graphql-client/examples/custom_query.rs b/crates/iota-graphql-client/examples/custom_query.rs similarity index 92% rename from crates/sui-graphql-client/examples/custom_query.rs rename to crates/iota-graphql-client/examples/custom_query.rs index 4c995205d..a65d0c6be 100644 --- a/crates/sui-graphql-client/examples/custom_query.rs +++ b/crates/iota-graphql-client/examples/custom_query.rs @@ -1,12 +1,13 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use anyhow::Result; use cynic::QueryBuilder; - -use sui_graphql_client::query_types::schema; -use sui_graphql_client::query_types::BigInt; -use sui_graphql_client::Client; +use iota_graphql_client::{ + Client, + query_types::{BigInt, schema}, +}; // The data returned by the custom query. #[derive(cynic::QueryFragment, Debug)] @@ -46,8 +47,8 @@ pub struct ChainIdQuery { async fn main() -> Result<()> { let client = Client::new_devnet(); - // Query the data for the last known epoch. Note that id variable is None, so last epoch data - // will be returned. + // Query the data for the last known epoch. Note that id variable is None, so + // last epoch data will be returned. let operation = CustomQuery::build(CustomVariables { id: None }); let response = client .run_query::(&operation) diff --git a/crates/sui-graphql-client/queries/coin_metadata.graphql b/crates/iota-graphql-client/queries/coin_metadata.graphql similarity index 100% rename from crates/sui-graphql-client/queries/coin_metadata.graphql rename to crates/iota-graphql-client/queries/coin_metadata.graphql diff --git a/crates/sui-graphql-client/queries/custom_query.graphql b/crates/iota-graphql-client/queries/custom_query.graphql similarity index 100% rename from crates/sui-graphql-client/queries/custom_query.graphql rename to crates/iota-graphql-client/queries/custom_query.graphql diff --git a/crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql b/crates/iota-graphql-client/queries/epoch_total_checkpoints.graphql similarity index 51% rename from crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql rename to crates/iota-graphql-client/queries/epoch_total_checkpoints.graphql index a60c1cb90..f277df03f 100644 --- a/crates/sui-graphql-client/queries/epoch_total_checkpoints.graphql +++ b/crates/iota-graphql-client/queries/epoch_total_checkpoints.graphql @@ -1,4 +1,4 @@ -query EpochTotalCheckpoints($id: UInt53){ +query EpochTotalCheckpoints($id: UInt53) { epoch(id: $id) { totalCheckpoints } diff --git a/crates/sui-graphql-client/queries/object.graphql b/crates/iota-graphql-client/queries/object.graphql similarity index 50% rename from crates/sui-graphql-client/queries/object.graphql rename to crates/iota-graphql-client/queries/object.graphql index 8daef0638..b00de0b74 100644 --- a/crates/sui-graphql-client/queries/object.graphql +++ b/crates/iota-graphql-client/queries/object.graphql @@ -1,6 +1,5 @@ -query ObjectQuery($address: SuiAddress!, $version: UInt53) { +query ObjectQuery($address: IotaAddress!, $version: UInt53) { object(address: $address, version: $version) { bcs } } - diff --git a/crates/iota-graphql-client/queries/objects.graphql b/crates/iota-graphql-client/queries/objects.graphql new file mode 100644 index 000000000..5614df43f --- /dev/null +++ b/crates/iota-graphql-client/queries/objects.graphql @@ -0,0 +1,25 @@ +query ObjectsQuery( + $after: String + $before: String + $filter: ObjectFilter + $first: Int + $last: Int +) { + objects( + after: $after + before: $before + filter: $filter + first: $first + last: $last + ) { + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + nodes { + bcs + } + } +} diff --git a/crates/sui-graphql-client/src/error.rs b/crates/iota-graphql-client/src/error.rs similarity index 92% rename from crates/sui-graphql-client/src/error.rs rename to crates/iota-graphql-client/src/error.rs index 2d04a84c2..834322b82 100644 --- a/crates/sui-graphql-client/src/error.rs +++ b/crates/iota-graphql-client/src/error.rs @@ -1,27 +1,26 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::num::ParseIntError; -use std::num::TryFromIntError; +use std::num::{ParseIntError, TryFromIntError}; use cynic::GraphQlError; - -use sui_types::AddressParseError; -use sui_types::DigestParseError; -use sui_types::TypeParseError; +use iota_types::{AddressParseError, DigestParseError, TypeParseError}; type BoxError = Box; pub type Result = std::result::Result; -/// General error type for the client. It is used to wrap all the possible errors that can occur. +/// General error type for the client. It is used to wrap all the possible +/// errors that can occur. #[derive(Debug)] pub struct Error { inner: Box, } -/// Error type for the client. It is split into multiple fields to allow for more granular error -/// handling. The `source` field is used to store the original error. +/// Error type for the client. It is split into multiple fields to allow for +/// more granular error handling. The `source` field is used to store the +/// original error. #[derive(Debug)] struct InnerError { /// Error kind. @@ -73,7 +72,8 @@ impl Error { } } - /// Special constructor for queries that expect to return data but it's none. + /// Special constructor for queries that expect to return data but it's + /// none. pub(crate) fn empty_response_error() -> Self { Self { inner: Box::new(InnerError { diff --git a/crates/sui-graphql-client/src/faucet.rs b/crates/iota-graphql-client/src/faucet.rs similarity index 85% rename from crates/sui-graphql-client/src/faucet.rs rename to crates/iota-graphql-client/src/faucet.rs index dd0b23962..beb12cc15 100644 --- a/crates/sui-graphql-client/src/faucet.rs +++ b/crates/iota-graphql-client/src/faucet.rs @@ -1,23 +1,18 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use sui_types::Address; -use sui_types::ObjectId; -use sui_types::TransactionDigest; +use std::time::Duration; -use anyhow::anyhow; -use anyhow::bail; -use reqwest::StatusCode; -use reqwest::Url; -use serde::Deserialize; -use serde::Serialize; +use anyhow::{anyhow, bail}; +use iota_types::{Address, ObjectId, TransactionDigest}; +use reqwest::{StatusCode, Url}; +use serde::{Deserialize, Serialize}; use serde_json::json; -use std::time::Duration; -use tracing::error; -use tracing::info; +use tracing::{error, info}; -pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.sui.io"; -pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.sui.io"; +pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.iota.io"; +pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.iota.io"; pub const FAUCET_LOCAL_HOST: &str = "http://localhost:9123"; const FAUCET_REQUEST_TIMEOUT: Duration = Duration::from_secs(120); @@ -74,9 +69,10 @@ pub struct CoinInfo { } impl FaucetClient { - /// Construct a new `FaucetClient` with the given faucet service URL. This [`FaucetClient`] - /// expects that the service provides two endpoints: /v1/gas and /v1/status. As such, do not - /// provide the request endpoint, just the top level service endpoint. + /// Construct a new `FaucetClient` with the given faucet service URL. This + /// [`FaucetClient`] expects that the service provides two endpoints: + /// /v1/gas and /v1/status. As such, do not provide the request + /// endpoint, just the top level service endpoint. /// /// - /v1/gas is used to request gas /// - /v1/status/taks-uuid is used to check the status of the request @@ -110,13 +106,15 @@ impl FaucetClient { } } - /// Request gas from the faucet. Note that this will return the UUID of the request and not - /// wait until the token is received. Use `request_and_wait` to wait for the token. + /// Request gas from the faucet. Note that this will return the UUID of the + /// request and not wait until the token is received. Use + /// `request_and_wait` to wait for the token. pub async fn request(&self, address: Address) -> Result, anyhow::Error> { self.request_impl(address).await } - /// Internal implementation of a faucet request. It returns the task Uuid as a String. + /// Internal implementation of a faucet request. It returns the task Uuid as + /// a String. async fn request_impl(&self, address: Address) -> Result, anyhow::Error> { let address = address.to_string(); let json_body = json![{ @@ -150,11 +148,15 @@ impl FaucetClient { } StatusCode::TOO_MANY_REQUESTS => { error!("Faucet service received too many requests from this IP address."); - bail!("Faucet service received too many requests from this IP address. Please try again after 60 minutes."); + bail!( + "Faucet service received too many requests from this IP address. Please try again after 60 minutes." + ); } StatusCode::SERVICE_UNAVAILABLE => { error!("Faucet service is currently overloaded or unavailable."); - bail!("Faucet service is currently overloaded or unavailable. Please try again later."); + bail!( + "Faucet service is currently overloaded or unavailable. Please try again later." + ); } status_code => { error!("Faucet request was unsuccessful: {status_code}"); @@ -163,12 +165,13 @@ impl FaucetClient { } } - /// Request gas from the faucet and wait until the request is completed and token is - /// transferred. Returns `FaucetReceipt` if the request is successful, which contains the list - /// of tokens transferred, and the transaction digest. + /// Request gas from the faucet and wait until the request is completed and + /// token is transferred. Returns `FaucetReceipt` if the request is + /// successful, which contains the list of tokens transferred, and the + /// transaction digest. /// - /// Note that the faucet is heavily rate-limited, so calling repeatedly the faucet would likely - /// result in a 429 code or 502 code. + /// Note that the faucet is heavily rate-limited, so calling repeatedly the + /// faucet would likely result in a 429 code or 502 code. pub async fn request_and_wait( &self, address: Address, diff --git a/crates/sui-graphql-client/src/lib.rs b/crates/iota-graphql-client/src/lib.rs similarity index 86% rename from crates/sui-graphql-client/src/lib.rs rename to crates/iota-graphql-client/src/lib.rs index d1c7c0f81..58d7a65a6 100644 --- a/crates/sui-graphql-client/src/lib.rs +++ b/crates/iota-graphql-client/src/lib.rs @@ -1,4 +1,5 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 #![doc = include_str!("../README.md")] @@ -8,113 +9,47 @@ pub mod faucet; pub mod query_types; pub mod streams; -use error::Error; -use query_types::ActiveValidatorsArgs; -use query_types::ActiveValidatorsQuery; -use query_types::BalanceArgs; -use query_types::BalanceQuery; -use query_types::ChainIdentifierQuery; -use query_types::CheckpointArgs; -use query_types::CheckpointId; -use query_types::CheckpointQuery; -use query_types::CheckpointsArgs; -use query_types::CheckpointsQuery; -use query_types::CoinMetadata; -use query_types::CoinMetadataArgs; -use query_types::CoinMetadataQuery; -use query_types::DefaultSuinsNameQuery; -use query_types::DefaultSuinsNameQueryArgs; -use query_types::DryRunArgs; -use query_types::DryRunQuery; -use query_types::DynamicFieldArgs; -use query_types::DynamicFieldConnectionArgs; -use query_types::DynamicFieldQuery; -use query_types::DynamicFieldsOwnerQuery; -use query_types::DynamicObjectFieldQuery; -use query_types::EpochSummaryArgs; -use query_types::EpochSummaryQuery; -use query_types::EventFilter; -use query_types::EventsQuery; -use query_types::EventsQueryArgs; -use query_types::ExecuteTransactionArgs; -use query_types::ExecuteTransactionQuery; -use query_types::LatestPackageQuery; -use query_types::MoveFunction; -use query_types::MoveModule; -use query_types::MovePackageVersionFilter; -use query_types::NormalizedMoveFunctionQuery; -use query_types::NormalizedMoveFunctionQueryArgs; -use query_types::NormalizedMoveModuleQuery; -use query_types::NormalizedMoveModuleQueryArgs; -use query_types::ObjectFilter; -use query_types::ObjectQuery; -use query_types::ObjectQueryArgs; -use query_types::ObjectsQuery; -use query_types::ObjectsQueryArgs; -use query_types::PackageArgs; -use query_types::PackageByNameArgs; -use query_types::PackageByNameQuery; -use query_types::PackageCheckpointFilter; -use query_types::PackageQuery; -use query_types::PackageVersionsArgs; -use query_types::PackageVersionsQuery; -use query_types::PackagesQuery; -use query_types::PackagesQueryArgs; -use query_types::PageInfo; -use query_types::ProtocolConfigQuery; -use query_types::ProtocolConfigs; -use query_types::ProtocolVersionArgs; -use query_types::ResolveSuinsQuery; -use query_types::ResolveSuinsQueryArgs; -use query_types::ServiceConfig; -use query_types::ServiceConfigQuery; -use query_types::TransactionBlockArgs; -use query_types::TransactionBlockEffectsQuery; -use query_types::TransactionBlockQuery; -use query_types::TransactionBlocksEffectsQuery; -use query_types::TransactionBlocksQuery; -use query_types::TransactionBlocksQueryArgs; -use query_types::TransactionMetadata; -use query_types::TransactionsFilter; -use query_types::Validator; -use streams::stream_paginated_query; - -use sui_types::framework::Coin; -use sui_types::Address; -use sui_types::CheckpointDigest; -use sui_types::CheckpointSequenceNumber; -use sui_types::CheckpointSummary; -use sui_types::Event; -use sui_types::MovePackage; -use sui_types::Object; -use sui_types::SignedTransaction; -use sui_types::Transaction; -use sui_types::TransactionDigest; -use sui_types::TransactionEffects; -use sui_types::TransactionKind; -use sui_types::TypeTag; -use sui_types::UserSignature; +use std::str::FromStr; use base64ct::Encoding; -use cynic::serde; -use cynic::GraphQlResponse; -use cynic::MutationBuilder; -use cynic::Operation; -use cynic::QueryBuilder; +use cynic::{GraphQlResponse, MutationBuilder, Operation, QueryBuilder, serde}; +use error::Error; use futures::Stream; +use iota_types::{ + Address, CheckpointDigest, CheckpointSequenceNumber, CheckpointSummary, Event, MovePackage, + Object, SignedTransaction, Transaction, TransactionDigest, TransactionEffects, TransactionKind, + TypeTag, UserSignature, framework::Coin, +}; +use query_types::{ + ActiveValidatorsArgs, ActiveValidatorsQuery, BalanceArgs, BalanceQuery, ChainIdentifierQuery, + CheckpointArgs, CheckpointId, CheckpointQuery, CheckpointsArgs, CheckpointsQuery, CoinMetadata, + CoinMetadataArgs, CoinMetadataQuery, DryRunArgs, DryRunQuery, DynamicFieldArgs, + DynamicFieldConnectionArgs, DynamicFieldQuery, DynamicFieldsOwnerQuery, + DynamicObjectFieldQuery, EpochSummaryArgs, EpochSummaryQuery, EventFilter, EventsQuery, + EventsQueryArgs, ExecuteTransactionArgs, ExecuteTransactionQuery, LatestPackageQuery, + MoveFunction, MoveModule, MovePackageVersionFilter, NormalizedMoveFunctionQuery, + NormalizedMoveFunctionQueryArgs, NormalizedMoveModuleQuery, NormalizedMoveModuleQueryArgs, + ObjectFilter, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, PackageArgs, + PackageByNameArgs, PackageByNameQuery, PackageCheckpointFilter, PackageQuery, + PackageVersionsArgs, PackageVersionsQuery, PackagesQuery, PackagesQueryArgs, PageInfo, + ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs, ServiceConfig, ServiceConfigQuery, + TransactionBlockArgs, TransactionBlockEffectsQuery, TransactionBlockQuery, + TransactionBlocksEffectsQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, + TransactionMetadata, TransactionsFilter, Validator, +}; use reqwest::Url; -use serde::de::DeserializeOwned; -use serde::Serialize; -use std::str::FromStr; +use serde::{Serialize, de::DeserializeOwned}; +use streams::stream_paginated_query; -use crate::error::Kind; -use crate::error::Result; -use crate::query_types::CheckpointTotalTxQuery; +use crate::{ + error::{Kind, Result}, + query_types::CheckpointTotalTxQuery, +}; const DEFAULT_ITEMS_PER_PAGE: i32 = 10; -const MAINNET_HOST: &str = "https://sui-mainnet.mystenlabs.com/graphql"; -const TESTNET_HOST: &str = "https://sui-testnet.mystenlabs.com/graphql"; -const DEVNET_HOST: &str = "https://sui-devnet.mystenlabs.com/graphql"; +const MAINNET_HOST: &str = "https://graphql.mainnet.iota.cafe"; +const TESTNET_HOST: &str = "https://graphql.testnet.iota.cafe"; +const DEVNET_HOST: &str = "https://graphql.devnet.iota.cafe"; const LOCAL_HOST: &str = "http://localhost:9125/graphql"; static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); @@ -122,15 +57,16 @@ static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_V // Output Types // =========================================================================== -/// The result of a dry run, which includes the effects of the transaction and any errors that may -/// have occurred. +/// The result of a dry run, which includes the effects of the transaction and +/// any errors that may have occurred. #[derive(Debug)] pub struct DryRunResult { pub effects: Option, pub error: Option, } -/// The name part of a dynamic field, including its type, bcs, and json representation. +/// The name part of a dynamic field, including its type, bcs, and json +/// representation. #[derive(Clone, Debug)] pub struct DynamicFieldName { /// The type name of this dynamic field name @@ -141,8 +77,8 @@ pub struct DynamicFieldName { pub json: Option, } -/// The output of a dynamic field query, that includes the name, value, and value's json -/// representation. +/// The output of a dynamic field query, that includes the name, value, and +/// value's json representation. #[derive(Clone, Debug)] pub struct DynamicFieldOutput { /// The name of the dynamic field @@ -153,8 +89,8 @@ pub struct DynamicFieldOutput { pub value_as_json: Option, } -/// Helper struct for passing a value that has a type that implements Serialize, for the dynamic -/// fields API. +/// Helper struct for passing a value that has a type that implements Serialize, +/// for the dynamic fields API. pub struct NameValue(Vec); /// Helper struct for passing a raw bcs value. pub struct BcsName(pub Vec); @@ -162,7 +98,8 @@ pub struct BcsName(pub Vec); #[derive(Clone, Debug)] /// A page of items returned by the GraphQL server. pub struct Page { - /// Information about the page, such as the cursor and whether there are more pages. + /// Information about the page, such as the cursor and whether there are + /// more pages. page_info: PageInfo, /// The data returned by the server. data: Vec, @@ -179,7 +116,8 @@ impl Page { &self.data } - /// Internal function to create a new page with the provided data and page information. + /// Internal function to create a new page with the provided data and page + /// information. fn new(page_info: PageInfo, data: Vec) -> Self { Self { page_info, data } } @@ -207,16 +145,16 @@ pub enum Direction { Backward, } -/// Pagination options for querying the GraphQL server. It defaults to forward pagination with the -/// GraphQL server's max page size. +/// Pagination options for querying the GraphQL server. It defaults to forward +/// pagination with the GraphQL server's max page size. #[derive(Clone, Debug, Default)] pub struct PaginationFilter { /// The direction of pagination. pub direction: Direction, /// An opaque cursor used for pagination. pub cursor: Option, - /// The maximum number of items to return. If this is ommitted, it will lazily query the - /// service configuration for the max page size. + /// The maximum number of items to return. If this is ommitted, it will + /// lazily query the service configuration for the max page size. pub limit: Option, } @@ -264,7 +202,7 @@ impl DynamicFieldOutput { } } -/// The GraphQL client for interacting with the Sui blockchain. +/// The GraphQL client for interacting with the IOTA blockchain. /// By default, it uses the `reqwest` crate as the HTTP client. pub struct Client { /// The URL of the GraphQL server. @@ -292,17 +230,20 @@ impl Client { Ok(client) } - /// Create a new GraphQL client connected to the `mainnet` GraphQL server: {MAINNET_HOST}. + /// Create a new GraphQL client connected to the `mainnet` GraphQL server: + /// {MAINNET_HOST}. pub fn new_mainnet() -> Self { Self::new(MAINNET_HOST).expect("Invalid mainnet URL") } - /// Create a new GraphQL client connected to the `testnet` GraphQL server: {TESTNET_HOST}. + /// Create a new GraphQL client connected to the `testnet` GraphQL server: + /// {TESTNET_HOST}. pub fn new_testnet() -> Self { Self::new(TESTNET_HOST).expect("Invalid testnet URL") } - /// Create a new GraphQL client connected to the `devnet` GraphQL server: {DEVNET_HOST}. + /// Create a new GraphQL client connected to the `devnet` GraphQL server: + /// {DEVNET_HOST}. pub fn new_devnet() -> Self { Self::new(DEVNET_HOST).expect("Invalid devnet URL") } @@ -313,8 +254,8 @@ impl Client { Self::new(LOCAL_HOST).expect("Invalid localhost URL") } - /// Set the server address for the GraphQL GraphQL client. It should be a valid URL with a host and - /// optionally a port number. + /// Set the server address for the GraphQL GraphQL client. It should be a + /// valid URL with a host and optionally a port number. pub fn set_rpc_server(&mut self, server: &str) -> Result<()> { let rpc = reqwest::Url::parse(server)?; self.rpc = rpc; @@ -326,8 +267,9 @@ impl Client { self.rpc.as_str() } - /// Internal function to handle pagination filters and return the appropriate values. - /// If limit is omitted, it will use the max page size from the service config. + /// Internal function to handle pagination filters and return the + /// appropriate values. If limit is omitted, it will use the max page + /// size from the service config. async fn pagination_filter( &self, pagination_filter: PaginationFilter, @@ -349,8 +291,8 @@ impl Client { } /// Run a query on the GraphQL server and return the response. - /// This method returns [`cynic::GraphQlResponse`] over the query type `T`, and it is - /// intended to be used with custom queries. + /// This method returns [`cynic::GraphQlResponse`] over the query type `T`, + /// and it is intended to be used with custom queries. pub async fn run_query(&self, operation: &Operation) -> Result> where T: serde::de::DeserializeOwned, @@ -386,11 +328,11 @@ impl Client { .ok_or_else(Error::empty_response_error) } - /// Get the reference gas price for the provided epoch or the last known one if no epoch is - /// provided. + /// Get the reference gas price for the provided epoch or the last known one + /// if no epoch is provided. /// - /// This will return `Ok(None)` if the epoch requested is not available in the GraphQL service - /// (e.g., due to pruning). + /// This will return `Ok(None)` if the epoch requested is not available in + /// the GraphQL service (e.g., due to pruning). pub async fn reference_gas_price(&self, epoch: Option) -> Result> { let operation = EpochSummaryQuery::build(EpochSummaryArgs { id: epoch }); let response = self.run_query(&operation).await?; @@ -414,8 +356,8 @@ impl Client { Ok(response.data.map(|p| p.protocol_config)) } - /// Get the GraphQL service configuration, including complexity limits, read and mutation limits, - /// supported versions, and others. + /// Get the GraphQL service configuration, including complexity limits, read + /// and mutation limits, supported versions, and others. pub async fn service_config(&self) -> Result<&ServiceConfig> { // If the value is already initialized, return it if let Some(service_config) = self.service_config.get() { @@ -442,9 +384,10 @@ impl Client { Ok(service_config) } - /// Get the list of active validators for the provided epoch, including related metadata. - /// If no epoch is provided, it will return the active validators for the current epoch. - pub async fn active_validators<'a>( + /// Get the list of active validators for the provided epoch, including + /// related metadata. If no epoch is provided, it will return the active + /// validators for the current epoch. + pub async fn active_validators( &self, epoch: Option, pagination_filter: PaginationFilter, @@ -481,8 +424,8 @@ impl Client { } } - /// The total number of transaction blocks in the network by the end of the provided - /// checkpoint digest. + /// The total number of transaction blocks in the network by the end of the + /// provided checkpoint digest. pub async fn total_transaction_blocks_by_digest( &self, digest: CheckpointDigest, @@ -491,21 +434,21 @@ impl Client { .await } - /// The total number of transaction blocks in the network by the end of the provided checkpoint - /// sequence number. + /// The total number of transaction blocks in the network by the end of the + /// provided checkpoint sequence number. pub async fn total_transaction_blocks_by_seq_num(&self, seq_num: u64) -> Result> { self.internal_total_transaction_blocks(None, Some(seq_num)) .await } - /// The total number of transaction blocks in the network by the end of the last known - /// checkpoint. + /// The total number of transaction blocks in the network by the end of the + /// last known checkpoint. pub async fn total_transaction_blocks(&self) -> Result> { self.internal_total_transaction_blocks(None, None).await } - /// Internal function to get the total number of transaction blocks based on the provided - /// checkpoint digest or sequence number. + /// Internal function to get the total number of transaction blocks based on + /// the provided checkpoint digest or sequence number. async fn internal_total_transaction_blocks( &self, digest: Option, @@ -540,8 +483,9 @@ impl Client { // Balance API // =========================================================================== - /// Get the balance of all the coins owned by address for the provided coin type. - /// Coin type will default to `0x2::coin::Coin<0x2::sui::SUI>` if not provided. + /// Get the balance of all the coins owned by address for the provided coin + /// type. Coin type will default to `0x2::coin::Coin<0x2::iota::IOTA>` + /// if not provided. pub async fn balance(&self, address: Address, coin_type: Option<&str>) -> Result> { let operation = BalanceQuery::build(BalanceArgs { address, @@ -569,9 +513,10 @@ impl Client { /// Get the list of coins for the specified address. /// - /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all - /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. - pub async fn coins<'a>( + /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, + /// which will return all coins. For IOTA coin, pass in the coin type: + /// `0x2::coin::Coin<0x2::iota::IOTA>`. + pub async fn coins( &self, owner: Address, coin_type: Option<&str>, @@ -602,8 +547,9 @@ impl Client { /// Get the list of coins for the specified address as a stream. /// - /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, which will return all - /// coins. For SUI coin, pass in the coin type: `0x2::coin::Coin<0x2::sui::SUI>`. + /// If `coin_type` is not provided, it will default to `0x2::coin::Coin`, + /// which will return all coins. For IOTA coin, pass in the coin type: + /// `0x2::coin::Coin<0x2::iota::IOTA>`. pub async fn coins_stream( &self, address: Address, @@ -642,8 +588,9 @@ impl Client { // Checkpoints API // =========================================================================== - /// Get the [`CheckpointSummary`] for a given checkpoint digest or checkpoint id. If none is - /// provided, it will use the last known checkpoint id. + /// Get the [`CheckpointSummary`] for a given checkpoint digest or + /// checkpoint id. If none is provided, it will use the last known + /// checkpoint id. pub async fn checkpoint( &self, digest: Option, @@ -675,7 +622,7 @@ impl Client { } /// Get a page of [`CheckpointSummary`] for the provided parameters. - pub async fn checkpoints<'a>( + pub async fn checkpoints( &self, pagination_filter: PaginationFilter, ) -> Result> { @@ -708,8 +655,8 @@ impl Client { } } - /// Get a stream of [`CheckpointSummary`]. Note that this will fetch all checkpoints which may - /// trigger a lot of requests. + /// Get a stream of [`CheckpointSummary`]. Note that this will fetch all + /// checkpoints which may trigger a lot of requests. pub async fn checkpoints_stream( &self, streaming_direction: Direction, @@ -717,7 +664,8 @@ impl Client { stream_paginated_query(move |filter| self.checkpoints(filter), streaming_direction) } - /// Return the sequence number of the latest checkpoint that has been executed. + /// Return the sequence number of the latest checkpoint that has been + /// executed. pub async fn latest_checkpoint_sequence_number( &self, ) -> Result> { @@ -731,19 +679,20 @@ impl Client { // Dynamic Field(s) API // =========================================================================== - /// Access a dynamic field on an object using its name. Names are arbitrary Move values whose - /// type have copy, drop, and store, and are specified using their type, and their BCS - /// contents, Base64 encoded. + /// Access a dynamic field on an object using its name. Names are arbitrary + /// Move values whose type have copy, drop, and store, and are specified + /// using their type, and their BCS contents, Base64 encoded. /// - /// The `name` argument can be either a [`BcsName`] for passing raw bcs bytes or a type that - /// implements Serialize. + /// The `name` argument can be either a [`BcsName`] for passing raw bcs + /// bytes or a type that implements Serialize. /// - /// This returns [`DynamicFieldOutput`] which contains the name, the value as json, and object. + /// This returns [`DynamicFieldOutput`] which contains the name, the value + /// as json, and object. /// /// # Example /// ```rust,ignore - /// - /// let client = sui_graphql_client::Client::new_devnet(); + /// + /// let client = iota_graphql_client::Client::new_devnet(); /// let address = Address::from_str("0x5").unwrap(); /// let df = client.dynamic_field_with_name(address, "u64", 2u64).await.unwrap(); /// @@ -782,14 +731,15 @@ impl Client { Ok(result) } - /// Access a dynamic object field on an object using its name. Names are arbitrary Move values whose - /// type have copy, drop, and store, and are specified using their type, and their BCS - /// contents, Base64 encoded. + /// Access a dynamic object field on an object using its name. Names are + /// arbitrary Move values whose type have copy, drop, and store, and are + /// specified using their type, and their BCS contents, Base64 encoded. /// - /// The `name` argument can be either a [`BcsName`] for passing raw bcs bytes or a type that - /// implements Serialize. + /// The `name` argument can be either a [`BcsName`] for passing raw bcs + /// bytes or a type that implements Serialize. /// - /// This returns [`DynamicFieldOutput`] which contains the name, the value as json, and object. + /// This returns [`DynamicFieldOutput`] which contains the name, the value + /// as json, and object. pub async fn dynamic_object_field( &self, address: Address, @@ -820,11 +770,11 @@ impl Client { Ok(result) } - /// Get a page of dynamic fields for the provided address. Note that this will also fetch - /// dynamic fields on wrapped objects. + /// Get a page of dynamic fields for the provided address. Note that this + /// will also fetch dynamic fields on wrapped objects. /// /// This returns [`Page`] of [`DynamicFieldOutput`]s. - pub async fn dynamic_fields<'a>( + pub async fn dynamic_fields( &self, address: Address, pagination_filter: PaginationFilter, @@ -857,8 +807,8 @@ impl Client { )) } - /// Get a stream of dynamic fields for the provided address. Note that this will also fetch - /// dynamic fields on wrapped objects. + /// Get a stream of dynamic fields for the provided address. Note that this + /// will also fetch dynamic fields on wrapped objects. pub async fn dynamic_fields_stream( &self, address: Address, @@ -874,8 +824,9 @@ impl Client { // Epoch API // =========================================================================== - /// Return the number of checkpoints in this epoch. This will return `Ok(None)` if the epoch - /// requested is not available in the GraphQL service (e.g., due to pruning). + /// Return the number of checkpoints in this epoch. This will return + /// `Ok(None)` if the epoch requested is not available in the GraphQL + /// service (e.g., due to pruning). pub async fn epoch_total_checkpoints(&self, epoch: Option) -> Result> { let response = self.epoch_summary(epoch).await?; @@ -889,8 +840,9 @@ impl Client { .and_then(|e| e.total_checkpoints)) } - /// Return the number of transaction blocks in this epoch. This will return `Ok(None)` if the - /// epoch requested is not available in the GraphQL service (e.g., due to pruning). + /// Return the number of transaction blocks in this epoch. This will return + /// `Ok(None)` if the epoch requested is not available in the GraphQL + /// service (e.g., due to pruning). pub async fn epoch_total_transaction_blocks(&self, epoch: Option) -> Result> { let response = self.epoch_summary(epoch).await?; @@ -904,8 +856,8 @@ impl Client { .and_then(|e| e.total_transactions)) } - /// Internal method for getting the epoch summary that is called in a few other APIs for - /// convenience. + /// Internal method for getting the epoch summary that is called in a few + /// other APIs for convenience. async fn epoch_summary( &self, epoch: Option, @@ -918,7 +870,8 @@ impl Client { // Events API // =========================================================================== - /// Return a page of tuple (event, transaction digest) based on the (optional) event filter. + /// Return a page of tuple (event, transaction digest) based on the + /// (optional) event filter. pub async fn events( &self, filter: Option, @@ -992,8 +945,9 @@ impl Client { /// Return an object based on the provided [`Address`]. /// - /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. - /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + /// If the object does not exist (e.g., due to pruning), this will return + /// `Ok(None)`. Similarly, if this is not an object but an address, it + /// will return `Ok(None)`. pub async fn object(&self, address: Address, version: Option) -> Result> { let operation = ObjectQuery::build(ObjectQueryArgs { address, version }); @@ -1011,7 +965,7 @@ impl Client { .transpose()?; let object = bcs - .map(|b| bcs::from_bytes::(&b)) + .map(|b| bcs::from_bytes::(&b)) .transpose()?; Ok(object) @@ -1022,8 +976,8 @@ impl Client { /// Return a page of objects based on the provided parameters. /// - /// Use this function together with the [`ObjectFilter::owner`] to get the objects owned by an - /// address. + /// Use this function together with the [`ObjectFilter::owner`] to get the + /// objects owned by an address. /// /// # Example /// @@ -1070,7 +1024,7 @@ impl Client { .collect::, base64ct::Error>>()?; let objects = bcs .iter() - .map(|b| bcs::from_bytes::(b)) + .map(|b| bcs::from_bytes::(b)) .collect::, bcs::Error>>()?; Ok(Page::new(page_info, objects)) @@ -1091,7 +1045,8 @@ impl Client { ) } - /// Return the object's bcs content [`Vec`] based on the provided [`Address`]. + /// Return the object's bcs content [`Vec`] based on the provided + /// [`Address`]. pub async fn object_bcs(&self, object_id: Address) -> Result>> { let operation = ObjectQuery::build(ObjectQueryArgs { address: object_id, @@ -1116,8 +1071,9 @@ impl Client { /// Return the contents' JSON of an object that is a Move object. /// - /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. - /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + /// If the object does not exist (e.g., due to pruning), this will return + /// `Ok(None)`. Similarly, if this is not an object but an address, it + /// will return `Ok(None)`. pub async fn move_object_contents( &self, address: Address, @@ -1143,8 +1099,9 @@ impl Client { } /// Return the BCS of an object that is a Move object. /// - /// If the object does not exist (e.g., due to pruning), this will return `Ok(None)`. - /// Similarly, if this is not an object but an address, it will return `Ok(None)`. + /// If the object does not exist (e.g., due to pruning), this will return + /// `Ok(None)`. Similarly, if this is not an object but an address, it + /// will return `Ok(None)`. pub async fn move_object_contents_bcs( &self, address: Address, @@ -1174,15 +1131,17 @@ impl Client { // Package API // =========================================================================== - /// The package corresponding to the given address (at the optionally given version). - /// When no version is given, the package is loaded directly from the address given. Otherwise, - /// the address is translated before loading to point to the package whose original ID matches - /// the package at address, but whose version is version. For non-system packages, this - /// might result in a different address than address because different versions of a package, - /// introduced by upgrades, exist at distinct addresses. + /// The package corresponding to the given address (at the optionally given + /// version). When no version is given, the package is loaded directly + /// from the address given. Otherwise, the address is translated before + /// loading to point to the package whose original ID matches + /// the package at address, but whose version is version. For non-system + /// packages, this might result in a different address than address + /// because different versions of a package, introduced by upgrades, + /// exist at distinct addresses. /// - /// Note that this interpretation of version is different from a historical object read (the - /// interpretation of version for the object query). + /// Note that this interpretation of version is different from a historical + /// object read (the interpretation of version for the object query). pub async fn package( &self, address: Address, @@ -1206,9 +1165,9 @@ impl Client { .transpose()?) } - /// Fetch all versions of package at address (packages that share this package's original ID), - /// optionally bounding the versions exclusively from below with afterVersion, or from above - /// with beforeVersion. + /// Fetch all versions of package at address (packages that share this + /// package's original ID), optionally bounding the versions exclusively + /// from below with afterVersion, or from above with beforeVersion. pub async fn package_versions( &self, address: Address, @@ -1259,8 +1218,8 @@ impl Client { } /// Fetch the latest version of the package at address. - /// This corresponds to the package with the highest version that shares its original ID with - /// the package at address. + /// This corresponds to the package with the highest version that shares its + /// original ID with the package at address. pub async fn package_latest(&self, address: Address) -> Result> { let operation = LatestPackageQuery::build(PackageArgs { address, @@ -1304,11 +1263,13 @@ impl Client { .and_then(|bcs| bcs::from_bytes::(&bcs).ok())) } - /// The Move packages that exist in the network, optionally filtered to be strictly before - /// beforeCheckpoint and/or strictly after afterCheckpoint. + /// The Move packages that exist in the network, optionally filtered to be + /// strictly before beforeCheckpoint and/or strictly after + /// afterCheckpoint. /// - /// This query returns all versions of a given user package that appear between the specified - /// checkpoints, but only records the latest versions of system packages. + /// This query returns all versions of a given user package that appear + /// between the specified checkpoints, but only records the latest + /// versions of system packages. pub async fn packages( &self, pagination_filter: PaginationFilter, @@ -1361,11 +1322,13 @@ impl Client { // Dry Run API // =========================================================================== - /// Dry run a [`Transaction`] and return the transaction effects and dry run error (if any). + /// Dry run a [`Transaction`] and return the transaction effects and dry run + /// error (if any). /// - /// `skipChecks` optional flag disables the usual verification checks that prevent access to - /// objects that are owned by addresses other than the sender, and calling non-public, - /// non-entry functions, and some other checks. Defaults to false. + /// `skipChecks` optional flag disables the usual verification checks that + /// prevent access to objects that are owned by addresses other than the + /// sender, and calling non-public, non-entry functions, and some other + /// checks. Defaults to false. pub async fn dry_run_tx( &self, tx: &Transaction, @@ -1375,11 +1338,13 @@ impl Client { self.dry_run(tx_bytes, skip_checks, None).await } - /// Dry run a [`TransactionKind`] and return the transaction effects and dry run error (if any). + /// Dry run a [`TransactionKind`] and return the transaction effects and dry + /// run error (if any). /// - /// `skipChecks` optional flag disables the usual verification checks that prevent access to - /// objects that are owned by addresses other than the sender, and calling non-public, - /// non-entry functions, and some other checks. Defaults to false. + /// `skipChecks` optional flag disables the usual verification checks that + /// prevent access to objects that are owned by addresses other than the + /// sender, and calling non-public, non-entry functions, and some other + /// checks. Defaults to false. /// /// `tx_meta` is the transaction metadata. pub async fn dry_run_tx_kind( @@ -1475,9 +1440,9 @@ impl Client { } /// Get a page of transactions based on the provided filters. - pub async fn transactions<'a>( + pub async fn transactions( &self, - filter: Option>, + filter: Option>, pagination_filter: PaginationFilter, ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; @@ -1513,9 +1478,9 @@ impl Client { } /// Get a page of transactions' effects based on the provided filters. - pub async fn transactions_effects<'a>( + pub async fn transactions_effects( &self, - filter: Option>, + filter: Option>, pagination_filter: PaginationFilter, ) -> Result> { let (after, before, first, last) = self.pagination_filter(pagination_filter).await; @@ -1558,7 +1523,8 @@ impl Client { ) } - /// Get a stream of transactions' effects based on the (optional) transaction filter. + /// Get a stream of transactions' effects based on the (optional) + /// transaction filter. pub async fn transactions_effects_stream<'a>( &'a self, filter: Option>, @@ -1601,7 +1567,8 @@ impl Client { // =========================================================================== // Normalized Move Package API // =========================================================================== - /// Return the normalized Move function data for the provided package, module, and function. + /// Return the normalized Move function data for the provided package, + /// module, and function. pub async fn normalized_move_function( &self, package: &str, @@ -1679,61 +1646,22 @@ impl Client { Ok(response.data.and_then(|p| p.package).and_then(|p| p.module)) } - - // =========================================================================== - // SuiNS - // =========================================================================== - - /// Get the address for the provided Suins domain name. - pub async fn resolve_suins_to_address(&self, domain: &str) -> Result> { - let operation = ResolveSuinsQuery::build(ResolveSuinsQueryArgs { name: domain }); - - let response = self.run_query(&operation).await?; - - if let Some(errors) = response.errors { - return Err(Error::graphql_error(errors)); - } - Ok(response - .data - .and_then(|d| d.resolve_suins_address) - .map(|a| a.address)) - } - - /// Get the default Suins domain name for the provided address. - pub async fn default_suins_name(&self, address: Address) -> Result> { - let operation = DefaultSuinsNameQuery::build(DefaultSuinsNameQueryArgs { address }); - - let response = self.run_query(&operation).await?; - - if let Some(errors) = response.errors { - return Err(Error::graphql_error(errors)); - } - Ok(response - .data - .and_then(|d| d.address) - .and_then(|a| a.default_suins_name)) - } } -// This function is used in tests to create a new client instance for the local server. +// This function is used in tests to create a new client instance for the local +// server. #[cfg(test)] mod tests { use base64ct::Encoding; use futures::StreamExt; - use sui_types::Ed25519PublicKey; - use sui_types::TypeTag; - - use crate::faucet::FaucetClient; - use crate::BcsName; - use crate::Client; - use crate::Direction; - use crate::PaginationFilter; - use crate::DEVNET_HOST; - use crate::LOCAL_HOST; - use crate::MAINNET_HOST; - use crate::TESTNET_HOST; + use iota_types::{Ed25519PublicKey, TypeTag}; use tokio::time; + use crate::{ + BcsName, Client, DEVNET_HOST, Direction, LOCAL_HOST, MAINNET_HOST, PaginationFilter, + TESTNET_HOST, faucet::FaucetClient, + }; + const NUM_COINS_FROM_FAUCET: usize = 5; fn test_client() -> Client { @@ -1846,7 +1774,7 @@ mod tests { #[tokio::test] async fn test_coin_metadata_query() { let client = test_client(); - let cm = client.coin_metadata("0x2::sui::SUI").await; + let cm = client.coin_metadata("0x2::iota::IOTA").await; assert!( cm.is_ok(), "Coin metadata query failed for {} network", @@ -2076,7 +2004,7 @@ mod tests { #[tokio::test] async fn test_total_supply() { let client = test_client(); - let ts = client.total_supply("0x2::sui::SUI").await; + let ts = client.total_supply("0x2::iota::IOTA").await; assert!( ts.is_ok(), "Total supply query failed for {} network. Error: {}", @@ -2200,7 +2128,7 @@ mod tests { #[ignore] // don't know which name is not malformed async fn test_package_by_name() { let client = Client::new_testnet(); - let package = client.package_by_name("sui@sui").await; + let package = client.package_by_name("iota@iota").await; assert!(package.is_ok()); } diff --git a/crates/sui-graphql-client/src/query_types/active_validators.rs b/crates/iota-graphql-client/src/query_types/active_validators.rs similarity index 86% rename from crates/sui-graphql-client/src/query_types/active_validators.rs rename to crates/iota-graphql-client/src/query_types/active_validators.rs index 4842f7c2a..97780e420 100644 --- a/crates/sui-graphql-client/src/query_types/active_validators.rs +++ b/crates/iota-graphql-client/src/query_types/active_validators.rs @@ -1,13 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::Base64; -use crate::query_types::BigInt; -use crate::query_types::GQLAddress; -use crate::query_types::MoveObject; -use crate::query_types::PageInfo; +use crate::query_types::{Address, Base64, BigInt, GQLAddress, MoveObject, PageInfo, schema}; #[derive(cynic::QueryFragment, Debug)] #[cynic( @@ -86,19 +81,22 @@ pub struct Validator { pub next_epoch_credentials: Option, /// The validator's gas price quote for the next epoch. pub next_epoch_gas_price: Option, - /// The total number of SUI tokens in this pool plus + /// The total number of IOTA tokens in this pool plus /// the pending stake amount for this epoch. pub next_epoch_stake: Option, /// The validator's current valid `Cap` object. Validators can delegate - /// the operation ability to another address. The address holding this `Cap` object - /// can then update the reference gas price and tallying rule on behalf of the validator. + /// the operation ability to another address. The address holding this `Cap` + /// object can then update the reference gas price and tallying rule on + /// behalf of the validator. pub operation_cap: Option, - /// Pending pool token withdrawn during the current epoch, emptied at epoch boundaries. + /// Pending pool token withdrawn during the current epoch, emptied at epoch + /// boundaries. pub pending_pool_token_withdraw: Option, /// Pending stake amount for this epoch. pub pending_stake: Option, - /// Pending stake withdrawn during the current epoch, emptied at epoch boundaries. - pub pending_total_sui_withdraw: Option, + /// Pending stake withdrawn during the current epoch, emptied at epoch + /// boundaries. + pub pending_total_iota_withdraw: Option, /// Total number of pool tokens issued by the pool. pub pool_token_balance: Option, /// Validator's homepage URL. @@ -109,9 +107,10 @@ pub struct Validator { pub staking_pool_activation_epoch: Option, /// The ID of this validator's `0x3::staking_pool::StakingPool`. pub staking_pool_id: Address, - /// The total number of SUI tokens in this pool. - pub staking_pool_sui_balance: Option, - /// The voting power of this validator in basis points (e.g., 100 = 1% voting power). + /// The total number of IOTA tokens in this pool. + pub staking_pool_iota_balance: Option, + /// The voting power of this validator in basis points (e.g., 100 = 1% + /// voting power). pub voting_power: Option, } diff --git a/crates/sui-graphql-client/src/query_types/balance.rs b/crates/iota-graphql-client/src/query_types/balance.rs similarity index 87% rename from crates/sui-graphql-client/src/query_types/balance.rs rename to crates/iota-graphql-client/src/query_types/balance.rs index 843e861ac..00f838d76 100644 --- a/crates/sui-graphql-client/src/query_types/balance.rs +++ b/crates/iota-graphql-client/src/query_types/balance.rs @@ -1,9 +1,11 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::BigInt; -use crate::Address; +use crate::{ + Address, + query_types::{BigInt, schema}, +}; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "BalanceArgs")] diff --git a/crates/sui-graphql-client/src/query_types/chain.rs b/crates/iota-graphql-client/src/query_types/chain.rs similarity index 90% rename from crates/sui-graphql-client/src/query_types/chain.rs rename to crates/iota-graphql-client/src/query_types/chain.rs index c1b4226b4..cecae8226 100644 --- a/crates/sui-graphql-client/src/query_types/chain.rs +++ b/crates/iota-graphql-client/src/query_types/chain.rs @@ -1,4 +1,5 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; diff --git a/crates/sui-graphql-client/src/query_types/checkpoint.rs b/crates/iota-graphql-client/src/query_types/checkpoint.rs similarity index 93% rename from crates/sui-graphql-client/src/query_types/checkpoint.rs rename to crates/iota-graphql-client/src/query_types/checkpoint.rs index 3e6cf886a..a9a7d91b9 100644 --- a/crates/sui-graphql-client/src/query_types/checkpoint.rs +++ b/crates/iota-graphql-client/src/query_types/checkpoint.rs @@ -1,15 +1,15 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use base64ct::Encoding; -use sui_types::CheckpointSummary; +use iota_types::CheckpointSummary; -use crate::error; -use crate::error::Error; -use crate::error::Kind; -use crate::query_types::schema; -use crate::query_types::Base64; -use crate::query_types::PageInfo; +use crate::{ + error, + error::{Error, Kind}, + query_types::{Base64, PageInfo, schema}, +}; // =========================================================================== // Checkpoint Queries diff --git a/crates/sui-graphql-client/src/query_types/coin.rs b/crates/iota-graphql-client/src/query_types/coin.rs similarity index 94% rename from crates/sui-graphql-client/src/query_types/coin.rs rename to crates/iota-graphql-client/src/query_types/coin.rs index 10632ac9b..da212d3f1 100644 --- a/crates/sui-graphql-client/src/query_types/coin.rs +++ b/crates/iota-graphql-client/src/query_types/coin.rs @@ -1,4 +1,5 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 // =========================================================================== @@ -25,8 +26,7 @@ pub struct CoinMetadataArgs<'a> { // Types // =========================================================================== -use crate::query_types::schema; -use crate::query_types::BigInt; +use crate::query_types::{BigInt, schema}; /// The coin metadata associated with the given coin type. #[derive(cynic::QueryFragment, Debug)] diff --git a/crates/sui-graphql-client/src/query_types/dry_run.rs b/crates/iota-graphql-client/src/query_types/dry_run.rs similarity index 92% rename from crates/sui-graphql-client/src/query_types/dry_run.rs rename to crates/iota-graphql-client/src/query_types/dry_run.rs index 5d6c26cb6..c24bbb67d 100644 --- a/crates/sui-graphql-client/src/query_types/dry_run.rs +++ b/crates/iota-graphql-client/src/query_types/dry_run.rs @@ -1,12 +1,11 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use sui_types::ObjectReference; - -use crate::query_types::schema; -use crate::query_types::Address; +use iota_types::ObjectReference; use super::transaction::TxBlockEffects; +use crate::query_types::{Address, schema}; #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "Query", variables = "DryRunArgs")] diff --git a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs b/crates/iota-graphql-client/src/query_types/dynamic_fields.rs similarity index 94% rename from crates/sui-graphql-client/src/query_types/dynamic_fields.rs rename to crates/iota-graphql-client/src/query_types/dynamic_fields.rs index 61f683065..7950f0b1d 100644 --- a/crates/sui-graphql-client/src/query_types/dynamic_fields.rs +++ b/crates/iota-graphql-client/src/query_types/dynamic_fields.rs @@ -1,20 +1,16 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use std::str::FromStr; use base64ct::Encoding; -use sui_types::TypeTag; - -use crate::error; -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::Base64; -use crate::query_types::JsonValue; -use crate::query_types::MoveObjectContents; -use crate::query_types::MoveValue; -use crate::query_types::PageInfo; -use crate::DynamicFieldOutput; +use iota_types::TypeTag; + +use crate::{ + DynamicFieldOutput, error, + query_types::{Address, Base64, JsonValue, MoveObjectContents, MoveValue, PageInfo, schema}, +}; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/sui-graphql-client/src/query_types/epoch.rs b/crates/iota-graphql-client/src/query_types/epoch.rs similarity index 95% rename from crates/sui-graphql-client/src/query_types/epoch.rs rename to crates/iota-graphql-client/src/query_types/epoch.rs index 1ee12ba5e..ae357845f 100644 --- a/crates/sui-graphql-client/src/query_types/epoch.rs +++ b/crates/iota-graphql-client/src/query_types/epoch.rs @@ -1,10 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 // -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::BigInt; -use crate::query_types::DateTime; +use crate::query_types::{Address, BigInt, DateTime, schema}; // =========================================================================== // Epoch Queries diff --git a/crates/sui-graphql-client/src/query_types/events.rs b/crates/iota-graphql-client/src/query_types/events.rs similarity index 90% rename from crates/sui-graphql-client/src/query_types/events.rs rename to crates/iota-graphql-client/src/query_types/events.rs index 1d3f4b8ed..ee4b4281a 100644 --- a/crates/sui-graphql-client/src/query_types/events.rs +++ b/crates/iota-graphql-client/src/query_types/events.rs @@ -1,11 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::transaction::TransactionBlockDigest; -use crate::query_types::Address; -use crate::query_types::Base64; -use crate::query_types::PageInfo; +use crate::query_types::{Address, Base64, PageInfo, schema, transaction::TransactionBlockDigest}; // =========================================================================== // Events Queries diff --git a/crates/sui-graphql-client/src/query_types/execute_tx.rs b/crates/iota-graphql-client/src/query_types/execute_tx.rs similarity index 90% rename from crates/sui-graphql-client/src/query_types/execute_tx.rs rename to crates/iota-graphql-client/src/query_types/execute_tx.rs index a0c4ac5b8..ab91d29be 100644 --- a/crates/sui-graphql-client/src/query_types/execute_tx.rs +++ b/crates/iota-graphql-client/src/query_types/execute_tx.rs @@ -1,8 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::Base64; +use crate::query_types::{Base64, schema}; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/iota-graphql-client/src/query_types/mod.rs b/crates/iota-graphql-client/src/query_types/mod.rs new file mode 100644 index 000000000..2d2ff79de --- /dev/null +++ b/crates/iota-graphql-client/src/query_types/mod.rs @@ -0,0 +1,149 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod active_validators; +mod balance; +mod chain; +mod checkpoint; +mod coin; +mod dry_run; +mod dynamic_fields; +mod epoch; +mod events; +mod execute_tx; +mod normalized_move; +mod object; +mod packages; +mod protocol_config; +mod service_config; +mod transaction; + +pub use active_validators::{ + ActiveValidatorsArgs, ActiveValidatorsQuery, EpochValidator, Validator, ValidatorConnection, + ValidatorSet, +}; +pub use balance::{Balance, BalanceArgs, BalanceQuery, Owner}; +pub use chain::ChainIdentifierQuery; +pub use checkpoint::{ + CheckpointArgs, CheckpointId, CheckpointQuery, CheckpointTotalTxQuery, CheckpointsArgs, + CheckpointsQuery, +}; +pub use coin::{CoinMetadata, CoinMetadataArgs, CoinMetadataQuery}; +use cynic::impl_scalar; +pub use dry_run::{DryRunArgs, DryRunQuery, DryRunResult, TransactionMetadata}; +pub use dynamic_fields::{ + DynamicFieldArgs, DynamicFieldConnectionArgs, DynamicFieldName, DynamicFieldQuery, + DynamicFieldsOwnerQuery, DynamicObjectFieldQuery, +}; +pub use epoch::{Epoch, EpochSummaryArgs, EpochSummaryQuery}; +pub use events::{Event, EventConnection, EventFilter, EventsQuery, EventsQueryArgs}; +pub use execute_tx::{ExecuteTransactionArgs, ExecuteTransactionQuery, ExecutionResult}; +use iota_types::Address; +pub use normalized_move::{ + MoveAbility, MoveFunction, MoveFunctionTypeParameter, MoveModule, MoveVisibility, + NormalizedMoveFunctionQuery, NormalizedMoveFunctionQueryArgs, NormalizedMoveModuleQuery, + NormalizedMoveModuleQueryArgs, OpenMoveType, +}; +pub use object::{ + ObjectFilter, ObjectKey, ObjectQuery, ObjectQueryArgs, ObjectsQuery, ObjectsQueryArgs, +}; +pub use packages::{ + LatestPackageQuery, MovePackage, MovePackageVersionFilter, PackageArgs, PackageByNameArgs, + PackageByNameQuery, PackageCheckpointFilter, PackageQuery, PackageVersionsArgs, + PackageVersionsQuery, PackagesQuery, PackagesQueryArgs, +}; +pub use protocol_config::{ProtocolConfigQuery, ProtocolConfigs, ProtocolVersionArgs}; +use serde_json::Value as JsonValue; +pub use service_config::{Feature, ServiceConfig, ServiceConfigQuery}; +pub use transaction::{ + TransactionBlock, TransactionBlockArgs, TransactionBlockEffectsQuery, TransactionBlockQuery, + TransactionBlocksEffectsQuery, TransactionBlocksQuery, TransactionBlocksQueryArgs, + TransactionsFilter, +}; + +use crate::error; + +#[cynic::schema("rpc")] +pub mod schema {} + +// =========================================================================== +// Scalars +// =========================================================================== + +impl_scalar!(Address, schema::IotaAddress); +impl_scalar!(u64, schema::UInt53); +impl_scalar!(JsonValue, schema::JSON); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "Base64")] +pub struct Base64(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "BigInt")] +pub struct BigInt(pub String); + +#[derive(cynic::Scalar, Debug, Clone)] +#[cynic(graphql_type = "DateTime")] +pub struct DateTime(pub String); + +// =========================================================================== +// Types used in several queries +// =========================================================================== + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "Address")] +pub struct GQLAddress { + pub address: Address, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveObject")] +pub struct MoveObject { + pub bcs: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveObject")] +pub struct MoveObjectContents { + pub contents: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveValue")] +pub struct MoveValue { + pub type_: MoveType, + pub bcs: Base64, + pub json: Option, +} + +#[derive(cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "MoveType")] +pub struct MoveType { + pub repr: String, +} +// =========================================================================== +// Utility Types +// =========================================================================== + +#[derive(Clone, Default, cynic::QueryFragment, Debug)] +#[cynic(schema = "rpc", graphql_type = "PageInfo")] +/// Information about pagination in a connection. +pub struct PageInfo { + /// When paginating backwards, are there more items? + pub has_previous_page: bool, + /// Are there more items when paginating forwards? + pub has_next_page: bool, + /// When paginating backwards, the cursor to continue. + pub start_cursor: Option, + /// When paginating forwards, the cursor to continue. + pub end_cursor: Option, +} + +impl TryFrom for u64 { + type Error = error::Error; + + fn try_from(value: BigInt) -> Result { + Ok(value.0.parse::()?) + } +} diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/function.rs b/crates/iota-graphql-client/src/query_types/normalized_move/function.rs similarity index 90% rename from crates/sui-graphql-client/src/query_types/normalized_move/function.rs rename to crates/iota-graphql-client/src/query_types/normalized_move/function.rs index e60b5fda6..630d32442 100644 --- a/crates/sui-graphql-client/src/query_types/normalized_move/function.rs +++ b/crates/iota-graphql-client/src/query_types/normalized_move/function.rs @@ -1,9 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::MoveFunction; +use crate::query_types::{Address, MoveFunction, schema}; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/mod.rs b/crates/iota-graphql-client/src/query_types/normalized_move/mod.rs similarity index 84% rename from crates/sui-graphql-client/src/query_types/normalized_move/mod.rs rename to crates/iota-graphql-client/src/query_types/normalized_move/mod.rs index 053fde61b..6f118263c 100644 --- a/crates/sui-graphql-client/src/query_types/normalized_move/mod.rs +++ b/crates/iota-graphql-client/src/query_types/normalized_move/mod.rs @@ -1,14 +1,12 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 mod function; mod module; -pub use function::NormalizedMoveFunctionQuery; -pub use function::NormalizedMoveFunctionQueryArgs; -pub use module::MoveModule; -pub use module::NormalizedMoveModuleQuery; -pub use module::NormalizedMoveModuleQueryArgs; +pub use function::{NormalizedMoveFunctionQuery, NormalizedMoveFunctionQueryArgs}; +pub use module::{MoveModule, NormalizedMoveModuleQuery, NormalizedMoveModuleQueryArgs}; use crate::query_types::schema; diff --git a/crates/sui-graphql-client/src/query_types/normalized_move/module.rs b/crates/iota-graphql-client/src/query_types/normalized_move/module.rs similarity index 96% rename from crates/sui-graphql-client/src/query_types/normalized_move/module.rs rename to crates/iota-graphql-client/src/query_types/normalized_move/module.rs index 3448d4501..8b535eb6a 100644 --- a/crates/sui-graphql-client/src/query_types/normalized_move/module.rs +++ b/crates/iota-graphql-client/src/query_types/normalized_move/module.rs @@ -1,11 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::MoveAbility; -use crate::query_types::MoveFunction; -use crate::query_types::PageInfo; +use crate::query_types::{Address, MoveAbility, MoveFunction, PageInfo, schema}; #[derive(cynic::QueryFragment, Debug)] #[cynic( diff --git a/crates/sui-graphql-client/src/query_types/object.rs b/crates/iota-graphql-client/src/query_types/object.rs similarity index 93% rename from crates/sui-graphql-client/src/query_types/object.rs rename to crates/iota-graphql-client/src/query_types/object.rs index e708ec058..fb4e23212 100644 --- a/crates/sui-graphql-client/src/query_types/object.rs +++ b/crates/iota-graphql-client/src/query_types/object.rs @@ -1,11 +1,8 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::Base64; -use crate::query_types::MoveObjectContents; -use crate::query_types::PageInfo; +use crate::query_types::{Address, Base64, MoveObjectContents, PageInfo, schema}; // =========================================================================== // Object(s) Queries diff --git a/crates/sui-graphql-client/src/query_types/packages.rs b/crates/iota-graphql-client/src/query_types/packages.rs similarity index 96% rename from crates/sui-graphql-client/src/query_types/packages.rs rename to crates/iota-graphql-client/src/query_types/packages.rs index 347317fc6..1f87137e6 100644 --- a/crates/sui-graphql-client/src/query_types/packages.rs +++ b/crates/iota-graphql-client/src/query_types/packages.rs @@ -1,11 +1,10 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use sui_types::Address; +use iota_types::Address; -use crate::query_types::schema; -use crate::query_types::Base64; -use crate::query_types::PageInfo; +use crate::query_types::{Base64, PageInfo, schema}; // =========================================================================== // Package by address (and optional version) diff --git a/crates/sui-graphql-client/src/query_types/protocol_config.rs b/crates/iota-graphql-client/src/query_types/protocol_config.rs similarity index 76% rename from crates/sui-graphql-client/src/query_types/protocol_config.rs rename to crates/iota-graphql-client/src/query_types/protocol_config.rs index 1441ddaa2..5044cf361 100644 --- a/crates/sui-graphql-client/src/query_types/protocol_config.rs +++ b/crates/iota-graphql-client/src/query_types/protocol_config.rs @@ -1,4 +1,5 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; @@ -33,24 +34,29 @@ pub struct ProtocolVersionArgs { /// Information about the configuration of the protocol. /// Constants that control how the chain operates. -/// These can only change during protocol upgrades which happen on epoch boundaries. +/// These can only change during protocol upgrades which happen on epoch +/// boundaries. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigs")] pub struct ProtocolConfigs { - /// The protocol is not required to change on every epoch boundary, so the protocol version - /// tracks which change to the protocol these configs are from. + /// The protocol is not required to change on every epoch boundary, so the + /// protocol version tracks which change to the protocol these configs + /// are from. pub protocol_version: u64, - /// List all available feature flags and their values. Feature flags are a form of boolean - /// configuration that are usually used to gate features while they are in development. Once a - /// flag has been enabled, it is rare for it to be disabled. + /// List all available feature flags and their values. Feature flags are a + /// form of boolean configuration that are usually used to gate features + /// while they are in development. Once a flag has been enabled, it is + /// rare for it to be disabled. pub feature_flags: Vec, - /// List all available configurations and their values. These configurations can take any value - /// (but they will all be represented in string form), and do not include feature flags. + /// List all available configurations and their values. These configurations + /// can take any value (but they will all be represented in string + /// form), and do not include feature flags. pub configs: Vec, } -/// Feature flags are a form of boolean configuration that are usually used to gate features while -/// they are in development. Once a lag has been enabled, it is rare for it to be disabled. +/// Feature flags are a form of boolean configuration that are usually used to +/// gate features while they are in development. Once a lag has been enabled, it +/// is rare for it to be disabled. #[derive(cynic::QueryFragment, Debug)] #[cynic(schema = "rpc", graphql_type = "ProtocolConfigFeatureFlag")] pub struct ProtocolConfigFeatureFlag { diff --git a/crates/sui-graphql-client/src/query_types/service_config.rs b/crates/iota-graphql-client/src/query_types/service_config.rs similarity index 70% rename from crates/sui-graphql-client/src/query_types/service_config.rs rename to crates/iota-graphql-client/src/query_types/service_config.rs index facc09565..566b0319c 100644 --- a/crates/sui-graphql-client/src/query_types/service_config.rs +++ b/crates/iota-graphql-client/src/query_types/service_config.rs @@ -1,4 +1,5 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use crate::query_types::schema; @@ -26,42 +27,50 @@ pub struct ServiceConfig { /// List of all features that are enabled on this RPC service. pub enabled_features: Vec, // TODO This field is retrieved as a string, instead of i32 - /// Maximum estimated cost of a database query used to serve a GraphQL request. This is - /// measured in the same units that the database uses in EXPLAIN queries. + /// Maximum estimated cost of a database query used to serve a GraphQL + /// request. This is measured in the same units that the database uses + /// in EXPLAIN queries. // pub max_db_query_cost: i32, - /// Maximum nesting allowed in struct fields when calculating the layout of a single Move Type. + /// Maximum nesting allowed in struct fields when calculating the layout of + /// a single Move Type. pub max_move_value_depth: i32, /// The maximum number of output nodes in a GraphQL response. - /// Non-connection nodes have a count of 1, while connection nodes are counted as - /// the specified 'first' or 'last' number of items, or the default_page_size - /// as set by the server if those arguments are not set. - /// Counts accumulate multiplicatively down the query tree. For example, if a query starts - /// with a connection of first: 10 and has a field to a connection with last: 20, the count - /// at the second level would be 200 nodes. This is then summed to the count of 10 nodes + /// Non-connection nodes have a count of 1, while connection nodes are + /// counted as the specified 'first' or 'last' number of items, or the + /// default_page_size as set by the server if those arguments are not + /// set. Counts accumulate multiplicatively down the query tree. For + /// example, if a query starts with a connection of first: 10 and has a + /// field to a connection with last: 20, the count at the second level + /// would be 200 nodes. This is then summed to the count of 10 nodes /// at the first level, for a total of 210 nodes. pub max_output_nodes: i32, /// Maximum number of elements allowed on a single page of a connection. pub max_page_size: i32, /// The maximum depth a GraphQL query can be to be accepted by this service. pub max_query_depth: i32, - /// The maximum number of nodes (field names) the service will accept in a single query. + /// The maximum number of nodes (field names) the service will accept in a + /// single query. pub max_query_nodes: i32, /// Maximum length of a query payload string. pub max_query_payload_size: i32, - /// Maximum nesting allowed in type arguments in Move Types resolved by this service. + /// Maximum nesting allowed in type arguments in Move Types resolved by this + /// service. pub max_type_argument_depth: i32, - /// Maximum number of type arguments passed into a generic instantiation of a Move Type resolved - /// by this service. + /// Maximum number of type arguments passed into a generic instantiation of + /// a Move Type resolved by this service. pub max_type_argument_width: i32, - /// Maximum number of structs that need to be processed when calculating the layout of a single - /// Move Type. + /// Maximum number of structs that need to be processed when calculating the + /// layout of a single Move Type. pub max_type_nodes: i32, - /// Maximum time in milliseconds spent waiting for a response from fullnode after issuing a - /// a transaction to execute. Note that the transaction may still succeed even in the case of a - /// timeout. Transactions are idempotent, so a transaction that times out should be resubmitted - /// until the network returns a definite response (success or failure, not timeout). + /// Maximum time in milliseconds spent waiting for a response from fullnode + /// after issuing a a transaction to execute. Note that the transaction + /// may still succeed even in the case of a timeout. Transactions are + /// idempotent, so a transaction that times out should be resubmitted + /// until the network returns a definite response (success or failure, not + /// timeout). pub mutation_timeout_ms: i32, - /// Maximum time in milliseconds that will be spent to serve one query request. + /// Maximum time in milliseconds that will be spent to serve one query + /// request. pub request_timeout_ms: i32, } diff --git a/crates/sui-graphql-client/src/query_types/transaction.rs b/crates/iota-graphql-client/src/query_types/transaction.rs similarity index 94% rename from crates/sui-graphql-client/src/query_types/transaction.rs rename to crates/iota-graphql-client/src/query_types/transaction.rs index 29b888014..035ab1f3f 100644 --- a/crates/sui-graphql-client/src/query_types/transaction.rs +++ b/crates/iota-graphql-client/src/query_types/transaction.rs @@ -1,19 +1,15 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use base64ct::Encoding; -use sui_types::SignedTransaction; -use sui_types::Transaction; -use sui_types::TransactionEffects; -use sui_types::UserSignature; - -use crate::error; -use crate::error::Error; -use crate::error::Kind; -use crate::query_types::schema; -use crate::query_types::Address; -use crate::query_types::Base64; -use crate::query_types::PageInfo; +use iota_types::{SignedTransaction, Transaction, TransactionEffects, UserSignature}; + +use crate::{ + error, + error::{Error, Kind}, + query_types::{Address, Base64, PageInfo, schema}, +}; // =========================================================================== // Transaction Block(s) Queries diff --git a/crates/sui-graphql-client/src/streams.rs b/crates/iota-graphql-client/src/streams.rs similarity index 91% rename from crates/sui-graphql-client/src/streams.rs rename to crates/iota-graphql-client/src/streams.rs index f16985e71..49ebf22ba 100644 --- a/crates/sui-graphql-client/src/streams.rs +++ b/crates/iota-graphql-client/src/streams.rs @@ -1,19 +1,19 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::error; -use crate::query_types::PageInfo; -use crate::Direction; -use crate::Page; -use crate::PaginationFilter; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; use futures::Stream; -use std::future::Future; -use std::pin::Pin; -use std::task::Context; -use std::task::Poll; -/// A stream that yields items from a paginated query with support for bidirectional pagination. +use crate::{Direction, Page, PaginationFilter, error, query_types::PageInfo}; + +/// A stream that yields items from a paginated query with support for +/// bidirectional pagination. pub struct PageStream { query_fn: F, direction: Direction, @@ -59,7 +59,8 @@ where } // For backward pagination, we check for previous page - // For the first page in backward pagination, we don't need to check has_previous_page + // For the first page in backward pagination, we don't need to check + // has_previous_page let should_continue = match direction { Direction::Forward => page_info.has_next_page, Direction::Backward => page_info.has_previous_page, @@ -150,17 +151,18 @@ where /// Creates a new `PageStream` for a paginated query. /// -/// Examples +/// ## Example +/// /// ```rust,ignore /// use futures::StreamExt; -/// use sui_graphql_client::streams::stream_paginated_query; -/// use sui_graphql_client::Client; -/// use sui_graphql_client::PaginationFilter; -/// use sui_graphql_client::Direction; +/// use iota_graphql_client::streams::stream_paginated_query; +/// use iota_graphql_client::Client; +/// use iota_graphql_client::PaginationFilter; +/// use iota_graphql_client::Direction; /// /// let client = Client::new_testnet(); /// let stream = stream_paginated_query(|pagination_filter, Direction::Forward| { -/// client.coins(owner, coin_type, pagination_filter }) +/// client.coins(owner, coin_type, pagination_filter) /// }); /// while let Some(result) = stream.next().await { /// match result { diff --git a/crates/iota-sdk-types/CHANGELOG.md b/crates/iota-sdk-types/CHANGELOG.md new file mode 100644 index 000000000..34b1469b7 --- /dev/null +++ b/crates/iota-sdk-types/CHANGELOG.md @@ -0,0 +1,32 @@ +# [0.0.2] - 2025-01-06 + +## Added + +- Added `proptest::Arbitrary` impls via the `proptest` feature [`6918fd8`] +- Added From impl for TypeTag [#77] + +## Changed + +- Update the passkey challenge format to use the same signing message as other key types ([`c5a25ce`]) +- Flattened the `types` module into the top-level ([`dc54c46`]) +- Folded the `EffectsObjectChange` type into the `ChangedObject` struct ([`aa546ca`]) + +## Removed + +- Removed the `unresolved` module and moved it to the `iota-transaction-builder` crate ([`d965897`]) +- Removed the `schemars` feature ([`bc6dd37`]) + +[`c5a25ce`]: https://github.com/iotaledger/iota-rust-sdk/commit/c5a25ce356a8cbe42ddcc6ec6bab380007790b44 +[`6918fd8`]: https://github.com/iotaledger/iota-rust-sdk/commit/6918fd88d40734b8c15fb5c519e9a40aec53eb74 +[#77]: https://github.com/iotaledger/iota-rust-sdk/pull/77 +[`d965897`]: https://github.com/iotaledger/iota-rust-sdk/commit/d9658978a4c6e928d036fbedaab9326d5e28de87 +[`dc54c46`]: https://github.com/iotaledger/iota-rust-sdk/commit/dc54c469f9d006f02d82ec5781d73e8e09ae26ae +[`aa546ca`]: https://github.com/iotaledger/iota-rust-sdk/commit/aa546ca91249932da3f8e3d55ba6e52e40cd8929 +[`bc6dd37`]: https://github.com/iotaledger/iota-rust-sdk/commit/bc6dd3732973ed3c1c3ae811a818fc8504a99f0b + +# [0.0.1] - 2024-09-25 + +Initial release + +[0.0.2]: https://github.com/iotaledger/iota-rust-sdk/releases/tag/iota-sdk-types-0.0.2 +[0.0.1]: https://github.com/iotaledger/iota-rust-sdk/releases/tag/iota-sdk-types-0.0.1 diff --git a/crates/sui-sdk-types/Cargo.toml b/crates/iota-sdk-types/Cargo.toml similarity index 92% rename from crates/sui-sdk-types/Cargo.toml rename to crates/iota-sdk-types/Cargo.toml index 9648d5811..8a9baa0ae 100644 --- a/crates/sui-sdk-types/Cargo.toml +++ b/crates/iota-sdk-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "iota-sdk-types" -version = "0.0.2" -authors = ["IOTA Foundation "] +version = "0.0.0" +authors = ["IOTA Foundation "] edition = "2021" license = "Apache-2.0" publish = false @@ -47,8 +47,8 @@ winnow = "0.6.20" # Serialization and Deserialization support bcs = { version = "0.1.6", optional = true } -serde = { version = "1.0.190", optional = true } -serde_derive = { version = "1.0.190", optional = true } +serde = { version = "1.0.210", optional = true } +serde_derive = { version = "1.0.210", optional = true } serde_json = { version = "1.0.128", optional = true } serde_with = { version = "3.9", default-features = false, features = ["alloc"], optional = true } diff --git a/crates/sui-sdk-types/Makefile b/crates/iota-sdk-types/Makefile similarity index 100% rename from crates/sui-sdk-types/Makefile rename to crates/iota-sdk-types/Makefile diff --git a/crates/iota-sdk-types/README.md b/crates/iota-sdk-types/README.md new file mode 100644 index 000000000..eab545b79 --- /dev/null +++ b/crates/iota-sdk-types/README.md @@ -0,0 +1,8 @@ +# iota-sdk-types + +[![iota-sdk-types on crates.io](https://img.shields.io/crates/v/iota-sdk-types)](https://crates.io/crates/iota-sdk-types) +[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-sdk-types) +[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) + +The `iota-sdk-types` crate provides the definitions of the core types that are +part of the public API of the IOTA blockchain. diff --git a/crates/sui-sdk-types/src/address.rs b/crates/iota-sdk-types/src/address.rs similarity index 98% rename from crates/sui-sdk-types/src/address.rs rename to crates/iota-sdk-types/src/address.rs index b17ea4376..cac774d6a 100644 --- a/crates/sui-sdk-types/src/address.rs +++ b/crates/iota-sdk-types/src/address.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( feature = "serde", diff --git a/crates/sui-sdk-types/src/checkpoint.rs b/crates/iota-sdk-types/src/checkpoint.rs similarity index 97% rename from crates/sui-sdk-types/src/checkpoint.rs rename to crates/iota-sdk-types/src/checkpoint.rs index 6525d45b9..82db90880 100644 --- a/crates/sui-sdk-types/src/checkpoint.rs +++ b/crates/iota-sdk-types/src/checkpoint.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{ CheckpointContentsDigest, CheckpointDigest, Digest, GasCostSummary, Object, SignedTransaction, TransactionDigest, TransactionEffects, TransactionEffectsDigest, TransactionEvents, @@ -565,8 +569,8 @@ mod serialization { #[test] fn signed_checkpoint_fixture() { const FIXTURES: &[&str] = &[ - "CgAAAAAAAAAUAAAAAAAAABUAAAAAAAAAIJ6CIMG/6Un4MKNM8h+R9r8bQ6dNTk0WZxBMUQH1XFQBASCWUVucdQkje+4YbXVpvQZcg74nndL1NK7ccj1dDR04agAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAoAAAAAAAAAo6ieWnpV/x0kSNBhr9XKKBmu5pnBLC7e79llUKx2tLWJoY8gARFlw2d8zvm06HXSEjowAAABAAAAAAAAABAAAAAAAA==", - "AgAAAAAAAAAFAAAAAAAAAAYAAAAAAAAAIINaPEm+WRQV2vGcPR9fe6fYhxl48GpqB+DqDYQqRHkuASBe+6BDLHSRCMiWqBkvVMqWXPWUsZnpc2gbOVdre3vnowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAABAWCoYmV3PFYy3YJmjDVCIuYN/RF0sTq527RtV1zXFoTFffs0HIXbusMfkLUMX6aSkt0Dphmdcy+5TD7OhajvWp7YNN0gNTgBw1pJQTc6gQdx65P58Frp0yJvfIOSSGY5kvIQJwAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAr5Y5/tS1hUqbtzxseAZSJVWau25RSvn1e0jjxFNM/Z8+5YJL/GtdhAPUdLRpvIRGEjowAAABAAAAAAAAABAAAAAAAA==", + "CgAAAAAAAAAUAAAAAAAAABUAAAAAAAAAIJ6CIMG/6Un4MKNM8h+R9r8bQ6dNTk0WZxBMUQH1XFQBASCWUVucdQkje+4YbXVpvQZcg74nndL1NK7ccj1dDR04agAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAKAAAAAAAAAKOonlp6Vf8dJEjQYa/VyigZruaZwSwu3u/ZZVCsdrS1iaGPIAERZcNnfM75tOh10hI6MAAAAQAAAAAAAAAQAAAAAAA=", + "AgAAAAAAAAAFAAAAAAAAAAYAAAAAAAAAIINaPEm+WRQV2vGcPR9fe6fYhxl48GpqB+DqDYQqRHkuASBe+6BDLHSRCMiWqBkvVMqWXPWUsZnpc2gbOVdre3vnowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAQFgqGJldzxWMt2CZow1QiLmDf0RdLE6udu0bVdc1xaExX37NByF27rDH5C1DF+mkpLdA6YZnXMvuUw+zoWo71qe2DTdIDU4AcNaSUE3OoEHceuT+fBa6dMib3yDkkhmOZLyECcAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAK+WOf7UtYVKm7c8bHgGUiVVmrtuUUr59XtI48RTTP2fPuWCS/xrXYQD1HS0abyERhI6MAAAAQAAAAAAAAAQAAAAAAA=", ]; for fixture in FIXTURES { diff --git a/crates/sui-sdk-types/src/crypto/bls12381.rs b/crates/iota-sdk-types/src/crypto/bls12381.rs similarity index 97% rename from crates/sui-sdk-types/src/crypto/bls12381.rs rename to crates/iota-sdk-types/src/crypto/bls12381.rs index 4f2dcff94..d29e69ea8 100644 --- a/crates/sui-sdk-types/src/crypto/bls12381.rs +++ b/crates/iota-sdk-types/src/crypto/bls12381.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Implementation of bls12381 min-sig public-key cryptogrophy. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/sui-sdk-types/src/crypto/ed25519.rs b/crates/iota-sdk-types/src/crypto/ed25519.rs similarity index 97% rename from crates/sui-sdk-types/src/crypto/ed25519.rs rename to crates/iota-sdk-types/src/crypto/ed25519.rs index 8a6c9e669..9aa56f33c 100644 --- a/crates/sui-sdk-types/src/crypto/ed25519.rs +++ b/crates/iota-sdk-types/src/crypto/ed25519.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Implementation of ed25519 public-key cryptogrophy. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/sui-sdk-types/src/crypto/intent.rs b/crates/iota-sdk-types/src/crypto/intent.rs similarity index 80% rename from crates/sui-sdk-types/src/crypto/intent.rs rename to crates/iota-sdk-types/src/crypto/intent.rs index 110a31ba0..94c0598b0 100644 --- a/crates/sui-sdk-types/src/crypto/intent.rs +++ b/crates/iota-sdk-types/src/crypto/intent.rs @@ -1,13 +1,18 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + /// A Signing Intent /// -/// An intent is a compact struct serves as the domain separator for a message that a signature -/// commits to. It consists of three parts: +/// An intent is a compact struct serves as the domain separator for a message +/// that a signature commits to. It consists of three parts: /// 1. [enum IntentScope] (what the type of the message is) /// 2. [enum IntentVersion] /// 3. [enum AppId] (what application that the signature refers to). /// -/// The serialization of an Intent is a 3-byte array where each field is represented by a byte and -/// it is prepended onto a message before it is signed in Sui. +/// The serialization of an Intent is a 3-byte array where each field is +/// represented by a byte and it is prepended onto a message before it is signed +/// in IOTA. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Intent { pub scope: IntentScope, @@ -50,7 +55,8 @@ pub enum IntentScope { CheckpointSummary = 2, // Used for an authority signature on a checkpoint summary. PersonalMessage = 3, // Used for a user signature on a personal message. SenderSignedTransaction = 4, // Used for an authority signature on a user signed transaction. - ProofOfPossession = 5, // Used as a signature representing an authority's proof of possession of its authority protocol key. + ProofOfPossession = 5, /* Used as a signature representing an authority's proof of + * possession of its authority protocol key. */ HeaderDigest = 6, // Used for narwhal authority signature on header digest. BridgeEventUnused = 7, // for bridge purposes but it's currently not included in messages. ConsensusBlock = 8, // Used for consensus authority signature on block's digest @@ -67,7 +73,7 @@ pub enum IntentVersion { #[repr(u8)] #[non_exhaustive] pub enum IntentAppId { - Sui = 0, + Iota = 0, Narwhal = 1, Consensus = 2, } diff --git a/crates/sui-sdk-types/src/crypto/mod.rs b/crates/iota-sdk-types/src/crypto/mod.rs similarity index 97% rename from crates/sui-sdk-types/src/crypto/mod.rs rename to crates/iota-sdk-types/src/crypto/mod.rs index e909337ad..3d2d70edd 100644 --- a/crates/sui-sdk-types/src/crypto/mod.rs +++ b/crates/iota-sdk-types/src/crypto/mod.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + mod bls12381; mod ed25519; mod intent; diff --git a/crates/sui-sdk-types/src/crypto/multisig.rs b/crates/iota-sdk-types/src/crypto/multisig.rs similarity index 99% rename from crates/sui-sdk-types/src/crypto/multisig.rs rename to crates/iota-sdk-types/src/crypto/multisig.rs index c232649a3..e6b9243bf 100644 --- a/crates/sui-sdk-types/src/crypto/multisig.rs +++ b/crates/iota-sdk-types/src/crypto/multisig.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{ Ed25519PublicKey, Ed25519Signature, Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, diff --git a/crates/sui-sdk-types/src/crypto/passkey.rs b/crates/iota-sdk-types/src/crypto/passkey.rs similarity index 99% rename from crates/sui-sdk-types/src/crypto/passkey.rs rename to crates/iota-sdk-types/src/crypto/passkey.rs index a19f131c8..e0598cdb6 100644 --- a/crates/sui-sdk-types/src/crypto/passkey.rs +++ b/crates/iota-sdk-types/src/crypto/passkey.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{Secp256r1PublicKey, Secp256r1Signature, SimpleSignature}; /// An passkey authenticator with parsed fields. See field definition below. Can diff --git a/crates/sui-sdk-types/src/crypto/secp256k1.rs b/crates/iota-sdk-types/src/crypto/secp256k1.rs similarity index 97% rename from crates/sui-sdk-types/src/crypto/secp256k1.rs rename to crates/iota-sdk-types/src/crypto/secp256k1.rs index 94f556192..c7fbf3c07 100644 --- a/crates/sui-sdk-types/src/crypto/secp256k1.rs +++ b/crates/iota-sdk-types/src/crypto/secp256k1.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Implementation of secp256k1 public-key cryptogrophy. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/sui-sdk-types/src/crypto/secp256r1.rs b/crates/iota-sdk-types/src/crypto/secp256r1.rs similarity index 98% rename from crates/sui-sdk-types/src/crypto/secp256r1.rs rename to crates/iota-sdk-types/src/crypto/secp256r1.rs index 0e01918fc..bc937b868 100644 --- a/crates/sui-sdk-types/src/crypto/secp256r1.rs +++ b/crates/iota-sdk-types/src/crypto/secp256r1.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Implementation of secp256r1 public-key cryptogrophy. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] diff --git a/crates/sui-sdk-types/src/crypto/signature.rs b/crates/iota-sdk-types/src/crypto/signature.rs similarity index 98% rename from crates/sui-sdk-types/src/crypto/signature.rs rename to crates/iota-sdk-types/src/crypto/signature.rs index b1eddc074..9b10d7cd4 100644 --- a/crates/sui-sdk-types/src/crypto/signature.rs +++ b/crates/iota-sdk-types/src/crypto/signature.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{ Ed25519PublicKey, Ed25519Signature, MultisigAggregatedSignature, PasskeyAuthenticator, Secp256k1PublicKey, Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, @@ -424,12 +428,11 @@ mod serialization { fn from_serialized_bytes(bytes: impl AsRef<[u8]>) -> Result { let bytes = bytes.as_ref(); - let flag = SignatureScheme::from_byte( - *bytes - .first() - .ok_or_else(|| serde::de::Error::custom("missing signature scheme flag"))?, - ) - .map_err(serde::de::Error::custom)?; + let flag = + SignatureScheme::from_byte(*bytes.first().ok_or_else(|| { + SignatureFromBytesError::new("missing signature scheme flag") + })?) + .map_err(SignatureFromBytesError::new)?; match flag { SignatureScheme::Ed25519 | SignatureScheme::Secp256k1 diff --git a/crates/sui-sdk-types/src/crypto/validator.rs b/crates/iota-sdk-types/src/crypto/validator.rs similarity index 97% rename from crates/sui-sdk-types/src/crypto/validator.rs rename to crates/iota-sdk-types/src/crypto/validator.rs index 4cb499652..4976c87a5 100644 --- a/crates/sui-sdk-types/src/crypto/validator.rs +++ b/crates/iota-sdk-types/src/crypto/validator.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{Bls12381PublicKey, Bls12381Signature}; use crate::checkpoint::{EpochId, StakeUnit}; diff --git a/crates/sui-sdk-types/src/crypto/zklogin.rs b/crates/iota-sdk-types/src/crypto/zklogin.rs similarity index 99% rename from crates/sui-sdk-types/src/crypto/zklogin.rs rename to crates/iota-sdk-types/src/crypto/zklogin.rs index 27c74f632..4f42e3419 100644 --- a/crates/sui-sdk-types/src/crypto/zklogin.rs +++ b/crates/iota-sdk-types/src/crypto/zklogin.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::SimpleSignature; use crate::{checkpoint::EpochId, u256::U256}; diff --git a/crates/sui-sdk-types/src/digest.rs b/crates/iota-sdk-types/src/digest.rs similarity index 98% rename from crates/sui-sdk-types/src/digest.rs rename to crates/iota-sdk-types/src/digest.rs index 963686ee8..442746f86 100644 --- a/crates/sui-sdk-types/src/digest.rs +++ b/crates/iota-sdk-types/src/digest.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + /// A representation of a 32 byte digest #[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -315,7 +319,8 @@ impl_digest!(ObjectDigest); impl_digest!(ConsensusCommitDigest); impl_digest!(EffectsAuxiliaryDataDigest); -// Don't implement like the other digest types since this isn't intended to be serialized +// Don't implement like the other digest types since this isn't intended to be +// serialized pub type SigningDigest = [u8; Digest::LENGTH]; #[cfg(test)] diff --git a/crates/sui-sdk-types/src/effects/fixtures/genesis-transaction-effects b/crates/iota-sdk-types/src/effects/fixtures/genesis-transaction-effects similarity index 100% rename from crates/sui-sdk-types/src/effects/fixtures/genesis-transaction-effects rename to crates/iota-sdk-types/src/effects/fixtures/genesis-transaction-effects diff --git a/crates/sui-sdk-types/src/effects/fixtures/sponsor-tx-effects b/crates/iota-sdk-types/src/effects/fixtures/sponsor-tx-effects similarity index 100% rename from crates/sui-sdk-types/src/effects/fixtures/sponsor-tx-effects rename to crates/iota-sdk-types/src/effects/fixtures/sponsor-tx-effects diff --git a/crates/sui-sdk-types/src/effects/mod.rs b/crates/iota-sdk-types/src/effects/mod.rs similarity index 96% rename from crates/sui-sdk-types/src/effects/mod.rs rename to crates/iota-sdk-types/src/effects/mod.rs index 579b89dae..4786d9852 100644 --- a/crates/sui-sdk-types/src/effects/mod.rs +++ b/crates/iota-sdk-types/src/effects/mod.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + mod v1; pub use v1::{ @@ -25,7 +29,6 @@ impl TransactionEffects { pub fn status(&self) -> &ExecutionStatus { match self { TransactionEffects::V1(e) => e.status(), - TransactionEffects::V2(e) => e.status(), } } @@ -33,7 +36,6 @@ impl TransactionEffects { pub fn epoch(&self) -> u64 { match self { TransactionEffects::V1(e) => e.epoch(), - TransactionEffects::V2(e) => e.epoch(), } } @@ -41,7 +43,6 @@ impl TransactionEffects { pub fn gas_summary(&self) -> &crate::gas::GasCostSummary { match self { TransactionEffects::V1(e) => e.gas_summary(), - TransactionEffects::V2(e) => e.gas_summary(), } } } diff --git a/crates/sui-sdk-types/src/effects/v1.rs b/crates/iota-sdk-types/src/effects/v1.rs similarity index 99% rename from crates/sui-sdk-types/src/effects/v1.rs rename to crates/iota-sdk-types/src/effects/v1.rs index b7e9657b9..12ebd2df1 100644 --- a/crates/sui-sdk-types/src/effects/v1.rs +++ b/crates/iota-sdk-types/src/effects/v1.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use crate::{ EpochId, GasCostSummary, ObjectDigest, ObjectId, TransactionDigest, TransactionEventsDigest, digest::EffectsAuxiliaryDataDigest, diff --git a/crates/sui-sdk-types/src/events.rs b/crates/iota-sdk-types/src/events.rs similarity index 93% rename from crates/sui-sdk-types/src/events.rs rename to crates/iota-sdk-types/src/events.rs index 398d0bf0b..acf774e05 100644 --- a/crates/sui-sdk-types/src/events.rs +++ b/crates/iota-sdk-types/src/events.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{Address, Identifier, ObjectId, StructTag, TypeTag}; #[derive(Eq, PartialEq, Clone, Debug)] diff --git a/crates/sui-sdk-types/src/execution_status.rs b/crates/iota-sdk-types/src/execution_status.rs similarity index 99% rename from crates/sui-sdk-types/src/execution_status.rs rename to crates/iota-sdk-types/src/execution_status.rs index 22bce2cc3..3654313a3 100644 --- a/crates/sui-sdk-types/src/execution_status.rs +++ b/crates/iota-sdk-types/src/execution_status.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{Address, Digest, Identifier, ObjectId}; #[derive(Eq, PartialEq, Clone, Debug)] diff --git a/crates/sui-sdk-types/src/framework.rs b/crates/iota-sdk-types/src/framework.rs similarity index 92% rename from crates/sui-sdk-types/src/framework.rs rename to crates/iota-sdk-types/src/framework.rs index 709830246..00da9dc42 100644 --- a/crates/sui-sdk-types/src/framework.rs +++ b/crates/iota-sdk-types/src/framework.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + //! Rust definitions of move/iota framework types. use std::borrow::Cow; diff --git a/crates/sui-sdk-types/src/gas.rs b/crates/iota-sdk-types/src/gas.rs similarity index 97% rename from crates/sui-sdk-types/src/gas.rs rename to crates/iota-sdk-types/src/gas.rs index b41e1e5fc..43d83cfc4 100644 --- a/crates/sui-sdk-types/src/gas.rs +++ b/crates/iota-sdk-types/src/gas.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + /// Summary of gas charges. /// /// Storage is charged independently of computation. diff --git a/crates/sui-sdk-types/src/hash.rs b/crates/iota-sdk-types/src/hash.rs similarity index 98% rename from crates/sui-sdk-types/src/hash.rs rename to crates/iota-sdk-types/src/hash.rs index d796fe108..52c8d5016 100644 --- a/crates/sui-sdk-types/src/hash.rs +++ b/crates/iota-sdk-types/src/hash.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use blake2::Digest as DigestTrait; use crate::{Address, Digest}; @@ -238,7 +242,7 @@ mod signing_message { const INTENT: Intent = Intent { scope: IntentScope::TransactionData, version: IntentVersion::V0, - app_id: IntentAppId::Sui, + app_id: IntentAppId::Iota, }; let digest = signing_digest(INTENT, self); digest.into_inner() @@ -257,7 +261,7 @@ mod signing_message { const INTENT: Intent = Intent { scope: IntentScope::PersonalMessage, version: IntentVersion::V0, - app_id: IntentAppId::Sui, + app_id: IntentAppId::Iota, }; let digest = signing_digest(INTENT, &self.0); digest.into_inner() diff --git a/crates/sui-sdk-types/src/lib.rs b/crates/iota-sdk-types/src/lib.rs similarity index 64% rename from crates/sui-sdk-types/src/lib.rs rename to crates/iota-sdk-types/src/lib.rs index bc16f9405..fdd47f6c4 100644 --- a/crates/sui-sdk-types/src/lib.rs +++ b/crates/iota-sdk-types/src/lib.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + #![cfg_attr(doc_cfg, feature(doc_cfg))] #[cfg(feature = "hash")] @@ -19,139 +23,53 @@ mod transaction; mod type_tag; mod u256; -pub use address::Address; -pub use address::AddressParseError; -pub use checkpoint::CheckpointCommitment; -pub use checkpoint::CheckpointContents; -pub use checkpoint::CheckpointData; -pub use checkpoint::CheckpointSequenceNumber; -pub use checkpoint::CheckpointSummary; -pub use checkpoint::CheckpointTimestamp; -pub use checkpoint::CheckpointTransaction; -pub use checkpoint::CheckpointTransactionInfo; -pub use checkpoint::EndOfEpochData; -pub use checkpoint::EpochId; -pub use checkpoint::ProtocolVersion; -pub use checkpoint::SignedCheckpointSummary; -pub use checkpoint::StakeUnit; -pub use crypto::Bls12381PublicKey; -pub use crypto::Bls12381Signature; -pub use crypto::Bn254FieldElement; -pub use crypto::CircomG1; -pub use crypto::CircomG2; -pub use crypto::Claim; -pub use crypto::Ed25519PublicKey; -pub use crypto::Ed25519Signature; -pub use crypto::Intent; -pub use crypto::IntentAppId; -pub use crypto::IntentScope; -pub use crypto::IntentVersion; -pub use crypto::Jwk; -pub use crypto::JwkId; -pub use crypto::MultisigAggregatedSignature; -pub use crypto::MultisigCommittee; -pub use crypto::MultisigMember; -pub use crypto::MultisigMemberPublicKey; -pub use crypto::MultisigMemberSignature; -pub use crypto::PasskeyAuthenticator; -pub use crypto::PasskeyPublicKey; -pub use crypto::Secp256k1PublicKey; -pub use crypto::Secp256k1Signature; -pub use crypto::Secp256r1PublicKey; -pub use crypto::Secp256r1Signature; -pub use crypto::SignatureScheme; -pub use crypto::SimpleSignature; -pub use crypto::UserSignature; -pub use crypto::ValidatorAggregatedSignature; -pub use crypto::ValidatorCommittee; -pub use crypto::ValidatorCommitteeMember; -pub use crypto::ValidatorSignature; -pub use crypto::ZkLoginAuthenticator; -pub use crypto::ZkLoginInputs; -pub use crypto::ZkLoginProof; -pub use crypto::ZkLoginPublicIdentifier; -pub use digest::CheckpointContentsDigest; -pub use digest::CheckpointDigest; -pub use digest::ConsensusCommitDigest; -pub use digest::Digest; -pub use digest::DigestParseError; -pub use digest::EffectsAuxiliaryDataDigest; -pub use digest::ObjectDigest; -pub use digest::SigningDigest; -pub use digest::TransactionDigest; -pub use digest::TransactionEffectsDigest; -pub use digest::TransactionEventsDigest; -pub use effects::ChangedObject; -pub use effects::IdOperation; -pub use effects::ModifiedAtVersion; -pub use effects::ObjectIn; -pub use effects::ObjectOut; -pub use effects::ObjectReferenceWithOwner; -pub use effects::TransactionEffects; -pub use effects::TransactionEffectsV1; -pub use effects::TransactionEffectsV2; -pub use effects::UnchangedSharedKind; -pub use effects::UnchangedSharedObject; -pub use events::BalanceChange; -pub use events::Event; -pub use events::TransactionEvents; -pub use execution_status::CommandArgumentError; -pub use execution_status::ExecutionError; -pub use execution_status::ExecutionStatus; -pub use execution_status::MoveLocation; -pub use execution_status::PackageUpgradeError; -pub use execution_status::TypeArgumentError; +pub use address::{Address, AddressParseError}; +pub use checkpoint::{ + CheckpointCommitment, CheckpointContents, CheckpointData, CheckpointSequenceNumber, + CheckpointSummary, CheckpointTimestamp, CheckpointTransaction, CheckpointTransactionInfo, + EndOfEpochData, EpochId, ProtocolVersion, SignedCheckpointSummary, StakeUnit, +}; +pub use crypto::{ + Bls12381PublicKey, Bls12381Signature, Bn254FieldElement, CircomG1, CircomG2, Claim, + Ed25519PublicKey, Ed25519Signature, Intent, IntentAppId, IntentScope, IntentVersion, Jwk, + JwkId, MultisigAggregatedSignature, MultisigCommittee, MultisigMember, MultisigMemberPublicKey, + MultisigMemberSignature, PasskeyAuthenticator, PasskeyPublicKey, Secp256k1PublicKey, + Secp256k1Signature, Secp256r1PublicKey, Secp256r1Signature, SignatureScheme, SimpleSignature, + UserSignature, ValidatorAggregatedSignature, ValidatorCommittee, ValidatorCommitteeMember, + ValidatorSignature, ZkLoginAuthenticator, ZkLoginInputs, ZkLoginProof, ZkLoginPublicIdentifier, +}; +pub use digest::{ + CheckpointContentsDigest, CheckpointDigest, ConsensusCommitDigest, Digest, DigestParseError, + EffectsAuxiliaryDataDigest, ObjectDigest, SigningDigest, TransactionDigest, + TransactionEffectsDigest, TransactionEventsDigest, +}; +pub use effects::{ + ChangedObject, IdOperation, ObjectIn, ObjectOut, TransactionEffects, TransactionEffectsV1, + UnchangedSharedKind, UnchangedSharedObject, +}; +pub use events::{BalanceChange, Event, TransactionEvents}; +pub use execution_status::{ + CommandArgumentError, ExecutionError, ExecutionStatus, MoveLocation, PackageUpgradeError, + TypeArgumentError, +}; pub use gas::GasCostSummary; -pub use object::GenesisObject; -pub use object::MovePackage; -pub use object::MoveStruct; -pub use object::Object; -pub use object::ObjectData; -pub use object::ObjectReference; -pub use object::ObjectType; -pub use object::Owner; -pub use object::TypeOrigin; -pub use object::UpgradeInfo; -pub use object::Version; +pub use object::{ + GenesisObject, MovePackage, MoveStruct, Object, ObjectData, ObjectReference, ObjectType, Owner, + TypeOrigin, UpgradeInfo, Version, +}; pub use object_id::ObjectId; -pub use transaction::ActiveJwk; -pub use transaction::Argument; -pub use transaction::AuthenticatorStateExpire; -pub use transaction::AuthenticatorStateUpdate; -pub use transaction::CancelledTransaction; -pub use transaction::ChangeEpoch; -pub use transaction::Command; -pub use transaction::ConsensusCommitPrologue; -pub use transaction::ConsensusCommitPrologueV2; -pub use transaction::ConsensusCommitPrologueV3; -pub use transaction::ConsensusDeterminedVersionAssignments; -pub use transaction::EndOfEpochTransactionKind; -pub use transaction::GasPayment; -pub use transaction::GenesisTransaction; -pub use transaction::Input; -pub use transaction::MakeMoveVector; -pub use transaction::MergeCoins; -pub use transaction::MoveCall; -pub use transaction::ProgrammableTransaction; -pub use transaction::Publish; -pub use transaction::RandomnessStateUpdate; -pub use transaction::SignedTransaction; -pub use transaction::SplitCoins; -pub use transaction::SystemPackage; -pub use transaction::Transaction; -pub use transaction::TransactionExpiration; -pub use transaction::TransactionKind; -pub use transaction::TransferObjects; -pub use transaction::Upgrade; -pub use transaction::VersionAssignment; -pub use type_tag::Identifier; -pub use type_tag::StructTag; -pub use type_tag::TypeParseError; -pub use type_tag::TypeTag; - #[cfg(feature = "serde")] #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] pub(crate) use transaction::SignedTransactionWithIntentMessage; +pub use transaction::{ + ActiveJwk, Argument, AuthenticatorStateExpire, AuthenticatorStateUpdateV1, + CancelledTransaction, ChangeEpoch, Command, ConsensusCommitPrologueV1, + ConsensusDeterminedVersionAssignments, EndOfEpochTransactionKind, GasPayment, + GenesisTransaction, Input, MakeMoveVector, MergeCoins, MoveCall, ProgrammableTransaction, + Publish, RandomnessStateUpdate, SignedTransaction, SplitCoins, SystemPackage, Transaction, + TransactionExpiration, TransactionKind, TransferObjects, Upgrade, VersionAssignment, +}; +pub use type_tag::{Identifier, StructTag, TypeParseError, TypeTag}; #[cfg(test)] mod serialization_proptests; diff --git a/crates/sui-sdk-types/src/object.rs b/crates/iota-sdk-types/src/object.rs similarity index 98% rename from crates/sui-sdk-types/src/object.rs rename to crates/iota-sdk-types/src/object.rs index bb4b08d92..35ba8e7dd 100644 --- a/crates/sui-sdk-types/src/object.rs +++ b/crates/iota-sdk-types/src/object.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use std::collections::BTreeMap; use super::{Address, Identifier, ObjectDigest, ObjectId, StructTag, TransactionDigest}; @@ -205,42 +209,6 @@ pub struct MoveStruct { pub contents: Vec, } -impl MoveStruct { - pub fn new( - type_: StructTag, - has_public_transfer: bool, - version: Version, - contents: Vec, - ) -> Option { - id_opt(&contents).map(|_| Self { - type_, - has_public_transfer, - version, - contents, - }) - } - - pub fn object_type(&self) -> &StructTag { - &self.type_ - } - - pub fn has_public_transfer(&self) -> bool { - self.has_public_transfer - } - - pub fn version(&self) -> Version { - self.version - } - - pub fn contents(&self) -> &[u8] { - &self.contents - } - - pub fn object_id(&self) -> ObjectId { - id_opt(self.contents()).unwrap() - } -} - /// Type of an IOTA object #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug)] pub enum ObjectType { diff --git a/crates/sui-sdk-types/src/object_id.rs b/crates/iota-sdk-types/src/object_id.rs similarity index 93% rename from crates/sui-sdk-types/src/object_id.rs rename to crates/iota-sdk-types/src/object_id.rs index 89c8288c0..7f374f1eb 100644 --- a/crates/sui-sdk-types/src/object_id.rs +++ b/crates/iota-sdk-types/src/object_id.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::Address; #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/crates/sui-sdk-types/src/serialization_proptests.rs b/crates/iota-sdk-types/src/serialization_proptests.rs similarity index 96% rename from crates/sui-sdk-types/src/serialization_proptests.rs rename to crates/iota-sdk-types/src/serialization_proptests.rs index da7ace404..3e84cf1c6 100644 --- a/crates/sui-sdk-types/src/serialization_proptests.rs +++ b/crates/iota-sdk-types/src/serialization_proptests.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use test_strategy::proptest; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::wasm_bindgen_test as test; @@ -39,10 +43,10 @@ where { let root_schema = schemars::gen::SchemaGenerator::default().into_root_schema_for::(); let schema = serde_json::json!(root_schema); - let compiled = jsonschema::Validator::new(&schema).unwrap(); + let validator = jsonschema::Validator::new(&schema).unwrap(); let instance = serde_json::json!(instance); - let result = compiled.validate(&instance); + let result = validator.validate(&instance); let r = result.is_ok(); if let Err(errors) = result { for error in errors { diff --git a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml similarity index 100% rename from crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml rename to crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml index 605a044c2..60cb06cb1 100644 --- a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml +++ b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml @@ -9,10 +9,10 @@ edition = "2021" anyhow = "1.0.71" bcs = "0.1.4" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597" } +futures = "0.3.28" iota-json-rpc-types = { path = "../../../../../../../../iota/crates/iota-json-rpc-types" } -iota-sdk = { path = "../../../../../../../../iota/crates/iota-sdk" } iota-keys = { path = "../../../../../../../../iota/crates/iota-keys" } +iota-sdk = { path = "../../../../../../../../iota/crates/iota-sdk" } iota-types = { path = "../../../../../../../../iota/crates/iota-types" } test-cluster = { path = "../../../../../../../../iota/crates/test-cluster" } tokio = "1.39.2" -futures = "0.3.28" diff --git a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs similarity index 98% rename from crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs rename to crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs index 8ea2aeb2d..2426567f1 100644 --- a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs +++ b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2024 IOTA Stiftung +// Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 //! Update the fixtures for the transaction_fixtures() test. @@ -77,7 +77,7 @@ async fn main() -> Result<(), anyhow::Error> { got_genesis = true; } } - IotaTransactionBlockKind::ConsensusCommitPrologueV1(_consensus_commit_prologue) => { + IotaTransactionBlockKind::ConsensusCommitPrologueV1(_consensus_commit_prologue_v1) => { if !got_consensus_commit_prologue_v1 { write_bs64_tx_to_file( &raw_tx_bytes_to_transaction_data_bytes(&tx.raw_transaction)?, diff --git a/crates/sui-sdk-types/src/transaction/mod.rs b/crates/iota-sdk-types/src/transaction/mod.rs similarity index 97% rename from crates/sui-sdk-types/src/transaction/mod.rs rename to crates/iota-sdk-types/src/transaction/mod.rs index 9ef1a617a..5f7cb3cd9 100644 --- a/crates/sui-sdk-types/src/transaction/mod.rs +++ b/crates/iota-sdk-types/src/transaction/mod.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use super::{ Address, CheckpointTimestamp, ConsensusCommitDigest, EpochId, Event, GenesisObject, Identifier, Jwk, JwkId, ObjectId, ObjectReference, ProtocolVersion, TransactionDigest, TypeTag, @@ -98,20 +102,11 @@ pub enum TransactionKind { /// A transaction that allows the interleaving of native commands and Move /// calls ProgrammableTransaction(ProgrammableTransaction), - /// A system transaction that will update epoch information on-chain. - /// It will only ever be executed once in an epoch. - /// The argument is the next epoch number, which is critical - /// because it ensures that this transaction has a unique digest. - /// This will eventually be translated to a Move call during execution. - /// It also doesn't require/use a gas object. - /// A validator will not sign a transaction of this kind from outside. It - /// only signs internally during epoch changes. Genesis(GenesisTransaction), ConsensusCommitPrologueV1(ConsensusCommitPrologueV1), AuthenticatorStateUpdateV1(AuthenticatorStateUpdateV1), - /// EndOfEpochTransaction replaces ChangeEpoch with a list of transactions - /// that are allowed to run at the end of the epoch. + /// A list of transactions that are allowed to run at the end of the epoch. EndOfEpoch(Vec), RandomnessStateUpdate(RandomnessStateUpdate), @@ -155,7 +150,7 @@ pub struct AuthenticatorStateExpire { /// The initial version of the authenticator object that it was shared at. #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] #[cfg_attr(feature = "schemars", schemars(with = "crate::_schemars::U64"))] - pub authenticator_object_initial_shared_version: u64, + pub authenticator_obj_initial_shared_version: u64, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/sui-sdk-types/src/transaction/serialization.rs b/crates/iota-sdk-types/src/transaction/serialization.rs similarity index 99% rename from crates/sui-sdk-types/src/transaction/serialization.rs rename to crates/iota-sdk-types/src/transaction/serialization.rs index 8d628463b..920ad7397 100644 --- a/crates/sui-sdk-types/src/transaction/serialization.rs +++ b/crates/iota-sdk-types/src/transaction/serialization.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; @@ -536,7 +540,7 @@ mod version_assignments { mod input_argument { use super::*; - use crate::transaction::{Input, InputArgument}; + use crate::transaction::Input; #[derive(serde_derive::Serialize, serde_derive::Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] diff --git a/crates/sui-sdk-types/src/type_tag/mod.rs b/crates/iota-sdk-types/src/type_tag/mod.rs similarity index 97% rename from crates/sui-sdk-types/src/type_tag/mod.rs rename to crates/iota-sdk-types/src/type_tag/mod.rs index dd01a0cea..4c64cdc83 100644 --- a/crates/sui-sdk-types/src/type_tag/mod.rs +++ b/crates/iota-sdk-types/src/type_tag/mod.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + mod parse; #[cfg(feature = "serde")] diff --git a/crates/sui-sdk-types/src/type_tag/parse.rs b/crates/iota-sdk-types/src/type_tag/parse.rs similarity index 94% rename from crates/sui-sdk-types/src/type_tag/parse.rs rename to crates/iota-sdk-types/src/type_tag/parse.rs index a3386c18d..76fa5e1e8 100644 --- a/crates/sui-sdk-types/src/type_tag/parse.rs +++ b/crates/iota-sdk-types/src/type_tag/parse.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use winnow::{ PResult, Parser, ascii::space0, @@ -145,7 +149,7 @@ mod tests { "0x1::__::__", "0x1::_bar::_BAR<0x2::_____::______fooo______>", "0x1::__::__<0x2::_____::______fooo______, 0xff::Bar____::_______foo>", - "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::sui::SUI, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", + "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::iota::IOTA, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", ] { assert!(parse_type_tag(s).is_ok(), "Failed to parse tag {}", s); } @@ -181,7 +185,7 @@ mod tests { "0x1::__::__", "0x1::_bar::_BAR<0x2::_____::______fooo______>", "0x1::__::__<0x2::_____::______fooo______, 0xff::Bar____::_______foo>", - "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::sui::SUI, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", + "0x5d32d749705c5f07c741f1818df3db466128bf01677611a959b03040ac5dc774::slippage::HopSwapEvent<0x2::iota::IOTA, 0x3c86bba6a3d3ce958615ae51cc5604f58956b1583323f664cf5f048da0fcbb19::_spd::_SPD>", ]; for s in valid { let mut input = s; diff --git a/crates/sui-sdk-types/src/type_tag/serialization.rs b/crates/iota-sdk-types/src/type_tag/serialization.rs similarity index 98% rename from crates/sui-sdk-types/src/type_tag/serialization.rs rename to crates/iota-sdk-types/src/type_tag/serialization.rs index f2a72ddfe..fce8cfdb0 100644 --- a/crates/sui-sdk-types/src/type_tag/serialization.rs +++ b/crates/iota-sdk-types/src/type_tag/serialization.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor}; use serde_with::{DeserializeAs, SerializeAs}; diff --git a/crates/sui-sdk-types/src/u256.rs b/crates/iota-sdk-types/src/u256.rs similarity index 96% rename from crates/sui-sdk-types/src/u256.rs rename to crates/iota-sdk-types/src/u256.rs index a31ebed88..7a590ece1 100644 --- a/crates/sui-sdk-types/src/u256.rs +++ b/crates/iota-sdk-types/src/u256.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + // Before we can expose this in the public interface it likely needs to be // wrapped so that the type from our dependency doesn't leak pub(crate) type U256 = bnum::BUintD8<32>; diff --git a/crates/sui-transaction-builder/.gitignore b/crates/iota-transaction-builder/.gitignore similarity index 100% rename from crates/sui-transaction-builder/.gitignore rename to crates/iota-transaction-builder/.gitignore diff --git a/crates/iota-transaction-builder/Cargo.toml b/crates/iota-transaction-builder/Cargo.toml new file mode 100644 index 000000000..aeaa3dedc --- /dev/null +++ b/crates/iota-transaction-builder/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "iota-transaction-builder" +version = "0.0.0" +authors = ["IOTA Foundation "] +edition = "2021" +license = "Apache-2.0" +publish = false +readme = "README.md" +description = "Transaction API for the IOTA Blockchain Rust SDK" + +[features] +default = [] +schemars = ["dep:schemars", "iota-types/schemars"] + +[dependencies] +base64ct = { version = "1.6", features = ["std"] } +bcs = "0.1.6" +iota-types = { package = "iota-sdk-types", path = "../iota-sdk-types", features = ["serde", "hash"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0.128" } +serde_with = { version = "3.9", default-features = false, features = ["alloc"] } +thiserror = "2.0" + +# JsonSchema definitions for types, useful for generating an OpenAPI Specificaiton. +schemars = { version = "0.8.21", optional = true } + +[dev-dependencies] +anyhow = "1.0" +iota-crypto = { package = "iota-crypto", path = "../iota-crypto", features = ["ed25519"] } +iota-graphql-client = { package = "iota-graphql-client", path = "../iota-graphql-client" } +iota-types = { package = "iota-sdk-types", path = "../iota-sdk-types", features = ["rand"] } +rand = "0.8" +serde_json = "1.0" +tokio = { version = "1.0", features = ["full"] } diff --git a/crates/sui-transaction-builder/src/error.rs b/crates/iota-transaction-builder/src/error.rs similarity index 94% rename from crates/sui-transaction-builder/src/error.rs rename to crates/iota-transaction-builder/src/error.rs index 8ef4721c6..72f71aad4 100644 --- a/crates/sui-transaction-builder/src/error.rs +++ b/crates/iota-transaction-builder/src/error.rs @@ -1,8 +1,9 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 use base64ct::Error as Base64Error; -use sui_types::ObjectId; +use iota_types::ObjectId; #[derive(thiserror::Error, Debug, Clone)] #[non_exhaustive] diff --git a/crates/sui-transaction-builder/src/lib.rs b/crates/iota-transaction-builder/src/lib.rs similarity index 86% rename from crates/sui-transaction-builder/src/lib.rs rename to crates/iota-transaction-builder/src/lib.rs index 050648d8f..613b086cc 100644 --- a/crates/sui-transaction-builder/src/lib.rs +++ b/crates/iota-transaction-builder/src/lib.rs @@ -1,42 +1,31 @@ // Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 mod error; pub mod unresolved; -use error::Error; -use sui_types::Address; -use sui_types::Argument; -use sui_types::Command; -use sui_types::GasPayment; -use sui_types::Identifier; -use sui_types::Input; -use sui_types::MakeMoveVector; -use sui_types::MergeCoins; -use sui_types::MoveCall; -use sui_types::ObjectId; -use sui_types::ObjectReference; -use sui_types::Publish; -use sui_types::SplitCoins; -use sui_types::Transaction; -use sui_types::TransactionExpiration; -use sui_types::TransferObjects; -use sui_types::TypeTag; -use sui_types::Upgrade; - use base64ct::Encoding; +pub use error::Error; +use iota_types::{ + Address, Argument, Command, GasPayment, Identifier, Input, MakeMoveVector, MergeCoins, + MoveCall, ObjectId, ObjectReference, Publish, SplitCoins, Transaction, TransactionExpiration, + TransferObjects, TypeTag, Upgrade, +}; use serde::Serialize; -/// A builder for creating transactions. Use [`resolve`] to finalize the transaction data. +/// A builder for creating transactions. Use [`finish`](Self::finish) to +/// finalize the transaction data. #[derive(Clone, Default, Debug)] pub struct TransactionBuilder { /// The inputs to the transaction. inputs: Vec, - /// The list of commands in the transaction. A command is a single operation in a programmable - /// transaction. + /// The list of commands in the transaction. A command is a single operation + /// in a programmable transaction. commands: Vec, - /// The gas objects that will be used to pay for the transaction. The most common way is to - /// use [`unresolved::Input::owned`] function to create a gas object and use the [`add_gas`] + /// The gas objects that will be used to pay for the transaction. The most + /// common way is to use [`unresolved::Input::owned`] function to create + /// a gas object and use the [`add_gas_objects`](Self::add_gas_objects) /// method to set the gas objects. gas: Vec, /// The gas budget for the transaction. @@ -47,18 +36,20 @@ pub struct TransactionBuilder { sender: Option
, /// The sponsor of the transaction. If None, the sender is also the sponsor. sponsor: Option
, - /// The expiration of the transaction. The default value of this type is no expiration. + /// The expiration of the transaction. The default value of this type is no + /// expiration. expiration: TransactionExpiration, } -/// A transaction input that bypasses serialization. The input contents is already BCS serialized -/// and is put verbatim into the transaction. +/// A transaction input that bypasses serialization. The input contents is +/// already BCS serialized and is put verbatim into the transaction. struct RawBytes(Vec); /// A transaction input that will be serialized from BCS. pub struct Serialized<'a, T: Serialize>(pub &'a T); -/// A separate type to support denoting a function by a more structured representation. +/// A separate type to support denoting a function by a more structured +/// representation. pub struct Function { /// The package that contains the module with the function. package: Address, @@ -95,9 +86,10 @@ impl TransactionBuilder { /// Add one or more gas objects to use to pay for the transaction. /// - /// Most commonly the gas can be passed as a reference to an owned/immutable [`Object`], - /// or can created using one of the of the constructors of the [`unresolved::Input`] enum, - /// e.g., [`unresolved::Input::owned`]. + /// Most commonly the gas can be passed as a reference to an owned/immutable + /// [`Object`](iota_types::Object), or can created using one of the of + /// the constructors of the [`unresolved::Input`] enum, e.g., + /// [`unresolved::Input::owned`]. pub fn add_gas_objects(&mut self, gas: I) where O: Into, @@ -135,12 +127,12 @@ impl TransactionBuilder { /// Call a Move function with the given arguments. /// - /// - `function` is a structured representation of a package::module::function argument, - /// optionally with type arguments. + /// - `function` is a structured representation of a + /// package::module::function argument, optionally with type arguments. /// - /// The return value is a result argument that can be used in subsequent commands. - /// If the move call returns multiple results, you can access them using the - /// [`Argument::nested`] method. + /// The return value is a result argument that can be used in subsequent + /// commands. If the move call returns multiple results, you can access + /// them using the [`Argument::nested`] method. pub fn move_call(&mut self, function: Function, arguments: Vec) -> Argument { let cmd = Command::MoveCall(MoveCall { package: function.package.into(), @@ -153,15 +145,16 @@ impl TransactionBuilder { Argument::Result(self.commands.len() as u16 - 1) } - /// Transfer a list of objects to the given address, without producing any result. + /// Transfer a list of objects to the given address, without producing any + /// result. pub fn transfer_objects(&mut self, objects: Vec, address: Argument) { let cmd = Command::TransferObjects(TransferObjects { objects, address }); self.commands.push(cmd); } - /// Split a coin by the provided amounts, returning multiple results (as many as there are - /// amounts). To access the results, use the [`Argument::nested`] method to access the desired - /// coin by its index. + /// Split a coin by the provided amounts, returning multiple results (as + /// many as there are amounts). To access the results, use the + /// [`Argument::nested`] method to access the desired coin by its index. pub fn split_coins(&mut self, coin: Argument, amounts: Vec) -> Argument { let cmd = Command::SplitCoins(SplitCoins { coin, amounts }); self.commands.push(cmd); @@ -177,9 +170,10 @@ impl TransactionBuilder { self.commands.push(cmd); } - /// Make a move vector from a list of elements. If the elements are not objects, or the vector - /// is empty, a type must be supplied. - /// It returns the Move vector as an argument, that can be used in subsequent commands. + /// Make a move vector from a list of elements. If the elements are not + /// objects, or the vector is empty, a type must be supplied. + /// It returns the Move vector as an argument, that can be used in + /// subsequent commands. pub fn make_move_vec(&mut self, type_: Option, elements: Vec) -> Argument { let cmd = Command::MakeMoveVector(MakeMoveVector { type_, elements }); self.commands.push(cmd); @@ -187,8 +181,8 @@ impl TransactionBuilder { } /// Publish a list of modules with the given dependencies. The result is the - /// `0x2::package::UpgradeCap` Move type. Note that the upgrade capability needs to be handled - /// after this call: + /// `0x2::package::UpgradeCap` Move type. Note that the upgrade capability + /// needs to be handled after this call: /// - transfer it to the transaction sender or another address /// - burn it /// - wrap it for access control @@ -196,7 +190,8 @@ impl TransactionBuilder { /// /// The arguments required for this command are: /// - `modules`: is the modules' bytecode to be published - /// - `dependencies`: is the list of IDs of the transitive dependencies of the package + /// - `dependencies`: is the list of IDs of the transitive dependencies of + /// the package pub fn publish(&mut self, modules: Vec>, dependencies: Vec) -> Argument { let cmd = Command::Publish(Publish { modules, @@ -209,30 +204,34 @@ impl TransactionBuilder { /// Upgrade a Move package. /// /// - `modules`: is the modules' bytecode for the modules to be published - /// - `dependencies`: is the list of IDs of the transitive dependencies of the package to be - /// upgraded + /// - `dependencies`: is the list of IDs of the transitive dependencies of + /// the package to be upgraded /// - `package`: is the ID of the current package being upgraded /// - `ticket`: is the upgrade ticket /// - /// To get the ticket, you have to call the `0x2::package::authorize_upgrade` function, - /// and pass the package ID, the upgrade policy, and package digest. + /// To get the ticket, you have to call the + /// `0x2::package::authorize_upgrade` function, and pass the package + /// ID, the upgrade policy, and package digest. /// /// Examples: /// ### Upgrade a package with some pre-known data. + /// /// ```rust,ignore - /// use sui_graphql_client::Client; - /// use sui_sdk_types::unresolved; - /// use sui_transaction_builder::TransactionBuilder; - /// use sui_transaction_builder::Function; + /// use iota_graphql_client::Client; + /// use iota_sdk_types::unresolved; + /// use iota_transaction_builder::TransactionBuilder; + /// use iota_transaction_builder::Function; /// /// let mut tx = TransactionBuilder::new(); /// let package_id = "0x...".parse().unwrap(); - /// let upgrade_cap = tx.input(unresolved::Input::by_id("0x...".parse().unwrap()); + /// let upgrade_cap = + /// tx.input(unresolved::Input::by_id("0x...".parse().unwrap())); /// let upgrade_policy = tx.input(Serialized(&0u8)); /// // the digest of the new package that was compiled /// let package_digest: &[u8] = &[ - /// 68, 89, 156, 51, 190, 35, 155, 216, 248, 49, 135, 170, 106, 42, 190, 4, 208, 59, 155, - /// 89, 74, 63, 70, 95, 207, 78, 227, 22, 136, 146, 57, 79, + /// 68, 89, 156, 51, 190, 35, 155, 216, 248, 49, 135, 170, 106, 42, + /// 190, 4, 208, 59, 155, 89, 74, 63, 70, 95, 207, 78, 227, 22, + /// 136, 146, 57, 79 /// ]; /// let digest_arg = tx.input(Serialized(&package_digest)); /// @@ -286,7 +285,8 @@ impl TransactionBuilder { } /// Assuming everything is resolved, convert this transaction into the - /// resolved form. Returns a [`Transaction`] if successful, or an [`Error`] if not. + /// resolved form. Returns a [`Transaction`] if successful, or an [`Error`] + /// if not. pub fn finish(self) -> Result { let Some(sender) = self.sender else { return Err(Error::MissingSender); @@ -302,8 +302,8 @@ impl TransactionBuilder { }; Ok(Transaction { - kind: sui_types::TransactionKind::ProgrammableTransaction( - sui_types::ProgrammableTransaction { + kind: iota_types::TransactionKind::ProgrammableTransaction( + iota_types::ProgrammableTransaction { inputs: self .inputs .into_iter() @@ -371,8 +371,8 @@ impl<'a, T: Serialize> From> for unresolved::Input { } } -/// Convert from an [`unresolved::Input`] to a [`unresolved::ObjectReference`]. This is used to -/// convert gas objects into unresolved object references. +/// Convert from an [`unresolved::Input`] to a [`unresolved::ObjectReference`]. +/// This is used to convert gas objects into unresolved object references. fn try_from_gas_unresolved_input_to_unresolved_obj_ref( input: unresolved::Input, ) -> Result { @@ -479,30 +479,21 @@ mod tests { use anyhow::Context; use base64ct::Encoding; - use serde::de; - use serde::Deserialize; - use serde::Deserializer; - use sui_crypto::ed25519::Ed25519PrivateKey; - use sui_crypto::SuiSigner; - use sui_graphql_client::faucet::CoinInfo; - use sui_graphql_client::faucet::FaucetClient; - use sui_graphql_client::Client; - use sui_graphql_client::PaginationFilter; - use sui_types::Address; - use sui_types::ExecutionStatus; - use sui_types::IdOperation; - use sui_types::ObjectId; - use sui_types::ObjectType; - use sui_types::TransactionEffects; - use sui_types::TypeTag; - - use crate::unresolved::Input; - use crate::Function; - use crate::Serialized; - use crate::TransactionBuilder; - use sui_types::TransactionDigest; - - /// Type corresponding to the output of `sui move build --dump-bytecode-as-base64` + use iota_crypto::{IotaSigner, ed25519::Ed25519PrivateKey}; + use iota_graphql_client::{ + Client, PaginationFilter, + faucet::{CoinInfo, FaucetClient}, + }; + use iota_types::{ + Address, ExecutionStatus, IdOperation, ObjectId, ObjectType, TransactionDigest, + TransactionEffects, TypeTag, + }; + use serde::{Deserialize, Deserializer, de}; + + use crate::{Function, Serialized, TransactionBuilder, unresolved::Input}; + + /// Type corresponding to the output of `iota move build + /// --dump-bytecode-as-base64` #[derive(serde::Deserialize, Debug)] struct MovePackageData { #[serde(deserialize_with = "bcs_from_str")] @@ -532,11 +523,12 @@ mod tests { .collect() } - /// This is used to read the json file that contains the modules/deps/digest generated with sui - /// move build --dump-bytecode-as-base64 on the `test_example_v1 and test_example_v2` projects - /// in the tests directory. - /// The json files are generated automatically when running `make test-with-localnet` in the - /// root of the sui-transaction-builder crate. + /// This is used to read the json file that contains the modules/deps/digest + /// generated with iota move build --dump-bytecode-as-base64 on the + /// `test_example_v1 and test_example_v2` projects in the tests + /// directory. The json files are generated automatically when running + /// `make test-with-localnet` in the root of the + /// iota-transaction-builder crate. fn move_package_data(file: &str) -> MovePackageData { let data = std::fs::read_to_string(file) .with_context(|| { @@ -563,11 +555,12 @@ mod tests { /// - set gas price /// - set gas budget /// - call faucet which returns 5 coin objects - /// - set the gas object (last coin from the list of the 5 objects returned by faucet) + /// - set the gas object (last coin from the list of the 5 objects returned + /// by faucet) /// - return the address, private key, and coins. /// - /// NB! This assumes that these tests run on a network whose faucet returns 5 coins per - /// each faucet request. + /// NB! This assumes that these tests run on a network whose faucet returns + /// 5 coins per each faucet request. async fn helper_setup( tx: &mut TransactionBuilder, client: &Client, @@ -593,20 +586,20 @@ mod tests { (address, pk, coins) } - /// Wait for the transaction to be finalized and indexed. This queries the GraphQL server until - /// it retrieves the requested transaction. + /// Wait for the transaction to be finalized and indexed. This queries the + /// GraphQL server until it retrieves the requested transaction. async fn wait_for_tx(client: &Client, digest: TransactionDigest) { while client.transaction(digest).await.unwrap().is_none() { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } } - /// Wait for the transaction to be finalized and indexed, and check the effects' to ensure the - /// transaction was successfully executed. + /// Wait for the transaction to be finalized and indexed, and check the + /// effects' to ensure the transaction was successfully executed. async fn wait_for_tx_and_check_effects_status_success( client: &Client, digest: TransactionDigest, - effects: Result, sui_graphql_client::error::Error>, + effects: Result, iota_graphql_client::error::Error>, ) { assert!(effects.is_ok(), "Execution failed. Effects: {:?}", effects); // wait for the transaction to be finalized @@ -690,7 +683,8 @@ mod tests { // Check that `0x1::option::is_none` move call works when passing `1` let client = Client::new_localhost(); let mut tx = TransactionBuilder::new(); - // set up the sender, gas object, gas budget, and gas price and return the pk to sign + // set up the sender, gas object, gas budget, and gas price and return the pk to + // sign let (_, pk, _) = helper_setup(&mut tx, &client).await; let function = Function::new( "0x1".parse().unwrap(), @@ -713,7 +707,7 @@ mod tests { let mut tx = TransactionBuilder::new(); let (_, pk, _) = helper_setup(&mut tx, &client).await; - // transfer 1 SUI from Gas coin + // transfer 1 IOTA from Gas coin let amount = tx.input(Serialized(&1_000_000_000u64)); let result = tx.split_coins(tx.gas(), vec![amount]); let recipient_address = Address::generate(rand::thread_rng()); @@ -744,7 +738,7 @@ mod tests { let coin_obj: Input = (&client.object(coin.into(), None).await.unwrap().unwrap()).into(); let coin_input = tx.input(coin_obj.with_owned_kind()); - // transfer 1 SUI + // transfer 1 IOTA let amount = tx.input(Serialized(&1_000_000_000u64)); tx.split_coins(coin_input, vec![amount]); @@ -849,15 +843,15 @@ mod tests { let mut created_objs = vec![]; if let Ok(Some(ref effects)) = effects { match effects { - TransactionEffects::V2(e) => { + TransactionEffects::V1(e) => { for obj in e.changed_objects.clone() { if obj.id_operation == IdOperation::Created { let change = obj.output_state; match change { - sui_types::ObjectOut::PackageWrite { .. } => { + iota_types::ObjectOut::PackageWrite { .. } => { package_id = Some(obj.object_id); } - sui_types::ObjectOut::ObjectWrite { .. } => { + iota_types::ObjectOut::ObjectWrite { .. } => { created_objs.push(obj.object_id); } _ => {} @@ -865,7 +859,6 @@ mod tests { } } } - _ => panic!("Expected V2 effects"), } } wait_for_tx_and_check_effects_status_success(&client, tx.digest(), effects).await; @@ -877,19 +870,20 @@ mod tests { match obj.object_type() { ObjectType::Struct(x) if x.name.to_string() == "UpgradeCap" => { match obj.owner() { - sui_types::Owner::Address(_) => { + iota_types::Owner::Address(_) => { let obj: Input = (&obj).into(); upgrade_cap = Some(tx.input(obj.with_owned_kind())) } - sui_types::Owner::Shared(_) => { - upgrade_cap = Some(tx.input(&obj)) - } - // If the capability is owned by an object, then the module defining the owning - // object gets to decide how the upgrade capability should be used. - sui_types::Owner::Object(_) => { + iota_types::Owner::Shared(_) => upgrade_cap = Some(tx.input(&obj)), + // If the capability is owned by an object, then the module defining the + // owning object gets to decide how the upgrade + // capability should be used. + iota_types::Owner::Object(_) => { panic!("Upgrade capability controlled by object") } - sui_types::Owner::Immutable => panic!("Upgrade capability is stored immutably and cannot be used for upgrades"), + iota_types::Owner::Immutable => panic!( + "Upgrade capability is stored immutably and cannot be used for upgrades" + ), }; break; } diff --git a/crates/sui-transaction-builder/src/unresolved.rs b/crates/iota-transaction-builder/src/unresolved.rs similarity index 97% rename from crates/sui-transaction-builder/src/unresolved.rs rename to crates/iota-transaction-builder/src/unresolved.rs index 2149257b6..f4870e0cf 100644 --- a/crates/sui-transaction-builder/src/unresolved.rs +++ b/crates/iota-transaction-builder/src/unresolved.rs @@ -1,4 +1,4 @@ -use sui_types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; +use iota_types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; // A potentially unresolved user transaction. Note that one can construct a // fully resolved transaction using this type by providing all the required @@ -83,9 +83,8 @@ pub enum InputKind { /// If used in the context of transaction builder, make sure to call /// `tx.resolve` function on the transaction builder to resolve all unresolved /// inputs. -#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[derive(serde::Serialize, serde::Deserialize)] #[serde(rename = "UnresolvedInput")] pub struct Input { #[serde(skip_serializing_if = "Option::is_none")] @@ -314,9 +313,9 @@ impl From for serde_json::Value { } } -impl From<&sui_types::Object> for Input { - fn from(object: &sui_types::Object) -> Self { - use sui_types::Owner; +impl From<&iota_types::Object> for Input { + fn from(object: &iota_types::Object) -> Self { + use iota_types::Owner; let input = Input::by_id(object.object_id()) .with_digest(object.digest()) diff --git a/crates/sui-transaction-builder/tests/test_example_v2/Move.toml b/crates/iota-transaction-builder/tests/test_example_v1/Move.toml similarity index 90% rename from crates/sui-transaction-builder/tests/test_example_v2/Move.toml rename to crates/iota-transaction-builder/tests/test_example_v1/Move.toml index d69022f4f..860df47c1 100644 --- a/crates/sui-transaction-builder/tests/test_example_v2/Move.toml +++ b/crates/iota-transaction-builder/tests/test_example_v1/Move.toml @@ -5,7 +5,7 @@ edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move # authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] [dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } +Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } # For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. # Revision can be a branch, a tag, and a commit hash. @@ -34,4 +34,3 @@ test_example = "0x0" # The dev-addresses section allows overwriting named addresses for the `--test` # and `--dev` modes. # alice = "0xB0B" - diff --git a/crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move b/crates/iota-transaction-builder/tests/test_example_v1/sources/test_example.move similarity index 100% rename from crates/sui-transaction-builder/tests/test_example_v1/sources/test_example.move rename to crates/iota-transaction-builder/tests/test_example_v1/sources/test_example.move diff --git a/crates/sui-transaction-builder/tests/test_example_v1/Move.toml b/crates/iota-transaction-builder/tests/test_example_v2/Move.toml similarity index 90% rename from crates/sui-transaction-builder/tests/test_example_v1/Move.toml rename to crates/iota-transaction-builder/tests/test_example_v2/Move.toml index d69022f4f..860df47c1 100644 --- a/crates/sui-transaction-builder/tests/test_example_v1/Move.toml +++ b/crates/iota-transaction-builder/tests/test_example_v2/Move.toml @@ -5,7 +5,7 @@ edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move # authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] [dependencies] -Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" } +Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } # For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. # Revision can be a branch, a tag, and a commit hash. @@ -34,4 +34,3 @@ test_example = "0x0" # The dev-addresses section allows overwriting named addresses for the `--test` # and `--dev` modes. # alice = "0xB0B" - diff --git a/crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move b/crates/iota-transaction-builder/tests/test_example_v2/sources/test_example.move similarity index 100% rename from crates/sui-transaction-builder/tests/test_example_v2/sources/test_example.move rename to crates/iota-transaction-builder/tests/test_example_v2/sources/test_example.move diff --git a/crates/sui-crypto/CHANGELOG.md b/crates/sui-crypto/CHANGELOG.md deleted file mode 100644 index 17b00dece..000000000 --- a/crates/sui-crypto/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# [0.0.2] - 2025-01-06 - -## Added - -- Added support for multisig verification and aggregation ([#25]) -- Added blanket implementation for SuiSigner and SuiVerifier ([`bc481a1`]) -- Added support for der and pem format for public and private keys ([`df32a46`]) -- Added a `SimpleKeypair` type which could be either an ed25519, secp256k1, or secp256r1 keypair ([`8d64c06`]) -- Added support for verifying passkey authenticators ([#81]) - -[#25]: https://github.com/mystenlabs/sui-rust-sdk/pull/25 -[`bc481a1`]: https://github.com/mystenlabs/sui-rust-sdk/commit/bc481a1ea156e6ccb528b5b49e62a511be5ba60a -[`df32a46`]: https://github.com/mystenlabs/sui-rust-sdk/commit/df32a46bfbecbbbf4ec7e9c1974eef0916ccd359 -[`8d64c06`]: https://github.com/mystenlabs/sui-rust-sdk/commit/8d64c06628b9494c674c27158ce74036fe45080e -[#81]: https://github.com/MystenLabs/sui-rust-sdk/pull/81 - -# [0.0.1] - 2024-09-25 - -Initial release - -[0.0.2]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-crypto-0.0.2 -[0.0.1]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-crypto-0.0.1 diff --git a/crates/sui-crypto/README.md b/crates/sui-crypto/README.md deleted file mode 100644 index 84a02d5b8..000000000 --- a/crates/sui-crypto/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# sui-crypto - -[![sui-crypto on crates.io](https://img.shields.io/crates/v/sui-crypto)](https://crates.io/crates/sui-crypto) -[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-crypto) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_crypto/) - -The `sui-crypto` crate provides the interface for signing and verifying -transactions and messages in the Sui ecosystem. diff --git a/crates/sui-graphql-client-build/Cargo.toml b/crates/sui-graphql-client-build/Cargo.toml deleted file mode 100644 index 583763ea3..000000000 --- a/crates/sui-graphql-client-build/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "sui-graphql-client-build" -version = "0.1.0" -edition = "2021" - -[dependencies] -cynic-codegen = "3.8.0" - diff --git a/crates/sui-graphql-client/build.rs b/crates/sui-graphql-client/build.rs deleted file mode 100644 index 1ad185a9b..000000000 --- a/crates/sui-graphql-client/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Register Sui RPC schema for creating structs for queries -fn main() { - sui_graphql_client_build::register_schema("rpc"); -} diff --git a/crates/sui-graphql-client/queries/objects.graphql b/crates/sui-graphql-client/queries/objects.graphql deleted file mode 100644 index c287294c8..000000000 --- a/crates/sui-graphql-client/queries/objects.graphql +++ /dev/null @@ -1,20 +0,0 @@ -query ObjectsQuery($after: String, $before: String, $filter: ObjectFilter, $first: Int, $last: Int) { - objects( - after: $after, - before: $before, - filter: $filter, - first: $first, - last: $last - ) { - pageInfo { - endCursor - hasNextPage - hasPreviousPage - startCursor - } - nodes { - bcs - } - } -} - diff --git a/crates/sui-graphql-client/src/query_types/mod.rs b/crates/sui-graphql-client/src/query_types/mod.rs deleted file mode 100644 index 080800e1e..000000000 --- a/crates/sui-graphql-client/src/query_types/mod.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -mod active_validators; -mod balance; -mod chain; -mod checkpoint; -mod coin; -mod dry_run; -mod dynamic_fields; -mod epoch; -mod events; -mod execute_tx; -mod normalized_move; -mod object; -mod packages; -mod protocol_config; -mod service_config; -mod suins; -mod transaction; - -pub use active_validators::ActiveValidatorsArgs; -pub use active_validators::ActiveValidatorsQuery; -pub use active_validators::EpochValidator; -pub use active_validators::Validator; -pub use active_validators::ValidatorConnection; -pub use active_validators::ValidatorSet; -pub use balance::Balance; -pub use balance::BalanceArgs; -pub use balance::BalanceQuery; -pub use balance::Owner; -pub use chain::ChainIdentifierQuery; -pub use checkpoint::CheckpointArgs; -pub use checkpoint::CheckpointId; -pub use checkpoint::CheckpointQuery; -pub use checkpoint::CheckpointTotalTxQuery; -pub use checkpoint::CheckpointsArgs; -pub use checkpoint::CheckpointsQuery; -pub use coin::CoinMetadata; -pub use coin::CoinMetadataArgs; -pub use coin::CoinMetadataQuery; -pub use dry_run::DryRunArgs; -pub use dry_run::DryRunQuery; -pub use dry_run::DryRunResult; -pub use dry_run::TransactionMetadata; -pub use dynamic_fields::DynamicFieldArgs; -pub use dynamic_fields::DynamicFieldConnectionArgs; -pub use dynamic_fields::DynamicFieldName; -pub use dynamic_fields::DynamicFieldQuery; -pub use dynamic_fields::DynamicFieldsOwnerQuery; -pub use dynamic_fields::DynamicObjectFieldQuery; -pub use epoch::Epoch; -pub use epoch::EpochSummaryArgs; -pub use epoch::EpochSummaryQuery; -pub use events::Event; -pub use events::EventConnection; -pub use events::EventFilter; -pub use events::EventsQuery; -pub use events::EventsQueryArgs; -pub use execute_tx::ExecuteTransactionArgs; -pub use execute_tx::ExecuteTransactionQuery; -pub use execute_tx::ExecutionResult; -pub use normalized_move::MoveAbility; -pub use normalized_move::MoveFunction; -pub use normalized_move::MoveFunctionTypeParameter; -pub use normalized_move::MoveModule; -pub use normalized_move::MoveVisibility; -pub use normalized_move::NormalizedMoveFunctionQuery; -pub use normalized_move::NormalizedMoveFunctionQueryArgs; -pub use normalized_move::NormalizedMoveModuleQuery; -pub use normalized_move::NormalizedMoveModuleQueryArgs; -pub use normalized_move::OpenMoveType; -pub use object::ObjectFilter; -pub use object::ObjectKey; -pub use object::ObjectQuery; -pub use object::ObjectQueryArgs; -pub use object::ObjectsQuery; -pub use object::ObjectsQueryArgs; -pub use packages::LatestPackageQuery; -pub use packages::MovePackage; -pub use packages::MovePackageVersionFilter; -pub use packages::PackageArgs; -pub use packages::PackageByNameArgs; -pub use packages::PackageByNameQuery; -pub use packages::PackageCheckpointFilter; -pub use packages::PackageQuery; -pub use packages::PackageVersionsArgs; -pub use packages::PackageVersionsQuery; -pub use packages::PackagesQuery; -pub use packages::PackagesQueryArgs; -pub use protocol_config::ProtocolConfigQuery; -pub use protocol_config::ProtocolConfigs; -pub use protocol_config::ProtocolVersionArgs; -pub use service_config::Feature; -pub use service_config::ServiceConfig; -pub use service_config::ServiceConfigQuery; -pub use suins::DefaultSuinsNameQuery; -pub use suins::DefaultSuinsNameQueryArgs; -pub use suins::ResolveSuinsQuery; -pub use suins::ResolveSuinsQueryArgs; -pub use transaction::TransactionBlock; -pub use transaction::TransactionBlockArgs; -pub use transaction::TransactionBlockEffectsQuery; -pub use transaction::TransactionBlockQuery; -pub use transaction::TransactionBlocksEffectsQuery; -pub use transaction::TransactionBlocksQuery; -pub use transaction::TransactionBlocksQueryArgs; -pub use transaction::TransactionsFilter; - -use sui_types::Address; - -use cynic::impl_scalar; -use serde_json::Value as JsonValue; - -use crate::error; - -#[cynic::schema("rpc")] -pub mod schema {} - -// =========================================================================== -// Scalars -// =========================================================================== - -impl_scalar!(Address, schema::SuiAddress); -impl_scalar!(u64, schema::UInt53); -impl_scalar!(JsonValue, schema::JSON); - -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "Base64")] -pub struct Base64(pub String); - -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "BigInt")] -pub struct BigInt(pub String); - -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "DateTime")] -pub struct DateTime(pub String); - -// =========================================================================== -// Types used in several queries -// =========================================================================== - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Address")] -pub struct GQLAddress { - pub address: Address, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveObject")] -pub struct MoveObject { - pub bcs: Option, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveObject")] -pub struct MoveObjectContents { - pub contents: Option, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveValue")] -pub struct MoveValue { - pub type_: MoveType, - pub bcs: Base64, - pub json: Option, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "MoveType")] -pub struct MoveType { - pub repr: String, -} -// =========================================================================== -// Utility Types -// =========================================================================== - -#[derive(Clone, Default, cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "PageInfo")] -/// Information about pagination in a connection. -pub struct PageInfo { - /// When paginating backwards, are there more items? - pub has_previous_page: bool, - /// Are there more items when paginating forwards? - pub has_next_page: bool, - /// When paginating backwards, the cursor to continue. - pub start_cursor: Option, - /// When paginating forwards, the cursor to continue. - pub end_cursor: Option, -} - -impl TryFrom for u64 { - type Error = error::Error; - - fn try_from(value: BigInt) -> Result { - Ok(value.0.parse::()?) - } -} diff --git a/crates/sui-graphql-client/src/query_types/suins.rs b/crates/sui-graphql-client/src/query_types/suins.rs deleted file mode 100644 index 3c4b0b4a1..000000000 --- a/crates/sui-graphql-client/src/query_types/suins.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -// =========================================================================== -// Suins Queries -// =========================================================================== - -use crate::query_types::schema; -use crate::query_types::Address as SdkAddress; - -#[derive(cynic::QueryFragment, Debug)] -#[cynic( - schema = "rpc", - graphql_type = "Query", - variables = "ResolveSuinsQueryArgs" -)] -pub struct ResolveSuinsQuery { - #[arguments(domain: $name)] - pub resolve_suins_address: Option, -} - -#[derive(cynic::QueryVariables, Debug)] -pub struct ResolveSuinsQueryArgs<'a> { - pub name: &'a str, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Address")] -pub struct DomainAddress { - pub address: SdkAddress, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic( - schema = "rpc", - graphql_type = "Query", - variables = "DefaultSuinsNameQueryArgs" -)] -pub struct DefaultSuinsNameQuery { - #[arguments(address: $address)] - pub address: Option, -} - -#[derive(cynic::QueryVariables, Debug)] -pub struct DefaultSuinsNameQueryArgs { - pub address: SdkAddress, -} - -#[derive(cynic::QueryFragment, Debug)] -#[cynic(schema = "rpc", graphql_type = "Address")] -pub struct AddressDefaultSuins { - pub default_suins_name: Option, -} diff --git a/crates/sui-sdk-types/CHANGELOG.md b/crates/sui-sdk-types/CHANGELOG.md deleted file mode 100644 index 2dca6b50b..000000000 --- a/crates/sui-sdk-types/CHANGELOG.md +++ /dev/null @@ -1,32 +0,0 @@ -# [0.0.2] - 2025-01-06 - -## Added - -- Added `proptest::Arbitrary` impls via the `proptest` feature [`6918fd8`] -- Added From impl for TypeTag [#77] - -## Changed - -- Update the passkey challenge format to use the same signing message as other key types ([`c5a25ce`]) -- Flattened the `types` module into the top-level ([`dc54c46`]) -- Folded the `EffectsObjectChange` type into the `ChangedObject` struct ([`aa546ca`]) - -## Removed - -- Removed the `unresolved` module and moved it to the `sui-transaction-builder` crate ([`d965897`]) -- Removed the `schemars` feature ([`bc6dd37`]) - -[`c5a25ce`]: https://github.com/mystenlabs/sui-rust-sdk/commit/c5a25ce356a8cbe42ddcc6ec6bab380007790b44 -[`6918fd8`]: https://github.com/mystenlabs/sui-rust-sdk/commit/6918fd88d40734b8c15fb5c519e9a40aec53eb74 -[#77]: https://github.com/mystenlabs/sui-rust-sdk/pull/77 -[`d965897`]: https://github.com/mystenlabs/sui-rust-sdk/commit/d9658978a4c6e928d036fbedaab9326d5e28de87 -[`dc54c46`]: https://github.com/mystenlabs/sui-rust-sdk/commit/dc54c469f9d006f02d82ec5781d73e8e09ae26ae -[`aa546ca`]: https://github.com/mystenlabs/sui-rust-sdk/commit/aa546ca91249932da3f8e3d55ba6e52e40cd8929 -[`bc6dd37`]: https://github.com/mystenlabs/sui-rust-sdk/commit/bc6dd3732973ed3c1c3ae811a818fc8504a99f0b - -# [0.0.1] - 2024-09-25 - -Initial release - -[0.0.2]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-sdk-types-0.0.2 -[0.0.1]: https://github.com/mystenlabs/sui-rust-sdk/releases/tag/sui-sdk-types-0.0.1 diff --git a/crates/sui-sdk-types/README.md b/crates/sui-sdk-types/README.md deleted file mode 100644 index 8d4f97dee..000000000 --- a/crates/sui-sdk-types/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# sui-sdk-types - -[![sui-sdk-types on crates.io](https://img.shields.io/crates/v/sui-sdk-types)](https://crates.io/crates/sui-sdk-types) -[![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/sui-sdk-types) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://mystenlabs.github.io/sui-rust-sdk/sui_sdk_types/) - -The `sui-sdk-types` crate provides the definitions of the core types that are -part of the public API of the Sui blockchain. diff --git a/crates/sui-sdk-types/src/transaction/fixtures/change-epoch b/crates/sui-sdk-types/src/transaction/fixtures/change-epoch deleted file mode 100644 index b20aebe94..000000000 --- a/crates/sui-sdk-types/src/transaction/fixtures/change-epoch +++ /dev/null @@ -1 +0,0 @@ -AAQBAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCvRQiTAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 b/crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 deleted file mode 100644 index 183522ccd..000000000 --- a/crates/sui-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 +++ /dev/null @@ -1 +0,0 @@ -AAIAAAAAAAAAAAMAAAAAAAAAAPSuRQiTAQAAIMqkGXCaiwgO7vW+HxGGRJKqK/XG/PDv0XQvYDPtrP9BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA== \ No newline at end of file diff --git a/crates/sui-sdk-types/src/transaction/fixtures/genesis b/crates/sui-sdk-types/src/transaction/fixtures/genesis deleted file mode 100644 index a83aae684..000000000 --- a/crates/sui-sdk-types/src/transaction/fixtures/genesis +++ /dev/null @@ -1 +0,0 @@ -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AQcLAAEJAAEHCwUBCQACCwUBCQAHCAoBCwABCQABCwUBCQADBwsFAQkAAwcICgIHCwUBCQALAAEJAAACBwsAAQkACwABCQADBwsAAQkAAwcICgEKCwABCQABBwgKBwkAAgoCCgIKAgsNAQgLBwgKAgsDAQkACwEBCQAICQACCgIKAgoCCw0BCAsBBwgKAwsDAQkACwQBCQALAQEJAAMHCwMBCQADBwgKAgcLAwEJAAMCBwsDAQkACwABCQAEBwgHBwsEAQkABQcICgMGCAcFBggKAQECBggHBQMHCAcHCwQBCQAHCAoCBggHBggKAQYIBwQHCwMBCQADBQcICgMGCwMBCQAHCwEBCQAIDgMGCwMBCQAHCwEBCQAIDAEGCwEBCQABAgEIDgEIDAELDQEICwEJAAEICQIHCwUBCQADAgcLBQEJAAsFAQkAAwMDCgsAAQkAAQYJAAEKAgMLBAEJAAsBAQkACwMBCQABCwEBCQABCAgBCwQBCQABCwIBCQACBwsGAQkAAwIHCwYBCQALBQEJAAEIDwUHCAcDCgIFBwgKBQYIBwMKAgUGCAoEBggHAwoCBQQHCAcDCgIHCAoEBggHAwoCBggKAwYIBwMKAgIJAAUBCAsBCw0BCQAHQmFsYW5jZQRDb2luDENvaW5NZXRhZGF0YQlEZW55Q2FwVjEIRGVueUxpc3QCSUQGT3B0aW9uFVJlZ3VsYXRlZENvaW5NZXRhZGF0YQZTdHJpbmcGU3VwcGx5C1RyZWFzdXJ5Q2FwCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQDVXJsA2FkZBJhbGxvd19nbG9iYWxfcGF1c2UFYXNjaWkHYmFsYW5jZQtiYWxhbmNlX211dARidXJuBGNvaW4UY29pbl9tZXRhZGF0YV9vYmplY3QWY29udGFpbnNfY3VycmVudF9lcG9jaBNjb250YWluc19uZXh0X2Vwb2NoD2NyZWF0ZV9jdXJyZW5jeRxjcmVhdGVfcmVndWxhdGVkX2N1cnJlbmN5X3YxDWNyZWF0ZV9zdXBwbHkIZGVjaW1hbHMPZGVjcmVhc2Vfc3VwcGx5BmRlbGV0ZQ9kZW55X2NhcF9vYmplY3QJZGVueV9saXN0EGRlbnlfbGlzdF92MV9hZGQjZGVueV9saXN0X3YxX2NvbnRhaW5zX2N1cnJlbnRfZXBvY2ggZGVueV9saXN0X3YxX2NvbnRhaW5zX25leHRfZXBvY2ghZGVueV9saXN0X3YxX2Rpc2FibGVfZ2xvYmFsX3BhdXNlIGRlbnlfbGlzdF92MV9lbmFibGVfZ2xvYmFsX3BhdXNlMmRlbnlfbGlzdF92MV9pc19nbG9iYWxfcGF1c2VfZW5hYmxlZF9jdXJyZW50X2Vwb2NoL2RlbnlfbGlzdF92MV9pc19nbG9iYWxfcGF1c2VfZW5hYmxlZF9uZXh0X2Vwb2NoE2RlbnlfbGlzdF92MV9yZW1vdmULZGVzY3JpcHRpb24MZGVzdHJveV96ZXJvFGRpc2FibGVfZ2xvYmFsX3BhdXNlDWRpdmlkZV9pbnRvX24TZW5hYmxlX2dsb2JhbF9wYXVzZQ1mcmVlemVfb2JqZWN0DGZyb21fYmFsYW5jZQxnZXRfZGVjaW1hbHMPZ2V0X2Rlc2NyaXB0aW9uDGdldF9pY29uX3VybAhnZXRfbmFtZQpnZXRfc3ltYm9sFWdldF93aXRoX29yaWdpbmFsX2lkcwhpY29uX3VybAJpZA9pbmNyZWFzZV9zdXBwbHkMaW50b19iYWxhbmNlCmludG9fYnl0ZXMLaW50b19zdHJpbmclaXNfZ2xvYmFsX3BhdXNlX2VuYWJsZWRfY3VycmVudF9lcG9jaCJpc19nbG9iYWxfcGF1c2VfZW5hYmxlZF9uZXh0X2Vwb2NoE2lzX29uZV90aW1lX3dpdG5lc3MEam9pbgRtaW50EW1pbnRfYW5kX3RyYW5zZmVyDG1pbnRfYmFsYW5jZQRuYW1lA25ldwpuZXdfdW5zYWZlBm9iamVjdAZvcHRpb24PcHVibGljX3RyYW5zZmVyA3B1dAZyZW1vdmUEc29tZQVzcGxpdAZzdHJpbmcMc3VwcGx5X2ltbXV0CnN1cHBseV9tdXQMc3VwcGx5X3ZhbHVlBnN5bWJvbAR0YWtlDHRvdGFsX3N1cHBseQh0cmFuc2ZlchR0cmVhc3VyeV9pbnRvX3N1cHBseQp0eF9jb250ZXh0CXR5cGVfbmFtZQV0eXBlcxJ1cGRhdGVfZGVzY3JpcHRpb24PdXBkYXRlX2ljb25fdXJsC3VwZGF0ZV9uYW1lDXVwZGF0ZV9zeW1ib2wDdXJsBHV0ZjgFdmFsdWUEemVybwAAAAAAAAAAAAAAAAAA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b3NrT3duZXJDYXAHTGlzdGluZwRMb2NrBk9wdGlvbgtQdXJjaGFzZUNhcA5UcmFuc2ZlclBvbGljeQ9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0A1VJRANhZGQHYmFsYW5jZQZib3Jyb3cKYm9ycm93X211dApib3Jyb3dfdmFsEmNsb3NlX2FuZF93aXRoZHJhdwRjb2luB2RlZmF1bHQGZGVsZXRlBmRlbGlzdAxkZXN0cm95X3NvbWUNZHluYW1pY19maWVsZBRkeW5hbWljX29iamVjdF9maWVsZARlbWl0BWV2ZW50B2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZQNmb3IMZnJvbV9iYWxhbmNlCmhhc19hY2Nlc3MIaGFzX2l0ZW0SaGFzX2l0ZW1fd2l0aF90eXBlAmlkBGlvdGEMaXNfZXhjbHVzaXZlCWlzX2xpc3RlZBVpc19saXN0ZWRfZXhjbHVzaXZlbHkJaXNfbG9ja2VkB2lzX3NvbWUKaXRlbV9jb3VudAdpdGVtX2lkBWtpb3NrD2tpb3NrX2V4dGVuc2lvbghraW9za19pZBNraW9za19vd25lcl9jYXBfZm9yBGxpc3QWbGlzdF93aXRoX3B1cmNoYXNlX2NhcARsb2NrDWxvY2tfaW50ZXJuYWwJbWluX3ByaWNlA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uBW93bmVyBXBsYWNlDnBsYWNlX2FuZF9saXN0DnBsYWNlX2ludGVybmFsBXByaWNlB3Byb2ZpdHMOcHJvZml0c19hbW91bnQLcHJvZml0c19tdXQIcHVyY2hhc2URcHVyY2hhc2VfY2FwX2l0ZW0ScHVyY2hhc2VfY2FwX2tpb3NrFnB1cmNoYXNlX2NhcF9taW5fcHJpY2URcHVyY2hhc2Vfd2l0aF9jYXADcHV0BnJlbW92ZRByZW1vdmVfaWZfZXhpc3RzE3JldHVybl9wdXJjaGFzZV9jYXAKcmV0dXJuX3ZhbAZzZW5kZXIJc2V0X293bmVyEHNldF9vd25lcl9jdXN0b20Mc2hhcmVfb2JqZWN0BHRha2UIdHJhbnNmZXIPdHJhbnNmZXJfcG9saWN5CnR4X2NvbnRleHQDdWlkEHVpZF9tdXRfYXNfb3duZXIQdWlkX211dF9pbnRlcm5hbAx1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Ny41LjAuMS4uLkxBVC9FMFEvRzBOQUtBVS9TLwwuNi5SLy0uWU5NQSxOOFAWUjlQOlA9UDtWPFBJMEowFy5IME9BLy5QL1ciViICBgsJAQkABwgMAgsCAQkACwEBCQABCwIBCQAAAwsAAQkABQcIDAELAwEJAAILAAEJAAcIDAILCAEJAAsDAQkAAgsIAQkABwgMAgsAAQkACwMBCQACBwsAAQkACwABCQADBwsAAQkAAwcIDAELAAEJAAEHCAwFCBADCw8BBQsPAQsGAQkABggMAwYLAgEJAAsDAQkABwgMBAgQAwULDwEFAwcLAgEJAAsDAQkABwgMAwYLAQEJAAsDAQkABwgMAwcLCQEJAAsDAQkABwgMAwkBBwsDAQkABwgMBQkBBwsCAQkABgsBAQkACQIHCAwCCQEGCwIBCQABBgkCAwkBBwsCAQkABgsBAQkAAQcJAgMHCwIBCQAGCwEBCQAHCAwBCQIBBgsCAQkAAQEEBwsCAQkABgsBAQkACBAHCAwDBwsJAQkAAwcIDAIHCwkBCQALAAEJAAMHCwIBCQAHCwkBCQAHCAwBAwIGCwIBCQAGCBABCw4BCBEBBgsAAQkAAQgQAQYLAwEJAAEFAQsPAQUBCw8BAwELBAEJAAILAQEJAAsCAQkAAQgLAQkAAQsGAQkAAggQCw4BCBEBCw0CCQAJAQEGCQABCAoBCwUBCQABBgsGAQkAAgkABQELDwEJAAMDCwYBCQAICwILBgEJAAcIDAELCAEJAAEGCwgBCQACCwYBCQAICwIHCwYBCQALBgEJAAIHCwYBCQADAQYIDAYIEAMLDwEFCw8BCwYBCQAFCw4BCBEBCBEBCw4BCQALCggRAwsOAQgRAwgQCw8BBQYIEQYKCBEDBQsPAQsGAQkAAQYLDwEJAAIGCw0CCQAJAQYJAAEGCQEBCgkAAgYLDgEJAAYJAAEHCw8BCQAFAwgQCw8BBQULDwELBgEJAAEHCwkBCQABBwsHAQkAAgcLBwEJAAsGAQkAAQkBAgcLDgEJAAkAAgsEAQkBCQIDBwgLCQAJAQMJAAkBCQICBggLCQACBwgLCQABBwkBAQsEAQkBAwcLDQIJAAkBCQAJAQIHCw0CCQAJAQYJAAIJAAkBAggRBwsOAQgRAgcLDgEJAAYJAAIHCwcBCQADAgMLBgEJAAEKAg1BY3Rpb25SZXF1ZXN0B0JhbGFuY2UEQ29pbgJJRAZPcHRpb24HUnVsZUtleQZTdHJpbmcGU3VwcGx5BVRva2VuC1Rva2VuUG9saWN5DlRva2VuUG9saWN5Q2FwElRva2VuUG9saWN5Q3JlYXRlZAtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEBlZlY01hcAZWZWNTZXQGYWN0aW9uA2FkZAxhZGRfYXBwcm92YWwPYWRkX3J1bGVfY29uZmlnE2FkZF9ydWxlX2Zvcl9hY3Rpb24FYWxsb3cGYW1vdW50CWFwcHJvdmFscwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0BGJ1cm4EY29pbg9jb25maXJtX3JlcXVlc3QTY29uZmlybV9yZXF1ZXN0X211dBdjb25maXJtX3dpdGhfcG9saWN5X2NhcBljb25maXJtX3dpdGhfdHJlYXN1cnlfY2FwCGNvbnRhaW5zD2RlY3JlYXNlX3N1cHBseQZkZWxldGUMZGVzdHJveV9ub25lDGRlc3Ryb3lfc29tZQxkZXN0cm95X3plcm8IZGlzYWxsb3cNZHluYW1pY19maWVsZARlbWl0BWVtcHR5BWV2ZW50B2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZQdleHRyYWN0BWZsdXNoA2Zvcgxmcm9tX2JhbGFuY2UJZnJvbV9jb2luEGZyb21fY29pbl9hY3Rpb24DZ2V0B2dldF9tdXQPaGFzX3J1bGVfY29uZmlnGWhhc19ydWxlX2NvbmZpZ193aXRoX3R5cGUCaWQPaW5jcmVhc2Vfc3VwcGx5Bmluc2VydAxpbnRvX2JhbGFuY2UJaW50b19rZXlzCmlzX2FsbG93ZWQKaXNfbXV0YWJsZQdpc19ub25lDGlzX3Byb3RlY3RlZAdpc19zb21lBGpvaW4Ea2VlcANrZXkEbWludARuYW1lA25ldwpuZXdfcG9saWN5C25ld19yZXF1ZXN0BG5vbmUGb2JqZWN0Bm9wdGlvbglyZWNpcGllbnQGcmVtb3ZlEnJlbW92ZV9ydWxlX2NvbmZpZxZyZW1vdmVfcnVsZV9mb3JfYWN0aW9uC3J1bGVfY29uZmlnD3J1bGVfY29uZmlnX211dAVydWxlcwZzZW5kZXIMc2hhcmVfb2JqZWN0DHNoYXJlX3BvbGljeQRz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AAAaFg8AABgXEAAAFRgRAAAcGRIAAC8aAQEAATAdCgACHi0KAAMrAR4ABC4jAQEIBQ0cDwAFDwAdAAUtHB0ABgsgIQEEBiQlKQEEBiUmJwEEBjclDwAHMCkKAQAIBy4BAQAIISsTAQAeHxoiIB8fHyQSIhEjEgEHCAQAAQcIAAEHCAEBBggAAQYIAQQHCAADCgIGCAQCBggABwgEAQgCAQcIAgEKAgIHCAINAgcIAgIBDwEEAQMBDgENAQIBAQQHCAIEBAIDBwgCBAQDBwgCAwMDBwgCDg4DBwgCDQ0DBwgCAgICBwgCBwoJAAIIAQMBBggEAQUBCAMBCAEDAwkABwgEAQgFAQgAAQkAAgcIAQMBBggFAQcIBQEHCQACBggBAwEGCQAGAQEBAQMHCAEBBgoJAAIKAgYKAgIGCgIGCgICBwoJAAoJAAMNAwoCAwICDwIPDwUNDQ0DDQZSYW5kb20PUmFuZG9tR2VuZXJhdG9yC1JhbmRvbUlubmVyCVR4Q29udGV4dANVSUQJVmVyc2lvbmVkB2FkZHJlc3MGYXBwZW5kA2JjcwZidWZmZXIHY291bnRlcgZjcmVhdGURZGVyaXZlX25leHRfYmxvY2sFZXBvY2gLZmlsbF9idWZmZXIUZnJlc2hfb2JqZWN0X2FkZHJlc3MNZ2VuZXJhdGVfYm9vbA5nZW5lcmF0ZV9ieXRlcw1nZW5lcmF0ZV91MTI4FmdlbmVyYXRlX3UxMjhfaW5fcmFuZ2UMZ2VuZXJhdGVfdTE2FWdlbmVyYXRlX3UxNl9pbl9yYW5nZQ1nZW5lcmF0ZV91MjU2DGdlbmVyYXRlX3UzMhVnZW5lcmF0ZV91MzJfaW5fcmFuZ2UMZ2VuZXJhdGVfdTY0FWdlbmVyYXRlX3U2NF9pbl9yYW5nZQtnZW5lcmF0ZV91OBRnZW5lcmF0ZV91OF9pbl9yYW5nZQRobWFjDWhtYWNfc2hhM18yNTYCaWQFaW5uZXIIaXNfZW1wdHkKbG9hZF9pbm5lcg5sb2FkX2lubmVyX211dApsb2FkX3ZhbHVlDmxvYWRfdmFsdWVfbXV0DW5ld19nZW5lcmF0b3IGb2JqZWN0BnJhbmRvbQxyYW5kb21fYnl0ZXMQcmFuZG9tbmVzc19yb3VuZBByYW5kb21uZXNzX3N0YXRlBHNlZWQGc2VuZGVyDHNo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ARwfGwABIBYXAAImHAIBDAMsGhsABB0VBAECBhcYGQAGGBgZAAYZAhMBAB8UIhQAFB0BGQ4CCQAHCAYBCAAAAQYIAAEBAQYIBwEGCAEBCAQBAwECAQYIAgEGCAMBBgoCAQcIAQEIAQMHCAECCgIBCAICBwgBCAMCBwgBAgEICAEJAAEGCQABBwgGAQgFAQYICAEIBwEGCAYBBQIJAAUCAQgIAggECAQBBggEAklECVB1Ymxpc2hlcgZTdHJpbmcJVHhDb250ZXh0CFR5cGVOYW1lA1VJRApVcGdyYWRlQ2FwDlVwZ3JhZGVSZWNlaXB0DVVwZ3JhZGVUaWNrZXQPYWRkaXRpdmVfcG9saWN5BWFzY2lpEWF1dGhvcml6ZV91cGdyYWRlDmJ1cm5fcHVibGlzaGVyA2NhcAVjbGFpbQ5jbGFpbV9hbmRfa2VlcA5jb21taXRfdXBncmFkZRFjb21wYXRpYmxlX3BvbGljeQZkZWxldGUPZGVwX29ubHlfcG9saWN5BmRpZ2VzdAtmcm9tX21vZHVsZQxmcm9tX3BhY2thZ2ULZ2V0X2FkZHJlc3MKZ2V0X21vZHVsZRVnZXRfd2l0aF9vcmlnaW5hbF9pZHMCaWQPaWRfZnJvbV9hZGRyZXNzDWlkX3RvX2FkZHJlc3MTaXNfb25lX3RpbWVfd2l0bmVzcw5tYWtlX2ltbXV0YWJsZQttb2R1bGVfbmFtZQNuZXcGb2JqZWN0Fm9ubHlfYWRkaXRpdmVfdXBncmFkZXMRb25seV9kZXBfdXBncmFkZXMHcGFja2FnZQZwb2xpY3kPcHVibGljX3RyYW5zZmVyEHB1Ymxpc2hlZF9tb2R1bGURcHVibGlzaGVkX3BhY2thZ2ULcmVjZWlwdF9jYXAPcmVjZWlwdF9wYWNrYWdlCHJlc3RyaWN0BnNlbmRlcg10aWNrZXRfZGlnZXN0DnRpY2tldF9wYWNrYWdlDXRpY2tldF9wb2xpY3kIdHJhbnNmZXIKdHhfY29udGV4dAl0eXBlX25hbWUFdHlwZXMPdXBncmFkZV9wYWNrYWdlDnVwZ3JhZGVfcG9saWN5B3ZlcnNpb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAA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b2luTWV0YWRhdGEaTWV0YWRhdGFPd25lcnNoaXBSZW5vdW5jZWQGT3B0aW9uBlN0cmluZwZTdXBwbHkLVHJlYXN1cnlDYXAaVHJlYXN1cnlPd25lcnNoaXBSZW5vdW5jZWQJVHhDb250ZXh0CFR5cGVOYW1lA1VJRANVcmwDYWRkF2FkZF9hZGRpdGlvbmFsX21ldGFkYXRhE2FkZGl0aW9uYWxfbWV0YWRhdGEFYXNjaWkQYXZhaWxhYmxlX3N1cHBseQdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0BGJ1cm4EY29pbgxjb2luX21hbmFnZXIJY29pbl9uYW1lBmNyZWF0ZQ9jcmVhdGVfY3VycmVuY3kIZGVjaW1hbHMGZGVsZXRlC2Rlc2NyaXB0aW9uDWR5bmFtaWNfZmllbGQEZW1pdBZlbmZvcmNlX21heGltdW1fc3VwcGx5BWV2ZW50B2V4aXN0c18EZmlsbANnZXQMZ2V0X2RlY2ltYWxzD2dldF9kZXNjcmlwdGlvbgxnZXRfaWNvbl91cmwIZ2V0X25hbWUKZ2V0X3N5bWJvbBBnZXRfd2l0aF9kZWZhdWx0Emhhc19tYXhpbXVtX3N1cHBseQhpY29uX3VybAJpZBJpbW11dGFibGVfbWV0YWRhdGELaW50b19zdHJpbmcHaXNfbm9uZQdpc19zb21lDm1heGltdW1fc3VwcGx5CG1ldGFkYXRhEm1ldGFkYXRhX2ltbXV0YWJsZRVtZXRhZGF0YV9pc19pbW11dGFibGUEbWludBFtaW50X2FuZF90cmFuc2ZlcgxtaW50X2JhbGFuY2UEbmFtZQNuZXcbbmV3X3dpdGhfaW1tdXRhYmxlX21ldGFkYXRhBG5vbmUGb2JqZWN0Bm9wdGlvbgZyZW1vdmUbcmVub3VuY2VfbWV0YWRhdGFfb3duZXJzaGlwG3Jlbm91bmNlX3RyZWFzdXJ5X293bmVyc2hpcBtyZXBsYWNlX2FkZGl0aW9uYWxfbWV0YWRhdGEEc29tZQZzdHJpbmcMc3VwcGx5X2ltbXV0EHN1cHBseV9pbW11dGFibGUTc3VwcGx5X2lzX2ltbXV0YWJsZQRzd2FwBnN5bWJvbAx0b3RhbF9zdXBwbHkMdHJlYXN1cnlfY2FwCnR4X2NvbnRleHQJdHlwZV9uYW1lEnVw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BwsNAQkACQACBggKCQABBgkBAgcLBgEJAAsHAQkAAQsFAQkBAggPBwsNAQgPAgcICgkAAgcLDQEJAAYJAAdCYWxhbmNlBENvaW4CSUQESU9UQQZPcHRpb24JUHVibGlzaGVyB1J1bGVLZXkOVHJhbnNmZXJQb2xpY3kRVHJhbnNmZXJQb2xpY3lDYXAVVHJhbnNmZXJQb2xpY3lDcmVhdGVkF1RyYW5zZmVyUG9saWN5RGVzdHJveWVkD1RyYW5zZmVyUmVxdWVzdAlUeENvbnRleHQIVHlwZU5hbWUDVUlEBlZlY1NldANhZGQLYWRkX3JlY2VpcHQIYWRkX3J1bGUOYWRkX3RvX2JhbGFuY2UHYmFsYW5jZQZib3Jyb3cEY29pbg9jb25maXJtX3JlcXVlc3QIY29udGFpbnMHZGVmYXVsdAZkZWxldGUUZGVzdHJveV9hbmRfd2l0aGRyYXcMZGVzdHJveV9zb21lC2R1bW15X2ZpZWxkDWR5bmFtaWNfZmllbGQEZW1pdAVlbXB0eQVldmVudAdleGlzdHNfBGZyb20MZnJvbV9iYWxhbmNlDGZyb21fcGFja2FnZQNnZXQIZ2V0X3J1bGUIaGFzX3J1bGUCaWQGaW5zZXJ0CWludG9fa2V5cwRpb3RhB2lzX3NvbWUEaXRlbQNuZXcLbmV3X3JlcXVlc3QGb2JqZWN0Bm9wdGlvbgdwYWNrYWdlBHBhaWQJcG9saWN5X2lkA3B1dAhyZWNlaXB0cwZyZW1vdmULcmVtb3ZlX3J1bGUFcnVsZXMGc2VuZGVyDHNoYXJlX29iamVjdARzaXplBHRha2UIdHJhbnNmZXIPdHJhbnNmZXJfcG9saWN5CnR4X2NvbnRleHQJdHlwZV9uYW1lA3VpZBB1aWRfbXV0X2FzX293bmVyDHVpZF90b19pbm5lcgV2YWx1ZQd2ZWNfc2V0CHdpdGhkcmF3BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAAIELggJNAMjCAk3Cw0BCA8BAgMpCAoUCwYBCAg6Cw0BCA8CAgIp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DgEADikUDgEAAwgTExIXERcSBhEGBAgICAgIBwgBCggACAILDQEIDggJBwgKAAUHCAcKCAMHCggMCw0BCA4HCAoBBwoIDBYKCAMKAgMDCgIDAwoCBQoCCgIKAgoCAwoCCgIKAgoCCwQBCAYICwgMCggMAQYICgEDAQYIBwEIBgELBAEJAAEIDAEIAA8FCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgMDBwgKAgYKCAwGCAwBAQcDAwMDAwMHCAoBCAsJCAgIBwoIDAsEAQgGAwMICwgJBwgKCAsEAQgGAwULDQEDCw0BBQMHCAwFAQgDAQYKCQADBwgHAwYICgELBAEIBgEFAQYLDQEJAAELDQEJAAEJAAIHCggMBQEHCAwGBwgMCwQBCAYFAwsNAQgOBwgKBAcIDAsEAQgGBQcICgILBAEJAAcICgELBQEJAAILBQEIBgUCAwMCBwgMAwdCYWxhbmNlBENvaW4WR2VuZXNpc0NoYWluUGFyYW1ldGVycxhHZW5lc2lzVmFsaWRhdG9yTWV0YWRhdGEESU9UQRJJb3RhU3lzdGVtQWRtaW5DYXAPSW90YVRyZWFzdXJ5Q2FwBk9wdGlvbgZTdHJpbmcSU3lzdGVtUGFyYW1ldGVyc1YxD1Rva2VuQWxsb2NhdGlvbhlUb2tlbkRpc3RyaWJ1dGlvblNjaGVkdWxlCVR4Q29udGV4dANVSUQLVmFsaWRhdG9yVjEIYWN0aXZhdGUTYWN0aXZhdGVfdmFsaWRhdG9ycw9hbGxvY2F0ZV90b2tlbnMLYWxsb2NhdGlvbnMMYW1vdW50X25hbm9zFGF1dGhvcml0eV9wdWJsaWNfa2V5B2JhbGFuY2UYY2hhaW5fc3RhcnRfdGltZXN0YW1wX21zBGNvaW4PY29tbWlzc2lvbl9yYXRlBmNyZWF0ZRhjcmVhdGVfc3lzdGVtX3BhcmFtZXRlcnMLZGVzY3JpcHRpb24MZGVzdHJveV9zb21lBWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zDGZyb21fYmFsYW5jZQlnYXNfcHJpY2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwEaW90YQxpb3RhX2FkZHJlc3MLaW90YV9zeXN0ZW0XaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlDG1pbnRfYmFsYW5jZQRuYW1lD25ldHdvcmtfYWRkcmVzcxJuZXR3b3JrX3B1YmxpY19rZXkDbmV3Bm9iamVjdAZvcHRpb24LcDJwX2FkZHJlc3MRcHJlX21pbnRlZF9zdXBwbHkPcHJpbWFyeV9hZGRyZXNzC3Byb2plY3RfdXJsE3Byb29mX29mX3Bvc3Nlc3Npb24TcHJvdG9jb2xfcHVibGljX2tleRBwcm90b2NvbF92ZXJzaW9uEXJlY2lwaWVudF9hZGRyZXNzHHJlcXVlc3RfYWRkX3N0YWtlX2F0X2dlbmVzaXMfc3Rha2VkX3dpdGhfdGltZWxvY2tfZXhwaXJhdGlvbhVzdGFrZWRfd2l0aF92YWxpZGF0b3IGc3RyaW5nEHN5c3RlbV9hZG1pbl9jYXASdGltZWxvY2tlZF9zdGFraW5nDHRvdGFsX3N1cHBseQh0cmFuc2Zlcgp0eF9jb250ZXh0CXZhbGlkYXRvciB2YWxpZGF0b3JfbG93X3N0YWtlX2dyYWNlX3BlcmlvZB12YWxpZGF0b3JfbG93X3N0YWtlX3RocmVzaG9sZA12YWxpZGF0b3Jfc2V0InZhbGlkYXRvcl92ZXJ5X2xvd19zdGFrZV90aHJlc2hvbGQGdmVjdG9yBHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAAAAg4uCgIbCgIjCgI3CgIlBSADGAMUCgI4CgIwCgI5CgIvCgI0CgI2CgIBAgg6AxYDHgMrAywDRwNJA0YDAgICNQMSCggDAwIEOwUTAz4LDQEFPQsNAQMAAAAABIgBCgcuEQgGAAAAAAAAAAAhBAcFCwsHAQcAJwsEEwIMCAwVDgERBgsVIQQVBRkLBwEHAic4AAwaQAoAAAAAAAAAAAwdDgNBCwwLBgAAAAAAAAAADA4KDgoLIwRcDgMKDkILFBMADBYMFAwSDBkM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CVR4Q29udGV4dBVVbnN0YWtpbmdSZXF1ZXN0RXZlbnQDVXJsE1ZhbGlkYXRvck1ldGFkYXRhVjEVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwC1ZhbGlkYXRvclYxCGFjdGl2YXRlFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBphZGp1c3Rfc3Rha2VfYW5kX2dhc19wcmljZQZhbW91bnQFYXNjaWkWYXV0aG9yaXR5X3B1YmtleV9ieXRlcwNiYWcHYmFsYW5jZQNiY3MGYm9ycm93D2NvbW1pc3Npb25fcmF0ZQpkZWFjdGl2YXRlF2RlYWN0aXZhdGVfc3Rha2luZ19wb29sD2RlcG9zaXRfcmV3YXJkcxVkZXBvc2l0X3N0YWtlX3Jld2FyZHMLZGVzY3JpcHRpb24aZWZmZWN0dWF0ZV9zdGFnZWRfbWV0YWRhdGEEZW1pdAVlcG9jaAVldmVudAxleHRyYV9maWVsZHMHZXh0cmFjdApmcm9tX2FzY2lpCWdhc19wcmljZQdnZW5lc2lzFGdldF9zdGFraW5nX3Bvb2xfcmVmAmlkCWltYWdlX3VybARpb3RhDGlvdGFfYWRkcmVzcwxpb3RhX2JhbGFuY2UXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIhbmV4dF9lcG9jaF9hdXRob3JpdHlfcHVia2V5X2J5dGVzGm5leHRfZXBvY2hfY29tbWlzc2lvbl9yYXRlFG5leHRfZXBvY2hfZ2FzX3ByaWNlFm5leHRfZXBvY2hfbmV0X2FkZHJlc3MabmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleV9ieXRlcxZuZXh0X2Vwb2NoX3AycF9hZGRyZXNzGm5leHRfZXBvY2hfcHJpbWFyeV9hZGRyZXNzHm5leHRfZXBvY2hfcHJvb2Zfb2ZfcG9zc2Vzc2lvbiBuZXh0X2Vwb2NoX3Byb3RvY29sX3B1YmtleV9ieXRlcxBuZXh0X2Vwb2NoX3N0YWtlBG5vbmUGb2JqZWN0EG9wZXJhdGlvbl9jYXBfaWQGb3B0aW9uC3AycF9hZGRyZXNzFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50B3Bvb2xfaWQhcG9vbF90b2tlbl9leGNoYW5nZV9yYXRlX2F0X2Vwb2NoD3ByaW1hcnlfYWRkcmVzcxBwcmluY2lwYWxfYW1vdW50FXByb2Nlc3NfcGVuZGluZ19zdGFrZSRwcm9jZXNzX3BlbmRpbmdfc3Rha2VzX2FuZF93aXRoZHJhd3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhVwcm90b2NvbF9wdWJrZXlfYnl0ZXMPcHVibGljX3RyYW5zZmVyEXJlcXVlc3RfYWRkX3N0YWtlHHJlcXVlc3RfYWRkX3N0YWtlX2F0X2dlbmVzaXMpcmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpc193aXRoX3JlY2VpcHQbcmVxdWVzdF9zZXRfY29tbWlzc2lvbl9yYXRlFXJlcXVlc3Rfc2V0X2dhc19wcmljZRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlDXJld2FyZF9hbW91bnQGc2VuZGVyHXNldF9jYW5kaWRhdGVfY29tbWlzc2lvbl9yYXRlF3NldF9jYW5kaWRhdGVfZ2FzX3ByaWNlEHNldF92b3RpbmdfcG93ZXIEc29tZRZzdGFrZV9hY3RpdmF0aW9uX2Vwb2NoDHN0YWtlX2Ftb3VudBJzdGFrZWRfaW90YV9hbW91bnQOc3Rha2VyX2FkZHJlc3MMc3Rha2luZ19wb29sD3N0YWtpbmdfcG9vbF9pZAZzdHJpbmcSdGltZWxvY2tlZF9zdGFraW5nCHRvX2J5dGVzC3RvdGFsX3N0YWtlEnRvdGFsX3N0YWtlX2Ftb3VudAh0cmFuc2Zlcgp0eF9jb250ZXh0D3Vuc3Rha2luZ19lcG9jaCF1cGRhdGVfY2FuZGlkYXRlX2F1dGhvcml0eV9wdWJrZXkgdXBkYXRlX2NhbmRpZGF0ZV9uZXR3b3JrX2FkZHJlc3MfdXBkYXRlX2NhbmRpZGF0ZV9uZXR3b3JrX3B1YmtleRx1cGRhdGVfY2FuZGlkYXRlX3AycF9hZGRyZXNzIHVwZGF0ZV9jYW5kaWRhdGVfcHJpbWFyeV9hZGRyZXNzIHVwZGF0ZV9jYW5kaWRhdGVfcHJvdG9jb2xfcHVia2V5EnVwZGF0ZV9kZXNjcmlwdGlvbhB1cGRhdGVfaW1hZ2VfdXJsC3VwZGF0ZV9uYW1lInVwZGF0ZV9uZXh0X2Vwb2NoX2F1dGhvcml0eV9wdWJrZXkhdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzIHVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5HXVwZGF0ZV9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzIXVwZGF0ZV9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkSdXBkYXRlX3Byb2plY3RfdXJsA3VybBF2YWxpZGF0ZV9tZXRhZGF0YRV2YWxpZGF0ZV9tZXRhZGF0YV9iY3MJdmFsaWRhdG9yEXZhbGlkYXRvcl9hZGRyZXNzDXZhbGlkYXRvcl9jYXANdmFsaWRhdG9yX3NldAV2YWx1ZR52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MMdm90aW5nX3Bvd2VyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAMICgAAAAAAAAADCAsAAAAAAAAAAwhkAAAAAAAAAAMIZQAAAAAAAAADCGYAAAAAAAAAAwjQBwAAAAAAAAMIAAEAAAAAAAADCKCGAQAAAAAAAAIULQUVCgI6CgJa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CgIKAgoCCgIKAgoCCgIKAgoCAwMHCAkCBwgLBwgJAgcICwYICQMHCAsGCBADAwcICwMGCAkBBggJAQUCCQAFBAcICwsCAQgDBQcICQUHCAsKCwIBCAMLEQEDBQcICQEIAwILAQEJAAcICQELAgEJAAELAgEIAwMHCAsIDgYICQMHCAsGCBAFAwcICwoCBggJBAcICwoCCgIGCAkCBwgLBggFCwcICwMDAwsBAQgDCwEBCAMDAwMDBwgJAgcIBgkAAQcJAQdCYWxhbmNlBENvaW4CSUQESU9UQRJJb3RhU3lzdGVtQWRtaW5DYXAPSW90YVN5c3RlbVN0YXRlEUlvdGFTeXN0ZW1TdGF0ZVYxD0lvdGFUcmVhc3VyeUNhcAZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlClN0YWtlZElvdGESU3lzdGVtUGFyYW1ldGVyc1YxBVRhYmxlCVR4Q29udGV4dANVSUQfVW52ZXJpZmllZFZhbGlkYXRvck9wZXJhdGlvbkNhcAtWYWxpZGF0b3JWMQZWZWNNYXAaYWN0aXZlX3ZhbGlkYXRvcl9hZGRyZXNzZXMeYWN0aXZlX3ZhbGlkYXRvcl92b3RpbmdfcG93ZXJzA2FkZA1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UKYm9ycm93X211dARjb2luBmNyZWF0ZQ1keW5hbWljX2ZpZWxkDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24VZ2V0X3RvdGFsX2lvdGFfc3VwcGx5AmlkBGlvdGELaW90YV9zeXN0ZW0VaW90YV9zeXN0ZW1fYWRtaW5fY2FwF2lvdGFfc3lzdGVtX3N0YXRlX2lubmVyGGxvYWRfaW5uZXJfbWF5YmVfdXBncmFkZRpsb2FkX2lvdGFfc3lzdGVtX2FkbWluX2NhcBFsb2FkX3N5c3RlbV9zdGF0ZRVsb2FkX3N5c3RlbV9zdGF0ZV9tdXQGb2JqZWN0Bm9wdGlvbhNwb29sX2V4Y2hhbmdlX3JhdGVzD3B1YmxpY190cmFuc2ZlchByZXBvcnRfdmFsaWRhdG9yEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luG3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkUcm90YXRlX29wZXJhdGlvbl9jYXAGc2VuZGVyJ3NldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2NvbW1pc3Npb25fcmF0ZSFzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9nYXNfcHJpY2UMc2hhcmVfb2JqZWN0DHN0YWtpbmdfcG9vbBBzeXN0ZW1fYWRtaW5fY2FwFHN5c3RlbV9zdGF0ZV92ZXJzaW9uBXRhYmxlEnRpbWVsb2NrZWRfc3Rha2luZwh0cmFuc2Zlcgp0eF9jb250ZXh0FXVuZG9fcmVwb3J0X3ZhbGlkYXRvcit1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9hdXRob3JpdHlfcHVia2V5KnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfYWRkcmVzcyl1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9uZXR3b3JrX3B1YmtleSZ1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wMnBfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcmltYXJ5X2FkZHJlc3MqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcHJvdG9jb2xfcHVia2V5HHVwZGF0ZV92YWxpZGF0b3JfZGVzY3JpcHRpb24adXBkYXRlX3ZhbGlkYXRvcl9pbWFnZV91cmwVdXBkYXRlX3ZhbGlkYXRvcl9uYW1lLHVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9hdXRob3JpdHlfcHVia2V5K3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MqdXBkYXRlX3ZhbGlkYXRvcl9u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BggJAQcIAAQHCAADAwMCBwgAAwEGCAABAwEGCAIBCAYBAQMHCAIDBwgJAgcIAggCAgYIAgYIAgIGCAADAQgBAQYLCAIDCAEBBggBAgYIAQMBCwgCAwgBAgMIAQELCAIJAAkBAQgHAQsKAQkAAQgFAQsEAQkAAQgDAgMIAgEGCwQBCQABBgkABgMLBAEIBQsEAQgFAwsEAQgFAwEGCAkCBwsEAQkACwQBCQACCAELBAEIBQMHCwgCCQAJAQkACQEEAwgBAwMCAwMCBwsEAQkAAwIHCwoBCQAJAAEJAAEGCwoBCQABBQIJAAUCBgsKAQkACQACBgsIAgkACQEJAAEGCQEDAwgBAwNCYWcHQmFsYW5jZQJJRARJT1RBBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUKU3Rha2VkSW90YQ1TdGFraW5nUG9vbFYxBVRhYmxlCVR4Q29udGV4dANVSUQVYWN0aXZhdGVfc3Rha2luZ19wb29sEGFjdGl2YXRpb25fZXBvY2gDYWRkA2JhZwdiYWxhbmNlBmJvcnJvdxhjaGVja19iYWxhbmNlX2ludmFyaWFudHMIY29udGFpbnMXZGVhY3RpdmF0ZV9zdGFraW5nX3Bvb2wSZGVhY3RpdmF0aW9uX2Vwb2NoBmRlbGV0ZQ9kZXBvc2l0X3Jld2FyZHMFZXBvY2gOZXhjaGFuZ2VfcmF0ZXMMZXh0cmFfZmllbGRzBGZpbGwPZ2V0X2lvdGFfYW1vdW50EGdldF90b2tlbl9hbW91bnQQZ2V0X3dpdGhfZGVmYXVsdAJpZBVpbml0aWFsX2V4Y2hhbmdlX3JhdGUEaW90YQtpb3RhX2Ftb3VudAxpb3RhX2JhbGFuY2UZaXNfZXF1YWxfc3Rha2luZ19tZXRhZGF0YQtpc19pbmFjdGl2ZQdpc19ub25lDGlzX3ByZWFjdGl2ZRVpc19wcmVhY3RpdmVfYXRfZXBvY2gHaXNfc29tZQRqb2luEGpvaW5fc3Rha2VkX2lvdGEDbWluA25ldwRub25lBm9iamVjdAZvcHRpb24bcGVuZGluZ19wb29sX3Rva2VuX3dpdGhkcmF3DXBlbmRpbmdfc3Rha2UUcGVuZGluZ19zdGFrZV9hbW91bnQdcGVuZGluZ19zdGFrZV93aXRoZHJhd19hbW91bnQbcGVuZGluZ190b3RhbF9pb3RhX3dpdGhkcmF3B3Bvb2xfaWQRcG9vbF90b2tlbl9hbW91bnQScG9vbF90b2tlbl9iYWxhbmNlIXBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZV9hdF9lcG9jaAlwcmluY2lwYWwVcHJvY2Vzc19wZW5kaW5nX3N0YWtlHnByb2Nlc3NfcGVuZGluZ19zdGFrZV93aXRoZHJhdyRwcm9jZXNzX3BlbmRpbmdfc3Rha2VzX2FuZF93aXRoZHJhd3MRcmVxdWVzdF9hZGRfc3Rha2UWcmVxdWVzdF93aXRoZHJhd19zdGFrZQxyZXdhcmRzX3Bvb2wGc2VuZGVyBHNvbWUFc3BsaXQRc3BsaXRfc3Rha2VkX2lvdGEWc3Rha2VfYWN0aXZhdGlvbl9lcG9jaBJzdGFrZWRfaW90YV9hbW91bnQMc3Rha2luZ19wb29sBXRhYmxlCHRyYW5zZmVyCnR4X2NvbnRleHQDdTY0EnVud3JhcF9zdGFrZWRfaW90YQl2YWxpZGF0b3INdmFsaWRhdG9yX3NldAV2YWx1ZRd3aXRoZHJhd19mcm9tX3ByaW5jaXBhbBB3aXRoZHJhd19yZXdhcmRzBHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCADKmjsAAAAAAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAMICgAAAAAAAAADCAsAAAAAAAAAAwgMAAAAAAAAAAMIDQAAAAAAAAADCA4AAAAAAAAAAwgPAAAAAAAAAAMIEAAAAAAAAAADCBEAAAAAAAAAAwgSAAAAAAAAAAACCx4IBwwLCgEDFAsKAQMiAz8LBAEIBTcDGAsIAgMIATEDNAMwAxkIAwECAiEDNgMCAgQeCAc1CAZEAzkLBAEIBQADAAAbEgoAOAAMAQoAESg4ATgBBgAAAAAAAAAAOAIGAAAAAAAAAAALAQYAAAAAAAAAAAYAAAAAAAAAAAYAAAAAAAAAAAsAESESAAIBAwAAIy0OATgDDAQKAC4RESAECQUPCwABCwMBBwsnCgQGAAAAAAAAAAAkBBQFGgsAAQsDAQcSJwsDESgKAC44BAsCCwESAgwFCgAQABQLBBYLAA8AFQsFAgIDAAAmSw4BEAEUCgIRLiQEFwsCAQsBEQQMBAoAEAAUDgQ4AxcLAA8AFQsEAgoALgsBEQMMBQwDDgU4AwwGCgAKBgoDCwIRLhEJDAcLBg4HOAMWDAgKABACFAsIFgoADwIVCgAQAxQLAxYKAA8DFQoALhERBEMLABEHBUULAAENBQsHOAUBCwUCAwMAACkbDgEQBBQKADgEIQQIBQwLAAEHAicLAA4BEAEUERYMAgsBEQQMAw4CDgM4AxEeCwMCBAAAAAUICwATAgwBAQERJgsBAgUDAAAJDwoAEAUUDgE4AxYKAA8FFQsADwYLATgFAQIGAwAADxkLAREuBgEAAAAAAAAAFgwCCgARBwoAEQgKAA8HCgIKABAFFAoAEAgUEgE4BgsALgsCESACBwAAAAkdCgAQBRQKABACFBcKAA8FFQoAEAgUCgAQAxQXCgAPCBUGAAAAAAAAAAAKAA8CFQYAAAAAAAAAAAsADwMVAggDAAAXHwoAEAUUCgAQCBQSAQwBCgAQBRQKABAAFBYKAA8FFQ4BCgAQBRQRHgoADwgVBgAAAAAAAAAACwAPABUCCQAAACsfCgAuCwMRFgwFDgULAhEdDAcKBwoBJgQSCwcLARcMBAUUBgAAAAAAAAAADAQLBAoAEAY4AxE3DAYLAA8GCwY4BwIKAwAACR0KAA8HCgERHzgGCgAuERAECgUOCwABBw8nCgAuEREgBBQFGAsAAQcRJwsADwkLATgIAgsDAAAJEAoALhERIAQGBQoLAAEHDCcLATgJCwAPChUCDAEAAAkECwAQBRQCDQEAAAkECwAQBBQCDgEAAAkECwAQCzgDAg8BAAAJBAsAEAEUAhABAAAJBAsAEAk4CgIRAQAACQQLABAKOAsCEgEAAA81CgAQCzgDDAMKAQoDJQQJBQ8LAAELAgEHBCcLAwoBFwcAJgQWBRwLAAELAgEHEycKAQcAJgQhBScLAAELAgEHEycLAhEoCgAQBBQKABABFAsADwsLATgHEgICEwEEAAkJCwALAQoCERILAi4RLzgMAhQBBAAFFgoALg4BERUEBgUKCwABBw0nCwETAgwCAQERJgsADwsLAjgFAQIVAQAAEhkKABAEFAoBEAQUIQQRCwAQARQLARABFCEMAgUXCwABCwEBCQwCCwICFgEAACwtCgAKAREcBAgLAAERHwIKABAKCgE4DQsBETcMAwoAEAk4DhQMAgoDCgImBCkKABAHCgM4DwQkBR4LABAHCwM4EBQCCwMGAQAAAAAAAAAXDAMFFAsAAREfAhcBAAAJBAsAEAAUAhgBAAAJBAsAEAIUAhkDAAAJAwsAEAcCGgEAAAkECwAQDBQCGwEAAAkECwAQDRQCHAAAABIRCgAREAQICwABCAwCBQ8LABAJOA4UCwEkDAILAgIdAAAAEiMKABAMFAYAAAAAAAAAACEECQgMAgUPCgAQDRQGAAAAAAAAAAAhDAILAgQVCwABCwECCgAQDBQ1CwE1GAsAEA0UNRo0Ah4AAAASIwoAEAwUBgAAAAAAAAAAIQQJCAwCBQ8KABANFAYAAAAAAAAAACEMAgsCBBULAAELAQIKABANFDULATUYCwAQDBQ1GjQCHwAAAAkEBgAAAAAAAAAABgAAAAAAAAAAEgECIAAAADYWCgALAREWDAMOAwoAEAUUER4MBAsAEAgUDAILBAsCIQQTBRUHCicCAAcCAgAIAAkCAQADAAQABgAFAAEAAgIDAQABAQBMAE0ADHN0b3JhZ2VfZnVuZJ4EoRzrCwYAAAALAQAGAgYOAxQsBEAIBUg/B4cBtwEIvgJACv4CDwyNA1kN5gMED+oDAgALAQQBBQACBAABAAQBAAECAQIAAAgAAQAAAwIAAAANAwQAAAwDBAABBwgEAQABCgkHAQABDgoEAQABDwUHAQAHBgQGBQYGBgELAQEIAgEIAAQHCAALAQEIAgMDAQYIAAEDAAEIAgELAQEJAAIHCwEBCQALAQEJAAIHCwEBCQADAQYLAQEJAAdCYWxhbmNlBElPVEENU3RvcmFnZUZ1bmRWMQ1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UEaW90YRdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lcgRqb2luA25ldxZub25fcmVmdW5kYWJsZV9iYWxhbmNlBXNwbGl0DHN0b3JhZ2VfZnVuZA10b3RhbF9iYWxhbmNlHHRvdGFsX29iamVjdF9zdG9yYWdlX3JlYmF0ZXMFdmFsdWUEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgINCwEBCAIJCwEBCAIAAwAABQQ4AAsAEgACAQMAAAAUCgAPAAsBOAEBCgAPAAsDOAIMBAoADwELBDgBAQsADwALAjgCAgIBAAAFBAsAEAA4AwIDAQAABQgKABAAOAMLABABOAMWAgAAAAEABgAMdm90aW5nX3Bvd2Vy0QyhHOsLBgAAAAwBAAgCCAgDEFcEZwQFa4QBB+8BoQIIkARABtAERgqWBQkMnwX0Bg2TDAQPlwwCABUAEQEPARQAAQIAAQAEAAALAAEAAAUCAwAADQQFAAAGBgEAAAIHAQAAEAgBAAADBAEAAA4BBQAACgEFAAELFQEAAQ0OBQABFQ4FAAIECwUAAggLBQACCQsFAAMGEQEBAAMHExQBAA8NEA0BBwoIAQACBgoIAQMCCggAAwEGCggBAQMCBwoIAAgAAwcKCAADAwIHCggBCggAAwoIAAMDAQgBAgMDCAMIAAMKCAADAwMDAQgAAQYIAQMDAwMDAQMDAwcKCQAJAAMHAQMDAwMDBwgAAQYKCQABAQIHCAEDDAMDAwMDAwMDAwYIAQYIAQMLVmFsaWRhdG9yVjERVm90aW5nUG93ZXJJbmZvVjETYWRqdXN0X3ZvdGluZ19wb3dlchBjaGVja19pbnZhcmlhbnRzE2RpdmlkZV9hbmRfcm91bmRfdXAWaW5pdF92b3RpbmdfcG93ZXJfaW5mbwZpbnNlcnQIaXNfZW1wdHkDbWF4A21pbhBxdW9ydW1fdGhyZXNob2xkEHNldF92b3RpbmdfcG93ZXIFc3Rha2ULdG90YWxfc3Rha2USdG90YWxfdm90aW5nX3Bvd2VyA3U2NBN1cGRhdGVfdm90aW5nX3Bvd2VyCXZhbGlkYXRvcg92YWxpZGF0b3JfaW5kZXgNdmFsaWRhdG9yX3NldAZ2ZWN0b3IMdm90aW5nX3Bvd2VyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIECcAAAAAAAADCAsaAAAAAAAAAwjoAwAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAAAAgMSAxUDDAMAAwAACRsHAAcCBwAKAC5BChEMEQ0RDgwDCgAuCgMRAQwCDAENAQsDCwIRBAoACwERBQsALhEGAgEAAAAMOgoAEQIMCAYAAAAAAAAAAAwCCgBBCgwEBgAAAAAAAAAADAdADQAAAAAAAAAADAUKAgoEIwQzBREKAAoCQgoRCgwGCgY1BwA1GAoINRo0CgERDgwJCgIKCQsGEgAMAw0FCwMRAwsHCwkWDAcLAgYBAAAAAAAAABYMAgUMCwABCwUHAAsHFwICAAAADxwGAAAAAAAAAAAMAQoAQQoMAgYAAAAAAAAAAAwDCgEKAiMEGAUMCwMKAAoBQgoRChYMAwsBBgEAAAAAAAAAFgwBBQcLAAELAwIDAAAAECUGAAAAAAAAAAAMAwoALkENDAQKAwoEIwQXBQsKAC4KA0INEAAUDgEQABQkDAIFGQkMAgsCBCALAwYBAAAAAAAAABYMAwUGCwALAQsDOAACBAAAABJUBgAAAAAAAAAADAUKAC5BDQwGCgUKBiMEEAULCgIGAAAAAAAAAAAkDAMFEgkMAwsDBEoKAAoFQw0MCQoCCgYKBRcRDAwHCgEKCRABFAsHFhEODAgKAgsICgkQARQXEQ4MBAoJEAEUCgQWCgkPARULCRABFAoBJQQ9BUELAAEHBScLAgsEFwwCCwUGAQAAAAAAAAAWDAUFBgsAAQsCBgAAAAAAAAAAIQRRBVMHAycCBQAAAAsWDgE4ASAEEQUFDQFFDRMAAQwDDAIKAAsCQwoLAxEJBQALAAELAUYNAAAAAAAAAAACBgAAABZ2BgAAAAAAAAAADAMKAEEKDAQGAAAAAAAAAAAMCQoDCgQjBCMFDAoACgNCChELDAwKDAYAAAAAAAAAACQEFgUaCwABBwYnCwkLDBYMCQsDBgEAAAAAAAAAFgwDBQcLCQcAIQQoBSwLAAEHAycGAAAAAAAAAAAMAQoBCgQjBHMKAQYBAAAAAAAAABYMAgoCCgQjBG4FOwoACgFCCgwKCgAKAkIKDAsKChEKDAcKCxEKDAgLChELDAULCxELDAYKBwoIJARcCgUKBiYEWAVcCwABBwQnCwcLCCMEaQsFCwYlBGUFaQsAAQcEJwsCBgEAAAAAAAAAFgwCBTYLAQYBAAAAAAAAABYMAQUuCwABAgcBAAABAgcAAggBAAABAgcBAgACAAEAEwANdmFsaWRhdG9yX2NhcIIGoRzrCwYAAAAMAQAIAggUAxwqBEYEBUo2B4AB4wII4wNABqMEIgrFBA0M0gRwDcIFBA/GBQYAEgELAQ4BDwADDAAABAIAAQAHAAECBAADAQIAABAAAQAAFAIBAAAKAwQAAAkABQABBg0EAQgBCAoLAAIMDgYBDAMNCAkABAwGDAEGCAABBgUBBggBAgUHCAQBCAIBCAEABAEIAAgCBQEGCAQBBQEHCAQBCAMBCAABBgkAAgkABQJJRAlUeENvbnRleHQDVUlEH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwHGF1dGhvcml6ZXJfdmFsaWRhdG9yX2FkZHJlc3MCaWQXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIDbmV3E25ld19mcm9tX3VudmVyaWZpZWQzbmV3X3VudmVyaWZpZWRfdmFsaWRhdG9yX29wZXJhdGlvbl9jYXBfYW5kX3RyYW5zZmVyBm9iamVjdA9wdWJsaWNfdHJhbnNmZXIGc2VuZGVyCHRyYW5zZmVyCnR4X2NvbnRleHQgdW52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MJdmFsaWRhdG9yDXZhbGlkYXRvcl9jYXANdmFsaWRhdG9yX3NldB52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgYIAwUFAQIBBQUAAwAABgMLABAAAgEDAAAGAwsAEAECAgMAAAcjCgEuEQcMBQoFBwAhBAsIDAIFDwsFCgAhDAILAgQSBRYLAQEGAAAAAAAAAAAnCwERBQoAEgAMAw4DOAAMBAsDCwA4AQsEAgMDAAAGBQsAEAAUEgECAAEBAAAHABEAEwANdmFsaWRhdG9yX3NldLdPoRzrCwYAAAAMAQA1AjVwA6UBowYEyAeOAQXWCJEJB+cRjRgI9ClgBtQqwwEKlyxsDIMt4iEN5U4QD/VOBACiAQIfAiACOgJUAmoCdwKTAQKUAQKaAQKbAQKpAQKqAQCPAQCeAQChAQCnAQCuAQFsAasBABMEAAAPAwAAEAMAABEDAAEADAACAQQBAAEEBAIABQMHAAYCBgECAAYHBgECAAcKDAIHAQQBCAsEAQQBCgwCAAsVBwIBAAAADBYHAQMADQYHAA0IDAANCQwADhQEAA8NDAAPEgIAEA4EABIFBwEAAABlAAEAAIMBAgMAAIUBBAMAAIIBBQMAAB0GAwAAhAEHAwAAgQEICQAAhwEKCwAAhgEFAwAAHAwDAACdAQ0DAAA1DgMAADAPEAAAlwEPEAAApQEREAAAowEREAAApgEREAAApAEREgAAkQEPEwAAbxQVAABoDxAAAFcRFgAAWgYWAABZFxYAACwXEAAAWwYWAAArGBAAAEYZGgAAPhscAAA/HRwAAEoeHwAASyAaAABDIRoAAE4iGgAATAcaAABNBxoAAE8bIwAARCQjAABFESMAAEgRIwAArQElJgAAeCcDAAB7KAMAACQpAwAAeioDAACLASsDAAB5LAMAACMtEAAAGy4DAAAnLzAAACgxMgAAKTMfAAAmNB8AADQ1AwAANzYDAACSAR4QAAAZDy0AAGARFgAAXTcWAAAYDzIAAWU6RAACM5kBAwEAAowBmAGZAQEAAqgBVhABAAM2UwMBAwVQcxIBCAZlamsBAgZmaGkBAgZ2bGgBAgcaPgMCBwQHIUZaAgcEByJLWwIHBAcqRhYCBwQHZTo7AgcEB4ABS0wCBwQIIXJzAQQIInh5AQQIODo/AQQIXG8WAQQIY28QAQQIdYwBUwEECH1PAwEECXyaAQMBDAo5ShAACogBSj0ACypgFgIBAAs4A0MCAQALQmBaAgEAC0dhWwIBAAtSYwMCAQALXIcBFgIBAAtihwGIAQIBAAt0kwFiAgEAC4ABYWICAQAMKokBFgEDDFOUAYgBAQMMXIsBFgEDDIABigEDAQMNO24VAA1wWRIADhdNAwAOGxoDAA4lIxAADi5NAwAOL5sBAwAONRoDAA5AIxAADkkjbgAOVSM9AA5YcRYADl4jFgAOayN/AA5znQGeAQAOeY8BAwAOgQFXCQAOhgFNAwAOhwFdCwAOjgEjEAAOkAEjEgAOmAEjEAAOrgEjEAAPZ34mAA+cAX57AA+sAXp7ABAtR0gAEDFIPAAQZFwaABF+AxAAEYkBLgMAEZkBAxAAEjJ0UwEAEj1SUwEAEl9RFgEAEmkDdAEAEooBU3QBABMqVBYBABNcggEWAQATgAFkUwEASTlFOU08SUBJQVZCSEFFQUpBSjlFQFE8hAEQgwEQhwEQP1VIOUY5SEBHQFVCXUJYQllCiQE8QxBCEEQQTzxLPEdBhgEQhQEQggEQTDxBgAGIARBAhAFVhgFdhgFbhgFYhgFePWE9YD1OPFA8QI0BVmVZZVqGAVyGAV89VWVXZT5VUgk9VVeGAYcBPUCfAQIKCBIHCAwBCAADBwgACBIHCAwAAgcIAAcIDAMHCAADBggMAgYIAAYIEgIHCAAGCAwEBwgABQsFAQgGBwgMAQgQAwcIAAgQBggMAQsFAQgGCAcIAAcLBQEIBgcLDQIFCw4BBQMDAwMHCAwGBwgAAwMDBwsNAgULDgEFBwgMAQcIAAEGCAABAwIGCAAFAQgHAQYLCgIIBwUCBwgABggHAQYLCgIDCA8BAQIGCggSBggSAgYLCwEIEgYIEgIHCAAFAQcIEgIGCggSBQELFgEDAgYLCwEIEgUCBgoIEgYKBQEKAwIHCggSBQMHCAAFAQMHCAAGCBQBAQYIEgMHCAAFAgMHCAAGCBMCAQgUAwcIAAcLDQIFCw4BBQcIDAUHCAAIEgcLDQIFCw4BBQEHCAwCBwsNAgULDgEFBQIHCAADAQcKAwIHCggSBggMAQYKCBIBBwoIEgMKAwMGCgMCAwsNAgMDAgYIAAsNAgULDgEFAQoFAwYKCBIDAwYGCggSAwMKAwMLDQIDAwQHCggSBgoDBwsFAQgGBwgMBQMGCggSBgoDBgsNAgULDgEFBgoFAgYIAAgHBgMDCwoCCAcFAwYIEggAAggHBQEHCAwBCwoCCQAJAQEIEgEFAwcLCgIJAAkBCQAJAQELCwEJAAIIBwgVAgUIFQIFAwELDQIJAAkBAQgEAgEFAgYLCgIJAAkBCQACCBIHCAwBCBUDCAcIEgUBBggMAgcLCgIJAAkBCQABCQECBwgSAwMBCBIFAgcLCwEJAAkAAwUDCxYBAwEGCxYBCQABBwsWAQkAAQkAAgYKCQAGCQABCAYBBgsFAQkABAcIEgsFAQgGBQcIDAMHCBIIBwUBBggQAQYJAQEHCQEBBwgVAwcIEggQBggMCAoDCw0CAwMDCgUDAwMKAwgDAwcDAwgSCBIFBggSAgYLDQIJAAkBBgkAAgcLDQIJAAkBBgkAAgkACQEDBwsNAgkACQEJAAkBAgcKCQADAgMDCgoLCAEDAwMLCQEDAwMDBggSAwYKCBIBCwgBAwIDCQABCwgBCQABCgsIAQkAAQsJAQkAAQcLCQEJAAIGCBIFAQYIEQEGCwsBCQADAwMDAgYIEgYIEgIGCwsBCQADAQYJAAELFgEJAAUFAwsWAQMDCgMCAwsWAQMEAwMLFgEDCxYBAwIHCwsBCQADAQcJAAEGCBQBBgUGAQEDAwsWAQMLFgEDBAYIEggHBggHBQEGCBMBBggHAQgTAgMIEgEGCgkAAwMFCAcBCAMFAwMGBQoFBwsOAQUCBQsOAQUBBgsNAgkACQEBCgkAAgYLDgEJAAYJAAIHCw4BCQAGCQABBgsOAQkAAQcLCwEJAAEIAgQDAwMDAgcIEgYIDAQDAwMGCBIECw0CAwMEAwMFCgUGCggSCw4BBQoFBQEHCw0CCQAJAQELDgEJAAQDAwQKAwoDAwoDAwQDAwMDBAgDAwsFAQgGAwcIEgUECwUBCAYCBwsFAQkAAwELBQEJAAIJAAUCBwgSCwUBCAYICgUDAwMDCgUGCBIFAgYIEgMBCA8BCAEFAwMKBQUGCggSA0JhZwdCYWxhbmNlBUVudHJ5AklEBElPVEEGT3B0aW9uFVBvb2xUb2tlbkV4Y2hhbmdlUmF0ZQ1Qcmlvcml0eVF1ZXVlClN0YWtlZElvdGENU3Rha2luZ1Bvb2xWMQVUYWJsZQhUYWJsZVZlYwlUeENvbnRleHQfVW52ZXJpZmllZFZhbGlkYXRvck9wZXJhdGlvbkNhcAlWYWxpZGF0b3IZVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnRWMRJWYWxpZGF0b3JKb2luRXZlbnQTVmFsaWRhdG9yTGVhdmVFdmVudBVWYWxpZGF0b3JPcGVyYXRpb25DYXAOVmFsaWRhdG9yU2V0VjELVmFsaWRhdG9yVjEGVmVjTWFwBlZlY1NldAhhY3RpdmF0ZRphY3RpdmVfdmFsaWRhdG9yX2FkZHJlc3NlcxFhY3RpdmVfdmFsaWRhdG9ycwNhZGQaYWRqdXN0X3N0YWtlX2FuZF9nYXNfcHJpY2UNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcxJhdF9yaXNrX3ZhbGlkYXRvcnMDYmFnB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQWY2FsY3VsYXRlX3RvdGFsX3N0YWtlcyZjbGVhbl9yZXBvcnRfcmVjb3Jkc19sZWF2aW5nX3ZhbGlkYXRvcg9jb21taXNzaW9uX3JhdGUkY29tcHV0ZV9hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uGmNvbXB1dGVfcmV3YXJkX2FkanVzdG1lbnRzGmNvbXB1dGVfc2xhc2hlZF92YWxpZGF0b3JzJmNvbXB1dGVfdW5hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uCGNvbnRhaW5zGWNvdW50X2R1cGxpY2F0ZXNfdGFibGV2ZWMUY291bnRfZHVwbGljYXRlc192ZWMJY3JlYXRlX3YxCmRlYWN0aXZhdGUVZGVwb3NpdF9zdGFrZV9yZXdhcmRzGmRlcml2ZV9yZWZlcmVuY2VfZ2FzX3ByaWNlB2Rlc3Ryb3kMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybxFkaXN0cmlidXRlX3Jld2FyZBplZmZlY3R1YXRlX3N0YWdlZF9tZXRhZGF0YQRlbWl0G2VtaXRfdmFsaWRhdG9yX2Vwb2NoX2V2ZW50cwVlbXB0eQVlcG9jaAVldmVudA5leGNoYW5nZV9yYXRlcwxleHRyYV9maWVsZHMHZXh0cmFjdA5maW5kX3ZhbGlkYXRvch1maW5kX3ZhbGlkYXRvcl9mcm9tX3RhYmxlX3ZlYwlnYXNfcHJpY2UHZ2VuZXNpcwNnZXQwZ2V0X2FjdGl2ZV9vcl9wZW5kaW5nX29yX2NhbmRpZGF0ZV92YWxpZGF0b3JfbXV0MGdldF9hY3RpdmVfb3JfcGVuZGluZ19vcl9jYW5kaWRhdGVfdmFsaWRhdG9yX3JlZhhnZXRfYWN0aXZlX3ZhbGlkYXRvcl9yZWYlZ2V0X2NhbmRpZGF0ZV9vcl9hY3RpdmVfdmFsaWRhdG9yX211dAdnZXRfbXV0GWdldF9wZW5kaW5nX3ZhbGlkYXRvcl9yZWYUZ2V0X3N0YWtpbmdfcG9vbF9yZWYVZ2V0X3ZhbGlkYXRvcl9pbmRpY2VzEWdldF92YWxpZGF0b3JfbXV0GmdldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4L2dldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4X2luY2x1ZGluZ19jYW5kaWRhdGVzI2dldF92YWxpZGF0b3JfbXV0X3dpdGhfdmVyaWZpZWRfY2FwEWdldF92YWxpZGF0b3JfcmVmAmlkE2luYWN0aXZlX3ZhbGlkYXRvcnMGaW5zZXJ0CWludG9fa2V5cwRpb3RhDGlvdGFfYWRkcmVzcxdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lciNpc19hY3RpdmVfdmFsaWRhdG9yX2J5X2lvdGFfYWRkcmVzcwxpc19kdXBsaWNhdGUWaXNfZHVwbGljYXRlX3ZhbGlkYXRvciJpc19kdXBsaWNhdGVfd2l0aF9hY3RpdmVfdmFsaWRhdG9yI2lzX2R1cGxpY2F0ZV93aXRoX3BlbmRpbmdfdmFsaWRhdG9yCGlzX2VtcHR5FWlzX2luYWN0aXZlX3ZhbGlkYXRvcgxpc19wcmVhY3RpdmUHaXNfc29tZRZpc192YWxpZGF0b3JfY2FuZGlkYXRlDGlzX3ZvbHVudGFyeQRrZXlzBmxlbmd0aBxsb2FkX3ZhbGlkYXRvcl9tYXliZV91cGdyYWRlA25ldwluZXdfZW50cnkTbmV3X2Zyb21fdW52ZXJpZmllZBpuZXh0X2Vwb2NoX3ZhbGlkYXRvcl9jb3VudARub25lBm9iamVjdBBvcGVyYXRpb25fY2FwX2lkBm9wdGlvbhlwZW5kaW5nX2FjdGl2ZV92YWxpZGF0b3JzEHBlbmRpbmdfcmVtb3ZhbHMTcG9vbF9leGNoYW5nZV9yYXRlcwdwb29sX2lkE3Bvb2xfc3Rha2luZ19yZXdhcmQYcG9vbF90b2tlbl9leGNoYW5nZV9yYXRlIXBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZV9hdF9lcG9jaANwb3AIcG9wX2JhY2sHcG9wX21heA5wcmlvcml0eV9xdWV1ZRhwcm9jZXNzX3BlbmRpbmdfcmVtb3ZhbHMkcHJvY2Vzc19wZW5kaW5nX3N0YWtlc19hbmRfd2l0aGRyYXdzGnByb2Nlc3NfcGVuZGluZ192YWxpZGF0b3JzG3Byb2Nlc3NfdmFsaWRhdG9yX2RlcGFydHVyZQ9wdWJsaWNfdHJhbnNmZXIJcHVzaF9iYWNrEHF1b3J1bV90aHJlc2hvbGQacmVmZXJlbmNlX2dhc19zdXJ2ZXlfcXVvdGUGcmVtb3ZlEXJlcXVlc3RfYWRkX3N0YWtlFXJlcXVlc3RfYWRkX3ZhbGlkYXRvch9yZXF1ZXN0X2FkZF92YWxpZGF0b3JfY2FuZGlkYXRlGHJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvciJyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3JfY2FuZGlkYXRlG3JlcXVlc3Rfc2V0X2NvbW1pc3Npb25fcmF0ZRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlBnNlbmRlchBzZXRfdm90aW5nX3Bvd2VyBHNvbWURc29ydF9yZW1vdmFsX2xpc3QFc3BsaXQFc3Rha2UMc3Rha2VfYW1vdW50DHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQVc3Rha2luZ19wb29sX21hcHBpbmdzHXN1bV92b3RpbmdfcG93ZXJfYnlfYWRkcmVzc2VzBXRhYmxlCXRhYmxlX3ZlYxp0YWxseWluZ19ydWxlX2dsb2JhbF9zY29yZRd0YWxseWluZ19ydWxlX3JlcG9ydGVycwt0b3RhbF9zdGFrZRJ0b3RhbF9zdGFrZV9hbW91bnQSdG90YWxfdm90aW5nX3Bvd2VyCHRyYW5zZmVyCnR4X2NvbnRleHQgdW52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MndXBkYXRlX2FuZF9wcm9jZXNzX2xvd19zdGFrZV9kZXBhcnR1cmVzCXZhbGlkYXRvchF2YWxpZGF0b3JfYWRkcmVzcxR2YWxpZGF0b3JfY2FuZGlkYXRlcw12YWxpZGF0b3JfY2FwDXZhbGlkYXRvcl9zZXQWdmFsaWRhdG9yX3N0YWtlX2Ftb3VudBl2YWxpZGF0b3Jfc3Rha2luZ19wb29sX2lkHHZhbGlkYXRvcl90b3RhbF9zdGFrZV9hbW91bnQWdmFsaWRhdG9yX3ZvdGluZ19wb3dlchF2YWxpZGF0b3Jfd3JhcHBlcgV2YWx1ZQd2ZWNfbWFwB3ZlY19zZXQGdmVjdG9yHnZlcmlmaWVkX29wZXJhdGlvbl9jYXBfYWRkcmVzcwp2ZXJpZnlfY2FwDHZvdGluZ19wb3dlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIBAQIBAgIBAwQQECcAAAAAAAAAAAAAAAAAAAMIAMqaOwAAAAADCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAADCAYAAAAAAAAAAwgHAAAAAAAAAAMICAAAAAAAAAADCAkAAAAAAAAAAwgKAAAAAAAAAAMICwAAAAAAAAADCAwAAAAAAAAAAwgNAAAAAAAAAAMIZQAAAAAAAAAKAwEACgUBAAACCZcBAxkKCBJtCwsBCBJuCgORAQsKAggHBVELCgIIBwgVoAELCgIFCBUeCw0CBQM8CAQBAgo5A58BBX8DjQEDrgEDJQNxA3IID5YBCgWVAQMCAgM5A58BBZABCAcDAgQ5A58BBZABCAdhAQADAAA4Mw4AES8MBQoBOAAMBA4AQTwMAwYAAAAAAAAAAAwCCgIKAyMEHwUQDgAKAkI8DAYNBAoGEXYLBhFsOAELAgYBAAAAAAAAABYMAgULCwULAAoBOAIHFAsECgE4AwoBOAQ4BQsBETwSAAwHDQcPABGAAQsHAgEDAABFQQoALg4BERYgBA0KAC4OAREZIAwDBQ8JDAMLAwQSBRgLAAELAgEHBycOARFsDAQKABABCgQ4BiAEIgUoCwABCwIBBwsnDgERbgQsBTILAAELAgEHDCcKAA8CDgERdgsEOAELAA8BDgERbAsBCwIRfDgHAgIDAABJNQoBLhFUDAQKABABCgQ4BgQKBRALAAELAQEHDScKAA8BCwQ4CBF9DAMOAxFuBBoFIAsAAQsBAQcMJw4DEXYMAgoADwIKAjgJAQ0DCgEuEVMRZwsADwMLAgsDCwERfDgKAgMDAABOQAsCEVQMBQoAEAEKBTgGBAkFDQsAAQcNJwoADwELBTgIEX0MBAoALg4EERYgBCAKAC4OBBEZIAwDBSIJDAMLAwQlBSkLAAEHBycOBBFuBC0FMQsAAQcMJw4EEXcLASYENwU7CwABBwonCwAPBAsEOAsCBAMAAAMQCgAQAAoBERgLABAECwERGhYGAQAAAAAAAAAhBA0FDwcHJwIFAwAAUCMLARFUDAIKABAACwIRHAwEDgQ4DAQMBRALAAEHCScNBDgNDAMKABAFDgM4DiAEGgUeCwABBxAnCwAPBQsDRBACBgMAAAMWDgI4DwcEJgQGBQwLAAELAwEHDycLAAsBERsLAgoDLhFUCwMRcgIHAwAAWCsOARFjDAQKABACCgQ4EAQUCgAQAg4BEWM4ERQMBQsACwURGwwDBSYKABADCgQ4EgQaBSALAAELAgEHCCcLAA8DCwQ4ExF+DAMLAwsBCwIRdAIIAwAAPQoLAhFUDAMLAA8ACwMRHwsBEXMCCQMAAF5dCgcuEVMGAQAAAAAAAAAWDAoRgQEMDgoAEAAKDgoBLjgPETMMDwoALgoCFBEyDAsKABAADgsRNwwMCgAQAA4LER4LAw4PETEMCQwNCgAQAAsOCwwLDwsNCwkRNAwICgAPAA4ICwEKBxE1CgAPABEwCgAPAAoHLhEuCgoKABAADggKAi4OCxE2CgALChEsCgAKAgoHESkKAAsECwULBgsCCwcRCgoAEAARLwoADwYVCgAPABGAAQsAEQsCCgAAAF9qCgAQAEE8DAcKBwYAAAAAAAAAACQEYwUJCwcGAQAAAAAAAAAXDAcKABAACgdCPAwNCg0RbAwMCw0RdwwJCgkKASYEKAoAEAcODDgUBAQKAA8HDgw4FQEBBQQLCQoCJgRXCgAQBw4MOBQEQAoADwcODDgWDAgKCBQGAQAAAAAAAAAWCggVCwgUDAYFRwoADwcLDAYBAAAAAAAAADgXBgEAAAAAAAAADAYLBgoDJAQECgAPAAoHOBgMCgoACwoKBAkKBREqBQQKAA8ACgc4GAwLCgALCwoECQoFESoFBAsEAQsAAQsFAQILAAAAZRgKABAAQTwMAgYAAAAAAAAAAAwBCgEKAiMEFQULCgAPAAoBQzwRaQsBBgEAAAAAAAAAFgwBBQYLAAECDAEAAGY8CwAQAAwKCgpBPAwDQGcAAAAAAAAAAAwBBgAAAAAAAAAADAIKAgoDIwQfBQ8KCgoCQjwMCA0BCggRagsIEXg4GURnCwIGAQAAAAAAAAAWDAIFCgsKAQsBOBoMBAYAAAAAAAAAAAwGEYEBEX8XDAcGAAAAAAAAAAAMBQoGCgcjBDoFMQ0EOBsMCQwFCwYLCRYMBgUsCwUCDQEAAAMECwAQBhQCDgEAAAMGCwAQAAsBESQRdwIPAQAAAwYLABAACwERJBF1AhABAAADBgsAEAALAREkEXgCEQEAAAMGCwAQAAsBESQRdgISAQAAAwMLABACAhMDAABtHwoAEAIKARQ4EAQTCgAQAgsBFDgRFAwDCwALAwcCESUMAgUbCwAPAwsBFDgTEX4uDAILAhFrEWICFAMAAAMMCgAQAEE8CgAQBUEQFwsAEAQ4HBYCFQMAABwICwAQAAsBERwMAg4COAwCFgAAAAMFCwAQAAsBERcCFwMAAAMGCwALAREYBgAAAAAAAAAAJAIYAAAAcCEKAEE8DAMGAAAAAAAAAAAMAgYAAAAAAAAAAAwECgIKAyMEGwUMCgAKAkI8CgERbQQWCwQGAQAAAAAAAAAWDAQLAgYBAAAAAAAAABYMAgUHCwABCwEBCwQCGQAAAAMHCwAQBAsBERoGAAAAAAAAAAAkAhoAAABwIQoAOBwMAwYAAAAAAAAAAAwCBgAAAAAAAAAADAQKAgoDIwQbBQwKAAoCOB0KARFtBBYLBAYBAAAAAAAAABYMBAsCBgEAAAAAAAAAFgwCBQcLAAELAQELBAIbAAAAAxAKABABCgE4BgQLCwAPAQsBOB4RfgILAA8ACwERHwIcAAAAZR8KAEE8DAMGAAAAAAAAAAAMAgoCCgMjBBsFCgoACgJCPBFsCgEhBBYLAAELAjgfAgsCBgEAAAAAAAAAFgwCBQULAAE4IAIdAAAAZR8KADgcDAMGAAAAAAAAAAAMAgoCCgMjBBsFCgoACgI4HRFsCgEhBBYLAAELAjgfAgsCBgEAAAAAAAAAFgwCBQULAAE4IAIeAAAAdS4KAUE9DAUGAAAAAAAAAAAMAwcUDAYKAwoFIwQoBQwKAQoDQj0UDAIKAAsCERwMBA4EOAwEGQUfCwABCwEBBwknDQYLBDghRBALAwYBAAAAAAAAABYMAwUHCwABCwEBCwYCHwMAAHYUCgAuCwERHAwDDgM4DAQJBQ0LAAEHCScNAzgNDAILAAsCQzwCIAAAAHctCgAQAAoBERwMBQ4FOAwEEA0FOA0MAwsADwALA0M8AgoAEAQKAREdDAYOBjgMBCANBjgNDAQLAA8ECwQ4IgILAgQjBScLAAEHDicLAA8BCwE4HhF+AiEDAAADBwsACwERexQLAhEgAiIDAAA9CAsBEVQMAgsACwIJESACIwMAAD0ICwERVAwCCwALAggRIAIkAAAAdhMKAAsBERwMAw4DOAwECAUMCwABBwknDQM4DQwCCwALAkI8AiUDAAB8OQoAEAAKAREcDAcOBzgMBAsIDAMFDwoCBwAhDAMLAwQZDQc4DQwFCwAQAAsFQjwCCgAQBAoBER0MCA4IOAwEJAgMBAUoCwIHASEMBAsEBDINCDgNDAYLABAECwY4HQILAA8BCwE4HhF+LgImAQAAdhUKABAACwERHAwDDgM4DAQJBQ0LAAEHCScNAzgNDAILABAACwJCPAInAQAAdhUKABAECwERHQwDDgM4DAQJBQ0LAAEHEScNAzgNDAILABAECwI4HQIoAwAAfSUKARF6FAwGCgIHACEEDgsALgsGESYMAwUTCwALBgsCESUMAwsDEW8MBQoBOCMMBAsFDgQhBB4FIgsBAQcTJwsBEXkCKQAAAIEBIAoADwURLQoAEAU4JCAEGQUJCgAPBUUQDAMKAA8ACwM4GAwECgALBAoBCAoCESoFAwsBAQsAAQsCAQIqAAAAgwE6CgQuEVMGAQAAAAAAAAAWDAUOARFsDAYOARF2DAcKAA8CCgc4CQEKABAHDgY4FAQcCgAPBw4GOBUBAQoAEAYUDgERdxcKAA8GFQsCCgYRKwoFCwYOARF2CwMSAzglDQELBRFnCwAPAwsHCwELBBF8OAoCKwAAAIUBQQoALg4BOCYECgoADgE4JwEBCgAuOCgMBQ4FQT0MAwYAAAAAAAAAAAwCCgIKAyMEPg4FCgJCPQwECgAKBDgpDAYKBi4OATgqBDUFJQoGDgE4KwsGLjgsBDIKAAsEOCcBAQU5CwQBBTkLBgELBAELAgYBAAAAAAAAABYMAgUTCwABAiwAAAA8HAoAEAQ4LSAEGQUGCgAPBDguDAINAgoBEWQKAQ4CEWwOAhF2EgI4LwoADwALAkQ8BQALAAECLQAAAI4BMwoALkEQDAQGAQAAAAAAAAAMAgoCCgQjBDAFCwoALgoCQhAUDAEKAgwDCgMGAAAAAAAAAAAkBCsFGAsDBgEAAAAAAAAAFwwDCgAuCgNCEBQKASQEKwoACgMKAwYBAAAAAAAAABZHEAUTCwIGAQAAAAAAAAAWDAIFBgsAAQIuAAAAZRoKAC5BPAwDBgAAAAAAAAAADAIKAgoDIwQVBQsKAAoCQzwKARFxCwIGAQAAAAAAAAAWDAIFBgsAAQsBAQIvAAAAkAEeBgAAAAAAAAAADAMKAEE8DAIGAAAAAAAAAAAMAQoBCgIjBBoFDAoACgFCPAwECwMLBBF3FgwDCwEGAQAAAAAAAAAWDAEFBwsAAQsDAjAAAABlFwoALkE8DAIGAAAAAAAAAAAMAQoBCgIjBBQFCwoACgFDPBFlCwEGAQAAAAAAAAAWDAEFBgsAAQIxAAAAkQEnBgAAAAAAAAAADAU4MAwDDgA4JCAEIgUJDQBFEAwGCgIKBkIQFDUKATUYBwMaDAQNAwsGCgQ0ODELBQsENBYMBQUECwIBCwULAwIyAAAAkgEoBxUMBQ4BODIgBCQFBw0BODMMBAwGCgAKBhEVBBAFFAsAAQcFJwoAEAAMAwsEODQMAgsDDgIRNxF/JgQCDQULBkQ9BQILAAELBQIzAAAAlQElBxQMBgoAQTwMBAYAAAAAAAAAAAwDCgMKBCMEIQUMCgAKA0I8EXg1CgI1GAoBNRoMBQ0GCwU0RBALAwYBAAAAAAAAABYMAwUHCwABCwYCNAAAAJYBRAsBCwIXDA0HFAwICgBBPAwMBgAAAAAAAAAADAsKCwoMIwRABRAKAAoLQjwReDUMDw4DCgtCEBQMDg4FDgs4NQQpDgUOCzg2FAwJCw4LCRcMBgU2CgQ1Cw8YCg01GgwKCw4LCjQWDAYLBgwHDQgLB0QQCwsGAQAAAAAAAAAWDAsFCwsAAQsIAjUAAACXAVoKAC5BPAwFCgUGAAAAAAAAAAAkBAkFEwsAAQsCAQsDAQsBAQcSJwYAAAAAAAAAAAwECgQKBSMEUQoACgRDPAwICgEKBEIQFAwHCgIKBzg3DAYLBzUKCC4RZjUYBwMaDAoNBgsKNDg3DAsOCzgPBgAAAAAAAAAAJARHBTsKCC4RbAwJCggLCwoJCgMRcgsJODgFSQsLODkLCAsGEWgLBAYBAAAAAAAAABYMBAUVCwABCwIBCwMBCwEBAjYAAACcAU4KAUE8DAgGAAAAAAAAAAAMBwoHCggjBEUFCgoBCgdCPAwLCgsRbAwMCgMODDgmBBwKAw4MODoUODQMBQUeBxUMBQsFDAoKBA4MODsEJwYAAAAAAAAAAAwGBSkGAQAAAAAAAAAMBgsGDAkKAAsMCgsRagoLEXcKCxF4CgsRZgoCCgdCEBQLCwoAEXALCgsJEgE4PAsHBgEAAAAAAAAAFgwHBQULAQELBAELAwELAgECNwEAAJABIwYAAAAAAAAAAAwEBgAAAAAAAAAADAIKAUE9DAMKAgoDIwQdBQwKAAoBCgJCPRQRJAwFCwQLBRF4FgwECwIGAQAAAAAAAAAWDAIFBwsAAQsBAQsEAjgBAAADAwsAEAACOQEAAAMFCwAQAQsBOAYCOgEAAAMFCwAQAwsBOBICOwMAAKABIAsAEAAMBQcVDAMGAAAAAAAAAAAMAQoFQTwMAgoBCgIjBBwFDwoFCgFCPBFsDAQNAwsERD0LAQYBAAAAAAAAABYMAQUKCwUBCwMCAAEABgAEAAUAAgADAAAABwBBAFYAEXZhbGlkYXRvcl93cmFwcGVyvgShHOsLBgAAAAwBAAgCCBADGDAESAYFTjQHggHOAQjQAkAGkAMKCpoDBgygA2QNhAQCD4YEAgAOAQoBEAAMAAEEAAEAAgACAwwAAwIEAAAFAAEAAAgCAwAABgEEAAALBQYAAA8FBwACBAgJAQQCBgkMAQQCCQoLAQQCDw0HAAUEBwQGBAIIAwcIAQEIAAEHCAABBwgDAQgDAQYIAAABAwMDCQAHCAEBCAIBBwgCAQcJAAEJAAEGCAIJVHhDb250ZXh0CVZhbGlkYXRvcgtWYWxpZGF0b3JWMQlWZXJzaW9uZWQGY3JlYXRlCWNyZWF0ZV92MQdkZXN0cm95BWlubmVyHGxvYWRfdmFsaWRhdG9yX21heWJlX3VwZ3JhZGUObG9hZF92YWx1ZV9tdXQKdHhfY29udGV4dBF1cGdyYWRlX3RvX2xhdGVzdAl2YWxpZGF0b3INdmFsaWRhdG9yX3NldBF2YWxpZGF0b3Jfd3JhcHBlcgd2ZXJzaW9uCXZlcnNpb25lZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAAIBBwgCAAMAAAYGBgEAAAAAAAAACwALATgAEgACAQMAAAYHCgAuEQMLAA8AOAECAgMAAAYGDgARAwsAEwA4AgIDAAAABgkLABEEBgEAAAAAAAAAIQQGBQgHACcCBAAAAAYECwAQABEIAgAAAA0AEnRpbWVsb2NrZWRfc3Rha2luZ98VoRzrCwYAAAAMAQAeAh5AA16EAgTiAhwF/gLGAwfEBqsHCO8NYAbPDhQK4w4RDPQOqAYNnBUGD6IVAgA6Ag4CEAIXAiICNgI5AjsCPwAYADQAQgEjATUBRAAKCAABAAQBAAECAQwBAAEDAwIABAIHAAQMBAAFBAQABgkIAQQACAsCAAkFCAAKBwwACw0EAAwGBwEAAA0IBwAAJgABAAApAgEAACwDAQAAKwAEAAAqAgUAAC0DBgAALwcEAAAwBwEAAB4IAQAAPQkBAAA+CgEAABoLDAAAJA0OAAAzDQ8AADENDwAAEw0PAAAfDRAAABsNDAEAAEEEEQAAOxIBAAA8EwEAACcUAQABEiABAQABLzAgAQABQxkPAQACFBobAQAEESoBAAQhKSoABhwiDAEEBjcxMgEEBjglJgEEBj0XAQEEBkABOAEAByUfAQEMBzsfAQEICC4cHQAJICMkAAkrJygACS0vFgAKGjYMAAoeNAEACiQuDgAKLzMoAAoxLg8ACjMuDwALKD4oAAwPOToBAAwdOQwBAA4ZPQwBAB8WGBgZGCEeFhgcFh4WFxgdFi84LjggOyIEMAQEBwgJCwcBCwEBCAMFBwgIAAQHCAkKCwcBCwEBCAMFBwgIAwcICQgABwgIAQgAAQoIAAILBwELAQEIAwsBAQgDAwcIAAMHCAgCBwgACAACCAAGCAgCCggABggIAgYIAAYIAAEBAQYIAAEIBAEDAQsMAQgNAwgKAwsMAQgNAggABQIKCAAFBgcICwsBAQgDBQMLDAEIDQcICAMDAwoIAAELAQEIAwILBwEJAAYICAEIAwEGCwEBCQACCwEBCQAHCAgBCwIBCQABBggIAQUBCwIBCAMCCQAFAQsBAQkABAsBAQgDAwsMAQgNCAoCBgsHAQkABggIAQcICQEGCAYCBggGCwcBCQADCQADCwwBCA0EBwgJCwIBCAMFBwgIAQgKAQcICAEIBQUDAwoIAAsHAQsBAQgDCAABCwcBCwEBCAMGAwsMAQgNAwsBAQgDCAoLAQEIAwEGCAoDBwgJCAoHCAgCBwsBAQkAAwUGCAYJAAMLDAEIDQcICAELBwEJAAMHCAoDBwgIAgcICggKAgEBAgYICgYICgMIDQYIDQEBCA0BBgsMAQkAAQYJAAEJAAMDCwwBCA0ICgEGCgkAAwcICwsBAQgDBwgIB0JhbGFuY2UEQ29pbgJJRARJT1RBEklvdGFTeXN0ZW1BZG1pbkNhcA9Jb3RhU3lzdGVtU3RhdGUGT3B0aW9uClN0YWtlZElvdGEGU3RyaW5nCFRpbWVMb2NrFFRpbWVsb2NrZWRTdGFrZWRJb3RhCVR4Q29udGV4dANVSUQLVmFsaWRhdG9yVjEHYmFsYW5jZQZib3Jyb3cEY29pbgZkZWxldGUMZGVzdHJveV96ZXJvF2V4cGlyYXRpb25fdGltZXN0YW1wX21zDGZyb21fYmFsYW5jZQdnZW5lc2lzAmlkBGlvdGELaW90YV9zeXN0ZW0IaXNfZW1wdHkZaXNfZXF1YWxfc3Rha2luZ19tZXRhZGF0YQ9pc19sYWJlbGVkX3dpdGgJaXNfbG9ja2VkB2lzX3NvbWUQam9pbl9zdGFrZWRfaW90YQVsYWJlbBpsb2FkX2lvdGFfc3lzdGVtX2FkbWluX2NhcANuZXcGb2JqZWN0Bm9wdGlvbgdwb29sX2lkD3B1YmxpY190cmFuc2ZlchFyZXF1ZXN0X2FkZF9zdGFrZRxyZXF1ZXN0X2FkZF9zdGFrZV9hdF9nZW5lc2lzKXJlcXVlc3RfYWRkX3N0YWtlX2F0X2dlbmVzaXNfd2l0aF9yZWNlaXB0GXJlcXVlc3RfYWRkX3N0YWtlX211bF9iYWwjcmVxdWVzdF9hZGRfc3Rha2VfbXVsX2JhbF9ub25fZW50cnkbcmVxdWVzdF9hZGRfc3Rha2Vfbm9uX2VudHJ5FnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkGc2VuZGVyBXNwbGl0EXNwbGl0X3N0YWtlZF9pb3RhFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gLc3Rha2VkX2lvdGESc3Rha2VkX2lvdGFfYW1vdW50DHN0YWtpbmdfcG9vbAZzdHJpbmcQc3lzdGVtX2FkbWluX2NhcAtzeXN0ZW1fcGFjaw1zeXN0ZW1fdW5wYWNrCHRpbWVsb2NrEnRpbWVsb2NrZWRfc3Rha2luZwh0cmFuc2ZlchF0cmFuc2Zlcl9tdWx0aXBsZRJ0cmFuc2Zlcl90b19zZW5kZXIbdHJhbnNmZXJfdG9fc2VuZGVyX211bHRpcGxlCnR4X2NvbnRleHQJdHlwZV9uYW1lBnVucGFjawl2YWxpZGF0b3IFdmFsdWUGdmVjdG9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAA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AwQKBQUDCwwCBQMCBQMBBgoJAAEGCAcBBgkBAgcIFAYICAULBAEIBgMLBAEIBgsFAQgGCwQBCAYBCwUBCAYCBwsFAQkACgsFAQkAAQYLFQEJAAELFQEJAAILBAEJAAcICwIJAAUDQmFnB0JhbGFuY2UEQ29pbgJJRARJT1RBEklvdGFTeXN0ZW1BZG1pbkNhcBFJb3RhU3lzdGVtU3RhdGVWMQ9Jb3RhVHJlYXN1cnlDYXAGT3B0aW9uFVBvb2xUb2tlbkV4Y2hhbmdlUmF0ZQpTdGFrZWRJb3RhDVN0b3JhZ2VGdW5kVjEWU3lzdGVtRXBvY2hJbmZvRXZlbnRWMRJTeXN0ZW1QYXJhbWV0ZXJzVjEFVGFibGUJVHhDb250ZXh0H1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwDlZhbGlkYXRvclNldFYxC1ZhbGlkYXRvclYxBlZlY01hcAZWZWNTZXQaYWN0aXZlX3ZhbGlkYXRvcl9hZGRyZXNzZXMeYWN0aXZlX3ZhbGlkYXRvcl92b3RpbmdfcG93ZXJzEWFjdGl2ZV92YWxpZGF0b3JzDWFkdmFuY2VfZXBvY2gmYXNzZXJ0X25vX3BlbmRpbmdfb3JfYWN0aXZlX2R1cGxpY2F0ZXMDYmFnB2JhbGFuY2UMYnVybl9iYWxhbmNlE2J1cm50X3Rva2Vuc19hbW91bnQEY29pbghjb250YWlucwZjcmVhdGUYY3JlYXRlX3N5c3RlbV9wYXJhbWV0ZXJzGmRlcml2ZV9yZWZlcmVuY2VfZ2FzX3ByaWNlDGRlc3Ryb3lfc29tZQxkZXN0cm95X3plcm8EZW1pdAVlbXB0eQVlcG9jaBFlcG9jaF9kdXJhdGlvbl9tcxhlcG9jaF9zdGFydF90aW1lc3RhbXBfbXMFZXZlbnQMZXh0cmFfZmllbGRzFGV4dHJhY3RfY29pbl9iYWxhbmNlDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24DZ2V0B2dldF9tdXQQZ2V0X3JlcG9ydGVyc19vZh9nZXRfc3RvcmFnZV9mdW5kX29iamVjdF9yZWJhdGVzHmdldF9zdG9yYWdlX2Z1bmRfdG90YWxfYmFsYW5jZRVnZXRfdG90YWxfaW90YV9zdXBwbHkaZ2V0X3ZhbGlkYXRvcl9tdXRfd2l0aF9jdHgvZ2V0X3ZhbGlkYXRvcl9tdXRfd2l0aF9jdHhfaW5jbHVkaW5nX2NhbmRpZGF0ZXMjZ2V0X3ZhbGlkYXRvcl9tdXRfd2l0aF92ZXJpZmllZF9jYXAGaW5zZXJ0DGludG9fYmFsYW5jZQRpb3RhC2lvdGFfc3lzdGVtFWlvdGFfc3lzdGVtX2FkbWluX2NhcBdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lchFpb3RhX3RyZWFzdXJ5X2NhcCNpc19hY3RpdmVfdmFsaWRhdG9yX2J5X2lvdGFfYWRkcmVzcwhpc19lbXB0eQdpc19zb21lBGpvaW4Iam9pbl92ZWMpbWF0Y2hfY29tcHV0YXRpb25fcmV3YXJkX3RvX3RhcmdldF9yZXdhcmQTbWF4X3ZhbGlkYXRvcl9jb3VudBNtaW5fdmFsaWRhdG9yX2NvdW50G21pbl92YWxpZGF0b3Jfam9pbmluZ19zdGFrZQxtaW50X2JhbGFuY2UUbWludGVkX3Rva2Vuc19hbW91bnQDbmV3M25ld191bnZlcmlmaWVkX3ZhbGlkYXRvcl9vcGVyYXRpb25fY2FwX2FuZF90cmFuc2ZlchpuZXh0X2Vwb2NoX3ZhbGlkYXRvcl9jb3VudAZvYmplY3QGb3B0aW9uCnBhcmFtZXRlcnMDcGF5E3Bvb2xfZXhjaGFuZ2VfcmF0ZXMQcHJvdG9jb2xfdmVyc2lvbg9wdWJsaWNfdHJhbnNmZXITcmVmZXJlbmNlX2dhc19wcmljZQZyZW1vdmUQcmVwb3J0X3ZhbGlkYXRvchVyZXBvcnRfdmFsaWRhdG9yX2ltcGwRcmVxdWVzdF9hZGRfc3Rha2UacmVxdWVzdF9hZGRfc3Rha2VfbXVsX2NvaW4VcmVxdWVzdF9hZGRfdmFsaWRhdG9yH3JlcXVlc3RfYWRkX3ZhbGlkYXRvcl9jYW5kaWRhdGUYcmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yInJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvcl9jYW5kaWRhdGUbcmVxdWVzdF9zZXRfY29tbWlzc2lvbl9yYXRlFXJlcXVlc3Rfc2V0X2dhc19wcmljZRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlFHJvdGF0ZV9vcGVyYXRpb25fY2FwCXNhZmVfbW9kZR1zYWZlX21vZGVfY29tcHV0YXRpb25fcmV3YXJkcyRzYWZlX21vZGVfbm9uX3JlZnVuZGFibGVfc3RvcmFnZV9mZWUZc2FmZV9tb2RlX3N0b3JhZ2VfY2hhcmdlcxlzYWZlX21vZGVfc3RvcmFnZV9yZWJhdGVzBnNlbmRlch1zZXRfY2FuZGlkYXRlX2NvbW1pc3Npb25fcmF0ZRdzZXRfY2FuZGlkYXRlX2dhc19wcmljZSdzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9jb21taXNzaW9uX3JhdGUhc2V0X2NhbmRpZGF0ZV92YWxpZGF0b3JfZ2FzX3ByaWNlCXNpbmdsZXRvbgVzcGxpdAxzdGFraW5nX3Bvb2wVc3Rha2luZ19wb29sX21hcHBpbmdzDnN0b3JhZ2VfY2hhcmdlDHN0b3JhZ2VfZnVuZBRzdG9yYWdlX2Z1bmRfYmFsYW5jZQ5zdG9yYWdlX3JlYmF0ZRBzeXN0ZW1fYWRtaW5fY2FwFHN5c3RlbV9zdGF0ZV92ZXJzaW9uBXRhYmxlDXRvdGFsX2JhbGFuY2UOdG90YWxfZ2FzX2ZlZXMcdG90YWxfb2JqZWN0X3N0b3JhZ2VfcmViYXRlcwt0b3RhbF9zdGFrZR90b3RhbF9zdGFrZV9yZXdhcmRzX2Rpc3RyaWJ1dGVkDHRvdGFsX3N1cHBseQh0cmFuc2Zlcgp0eF9jb250ZXh0FXVuZG9fcmVwb3J0X3ZhbGlkYXRvchp1bmRvX3JlcG9ydF92YWxpZGF0b3JfaW1wbCF1cGRhdGVfY2FuZGlkYXRlX2F1dGhvcml0eV9wdWJrZXkgdXBkYXRlX2NhbmRpZGF0ZV9uZXR3b3JrX2FkZHJlc3MfdXBkYXRlX2NhbmRpZGF0ZV9uZXR3b3JrX3B1YmtleRx1cGRhdGVfY2FuZGlkYXRlX3AycF9hZGRyZXNzIHVwZGF0ZV9jYW5kaWRhdGVfcHJpbWFyeV9hZGRyZXNzIHVwZGF0ZV9jYW5kaWRhdGVfcHJvdG9jb2xfcHVia2V5K3VwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX2F1dGhvcml0eV9wdWJrZXkqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkSdXBkYXRlX2Rlc2NyaXB0aW9uEHVwZGF0ZV9pbWFnZV91cmwLdXBkYXRlX25hbWUidXBkYXRlX25leHRfZXBvY2hfYXV0aG9yaXR5X3B1YmtleSF1cGRhdGVfbmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MgdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19wdWJrZXkddXBkYXRlX25leHRfZXBvY2hfcDJwX2FkZHJlc3MhdXBkYXRlX25leHRfZXBvY2hfcHJpbWFyeV9hZGRyZXNzIXVwZGF0ZV9uZXh0X2Vwb2NoX3Byb3RvY29sX3B1YmtleRJ1cGRhdGVfcHJvamVjdF91cmwcdXBkYXRlX3ZhbGlkYXRvcl9kZXNjcmlwdGlvbhp1cGRhdGVfdmFsaWRhdG9yX2ltYWdlX3VybBV1cGRhdGVfdmFsaWRhdG9yX25hbWUsdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX2F1dGhvcml0eV9wdWJrZXkrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcyp1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19wdWJrZXkndXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzK3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3Byb3RvY29sX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX3Byb2plY3RfdXJsCXZhbGlkYXRvcg12YWxpZGF0b3JfY2FwIHZhbGlkYXRvcl9sb3dfc3Rha2VfZ3JhY2VfcGVyaW9kHXZhbGlkYXRvcl9sb3dfc3Rha2VfdGhy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EAoKAgEIFAEGCBQCBwsdAgkACQEJAAEHCQEBBgslAQkAAQgHAgYLHQIJAAkBCQABCAUCBQslAQsaAQkAAgkABQgDBwgBAggSCA8IFggVCA4BCBIBCA8CBwgLCA8BCBYBCBUBCA4DBggBCBEGCAQDAgIDAQYJAQIGCAEIEQIGCAEDAQYIIgEGCQACBwgBAwEHCCIBBwkADAICBgIDBwgBCBEFBwgECAoCCxoBCQAIFAEICAEICgUHCA0GCBcGCBkICgMBCAkDBwgXAwcIIAEIBgEGCBIBCAMCAggKAQYIFgMHCA0GCAoDAQYIFQMHCBcCAwcBAgoCAwoDCCQKCCQBBggOAQoIJAEKAwEIJAUHCBcIJAIBAwIHAwMCBgshAgkACQEGCQADBwshAgkACQEJAAkBAgcLIQIJAAkBBgkAAQgTDkFkZFRva2VuT25Jb3RhB0JhbGFuY2UJQmxvY2tsaXN0BkJyaWRnZQ9CcmlkZ2VDb21taXR0ZWULQnJpZGdlSW5uZXINQnJpZGdlTWVzc2FnZRBCcmlkZ2VNZXNzYWdlS2V5DEJyaWRnZVJlY29yZAtCcmlkZ2VSb3V0ZQ5CcmlkZ2VUcmVhc3VyeQVDbG9jawRDb2luDENvaW5NZXRhZGF0YQ9Db21taXR0ZWVNZW1iZXILRW1lcmdlbmN5T3AQRW1lcmdlbmN5T3BFdmVudA9Jb3RhU3lzdGVtU3RhdGULTGlua2VkVGFibGUGT3B0aW9uGlBhcnNlZFRva2VuVHJhbnNmZXJNZXNzYWdlBlN0cmluZxNUb2tlbkRlcG9zaXRlZEV2ZW50HFRva2VuVHJhbnNmZXJBbHJlYWR5QXBwcm92ZWQbVG9rZW5UcmFuc2ZlckFscmVhZHlDbGFpbWVkFVRva2VuVHJhbnNmZXJBcHByb3ZlZBRUb2tlblRyYW5zZmVyQ2xhaW1lZBhUb2tlblRyYW5zZmVyTGltaXRFeGNlZWQUVG9rZW5UcmFuc2ZlclBheWxvYWQPVHJhbnNmZXJMaW1pdGVyC1RyZWFzdXJ5Q2FwCVR4Q29udGV4dANVSUQQVXBkYXRlQXNzZXRQcmljZRFVcGRhdGVCcmlkZ2VMaW1pdApVcGdyYWRlQ2FwBlZlY01hcAlWZXJzaW9uZWQNYWRkX25ld190b2tlbhJhZGRfdG9rZW5zX29uX2lvdGEHYWRkcmVzcwZhbW91bnQWYXBwcm92ZV90b2tlbl90cmFuc2ZlcgVhc2NpaQdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0BmJyaWRnZQ5icmlkZ2VfdmVyc2lvbgRidXJuCGNoYWluX2lkCWNoYWluX2lkcyFjaGVja19hbmRfcmVjb3JkX3NlbmRpbmdfdHJhbnNmZXIYY2xhaW1fYW5kX3RyYW5zZmVyX3Rva2VuC2NsYWltX3Rva2VuFGNsYWltX3Rva2VuX2ludGVybmFsB2NsYWltZWQFY2xvY2sEY29pbgljb21taXR0ZWUTY29tbWl0dGVlX2Jsb2NrbGlzdBFjb21taXR0ZWVfbWVtYmVycxZjb21taXR0ZWVfcmVnaXN0cmF0aW9uCGNvbnRhaW5zBmNyZWF0ZQpjcmVhdGVfa2V5G2NyZWF0ZV90b2tlbl9icmlkZ2VfbWVzc2FnZQxkZXN0cm95X25vbmUMZGVzdHJveV9zb21lDGVtZXJnZW5jeV9vcBJlbWVyZ2VuY3lfb3BfcGF1c2URZW1lcmdlbmN5X29wX3R5cGUUZW1lcmdlbmN5X29wX3VucGF1c2UEZW1pdAVlbXB0eQVldmVudBpleGVjdXRlX2FkZF90b2tlbnNfb25faW90YRFleGVjdXRlX2Jsb2NrbGlzdBRleGVjdXRlX2VtZXJnZW5jeV9vcBZleGVjdXRlX3N5c3RlbV9tZXNzYWdlGmV4ZWN1dGVfdXBkYXRlX2Fzc2V0X3ByaWNlG2V4ZWN1dGVfdXBkYXRlX2JyaWRnZV9saW1pdBpleHRyYWN0X2FkZF90b2tlbnNfb25faW90YRlleHRyYWN0X2Jsb2NrbGlzdF9wYXlsb2FkHGV4dHJhY3RfZW1lcmdlbmN5X29wX3BheWxvYWQcZXh0cmFjdF90b2tlbl9icmlkZ2VfcGF5bG9hZBpleHRyYWN0X3VwZGF0ZV9hc3NldF9wcmljZRtleHRyYWN0X3VwZGF0ZV9icmlkZ2VfbGltaXQKZnJvbV9ieXRlcwZmcm96ZW4hZ2V0X2N1cnJlbnRfc2VxX251bV9hbmRfaW5jcmVtZW50B2dldF9tdXQhZ2V0X3BhcnNlZF90b2tlbl90cmFuc2Zlcl9tZXNzYWdlCWdldF9yb3V0ZSRnZXRfdG9rZW5fdHJhbnNmZXJfYWN0aW9uX3NpZ25hdHVyZXMgZ2V0X3Rva2VuX3RyYW5zZmVyX2FjdGlvbl9zdGF0dXMCaWQVaW5pdF9icmlkZ2VfY29tbWl0dGVlBWlubmVyBmluc2VydAtpb3RhX3N5c3RlbQhpc19lbXB0eQlpc19uYXRpdmUHaXNfc29tZQ5pc192YWxpZF9yb3V0ZQNrZXkHbGltaXRlcgxsaW5rZWRfdGFibGUKbG9hZF9pbm5lcg5sb2FkX2lubmVyX211dApsb2FkX3ZhbHVlDmxvYWRfdmFsdWVfbXV0B21lc3NhZ2ULbWVzc2FnZV9rZXkMbWVzc2FnZV90eXBlDW1lc3NhZ2VfdHlwZXMPbWVzc2FnZV92ZXJzaW9uBG1pbnQDbmV3BG5vbmUGb2JqZWN0Bm9wdGlvbgdwYWNrYWdlBnBhdXNlZA9wdWJsaWNfdHJhbnNmZXIJcHVzaF9iYWNrCHJlZ2lzdGVyFnJlZ2lzdGVyX2ZvcmVpZ25fdG9rZW4Kc2VuZF90b2tlbgZzZW5kZXIOc2VuZGVyX2FkZHJlc3MHc2VxX251bQ1zZXF1ZW5jZV9udW1zDHNoYXJlX29iamVjdARzb21lDHNvdXJjZV9jaGFpbg50YXJnZXRfYWRkcmVzcwx0YXJnZXRfY2hhaW4IdG9fYnl0ZXMgdG9fcGFyc2VkX3Rva2VuX3RyYW5zZmVyX21lc3NhZ2UFdG9rZW4MdG9rZW5fYW1vdW50CHRva2VuX2lkCXRva2VuX2lkcwx0b2tlbl9wcmljZXMUdG9rZW5fdGFyZ2V0X2FkZHJlc3MSdG9rZW5fdGFyZ2V0X2NoYWluFnRva2VuX3RyYW5zZmVyX3JlY29yZHMKdG9rZW5fdHlwZRB0b2tlbl90eXBlX25hbWVzCHRyYW5zZmVyCHRyZWFzdXJ5GXRyeV9jcmVhdGVfbmV4dF9jb21taXR0ZWUKdHhfY29udGV4dBt1cGRhdGVfYXNzZXRfbm90aW9uYWxfcHJpY2USdXBkYXRlX2Fzc2V0X3ByaWNlJHVwZGF0ZV9hc3NldF9wcmljZV9wYXlsb2FkX25ld19wcmljZSN1cGRhdGVfYXNzZXRfcHJpY2VfcGF5bG9hZF90b2tlbl9pZBN1cGRhdGVfYnJpZGdlX2xpbWl0IXVwZGF0ZV9icmlkZ2VfbGltaXRf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Y2sGT3B0aW9uD1RyYW5zZmVyTGltaXRlcg5UcmFuc2ZlclJlY29yZBVVcGRhdGVSb3V0ZUxpbWl0RXZlbnQGVmVjTWFwF2FkanVzdF90cmFuc2Zlcl9yZWNvcmRzBmJyaWRnZQljaGFpbl9pZHMhY2hlY2tfYW5kX3JlY29yZF9zZW5kaW5nX3RyYW5zZmVyBWNsb2NrCGNvbnRhaW5zGGN1cnJlbnRfaG91cl9zaW5jZV9lcG9jaBJkZWNpbWFsX211bHRpcGxpZXIMZGVzdHJveV9zb21lBGVtaXQFZW1wdHkKZXRoX2N1c3RvbQtldGhfbWFpbm5ldAtldGhfc2Vwb2xpYQVldmVudANnZXQHZ2V0X211dAlnZXRfcm91dGUPZ2V0X3JvdXRlX2xpbWl0CWhvdXJfaGVhZAlob3VyX3RhaWwXaW5pdGlhbF90cmFuc2Zlcl9saW1pdHMGaW5zZXJ0C2lvdGFfY3VzdG9tDGlvdGFfbWFpbm5ldAxpb3RhX3Rlc3RuZXQHaXNfc29tZQdsaW1pdGVyA25ldwluZXdfbGltaXQObm90aW9uYWxfdmFsdWUGb3B0aW9uEHBlcl9ob3VyX2Ftb3VudHMPcmVjZWl2aW5nX2NoYWluBnJlbW92ZRFyb3V0ZV9kZXN0aW5hdGlvbgxyb3V0ZV9zb3VyY2UNc2VuZGluZ19jaGFpbgx0aW1lc3RhbXBfbXMMdG90YWxfYW1vdW50D3RyYW5zZmVyX2xpbWl0cxB0cmFuc2Zlcl9yZWNvcmRzCHRyZWFzdXJ5B3RyeV9nZXQSdXBkYXRlX3JvdXRlX2xpbWl0B3ZlY19tYXAGdmVjdG9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMI//////////8DCADh9QUAAAAACgMBAAACAjALBgIIAwMxCwYCCAMIAQECBBsDHAMoCgMvAwICAy0CKQIlAwABAAACBgsAEAALATgAFAIBAwAAAgQRBjgBEgACAgMAAA9mCgAQAQ4DOAIgBA8KAA8BCgMGAAAAAAAAAAAGAAAAAAAAAAAHAwYAAAAAAAAAABIBOAMKAA8BDgM4BAwJCwIRBAwFCgkLBREFCwAQAA4DOAUMCg4KOAYEIwUpCwEBCwkBBwAnCwo4BzUKATgINRgMCwoBOAk1CwQ1GAwICgkQAhQ1CgE4CDUYCggWCwskBEsLAQELCQEJAgsICwE4CDUaNAwHCgkPA0UBCgcWDAYKCQ8DCwZEAQoJEAIUCwcWCwkPAhUIAgMDAAAYHwoBEQ4UDAMKABAACgE4CiAEEQsADwAKARQKAjgLBRcKAgsADwAKATgMFQsBEQ8UCwMLAhICOA0CBAAAAAIFCwAREgaA7jYAAAAAABoCBQAAAAFZCgAQBBQKASEECQsAAQIKAQYXAAAAAAAAABcMAgoAEAQUCgIjBCgHAwoADwMVBgAAAAAAAAAACgAPAhUKAgoADwUVCwIKAA8EFQoADwMGAAAAAAAAAABEAQVCCgAQBRQKAiMEQgoAEAIUCgAPAwYAAAAAAAAAADgOFwoADwIVCgAQBRQGAQAAAAAAAAAWCgAPBRUFKAoAEAQUCgEjBFYFSQoADwMGAAAAAAAAAABEAQoAEAQUBgEAAAAAAAAAFgoADwQVBUILAAECBgAAAAkkOA8MAA0AEQgRDBEKBkBLTAAAAAAABwIYOAsNABEJEQ0RCgcBOAsNABEJEQsRCgcBOAsNABEHEQ0RCgcBOAsNABEHEQsRCgcBOAsLAAIAAAABAQMBAgEAAQEACQAHbWVzc2FnZc0goRzrCwYAAAALAQAMAgwsAzi1AgTtAhAF/QLsAQfpBJ4MCIcRYAbnEV8KxhJiDKgTxAwN7B8qACwAFAAuAhABDgFbAAMHAAAEBwAACAIAAAUCAAACAgAACgIAAAkCAAAAAgAABgIAAwEHAAQHBwAAJAABAAAjAAIAACIAAwAAJgAEAAAlAAUAACEABgAAQwcIAAAaCQcAABgKBwAAFwsHAAAcDAcAABsNBwAAFg4HAAAZDxAAACoAEAAALwARAAAtABEAAEIAEgAARAARAAA1AAgAAFATEQAATxMIAABRExEAAEsTEgAAHxQRAAARFREAABIVFgAAWRcRAABYFxEAAFcXEgAAVRgRAABUGBIAACkZGgAATRkIAABSGRsAAE4ZHAAAHh0RAAAgHREAAD0AEgAASQAeAAA/CAgAADcfEgABDxEdAAILHREAAhUdEQACHR0RAAJKHREAAlMdEQACVh0RAAMnIQgAAzEIIQADNh8aAAM4HxEAAzkfHAADOh8IAAM7HyYAA0gpCAEABEUIJwAFDSodAQAFKCIaAQAFPi0dAQA7ETgSOhE4GjgIOBs4HDwRAQYIAAEIAgEIAwEIBAEIBQEIBgEIBwEIAAEKAgcCAwoCAgoCAgMDAgMCBAIDAgoKAgQCAwIDBAICAwMGAgMBCgIKCAoKAwMCAgMBCAEBAgEDAQYIAgEGCAMBBggEAQYKCgIBBggFAQYIBgEGCAcBAQEKCAoBCgMAAQgIAQcICQcKAgMICQoCCgICAgEICQEGCgkABwoCCgICCAkCAwoKAgQKAggJAwIICgIICQMBCgIKAwoICgoKAgEKCgIBCAoECgIKAgMCAQYJAAIHCgkACgkABAoCAwMKAggDAwMDAwMCCAMBBwoJAAMDAgMOQWRkVG9rZW5PbklvdGEDQkNTCUJsb2NrbGlzdA1CcmlkZ2VNZXNzYWdlEEJyaWRnZU1lc3NhZ2VLZXkLRW1lcmdlbmN5T3AaUGFyc2VkVG9rZW5UcmFuc2Zlck1lc3NhZ2UGU3RyaW5nFFRva2VuVHJhbnNmZXJQYXlsb2FkEFVwZGF0ZUFzc2V0UHJpY2URVXBkYXRlQnJpZGdlTGltaXQSYWRkX3Rva2Vuc19vbl9pb3RhBmFtb3VudAZhcHBlbmQFYXNjaWkVYXNzZXJ0X3ZhbGlkX2NoYWluX2lkA2Jjcw5ibG9ja2xpc3RfdHlwZR1ibG9ja2xpc3RfdmFsaWRhdG9yX2FkZHJlc3Nlcw5icmlkZ2Vfc2VxX251bQljaGFpbl9pZHMTY29tbWl0dGVlX2Jsb2NrbGlzdCFjcmVhdGVfYWRkX3Rva2Vuc19vbl9pb3RhX21lc3NhZ2UYY3JlYXRlX2Jsb2NrbGlzdF9tZXNzYWdlG2NyZWF0ZV9lbWVyZ2VuY3lfb3BfbWVzc2FnZQpjcmVhdGVfa2V5G2NyZWF0ZV90b2tlbl9icmlkZ2VfbWVzc2FnZSFjcmVhdGVfdXBkYXRlX2Fzc2V0X3ByaWNlX21lc3NhZ2UiY3JlYXRlX3VwZGF0ZV9icmlkZ2VfbGltaXRfbWVzc2FnZQxlbWVyZ2VuY3lfb3ASZW1lcmdlbmN5X29wX3BhdXNlEWVtZXJnZW5jeV9vcF90eXBlFGVtZXJnZW5jeV9vcF91bnBhdXNlGmV4dHJhY3RfYWRkX3Rva2Vuc19vbl9pb3RhGWV4dHJhY3RfYmxvY2tsaXN0X3BheWxvYWQcZXh0cmFjdF9lbWVyZ2VuY3lfb3BfcGF5bG9hZBxleHRyYWN0X3Rva2VuX2JyaWRnZV9wYXlsb2FkGmV4dHJhY3RfdXBkYXRlX2Fzc2V0X3ByaWNlG2V4dHJhY3RfdXBkYXRlX2JyaWRnZV9saW1pdBRpbnRvX3JlbWFpbmRlcl9ieXRlcwhpc19lbXB0eQlpc19uYXRpdmUDa2V5BWxpbWl0B21lc3NhZ2UMbWVzc2FnZV90eXBlDW1lc3NhZ2VfdHlwZXMPbWVzc2FnZV92ZXJzaW9uDG5hdGl2ZV90b2tlbgNuZXcJbmV3X3ByaWNlB29wX3R5cGUOcGFyc2VkX3BheWxvYWQHcGF5bG9hZAlwZWVsX2Jvb2wLcGVlbF91NjRfYmUHcGVlbF91OAxwZWVsX3ZlY191NjQLcGVlbF92ZWNfdTgPcGVlbF92ZWNfdmVjX3U4D3JlY2VpdmluZ19jaGFpbhVyZXF1aXJlZF92b3RpbmdfcG93ZXIHcmV2ZXJzZQ1yZXZlcnNlX2J5dGVzDnNlbmRlcl9hZGRyZXNzDXNlbmRpbmdfY2hhaW4Hc2VxX251bRFzZXJpYWxpemVfbWVzc2FnZQxzb3VyY2VfY2hhaW4Gc3RyaW5nDnRhcmdldF9hZGRyZXNzDHRhcmdldF9jaGFpbgh0b19ieXRlcyB0b19wYXJzZWRfdG9rZW5fdHJhbnNmZXJfbWVzc2FnZQV0b2tlbgx0b2tlbl9hbW91bnQIdG9rZW5faWQJdG9rZW5faWRzDHRva2VuX3ByaWNlcxR0b2tlbl90YXJnZXRfYWRkcmVzcxJ0b2tlbl90YXJnZXRfY2hhaW4KdG9rZW5fdHlwZRB0b2tlbl90eXBlX25hbWVzEnVwZGF0ZV9hc3NldF9wcmljZSR1cGRhdGVfYXNzZXRfcHJpY2VfcGF5bG9hZF9uZXdfcHJpY2UjdXBkYXRlX2Fzc2V0X3ByaWNlX3BheWxvYWRfdG9rZW5faWQTdXBkYXRlX2JyaWRnZV9saW1pdCF1cGRhdGVfYnJpZGdlX2xpbWl0X3BheWxvYWRfbGltaXQrdXBkYXRlX2JyaWRnZV9saW1pdF9wYXlsb2FkX3JlY2VpdmluZ19j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CwcBCQADBwsJAQkAAwcIDQMHCAELEAEIEQgRAgYLDgIJAAkBBgkAAQsQAQkBAQYLEAEJAAEBAQsQAQkAAgcLDgIJAAkBBgkAAQgDAggRCxABCAEDQmFnE0JyaWRnZVRva2VuTWV0YWRhdGEOQnJpZGdlVHJlYXN1cnkEQ29pbgxDb2luTWV0YWRhdGEYRm9yZWlnblRva2VuUmVnaXN0cmF0aW9uAklEDU5ld1Rva2VuRXZlbnQJT2JqZWN0QmFnBk9wdGlvbgZTdHJpbmcWVG9rZW5SZWdpc3RyYXRpb25FdmVudAtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUVVXBkYXRlVG9rZW5QcmljZUV2ZW50ClVwZ3JhZGVDYXAGVmVjTWFwA2FkZA1hZGRfbmV3X3Rva2VuB2FkZHJlc3MFYXNjaWkDYmFnCmJvcnJvd19tdXQGYnJpZGdlBGJ1cm4EY29pbgZjcmVhdGUHZGVjaW1hbBJkZWNpbWFsX211bHRpcGxpZXIGZGVjb2RlDGRlc3Ryb3lfc29tZQRlbWl0BWVtcHR5BWV2ZW50CmZyb21fYnl0ZXMDZ2V0C2dldF9hZGRyZXNzDGdldF9kZWNpbWFscwdnZXRfbXV0EmdldF90b2tlbl9tZXRhZGF0YQNoZXgCaWQNaWRfdG9fYWRkcmVzcxFpZF90b2tlbl90eXBlX21hcAZpbnNlcnQKaW50b19ieXRlcwtpbnRvX3N0cmluZwdpc19zb21lBG1pbnQMbmF0aXZlX3Rva2VuA25ldwluZXdfcHJpY2UObm90aW9uYWxfdmFsdWUGb2JqZWN0Cm9iamVjdF9iYWcGb3B0aW9uB3BhY2thZ2UDcG93FHB1YmxpY19mcmVlemVfb2JqZWN0FnJlZ2lzdGVyX2ZvcmVpZ25fdG9rZW4GcmVtb3ZlEHN1cHBvcnRlZF90b2tlbnMIdG9rZW5faWQMdG90YWxfc3VwcGx5CHRyYW5zZmVyCnRyZWFzdXJpZXMIdHJlYXN1cnkHdHJ5X2dldAp0eF9jb250ZXh0CXR5cGVfbmFtZQN1NjQCdWMbdXBkYXRlX2Fzc2V0X25vdGlvbmFsX3ByaWNlD3VwZ3JhZGVfcGFja2FnZQd2ZWNfbWFwDHdhaXRpbmdfcm9vbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAAAAgRCCAs+Cw4CCBEIASwLDgICCBFMCAYBAgQqAh0DNQMyAQICA0YIEUgIDBwCAwICPwI0AwQCBT8CRggRMgEdAzUDBQIDRggRHAIyAQABAAAMBwsAOAAMAQ4BEAAUAgEBAAAMBwsAOAAMAQ4BEAEUAgIBAAAMBwsAOAAMAQ4BEAIUAgMDAAAOOw4BOAEGAAAAAAAAAAAhBAYFDAsAAQsDAQcCJzgCDAcOBxEiER4RExEKDAUOAhEYDAQOBBEUCwUhBB0FIwsAAQsDAQcBJwoHCwIKAzgDEgIMBgoADwMKBxEjCwY4BAsADwQKBwsBOAULBwsDOAMJEgU4BgIEAwAAHjMKAyAEMAoEBgAAAAAAAAAAJAQIBQwLAAEHAycKAA8DCwE4BxMCDAUMCAwHBgoAAAAAAAAACwURJAwGCgAPBQoHCgIKBgoECgMSATgICwAPBgoCCgc4CQsIOAoLAgsHCwMLBgsEEgQ4CwUyCwABAgUDAAAECAoAERc4DDgNCwARDBIAAgYDAAAECAsADwQ4AjgOCwE4DwECBwMAAAQICwAPBDgCOA4LAQsCOBACCAMAAC4nCgAQBg4BOBEMBA4EOBIECQUNCwABBwAnCgIGAAAAAAAAAAAkBBIFFgsAAQcDJwsEOBMMBQsADwUOBTgUDAMKAgsDDwIVCwELAhIDOBUCCQAAADYQOAIMAQsAEAUOATgWDAIOAjgXBAsFDQcAJwsCOBgCAQABAQECAAMAAAABAAIAGAAJY2hhaW5faWRzowahHOsLBgAAAAsBAAQCBAQDCEIESgIFTCoHdt0BCNMCQAaTAxwKrwMHDLYDtwIN7QUEAAIBEQAABwAACgABAAALAAEAAAkAAQAABgABAAAHAAEAAAUAAQAADgIDAAANAgMAAAEBAAAAEAAEAAAMBQYAAAgFBwABAwoGAQAMBwABAgEGCAABBgIBCggAAgICAQEBCAAFAQEBAQECCggACAACBgoJAAYJAAtCcmlkZ2VSb3V0ZRVhc3NlcnRfdmFsaWRfY2hhaW5faWQJY2hhaW5faWRzCGNvbnRhaW5zC2Rlc3RpbmF0aW9uCmV0aF9jdXN0b20LZXRoX21haW5uZXQLZXRoX3NlcG9saWEJZ2V0X3JvdXRlC2lvdGFfY3VzdG9tDGlvdGFfbWFpbm5ldAxpb3RhX3Rlc3RuZXQOaXNfdmFsaWRfcm91dGURcm91dGVfZGVzdGluYXRpb24Mcm91dGVfc291cmNlBnNvdXJjZQx2YWxpZF9yb3V0ZXMGdmVjdG9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIBAAIBAQIBAgIBCgIBCwIBDAMIAAAAAAAAAAAAAgIPAgQCAAEAAAACBwACAQEAAAACBwECAgEAAAACBwICAwEAAAACBwMCBAEAAAACBwQCBQEAAAACBwUCBgEAAAADCwAQAAIHAQAAAAMLABABAggBAAAINQoABwAhBAcIDAEFCwoABwEhDAELAQQQCAwCBRQKAAcCIQwCCwIEGQgMAwUdCgAHAyEMAwsDBCIIDAQFJgoABwQhDAQLBAQrCAwFBS8LAAcFIQwFCwUEMgU0BwYnAgkBAAAAIAcABwMSAAcDBwASAAcBBwQSAAcBBwUSAAcCBwUSAAcCBwQSAAcEBwESAAcEBwISAAcFBwESAAcFBwISAEAHCgAAAAAAAAACCgEAAAkKCwALARIADAMRCQwCDgIOAzgAAgsBAAAJDwsACwESAAwDEQkMAg4CDgM4AAQLBQ0HBicLAwIAAAABAAljb21taXR0ZWWoGKEc6wsGAAAADAEAFgIWPANSywEEnQIyBc8CiAMH1wWnBwj+DIABBv4NrAEKqg9NDPcP3AcN0xcWD+kXAgAVABkAMAIbAiACPQI/AkADKQEyAUEAAQMAAAIEAAAHAwAABgMAAAQHAAAFBwACAAIAAgMHAAUKAgAGCwcCAQAAAAcMBwEDAAgICAAJCQcBAAAAQgABAAAYAgMAADQEAQAAOwUBAAAhBgEAABYHCAAAPgkBAAAUCgEAARw0DAACDzARAAIQMDEAAjUODwACOBAMAAM2EwwABB0mAQEDBR8CDwAFNwIaAAYXFxUCAQAGHgEbAgEABiIXGAIBAAYjKCkCAQAGJDIzAgEABiUiIwIBAAYnJAECAQAGKh4VAgEABjkeDwIBAAY8FysCAQEHFxQVAQMHHgENAQMHJxkBAQMIDR8gAAkaLSYBAAkrLBUBAAoOEgEBAAoXIRUBABwMIREbDBEWExYdDBIWEhwYFiIaERwWHBccDiUZHBQcGiogDx8PFxYOLhkWFRYONQ43AwYIAQgHCgoCAAEGCAgBCAEFBwgBBwgLCgIKAgYICAQHCAELCQIFAwMGCAgCBwgBCAYBBggBAQYLCQIKAggEAwcIAQoCBggIAgYIAQoCCAMGCAQKAgoCAwsKAQoCAwMBCgIBCwoBCQABBggHAQMBCAcBAgIHCgkACgkAAwYKAgYKAgICBgsKAQkABgkAAQECCgIIBAIGCwkCCQAJAQYJAAEGCQECBwsKAQkACQABBQELCQIJAAkBAgUIBQYIBQcIBQgFCAUFCgUBBgsJAgkACQEBBwgLAQoFAgYKCQAGCQACBwsJAgkACQEGCQABBwkBAwcLCQIJAAkBCQAJAQEIBQEJAAcDCAQLCQIKAggEBggFAwsMAQMDAgYLCQIJAAkBAwIGCQAGCQECBQMBCwwBCQEBBgsMAQkAAQsMAQkAAQgCCwEKAgYKCgIBAwMHCAQDBgoCCgoCBgoCAQYIBgEGCgoCAgcLCQIJAAkBAwIGCQAHCQEBBgoCAQgAAgMHCAQBCAMDAQMGCAUJQmxvY2tsaXN0F0Jsb2NrbGlzdFZhbGlkYXRvckV2ZW50D0JyaWRnZUNvbW1pdHRlZQ1CcmlkZ2VNZXNzYWdlD0NvbW1pdHRlZU1lbWJlchtDb21taXR0ZWVNZW1iZXJSZWdpc3RyYXRpb24dQ29tbWl0dGVlTWVtYmVyVXJsVXBkYXRlRXZlbnQUQ29tbWl0dGVlVXBkYXRlRXZlbnQPSW90YVN5c3RlbVN0YXRlBk9wdGlvbglUeENvbnRleHQGVmVjTWFwBlZlY1NldBphY3RpdmVfdmFsaWRhdG9yX2FkZHJlc3NlcwZhcHBlbmQOYmxvY2tsaXN0X3R5cGUdYmxvY2tsaXN0X3ZhbGlkYXRvcl9hZGRyZXNzZXMLYmxvY2tsaXN0ZWQGYnJpZGdlE2JyaWRnZV9wdWJrZXlfYnl0ZXMcY2hlY2tfdW5pcXVlbmVzc19icmlkZ2Vfa2V5cwljb21taXR0ZWURY29tbWl0dGVlX21lbWJlcnMIY29udGFpbnMGY3JlYXRlBmNyeXB0bwxkZXN0cm95X3NvbWUIZWNkc2FfazEcZWNkc2FfcHViX2tleV90b19ldGhfYWRkcmVzcwRlbWl0BWVtcHR5BWVwb2NoBWV2ZW50EWV4ZWN1dGVfYmxvY2tsaXN0A2dldBBnZXRfZW50cnlfYnlfaWR4FGdldF9lbnRyeV9ieV9pZHhfbXV0B2dldF9tdXQNaHR0cF9yZXN0X3VybAZpbnNlcnQMaW90YV9hZGRyZXNzC2lvdGFfc3lzdGVtCGlzX2VtcHR5B2lzX3NvbWUbbGFzdF9jb21taXR0ZWVfdXBkYXRlX2Vwb2NoBm1lbWJlchRtZW1iZXJfcmVnaXN0cmF0aW9ucwdtZW1iZXJzB21lc3NhZ2UHbmV3X3VybAZvcHRpb24LcHVibGljX2tleXMIcmVnaXN0ZXIVcmVxdWlyZWRfdm90aW5nX3Bvd2VyE3NlY3AyNTZrMV9lY3JlY292ZXIGc2VuZGVyEXNlcmlhbGl6ZV9tZXNzYWdlBHNpemUec3Rha2VfcGFydGljaXBhdGlvbl9wZXJjZW50YWdlGXRyeV9jcmVhdGVfbmV4dF9jb21taXR0ZWUHdHJ5X2dldAp0eF9jb250ZXh0D3VwZGF0ZV9ub2RlX3VybAd2ZWNfbWFwB3ZlY19zZXQGdmVjdG9yEXZlcmlmeV9zaWduYXR1cmVzDHZvdGluZ19wb3dlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAoCFBNJT1RBX0JSSURHRV9NRVNTQUdFAwghAAAAAAAAAAUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCgIBAAACAhEBMwoKAgECAy8LCQIKAggELgsJAgUIBSwDAgICLwsJAgoCCAQ6AwMCAi0KAjEKAgQCBSgFEwoCQwMmCgIRAQUCAygFEwoCJgoCAAEAAAtXBgAAAAAAAAAADgJBDAwJDAM4AAwIDgERCwwHBwoMBQ0FCwERDDgBBgAAAAAAAAAADAoKAwoJIwRNBRcOAgoDQgwOBTEAEQ0MBg4IDgY4AiAEJAUoCwABBwEnCgAQAA4GOAMELgUyCwABBwInCgAQAA4GOAQMBAoEEAEUIARDCwoLBBACFBYMCgVFCwQBDQgLBjgFCwMGAQAAAAAAAAAWDAMFEgsAAQsKCwcmBFQFVgcAJwIBAwAAAQ0LABEQBwwhBAYFCAcDJzgGOAcGAAAAAAAAAAASAQICAwAAHVUKABAAOAgEBQUNCwEBCwABCwQBBwcnDgJBEQcLIQQTBRsLAQELAAELBAEHBicLBBEQDAkLAREeDAoOCg4JOAkEJgUqCwABBwUnCgAQAw4JOAoEQAoADwMOCTgLDAYLAwoGDwQVCgIKBg8FFQsGFAwFBUwKCQoCCwMSBQwHCgAPAwsJCgc4DAsHDAULBQwICwAuCwIRBwsIOA0CAwMAACdZBgAAAAAAAAAADAQ4BgwGBgAAAAAAAAAADAgKBAoAEAM4DiMEPgUNCgAQAwoEOA8MBwEOAQoHEAY4EAwJDgk4EQQ3Cwk4EgwKCwgKChYMCAoHEAYUCgcQBRQLCjQKBxAEFAkSBAwFDQYLBxAFFAsFOBMFOQsHAQsEBgEAAAAAAAAAFgwEBQYKCAsCJgRUOAcKAA8DFQoGCgAPABULAxEPCwAPBxULBgsIEgI4FAVYCwABCwMBAgQDAAAvYA4BEQkxASIMAg4BEQoMBAoEQQwMBwYAAAAAAAAAAAwGBgAAAAAAAAAADAkHDQwLCgYKByMEVwUWCgQKBkIMDAwJDAUKCQoAEAA4FSMESQUjCgAPAAoJOBYMCAwKCgoRCAwDCgwUCwMhBEALDAEKAgsIDwEVDQsLChREDAgMBQYAAAAAAAAAAAwJBUkLCgELCAELCQYBAAAAAAAAABYMCQUcCwUETAVSCwABCwQBBwQnCwYGAQAAAAAAAAAWDAYFEQsAAQsEAQsCCwsSADgXAgUDAAABAwsAEAACBgMAADYyBgAAAAAAAAAADAMKAwoAEAA4FSMELAUJCgAPAAoDOBYMBAEKBBAIFAoCERAhBCULAAELAgEKAQoEDwkVCwQQChQLARIDOBgCCwQBCwMGAQAAAAAAAAAWDAMFAgsAAQsCAQcJJwcAAAA4KQoAEAM4DgwDCQwCCgMGAAAAAAAAAAAkBCYFCwsDBgEAAAAAAAAAFwwDCgAQAwoDOA8MBAELBBAFFAoBIQQGCwIgBB8FIwsAAQcIJwgMAgUGCwABAgEABAQEAgEBBQIFAQUAAQIEAAQDBAEAEgANbWVzc2FnZV90eXBlc5wCoRzrCwYAAAAHAQACAwIeBSADByNvCJIBIAayARIMxAE2AAMABAABAAABAAEAAAIAAQAABgABAAAFAAEAAAAAAQAAAQISYWRkX3Rva2Vuc19vbl9pb3RhE2NvbW1pdHRlZV9ibG9ja2xpc3QMZW1lcmdlbmN5X29wDW1lc3NhZ2VfdHlwZXMFdG9rZW4SdXBkYXRlX2Fzc2V0X3ByaWNlE3VwZGF0ZV9icmlkZ2VfbGltaXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwIBAAIBAQIBAgIBAwIBBAIBBgABAAAAAgcAAgEBAAAAAgcBAgIBAAAAAgcCAgMBAAAAAgcDAgQBAAAAAgcEAgUBAAAAAgcFAgAjCHRyZWFzdXJ5DkJyaWRnZVRyZWFzdXJ5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsIdHJlYXN1cnkTQnJpZGdlVG9rZW5NZXRhZGF0YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCHRyZWFzdXJ5GEZvcmVpZ25Ub2tlblJlZ2lzdHJhdGlvbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCHRyZWFzdXJ5FVVwZGF0ZVRva2VuUHJpY2VFdmVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCHRyZWFzdXJ5DU5ld1Rva2VuRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwh0cmVhc3VyeRZUb2tlblJlZ2lzdHJhdGlvbkV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsJY2hhaW5faWRzC0JyaWRnZVJvdXRlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHbWVzc2FnZQ1CcmlkZ2VNZXNzYWdlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHbWVzc2FnZRBCcmlkZ2VNZXNzYWdlS2V5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHbWVzc2FnZRRUb2tlblRyYW5zZmVyUGF5bG9hZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB21lc3NhZ2ULRW1lcmdlbmN5T3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwdtZXNzYWdlCUJsb2NrbGlzdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB21lc3NhZ2URVXBkYXRlQnJpZGdlTGltaXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwdtZXNzYWdlEFVwZGF0ZUFzc2V0UHJpY2UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwdtZXNzYWdlDkFkZFRva2VuT25Jb3RhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHbWVzc2FnZRpQYXJzZWRUb2tlblRyYW5zZmVyTWVzc2FnZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB2xpbWl0ZXIPVHJhbnNmZXJMaW1pdGVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHbGltaXRlcg5UcmFuc2ZlclJlY29yZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB2xpbWl0ZXIVVXBkYXRlUm91dGVMaW1pdEV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsJY29tbWl0dGVlF0Jsb2NrbGlzdFZhbGlkYXRvckV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsJY29tbWl0dGVlD0JyaWRnZUNvbW1pdHRlZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCWNvbW1pdHRlZRRDb21taXR0ZWVVcGRhdGVFdmVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCWNvbW1pdHRlZR1Db21taXR0ZWVNZW1iZXJVcmxVcGRhdGVFdmVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCWNvbW1pdHRlZQ9Db21taXR0ZWVNZW1iZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwljb21taXR0ZWUbQ29tbWl0dGVlTWVtYmVyUmVnaXN0cmF0aW9uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGYnJpZGdlBkJyaWRnZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBmJyaWRnZQtCcmlkZ2VJbm5lcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBmJyaWRnZRNUb2tlbkRlcG9zaXRlZEV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGYnJpZGdlEEVtZXJnZW5jeU9wRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwZicmlkZ2UMQnJpZGdlUmVjb3JkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGYnJpZGdlFVRva2VuVHJhbnNmZXJBcHByb3ZlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBmJyaWRnZRRUb2tlblRyYW5zZmVyQ2xhaW1lZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBmJyaWRnZRxUb2tlblRyYW5zZmVyQWxyZWFkeUFwcHJvdmVkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGYnJpZGdlG1Rva2VuVHJhbnNmZXJBbHJlYWR5Q2xhaW1lZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALBmJyaWRnZRhUb2tlblRyYW5zZmVyTGltaXRFeGNlZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJZGVueV9saXN0CERlbnlMaXN0AAAAAAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDCS1Ps1eYXDRb85eh7/8FIpwxpIFX30ndaLEffiSHpSEAAAAAAAAAAAIAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoBAAAAAAAAAAwDbmZ09AqhHOsLBgAAAAwBABICEigDOlQEjgEIBZYBdQeLAtACCNsEYAa7BfECCqwIJAzQCNwBDawKDA+4CgIAGAIOAhkCGwIfAiAAFAEaAR0AAgIAAAMMAAEADAEIAQIIBAADBQwABQcCAAYBBAAHBAcBAAAIBgcAABMAAQAADQIBAAAVAwQAABYDBQAAHgMFAAARAwQAABIDBgAAEAcIAAEXDxABCAEhEQEBCAIMFgEAAwoMAQADCwsMAQIEHBMBAQwGDRUBAAgiDQ4ADAoIAgkCDRICCAAHCAUAAQgBAQYIAQEGCwcBBQEGCwcBCgIBBggGAQcIAQEHCAMECwIBCAEKCAgIBAoICAEIAAIJAAcIBQEIBAEKAgEICAQGCAQKCAgKCAgHCAUBCwIBCQABBwsCAQkAAQsCAQgBAQkAAggDCAYBCAYBCAMHRGlzcGxheQ1JcmMyN01ldGFkYXRhA05GVANOZnQGT3B0aW9uCVB1Ymxpc2hlcgZTdHJpbmcJVHhDb250ZXh0A1VJRBhhZGRyZXNzX3VubG9ja19jb25kaXRpb24OYnVybl9wdWJsaXNoZXIFY2xhaW0GZGVsZXRlB2Rlc3Ryb3kHZGlzcGxheQtkdW1teV9maWVsZAJpZBBpbW11dGFibGVfaXNzdWVyEmltbXV0YWJsZV9tZXRhZGF0YQRpbml0BWlyYzI3DWxlZ2FjeV9zZW5kZXIIbWV0YWRhdGEPbmV3X3dpdGhfZmllbGRzA25mdAZvYmplY3QGb3B0aW9uB3BhY2thZ2UUcHVibGljX2ZyZWV6ZV9vYmplY3QGc3RyaW5nA3RhZwh0cmFuc2Zlcgp0eF9jb250ZXh0DnVwZGF0ZV92ZXJzaW9uBHV0ZjgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKAgUEbmFtZQoCCglpbWFnZV91cmwKAgwLZGVzY3JpcHRpb24KAggHY3JlYXRvcgoCCAd2ZXJzaW9uCgILCm1lZGlhX3R5cGUKAhAPY29sbGVjdGlvbl9uYW1lCgIREGltbXV0YWJsZV9pc3N1ZXIKAhoZe2ltbXV0YWJsZV9tZXRhZGF0YS5uYW1lfQoCGRh7aW1tdXRhYmxlX21ldGFkYXRhLnVyaX0KAiEge2ltbXV0YWJsZV9tZXRhZGF0YS5kZXNjcmlwdGlvbn0KAiEge2ltbXV0YWJsZV9tZXRhZGF0YS5pc3N1ZXJfbmFtZX0KAh0ce2ltbXV0YWJsZV9tZXRhZGF0YS52ZXJzaW9ufQoCIB97aW1tdXRhYmxlX21ldGFkYXRhLm1lZGlhX3R5cGV9CgIlJHtpbW11dGFibGVfbWV0YWRhdGEuY29sbGVjdGlvbl9uYW1lfQoCExJ7aW1tdXRhYmxlX2lzc3Vlcn0AAgEPAQECBhAIAxULBwEFFgsHAQoCHgsHAQoCEQsHAQUSCAYAAAAACTULAAoBOAAMBAcAEQ8HAREPBwIRDwcDEQ8HBBEPBwURDwcGEQ8HBxEPQA4IAAAAAAAAAAwDBwgRDwcJEQ8HChEPBwsRDwcMEQ8HDREPBw4RDwcPEQ9ADggAAAAAAAAADAUOBAsDCwULATgBDAINAjgCCwQRCwsCOAMCAQEAABQNCwATAQwCAQEBAQwBCwIRDgsBEQoCAgEAAAEDCwAQAAIDAQAAAQMLABABAgQBAAABAwsAEAICBQEAAAEDCwAQAwIGAQAAAQMLABAEAgcDAAABAwsADwUCAQEBAgEDAQQBBQEAAAkABWFsaWFz1QShHOsLBgAAAAoBAAYCBg4DFDIFRiUHa7cBCKICYAqCAyYMqANtDZUEEA+lBAIABAIMAQ0AAAwAAQIEAAIBBwEAAAAGAAEAAAoCAwAADwIEAAAQAgUAAA4CBgAACwIFAAAIAgYAAAkCBQAABwcIAAEFCQEAAQgAAAEGCAABBgUBDgEGCwIBCgIBBgsCAQUBBwgAAQcIAQEIAQVBbGlhcwZPcHRpb24DVUlEGGFkZHJlc3NfdW5sb2NrX2NvbmRpdGlvbgVhbGlhcwZkZWxldGUHZGVzdHJveQJpZBBpbW11dGFibGVfaXNzdWVyEmltbXV0YWJsZV9tZXRhZGF0YRdsZWdhY3lfc3RhdGVfY29udHJvbGxlcghtZXRhZGF0YQZvYmplY3QGb3B0aW9uBnNlbmRlcgtzdGF0ZV9pbmRleA5zdGF0ZV9tZXRhZGF0YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACCAcIAQoFDw4QCwIBCgIOCwIBBQsLAgEKAggLAgEFCQsCAQoCAAEAAAELCwATAAEBAQEBAQERCQIBAQAAAQMLABAAAgIBAAABBAsAEAEUAgMBAAABAwsAEAICBAEAAAEDCwAQAwIFAQAAAQMLABAEAgYBAAABAwsAEAUCBwEAAAEDCwAQBgIIAwAAAQMLAA8HAgABAAIAAwAEAAUABgAHAAAAAwAFaXJjMje7BaEc6wsGAAAACQEADAIMHgMqNwVhKAeJAdcBCOACYArAAzgM+AODAQ37BBQACwIUAhUBCgEQARIAAQQAAQQHAAIFBwIBAAAAAwAHAAQCBwEAAAUDBwAAFgABAAANAAEAABMAAgAADgABAAAHAAMAABEABAAADAADAAAIAAMAAAYABQAADwAFAAAJBgcAAQYIAAEGCAUBBggBAQYLBAEIBQEGCwICBQgDAQYLAgIIBQgFAQgAAAxGaXhlZFBvaW50MzINSXJjMjdNZXRhZGF0YQZPcHRpb24GU3RyaW5nA1VybAZWZWNNYXAKYXR0cmlidXRlcw9jb2xsZWN0aW9uX25hbWULZGVzY3JpcHRpb24HZGVzdHJveQ1maXhlZF9wb2ludDMyBWlyYzI3C2lzc3Vlcl9uYW1lCm1lZGlhX3R5cGUEbmFtZRNub25fc3RhbmRhcmRfZmllbGRzBm9wdGlvbglyb3lhbHRpZXMGc3RyaW5nA3VyaQN1cmwHdmVjX21hcAd2ZXJzaW9uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIKFggFDQgFEwgBDggFBwsEAQgFEQsCAgUIAwwLBAEIBQgLBAEIBQYLAgIIBQgFDwsCAggFCAUAAQAABwMLABAAAgEBAAAHAwsAEAECAgEAAAcDCwAQAgIDAQAABwMLABADAgQBAAAHAwsAEAQCBQEAAAcDCwAQBQIGAQAABwMLABAGAgcBAAAHAwsAEAcCCAEAAAcDCwAQCAIJAQAABwMLABAJAgoBAAAHDQsAEwABAQEBAQEBAQEBAgAAAAEAAgADAAQABQAGAAcACAAJAAl1dGlsaXRpZXP4BKEc6wsGAAAACQEAEAIQHAMsNgRiDAVuWwfJAckBCJIDYAbyAwoM/ANSABUCBwIIAgkCEgITAQYBFAEADAACAQQBAAEDAgwBAAEFBAIABgMHAAcFBwAADAABAQAACgECAQAACwMEAQABEQ4PAgcEAhYQEQEAAw0HCAEABBAJBQEMBw4FCwEABw8LDAACBgUGBggHBgMNBAYDCAAFBwgDAQgAAggACwEBCQABBwgAAQsBAQkAAAEJAAILAQEJAAcIAwELAgEJAAIJAAUCCwEBCQAIBAEIBQEIBAIIBAsBAQkAAgcIAAkAAQkBAQYLAQEJAAEDA0JhZwdCYWxhbmNlBENvaW4GU3RyaW5nCVR4Q29udGV4dAhUeXBlTmFtZQVhc2NpaQNiYWcHYmFsYW5jZQRjb2luB2V4dHJhY3QIZXh0cmFjdF8TZXh0cmFjdF9hbmRfc2VuZF90bwxmcm9tX2JhbGFuY2UDZ2V0C2ludG9fc3RyaW5nD3B1YmxpY190cmFuc2ZlcgZyZW1vdmUIdHJhbnNmZXIKdHhfY29udGV4dAl0eXBlX25hbWUJdXRpbGl0aWVzBXZhbHVlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAABAAAFCA0AOAALAjgBCwE4AgsAAgEBAAAEBg0AOAAMAQsACwECAgAAAAoROAMRCAwCCwALAjgEDAEOATgFBgAAAAAAAAAAIgQNBQ8HACcLAQIACm5mdF9vdXRwdXTNCaEc6wsGAAAADgEAGAIYNANMUwSfARwFuwGwAQfrAv4DCOkGYAbJBwcK0AchC/EHAgzzB5IBDYUJAg6HCQIPiQkCABwCDgIPAhICHQIlAiYAFAAbACIAJAEeAAQIAQABAQAMAAIBBAEAAQQKBAAFBgIBCAEGCQIABwIEAAgDDAAJBwQACggEAAsFBwEAAAAWAAEBAAAZAgMBAAANBAUBAAAfBgcBAAMLGAUCBwwDIBYXAgcMBBAUBQAFHxkJAQgHJxAFAAknEgUBAAonDgUACxETBQEACxUNCQEACxgLDAEAAQkNCgwKDQ8MDw0RDBEJCQsKCw8LEQUVBBUHBwILAAEJAAcIBQMLAgEJAAgBCAcBBwsAAQkAAQgHAgcLAAEJAAgHAAIHCAMLBAELAAEJAAELAAEJAAcLAgEJAAsKAQgGCAMIAQgHCwoBCAgLCgEICQEJAAEICQEGCwoBCQABAQEHCwoBCQACCAkGCAUBCAYCCAYHCAUBCAgDCAgHCwIBCQAHCAUBCwoBCQABCAMCCgIIBwIHCAMJAAEJAQMHCAMJAAkBAgcIAwsEAQkAA0JhZwdCYWxhbmNlGUV4cGlyYXRpb25VbmxvY2tDb25kaXRpb24DTmZ0CU5mdE91dHB1dAZPcHRpb24JUmVjZWl2aW5nI1N0b3JhZ2VEZXBvc2l0UmV0dXJuVW5sb2NrQ29uZGl0aW9uF1RpbWVsb2NrVW5sb2NrQ29uZGl0aW9uCVR4Q29udGV4dANVSUQDYWRkGGFkZHJlc3NfdW5sb2NrX2NvbmRpdGlvbgphdHRhY2hfbmZ0A2JhZwdiYWxhbmNlBmRlbGV0ZQxkZXN0cm95X25vbmUUZHluYW1pY19vYmplY3RfZmllbGQNZXhwaXJhdGlvbl91YxtleHBpcmF0aW9uX3VubG9ja19jb25kaXRpb24HZXh0cmFjdA5leHRyYWN0X2Fzc2V0cwJpZAdpc19zb21lCGxvYWRfbmZ0DW5hdGl2ZV90b2tlbnMDbmZ0Cm5mdF9vdXRwdXQGb2JqZWN0Bm9wdGlvbgdyZWNlaXZlBnJlbW92ZRlzdG9yYWdlX2RlcG9zaXRfcmV0dXJuX3VjJ3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbgt0aW1lbG9ja191Yxl0aW1lbG9ja191bmxvY2tfY29uZGl0aW9uCHRyYW5zZmVyCnR4X2NvbnRleHQGdW5sb2NrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCgIEA25mdAACBhcIAw8LAgEJABoIASELCgEICCMLCgEICRMLCgEIBgAJAAEAAAgxDQA4AAwGCwA6AAwDDAgMBwwFDAIMBA4IOAEEEw0IOAIKAS4RCg4DOAMEGg0DOAQKAREIDgc4BQQjDQc4Bg0CCwE4BwUlCwEBCwg4CAsDOAkLBzgKCwQRBgsCCwULBgIBAAAABQULADYABwA4CwICAQAABQYLADYABwALATgMAgMDAAAFBAsACwE4DQIAAAAJAAwADGFsaWFzX291dHB1dIcFoRzrCwYAAAAOAQAOAg4eAywxBF0IBWVfB8QB2wEInwNABt8DCQroAw8L9wMCDPkDSg3DBAIOxQQCD8cEAgAJAQsBDAEOARMBFgAIAAEIAQABAQIMAAIDBAEAAQQFBAAFBAIBCAEGAAwAAA8AAQEAABQCAAEAAAoDBAEAABEFBgEAAwYMBAIHDAMVDQ4CBwwEDQkEAAUUCggBCAMIBwAECwULAQsAAQkAAwsCAQkACAEIBQIHCAMLBAELAAEJAAIHCwABCQAIBQABBwsAAQkAAQgFAwgFCwIBCQAIAQEJAAEIAwIHCAMLBAEJAAIKAggFAwcIAwkACQECBwgDCQABCQEFQWxpYXMLQWxpYXNPdXRwdXQDQmFnB0JhbGFuY2UJUmVjZWl2aW5nA1VJRANhZGQYYWRkcmVzc191bmxvY2tfY29uZGl0aW9uBWFsaWFzDGFsaWFzX291dHB1dAxhdHRhY2hfYWxpYXMDYmFnB2JhbGFuY2UGZGVsZXRlFGR5bmFtaWNfb2JqZWN0X2ZpZWxkDmV4dHJhY3RfYXNzZXRzAmlkCmxvYWRfYWxpYXMNbmF0aXZlX3Rva2VucwZvYmplY3QHcmVjZWl2ZQZyZW1vdmUIdHJhbnNmZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCgIGBWFsaWFzAAIDEAgDDAsCAQkAEggBAAgAAQAABwwNADgADAELADoADAMMAhEGCwILAwsBAgEDAAAEBAsACwE4AQICAQAABAYLADYABwALATgCAgMAAAAEBQsANgAHADgDAgAAAAgABwAMYmFzaWNfb3V0cHV0pwihHOsLBgAAAAsBABQCFDADRDkEfRYFkwGDAQeWAtoDCPAFYArQBjILggcCDIQHbQ/xBwIADQILAgwCGAIhAiIAEQAdACABGQACCAEAAQEADAACAQQBAAEDCQQABAUCAQgBBQgCAAYDBAAHBgQACAcEAAkEBwEAAAATAAEBAAAaAgMBAAMOEQoABBoSBQEIBiMNCgAHIw8KAQAIIwsKAAkPEAoBAAkSCQUBAAkVBwgBAAkGCAYJDAgMCQ4IDgUFBwYHDAcOAwMCCwABCQAHCAUCCwIBCQAIAQIHCAMLBAELAAEJAAELAAEJAAYLAgEJAAsJAQgGCAMIAQsJAQgHCwkBCAgBCQABCAgBBgsJAQkAAQEBBwsJAQkAAAIICAYIBQEIBgIIBgcIBQEIBwMIBwcLAgEJAAcIBQELCQEJAAEIAwIHCAMLBAEJAANCYWcHQmFsYW5jZQtCYXNpY091dHB1dBlFeHBpcmF0aW9uVW5sb2NrQ29uZGl0aW9uBk9wdGlvbglSZWNlaXZpbmcjU3RvcmFnZURlcG9zaXRSZXR1cm5VbmxvY2tDb25kaXRpb24XVGltZWxvY2tVbmxvY2tDb25kaXRpb24JVHhDb250ZXh0A1VJRBhhZGRyZXNzX3VubG9ja19jb25kaXRpb24DYmFnB2JhbGFuY2UMYmFzaWNfb3V0cHV0BmRlbGV0ZQxkZXN0cm95X25vbmUNZXhwaXJhdGlvbl91YxtleHBpcmF0aW9uX3VubG9ja19jb25kaXRpb24HZXh0cmFjdA5leHRyYWN0X2Fzc2V0cwJpZAdpc19zb21lCG1ldGFkYXRhDW5hdGl2ZV90b2tlbnMGb2JqZWN0Bm9wdGlvbgdyZWNlaXZlBnNlbmRlchlzdG9yYWdlX2RlcG9zaXRfcmV0dXJuX3VjJ3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbgN0YWcLdGltZWxvY2tfdWMZdGltZWxvY2tfdW5sb2NrX2NvbmRpdGlvbgh0cmFuc2Zlcgp0eF9jb250ZXh0BnVubG9jawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACCRQIAwwLAgEJABcIARwLCQEIBx8LCQEICBALCQEIBhYLCQEKAh4LCQEKAhsLCQEFAAUAAQAABDALADoAAQEBDAMMBwwGDAUMAgwEDgc4AAQTDQc4AQoBLhEGDgM4AgQaDQM4AwoBEQQOBjgEBCMNBjgFDQILATgGBSULAQELBzgHCwM4CAsGOAkLBBECCwILBQIBAwAACgQLAAsBOAoCAAoAFnN0YXJkdXN0X3VwZ3JhZGVfbGFiZWx+oRzrCwYAAAAFAQACAgIEBwY6CEAgCmAFAAIAAAIAFlNUQVJEVVNUX1VQR1JBREVfTEFCRUwLZHVtbXlfZmllbGQWc3RhcmR1c3RfdXBncmFkZV9sYWJlbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AAIBAQEAGGFkZHJlc3NfdW5sb2NrX2NvbmRpdGlvbscHoRzrCwYAAAAIAQASAhIqAzxMBIgBCAWQAakBB7kCvAMI9QVADLUGaQAIAQwBEAETAAkACgALAA4ADwEDDAEAAQIHBAADBgIBCAEEAAwABQEIAQABBgIIAQABBwQMAAgFCAEAAQAVAAEBAAAXAgMBAAAUBAUBAAAWBgcBAAAZCAEBAAAaCQMBAAAYCgUBAAMREg4BDAQNDA0ABRIRBQEABhIPAQEABw0TDQAIEhADAQAKDgwOCQ4HBwIHCAMLAgELBQEJAAELBQEJAAIHCAMLAgELBwEJAAELBwEJAAIHCAMLAgELBAEJAAELBAEJAAIHCAMLAgELAAEJAAELAAEJAAIHCAYLAgELBQEJAAIHCAYLAgELBwEJAAIHCAYLAgELBAEJAAABBwgDAQcIAQEJAAIHCAELAgELBQEJAAIHCAELAgELBwEJAAIHCAELAgELBAEJAAIHCAELAgEJAAEHCAYFQWxpYXMLQWxpYXNPdXRwdXQLQmFzaWNPdXRwdXQWQ29pbk1hbmFnZXJUcmVhc3VyeUNhcANOZnQJTmZ0T3V0cHV0CVJlY2VpdmluZwNVSUQYYWRkcmVzc191bmxvY2tfY29uZGl0aW9uBWFsaWFzDGFsaWFzX291dHB1dAxiYXNpY19vdXRwdXQMY29pbl9tYW5hZ2VyAmlkA25mdApuZnRfb3V0cHV0Bm9iamVjdA5wdWJsaWNfcmVjZWl2ZQdyZWNlaXZlCHRyYW5zZmVyIHVubG9ja19hbGlhc19hZGRyZXNzX293bmVkX2FsaWFzIHVubG9ja19hbGlhc19hZGRyZXNzX293bmVkX2Jhc2ljL3VubG9ja19hbGlhc19hZGRyZXNzX293bmVkX2NvaW5tYW5hZ2VyX3RyZWFzdXJ5HnVubG9ja19hbGlhc19hZGRyZXNzX293bmVkX25mdB51bmxvY2tfbmZ0X2FkZHJlc3Nfb3duZWRfYWxpYXMedW5sb2NrX25mdF9hZGRyZXNzX293bmVkX2Jhc2ljHHVubG9ja19uZnRfYWRkcmVzc19vd25lZF9uZnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAEAAAsFCwARCAsBOAACAQEAAAsFCwARCAsBOAECAgEAAAsFCwARCAsBOAICAwEAAAsFCwARCAsBOAMCBAEAAAsFCwARCwsBOAACBQEAAAsFCwARCwsBOAECBgEAAAsFCwARCwsBOAICABl0aW1lbG9ja191bmxvY2tfY29uZGl0aW9u9AKhHOsLBgAAAAoBAAQCBAgDDBQFIBwHPHkItQFABvUBCgr/AQUMhAJBDcUCAgAEAQUAAAQAAQECAAAHAAEAAAMCAwAABgQFAAECBgcAAggABggBAAIGCAAGCAEBAQEGCAABDgEGCAEBAxdUaW1lbG9ja1VubG9ja0NvbmRpdGlvbglUeENvbnRleHQSZXBvY2hfdGltZXN0YW1wX21zDWlzX3RpbWVsb2NrZWQZdGltZWxvY2tfdW5sb2NrX2NvbmRpdGlvbgp0eF9jb250ZXh0CXVuaXhfdGltZQZ1bmxvY2sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAACAQYOAAEAAAEMDgALAREBIAQGBQgHACcLABMAAQIBAQAAAQkLABECCwERAwboAwAAAAAAABpMJAICAQAAAQQLABAAFAIAAAAbZXhwaXJhdGlvbl91bmxvY2tfY29uZGl0aW9u6gOhHOsLBgAAAAoBAAQCBAgDDCMFLx8HTp4BCOwBQAasAgoKtgIJDL8Cdw22AwYABAEIAAAEAAEBAgAACgABAAACAgMAAAUEAwAABgQDAAAJBAUAAQMGCAABBwYDAAIIAAcIAQACBggABggBAQUBBggAAQ4BBggBAgUOAQMZRXhwaXJhdGlvblVubG9ja0NvbmRpdGlvbglUeENvbnRleHQSY2FuX2JlX3VubG9ja2VkX2J5EmVwb2NoX3RpbWVzdGFtcF9tcxtleHBpcmF0aW9uX3VubG9ja19jb25kaXRpb24Fb3duZXIOcmV0dXJuX2FkZHJlc3MGc2VuZGVyCnR4X2NvbnRleHQJdW5peF90aW1lBnVubG9jawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAAIDBQUGBQkOAAEAAAESDgAKAS4RAQsBLhEGIQQKBQwHACcLABMAAQEBAgEBAAAHFAsBEQUG6AMAAAAAAAAaTAwDCgARBAsDJQQPCwARAwwCBRILABECDAILAgICAQAAAQQLABAAFAIDAQAAAQQLABABFAIEAQAAAQQLABACFAIAAAABAAIAJ3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbvkDoRzrCwYAAAAKAQAKAgoUAx4iBEAGBUY5B3/LAQjKAkAKigMHDJEDNw3IAwQACwEEAQUBDAENAAIEAAEABAEAAQIBDAEAAQQDAgAADgABAQAACAIDAAAJAgQAAQoGBwEAAgYICQEAAwcKAQEMAwUEBQUJAwgABwsBAQkABwgDAAEGCAABBQEDAQkAAgcLAQEJAAMBCwEBCQACCwEBCQAHCAMBCwIBCQACCQAFB0JhbGFuY2UEQ29pbiNTdG9yYWdlRGVwb3NpdFJldHVyblVubG9ja0NvbmRpdGlvbglUeENvbnRleHQHYmFsYW5jZQRjb2luDGZyb21fYmFsYW5jZQ9wdWJsaWNfdHJhbnNmZXIOcmV0dXJuX2FkZHJlc3MNcmV0dXJuX2Ftb3VudAVzcGxpdCdzdG9yYWdlX2RlcG9zaXRfcmV0dXJuX3VubG9ja19jb25kaXRpb24IdHJhbnNmZXIKdHhfY29udGV4dAZ1bmxvY2sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAICCAUJAwABAAABDgsBDgARAjgACwI4AQ4AEQE4AgsAEwABAQIBAQAAAQQLABAAFAICAQAAAQQLABABFAIAAAABAAsWc3RhcmR1c3RfdXBncmFkZV9sYWJlbBZTVEFSRFVTVF9VUEdSQURFX0xBQkVMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoZdGltZWxvY2tfdW5sb2NrX2NvbmRpdGlvbhdUaW1lbG9ja1VubG9ja0NvbmRpdGlvbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6J3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbiNTdG9yYWdlRGVwb3NpdFJldHVyblVubG9ja0NvbmRpdGlvbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6BWlyYzI3DUlyYzI3TWV0YWRhdGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegNuZnQDTkZUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoDbmZ0A05mdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6G2V4cGlyYXRpb25fdW5sb2NrX2NvbmRpdGlvbhlFeHBpcmF0aW9uVW5sb2NrQ29uZGl0aW9uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoKbmZ0X291dHB1dAlOZnRPdXRwdXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegxiYXNpY19vdXRwdXQLQmFzaWNPdXRwdXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegVhbGlhcwVBbGlhcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6DGFsaWFzX291dHB1dAtBbGlhc091dHB1dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAAAAAAAAAAMAAAIAAAAAAAAAAFAJpwmVA2zIxK7sHUHcRi5M40pzOpWAIkJg0Kn7g56vgvoK+dOQ95d+0X6PcmYAITgthiL8v1h1UMyeBzs167ptAAAAAAAAAAAAwCn3PVQFAACayDeQSKAd7bSBXEb31j8XAxWsjmyj7VDOGEu19DUNZQAAAQAAAAAAAAAAKA9AjEIzQGIAUjYqu3ZaGw7D+/t0I6wK+Ro6vP474xz4AABDT9eUagAAKKcFh6eybsyFARCayucBi7tbdn0tuIhBeFIoMwO8MxYAAAEAAAAAAAAAACgQXdpcMz0kwwXncXCMqrr5zSrD6x9jwAv+QWxnFh1cHQAAQ0/XlGoAAJ9uiJzdOLFlXIamYgJ8k67tmDtMTcVQhXj3DTS8sNxYAAABAAAAAAAAAAAoFpzNtR8fRvJugcwAqdNqwlgEFkFNjRy2K2t2KyGPBSQAAENP15RqAACayDeQSKAd7bSBXEb31j8XAxWsjmyj7VDOGEu19DUNZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQCAgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwxzdGFraW5nX3Bvb2wVUG9vbFRva2VuRXhjaGFuZ2VSYXRlAAAAAAAAAAAAOBgheOYpnHAbWDQ/bIQLb2tTSsamH/84PLk/oSG3hM9WAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbs0XAY2u2V00FP4djUP3gusyIYohyVYt+1CPPk/c3z6AAABAAAAAAAAAAAoIr/c9G/+L6btbxXpqg594yxPKoj6I542xqwn+c2IZ+QAAENP15RqAAAWs8E5pn65JAtMJIwkLL3K5R5Dz3kxLca0T/vjehLaOQAAAQAAAAAAAAAAKDSHDAjnIrKiUy6qHjJ9WarSDzVkPO0zYLrC04mvJImWAABDT9eUagAAn26InN04sWVchqZiAnyTru2YO0xNxVCFePcNNLyw3FgAAAEAAAAAAAAAACg4zviVoJTewBybDzYXjmhgxshk48g3MIJZC7BmnR/pDgAAQ0/XlGoAALw1/3oTxbsHO5ja9gyCRw7TDBjz+tuwmhJGUfo9Wo0CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAINZHluYW1pY19maWVsZAVGaWVsZAICBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDHN0YWtpbmdfcG9vbBVQb29sVG9rZW5FeGNoYW5nZVJhdGUAAAAAAAAAAAA4OkdtbDEhk2fiUy4o3M5GccmE9B3BamhirtR5KWGHWokAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABiOPBnALUBYPqu2dpu/ZTk5dcJFmpfX8cySI6OCAkg1gAAAEAAAAAAAAAAChIcHqRjTrG4wxEBPhfvzJV3qjx2BTn76/1ojzJqajJzAAAQ0/XlGoAAJ9uiJzdOLFlXIamYgJ8k67tmDtMTcVQhXj3DTS8sNxYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX2NhcB9VbnZlcmlmaWVkVmFsaWRhdG9yT3BlcmF0aW9uQ2FwAAAAAAAAAAAAQE5ZgUBj7Qm4ednXGFk/qEVR4lhqVNFEc+PyeowU1Za1F6Cb4Ab/Sy2akHDzUuWyclfiHDSgaQPFMhZ/W82F7ZMAF6Cb4Ab/Sy2akHDzUuWyclfiHDSgaQPFMhZ/W82F7ZMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw12YWxpZGF0b3JfY2FwH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAAAAAAAAAAAABAV9HYbnoUo31c7ag+V7K84iSMsgiRrXMBDO+8kfU1+UDk3qzLpPClCGfr1wefrpYQJXczVlplN5hOaNg96LlYEQDk3qzLpPClCGfr1wefrpYQJXczVlplN5hOaNg96LlYEQAAAQAAAAAAAAAAKF3vcU2PXRBqN/7nsazAVyFg0xp0eiiXmhbCiiq/Y6wdAABDT9eUagAAD3roDxNW8al/e8qSMX1VsrnQKhFCf2d00KrpSeBUxd4AAAEAAAAAAAAAAChfYfsYGjwPPrzv5yzZnPSOCbt5NUGv1ESh1npdEZv0ZwAAQ0/XlGoAAA966A8TVvGpf3vKkjF9VbK50CoRQn9ndNCq6UngVMXeAAABAAAAAAAAAAAoaUMEuiNhfG7MvabIJRLbNxIfZtvJM+3uw0e14s2InKkAAENP15RqAAC8Nf96E8W7BzuY2vYMgkcO0wwY8/rbsJoSRlH6PVqNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQCAgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lchFJb3RhU3lzdGVtU3RhdGVWMQAAAAAAAAAAAJYZavKit8pgv3YXSt/T6cSVf46Td1lgMYL5tGx/bF8ZxtIBAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAIt6ZosVAbXjgh49/RWejI4ImO3H8cKvMLF1s9mXla40AACBJjPCkgwAAKfc91AVAAQXoJvgBv9LLZqQcPNS5bJyV+IcNKBpA8UyFn9bzYXtk2CNz/bRVQQKZW9IGbExEbUsV0aoa6cvOV+6/i7DhH0egUDmJKdR/fa18gULxyBc+dMABMkLDHQK/9Mmzmc8wrI6LSTVPir+sobfxmj9QGAInW0rF7eZ3Tb5DTMuVKejONQgMm1xM6U8RzYIBYuJ7aBOTaoDzjxMAU1mqr76A9babKQgGlQocWxofkJY2LnG8pjm/96kxe+kxThwc+8Kq+ErJwowsoMtzyMvKIKueYW0Q0545/lwZ4Ph9Oo/+jzl3o46knt22EO7YIf+ofN4WddyQ0puC3ZhbGlkYXRvci0xAAAAHS9pcDQvMTI3LjAuMC4xL3RjcC8zNzUzMy9odHRwGC9pcDQvMTI3LjAuMC4xL3VkcC8zODY2MRgvaXA0LzEyNy4wLjAuMS91ZHAvNDY3OTEAAAAAAAAAPzlr9dapZjz5AI3mxIC2f43WKKiOWL2ZFhCM+Y+sd+MAAAAAAAAAAMQJAAAAAAAATlmBQGPtCbh52dcYWT+oRVHiWGpU0URz4/J6jBTVlrXoAwAAAAAAACktd8HacoPr9LGKZF7eTt9oPPulpEHtlaSm+yjFXkDMAQAAAAAAAAAAAADAKfc9VAUAAAAAAAAAAAAAwCn3PVQFALs0XAY2u2V00FP4djUP3gusyIYohyVYt+1CPPk/c3z6AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxlgxd5XxfD4XMPnpGNWym5/SA1jJBV/x98MyONRBxQQAAAAAAAAAAyAAAAAAAAAAAwCn3PVQFAOgDAAAAAAAAyAAAAAAAAADFAH2GaTYQyy8ImCpFwYTRfUt6689QmaTh9GzBH6pwYAAAAAAAAAAAmsg3kEigHe20gVxG99Y/FwMVrI5so+1QzhhLtfQ1DWVgmfJe9h+AMrkUY2RgmCxcxvE07x3a52ZX8sv+wev8jQlzdAgN9vzw3Li8Sw2OCvXYDrv/K0xZn1T0LWMS38MUJ2B4wcw0fru+xRmL4lhRPzhrkw0CwnSagD4jMJVevRoQIFELbJTU09ugovmgJ7Mlidafay2i2lMcwFHbNFeggDJiIBB/2EKoh1uj6gBuc5fv5fC2KfcuVE9DZ8HSrd0E0OV+MJDyu/mIvYN4aXjD1tnR+eSx75QQlXfvTESi89ju9CVxdiFPgez56DREqu1k+VR0sgt2YWxpZGF0b3ItMAAAAB0vaXA0LzEyNy4wLjAuMS90Y3AvMzk4NDcvaHR0cBgvaXA0LzEyNy4wLjAuMS91ZHAvMzQwNDkYL2lwNC8xMjcuMC4wLjEvdWRwLzQwMzQ5AAAAAAAAAHSG7alIjl0SshvEEhUpDe8c2eaABGI8+F4JQHULMvBfAAAAAAAAAADECQAAAAAAAHYkqyvcwdQ4aFr22q/hkgGV74JfR41cqCrofVPW8lh16AMAAAAAAAD6CvnTkPeXftF+j3JmACE4LYYi/L9YdVDMngc7Neu6bQEAAAAAAAAAAAAAwCn3PVQFAAAAAAAAAAAAAMAp9z1UBQCI48GcAtQFg+q7Z2m79lOTl1wkWal9fxzJIjo4ICSDWAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAze1OkNV3nJ/niDLXqk3NNmfu0i8a9cnEc6j4FrIt0PMAAAAAAAAAAMgAAAAAAAAAAMAp9z1UBQDoAwAAAAAAAMgAAAAAAAAAirLn/ulebuv7wfFkFZpFfrR4auCLWrZU4D3Xk9tYhh0AAAAAAAAAAAGzhLPYHqN1hYrl8FKPmwiwDUezEes0nXSJ6ik6Pv5WYK3e75TYmMiOQNve/5HF8QX5zyx0f7byvDfsA9bJHtsuGleM9US+iwkx4l8kPVmwww2YoWzjuh1s8df1BCaZLrCtsxNLBVvoFA0ttfJfsA2D+OQiK/Af1qyTof9NedpY8iDESDn2afsC5pO7tW3qTOk1Bknv3qcDgK2+LZxc3k6JZiAhC5aau5oV9IvXyrxl6FroPplllpBlYEJFWhM0ATmWFTCgEB0EKnuJhtgYNc1MhGDX6R4CE1NERxfPT+9qBvM+a2Px2eSl+nclBZxn5g2YH+MLdmFsaWRhdG9yLTIAAAAdL2lwNC8xMjcuMC4wLjEvdGNwLzM1MDE3L2h0dHAYL2lwNC8xMjcuMC4wLjEvdWRwLzQxNTIzGC9pcDQvMTI3LjAuMC4xL3VkcC80NTU5OQAAAAAAAABHJp048L6Z/6IOLS82qaQlF0h+L/cnI8l8lVayD8RoCAAAAAAAAAAAxAkAAAAAAAC/VAGch/czQ5AziVZreIGNuu7VKxBOh4Fsrguj3Kjg9egDAAAAAAAAFjR5EHQ23hJs9+jo04xIFMhNq6/PzBjxsG1n++6qM9oBAAAAAAAAAAAAAMAp9z1UBQAAAAAAAAAAAADAKfc9VAUAQ69dsrpnIKhYw3rV7Y9o69iFIKM4odfaffMYj0RTaD4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANMEHIADbDeUzovgBqSn29HBaGuX3hDezia6I1dA8JcaAAAAAAAAAADIAAAAAAAAAADAKfc9VAUA6AMAAAAAAADIAAAAAAAAAHe6WUDbd5Q7xPopYYBRBU74X9oY7Z9r5rNgcQLjZu0dAAAAAAAAAADk3qzLpPClCGfr1wefrpYQJXczVlplN5hOaNg96LlYEWCz/V77XIckA6StE/EZlRNgbSM1SoRSSa6hV1ZMI/88FcbJ5lK3LXQOjKy5PLzAaGsOwMwMHHYL+0K0NlGfxagFS3ZTOpep8jmH0JfvlrHyUmuyBz+hCncZlBdyNH7ydPQgp8cOwXjsMeNpYTrH6oX8dVO90TMgUXSoQo4Epua5kIMgIUQ/8NhhTcUuMlbWATQ7TuEmuOTyTQTd70pZN/likeAwiIQtUKNpS68H/r/5j59rO6ipe080f+rLvfYamYvRN/7P8cDg3uHTzzDyC6KSyz8lC3ZhbGlkYXRvci0zAAAAHS9pcDQvMTI3LjAuMC4xL3RjcC8zMzc0Ny9odHRwGC9pcDQvMTI3LjAuMC4xL3VkcC8zNzc2MRgvaXA0LzEyNy4wLjAuMS91ZHAvNDAyNjUAAAAAAAAAMeCS0DUYUh5B4wvmL9/9m/41L84ykOqsE5KjvQsv82QAAAAAAAAAAMQJAAAAAAAAV9HYbnoUo31c7ag+V7K84iSMsgiRrXMBDO+8kfU1+UDoAwAAAAAAAAT552l66vOxceBe5eX76mO4XLrAM5Zxld2MMJggBjK6AQAAAAAAAAAAAADAKfc9VAUAAAAAAAAAAAAAwCn3PVQFAKrA4Pg1N4PhH10WrSb948FgPM5jfgus1b/W8mtxgZNmAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQOeuEOE9Nfnk++yqgdBz78yAH7YaNcQIssZ5XOTACAQAAAAAAAAAAyAAAAAAAAAAAwCn3PVQFAOgDAAAAAAAAyAAAAAAAAAAI67+fu1djqpky3iqQ/UYS5MNEP93Cz08/HO9IvdtxMgAAAAAAAAAAtNiobexn0aRlO9PGrmU61EsXiQmjZrJU/GK/rVTQOvMAAAAAAAAAAAAJzwZhJ4cWtDlWjVT/THVyaarNpnB0Er3XR1wFahio7wQAAAAAAAAAlBz9/VSUbsdYQDpGxnOduipZQrBwYiPliwqIBBBJ/M4AAAAAAAAAACer3KgHj/yHujb1UH8wFoMeB1St7a/goShi/R1hK15mAAAAAAAAAAAAHGnyYs6WG/8N4I7wIw2xCoh0crRb3i4Z+9FKoRPdoVUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXCYFAAAAAAQAAAAAAAAAlgAAAAAAAAAAAI1J/RoHAADAKfc9VAUAAIDGpH6NAwAHAAAAAAAAAC0qZTTJJpldM5q820LiruIy3SSlfdIhyyR1Y8aXmXwaAAAAAAAAAAAA6AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1pZFCJMBAAAByMvWTyKAKArotMtSO4UyLt3J1+vQe6ih7hJ5zTnY+QAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAACAAAAAAAAAABQc3oygoajy94cuUJlI15b85GXSVyHBbyxigYr8Wpgrs0E+edpeurzsXHgXuXl++pjuFy6wDOWcZXdjDCYIAYyugAAAAAAAAAAAMAp9z1UBQAA5N6sy6TwpQhn69cHn66WECV3M1ZaZTeYTmjYPei5WBEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw12YWxpZGF0b3JfY2FwH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAAAAAAAAAAAABAdiSrK9zB1DhoWvbar+GSAZXvgl9HjVyoKuh9U9byWHWayDeQSKAd7bSBXEb31j8XAxWsjmyj7VDOGEu19DUNZQCayDeQSKAd7bSBXEb31j8XAxWsjmyj7VDOGEu19DUNZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQCBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBm9iamVjdAJJRAAEAAAAAAAAAABgeBXFSjpSfce242M4JpyPG/z/KEu4eFY1isYaQjDvfrEE+edpeurzsXHgXuXl++pjuFy6wDOWcZXdjDCYIAYyuuTerMuk8KUIZ+vXB5+ulhAldzNWWmU3mE5o2D3ouVgRAQnPBmEnhxa0OVaNVP9MdXJpqs2mcHQSvddHXAVqGKjvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHZGlzcGxheQdEaXNwbGF5AQcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegNuZnQDTmZ0AAAAAAAAAAAA5AJ4cwpJeFDNM5CvfdEBJs1bc9ONPgLI3vs7wdTMwAyDpQgEbmFtZRl7aW1tdXRhYmxlX21ldGFkYXRhLm5hbWV9CWltYWdlX3VybBh7aW1tdXRhYmxlX21ldGFkYXRhLnVyaX0LZGVzY3JpcHRpb24ge2ltbXV0YWJsZV9tZXRhZGF0YS5kZXNjcmlwdGlvbn0HY3JlYXRvciB7aW1tdXRhYmxlX21ldGFkYXRhLmlzc3Vlcl9uYW1lfQd2ZXJzaW9uHHtpbW11dGFibGVfbWV0YWRhdGEudmVyc2lvbn0KbWVkaWFfdHlwZR97aW1tdXRhYmxlX21ldGFkYXRhLm1lZGlhX3R5cGV9D2NvbGxlY3Rpb25fbmFtZSR7aW1tdXRhYmxlX21ldGFkYXRhLmNvbGxlY3Rpb25fbmFtZX0QaW1tdXRhYmxlX2lzc3VlchJ7aW1tdXRhYmxlX2lzc3Vlcn0BAAMAAAEAAAAAAAAAACh6DVB9U7xAl/QDg1CNkSXDOzHDXxbpgAv5EtxNWlr4IQAAQ0/XlGoAALw1/3oTxbsHO5ja9gyCRw7TDBjz+tuwmhJGUfo9Wo0CAAABAAAAAAAAAAAofimnmLVdeTaGJcpBZuJzBfEw+b8Y+nze1hRrxYJlMRkAAENP15RqAAAWs8E5pn65JAtMJIwkLL3K5R5Dz3kxLca0T/vjehLaOQAAAQAAAAAAAAAAKISwAMK8alwKtRA9SdtIY+FvcygFb6k0U1t6vNX/wBzNAABDT9eUagAASBhaYT2gVp/V1+khgD1grIFyUmvoX5WkFsjUmnhII/oAAAEAAAAAAAAAACiKsGEcycp6P7bQJ5RKenq5wbJ4MYa7n4sLhoxXOXlKTAAAQ0/XlGoAABazwTmmfrkkC0wkjCQsvcrlHkPPeTEtxrRP++N6Eto5AAABAAAAAAAAAAAoir9uEicxm0xaAdUyxJ9EDpSwmu9JIGCaP353GGn9Ew4AAENP15RqAACfboic3TixZVyGpmICfJOu7Zg7TE3FUIV49w00vLDcWAAAAQAAAAAAAAAAKI9DYZXC+ZeDW+c30bQp8UKuqK3y3HH0blRlK0q+zFfaAABDT9eUagAAvDX/ehPFuwc7mNr2DIJHDtMMGPP627CaEkZR+j1ajQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg1keW5hbWljX2ZpZWxkBUZpZWxkAgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgZvYmplY3QCSUQABAAAAAAAAAAAYI+aRvyMDwP/557e+AJg0sFQsJC55W2A/g6EYpcZy0wK+gr505D3l37Rfo9yZgAhOC2GIvy/WHVQzJ4HOzXrum2ayDeQSKAd7bSBXEb31j8XAxWsjmyj7VDOGEu19DUNZQEJzwZhJ4cWtDlWjVT/THVyaarNpnB0Er3XR1wFahio7wAAAQAAAAAAAAAAKJ8rrXULdHD/oLdpl0amFj4X99WvxEkhGtbh2PEXAvP/AABDT9eUagAASBhaYT2gVp/V1+khgD1grIFyUmvoX5WkFsjUmnhII/oAAAEAAAAAAAAAACifW3dlrY+flkocM4d6EMFlif6gMJ584HbiP1gxmwdTcgAAQ0/XlGoAAA966A8TVvGpf3vKkjF9VbK50CoRQn9ndNCq6UngVMXeAAABAAAAAAAAAAAoo+pHKTliQCD6LBs5ZEpSiIwnIAhdTQbaFw5fjiQmjHMAAENP15RqAAC8Nf96E8W7BzuY2vYMgkcO0wwY8/rbsJoSRlH6PVqNAgAAAQAAAAAAAAAAKKuAvxqSPQvyMOWfXqhf/NKK8OWG5xblTA/CSMCh57pDAABDT9eUagAASBhaYT2gVp/V1+khgD1grIFyUmvoX5WkFsjUmnhII/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg1keW5hbWljX2ZpZWxkBUZpZWxkAgIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGcmFuZG9tC1JhbmRvbUlubmVyAAAAAAAAAAAAQbAKCCrqPlcdgZKazbReFb3+oVQ276xdmGwnsscv98tVAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYeWAvuZi20jOydlTVAVHQTH+ebBzK08DXazn6Ff1qslAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAINZHluYW1pY19maWVsZAVGaWVsZAICBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDHN0YWtpbmdfcG9vbBVQb29sVG9rZW5FeGNoYW5nZVJhdGUAAAAAAAAAAAA4stWGTQaAfAgPc/IA/tp5EEgpVWrDUfiQsUj8dgsiYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqsDg+DU3g+EfXRatJv3jwWA8zmN+C6zVv9bya3GBk2YAAAEAAAAAAAAAACi3L8ajzbeWKPAEh612NhFFKvczJ29FzOLk43faj1uALQAAQ0/XlGoAAEgYWmE9oFaf1dfpIYA9YKyBclJr6F+VpBbI1Jp4SCP6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAINZHluYW1pY19maWVsZAVGaWVsZAIHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGb2JqZWN0AklEAAQAAAAAAAAAAGC4LTNpOODF6dKLeJ2sW/gkxFdmNoSrWp9WAjIdKN7VtCktd8HacoPr9LGKZF7eTt9oPPulpEHtlaSm+yjFXkDMF6Cb4Ab/Sy2akHDzUuWyclfiHDSgaQPFMhZ/W82F7ZMBCc8GYSeHFrQ5Vo1U/0x1cmmqzaZwdBK910dcBWoYqO8AAAIAAAAAAAAAAFC4inJwXm1qGPGu0guKRtBcgWSEOn5UABR7RoROmfbRUxY0eRB0Nt4SbPfo6NOMSBTITauvz8wY8bBtZ/vuqjPaAAAAAAAAAAAAwCn3PVQFAAABs4Sz2B6jdYWK5fBSj5sIsA1HsxHrNJ10ieopOj7+VgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDXZhbGlkYXRvcl9jYXAfVW52ZXJpZmllZFZhbGlkYXRvck9wZXJhdGlvbkNhcAAAAAAAAAAAAEC/VAGch/czQ5AziVZreIGNuu7VKxBOh4Fsrguj3Kjg9QGzhLPYHqN1hYrl8FKPmwiwDUezEes0nXSJ6ik6Pv5WAAGzhLPYHqN1hYrl8FKPmwiwDUezEes0nXSJ6ik6Pv5WAAABAAAAAAAAAAAoxfefigME9fxU87shBg6UFm8R0Cr2qW1awzPvXr7yj5wAAENP15RqAAABs4Sz2B6jdYWK5fBSj5sIsA1HsxHrNJ10ieopOj7+VgAAAQAAAAAAAAAAKMsj2C4HcfyU6gPSVI3bquC8ON/qqcF9SWdX4MBU+mWhAABDT9eUagAAD3roDxNW8al/e8qSMX1VsrnQKhFCf2d00KrpSeBUxd4AAAEAAAAAAAAAACjM7u/JIGdaV49nO/ycuuqXbdQ32UVEwzm0wAMsFWBehQAAQ0/XlGoAAJ9uiJzdOLFlXIamYgJ8k67tmDtMTcVQhXj3DTS8sNxYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEY29pbgxDb2luTWV0YWRhdGEBBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBGlvdGEESU9UQQAAAAAAAAAAAG/SuC/Kb63kXKy0q/bdftW4+qUjBj2HpUji8qXa0Z9wxwkESU9UQQRJT1RBKFRoZSBtYWluIChnYXMpdG9rZW4gb2YgdGhlIElPVEEgTmV0d29yay4BGWh0dHBzOi8vaW90YS5vcmcvbG9nby5wbmcDAAABAAAAAAAAAAAo3kJxKcBEuZKKpWKBa262eds25wuoOZo/xLtiBVQe1+wAAENP15RqAAAWs8E5pn65JAtMJIwkLL3K5R5Dz3kxLca0T/vjehLaOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQCBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBm9iamVjdAJJRAAEAAAAAAAAAABg3+BVSuXw/ZqvyKUZm1zByMjCYqj6TNC5QmqR2uTxvNoWNHkQdDbeEmz36OjTjEgUyE2rr8/MGPGwbWf77qoz2gGzhLPYHqN1hYrl8FKPmwiwDUezEes0nXSJ6ik6Pv5WAQnPBmEnhxa0OVaNVP9MdXJpqs2mcHQSvddHXAVqGKjvAAABAAAAAAAAAAAo4Y4tuArxRIaB4QYw14nbrxrWx1bnEp+xkNFZtgmvz+oAAENP15RqAAAPeugPE1bxqX97ypIxfVWyudAqEUJ/Z3TQqulJ4FTF3gAAAgAAAAAAAAAAUOMDN3bZACHVsA31KM+N73/Hw2dHda7DLz4J49KgEhR8KS13wdpyg+v0sYpkXt5O32g8+6WkQe2VpKb7KMVeQMwAAAAAAAAAAADAKfc9VAUAABegm+AG/0stmpBw81LlsnJX4hw0oGkDxTIWf1vNhe2TAAABAAAAAAAAAAAo7FXBkpsBaUMs0BLiyTMTqyFq4h2eh8opDnpnulT+y7wAAENP15RqAADk3qzLpPClCGfr1wefrpYQJXczVlplN5hOaNg96LlYEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQCAgcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwxzdGFraW5nX3Bvb2wVUG9vbFRva2VuRXhjaGFuZ2VSYXRlAAAAAAAAAAAAOPGkvm0Q9EULUMMjAYMIHlKsy0PBSu/+RFsAU4a9E6XyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUOvXbK6ZyCoWMN61e2PaOvYhSCjOKHX2n3zGI9EU2g+AAABAAAAAAAAAAAo81bsOvITsQc2US/FJldxHfmMECVD0HnG7/ib2nJBHfMAAENP15RqAAAXoJvgBv9LLZqQcPNS5bJyV+IcNKBpA8UyFn9bzYXtkwAAAQAAAAAAAAAAKPZQnXva+upv8hC/gjK5XzxBScRswT8Um3MeG54S2aeaAABDT9eUagAAFrPBOaZ+uSQLTCSMJCy9yuUeQ895MS3GtE/743oS2jkAAAEAAAAAAAAAACj3cCyvGje/fFlyK1dJaQO+Ara9N50U6kx1NjFBum+/IQAAQ0/XlGoAAEgYWmE9oFaf1dfpIYA9YKyBclJr6F+VpBbI1Jp4SCP6AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6A25mdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHZGlzcGxheQ5EaXNwbGF5Q3JlYXRlZAEHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoDbmZ0A05mdAAgeHMKSXhQzTOQr33RASbNW3PTjT4CyN77O8HUzMAMg6UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegNuZnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2Rpc3BsYXkOVmVyc2lvblVwZGF0ZWQBBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6A25mdANOZnQA5AJ4cwpJeFDNM5CvfdEBJs1bc9ONPgLI3vs7wdTMwAyDpQEACARuYW1lGXtpbW11dGFibGVfbWV0YWRhdGEubmFtZX0JaW1hZ2VfdXJsGHtpbW11dGFibGVfbWV0YWRhdGEudXJpfQtkZXNjcmlwdGlvbiB7aW1tdXRhYmxlX21ldGFkYXRhLmRlc2NyaXB0aW9ufQdjcmVhdG9yIHtpbW11dGFibGVfbWV0YWRhdGEuaXNzdWVyX25hbWV9B3ZlcnNpb24ce2ltbXV0YWJsZV9tZXRhZGF0YS52ZXJzaW9ufQptZWRpYV90eXBlH3tpbW11dGFibGVfbWV0YWRhdGEubWVkaWFfdHlwZX0PY29sbGVjdGlvbl9uYW1lJHtpbW11dGFibGVfbWV0YWRhdGEuY29sbGVjdGlvbl9uYW1lfRBpbW11dGFibGVfaXNzdWVyEntpbW11dGFibGVfaXNzdWVyfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/crates/sui-sdk-types/src/transaction/fixtures/ptb b/crates/sui-sdk-types/src/transaction/fixtures/ptb deleted file mode 100644 index 4769bfa2b..000000000 --- a/crates/sui-sdk-types/src/transaction/fixtures/ptb +++ /dev/null @@ -1 +0,0 @@ -AAACAQBd73FNj10Qajf+57GswFchYNMadHool5oWwooqv2OsHQEAAAAAAAAAIMywMsW0Eq8kxUMvPriunI4/5MFevTlujsluEO2eq3sBAAkBQEIPAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA3BheQlzcGxpdF92ZWMBBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBGlvdGEESU9UQQACAQAAAQEAD3roDxNW8al/e8qSMX1VsrnQKhFCf2d00KrpSeBUxd4BX2H7GBo8Dz687+cs2Zz0jgm7eTVBr9REodZ6XRGb9GcBAAAAAAAAACBUwR888cuuNQritn9jaBy8dbKeUZ0CwB8kRLqKHQMgig966A8TVvGpf3vKkjF9VbK50CoRQn9ndNCq6UngVMXe6AMAAAAAAABAQg8AAAAAAAA= \ No newline at end of file diff --git a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore b/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore deleted file mode 100644 index ea8c4bf7f..000000000 --- a/crates/sui-sdk-types/src/transaction/fixtures/update-transaction-fixtures/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/crates/sui-transaction-builder/Cargo.toml b/crates/sui-transaction-builder/Cargo.toml deleted file mode 100644 index 61e20b2ce..000000000 --- a/crates/sui-transaction-builder/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "sui-transaction-builder" -version = "0.1.0" -authors = ["Stefan Stanciulescu ", "Brandon Williams "] -license = "Apache-2.0" -edition = "2021" -publish = false -readme = "README.md" -description = "Transaction API for the Rust SDK for the Sui Blockchain" - -[features] -default = [] -schemars = ["dep:schemars", "sui-types/schemars"] - -[dependencies] -base64ct = { version = "1.6", features = ["std"] } -bcs = "0.1.6" -serde = { version = "1.0", features = ["derive"] } -serde_with = { version = "3.9", default-features = false, features = ["alloc"] } -sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["serde", "hash"] } -thiserror = "2.0" -serde_json = { version = "1.0.128" } - -# JsonSchema definitions for types, useful for generating an OpenAPI Specificaiton. -schemars = { version = "0.8.21", optional = true } - -[dev-dependencies] -anyhow = "1.0" -rand = "0.8" -serde_json = "1.0" -sui-crypto = { package = "sui-crypto", path = "../sui-crypto" , features = ["ed25519"] } -sui-graphql-client = { package = "sui-graphql-client", path = "../sui-graphql-client" } -sui-types = { package = "sui-sdk-types", path = "../sui-sdk-types", features = ["rand"] } -tokio = { version = "1.0", features = ["full"] } diff --git a/dprint.json b/dprint.json index f18a8fe8c..c7654b5b3 100644 --- a/dprint.json +++ b/dprint.json @@ -10,14 +10,14 @@ "**/CHANGELOG.md", "**/target/", "**/build/", - "**/pnpm-lock.yaml", ], "toml": { "lineWidth": 80 }, "plugins": [ - "https://plugins.dprint.dev/toml-0.6.2.wasm", - "https://plugins.dprint.dev/markdown-0.17.1.wasm", - "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.4.0.wasm" + "https://plugins.dprint.dev/markdown-0.17.8.wasm", + "https://plugins.dprint.dev/toml-0.6.4.wasm", + "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm", + "https://plugins.dprint.dev/g-plane/pretty_graphql-v0.2.1.wasm" ] -} \ No newline at end of file +} From f3f0adc212d3a16f678817c7b1f08a4cf509ca56 Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Mon, 26 May 2025 11:48:09 +0200 Subject: [PATCH 102/107] chore: Fix build issues --- crates/iota-sdk-types/src/transaction/mod.rs | 2 -- crates/iota-sdk-types/src/type_tag/parse.rs | 18 +++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/iota-sdk-types/src/transaction/mod.rs b/crates/iota-sdk-types/src/transaction/mod.rs index 5f7cb3cd9..b0584ce79 100644 --- a/crates/iota-sdk-types/src/transaction/mod.rs +++ b/crates/iota-sdk-types/src/transaction/mod.rs @@ -323,7 +323,6 @@ pub struct ChangeEpoch { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct ChangeEpochV2 { /// The next (to become) epoch ID. @@ -374,7 +373,6 @@ pub struct ChangeEpochV2 { derive(serde_derive::Serialize, serde_derive::Deserialize) )] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(test_strategy::Arbitrary))] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] pub struct SystemPackage { #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))] diff --git a/crates/iota-sdk-types/src/type_tag/parse.rs b/crates/iota-sdk-types/src/type_tag/parse.rs index 76fa5e1e8..bcff97048 100644 --- a/crates/iota-sdk-types/src/type_tag/parse.rs +++ b/crates/iota-sdk-types/src/type_tag/parse.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use winnow::{ - PResult, Parser, + ModalResult, Parser, ascii::space0, combinator::{alt, delimited, eof, opt, separated}, stream::AsChar, @@ -16,11 +16,11 @@ use super::{Address, Identifier, StructTag, TypeTag}; // r"(?:[a-zA-Z][a-zA-Z0-9_]*)|(?:_[a-zA-Z0-9_]+)"; static MAX_IDENTIFIER_LENGTH: usize = 128; -pub(super) fn parse_identifier(mut input: &str) -> PResult<&str> { +pub(super) fn parse_identifier(mut input: &str) -> ModalResult<&str> { (identifier, eof).take().parse_next(&mut input) } -fn identifier<'s>(input: &mut &'s str) -> PResult<&'s str> { +fn identifier<'s>(input: &mut &'s str) -> ModalResult<&'s str> { alt(( (one_of(|c: char| c.is_alpha()), valid_remainder(0)), ('_', valid_remainder(1)), @@ -42,17 +42,17 @@ fn valid_remainder<'a>( } } -fn parse_address<'s>(input: &mut &'s str) -> PResult<&'s str> { +fn parse_address<'s>(input: &mut &'s str) -> ModalResult<&'s str> { ("0x", take_while(1..=64, AsChar::is_hex_digit)) .take() .parse_next(input) } -pub(super) fn parse_type_tag(mut input: &str) -> PResult { +pub(super) fn parse_type_tag(mut input: &str) -> ModalResult { (type_tag, eof).parse_next(&mut input).map(|(t, _)| t) } -fn type_tag(input: &mut &str) -> PResult { +fn type_tag(input: &mut &str) -> ModalResult { alt(( "u8".value(TypeTag::U8), "u16".value(TypeTag::U16), @@ -69,11 +69,11 @@ fn type_tag(input: &mut &str) -> PResult { .parse_next(input) } -pub(super) fn parse_struct_tag(mut input: &str) -> PResult { +pub(super) fn parse_struct_tag(mut input: &str) -> ModalResult { (struct_tag, eof).parse_next(&mut input).map(|(s, _)| s) } -fn struct_tag(input: &mut &str) -> PResult { +fn struct_tag(input: &mut &str) -> ModalResult { let (address, _, module, _, name) = ( parse_address.try_map(|s| s.parse::
()), "::", @@ -96,7 +96,7 @@ fn struct_tag(input: &mut &str) -> PResult { }) } -fn generics(input: &mut &str) -> PResult> { +fn generics(input: &mut &str) -> ModalResult> { separated(1.., delimited(space0, type_tag, space0), ",").parse_next(input) } From 8507c951ad1e7c2caf32283232341c4fa1083548 Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Mon, 26 May 2025 12:01:44 +0200 Subject: [PATCH 103/107] chore: Rename branches and URLs --- .github/workflows/ci.yml | 7 +++---- README.md | 6 +++--- crates/iota-crypto/README.md | 2 +- crates/iota-graphql-client-build/README.md | 6 +++--- crates/iota-graphql-client/README.md | 6 +++--- crates/iota-graphql-client/src/faucet.rs | 4 ++-- crates/iota-sdk-types/README.md | 2 +- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2693f6ab..77b59b4f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,11 +110,10 @@ jobs: - name: Get the IOTA testnet binary and start a local network shell: bash env: - IOTA_BINARY_VERSION: "1.39.3" # used for downloading a specific IOTA binary versions that matches the GraphQL schema for local network tests - IOTA_NETWORK_RELEASE: "testnet" # which release to use + IOTA_BINARY_VERSION: "v0.12.0-rc" # used for downloading a specific IOTA binary versions that matches the GraphQL schema for local network tests run: | - ASSET_NAME="iota-$IOTA_NETWORK_RELEASE-v$IOTA_BINARY_VERSION-ubuntu-x86_64.tgz" - download_url="https://github.com/iotaledger/iota/releases/download/$IOTA_NETWORK_RELEASE-v$IOTA_BINARY_VERSION/$ASSET_NAME" + ASSET_NAME="iota-$IOTA_BINARY_VERSION-linux-x86_64.tgz" + download_url="https://github.com/iotaledger/iota/releases/download/$IOTA_BINARY_VERSION/$ASSET_NAME" echo "Downloading testnet binary from $download_url" wget -q $download_url -O iota.tgz diff --git a/README.md b/README.md index 48abaacc2..0b06e41b0 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,15 @@ In an effort to be modular, functionality is split between a number of crates. - [`iota-sdk-types`](crates/iota-sdk-types) [![iota-sdk-types on crates.io](https://img.shields.io/crates/v/iota-sdk-types)](https://crates.io/crates/iota-sdk-types) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-sdk-types) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) + [![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) - [`iota-crypto`](crates/iota-crypto) [![iota-crypto on crates.io](https://img.shields.io/crates/v/iota-crypto)](https://crates.io/crates/iota-crypto) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-crypto) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) + [![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) - [`iota-graphql-client`](crates/iota-crypto) [![iota-graphql-client on crates.io](https://img.shields.io/crates/v/iota-graphql-client)](https://crates.io/crates/iota-graphql-client) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-graphql-client) - [![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) + [![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) ## License diff --git a/crates/iota-crypto/README.md b/crates/iota-crypto/README.md index 42f5813bc..2f6731ade 100644 --- a/crates/iota-crypto/README.md +++ b/crates/iota-crypto/README.md @@ -2,7 +2,7 @@ [![iota-crypto on crates.io](https://img.shields.io/crates/v/iota-crypto)](https://crates.io/crates/iota-crypto) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-crypto) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) +[![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_crypto/) The `iota-crypto` crate provides the interface for signing and verifying transactions and messages in the IOTA ecosystem. diff --git a/crates/iota-graphql-client-build/README.md b/crates/iota-graphql-client-build/README.md index 629700f04..c9c4f6c43 100644 --- a/crates/iota-graphql-client-build/README.md +++ b/crates/iota-graphql-client-build/README.md @@ -9,7 +9,7 @@ this function in a `build.rs` file in your crate if you need to build custom que ```toml [build-dependencies] -iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "master" } +iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "develop" } ``` 2. Add a `build.rs` file in your crate root directory and call the `register_schema` function in it. @@ -29,10 +29,10 @@ fn main() { # ... [dependencies] cynic = "3.8.0" -iota-graphql-client = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client", branch = "master" } +iota-graphql-client = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client", branch = "develop" } [build-dependencies] -iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "master" } +iota-graphql-client-build = { git = "https://github.com/iotaledger/iota-rust-sdk", package = "iota-graphql-client-build", branch = "develop" } ``` 4. If using `cynic`, use the cynic generator to generate the Rust types from the GraphQL schema.\ diff --git a/crates/iota-graphql-client/README.md b/crates/iota-graphql-client/README.md index 27f776fa5..56552d387 100644 --- a/crates/iota-graphql-client/README.md +++ b/crates/iota-graphql-client/README.md @@ -2,7 +2,7 @@ [![iota-graphql-client on crates.io](https://img.shields.io/crates/v/iota-graphql-client)](https://crates.io/crates/iota-graphql-client) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-graphql-client) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) +[![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota-graphql-client/) The IOTA GraphQL client is a client for interacting with the IOTA blockchain via GraphQL. It provides a set of APIs for querying the blockchain for information such as chain identifier, @@ -71,7 +71,7 @@ async fn main() -> Result<()> { ### Example for custom faucet service. -Note that this `FaucetClient` is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.iota.io`). +Note that this `FaucetClient` is explicitly designed to work with two endpoints: `v1/gas`, and `v1/status`. When passing in the custom faucet URL, skip the final endpoint and only pass in the top-level url (e.g., `https://faucet.devnet.iota.cafe`). ```rust, no_run use iota_graphql_client::faucet::FaucetClient; @@ -123,7 +123,7 @@ query CustomQuery($id: UInt53) { } ``` -When using `cynic` and `iota-graphql-client`, you will need to register the schema by calling `iota-graphql-client-build::register_schema` in a `build.rs` file. See [iota-graphql-client-build](https://github.com/iotaledger/iota-rust-sdk/tree/master/crates/iota-graphql-client-build) for more information. +When using `cynic` and `iota-graphql-client`, you will need to register the schema by calling `iota-graphql-client-build::register_schema` in a `build.rs` file. See [iota-graphql-client-build](https://github.com/iotaledger/iota-rust-sdk/tree/develop/crates/iota-graphql-client-build) for more information. The generated query types are defined below. Note that the `id` variable is optional (to make it mandatory change the schema to $id: Uint53! -- note the ! character which indicates a mandatory field). That means that if the `id` variable is not provided, the query will return the data for the last known epoch. Note that instead of using `Uint53`, the scalar is mapped to `u64` in the library using `impl_scalar(u64, schema::Uint53)`, thus all references to `Uint53` in the schema are replaced with `u64` in the code below. diff --git a/crates/iota-graphql-client/src/faucet.rs b/crates/iota-graphql-client/src/faucet.rs index beb12cc15..648dc1c4e 100644 --- a/crates/iota-graphql-client/src/faucet.rs +++ b/crates/iota-graphql-client/src/faucet.rs @@ -11,8 +11,8 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use tracing::{error, info}; -pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.iota.io"; -pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.iota.io"; +pub const FAUCET_DEVNET_HOST: &str = "https://faucet.devnet.iota.cafe"; +pub const FAUCET_TESTNET_HOST: &str = "https://faucet.testnet.iota.cafe"; pub const FAUCET_LOCAL_HOST: &str = "http://localhost:9123"; const FAUCET_REQUEST_TIMEOUT: Duration = Duration::from_secs(120); diff --git a/crates/iota-sdk-types/README.md b/crates/iota-sdk-types/README.md index eab545b79..80e572f12 100644 --- a/crates/iota-sdk-types/README.md +++ b/crates/iota-sdk-types/README.md @@ -2,7 +2,7 @@ [![iota-sdk-types on crates.io](https://img.shields.io/crates/v/iota-sdk-types)](https://crates.io/crates/iota-sdk-types) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen)](https://docs.rs/iota-sdk-types) -[![Documentation (master)](https://img.shields.io/badge/docs-master-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) +[![Documentation (develop)](https://img.shields.io/badge/docs-develop-59f)](https://github.com/iotaledger/iota-rust-sdk/iota_sdk_types/) The `iota-sdk-types` crate provides the definitions of the core types that are part of the public API of the IOTA blockchain. From 8231368483fa5b94baae4070af322994bc70556a Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Mon, 26 May 2025 15:03:52 +0200 Subject: [PATCH 104/107] chore: Update CI rust version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77b59b4f2..388b8d71e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - name: rust version run: | - rustup default 1.81.0 + rustup default 1.82.0 rustc --version cargo --version From 9a648138df3d27088ceac351acddde077689ae79 Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Tue, 3 Jun 2025 16:05:25 +0200 Subject: [PATCH 105/107] chore: Fix errors and add fixtures --- crates/iota-sdk-types/src/crypto/multisig.rs | 25 +++------ crates/iota-sdk-types/src/crypto/passkey.rs | 2 +- crates/iota-sdk-types/src/crypto/signature.rs | 8 +-- .../src/transaction/fixtures/change-epoch-v2 | 1 + .../fixtures/consensus-commit-prologue-v1 | 1 + .../src/transaction/fixtures/genesis | 1 + .../src/transaction/fixtures/ptb | 1 + .../update-transaction-fixtures/Cargo.toml | 10 ++-- .../update-transaction-fixtures/src/main.rs | 1 + .../src/transaction/serialization.rs | 54 ++++++++++++++----- 10 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 crates/iota-sdk-types/src/transaction/fixtures/change-epoch-v2 create mode 100644 crates/iota-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 create mode 100644 crates/iota-sdk-types/src/transaction/fixtures/genesis create mode 100644 crates/iota-sdk-types/src/transaction/fixtures/ptb diff --git a/crates/iota-sdk-types/src/crypto/multisig.rs b/crates/iota-sdk-types/src/crypto/multisig.rs index e6b9243bf..a2d465e96 100644 --- a/crates/iota-sdk-types/src/crypto/multisig.rs +++ b/crates/iota-sdk-types/src/crypto/multisig.rs @@ -264,25 +264,12 @@ mod serialization { let mut buf = Vec::new(); buf.push(SignatureScheme::Multisig as u8); - if let Some(bitmap) = &self.legacy_bitmap { - let legacy = LegacyMultisigRef { - signatures: &self.signatures, - bitmap, - committee: LegacyMultisigCommitteeRef { - members: &self.committee.members, - threshold: self.committee.threshold, - }, - }; - - bcs::serialize_into(&mut buf, &legacy).expect("serialization cannot fail"); - } else { - let multisig = MultisigRef { - signatures: &self.signatures, - bitmap: self.bitmap, - committee: &self.committee, - }; - bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail"); - } + let multisig = MultisigRef { + signatures: &self.signatures, + bitmap: self.bitmap, + committee: &self.committee, + }; + bcs::serialize_into(&mut buf, &multisig).expect("serialization cannot fail"); buf } diff --git a/crates/iota-sdk-types/src/crypto/passkey.rs b/crates/iota-sdk-types/src/crypto/passkey.rs index e0598cdb6..16046d619 100644 --- a/crates/iota-sdk-types/src/crypto/passkey.rs +++ b/crates/iota-sdk-types/src/crypto/passkey.rs @@ -146,7 +146,7 @@ mod serialization { client_data_json: String, signature: SimpleSignature, ) -> Option { - Self::try_from_raw::(Authenticator { + Self::try_from_raw(Authenticator { authenticator_data, client_data_json, signature, diff --git a/crates/iota-sdk-types/src/crypto/signature.rs b/crates/iota-sdk-types/src/crypto/signature.rs index 9b10d7cd4..ed271010a 100644 --- a/crates/iota-sdk-types/src/crypto/signature.rs +++ b/crates/iota-sdk-types/src/crypto/signature.rs @@ -147,7 +147,7 @@ impl UserSignature { #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))] mod serialization { use super::*; - use crate::types::crypto::SignatureFromBytesError; + use crate::crypto::SignatureFromBytesError; impl SimpleSignature { fn to_bytes(&self) -> Vec { @@ -444,7 +444,7 @@ mod serialization { let multisig = MultisigAggregatedSignature::from_serialized_bytes(bytes)?; Ok(Self::Multisig(multisig)) } - SignatureScheme::Bls12381 => Err(serde::de::Error::custom( + SignatureScheme::Bls12381 => Err(SignatureFromBytesError::new( "bls not supported for user signatures", )), SignatureScheme::ZkLogin => { @@ -459,7 +459,7 @@ mod serialization { } pub fn from_bytes(bytes: &[u8]) -> Result { - Self::from_serialized_bytes(bytes) + Self::from_serialized_bytes(bytes).map_err(serde::de::Error::custom) } pub fn from_base64(s: &str) -> Result { @@ -608,7 +608,7 @@ mod serialization { let bytes: std::borrow::Cow<'de, [u8]> = serde_with::Bytes::deserialize_as(deserializer)?; - Self::from_serialized_bytes(bytes) + Self::from_serialized_bytes(bytes).map_err(serde::de::Error::custom) } } } diff --git a/crates/iota-sdk-types/src/transaction/fixtures/change-epoch-v2 b/crates/iota-sdk-types/src/transaction/fixtures/change-epoch-v2 new file mode 100644 index 000000000..98c7cd06c --- /dev/null +++ b/crates/iota-sdk-types/src/transaction/fixtures/change-epoch-v2 @@ -0,0 +1 @@ +AAQBAQEAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcfoPNpcBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA= \ No newline at end of file diff --git a/crates/iota-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 b/crates/iota-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 new file mode 100644 index 000000000..d68fc87da --- /dev/null +++ b/crates/iota-sdk-types/src/transaction/fixtures/consensus-commit-prologue-v1 @@ -0,0 +1 @@ +AAIAAAAAAAAAAAEAAAAAAAAAAAj5DzaXAQAAIHlbepyWl7WHoRCsNL8f3vGD9NKHZ24B/nqYslV0DrCaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA== \ No newline at end of file diff --git a/crates/iota-sdk-types/src/transaction/fixtures/genesis b/crates/iota-sdk-types/src/transaction/fixtures/genesis new file mode 100644 index 000000000..1c2f59bbd --- /dev/null +++ b/crates/iota-sdk-types/src/transaction/fixtures/genesis @@ -0,0 +1 @@ +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HCAoDBgsDAQkABwsBAQkACA4DBgsDAQkABwsBAQkACAwBBgsBAQkAAQIBCA4BCAwBCw0BCAsBCQABCAkCBwsFAQkAAwIHCwUBCQALBQEJAAMDAwoLAAEJAAEGCQABCgIDCwQBCQALAQEJAAsDAQkAAQsBAQkAAQgIAQsEAQkAAQsCAQkAAgcLBgEJAAMCBwsGAQkACwUBCQABCA8FBwgHAwoCBQcICgUGCAcDCgIFBggKBAYIBwMKAgUEBwgHAwoCBwgKBAYIBwMKAgYICgMGCAcDCgICCQAFAQgLAQsNAQkAB0JhbGFuY2UEQ29pbgxDb2luTWV0YWRhdGEJRGVueUNhcFYxCERlbnlMaXN0AklEBk9wdGlvbhVSZWd1bGF0ZWRDb2luTWV0YWRhdGEGU3RyaW5nBlN1cHBseQtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEA1VybANhZGQSYWxsb3dfZ2xvYmFsX3BhdXNlBWFzY2lpB2JhbGFuY2ULYmFsYW5jZV9tdXQEYnVybgRjb2luFGNvaW5fbWV0YWRhdGFfb2JqZWN0FmNvbnRhaW5zX2N1cnJlbnRfZXBvY2gTY29udGFpbnNfbmV4dF9lcG9jaA9jcmVhdGVfY3VycmVuY3kcY3JlYXRlX3JlZ3VsYXRlZF9jdXJyZW5jeV92MQ1jcmVhdGVfc3VwcGx5CGRlY2ltYWxzD2RlY3JlYXNlX3N1cHBseQZkZWxldGUPZGVueV9jYXBfb2JqZWN0CWRlbnlfbGlzdBBkZW55X2xpc3RfdjFfYWRkI2RlbnlfbGlzdF92MV9jb250YWluc19jdXJyZW50X2Vwb2NoIGRlbnlfbGlzdF92MV9jb250YWluc19uZXh0X2Vwb2NoIWRlbnlfbGlzdF92MV9kaXNhYmxlX2dsb2JhbF9wYXVzZSBkZW55X2xpc3RfdjFfZW5hYmxlX2dsb2JhbF9wYXVzZTJkZW55X2xpc3RfdjFfaXNfZ2xvYmFsX3BhdXNlX2VuYWJsZWRfY3VycmVudF9lcG9jaC9kZW55X2xpc3RfdjFfaXNfZ2xvYmFsX3BhdXNlX2VuYWJsZWRfbmV4dF9lcG9jaBNkZW55X2xpc3RfdjFfcmVtb3ZlC2Rlc2NyaXB0aW9uDGRlc3Ryb3lfemVybxRkaXNhYmxlX2dsb2JhbF9wYXVzZQ1kaXZpZGVfaW50b19uE2VuYWJsZV9nbG9iYWxfcGF1c2UNZnJlZXplX29iamVjdAxmcm9tX2JhbGFuY2UMZ2V0X2RlY2ltYWxzD2dldF9kZXNjcmlwdGlvbgxnZXRfaWNvbl91cmwIZ2V0X25hbWUKZ2V0X3N5bWJvbBVnZXRfd2l0aF9vcmlnaW5hbF9pZHMIaWNvbl91cmwCaWQPaW5jcmVhc2Vfc3VwcGx5DGludG9fYmFsYW5jZQppbnRvX2J5dGVzC2ludG9fc3RyaW5nJWlzX2dsb2JhbF9wYXVzZV9lbmFibGVkX2N1cnJlbnRfZXBvY2giaXNfZ2xvYmFsX3BhdXNlX2VuYWJsZWRfbmV4dF9lcG9jaBNpc19vbmVfdGltZV93aXRuZXNzBGpvaW4EbWludBFtaW50X2FuZF90cmFuc2ZlcgxtaW50X2JhbGFuY2UEbmFtZQNuZXcKbmV3X3Vuc2FmZQZvYmplY3QGb3B0aW9uD3B1YmxpY190cmFuc2ZlcgNwdXQGcmVtb3ZlBHNvbWUFc3BsaXQGc3RyaW5nDHN1cHBseV9pbW11dApzdXBwbHlfbXV0DHN1cHBseV92YWx1ZQZzeW1ib2wEdGFrZQx0b3RhbF9zdXBwbHkIdHJhbnNmZXIUdHJlYXN1cnlfaW50b19zdXBwbHkKdHhfY29udGV4dAl0eXBlX25hbWUFdHlwZXMSdXBkYXRlX2Rlc2NyaXB0aW9uD3VwZGF0ZV9pY29uX3VybAt1cGRhdGVfbmFtZQ11cGRhdGVfc3ltYm9sA3VybAR1dGY4BXZhbHVlBHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAACAjcICRILBQEJAAECBjcICRwCQwgOUQgMKQgONgsNAQgLAgIDNwgJFggIHwgIAwICNwgJUwsGAQkABAICNwgJEAEDKwArASsEKwIrAAEAABAECwA3ADgAAgEBAAADBgsAOgAMARE4CwECAgEAABADCwA3AAIDAQAAEAMLADYAAgQBAAAQBAsANwE4AQIFAQAAEAMLADcBAgYBAAAQAws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3aXRoX3R5cGUDZm9yDGZyb21fYmFsYW5jZQpoYXNfYWNjZXNzCGhhc19pdGVtEmhhc19pdGVtX3dpdGhfdHlwZQJpZARpb3RhDGlzX2V4Y2x1c2l2ZQlpc19saXN0ZWQVaXNfbGlzdGVkX2V4Y2x1c2l2ZWx5CWlzX2xvY2tlZAdpc19zb21lCml0ZW1fY291bnQHaXRlbV9pZAVraW9zaw9raW9za19leHRlbnNpb24Ia2lvc2tfaWQTa2lvc2tfb3duZXJfY2FwX2ZvcgRsaXN0Fmxpc3Rfd2l0aF9wdXJjaGFzZV9jYXAEbG9jaw1sb2NrX2ludGVybmFsCW1pbl9wcmljZQNuZXcLbmV3X3JlcXVlc3QGb2JqZWN0Bm9wdGlvbgVvd25lcgVwbGFjZQ5wbGFjZV9hbmRfbGlzdA5wbGFjZV9pbnRlcm5hbAVwcmljZQdwcm9maXRzDnByb2ZpdHNfYW1vdW50C3Byb2ZpdHNfbXV0CHB1cmNoYXNlEXB1cmNoYXNlX2NhcF9pdGVtEnB1cmNoYXNlX2NhcF9raW9zaxZwdXJjaGFzZV9jYXBfbWluX3ByaWNlEXB1cmNoYXNlX3dpdGhfY2FwA3B1dAZyZW1vdmUQcmVtb3ZlX2lmX2V4aXN0cxNyZXR1cm5fcHVyY2hhc2VfY2FwCnJldHVybl92YWwGc2VuZGVyCXNldF9vd25lchBzZXRfb3duZXJfY3VzdG9tDHNoYXJlX29iamVjdAR0YWtlCHRyYW5zZmVyD3RyYW5zZmVyX3BvbGljeQp0eF9jb250ZXh0A3VpZBB1aWRfbXV0X2FzX293bmVyEHVpZF9tdXRfaW50ZXJuYWwMdWlkX3RvX2lubmVyBXZhbHVlCHdpdGhkcmF3BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMICAAAAAAAAAADCAkAAAAAAAAAAwgKAAAAAAAAAAMICwAAAAAAAAADCAwAAAAAAAAAAAIEKQgORAsKAQgMPwUwDgECAikIDiQIDQICBCkIDjQIDTE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GCwEBCQALAwEJAAcIDAMHCwkBCQALAwEJAAcIDAMJAQcLAwEJAAcIDAUJAQcLAgEJAAYLAQEJAAkCBwgMAgkBBgsCAQkAAQYJAgMJAQcLAgEJAAYLAQEJAAEHCQIDBwsCAQkABgsBAQkABwgMAQkCAQYLAgEJAAEBBAcLAgEJAAYLAQEJAAgQBwgMAwcLCQEJAAMHCAwCBwsJAQkACwABCQADBwsCAQkABwsJAQkABwgMAQMCBgsCAQkABggQAQsOAQgRAQYLAAEJAAEIEAEGCwMBCQABBQELDwEFAQsPAQMBCwQBCQACCwEBCQALAgEJAAEICwEJAAELBgEJAAIIEAsOAQgRAQsNAgkACQEBBgkAAQgKAQsFAQkAAQYLBgEJAAIJAAUBCw8BCQADAwsGAQkACAsCCwYBCQAHCAwBCwgBCQABBgsIAQkAAgsGAQkACAsCBwsGAQkACwYBCQACBwsGAQkAAwEGCAwGCBADCw8BBQsPAQsGAQkABQsOAQgRAQgRAQsOAQkACwoIEQMLDgEIEQMIEAsPAQUGCBEGCggRAwULDwELBgEJAAEGCw8BCQACBgsNAgkACQEGCQABBgkBAQoJAAIGCw4BCQAGCQABBwsPAQkABQMIEAsPAQUFCw8BCwYBCQABBwsJAQkAAQcLBwEJAAIHCwcBCQALBgEJAAEJAQIHCw4BCQAJAAILBAEJAQkCAwcICwkACQEDCQAJAQkCAgYICwkAAgcICwkAAQcJAQELBAEJAQMHCw0CCQAJAQkACQECBwsNAgkACQEGCQACCQAJAQIIEQcLDgEIEQIHCw4BCQAGCQACBwsHAQkAAwIDCwYBCQABCgINQWN0aW9uUmVxdWVzdAdCYWxhbmNlBENvaW4CSUQGT3B0aW9uB1J1bGVLZXkGU3RyaW5nBlN1cHBseQVUb2tlbgtUb2tlblBvbGljeQ5Ub2tlblBvbGljeUNhcBJUb2tlblBvbGljeUNyZWF0ZWQLVHJlYXN1cnlDYXAJVHhDb250ZXh0CFR5cGVOYW1lA1VJRAZWZWNNYXAGVmVjU2V0BmFjdGlvbgNhZGQMYWRkX2FwcHJvdmFsD2FkZF9ydWxlX2NvbmZpZxNhZGRfcnVsZV9mb3JfYWN0aW9uBWFsbG93BmFtb3VudAlhcHByb3ZhbHMHYmFsYW5jZQZib3Jyb3cKYm9ycm93X211dARidXJuBGNvaW4PY29uZmlybV9yZXF1ZXN0E2NvbmZpcm1fcmVxdWVzdF9tdXQXY29uZmlybV93aXRoX3BvbGljeV9jYXAZY29uZmlybV93aXRoX3RyZWFzdXJ5X2NhcAhjb250YWlucw9kZWNyZWFzZV9zdXBwbHkGZGVsZXRlDGRlc3Ryb3lfbm9uZQxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvCGRpc2FsbG93DWR5bmFtaWNfZmllbGQEZW1pdAVlbXB0eQVldmVudAdleGlzdHNfEGV4aXN0c193aXRoX3R5cGUHZXh0cmFjdAVmbHVzaANmb3IMZnJvbV9iYWxhbmNlCWZyb21fY29pbhBmcm9tX2NvaW5fYWN0aW9uA2dldAdnZXRfbXV0D2hhc19ydWxlX2NvbmZpZxloYXNfcnVsZV9jb25maWdfd2l0aF90eXBlAmlkD2luY3JlYXNlX3N1cHBseQZpbnNlcnQMaW50b19iYWxhbmNlCWludG9fa2V5cwppc19hbGxvd2VkCmlzX211dGFibGUHaXNfbm9uZQxpc19wcm90ZWN0ZWQHaXNfc29tZQRqb2luBGtlZXADa2V5BG1pbnQEbmFtZQNuZXcKbmV3X3BvbGljeQtuZXdfcmVxdWVzdARub25lBm9iamVjdAZvcHRpb24JcmVjaXBpZW50BnJlbW92ZRJyZW1vdmVfcnVsZV9jb25maWcWcmVtb3ZlX3J1bGVfZm9yX2FjdGlvbgtydWxlX2NvbmZpZw9ydWxlX2NvbmZpZ19tdXQFcnVsZXMGc2VuZGVyDHNoYXJlX29iamVjdAxzaGFyZV9wb2xpY3kEc29tZQVzcGVuZAxzcGVuZF9hY3Rpb24Fc3BlbnQNc3BlbnRfYmFsYW5jZQVzcGxpdAZzdHJpbmcKc3VwcGx5X211dAd0b19jb2luDnRvX2NvaW5fYWN0aW9uBXRva2VuCHRyYW5zZmVyD3RyYW5zZmVyX2FjdGlvbgp0eF9jb250ZXh0CXR5cGVfbmFtZQR1dGY4BXZhbHVlB3ZlY19tYXAHdmVjX3NldAR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAAAAAAAAAADCAE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BFysqAQcBJh0WAgcEAhkpKgACHA8QAAMpEQIBCAMrEgIBCAQRFBUABQogJQEABQwfJwEABRUfEQEABRYhAgEABRogCAEABRsgCAEABR8CGQEABSoRGQEAFAEVAQ4WHhgdGB4aCxsNGxkaGxgaGhEbDxsMGxcaHBgYGhgYEBEKLAIHCQAHCAYBCwABCQAAAgsAAQkABQUHCwABCQAHCQAJAQkCBwgGAQsHAQkCBAcLAAEJAAcJAAkBBwgGAgYLAAEJAAkBAQEDBgsAAQkACQEGCAYBBwkCAwgECQAGCAYBCwcBCQEDBQUDAQsHAQkDAQcIBgEIBQEJAAIJAAULCwcBCQILBwEJAgsHAQkCAwsHAQkCAwsHAQkCCwcBCQILBwEJAgsBAQkCBwsBAQkCAQYIBgEDAQkBAgYIBQkAAQkCAQsHAQkAAQsCAQkCAgkBCwEBCQIDBwgFCQAJAQIHCAUJAAEHCQEBBwsHAQkAAQYLBwEJAAIHCwcBCQAJAAoLBwEJAgsHAQkCAwsHAQkCAwsHAQkCCwcBCQIBCwcBCQIHCwEBCQIEAQEDBgsBAQkCAQYJAQEGCQACBwsCAQkCAwEHCQACBQUBBggEAQUCBQkABAsDAgkACwEBCQELAQEJAQsCAQkBCQEGQ29uZmlnBUZpZWxkAklEBk9wdGlvbgdTZXR0aW5nC1NldHRpbmdEYXRhCVR4Q29udGV4dANVSUQDYWRkEmFkZF9mb3JfbmV4dF9lcG9jaAZib3Jyb3cZYm9ycm93X2Zvcl9uZXh0X2Vwb2NoX211dApib3Jyb3dfbXV0BmNvbmZpZwRkYXRhCWRlbnlfbGlzdA1keW5hbWljX2ZpZWxkBWVwb2NoB2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZR9leGlzdHNfd2l0aF90eXBlX2Zvcl9uZXh0X2Vwb2NoB2V4dHJhY3QEZmlsbBFoYXNoX3R5cGVfYW5kX2tleQJpZA1pZF90b19hZGRyZXNzB2lzX25vbmUHaXNfc29tZQNuZXcLbmV3ZXJfdmFsdWURbmV3ZXJfdmFsdWVfZXBvY2gEbm9uZQZvYmplY3QPb2xkZXJfdmFsdWVfb3B0Bm9wdGlvbgxyZWFkX3NldHRpbmcbcmVhZF9zZXR0aW5nX2Zvcl9uZXh0X2Vwb2NoEXJlYWRfc2V0dGluZ19pbXB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BAwMJAAcIBAEIBQEIAAEJAAIHCAEDAQYIBQEHCAUBBwkAAgYIAQMBBgkABQEBAQMHCAEBBgoJAAIKAgYKAgIGCgIGCgICBwoJAAoJAAMNAwoCAwICDwIPDwUNDQ0DDQZSYW5kb20PUmFuZG9tR2VuZXJhdG9yC1JhbmRvbUlubmVyCVR4Q29udGV4dANVSUQJVmVyc2lvbmVkB2FkZHJlc3MGYXBwZW5kA2JjcwZidWZmZXIHY291bnRlcgZjcmVhdGURZGVyaXZlX25leHRfYmxvY2sFZXBvY2gLZmlsbF9idWZmZXIUZnJlc2hfb2JqZWN0X2FkZHJlc3MNZ2VuZXJhdGVfYm9vbA5nZW5lcmF0ZV9ieXRlcw1nZW5lcmF0ZV91MTI4FmdlbmVyYXRlX3UxMjhfaW5fcmFuZ2UMZ2VuZXJhdGVfdTE2FWdlbmVyYXRlX3UxNl9pbl9yYW5nZQ1nZW5lcmF0ZV91MjU2DGdlbmVyYXRlX3UzMhVnZW5lcmF0ZV91MzJfaW5fcmFuZ2UMZ2VuZXJhdGVfdTY0FWdlbmVyYXRlX3U2NF9pbl9yYW5nZQtnZW5lcmF0ZV91OBRnZW5lcmF0ZV91OF9pbl9yYW5nZQRobWFjDWhtYWNfc2hhM18yNTYCaWQFaW5uZXIIaXNfZW1wdHkKbG9hZF9pbm5lcg5sb2FkX2lubmVyX211dApsb2FkX3ZhbHVlDmxvYWRfdmFsdWVfbXV0DW5ld19nZW5lcmF0b3IGb2JqZWN0BnJhbmRvbQxyYW5kb21fYnl0ZXMQcmFuZG9tbmVzc19yb3VuZBByYW5kb21uZXNzX3N0YXRlBHNlZWQGc2VuZGVyDHNoYXJlX29iamVjdAdzaHVmZmxlCHRvX2J5dGVzCHRyYW5zZmVyCnR4X2NvbnRleHQNdTEyOF9pbl9yYW5nZQ91MjU2X2Zyb21fYnl0ZXMXdXBkYXRlX3JhbmRvbW5lc3Nfc3RhdGUGdmVjdG9yB3ZlcnNpb24JdmVyc2lvbmVkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAADQIgAAMI//8AAAA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IAAA1BgkAAC4KBwAALwoJAAApCwcAACoLBwAALQoMAAARAgkAAAkCCQAAEwIJAAAiDQIAACMNAgAAHg4CAAALDxAAABARAgAAKxICAAESFwIAARoVBwEIARsbBwABHB8bAAEgFhcAAiYcAgEMAywaGwAEHRUEAQIGFxgZAAYYGBkABhkCEwEAHxQiFAAUHQEZDgIJAAcIBgEIAAABBggAAQEBBggHAQYIAQEIBAEDAQIBBggCAQYIAwEGCgIBBwgBAQgBAwcIAQIKAgEIAgIHCAEIAwIHCAECAQgIAQkAAQYJAAEHCAYBCAUBBggIAQgHAQYIBgEFAgkABQIBCAgCCAQIBAEGCAQCSUQJUHVibGlzaGVyBlN0cmluZwlUeENvbnRleHQIVHlwZU5hbWUDVUlEClVwZ3JhZGVDYXAOVXBncmFkZVJlY2VpcHQNVXBncmFkZVRpY2tldA9hZGRpdGl2ZV9wb2xpY3kFYXNjaWkRYXV0aG9yaXplX3VwZ3JhZGUOYnVybl9wdWJsaXNoZXIDY2FwBWNsYWltDmNsYWltX2FuZF9rZWVwDmNvbW1pdF91cGdyYWRlEWNvbXBhdGlibGVfcG9saWN5BmRlbGV0ZQ9kZXBfb25seV9wb2xpY3kGZGlnZXN0C2Zyb21fbW9kdWxlDGZyb21fcGFja2FnZQtnZXRfYWRkcmVzcwpnZXRfbW9kdWxlFWdldF93aXRoX29yaWdpbmFsX2lkcwJpZA9pZF9mcm9tX2FkZHJlc3MNaWRfdG9fYWRkcmVzcxNpc19vbmVfdGltZV93aXRuZXNzDm1ha2VfaW1tdXRhYmxlC21vZHVsZV9uYW1lA25ldwZvYmplY3QWb25seV9hZGRpdGl2ZV91cGdyYWRlcxFvbmx5X2RlcF91cGdyYWRlcwdwYWNrYWdlBnBvbGljeQ9wdWJsaWNfdHJhbnNmZXIQcHVibGlzaGVkX21vZHVsZRFwdWJsaXNoZWRfcGFja2FnZQtyZWNlaXB0X2NhcA9yZWNlaXB0X3BhY2thZ2UIcmVzdHJpY3QGc2VuZGVyDXRpY2tldF9kaWdlc3QOdGlja2V0X3BhY2thZ2UNdGlja2V0X3BvbGljeQh0cmFuc2Zlcgp0eF9jb250ZXh0CXR5cGVfbmFtZQV0eXBlcw91cGdyYWRlX3BhY2thZ2UOdXB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2DWcxX2Zyb21fYnl0ZXMMZzFfZ2VuZXJhdG9yC2cxX2lkZW50aXR5BmcxX211bB5nMV9tdWx0aV9zY2FsYXJfbXVsdGlwbGljYXRpb24GZzFfbmVnBmcxX3N1YhVnMV90b191bmNvbXByZXNzZWRfZzEGZzJfYWRkBmcyX2Rpdg1nMl9mcm9tX2J5dGVzDGcyX2dlbmVyYXRvcgtnMl9pZGVudGl0eQZnMl9tdWweZzJfbXVsdGlfc2NhbGFyX211bHRpcGxpY2F0aW9uBmcyX25lZwZnMl9zdWIJZ3JvdXBfb3BzBmd0X2FkZAZndF9kaXYMZ3RfZ2VuZXJhdG9yC2d0X2lkZW50aXR5Bmd0X211bAZndF9uZWcGZ3Rfc3ViB2hhc2hfdG8KaGFzaF90b19nMQpoYXNoX3RvX2cyA211bBttdWx0aV9zY2FsYXJfbXVsdGlwbGljYXRpb24HcGFpcmluZwpzY2FsYXJfYWRkCnNjYWxhcl9kaXYRc2NhbGFyX2Zyb21fYnl0ZXMPc2NhbGFyX2Zyb21fdTY0CnNjYWxhcl9pbnYKc2NhbGFyX211bApzY2FsYXJfbmVnCnNjYWxhcl9vbmUKc2NhbGFyX3N1YgtzY2FsYXJfemVybw1zZXRfYXNfcHJlZml4A3N1YgNzdW0TdW5jb21wcmVzc2VkX2cxX3N1bRV1bmNvbXByZXNzZWRfZzFfdG9fZzEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgoCISAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoCISAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoCMTDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAjEwl/HTpzGX15QmlWOMT6msD8NojE+XdLkFoU46PxcbrFhsVeg/+Xoa7/s68ArbIsa7CgJhYMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoCYWCT4CtgUnGfYH2s06CIJ09lWWvQ0Jkgthq12mG73H9QSTNM8RITlF1X5ax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lTG9jawlUeENvbnRleHQIVHlwZU5hbWUDVUlEBWFzY2lpB2JhbGFuY2UGYm9ycm93BWNsb2NrBmRlbGV0ZRJlcG9jaF90aW1lc3RhbXBfbXMXZXhwaXJhdGlvbl90aW1lc3RhbXBfbXMKZnJvbV9hc2NpaRVnZXRfd2l0aF9vcmlnaW5hbF9pZHMCaWQLaW50b19zdHJpbmcPaXNfbGFiZWxlZF93aXRoCWlzX2xvY2tlZBRpc19sb2NrZWRfd2l0aF9jbG9jawdpc19zb21lBGpvaW4Iam9pbl92ZWMFbGFiZWwHbGFiZWxlcgRsb2NrEWxvY2tfYW5kX3RyYW5zZmVyD2xvY2tfd2l0aF9sYWJlbBxsb2NrX3dpdGhfbGFiZWxfYW5kX3RyYW5zZmVyBmxvY2tlZANuZXcEbm9uZQZvYmplY3QGb3B0aW9uBHBhY2sOcmVtYWluaW5nX3RpbWUZcmVtYWluaW5nX3RpbWVfd2l0aF9jbG9jax1yZW1haW5pbmdfdGltZV93aXRoX3RpbWVzdGFtcAZzZW5kZXIEc29tZQVzcGxpdA1zcGxpdF9iYWxhbmNlBnN0cmluZxBzeXN0ZW1fYWRtaW5fY2FwC3N5c3RlbV9wYWNrDXN5c3RlbV91bnBhY2sIdGltZWxvY2sMdGltZXN0YW1wX21zCHRyYW5zZmVyEnRyYW5zZmVyX3RvX3NlbmRlcgp0eF9jb250ZXh0CXR5cGVfbmFtZQZ1bmxvY2sRdW5sb2NrX3dpdGhfY2xvY2sGdW5wYWNrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAACBBMIBCEJABADGwsIAQgJAAcAAQAABAYLAAsBOAALAjgBAgEBAAAQCTgCDAQLAQsCCwQ4AwsDOAECAgEAAAQHCwALAgsDOAQLATgFAgMBAAAECAsACwELAwsEOAYLAjgFAgQBAAAeDwsAOAcBDAIMAwsCCwERICUECwUNBwAnCwMCBQEAAB4PCwA4BwEMAgwDCwILAREcJQQLBQ0HACcLAwIGAQAAISMKAC44CA4BOAghBAgFDAsAAQcBJwoALjgJDgE4CSEEFAUYCwABBwInCwE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BCAgCCAMBAwgICQAGCAoBCwsBCQEBBgsLAQkAAQsLAQkAAQkAAgYLBwEJAAkBCggBBwgBBwsHAQgBBwgKBwgKBwgACAQHAQMKAgMIAQgEAQYIAQcLBwEIAQcICgcIAAMKAgIBCwsBAQIIBAEECAELBwEIAQgICAIBCAECBwkABwgKAQsHAQkAAggCCwcBCAEDBwgJCQAJAQEIBQEIAgIHCAkJAAEHCQECBggJCQABBgkBAQYICgEFAQgJAQgGAQgACkFkZHJlc3NLZXkDQmFnBkNvbmZpZwlDb25maWdLZXkOQ29uZmlnV3JpdGVDYXAIRGVueUxpc3QOR2xvYmFsUGF1c2VLZXkCSUQGT3B0aW9uFFBlclR5cGVDb25maWdDcmVhdGVkCVR4Q29udGV4dANVSUQDYWRkEmFkZF9mb3JfbmV4dF9lcG9jaBNhZGRfcGVyX3R5cGVfY29uZmlnA2JhZxlib3Jyb3dfZm9yX25leHRfZXBvY2hfbXV0FmJvcnJvd19wZXJfdHlwZV9jb25maWcaYm9ycm93X3Blcl90eXBlX2NvbmZpZ19tdXQEY29pbgZjb25maWcJY29uZmlnX2lkFmNvbnRhaW5zX2N1cnJlbnRfZXBvY2gTY29udGFpbnNfbmV4dF9lcG9jaAZjcmVhdGUJZGVueV9saXN0DGRlc3Ryb3lfbm9uZQxkZXN0cm95X3NvbWUUZGlzYWJsZV9nbG9iYWxfcGF1c2ULZHVtbXlfZmllbGQUZHluYW1pY19vYmplY3RfZmllbGQEZW1pdBNlbmFibGVfZ2xvYmFsX3BhdXNlBWV2ZW50B2V4aXN0c18fZXhpc3RzX3dpdGhfdHlwZV9mb3JfbmV4dF9lcG9jaAJpZAxpbnRlcm5hbF9hZGQPaW50ZXJuYWxfYm9ycm93E2ludGVybmFsX2JvcnJvd19tdXQYaW90YV9kZW55X2xpc3Rfb2JqZWN0X2lkJWlzX2dsb2JhbF9wYXVzZV9lbmFibGVkX2N1cnJlbnRfZXBvY2giaXNfZ2xvYmFsX3BhdXNlX2VuYWJsZWRfbmV4dF9lcG9jaAdpc19zb21lA2tleQVsaXN0cwNuZXcGb2JqZWN0Bm9wdGlvbg9wZXJfdHlwZV9leGlzdHMOcGVyX3R5cGVfaW5kZXgMcGVyX3R5cGVfa2V5BHBvczA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GU3VwcGx5C1RyZWFzdXJ5Q2FwGlRyZWFzdXJ5T3duZXJzaGlwUmVub3VuY2VkCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQDVXJsA2FkZBdhZGRfYWRkaXRpb25hbF9tZXRhZGF0YRNhZGRpdGlvbmFsX21ldGFkYXRhBWFzY2lpEGF2YWlsYWJsZV9zdXBwbHkHYmFsYW5jZQZib3Jyb3cKYm9ycm93X211dARidXJuBGNvaW4MY29pbl9tYW5hZ2VyCWNvaW5fbmFtZQZjcmVhdGUPY3JlYXRlX2N1cnJlbmN5CGRlY2ltYWxzBmRlbGV0ZQtkZXNjcmlwdGlvbg1keW5hbWljX2ZpZWxkBGVtaXQWZW5mb3JjZV9tYXhpbXVtX3N1cHBseQVldmVudAdleGlzdHNfBGZpbGwDZ2V0F2dldF9hZGRpdGlvbmFsX21ldGFkYXRhDGdldF9kZWNpbWFscw9nZXRfZGVzY3JpcHRpb24MZ2V0X2ljb25fdXJsCGdldF9uYW1lCmdldF9zeW1ib2wQZ2V0X3dpdGhfZGVmYXVsdBJoYXNfbWF4aW11bV9zdXBwbHkIaWNvbl91cmwCaWQSaW1tdXRhYmxlX21ldGFkYXRhC2ludG9fc3RyaW5nB2lzX25vbmUHaXNfc29tZQ5tYXhpbXVtX3N1cHBseQhtZXRhZGF0YRJtZXRhZGF0YV9pbW11dGFibGUVbWV0YWRhdGFfaXNfaW1tdXRhYmxlBG1pbnQRbWludF9hbmRfdHJhbnNmZXIMbWludF9iYWxhbmNlBG5hbWUDbmV3G25ld193aXRoX2ltbXV0YWJsZV9tZXRhZGF0YQRub25lBm9iamVjdAZvcHRpb24GcmVtb3ZlG3Jlbm91bmNlX21ldGFkYXRhX293bmVyc2hpcBtyZW5vdW5jZV90cmVhc3VyeV9vd25lcnNoaXAbcmVwbGFjZV9hZGRpdGlvbmFsX21ldGFkYXRhBHNvbWUGc3RyaW5nDHN1cHBseV9pbW11dBBzdXBwbHlfaW1tdXRhYmxlE3N1cHBseV9pc19pbW11dGFibGUEc3dhcAZzeW1ib2wMdG90YWxfc3VwcGx5DHRyZWFzdXJ5X2NhcAp0eF9jb250ZXh0CXR5cGVfbmFtZRJ1cGRhdGVfZGVzY3JpcHRpb24PdXBkYXRlX2ljb25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uY2UEQ29pbgJJRARJT1RBBk9wdGlvbglQdWJsaXNoZXIHUnVsZUtleQ5UcmFuc2ZlclBvbGljeRFUcmFuc2ZlclBvbGljeUNhcBVUcmFuc2ZlclBvbGljeUNyZWF0ZWQXVHJhbnNmZXJQb2xpY3lEZXN0cm95ZWQPVHJhbnNmZXJSZXF1ZXN0CVR4Q29udGV4dAhUeXBlTmFtZQNVSUQGVmVjU2V0A2FkZAthZGRfcmVjZWlwdAhhZGRfcnVsZQ5hZGRfdG9fYmFsYW5jZQdiYWxhbmNlBmJvcnJvdwRjb2luD2NvbmZpcm1fcmVxdWVzdAhjb250YWlucwdkZWZhdWx0BmRlbGV0ZRRkZXN0cm95X2FuZF93aXRoZHJhdwxkZXN0cm95X3NvbWULZHVtbXlfZmllbGQNZHluYW1pY19maWVsZARlbWl0BWVtcHR5BWV2ZW50B2V4aXN0c18EZnJvbQxmcm9tX2JhbGFuY2UMZnJvbV9wYWNrYWdlA2dldAhnZXRfcnVsZQhoYXNfcnVsZQJpZAZpbnNlcnQJaW50b19rZXlzBGlvdGEHaXNfc29tZQRpdGVtA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uB3BhY2thZ2UEcGFpZAlwb2xpY3lfaWQDcHV0CHJlY2VpcHRzBnJlbW92ZQtyZW1vdmVfcnVsZQVydWxlcwZzZW5kZXIMc2hhcmVfb2JqZWN0BHNpemUEdGFrZQh0cmFuc2Zlcg90cmFuc2Zlcl9wb2xpY3kKdHhfY29udGV4dAl0eXBlX25hbWUDdWlkEHVpZF9tdXRfYXNfb3duZXIMdWlkX3RvX2lubmVyBXZhbHVlB3ZlY19zZXQId2l0aGRyYXcEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAAAAgQuCAk0AyMICTcLDQEIDwECAykIChQLBgEICDoLDQEIDwICAikICjUICQMCASkICQQCASkICQUCAR0BABkDGQEZAhkEGQU1AAEAAAQGCwALAQsCOAA5AAIBAQAAGiALADgBBAQFCAs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IExMSFxEXEgYRBgQICAgICAcIAQoIAAgCCw0BCA4ICQcICgAFBwgHCggDBwoIDAsNAQgOBwgKAQcKCAwWCggDCgIDAwoCAwMKAgUKAgoCCgIKAgMKAgoCCgIKAgsEAQgGCAsIDAoIDAEGCAoBAwEGCAcBCAYBCwQBCQABCAwBCAAPBQoCCgIKAgoCCgIKAgoCCgIKAgoCCgIDAwcICgIGCggMBggMAQEHAwMDAwMDBwgKAQgLCQgICAcKCAwLBAEIBgMDCAsICQcICggLBAEIBgMFCw0BAwsNAQUDBwgMBQEIAwEGCgkAAwcIBwMGCAoBCwQBCAYBBQEGCw0BCQABCw0BCQABCQACBwoIDAUBBwgMBgcIDAsEAQgGBQMLDQEIDgcICgQHCAwLBAEIBgUHCAoCCwQBCQAHCAoBCwUBCQACCwUBCAYFAgMDAgcIDAMHQmFsYW5jZQRDb2luFkdlbmVzaXNDaGFpblBhcmFtZXRlcnMYR2VuZXNpc1ZhbGlkYXRvck1ldGFkYXRhBElPVEESSW90YVN5c3RlbUFkbWluQ2FwD0lvdGFUcmVhc3VyeUNhcAZPcHRpb24GU3RyaW5nElN5c3RlbVBhcmFtZXRlcnNWMQ9Ub2tlbkFsbG9jYXRpb24ZVG9rZW5EaXN0cmlidXRpb25TY2hlZHVsZQlUeENvbnRleHQDVUlEC1ZhbGlkYXRvclYxCGFjdGl2YXRlE2FjdGl2YXRlX3ZhbGlkYXRvcnMPYWxsb2NhdGVfdG9rZW5zC2FsbG9jYXRpb25zDGFtb3VudF9uYW5vcxRhdXRob3JpdHlfcHVibGljX2tleQdiYWxhbmNlGGNoYWluX3N0YXJ0X3RpbWVzdGFtcF9tcwRjb2luD2NvbW1pc3Npb25fcmF0ZQZjcmVhdGUYY3JlYXRlX3N5c3RlbV9wYXJhbWV0ZXJzC2Rlc2NyaXB0aW9uDGRlc3Ryb3lfc29tZQVlcG9jaBFlcG9jaF9kdXJhdGlvbl9tcwxmcm9tX2JhbGFuY2UJZ2FzX3ByaWNlB2dlbmVzaXMRZ2V0X3ZhbGlkYXRvcl9tdXQJaW1hZ2VfdXJsBGlvdGEMaW90YV9hZGRyZXNzC2lvdGFfc3lzdGVtF2lvdGFfc3lzdGVtX3N0YXRlX2lubmVyFmlzX2R1cGxpY2F0ZV92YWxpZGF0b3IIaXNfZW1wdHkHaXNfc29tZRNtYXhfdmFsaWRhdG9yX2NvdW50G21pbl92YWxpZGF0b3Jfam9pbmluZ19zdGFrZQxtaW50X2JhbGFuY2UEbmFtZQ9uZXR3b3JrX2FkZHJlc3MSbmV0d29ya19wdWJsaWNfa2V5A25ldwZvYmplY3QGb3B0aW9uC3AycF9hZGRyZXNzEXByZV9taW50ZWRfc3VwcGx5D3ByaW1hcnlfYWRkcmVzcwtwcm9qZWN0X3VybBNwcm9vZl9vZl9wb3NzZXNzaW9uE3Byb3RvY29sX3B1YmxpY19rZXkQcHJvdG9jb2xfdmVyc2lvbhFyZWNpcGllbnRfYWRkcmVzcxxyZXF1ZXN0X2FkZF9zdGFrZV9hdF9nZW5lc2lzH3N0YWtlZF93aXRoX3RpbWVsb2NrX2V4cGlyYXRpb24Vc3Rha2VkX3dpdGhfdmFsaWRhdG9yBnN0cmluZxBzeXN0ZW1fYWRtaW5fY2FwEnRpbWVsb2NrZWRfc3Rha2luZwx0b3RhbF9zdXBwbHkIdHJhbnNmZXIKdHhfY29udGV4dAl2YWxpZGF0b3IgdmFsaWRhdG9yX2xvd19zdGFrZV9ncmFjZV9wZXJpb2QddmFsaWRhdG9yX2xvd19zdGFrZV90aHJlc2hvbGQNdmFsaWRhdG9yX3NldCJ2YWxpZGF0b3JfdmVyeV9sb3dfc3Rha2VfdGhyZXNob2xkBnZlY3RvcgR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAAIOLgoCGwoCIwoCNwoCJQUgAxgDFAoCOAoCMAoCOQoCLwoCNAoCNgoCAQIIOgMWAx4DKwMsA0cDSQNGAwICAjUDEgoIAwMCBDsFEwM+Cw0BBT0LDQEDAAAAAASIAQoHLhEIBgAAAAAAAAAAIQQHBQsLBwEHACcLBBMCDAgMFQ4BEQYLFSEEFQUZCwcBBwInOAAMGkAKAAAAAAAAAAAMHQ4DQQsMCwYAAAAAAAAAAAwOCg4KCyMEXA4DCg5CCxQTAAwWDBQMEgwZDBMMGAwJDAoMDQw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sE1ZhbGlkYXRvck1ldGFkYXRhVjELVmFsaWRhdG9yVjEIYWN0aXZhdGUVYWN0aXZhdGVfc3Rha2luZ19wb29sIWFkanVzdF9uZXh0X2Vwb2NoX2NvbW1pc3Npb25fcmF0ZQZhbW91bnQFYXNjaWkWYXV0aG9yaXR5X3B1YmtleV9ieXRlcwNiYWcHYmFsYW5jZQNiY3MGYm9ycm93D2NvbW1pc3Npb25fcmF0ZQpkZWFjdGl2YXRlF2RlYWN0aXZhdGVfc3Rha2luZ19wb29sD2RlcG9zaXRfcmV3YXJkcxVkZXBvc2l0X3N0YWtlX3Jld2FyZHMLZGVzY3JpcHRpb24aZWZmZWN0dWF0ZV9zdGFnZWRfbWV0YWRhdGEEZW1pdAVlcG9jaAVldmVudAxleHRyYV9maWVsZHMHZXh0cmFjdApmcm9tX2FzY2lpCWdhc19wcmljZQdnZW5lc2lzFGdldF9zdGFraW5nX3Bvb2xfcmVmAmlkCWltYWdlX3VybARpb3RhDGlvdGFfYWRkcmVzcwxpb3RhX2JhbGFuY2UXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWULaXNfaW5hY3RpdmUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIhbmV4dF9lcG9jaF9hdXRob3JpdHlfcHVia2V5X2J5dGVzGm5leHRfZXBvY2hfY29tbWlzc2lvbl9yYXRlFG5leHRfZXBvY2hfZ2FzX3ByaWNlFm5leHRfZXBvY2hfbmV0X2FkZHJlc3MabmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleV9ieXRlcxZuZXh0X2Vwb2NoX3AycF9hZGRyZXNzGm5leHRfZXBvY2hfcHJpbWFyeV9hZGRyZXNzHm5leHRfZXBvY2hfcHJvb2Zfb2ZfcG9zc2Vzc2lvbiBuZXh0X2Vwb2NoX3Byb3RvY29sX3B1YmtleV9ieXRlcxBuZXh0X2Vwb2NoX3N0YWtlBG5vbmUGb2JqZWN0EG9wZXJhdGlvbl9jYXBfaWQGb3B0aW9uC3AycF9hZGRyZXNzFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50B3Bvb2xfaWQhcG9vbF90b2tlbl9leGNoYW5nZV9yYXRlX2F0X2Vwb2NoD3ByaW1hcnlfYWRkcmVzcxBwcmluY2lwYWxfYW1vdW50FXByb2Nlc3NfcGVuZGluZ19zdGFrZR5wcm9jZXNzX3BlbmRpbmdfc3Rha2Vfd2l0aGRyYXckcHJvY2Vzc19wZW5kaW5nX3N0YWtlc19hbmRfd2l0aGRyYXdzC3Byb2plY3RfdXJsE3Byb29mX29mX3Bvc3Nlc3Npb24VcHJvdG9jb2xfcHVia2V5X2J5dGVzD3B1YmxpY190cmFuc2ZlchFyZXF1ZXN0X2FkZF9zdGFrZRxyZXF1ZXN0X2FkZF9zdGFrZV9hdF9nZW5lc2lzKXJlcXVlc3RfYWRkX3N0YWtlX2F0X2dlbmVzaXNfd2l0aF9yZWNlaXB0G3JlcXVlc3Rfc2V0X2NvbW1pc3Npb25fcmF0ZRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlDXJld2FyZF9hbW91bnQGc2VuZGVyHXNldF9jYW5kaWRhdGVfY29tbWlzc2lvbl9yYXRlEHNldF92b3RpbmdfcG93ZXIMc21hbGxlcl90aGFuBHNvbWUWc3Rha2VfYWN0aXZhdGlvbl9lcG9jaAxzdGFrZV9hbW91bnQSc3Rha2VkX2lvdGFfYW1vdW50DnN0YWtlcl9hZGRyZXNzDHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQGc3RyaW5nEnRpbWVsb2NrZWRfc3Rha2luZwh0b19ieXRlcwt0b3RhbF9zdGFrZRJ0b3RhbF9zdGFrZV9hbW91bnQIdHJhbnNmZXIKdHhfY29udGV4dA91bnN0YWtpbmdfZXBvY2ghdXBkYXRlX2NhbmRpZGF0ZV9hdXRob3JpdHlfcHVia2V5IHVwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19hZGRyZXNzH3VwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19wdWJrZXkcdXBkYXRlX2NhbmRpZGF0ZV9wMnBfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3ByaW1hcnlfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3Byb3RvY29sX3B1YmtleRJ1cGRhdGVfZGVzY3JpcHRpb24QdXBkYXRlX2ltYWdlX3VybAt1cGRhdGVfbmFtZSJ1cGRhdGVfbmV4dF9lcG9jaF9hdXRob3JpdHlfcHVia2V5IXVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcyB1cGRhdGVfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleR11cGRhdGVfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MhdXBkYXRlX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5EnVwZGF0ZV9wcm9qZWN0X3VybAN1cmwRdmFsaWRhdGVfbWV0YWRhdGEVdmFsaWRhdGVfbWV0YWRhdGFfYmNzCXZhbGlkYXRvchF2YWxpZGF0b3JfYWRkcmVzcw12YWxpZGF0b3JfY2FwDXZhbGlkYXRvcl9zZXQFdmFsdWUMdm90aW5nX3Bvd2VyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAMICgAAAAAAAAADCAsAAAAAAAAAAwhkAAAAAAAAAAMIZQAAAAAAAAADCGYAAAAAAAAAAwhnAAAAAAAAAAMI0AcAAAAAAAADCAABAAAAAAAAAwighgEAAAAAAAACFCwFFAoCOgoCWwoCWgoCNwgPHggPKggJWQgJOAgPTwgPVAgPQAsOAQoCSAsOAQoCRQsOAQoCSQsOAQoCQwsOAQgPRgsOAQg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BBwgAAQoFAQYIBw0DCwEBCAMLAQEIAwMHCAADAwMDAwMDBwgJAQYIDAEHCAwBCwoCBQMBAwMIAAgLAwgIBAoIEAsBAQgDAwMIDQgHBwgJAQgLAgMICwMHCAYJAAkBAQgAAQkADwcIDAoCCgIKAgoCCgIKAgoCCgIKAgoCCgIDAwcICQIHCAwHCAkCBwgMBggJAwcIDAMGCAkBBggJAgkABQQHCAwLAgEIAwUHCAkFBwgMCgsCAQgDCxIBAwUHCAkBCAMCCwEBCQAHCAkBCwIBCQABCwIBCAMDBwgMCA8GCAkDBwgMBggRBQMHCAwKAgYICQQHCAwKAgoCBggJAgcIDAYIBQ0HCAwDAwMLAQEIAwsBAQgDAwMDAwMDBwgJAgcIDAgMAgcIBgkAAQkBAQgMAgMIDAEHCQEHQmFsYW5jZQRDb2luAklEBElPVEESSW90YVN5c3RlbUFkbWluQ2FwD0lvdGFTeXN0ZW1TdGF0ZRFJb3RhU3lzdGVtU3RhdGVWMRFJb3RhU3lzdGVtU3RhdGVWMg9Jb3RhVHJlYXN1cnlDYXAGT3B0aW9uFVBvb2xUb2tlbkV4Y2hhbmdlUmF0ZQpTdGFrZWRJb3RhElN5c3RlbVBhcmFtZXRlcnNWMQVUYWJsZQlUeENvbnRleHQDVUlEH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXALVmFsaWRhdG9yVjEGVmVjTWFwGmFjdGl2ZV92YWxpZGF0b3JfYWRkcmVzc2VzA2FkZA1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UKYm9ycm93X211dARjb2luHWNvbW1pdHRlZV92YWxpZGF0b3JfYWRkcmVzc2VzIWNvbW1pdHRlZV92YWxpZGF0b3Jfdm90aW5nX3Bvd2VycwZjcmVhdGUNZHluYW1pY19maWVsZAxmcm9tX2JhbGFuY2UHZ2VuZXNpcxxnZW5lc2lzX3N5c3RlbV9zdGF0ZV92ZXJzaW9uFWdldF90b3RhbF9pb3RhX3N1cHBseQJpZARpb3RhC2lvdGFfc3lzdGVtFWlvdGFfc3lzdGVtX2FkbWluX2NhcBdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lchhsb2FkX2lubmVyX21heWJlX3VwZ3JhZGUabG9hZF9pb3RhX3N5c3RlbV9hZG1pbl9jYXARbG9hZF9zeXN0ZW1fc3RhdGUVbG9hZF9zeXN0ZW1fc3RhdGVfbXV0Bm9iamVjdAZvcHRpb24TcG9vbF9leGNoYW5nZV9yYXRlcw9wdWJsaWNfdHJhbnNmZXIGcmVtb3ZlEHJlcG9ydF92YWxpZGF0b3IRcmVxdWVzdF9hZGRfc3Rha2UacmVxdWVzdF9hZGRfc3Rha2VfbXVsX2NvaW4bcmVxdWVzdF9hZGRfc3Rha2Vfbm9uX2VudHJ5FXJlcXVlc3RfYWRkX3ZhbGlkYXRvch9yZXF1ZXN0X2FkZF92YWxpZGF0b3JfY2FuZGlkYXRlGHJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvciJyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3JfY2FuZGlkYXRlG3JlcXVlc3Rfc2V0X2NvbW1pc3Npb25fcmF0ZRVyZXF1ZXN0X3NldF9nYXNfcHJpY2UWcmVxdWVzdF93aXRoZHJhd19zdGFrZSByZXF1ZXN0X3dpdGhkcmF3X3N0YWtlX25vbl9lbnRyeRRyb3RhdGVfb3BlcmF0aW9uX2NhcAZzZW5kZXInc2V0X2NhbmRpZGF0ZV92YWxpZGF0b3JfY29tbWlzc2lvbl9yYXRlIXNldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2dhc19wcmljZQxzaGFyZV9vYmplY3QMc3Rha2luZ19wb29sEHN5c3RlbV9hZG1pbl9jYXAUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGUSdGltZWxvY2tlZF9zdGFraW5nCHRyYW5zZmVyCnR4X2NvbnRleHQVdW5kb19yZXBvcnRfdmFsaWRhdG9yK3VwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX2F1dGhvcml0eV9wdWJrZXkqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkcdXBkYXRlX3ZhbGl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hbF9leGNoYW5nZV9yYXRlBGlvdGELaW90YV9hbW91bnQMaW90YV9iYWxhbmNlGWlzX2VxdWFsX3N0YWtpbmdfbWV0YWRhdGELaXNfaW5hY3RpdmUHaXNfbm9uZQxpc19wcmVhY3RpdmUVaXNfcHJlYWN0aXZlX2F0X2Vwb2NoB2lzX3NvbWUEam9pbhBqb2luX3N0YWtlZF9pb3RhA21pbgNuZXcEbm9uZQZvYmplY3QGb3B0aW9uG3BlbmRpbmdfcG9vbF90b2tlbl93aXRoZHJhdw1wZW5kaW5nX3N0YWtlFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50G3BlbmRpbmdfdG90YWxfaW90YV93aXRoZHJhdwdwb29sX2lkEXBvb2xfdG9rZW5fYW1vdW50EnBvb2xfdG9rZW5fYmFsYW5jZSFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gJcHJpbmNpcGFsFXByb2Nlc3NfcGVuZGluZ19zdGFrZR5wcm9jZXNzX3BlbmRpbmdfc3Rha2Vfd2l0aGRyYXckcHJvY2Vzc19wZW5kaW5nX3N0YWtlc19hbmRfd2l0aGRyYXdzEXJlcXVlc3RfYWRkX3N0YWtlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UMcmV3YXJkc19wb29sBnNlbmRlcgRzb21lBXNwbGl0EXNwbGl0X3N0YWtlZF9pb3RhFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gSc3Rha2VkX2lvdGFfYW1vdW50DHN0YWtpbmdfcG9vbAV0YWJsZQh0cmFuc2Zlcgp0eF9jb250ZXh0A3U2NBJ1bndyYXBfc3Rha2VkX2lvdGEJdmFsaWRhdG9yDXZhbGlkYXRvcl9zZXQFdmFsdWUXd2l0aGRyYXdfZnJvbV9wcmluY2lwYWwQd2l0aGRyYXdfcmV3YXJkcwR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAypo7AAAAAAMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAA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pbkV2ZW50HENvbW1pdHRlZVZhbGlkYXRvckxlYXZlRXZlbnQFRW50cnkCSUQESU9UQQZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlDVByaW9yaXR5UXVldWUKU3Rha2VkSW90YQ1TdGFraW5nUG9vbFYxBVRhYmxlCFRhYmxlVmVjCVR4Q29udGV4dB9VbnZlcmlmaWVkVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCVZhbGlkYXRvchlWYWxpZGF0b3JFcG9jaEluZm9FdmVudFYxElZhbGlkYXRvckpvaW5FdmVudBNWYWxpZGF0b3JMZWF2ZUV2ZW50FVZhbGlkYXRvck9wZXJhdGlvbkNhcA5WYWxpZGF0b3JTZXRWMQ5WYWxpZGF0b3JTZXRWMgtWYWxpZGF0b3JWMQZWZWNNYXAGVmVjU2V0CGFjdGl2YXRlGmFjdGl2ZV92YWxpZGF0b3JfYWRkcmVzc2VzEWFjdGl2ZV92YWxpZGF0b3JzF2FjdGl2ZV92YWxpZGF0b3JzX2lubmVyA2FkZCFhZGp1c3RfbmV4dF9lcG9jaF9jb21taXNzaW9uX3JhdGUNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcxJhdF9yaXNrX3ZhbGlkYXRvcnMDYmFnB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQdY2FsY3VsYXRlX3RvdGFsX2FjdGl2ZV9zdGFrZXMgY2FsY3VsYXRlX3RvdGFsX2NvbW1pdHRlZV9zdGFrZXMmY2xlYW5fcmVwb3J0X3JlY29yZHNfbGVhdmluZ192YWxpZGF0b3IPY29tbWlzc2lvbl9yYXRlEWNvbW1pdHRlZV9tZW1iZXJzHWNvbW1pdHRlZV92YWxpZGF0b3JfYWRkcmVzc2VzJGNvbXB1dGVfYWRqdXN0ZWRfcmV3YXJkX2Rpc3RyaWJ1dGlvbhpjb21wdXRlX3NsYXNoZWRfdmFsaWRhdG9ycyZjb21wdXRlX3VuYWRqdXN0ZWRfcmV3YXJkX2Rpc3RyaWJ1dGlvbghjb250YWlucxljb3VudF9kdXBsaWNhdGVzX3RhYmxldmVjFGNvdW50X2R1cGxpY2F0ZXNfdmVjCWNyZWF0ZV92MQpkZWFjdGl2YXRlFWRlcG9zaXRfc3Rha2VfcmV3YXJkcxpkZXJpdmVfcmVmZXJlbmNlX2dhc19wcmljZQdkZXN0cm95DGRlc3Ryb3lfbm9uZQxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvEWRpc3RyaWJ1dGVfcmV3YXJkGmVmZmVjdHVhdGVfc3RhZ2VkX21ldGFkYXRhBGVtaXQbZW1pdF92YWxpZGF0b3JfZXBvY2hfZXZlbnRzBWVtcHR5BWVwb2NoBWV2ZW50DmV4Y2hhbmdlX3JhdGVzDGV4dHJhX2ZpZWxkcwdleHRyYWN0DmZpbmRfdmFsaWRhdG9yHWZpbmRfdmFsaWRhdG9yX2Zyb21fdGFibGVfdmVjCWdhc19wcmljZQdnZW5lc2lzA2dldDBnZXRfYWN0aXZlX29yX3BlbmRpbmdfb3JfY2FuZGlkYXRlX3ZhbGlkYXRvcl9tdXQwZ2V0X2FjdGl2ZV9vcl9wZW5kaW5nX29yX2NhbmRpZGF0ZV92YWxpZGF0b3JfcmVmGGdldF9hY3RpdmVfdmFsaWRhdG9yX3JlZh5nZXRfYWN0aXZlX3ZhbGlkYXRvcl9yZWZfaW5uZXIlZ2V0X2NhbmRpZGF0ZV9vcl9hY3RpdmVfdmFsaWRhdG9yX211dCFnZXRfY29tbWl0dGVlX3ZhbGlkYXRvcl9yZWZfaW5uZXIHZ2V0X211dBlnZXRfcGVuZGluZ192YWxpZGF0b3JfcmVmH2dldF9wZW5kaW5nX3ZhbGlkYXRvcl9yZWZfaW5uZXIUZ2V0X3N0YWtpbmdfcG9vbF9yZWYZZ2V0X3ZhbGlkYXRvcl9pbmRpY2VzX3NldBFnZXRfdmFsaWRhdG9yX211dBpnZXRfdmFsaWRhdG9yX211dF93aXRoX2N0eC9nZXRfdmFsaWRhdG9yX211dF93aXRoX2N0eF9pbmNsdWRpbmdfY2FuZGlkYXRlcyNnZXRfdmFsaWRhdG9yX211dF93aXRoX3ZlcmlmaWVkX2NhcBFnZXRfdmFsaWRhdG9yX3JlZgJpZBNpbmFjdGl2ZV92YWxpZGF0b3JzBmluc2VydAlpbnRvX2tleXMEaW90YQxpb3RhX2FkZHJlc3MXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIjaXNfYWN0aXZlX3ZhbGlkYXRvcl9ieV9pb3RhX2FkZHJlc3MmaXNfY29tbWl0dGVlX3ZhbGlkYXRvcl9ieV9pb3RhX2FkZHJlc3MMaXNfZHVwbGljYXRlFmlzX2R1cGxpY2F0ZV92YWxpZGF0b3IiaXNfZHVwbGljYXRlX3dpdGhfYWN0aXZlX3ZhbGlkYXRvciNpc19kdXBsaWNhdGVfd2l0aF9wZW5kaW5nX3ZhbGlkYXRvcghpc19lbXB0eRVpc19pbmFjdGl2ZV92YWxpZGF0b3IbaXNfaW5hY3RpdmVfdmFsaWRhdG9yX2lubmVyDGlzX3ByZWFjdGl2ZQdpc19zb21lFmlzX3ZhbGlkYXRvcl9jYW5kaWRhdGUcaXNfdmFsaWRhdG9yX2NhbmRpZGF0ZV9pbm5lcgxpc192b2x1bnRhcnkEa2V5cwZsZW5ndGgcbG9hZF92YWxpZGF0b3JfbWF5YmVfdXBncmFkZQNuZXcJbmV3X2VudHJ5E25ld19mcm9tX3VudmVyaWZpZWQGbmV3X3YxEG5leHRfZXBvY2hfc3Rha2UabmV4dF9lcG9jaF92YWxpZGF0b3JfY291bnQEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24ZcGVuZGluZ19hY3RpdmVfdmFsaWRhdG9ycxBwZW5kaW5nX3JlbW92YWxzE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMHcG9vbF9pZBNwb29sX3N0YWtpbmdfcmV3YXJkGHBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZSFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gDcG9wCHBvcF9iYWNrB3BvcF9tYXgOcHJpb3JpdHlfcXVldWUVcHJvY2Vzc19uZXdfY29tbWl0dGVlGHByb2Nlc3NfcGVuZGluZ19yZW1vdmFscyRwcm9jZXNzX3BlbmRpbmdfc3Rha2VzX2FuZF93aXRoZHJhd3MacHJvY2Vzc19wZW5kaW5nX3ZhbGlkYXRvcnMbcHJvY2Vzc192YWxpZGF0b3JfZGVwYXJ0dXJlD3B1YmxpY190cmFuc2ZlcglwdXNoX2JhY2sQcXVvcnVtX3RocmVzaG9sZBpyZWZlcmVuY2VfZ2FzX3N1cnZleV9xdW90ZQZyZW1vdmURcmVxdWVzdF9hZGRfc3Rha2UVcmVxdWVzdF9hZGRfdmFsaWRhdG9yH3JlcXVlc3RfYWRkX3ZhbGlkYXRvcl9jYW5kaWRhdGUYcmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yInJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvcl9jYW5kaWRhdGUbcmVxdWVzdF9zZXRfY29tbWlzc2lvbl9yYXRlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2Umc2VsZWN0X2NvbW1pdHRlZV9tZW1iZXJzX3RvcF9uX3N0YWtlcnMGc2VuZGVyEHNldF92b3RpbmdfcG93ZXIMc21hbGxlcl90aGFuBHNvbWURc29ydF9yZW1vdmFsX2xpc3QFc3BsaXQFc3Rha2UMc3Rha2VfYW1vdW50DHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQVc3Rha2luZ19wb29sX21hcHBpbmdzG3N0YWtpbmdfcG9vbF9tYXBwaW5nc19pbm5lcidzdW1fY29tbWl0dGVlX3ZvdGluZ19wb3dlcl9ieV9hZGRyZXNzZXMdc3VtX3ZvdGluZ19wb3dlcl9ieV9hZGRyZXNzZXMFdGFibGUJdGFibGVfdmVjGnRhbGx5aW5nX3J1bGVfZ2xvYmFsX3Njb3JlF3RhbGx5aW5nX3J1bGVfcmVwb3J0ZXJzFXRvdGFsX2NvbW1pdHRlZV9zdGFrZQt0b3RhbF9zdGFrZRJ0b3RhbF9zdGFrZV9hbW91bnQRdG90YWxfc3Rha2VfaW5uZXISdG90YWxfdm90aW5nX3Bvd2VyCHRyYW5zZmVyCnR4X2NvbnRleHQgdW52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MndXBkYXRlX2FuZF9wcm9jZXNzX2xvd19zdGFrZV9kZXBhcnR1cmVzCHYxX3RvX3YyCXZhbGlkYXRvchF2YWxpZGF0b3JfYWRkcmVzcyJ2YWxpZGF0b3JfYWRkcmVzc19ieV9wb29sX2lkX2lubmVyFHZhbGlkYXRvcl9jYW5kaWRhdGVzDXZhbGlkYXRvcl9jYXANdmFsaWRhdG9yX3NldBZ2YWxpZGF0b3Jfc3Rha2VfYW1vdW50HHZhbGlkYXRvcl9zdGFrZV9hbW91bnRfaW5uZXIZdmFsaWRhdG9yX3N0YWtpbmdfcG9vbF9pZB92YWxpZGF0b3Jfc3Rha2luZ19wb29sX2lkX2lubmVyHHZhbGlkYXRvcl90b3RhbF9zdGFrZV9hbW91bnQidmFsaWRhdG9yX3RvdGFsX3N0YWtlX2Ftb3VudF9pbm5lchZ2YWxpZGF0b3Jfdm90aW5nX3Bvd2VyHHZhbGlkYXRvcl92b3RpbmdfcG93ZXJfaW5uZXIRdmFsaWRhdG9yX3dyYXBwZXIFdmFsdWUHdmVjX21hcAd2ZWNfc2V0BnZlY3Rvch52ZXJpZmllZF9vcGVyYXRpb25fY2FwX2FkZHJlc3MKdmVyaWZ5X2NhcAx2b3RpbmdfcG93ZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAQECAQICAQMEEBAnAAAAAAAAAAAAAAAAAAADCADKmjsAAAAAAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAMICgAAAAAAAAADCAsAAAAAAAAAAwgMAAAAAAAAAAMIDQAAAAAAAAADCA4AAAAAAAAAAwgPAAAAAAAAAAMIZQAAAAAAAAAKAwEACgUBAAoDCQEAAAAAAAAAAAACCawBAxwKCBV8Cw4BCBV9CgOjAQsNAggKBVsLDQIICggYuAELDQIFCBgiCxACBQNDCAcBAgqsAQMcCggVKwoDfAsOAQgVfQoDowELDQIICgVbCw0CCAoIGLgBCw0CBQgYIgsQAgUDQwgHAgIKQAO2AQWPAQOfAQPKAQMqA4ABA4EBCBKqAQoFqQEDAwIDQAO2AQWiAQgKBAIEQAO2AQWiAQgKbgEFAgNAA7YBBaIBCAoGAgNAA7YBBaIBCAoAAwAAQFQOABE7DAwKATgADAoOAEFEDAkGAAAAAAAAAAAMBgoGCgkjBB8FEA4ACgZCRAwODQoKDhGNAQsOEYEBOAELBgYBAAAAAAAAABYMBgULCwwLAAoBOAIHFgsKCgE4AwoBOAQ4BQsBEU8SAAwPDg8QAEFEDBAHFgwNCxAMAgYAAAAAAAAAAAwICwIMCwoICgsjBEwKCAwHDQ0MBAsHDAMLBAsDRBELCAYBAAAAAAAAABYMCAU6Cw0MBQ4FDQ8PABGYAQsPAgEDAABNNAsAEwAMBwwFDBEMCgwNDAwMCwwEDA8HFgwQDgRBRAwBBgAAAAAAAAAADAkLAQwOCgkKDiMEJgoJDAgNEAwDCwgMAgsDCwJEEQsJBgEAAAAAAAAAFgwJBRQLEAwGCw8LBAsGCwsLDAsNCwoLEQsFCwcSAQICAwAATkEKAC4OAREfIAQNCgAuDgERIiAMAwUPCQwDCwMEEgUYCwABCwIBBwcnDgERgQEMBAoAEAEKBDgGIAQiBSgLAAELAgEHCycOARGDAQQsBTILAAELAgEHDCcKAA8CDgERjQELBDgBCwAPAQ4BEYEBCwELAhGUATgHAgMDAABSNQoBLhFnDAQKABABCgQ4BgQKBRALAAELAQEHDScKAA8BCwQ4CBGVAQwDDgMRgwEEGgUgCwABCwEBBwwnDgMRjQEMAgoADwIKAjgJAQ0DCgEuEWYRfAsADwMLAgsDCwERlAE4CgIEAwAAV0sLAhFnDAUKABABCgU4BgQJBQ0LAAEHDScKAA8BCwU4CBGVAQwECgAuDgQRHyAEIAoALg4EESIgDAMFIgkMAwsDBCUFKQsAAQcHJw4EEYMBBC0FMQsAAQcMJw4EEY8BDgQRhAEhBDgFPAsAAQcUJw4EEY8BCwEmBEIFRgsAAQcKJwsADwQLBDgLAgUDAAAEEAoAEAUKAREhCwAQBAsBESMWBgEAAAAAAAAAIQQNBQ8HBycCBgMAAFkjCwERZwwCCgAQBQsCESUMBA4EOAwEDAUQCwABBwknDQQ4DQwDCgAQBg4DOA4gBBoFHgsAAQcQJwsADwYLA0QRAgcDAAAEFg4COA8HBCYEBgUMCwABCwMBBw8nCwALAREkCwIKAy4RZwsDEYgBAggDAABhKw4BEXgMBAoAEAIKBDgQBBQKABACDgEReDgRFAwFCwALBREkDAMFJgoAEAMKBDgSBBoFIAsAAQsCAQcIJwsADwMLBDgTEZYBDAMLAwsBCwIRigECCQMAABgKCwIRZwwDCwAPBQsDESgLARGJAQIKAwAAZ2YKCC4RZgYBAAAAAAAAABYMChGaAQwNCgAQBQoAEAcLDQoBLjgPET8MDgoALgoCFBE+DAwKABAHCw4KABAFDgwRJwsDEUAMCQoADwUKABAHDgkLAQoIEUEKAA8FET0KAA8FCgguEToKCgoAEAUKABAHDgkKAi4ODBFCCgAuEUwMCwoACwoROAoACgsKAgoIETUKAAsECwULBgsCCgsKCBELCgALBwsLCwguEU4KABAFCgAQBxE8CgAPCBUKABAHCgAPBRGYAQsAEQwCCwAAAGh+CgAQBUFEDAwKDAYAAAAAAAAAACQEdwUJCwwGAQAAAAAAAAAXDAwKABAFCgxCRAwUChQRgQEMEwsUEY8BDBAKEAoBJgQoCgAQCQ4TOBQEBAoADwkOEzgVAQEFBAsQCgImBGEKABAJDhM4FARACgAPCQ4TOBYMDwoPFAYBAAAAAAAAABYKDxULDxQMBwVHCgAPCQsTBgEAAAAAAAAAOBcGAQAAAAAAAAAMBwsHCgMkBAQKAA8FCgw4GAwRDgUMCQ4REYEBDAgLCQ4IOBkMDQoACxEKBAkLDQoGETYFBAoADwUKDDgYDBIOBQwLDhIRgQEMCgsLDgo4GQwOCgALEgoECQsOCgYRNgUECwQBCwABCwYBAgwAAABuHQsADwUMBQoFLkFEDAEGAAAAAAAAAAAMAgsBDAQKAgoEIwQaCgIMAwoFCwNDRBF+CwIGAQAAAAAAAAAWDAIFCwsFAQINAQAAbzwLABAADAoKCkFEDANAcAAAAAAAAAAADAEGAAAAAAAAAAAMAgoCCgMjBB8FDwoKCgJCRAwIDQEKCBF/CwgRkAE4GkRwCwIGAQAAAAAAAAAWDAIFCgsKAQsBOBsMBAYAAAAAAAAAAAwGEZoBEZcBFwwHBgAAAAAAAAAADAUKBgoHIwQ6BTENBDgcDAkMBQsGCwkWDAYFLAsFAg4BAAAEBAsAEAoUAg8BAAAEBgsAEAALAREtEY8BAhABAAAEBgsAEAALAREtEYwBAhEBAAAEBgsAEAALAREtEZABAhIBAAAEBgsAEAALAREtEY0BAhMBAAAEAwsAEAsCFAMAAAQECwAQCBQCFQMAAAQGCwAQBQsBES0RjwECFgMAAAQGCwAQBQsBES0RjAECFwMAAAQGCwAQBQsBES0RkAECGAMAAAQGCwAQBQsBES0RjQECGQMAAAQDCwAQAgIaAwAAGBkKABACCgEUOBAEDgsAEAILARQ4ERQMAgUXCwAPAwsBFDgTEZYBLhGBAQwCCwICGwMAAHYfCgAQAgoBFDgQBBMKABACCwEUOBEUDAMLAAsDBwIRLgwCBRsLAA8DCwEUOBMRlgEuDAILAhGAARF3AhwDAAAEDAoAEAVBRAoAEAZBERcLABAEOB0WAh0DAAAgCAsAEAULARElDAIOAjgMAh4DAAB5FQoAEAULARElDAMOAzgMBA8LABAHDgM4HjgODAIFEwsAAQkMAgsCAh8AAAAEBQsAEAULAREgAiADAAAEBgsACwERIQYAAAAAAAAAACQCIQAAAHsmBgAAAAAAAAAADAULAAwHCgdBRAwCBgAAAAAAAAAADAMLAgwGCgMKBiMEIAoDDAQKBwsEQkQKARGCAQQbCwUGAQAAAAAAAAAWDAULAwYBAAAAAAAAABYMAwULCwEBCwcBCwUCIgAAAAQHCwAQBAsBESMGAAAAAAAAAAAkAiMAAAB9IQoAOB0MAwYAAAAAAAAAAAwCBgAAAAAAAAAADAQKAgoDIwQbBQwKAAoCOB8KARGCAQQWCwQGAQAAAAAAAAAWDAQLAgYBAAAAAAAAABYMAgUHCwABCwEBCwQCJAAAAAQQCgAQAQoBOAYECwsADwELATggEZYBAgsADwULAREoAiUAAAB/JwsADAcKB0FEDAIGAAAAAAAAAAAMBAsCDAYKBAoGIwQhCgQMBQoHCgVCRBGBAQoBIQQcCwcBCwU4IQwDBSULBAYBAAAAAAAAABYMBAUJCwcBOCIMAwsDAiYAAACBAR8KADgdDAMGAAAAAAAAAAAMAgoCCgMjBBsFCgoACgI4HxGBAQoBIQQWCwABCwI4IQILAgYBAAAAAAAAABYMAgUFCwABOCICJwAAAIIBMzgjDAcLAQwJCglBGAwCBgAAAAAAAAAADAQLAgwICgQKCCMELQoEDAUKCQsFQhgMAwoACwMUESUMBg4GOAwEHgUkCwABCwkBBwknDQcLBjgkOCULBAYBAAAAAAAAABYMBAULCwABCwkBCwcCKAMAAIUBFAoALgsBESUMAw4DOAwECQUNCwABBwknDQM4DQwCCwALAkNEAikAAACGAS0KABAFCgERJQwFDgU4DAQQDQU4DQwDCwAPBQsDQ0QCCgAQBAoBESYMBg4GOAwEIA0GOA0MBAsADwQLBDgmAgsCBCMFJwsAAQcOJwsADwELATggEZYBAioDAAAEBwsACwERkwEUCwIRKQIrAwAAGAgLARFnDAILAAsCCREpAiwDAAAYCAsBEWcMAgsACwIIESkCLQAAAIUBEwoACwERJQwDDgM4DAQIBQwLAAEHCScNAzgNDAILAAsCQkQCLgMAAIsBOQoAEAUKARElDAcOBzgMBAsIDAMFDwoCBwAhDAMLAwQZDQc4DQwFCwAQBQsFQkQCCgAQBAoBESYMCA4IOAwEJAgMBAUoCwIHASEMBAsEBDINCDgNDAYLABAECwY4HwILAA8BCwE4IBGWAS4CLwEAAIUBFQoAEAALARElDAMOAzgMBAkFDQsAAQcJJw0DOA0MAgsAEAALAkJEAjABAACFARUKABAMCwERJgwDDgM4DAQJBQ0LAAEHEScNAzgNDAILABAMCwI4HwIxAwAAhQEVCgAQBQsBESUMAw4DOAwECQUNCwABBwknDQM4DQwCCwAQBQsCQkQCMgMAAIUBIAoAEAULARElDAMOAzgMBAkFDQsAAQcJJwoAEAcOAzgeOA4EFAUYCwABBxMnDQM4DQwCCwAQBQsCQkQCMwMAAIUBFQoAEAQLAREmDAMOAzgMBAkFDQsAAQcRJw0DOA0MAgsAEAQLAjgfAjQDAACMASUKARGSARQMBgoCBwAhBA4LAC4LBhEyDAMFEwsACwYLAhEuDAMLAxGFAQwFCgE4JwwECwUOBCEEHgUiCwEBBxUnCwERkQECNQAAAJABKgoADwYROQoAEAY4KCAEIwUJCgAPBkURDAYKAA8FCwY4GAwIDgEMBQ4IEYEBDAQLBQ4EOBkMBwoACwgKAggLBwoDETYFAwsCAQsAAQsDAQI2AAAAkgFCCgUuEWYGAQAAAAAAAAAWDAYOARGBAQwHDgERjQEMCAoADwIKCDgJAQoAEAkOBzgUBBwKAA8JDgc4FQEBCwQELQoAEAgUDgERjwEXCgAPCBUKBgoHDgERjQESBjgpCwIKBxE3CgYLBw4BEY0BCwMSBDgqDQELBhF8CwAPAwsICwELBRGUATgKAjcAAACVAUEKAC4OATgrBAoKAA4BOCwBAQoALjgtDAUOBUEYDAMGAAAAAAAAAAAMAgoCCgMjBD4OBQoCQhgMBAoACgQ4LgwGCgYuDgE4LwQ1BSUKBg4BODALBi44MQQyCgALBDgsAQEFOQsEAQU5CwYBCwQBCwIGAQAAAAAAAAAWDAIFEwsAAQI4AAAARBwKABAEODIgBBkFBgoADwQ4MwwCDQIKARF5CgEOAhGBAQ4CEY0BEgM4NAoADwULAkREBQALAAECOQAAAJ4BMwoALkERDAQGAQAAAAAAAAAMAgoCCgQjBDAFCwoALgoCQhEUDAEKAgwDCgMGAAAAAAAAAAAkBCsFGAsDBgEAAAAAAAAAFwwDCgAuCgNCERQKASQEKwoACgMKAwYBAAAAAAAAABZHEQUTCwIGAQAAAAAAAAAWDAIFBgsAAQI6AAAAgQEaCgAuQUQMAwYAAAAAAAAAAAwCCgIKAyMEFQULCgAKAkNECgERhwELAgYBAAAAAAAAABYMAgUGCwABCwEBAjsAAACgAR4GAAAAAAAAAAAMAwoAQUQMAgYAAAAAAAAAAAwBCgEKAiMEGgUMCgAKAUJEDAQLAwsEEY4BFgwDCwEGAQAAAAAAAAAWDAEFBwsAAQsDAjwAAAAEBAsACwERmQECPQAAAIEBFwoALkFEDAIGAAAAAAAAAAAMAQoBCgIjBBQFCwoACgFDRBF6CwEGAQAAAAAAAAAWDAEFBgsAAQI+AAAAoQEnBxcMBQ4BODUgBCMFBw0BODYMBAwGCgAKBhEeBBAFFAsAAQcFJwoADAMLBDg3DAILAw4CEUQRlwEmBAINBQsGRBgFAgsAAQsFAj8AAACjAUwLAAwFQBEAAAAAAAAAAAwNCwUMDgsBDAsKDkFEDBALCwwPCg9BEQwEBgAAAAAAAAAADAgLBAwMCggKDCMERgoIDAkKDwsJQhEMCgoKFAoQIwQkBSwLDwELDgELCgEG/////+EEAIAnCg4LChRCRAwRDQ0MBwsREZABNQoDNRgKAjUaNAwGCwcLBkQRCwgGAQAAAAAAAAAWDAgFFAsPAQsOAQsNAkAAAACkATUHFgwGCgBBEQwIBgAAAAAAAAAADAcKBwoIIwQxBQwOAQoHQhEUDAoOAgoACgdCETg4BCUKCjUKAzUYBwMaDAkLCgsJNBcMBAUnCwoMBAsEDAUNBgsFRBELBwYBAAAAAAAAABYMBwUHCwABCwYCQQAAAKUBggEKATgoIAQFBRELAAELAwELBAELAQELAgEHEicLAAwOCwEMCQoOLkFEDA8KCUERDAUGAAAAAAAAAAAMCAsFDA0KCAoNIwR3CggMCgoJCgpCERQMBgoGCg8jBDAFPAsOAQsDAQsJAQsEAQsCAQb/////IAUAgCcLCgwHCgYBCg4LBkNEDBAKAgsHQhEUDAwKAwoMODkMCwsMNQoQLhF7NRgHAxoMEg0LCxI0ODkMEw4TOA8GAAAAAAAAAAAkBG0KEC4RgQEMEQoQCxMKEQoEEYgBCxE4OgVvCxM4OwsQCwsRfQsIBgEAAAAAAAAAFgwIBSALDgELAwELCQELBAELAgECQgAAAKoBoAEKAkERCgNBESEEBwUTCwEBCwUBCwQBCwMBCwIBBv////9HBQCAJwYAAAAAAAAAAAwNCwEMFwoXQUQMBwYAAAAAAAAAAAwQCwcMFAoQChQjBJUBChAMEQoXCxFCRAwYChgRgQEMGgoEDho4KwQ3BTAKBA4aODwUODcMCAU5BxcMCAsIDBYKBQ4aOBkEQgYAAAAAAAAAAAwJBUQGAQAAAAAAAAAMCQsJDBUKAgwZChlBEQwGBgAAAAAAAAAADA4LBgwTCg4KEyMEZgoODA8KGQoPQhEODSEEYQsZAQsPOCEMCgVqCw4GAQAAAAAAAAAWDA4FTwsZATgiDAoLCgwMDgw4DAR2CgMNDDgNQhEUDAsFeAYAAAAAAAAAAAwLCwsMEgoACxoKGBF/ChgRjwEKGBGQAQoYEXsLEgsYCgARhgELFgsVEgI4PQsNBgEAAAAAAAAAFgwNCxAGAQAAAAAAAAAWDBAFHgsXAQsFAQsEAQsDAQsCAQJDAQAAoAEjBgAAAAAAAAAADAQGAAAAAAAAAAAMAgoBQRgMAwoCCgMjBB0FDAoACgEKAkIYFBEtDAULBAsFEZABFgwECwIGAQAAAAAAAAAWDAIFBwsAAQsBAQsEAkQDAACuASoGAAAAAAAAAAAMBwsBDAgKCEEYDAIGAAAAAAAAAAAMBAsCDAYKBAoGIwQkCgQMBQoICwVCGAwDCgALAxQRMgwJCwcLCRGQARYMBwsEBgEAAAAAAAAAFgwEBQsLAAELCAELBwJFAQAABAMLABAAAkYBAAAEBQsAEA0LATgGAkcBAAAEBQsAEA4LATgSAkgDAAAEAwsAEAUCSQMAAAQFCwAQAQsBOAYCSgMAAAQFCwAQAwsBOBICSwMAAK8BKQsAEAUMCQcXDAcLCQwKCgpBRAwBBgAAAAAAAAAADAYLAQwICgYKCCMEJQoGDAUKCgsFQkQMBA0HDAMLBBGBAQwCCwMLAkQYCwYGAQAAAAAAAAAWDAYFDgsKAQsHAkwDAACwAUYKABAFDAJAGAAAAAAAAAAADAoLAgwLCwAQBwwICgtBRAwNCwgMDAoMQREMAQYAAAAAAAAAAAwFCwEMCQoFCgkjBEAKBQwGCgwLBkIRDAcKBxQKDSMEJgUuCwwBCwsBCwcBBv////+yBQCAJwoLCwcUQkQMDg0KDAQLDhGBAQwDCwQLA0QYCwUGAQAAAAAAAAAWDAUFFgsMAQsLAQsKAk0DAACxAZsBCwAQBQwOCg5BRAwUCwEMDAoUCgwlBCsLDgEHFgwPCxQMAgYAAAAAAAAAAAwHCwIMDQoHCg0jBCgKBwwIDQ8MBQsIDAQLBQsERBELBwYBAAAAAAAAABYMBwUWCw8MAwWZAQoMBgAAAAAAAAAAIQQ0Cw4BQBEAAAAAAAAAAAwDBZkBBxgMCgYBAAAAAAAAAAwJCgkKFCMElQEFPQoOCglCRAwVDgpBEQYBAAAAAAAAABcMCwoJCgwjBE0NCgoJRBEKDg4KCgtCERRCRAwQChUMEgsQCxIRiwEEjgEKCQoMIwRjDQoKCQoLRxEFaAoJDQoKC0MRFQoLBgAAAAAAAAAAJAR8Cg4OCgoLBgEAAAAAAAAAF0IRFEJEDBEKFQwTCxELExGLAQwGBX4JDAYLBgSLAQ0KCgsKCwYBAAAAAAAAABdHEQsLBgEAAAAAAAAAFwwLBWgLFQEFkAELFQELCQYBAAAAAAAAABYMCQU4Cw4BCwoMAwsDAk4DAACyAZ4BCgAuCwERTQoADwcVCwMRZgYBAAAAAAAAABYMDQoAEAUMEQoAEAcMCwoRQUQMFAsLDBMKE0ERDAUGAAAAAAAAAAAMBgsFDA8KBgoPIwRUCgYMBwoTCwdCEQwICggUChQjBC4FOAsTAQsRAQsAAQsIAQb/////yAUAgCcKEQsIFEJEDBUKFRGBAQwXDgIOFzgZIARMCg0LFwsVEY0BEgU4PgVPCxUBBU8LBgYBAAAAAAAAABYMBgUeCxMBCxEBCgAuEUwMDA4CDBIKEkEYDAQGAAAAAAAAAAAMCQsEDBAKCQoQIwSZAQoJDAoKEgsKQhgMGA4MChg4GSAEkgEKABAFChgUESUMDg4OOAwEjQELDjgkDBkKABAFCxlCRAwWCg0LGBQLFhGNARIGOCkFlAELGAELDjg/BZQBCxgBCwkGAQAAAAAAAAAWDAkFZQsSAQsAAQIAAQEHAQUBBgEDAQEBBAECAQABCAAAAAQAAgAGAAUASABgABF2YWxpZGF0b3Jfd3JhcHBlcr4EoRzrCwYAAAAMAQAIAggQAxgwBEgGBU40B4IBzgEI0AJABpADCgqaAwYMoANkDYQEAg+GBAIADgEKARAADAABBAABAAIAAgMMAAMCBAAABQABAAAIAgMAAAYBBAAACwUGAAAPBQcAAgQICQEEAgYJDAEEAgkKCwEEAg8NBwAFBAcEBgQCCAMHCAEBCAABBwgAAQcIAwEIAwEGCAAAAQMDAwkABwgBAQgCAQcIAgEHCQABCQABBggCCVR4Q29udGV4dAlWYWxpZGF0b3ILVmFsaWRhdG9yVjEJVmVyc2lvbmVkBmNyZWF0ZQljcmVhdGVfdjEHZGVzdHJveQVpbm5lchxsb2FkX3ZhbGlkYXRvcl9tYXliZV91cGdyYWRlDmxvYWRfdmFsdWVfbXV0CnR4X2NvbnRleHQRdXBncmFkZV90b19sYXRlc3QJdmFsaWRhdG9yDXZhbGlkYXRvcl9zZXQRdmFsaWRhdG9yX3dyYXBwZXIHdmVyc2lvbgl2ZXJzaW9uZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAACAQcIAgADAAAGBgYBAAAAAAAAAAsACwE4ABIAAgEDAAAGBwoALhEDCwAPADgBAgIDAAAGBg4AEQMLABMAOAICAwAAAAYJCwARBAYBAAAAAAAAACEEBgUIBwAnAgQAAAAGBAsAEAARCAIAAAANABJ0aW1lbG9ja2VkX3N0YWtpbmfeFaEc6wsGAAAADAEAHgIeQANehAIE4gIcBf4CwwMHwQarBwjsDWAGzA4UCuAOEQzxDqoGDZsVBg+hFQIAOgIOAhACFwIiAjYCOQI7Aj8AGAA0AEIBIwE1AUQACggAAQAEAQABAgEMAQABAwMCAAQCBwAEDAQABQQEAAYJCAEEAAgLAgAJBQgACgcMAAsNBAAMBgcBAAANCAcAACYAAQAAKQIBAAAsAwEAACsABAAAKgIFAAAtAwYAAC8HBAAAMAcBAAAeCAEAAD0JAQAAPgoBAAAaCwwAACQNDgAAMw0PAAAxDQ8AABMNDwAAHw0QAAAbDQwBAABBBBEAADsSAQAAPBMBAAAnFAEAARIgAQEAAS8wIAEAAUMZDwEAAhQaGwEABBEqAQAEISkqAAYcIgwBBAY3MTIBBAY4JSYBBAY9FwEBBAZAATcBAAclHwEBDAc7HwEBCAguHB0ACSAjJAAJKycoAAktLxYACho1DAAKHjQBAAokLg4ACi8zKAAKMS4PAAozLg8ACyg9KAAMDzg5AQAMHTgMAQAOGTwMAQAfFhgYGRghHhYYHBYeFhcYHRYvNy43IDoiBDAEBAcICQsHAQsBAQgDBQcICAAEBwgJCgsHAQsBAQgDBQcICAMHCAkIAAcICAEIAAEKCAACCwcBCwEBCAMLAQEIAwMHCAADBwgIAgcIAAgAAggABggIAgoIAAYICAIGCAAGCAABAQEGCAABCAQBAwELDAEIDQMICgMLDAEIDQIIAAUCCggABQYHCAsLAQEIAwUDCwwBCA0HCAgDAwMKCAABCwEBCAMCCwcBCQAGCAgBCAMBBgsBAQkAAgsBAQkABwgIAQsCAQkAAQYICAEFAQsCAQgDAgkABQELAQEJAAQLAQEIAwMLDAEIDQgKAgYLBwEJAAYICAEHCAkBBggGAgYIBgsHAQkAAwkAAwsMAQgNBAcICQsCAQgDBQcICAEICgEHCAgBCAUFAwMKCAALBwELAQEIAwgAAQsHAQsBAQgDBgMLDAEIDQMLAQEIAwgKCwEBCAMBBggKAwcICQgKBwgIAgcLAQEJAAMFBggGCQADCwwBCA0HCAgBCwcBCQADBwgKAwcICAIHCAoICgIGCAoGCAoDCA0GCA0BAQgNAQYLDAEJAAEGCQABCQADAwsMAQgNCAoBBgoJAAMHCAsLAQEIAwcICAdCYWxhbmNlBENvaW4CSUQESU9UQRJJb3RhU3lzdGVtQWRtaW5DYXAPSW90YVN5c3RlbVN0YXRlBk9wdGlvbgpTdGFrZWRJb3RhBlN0cmluZwhUaW1lTG9jaxRUaW1lbG9ja2VkU3Rha2VkSW90YQlUeENvbnRleHQDVUlEC1ZhbGlkYXRvclYxB2JhbGFuY2UGYm9ycm93BGNvaW4GZGVsZXRlDGRlc3Ryb3lfemVybxdleHBpcmF0aW9uX3RpbWVzdGFtcF9tcwxmcm9tX2JhbGFuY2UHZ2VuZXNpcwJpZARpb3RhC2lvdGFfc3lzdGVtCGlzX2VtcHR5GWlzX2VxdWFsX3N0YWtpbmdfbWV0YWRhdGEPaXNfbGFiZWxlZF93aXRoCWlzX2xvY2tlZAdpc19zb21lEGpvaW5fc3Rha2VkX2lvdGEFbGFiZWwabG9hZF9pb3RhX3N5c3RlbV9hZG1pbl9jYXADbmV3Bm9iamVjdAZvcHRpb24HcG9vbF9pZA9wdWJsaWNfdHJhbnNmZXIRcmVxdWVzdF9hZGRfc3Rha2UccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcylyZXF1ZXN0X2FkZF9zdGFrZV9hdF9nZW5lc2lzX3dpdGhfcmVjZWlwdBlyZXF1ZXN0X2FkZF9zdGFrZV9tdWxfYmFsI3JlcXVlc3RfYWRkX3N0YWtlX211bF9iYWxfbm9uX2VudHJ5G3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRZyZXF1ZXN0X3dpdGhkcmF3X3N0YWtlIHJlcXVlc3Rfd2l0aGRyYXdfc3Rha2Vfbm9uX2VudHJ5BnNlbmRlcgVzcGxpdBFzcGxpdF9zdGFrZWRfaW90YRZzdGFrZV9hY3RpdmF0aW9uX2Vwb2NoC3N0YWtlZF9pb3RhEnN0YWtlZF9pb3RhX2Ftb3VudAxzdGFraW5nX3Bvb2wGc3RyaW5nEHN5c3RlbV9hZG1pbl9jYXALc3lzdGVtX3BhY2sNc3lzdGVtX3VucGFjawh0aW1lbG9jaxJ0aW1lbG9ja2VkX3N0YWtpbmcIdHJhbnNmZXIRdHJhbnNmZXJfbXVsdGlwbGUSdHJhbnNmZXJfdG9fc2VuZGVyG3RyYW5zZmVyX3RvX3NlbmRlcl9tdWx0aXBsZQp0eF9jb250ZXh0CXR5cGVfbmFtZQZ1bnBhY2sJdmFsaWRhdG9yBXZhbHVlBnZlY3RvcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAAAAAAAAAAADCAEAAAAAAAAAAAIEFggFMggKEwMfCwwBCA0AAQQAAQkLAAsBCwIKAxEDCwMuEQkCAQEEABUfCwALAQsCCgMRBAwGBgAAAAAAAAAADgZBBAwFDAQKBAoFIwQaBRANBkUECgMuEQkLBAYBAAAAAAAAABYMBAULCwMBCwZGBAAAAAAAAAAAAgIBBAAWGgsACwEKAhEFDAMKAi44AA4DOAEGAAAAAAAAAAAkBBULAwoCOAILAi4RIzgDBRk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DBwgNAAIHCAIHCA0CBwgCBggNAwcIAgMGCA0EBwgCCwcBCAgFBwgNAQgRBQcIAgoLBwEICAsYAQMFBwgNAwcIAggRBggNAQsGAQgIAwcIAgYIFAUDCBUFBwsOAgULDwEFAwcIAgoCBggNBAcIAgoCCgIGCA0NBwgCAwMDCwYBCAgLBgEICAMDAwMDAwcIDQUDCwYBCAgDBwgJBggNAwsGAQgIAwMBBggCAQMBBggLAgYIAgUBCw4CBQMBCAoBBgsMAggKBQELDwEFAgcIAgYICgEFAQYLDAIDCBABCgUDCgsHAQgICxgBAwcIDQIDCBYCCggTBwgNAQgWAQYIFgEIEgIFCw8BBQELDgIJAAkBAQgIAQsGAQkAAQcIDQEIBREDAwgFCAsICQgAAwMBAwsGAQgIAwsGAQgIAwgSCw4CBQsPAQUIFgEGCwYBCQABCBcBCBMBBggNDwUKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCA0DBwgXCBMHCA0CBwgXBwgNAQYIFwMHCBcDBggNAQYKCBMCBwgXBggNAQcIEwIHCBMDAQsHAQkABAcIFwULBgEICAcIDQMHCBcIEQYIDQIGCBcFAQEDBwgXBggUAgEIFQIFBwsPAQUBBggVAQYFAgYLDgIJAAkBBgkAAQkAAQsPAQkAAwcLDgIJAAkBCQAJAQIHCw4CCQAJAQYJAAEHCQECBgsPAQkABgkAAgcLDwEJAAkAAgcLDwEJAAYJAAEGCw8BCQACCQAJAQIHCBMHCA0CBwgTCgICBwgTBggTAgYIFwYIEwMHCBMKAgoCDwEDCwYBCAgDAwsGAQgIAwsGAQgICwYBCAgDAwMDCwYBCAgDAQcLBgEJAAIHCwYBCQALBgEJAAkHCBcHCwYBCAgHCw4CBQsPAQUDAwMDAwcIDQMHCAkLBgEICAYIDQQHCBILBgEICAMDAQYIEgEIBAYDAwsGAQgICwYBCAgDAwMHCAkDBggNAgcLBgEJAAMECgUFAwsOAgUDAgUDAQYKCQABBggJAQYJAQIHCBcGCAoFCwYBCAgDCwYBCAgLBwEICAsGAQgIAQsHAQgIAgcLBwEJAAoLBwEJAAEGCxgBCQABCxgBCQACCwYBCQAHCA0CCQAFA0JhZwdCYWxhbmNlBENvaW4CSUQESU9UQRJJb3RhU3lzdGVtQWRtaW5DYXARSW90YVN5c3RlbVN0YXRlVjERSW90YVN5c3RlbVN0YXRlVjIPSW90YVRyZWFzdXJ5Q2FwBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUKU3Rha2VkSW90YQ1TdG9yYWdlRnVuZFYxFlN5c3RlbUVwb2NoSW5mb0V2ZW50VjEWU3lzdGVtRXBvY2hJbmZvRXZlbnRWMhJTeXN0ZW1QYXJhbWV0ZXJzVjEFVGFibGUJVHhDb250ZXh0H1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwDlZhbGlkYXRvclNldFYxDlZhbGlkYXRvclNldFYyC1ZhbGlkYXRvclYxBlZlY01hcAZWZWNTZXQaYWN0aXZlX3ZhbGlkYXRvcl9hZGRyZXNzZXMXYWN0aXZlX3ZhbGlkYXRvcnNfaW5uZXINYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcwNiYWcHYmFsYW5jZQxidXJuX2JhbGFuY2UTYnVybnRfdG9rZW5zX2Ftb3VudARjb2luHWNvbW1pdHRlZV92YWxpZGF0b3JfYWRkcmVzc2VzIWNvbW1pdHRlZV92YWxpZGF0b3Jfdm90aW5nX3Bvd2Vycwhjb250YWlucwZjcmVhdGUYY3JlYXRlX3N5c3RlbV9wYXJhbWV0ZXJzGmRlcml2ZV9yZWZlcmVuY2VfZ2FzX3ByaWNlDGRlc3Ryb3lfc29tZQxkZXN0cm95X3plcm8EZW1pdAVlbXB0eQVlcG9jaBFlcG9jaF9kdXJhdGlvbl9tcxhlcG9jaF9zdGFydF90aW1lc3RhbXBfbXMFZXZlbnQMZXh0cmFfZmllbGRzFGV4dHJhY3RfY29pbl9iYWxhbmNlDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24DZ2V0B2dldF9tdXQQZ2V0X3JlcG9ydGVyc19vZh9nZXRfc3RvcmFnZV9mdW5kX29iamVjdF9yZWJhdGVzHmdldF9zdG9yYWdlX2Z1bmRfdG90YWxfYmFsYW5jZRVnZXRfdG90YWxfaW90YV9zdXBwbHkaZ2V0X3ZhbGlkYXRvcl9tdXRfd2l0aF9jdHgvZ2V0X3ZhbGlkYXRvcl9tdXRfd2l0aF9jdHhfaW5jbHVkaW5nX2NhbmRpZGF0ZXMGaW5zZXJ0DGludG9fYmFsYW5jZQRpb3RhC2lvdGFfc3lzdGVtFWlvdGFfc3lzdGVtX2FkbWluX2NhcBdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lchFpb3RhX3RyZWFzdXJ5X2NhcCZpc19jb21taXR0ZWVfdmFsaWRhdG9yX2J5X2lvdGFfYWRkcmVzcwhpc19lbXB0eQdpc19zb21lBGpvaW4Iam9pbl92ZWM0bWF0Y2hfY29tcHV0YXRpb25fY2hhcmdlX2J1cm5lZF90b192YWxpZGF0b3Jfc3Vic2lkeRNtYXhfdmFsaWRhdG9yX2NvdW50E21pbl92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlDG1pbnRfYmFsYW5jZRRtaW50ZWRfdG9rZW5zX2Ftb3VudANuZXczbmV3X3VudmVyaWZpZWRfdmFsaWRhdG9yX29wZXJhdGlvbl9jYXBfYW5kX3RyYW5zZmVyBm5ld192MRpuZXh0X2Vwb2NoX3ZhbGlkYXRvcl9jb3VudAZvYmplY3QGb3B0aW9uCnBhcmFtZXRlcnMDcGF5E3Bvb2xfZXhjaGFuZ2VfcmF0ZXMQcHJvdG9jb2xfdmVyc2lvbg9wdWJsaWNfdHJhbnNmZXITcmVmZXJlbmNlX2dhc19wcmljZQZyZW1vdmUQcmVwb3J0X3ZhbGlkYXRvchVyZXBvcnRfdmFsaWRhdG9yX2ltcGwRcmVxdWVzdF9hZGRfc3Rha2UacmVxdWVzdF9hZGRfc3Rha2VfbXVsX2NvaW4VcmVxdWVzdF9hZGRfdmFsaWRhdG9yH3JlcXVlc3RfYWRkX3ZhbGlkYXRvcl9jYW5kaWRhdGUYcmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yInJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvcl9jYW5kaWRhdGUbcmVxdWVzdF9zZXRfY29tbWlzc2lvbl9yYXRlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UUcm90YXRlX29wZXJhdGlvbl9jYXAJc2FmZV9tb2RlHXNhZmVfbW9kZV9jb21wdXRhdGlvbl9jaGFyZ2VzJHNhZmVfbW9kZV9jb21wdXRhdGlvbl9jaGFyZ2VzX2J1cm5lZB1zYWZlX21vZGVfY29tcHV0YXRpb25fcmV3YXJkcyRzYWZlX21vZGVfbm9uX3JlZnVuZGFibGVfc3RvcmFnZV9mZWUZc2FmZV9tb2RlX3N0b3JhZ2VfY2hhcmdlcxlzYWZlX21vZGVfc3RvcmFnZV9yZWJhdGVzBnNlbmRlch1zZXRfY2FuZGlkYXRlX2NvbW1pc3Npb25fcmF0ZSdzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9jb21taXNzaW9uX3JhdGUJc2luZ2xldG9uBXNwbGl0DHN0YWtpbmdfcG9vbBtzdGFraW5nX3Bvb2xfbWFwcGluZ3NfaW5uZXIOc3RvcmFnZV9jaGFyZ2UMc3RvcmFnZV9mdW5kFHN0b3JhZ2VfZnVuZF9iYWxhbmNlDnN0b3JhZ2VfcmViYXRlEHN5c3RlbV9hZG1pbl9jYXAUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGULdGlwc19hbW91bnQNdG90YWxfYmFsYW5jZQ50b3RhbF9nYXNfZmVlcxx0b3RhbF9vYmplY3Rfc3RvcmFnZV9yZWJhdGVzC3RvdGFsX3N0YWtlEXRvdGFsX3N0YWtlX2lubmVyH3RvdGFsX3N0YWtlX3Jld2FyZHNfZGlzdHJpYnV0ZWQMdG90YWxfc3VwcGx5CHRyYW5zZmVyCnR4X2NvbnRleHQVdW5kb19yZXBvcnRfdmFsaWRhdG9yGnVuZG9fcmVwb3J0X3ZhbGlkYXRvcl9pbXBsIXVwZGF0ZV9jYW5kaWRhdGVfYXV0aG9yaXR5X3B1YmtleSB1cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfYWRkcmVzcx91cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfcHVia2V5HHVwZGF0ZV9jYW5kaWRhdGVfcDJwX2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcmltYXJ5X2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcm90b2NvbF9wdWJrZXkrdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfYXV0aG9yaXR5X3B1YmtleSp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9uZXR3b3JrX2FkZHJlc3MpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19wdWJrZXkmdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcDJwX2FkZHJlc3MqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcHJpbWFyeV9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3Byb3RvY29sX3B1YmtleRJ1cGRhdGVfZGVzY3JpcHRpb24QdXBkYXRlX2ltYWdlX3VybAt1cGRhdGVfbmFtZSJ1cGRhdGVfbmV4dF9lcG9jaF9hdXRob3JpdHlfcHVia2V5IXVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcyB1cGRhdGVfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleR11cGRhdGVfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MhdXBkYXRlX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5EnVwZGF0ZV9wcm9qZWN0X3VybBx1cGRhdGVfdmFsaWRhdG9yX2Rlc2NyaXB0aW9uGnVwZGF0ZV92YWxpZGF0b3JfaW1hZ2VfdXJsFXVwZGF0ZV92YWxpZGF0b3JfbmFtZSx1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfYXV0aG9yaXR5X3B1YmtleSt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzKnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleSd1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcDJwX2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5HHVwZGF0ZV92YWxpZGF0b3JfcHJvamVjdF91cmwIdjFfdG9fdjIJdmFsaWRhdG9yHHZhbGlkYXRvcl9hZGRyZXNzX2J5X3Bvb2xfaWQidmFsaWRhdG9yX2FkZHJlc3NfYnlfcG9vbF9pZF9pbm5lcg12YWxpZGF0b3JfY2FwIHZhbGlkYXRvcl9sb3dfc3Rha2VfZ3JhY2VfcGVyaW9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AAAAAAAAAAAAAAw12YWxpZGF0b3Jfc2V0DlZhbGlkYXRvclNldFYxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldA5WYWxpZGF0b3JTZXRWMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDXZhbGlkYXRvcl9zZXQZVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnRWMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDXZhbGlkYXRvcl9zZXQSVmFsaWRhdG9ySm9pbkV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldBNWYWxpZGF0b3JMZWF2ZUV2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldBtDb21taXR0ZWVWYWxpZGF0b3JKb2luRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw12YWxpZGF0b3Jfc2V0HENvbW1pdHRlZVZhbGlkYXRvckxlYXZlRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwxzdG9yYWdlX2Z1bmQNU3RvcmFnZUZ1bmRWMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADF2lvdGFfc3lzdGVtX3N0YXRlX2lubmVyElN5c3RlbVBhcmFtZXRlcnNWMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADF2lvdGFfc3lzdGVtX3N0YXRlX2lubmVyEUlvdGFTeXN0ZW1TdGF0ZVYxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIRSW90YVN5c3RlbVN0YXRlVjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxdpb3RhX3N5c3RlbV9zdGF0ZV9pbm5lchZTeXN0ZW1FcG9jaEluZm9FdmVudFYxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMXaW90YV9zeXN0ZW1fc3RhdGVfaW5uZXIWU3lzdGVtRXBvY2hJbmZvRXZlbnRWMgAAAAAAAAA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rbGlzdAZCcmlkZ2UPQnJpZGdlQ29tbWl0dGVlC0JyaWRnZUlubmVyDUJyaWRnZU1lc3NhZ2UQQnJpZGdlTWVzc2FnZUtleQxCcmlkZ2VSZWNvcmQLQnJpZGdlUm91dGUOQnJpZGdlVHJlYXN1cnkFQ2xvY2sEQ29pbgxDb2luTWV0YWRhdGEPQ29tbWl0dGVlTWVtYmVyC0VtZXJnZW5jeU9wEEVtZXJnZW5jeU9wRXZlbnQPSW90YVN5c3RlbVN0YXRlC0xpbmtlZFRhYmxlBk9wdGlvbhpQYXJzZWRUb2tlblRyYW5zZmVyTWVzc2FnZQZTdHJpbmcTVG9rZW5EZXBvc2l0ZWRFdmVudBxUb2tlblRyYW5zZmVyQWxyZWFkeUFwcHJvdmVkG1Rva2VuVHJhbnNmZXJBbHJlYWR5Q2xhaW1lZBVUb2tlblRyYW5zZmVyQXBwcm92ZWQUVG9rZW5UcmFuc2ZlckNsYWltZWQYVG9rZW5UcmFuc2ZlckxpbWl0RXhjZWVkFFRva2VuVHJhbnNmZXJQYXlsb2FkD1RyYW5zZmVyTGltaXRlcgtUcmVhc3VyeUNhcAlUeENvbnRleHQDVUlEEFVwZGF0ZUFzc2V0UHJpY2URVXBkYXRlQnJpZGdlTGltaXQKVXBncmFkZUNhcAZWZWNNYXAJVmVyc2lvbmVkDWFkZF9uZXdfdG9rZW4SYWRkX3Rva2Vuc19vbl9pb3RhB2FkZHJlc3MGYW1vdW50FmFwcHJvdmVfdG9rZW5fdHJhbnNmZXIFYXNjaWkHYmFsYW5jZQZib3Jyb3cKYm9ycm93X211dAZicmlkZ2UOYnJpZGdlX3ZlcnNpb24EYnVybghjaGFpbl9pZAljaGFpbl9pZHMhY2hlY2tfYW5kX3JlY29yZF9zZW5kaW5nX3RyYW5zZmVyGGNsYWltX2FuZF90cmFuc2Zlcl90b2tlbgtjbGFpbV90b2tlbhRjbGFpbV90b2tlbl9pbnRlcm5hbAdjbGFpbWVkBWNsb2NrBGNvaW4JY29tbWl0dGVlE2NvbW1pdHRlZV9ibG9ja2xpc3QRY29tbWl0dGVlX21lbWJlcnMWY29tbWl0dGVlX3JlZ2lzdHJhdGlvbghjb250YWlucwZjcmVhdGUKY3JlYXRlX2tleRtjcmVhdGVfdG9rZW5fYnJpZGdlX21lc3NhZ2UMZGVzdHJveV9ub25lDGRlc3Ryb3lfc29tZQxlbWVyZ2VuY3lfb3ASZW1lcmdlbmN5X29wX3BhdXNlEWVtZXJnZW5jeV9vcF90eXBlFGVtZXJnZW5jeV9vcF91bnBhdXNlBGVtaXQFZW1wdHkFZXZlbnQaZXhlY3V0ZV9hZGRfdG9rZW5zX29uX2lvdGERZXhlY3V0ZV9ibG9ja2xpc3QUZXhlY3V0ZV9lbWVyZ2VuY3lfb3AWZXhlY3V0ZV9zeXN0ZW1fbWVzc2FnZRpleGVjdXRlX3VwZGF0ZV9hc3NldF9wcmljZRtleGVjdXRlX3VwZGF0ZV9icmlkZ2VfbGltaXQaZXh0cmFjdF9hZGRfdG9rZW5zX29uX2lvdGEZZXh0cmFjdF9ibG9ja2xpc3RfcGF5bG9hZBxleHRyYWN0X2VtZXJnZW5jeV9vcF9wYXlsb2FkHGV4dHJhY3RfdG9rZW5fYnJpZGdlX3BheWxvYWQaZXh0cmFjdF91cGRhdGVfYXNzZXRfcHJpY2UbZXh0cmFjdF91cGRhdGVfYnJpZGdlX2xpbWl0CmZyb21fYnl0ZXMGZnJvemVuIWdldF9jdXJyZW50X3NlcV9udW1fYW5kX2luY3JlbWVudAdnZXRfbXV0IWdldF9wYXJzZWRfdG9rZW5fdHJhbnNmZXJfbWVzc2FnZQlnZXRfcm91dGUkZ2V0X3Rva2VuX3RyYW5zZmVyX2FjdGlvbl9zaWduYXR1cmVzIGdldF90b2tlbl90cmFuc2Zlcl9hY3Rpb25fc3RhdHVzAmlkFWluaXRfYnJpZGdlX2NvbW1pdHRlZQVpbm5lcgZpbnNlcnQLaW90YV9zeXN0ZW0IaXNfZW1wdHkJaXNfbmF0aXZlB2lzX3NvbWUOaXNfdmFsaWRfcm91dGUDa2V5B2xpbWl0ZXIMbGlua2VkX3RhYmxlCmxvYWRfaW5uZXIObG9hZF9pbm5lcl9tdXQKbG9hZF92YWx1ZQ5sb2FkX3ZhbHVlX211dAdtZXNzYWdlC21lc3NhZ2Vfa2V5DG1lc3NhZ2VfdHlwZQ1tZXNzYWdlX3R5cGVzD21lc3NhZ2VfdmVyc2lvbgRtaW50A25ldwRub25lBm9iamVjdAZvcHRpb24HcGFja2FnZQZwYXVzZWQPcHVibGljX3RyYW5zZmVyCXB1c2hfYmFjawhyZWdpc3RlchZyZWdpc3Rlcl9mb3JlaWduX3Rva2VuCnNlbmRfdG9rZW4Gc2VuZGVyDnNlbmRlcl9hZGRyZXNzB3NlcV9udW0Nc2VxdWVuY2VfbnVtcwxzaGFyZV9vYmplY3QEc29tZQxzb3VyY2VfY2hhaW4OdGFyZ2V0X2FkZHJlc3MMdGFyZ2V0X2NoYWluCHRvX2J5dGVzIHRvX3BhcnNlZF90b2tlbl90cmFuc2Zlcl9tZXNzYWdlBXRva2VuDHRva2VuX2Ftb3VudAh0b2tlbl9pZAl0b2tlbl9pZHMMdG9rZW5fcHJpY2VzFHRva2VuX3RhcmdldF9hZGRyZXNzEnRva2VuX3RhcmdldF9jaGFpbhZ0b2tlbl90cmFuc2Zlcl9yZWNvcmRzCnRva2VuX3R5cGUQdG9rZW5fdHlwZV9uYW1lcwh0cmFuc2Zlcgh0cmVhc3VyeRl0cnlfY3JlYXRlX25leHRfY29tbWl0dGVlCnR4X2NvbnRleHQbdXBkYXRlX2Fzc2V0X25vdGlvbmFsX3ByaWNlEnVwZGF0ZV9hc3NldF9wcmljZSR1cGRhdGVfYXNzZXRfcHJpY2VfcGF5bG9hZF9uZXdfcHJpY2UjdXBkYXRlX2Fzc2V0X3ByaWNlX3BheWxvYWRfdG9rZW5faWQTdXBkYXRlX2JyaWRnZV9saW1pdCF1cGRhdGVfYnJpZGdlX2xpbWl0X3BheWxvYWRfbGltaXQrdXBkYXRlX2JyaWRnZV9saW1pdF9wYXlsb2FkX3JlY2VpdmluZ19jaGFpbil1cGRhdGVfYnJpZGdlX2xpbWl0X3BheWxvYWRfc2VuZGluZ19jaGFpbg91cGRhdGVfbm9kZV91cmwSdXBkYXRlX3JvdXRlX2xpbWl0BXZhbHVlB3ZlY19tYXATdmVyaWZpZWRfc2lnbmF0dXJlcxF2ZXJpZnlfc2lnbmF0dXJlcwd2ZXJzaW9uCXZlcnNpb25lZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAgEBAgEAAgECAgEDAwgUAAA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zc2FnZRBCcmlkZ2VNZXNzYWdlS2V5C0VtZXJnZW5jeU9wGlBhcnNlZFRva2VuVHJhbnNmZXJNZXNzYWdlBlN0cmluZxRUb2tlblRyYW5zZmVyUGF5bG9hZBBVcGRhdGVBc3NldFByaWNlEVVwZGF0ZUJyaWRnZUxpbWl0EmFkZF90b2tlbnNfb25faW90YQZhbW91bnQGYXBwZW5kBWFzY2lpFWFzc2VydF92YWxpZF9jaGFpbl9pZANiY3MOYmxvY2tsaXN0X3R5cGUdYmxvY2tsaXN0X3ZhbGlkYXRvcl9hZGRyZXNzZXMOYnJpZGdlX3NlcV9udW0JY2hhaW5faWRzE2NvbW1pdHRlZV9ibG9ja2xpc3QhY3JlYXRlX2FkZF90b2tlbnNfb25faW90YV9tZXNzYWdlGGNyZWF0ZV9ibG9ja2xpc3RfbWVzc2FnZRtjcmVhdGVfZW1lcmdlbmN5X29wX21lc3NhZ2UKY3JlYXRlX2tleRtjcmVhdGVfdG9rZW5fYnJpZGdlX21lc3NhZ2UhY3JlYXRlX3VwZGF0ZV9hc3NldF9wcmljZV9tZXNzYWdlImNyZWF0ZV91cGRhdGVfYnJpZGdlX2xpbWl0X21lc3NhZ2UMZW1lcmdlbmN5X29wEmVtZXJnZW5jeV9vcF9wYXVzZRFlbWVyZ2VuY3lfb3BfdHlwZRRlbWVyZ2VuY3lfb3BfdW5wYXVzZRpleHRyYWN0X2FkZF90b2tlbnNfb25faW90YRlleHRyYWN0X2Jsb2NrbGlzdF9wYXlsb2FkHGV4dHJhY3RfZW1lcmdlbmN5X29wX3BheWxvYWQcZXh0cmFjdF90b2tlbl9icmlkZ2VfcGF5bG9hZBpleHRyYWN0X3VwZGF0ZV9hc3NldF9wcmljZRtleHRyYWN0X3VwZGF0ZV9icmlkZ2VfbGltaXQUaW50b19yZW1haW5kZXJfYnl0ZXMIaXNfZW1wdHkJaXNfbmF0aXZlA2tleQVsaW1pdAdtZXNzYWdlDG1lc3NhZ2VfdHlwZQ1tZXNzYWdlX3R5cGVzD21lc3NhZ2VfdmVyc2lvbgxuYXRpdmVfdG9rZW4DbmV3CW5ld19wcmljZQdvcF90eXBlDnBhcnNlZF9wYXlsb2FkB3BheWxvYWQJcGVlbF9ib29sC3BlZWxfdTY0X2JlB3BlZWxfdTgMcGVlbF92ZWNfdTY0C3BlZWxfdmVjX3U4D3BlZWxfdmVjX3ZlY191OA9yZWNlaXZpbmdfY2hhaW4VcmVxdWlyZWRfdm90aW5nX3Bvd2VyB3JldmVyc2UNcmV2ZXJzZV9ieXRlcw5zZW5kZXJfYWRkcmVzcw1zZW5kaW5nX2NoYWluB3NlcV9udW0Rc2VyaWFsaXplX21lc3NhZ2UMc291cmNlX2NoYWluBnN0cmluZw50YXJnZXRfYWRkcmVzcwx0YXJnZXRfY2hhaW4IdG9fYnl0ZXMgdG9fcGFyc2VkX3Rva2VuX3RyYW5zZmVyX21lc3NhZ2UFdG9rZW4MdG9rZW5fYW1vdW50CHRva2VuX2lkCXRva2VuX2lkcwx0b2tlbl9wcmljZXMUdG9rZW5fdGFyZ2V0X2FkZHJlc3MSdG9rZW5fdGFyZ2V0X2NoYWluCnRva2VuX3R5cGUQdG9rZW5fdHlwZV9uYW1lcxJ1cGRhdGVfYXNzZXRfcHJpY2UkdXBkYXRlX2Fzc2V0X3ByaWNlX3BheWxvYWRfbmV3X3ByaWNlI3VwZGF0ZV9hc3NldF9wcmljZV9wYXlsb2FkX3Rva2VuX2lkE3VwZGF0ZV9icmlkZ2VfbGltaXQhdXBkYXRlX2JyaWRnZV9saW1pdF9wYXlsb2FkX2xpbWl0K3VwZGF0ZV9icmlkZ2VfbGltaXRfcGF5bG9hZF9yZWNlaXZpbmdfY2hhaW4pdXBkYXRlX2JyaWRnZV9saW1pdF9wYXlsb2FkX3NlbmRpbmdfY2hhaW4XdmFsaWRhdG9yX2V0aF9hZGRyZXNzZXMGdmVjdG9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgEBAwgUAAAAAAAAAAMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAAAAACAQAKCgIBAAoCAQAAAgUtAi8CQgNEAjUKAgECA0QCLQITAwICBUAKAkcCRgoCUQIMAwMCATMCBAICEQJaCgoCBQIDPAJBAisDBgICTAIyAwcCBDABTQoCUgoICk4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yaWRnZQRidXJuBGNvaW4GY3JlYXRlB2RlY2ltYWwSZGVjaW1hbF9tdWx0aXBsaWVyBmRlY29kZQxkZXN0cm95X3NvbWUEZW1pdAVlbXB0eQVldmVudApmcm9tX2J5dGVzA2dldAtnZXRfYWRkcmVzcwxnZXRfZGVjaW1hbHMHZ2V0X211dBJnZXRfdG9rZW5fbWV0YWRhdGEDaGV4AmlkDWlkX3RvX2FkZHJlc3MRaWRfdG9rZW5fdHlwZV9tYXAGaW5zZXJ0CmludG9fYnl0ZXMLaW50b19zdHJpbmcHaXNfc29tZQRtaW50DG5hdGl2ZV90b2tlbgNuZXcJbmV3X3ByaWNlDm5vdGlvbmFsX3ZhbHVlBm9iamVjdApvYmplY3RfYmFnBm9wdGlvbgdwYWNrYWdlA3BvdxRwdWJsaWNfZnJlZXplX29iamVjdBZyZWdpc3Rlcl9mb3JlaWduX3Rva2VuBnJlbW92ZRBzdXBwb3J0ZWRfdG9rZW5zCHRva2VuX2lkDHRvdGFsX3N1cHBseQh0cmFuc2Zlcgp0cmVhc3VyaWVzCHRyZWFzdXJ5B3RyeV9nZXQKdHhfY29udGV4dAl0eXBlX25hbWUDdTY0AnVjG3VwZGF0ZV9hc3NldF9ub3Rpb25hbF9wcmljZQ91cGdyYWRlX3BhY2thZ2UHdmVjX21hcAx3YWl0aW5nX3Jvb20AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAAIEQggLPgsOAggRCAEsCw4CAggRTAgGAQIEKgIdAzUDMgECAgNGCBFICAwcAgMCAj8CNAMEAgU/AkYIETIBHQM1AwUCA0YIERwCMgEAAQAADAcLADgADAEOARAAFAIBAQAADAcLADgADAEOARABFAICAQAADAcLADgADAEOARACFAIDAwAADjsOATgBBgAAAAAAAAAAIQQGBQwLAAELAwEHAic4AgwHDgcRIhEeERMRCgwFDgIRGAwEDgQRFAsFIQQdBSMLAAELAwEHAScKBwsCCgM4AxICDAYKAA8DCgcRIwsGOAQ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LCQIKAggEBggFAwsMAQMDAgYLCQIJAAkBAwIGCQAGCQECBQMBCwwBCQEBBgsMAQkAAQsMAQkAAQgCCwEKAgYKCgIBAwMHCAQDBgoCCgoCBgoCAQYIBgEGCgoCAgcLCQIJAAkBAwIGCQAHCQEBBgoCAQgAAgMHCAQBCAMDAQMGCAUJQmxvY2tsaXN0F0Jsb2NrbGlzdFZhbGlkYXRvckV2ZW50D0JyaWRnZUNvbW1pdHRlZQ1CcmlkZ2VNZXNzYWdlD0NvbW1pdHRlZU1lbWJlchtDb21taXR0ZWVNZW1iZXJSZWdpc3RyYXRpb24dQ29tbWl0dGVlTWVtYmVyVXJsVXBkYXRlRXZlbnQUQ29tbWl0dGVlVXBkYXRlRXZlbnQPSW90YVN5c3RlbVN0YXRlBk9wdGlvbglUeENvbnRleHQGVmVjTWFwBlZlY1NldBphY3RpdmVfdmFsaWRhdG9yX2FkZHJlc3NlcwZhcHBlbmQOYmxvY2tsaXN0X3R5cGUdYmxvY2tsaXN0X3ZhbGlkYXRvcl9hZGRyZXNzZXMLYmxvY2tsaXN0ZWQGYnJpZGdlE2JyaWRnZV9wdWJrZXlfYnl0ZXMcY2hlY2tfdW5pcXVlbmVzc19icmlkZ2Vfa2V5cwljb21taXR0ZWURY29tbWl0dGVlX21lbWJlcnMIY29udGFpbnMGY3JlYXRlBmNyeXB0bwxkZXN0cm95X3NvbWUIZWNkc2FfazEcZWNkc2FfcHViX2tleV90b19ldGhfYWRkcmVzcwRlbWl0BWVtcHR5BWVwb2NoBWV2ZW50EWV4ZWN1dGVfYmxvY2tsaXN0A2dldBBnZXRfZW50cnlfYnlfaWR4FGdldF9lbnRyeV9ieV9pZHhfbXV0B2dldF9tdXQNaHR0cF9yZXN0X3VybAZpbnNlcnQMaW90YV9hZGRyZXNzC2lvdGFfc3lzdGVtCGlzX2VtcHR5B2lzX3NvbWUbbGFzdF9jb21taXR0ZWVfdXBkYXRlX2Vwb2NoBm1lbWJlchRtZW1iZXJfcmVnaXN0cmF0aW9ucwdtZW1iZXJzB21lc3NhZ2UHbmV3X3VybAZvcHRpb24LcHVibGljX2tleXMIcmVnaXN0ZXIVcmVxdWlyZWRfdm90aW5nX3Bvd2VyE3NlY3AyNTZrMV9lY3JlY292ZXIGc2VuZGVyEXNlcml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BCAEhEQEBCAIMFgEAAwoMAQADCwsMAQIEHBMBAQwGDRUBAAgiDQ4ADAoIAgkCDRICCAAHCAUAAQgBAQYIAQEGCwcBBQEGCwcBCgIBBggGAQcIAQEHCAMECwIBCAEKCAgIBAoICAEIAAIJAAcIBQEIBAEKAgEICAQGCAQKCAgKCAgHCAUBCwIBCQABBwsCAQkAAQsCAQgBAQkAAggDCAYBCAYBCAMHRGlzcGxheQ1JcmMyN01ldGFkYXRhA05GVANOZnQGT3B0aW9uCVB1Ymxpc2hlcgZTdHJpbmcJVHhDb250ZXh0A1VJRBhhZGRyZXNzX3VubG9ja19jb25kaXRpb24OYnVybl9wdWJsaXNoZXIFY2xhaW0GZGVsZXRlB2Rlc3Ryb3kHZGlzcGxheQtkdW1teV9maWVsZAJpZBBpbW11dGFibGVfaXNzdWVyEmltbXV0YWJsZV9tZXRhZGF0YQRpbml0BWlyYzI3DWxlZ2FjeV9zZW5kZXIIbWV0YWRhdGEPbmV3X3dpdGhfZmllbGRzA25mdAZvYmplY3QGb3B0aW9uB3BhY2thZ2UUcHVibGljX2ZyZWV6ZV9vYmplY3QGc3RyaW5nA3RhZwh0cmFuc2Zlcgp0eF9jb250ZXh0DnVwZGF0ZV92ZXJzaW9uBHV0ZjgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKAgUEbmFtZQoCCglpbWFnZV91cmwKAgwLZGVzY3JpcHRpb24KAggHY3JlYXRvcgoCCAd2ZXJzaW9uCgILCm1lZGlhX3R5cGUKAhAPY29sbGVjdGlvbl9uYW1lCgIREGltbXV0YWJsZV9pc3N1ZXIKAhoZe2ltbXV0YWJsZV9tZXRhZGF0YS5uYW1lfQoCGRh7aW1tdXRhYmxlX21ldGFkYXRhLnVyaX0KAiEge2ltbXV0YWJsZV9tZXRhZGF0YS5kZXNjcmlwdGlvbn0KAiEge2ltbXV0YWJsZV9tZXRhZGF0YS5pc3N1ZXJfbmFtZX0KAh0ce2ltbXV0YWJsZV9tZXRhZGF0YS52ZXJzaW9ufQoCIB97aW1tdXRhYmxlX21ldGFkYXRhLm1lZGlhX3R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BCAMCCgIIBwIHCAMJAAEJAQMHCAMJAAkBAgcIAwsEAQkAA0JhZwdCYWxhbmNlGUV4cGlyYXRpb25VbmxvY2tDb25kaXRpb24DTmZ0CU5mdE91dHB1dAZPcHRpb24JUmVjZWl2aW5nI1N0b3JhZ2VEZXBvc2l0UmV0dXJuVW5sb2NrQ29uZGl0aW9uF1RpbWVsb2NrVW5sb2NrQ29uZGl0aW9uCVR4Q29udGV4dANVSUQDYWRkGGFkZHJlc3NfdW5sb2NrX2NvbmRpdGlvbgphdHRhY2hfbmZ0A2JhZwdiYWxhbmNlBmRlbGV0ZQxkZXN0cm95X25vbmUUZHluYW1pY19vYmplY3RfZmllbGQNZXhwaXJhdGlvbl91YxtleHBpcmF0aW9uX3VubG9ja19jb25kaXRpb24HZXh0cmFjdA5leHRyYWN0X2Fzc2V0cwJpZAdpc19zb21lCGxvYWRfbmZ0DW5hdGl2ZV90b2tlbnMDbmZ0Cm5mdF9vdXRwdXQGb2JqZWN0Bm9wdGlvbgdyZWNlaXZlBnJlbW92ZRlzdG9yYWdlX2RlcG9zaXRfcmV0dXJuX3VjJ3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbgt0aW1lbG9ja191Yxl0aW1lbG9ja191bmxvY2tfY29uZGl0aW9uCHRyYW5zZmVyCnR4X2NvbnRleHQGdW5sb2NrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCgIEA25mdAACBhcIAw8LAgEJABoIASELCgEICCMLCgEICRMLCgEIBgAJAAEAAAgxDQA4AAwGCwA6AAwDDAgMBwwFDAIMBA4IOAEEEw0IOAIKAS4RCg4DOAMEGg0DOAQKAREIDgc4BQQjDQc4Bg0CCwE4BwUlCwEBCwg4CAsDOAkLBzgKCwQRBgsCCwULBgIBAAAABQULADYABwA4CwICAQAABQYLADYABwALATgMAgMDAAAFBAsACwE4DQIAAAAJAAwADGFsaWFzX291dHB1dIcFoRzrCwYAAAAOAQAOAg4eAywxBF0IBWVfB8QB2wEInwNABt8DCQroAw8L9wMCDPkDSg3DBAIOxQQCD8cEAgAJAQsBDAEOARMBFgAIAAEIAQABAQIMAAIDBAEAAQQFBAAFBAIBCAEGAAwAAA8AAQEAABQCAAEAAAoDBAEAABEFBgEAAwYMBAIHDAMVDQ4CBwwEDQkEAAUUCggBCAMIBwAECwULAQsAAQkAAwsCAQkACAEIBQIHCAMLBAELAAEJAAIHCwABCQAIBQABBwsAAQkAAQgFAwgFCwIBCQAIAQEJAAEIAwIHCAMLBAEJAAIKAggFAwcIAwkACQECBwgDCQABCQEFQWxpYXMLQWxpYXNPdXRwdXQDQmFnB0JhbGFuY2UJUmVjZWl2aW5nA1VJRANhZGQYYWRkcmVzc191bmxvY2tfY29uZGl0aW9uBWFsaWFzDGFsaWFzX291dHB1dAxhdHRhY2hfYWxpYXMDYmFnB2JhbGFuY2UGZGVsZXRlFGR5bmFtaWNfb2JqZWN0X2ZpZWxkDmV4dHJhY3RfYXNzZXRzAmlkCmxvYWRfYWxpYXMNbmF0aXZlX3Rva2VucwZvYmplY3QHcmVjZWl2ZQZyZW1vdmUIdHJhbnNmZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCgIGBWFsaWFzAAIDEAgDDAsCAQkAEggBAAgAAQAABwwNADgADAELADoADAMMAhEGCwILAwsBAgEDAAAEBAsACwE4AQICAQAABAYLADYABwALATgCAgMAAAAEBQsANgAHADgDAgAAAAgABwAMYmFzaWNfb3V0cHV0pwihHOsLBgAAAAsBABQCFDADRDkEfRYFkwGDAQeWAtoDCPAFYArQBjILggcCDIQHbQ/xBwIADQILAgwCGAIhAiIAEQAdACABGQACCAEAAQEADAACAQQBAAEDCQQABAUCAQgBBQgCAAYDBAAHBgQACAcEAAkEBwEAAAATAAEBAAAaAgMBAAMOEQoABBoSBQEIBiMNCgAHIw8KAQAIIwsKAAkPEAoBAAkSCQUBAAkVBwgBAAkGCAYJDAgMCQ4IDgUFBwYHDAcOAwMCCwABCQAHCAUCCwIBCQAIAQIHCAMLBAELAAEJAAELAAEJAAYLAgEJAAsJAQgGCAMIAQsJAQgHCwkBCAgBCQABCAgBBgsJAQkAAQEBBwsJAQkAAAIICAYIBQEIBgIIBgcIBQEIBwMIBwcLAgEJAAcIBQELCQEJAAEIAwIHCAMLBAEJAANCYWcHQmFsYW5jZQtCYXNpY091dHB1dBlFeHBpcmF0aW9uVW5sb2NrQ29uZGl0aW9uBk9wdGlvbglSZWNlaXZpbmcjU3RvcmFnZURlcG9zaXRSZXR1cm5VbmxvY2tDb25kaXRpb24XVGltZWxvY2tVbmxvY2tDb25kaXRpb24JVHhDb250ZXh0A1VJRBhhZGRyZXNzX3VubG9ja19jb25kaXRpb24DYmFnB2JhbGFuY2UMYmFzaWNfb3V0cHV0BmRlbGV0ZQxkZXN0cm95X25vbmUNZXhwaXJhdGlvbl91YxtleHBpcmF0aW9uX3VubG9ja19jb25kaXRpb24HZXh0cmFjdA5leHRyYWN0X2Fzc2V0cwJpZAdpc19zb21lCG1ldGFkYXRhDW5hdGl2ZV90b2tlbnMGb2JqZWN0Bm9wdGlvbgdyZWNlaXZlBnNlbmRlchlzdG9yYWdlX2RlcG9zaXRfcmV0dXJuX3VjJ3N0b3JhZ2VfZGVwb3NpdF9yZXR1cm5fdW5sb2NrX2NvbmRpdGlvbgN0YWcLdGltZWxvY2tfdWMZdGltZWxvY2tfdW5sb2NrX2NvbmRpdGlvbgh0cmFuc2Zlcgp0eF9jb250ZXh0BnVubG9jawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACCRQIAwwLAgEJABcIARwLCQEIBx8LCQEICBALCQEIBhYLCQEKAh4LCQEKAhsLCQEFAAUAAQAABDALADoAAQEBDAMMBwwGDAUMAgwEDgc4AAQTDQc4AQoBLhEGDgM4AgQaDQM4AwoBEQQOBjgEBCMNBjgFDQILATgGBSULAQELBzgHCwM4CAsGOAkLBBECCwILBQIBAwAACgQLAAsBOAoCAAoAFnN0YXJkdXN0X3VwZ3JhZGVfbGFiZWx+oRzrCwYAAAAFAQACAgIEBwY6CEAgCmAFAAIAAAIAFlNUQVJEVVNUX1VQR1JBREVfTEFCRUwLZHVtbXlfZmllbGQWc3RhcmR1c3RfdXBncmFkZV9sYWJlbAAAAAAAAAAAAAAAAAA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 \ No newline at end of file diff --git a/crates/iota-sdk-types/src/transaction/fixtures/ptb b/crates/iota-sdk-types/src/transaction/fixtures/ptb new file mode 100644 index 000000000..fde860e5c --- /dev/null +++ b/crates/iota-sdk-types/src/transaction/fixtures/ptb @@ -0,0 +1 @@ +AAACAQBS9XyI6sGwH9FxcCrOn5RSUiPmZ3Fu4xD2m4N3Hx342gEAAAAAAAAAIHt7BozDSSJ4QPxmKLRItmyRXoPLWUSdkWuztiZgYnxZAAkBQEIPAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA3BheQlzcGxpdF92ZWMBBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBGlvdGEESU9UQQACAQAAAQEADiNZuoi26Te2Jk4reNY3qw73t7tef5T5Qm5mP/TA4JsBlLFzbB6Fkbin9ET4+hD+XTlWepIvlfB4djPIsp+8TZUBAAAAAAAAACCYb2LrBkiv+L5yEYGh73vrCmgXgdjkIaTlRkGfgS4BVQ4jWbqItuk3tiZOK3jWN6sO97e7Xn+U+UJuZj/0wOCb6AMAAAAAAABAQg8AAAAAAAA= \ No newline at end of file diff --git a/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml index 60cb06cb1..4e44d57eb 100644 --- a/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml +++ b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/Cargo.toml @@ -10,9 +10,9 @@ anyhow = "1.0.71" bcs = "0.1.4" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "5f2c63266a065996d53f98156f0412782b468597" } futures = "0.3.28" -iota-json-rpc-types = { path = "../../../../../../../../iota/crates/iota-json-rpc-types" } -iota-keys = { path = "../../../../../../../../iota/crates/iota-keys" } -iota-sdk = { path = "../../../../../../../../iota/crates/iota-sdk" } -iota-types = { path = "../../../../../../../../iota/crates/iota-types" } -test-cluster = { path = "../../../../../../../../iota/crates/test-cluster" } +iota-json-rpc-types = { path = "../../../../../../../iota/crates/iota-json-rpc-types" } +iota-keys = { path = "../../../../../../../iota/crates/iota-keys" } +iota-sdk = { path = "../../../../../../../iota/crates/iota-sdk" } +iota-types = { path = "../../../../../../../iota/crates/iota-types" } +test-cluster = { path = "../../../../../../../iota/crates/test-cluster" } tokio = "1.39.2" diff --git a/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs index 2426567f1..fe078f87e 100644 --- a/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs +++ b/crates/iota-sdk-types/src/transaction/fixtures/update-transaction-fixtures/src/main.rs @@ -107,6 +107,7 @@ async fn main() -> Result<(), anyhow::Error> { got_epoch_change = true; } } + _ => (), } } } diff --git a/crates/iota-sdk-types/src/transaction/serialization.rs b/crates/iota-sdk-types/src/transaction/serialization.rs index 920ad7397..28f268000 100644 --- a/crates/iota-sdk-types/src/transaction/serialization.rs +++ b/crates/iota-sdk-types/src/transaction/serialization.rs @@ -1116,19 +1116,47 @@ mod transaction_expiration { } fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - use schemars::schema::{Schema, SchemaObject}; - schemars::schema::Schema::Object(schemars::schema::SchemaObject { - subschemas: Some(Box::new(schemars::schema::SubschemaValidation { + use schemars::{ + Map, Set, + schema::{ + InstanceType, ObjectValidation, Schema, SchemaObject, SubschemaValidation, + }, + }; + let mut object = SchemaObject { + instance_type: Some(InstanceType::Object.into()), + object: Some(Box::new(ObjectValidation { + properties: { + let mut props = Map::new(); + props.insert( + "epoch".to_owned(), + gen.subschema_for::(), + ); + props + }, + required: { + let mut required = Set::new(); + required.insert("epoch".to_owned()); + required + }, + // Externally tagged variants must prohibit additional + // properties irrespective of the disposition of + // `deny_unknown_fields`. If additional properties were allowed + // one could easily construct an object that validated against + // multiple variants since here it's the properties rather than + // the values of a property that distingish between variants. + additional_properties: Some(Box::new(false.into())), + ..Default::default() + })), + ..Default::default() + }; + object.metadata().description = Some("Validators wont sign a transaction unless the expiration Epoch is greater than or equal to the current epoch".to_owned()); + let schema = Schema::Object(object); + Schema::Object(SchemaObject { + subschemas: Some(Box::new(SubschemaValidation { one_of: Some(vec![ - schemars::_private::metadata::add_description( - schemars::_private::new_externally_tagged_enum( - "epoch", - gen.subschema_for::(), - ), - "Validators wont sign a transaction unless the expiration Epoch is greater than or equal to the current epoch", - ), + schema, Schema::Object(SchemaObject { - instance_type: Some(schemars::schema::InstanceType::Null.into()), + instance_type: Some(InstanceType::Null.into()), ..SchemaObject::default() }), ]), @@ -1148,7 +1176,7 @@ mod test { use crate::{ ObjectDigest, ObjectId, ObjectReference, - transaction::{Argument, Input, InputArgument, Transaction}, + transaction::{Argument, Input, Transaction}, }; #[test] @@ -1237,7 +1265,7 @@ mod test { // Look in the fixtures folder to see how to update them const GENESIS_TRANSACTION: &str = include_str!("fixtures/genesis"); const CONSENSUS_PROLOGUE: &str = include_str!("fixtures/consensus-commit-prologue-v1"); - const EPOCH_CHANGE: &str = include_str!("fixtures/change-epoch"); + const EPOCH_CHANGE: &str = include_str!("fixtures/change-epoch-v2"); const PTB: &str = include_str!("fixtures/ptb"); for fixture in [GENESIS_TRANSACTION, CONSENSUS_PROLOGUE, EPOCH_CHANGE, PTB] { From b73bfbc113a6a496d89d0897198b3d3b1f19359d Mon Sep 17 00:00:00 2001 From: DaughterOfMars Date: Tue, 3 Jun 2025 16:07:57 +0200 Subject: [PATCH 106/107] chore: Fix typos Co-authored-by: /alex/ --- .github/workflows/ci.yml | 2 +- crates/iota-crypto/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 388b8d71e..b6612cbb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: - name: Get the IOTA testnet binary and start a local network shell: bash env: - IOTA_BINARY_VERSION: "v0.12.0-rc" # used for downloading a specific IOTA binary versions that matches the GraphQL schema for local network tests + IOTA_BINARY_VERSION: "v0.12.0-rc" # used for downloading a specific IOTA binary version that matches the GraphQL schema for local network tests run: | ASSET_NAME="iota-$IOTA_BINARY_VERSION-linux-x86_64.tgz" download_url="https://github.com/iotaledger/iota/releases/download/$IOTA_BINARY_VERSION/$ASSET_NAME" diff --git a/crates/iota-crypto/Cargo.toml b/crates/iota-crypto/Cargo.toml index 316ca1286..61396025b 100644 --- a/crates/iota-crypto/Cargo.toml +++ b/crates/iota-crypto/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" readme = "README.md" repository = "https://github.com/iotaledger/iota-rust-sdk/" -description = "Defines the interface for signing and verying messages in the IOTA ecosystem" +description = "Defines the interface for signing and verifying messages in the IOTA ecosystem" [package.metadata.docs.rs] # To build locally: From 94f74026d8e589c5cfe500c106b4ca70ab215583 Mon Sep 17 00:00:00 2001 From: Chloe Martin Date: Tue, 3 Jun 2025 16:51:09 +0200 Subject: [PATCH 107/107] chore: Add missing license header --- crates/iota-transaction-builder/src/unresolved.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/iota-transaction-builder/src/unresolved.rs b/crates/iota-transaction-builder/src/unresolved.rs index f4870e0cf..70294ad72 100644 --- a/crates/iota-transaction-builder/src/unresolved.rs +++ b/crates/iota-transaction-builder/src/unresolved.rs @@ -1,3 +1,7 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + use iota_types::{Address, Command, ObjectDigest, ObjectId, TransactionExpiration, Version}; // A potentially unresolved user transaction. Note that one can construct a