diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07cf19c73..a275f0120 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,45 +27,45 @@ jobs: all: true test: - name: Test and code coverage - needs: [fmt, toml-sort] - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/checkout@v4 - - name: Install tooling - run: | - sudo apt-get install -y protobuf-compiler - protoc --version - - name: Install latest nextest release - uses: taiki-e/install-action@nextest - - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - name: Rust Cache - uses: Swatinem/rust-cache@v2 - with: - cache-targets: true - cache-on-failure: true - - name: Run tests and print coverage data - run: cargo llvm-cov nextest --json --output-path lcov.json - --ignore-filename-regex "node/" --summary-only && - echo "Lines coverage " && jq ".data[0].totals.lines.percent" lcov.json - # if the PR is on the same repo, the coverage data can be reported as a comment - - if: github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository - name: Generate lcov report - run: cargo llvm-cov report --lcov --output-path lcov.info - --ignore-filename-regex "node/" - - if: github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository - name: Report code coverage - uses: romeovs/lcov-reporter-action@master - with: - lcov-file: lcov.info - pr-number: ${{ github.event.pull_request.number }} - delete-old-comments: true + name: Test and code coverage + needs: [fmt, toml-sort] + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Install tooling + run: | + sudo apt-get install -y protobuf-compiler + protoc --version + - name: Install latest nextest release + uses: taiki-e/install-action@nextest + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + cache-targets: true + cache-on-failure: true + - name: Run tests and print coverage data + run: cargo llvm-cov nextest --json --output-path lcov.json + --ignore-filename-regex "node/" --summary-only && + echo "Lines coverage " && jq ".data[0].totals.lines.percent" lcov.json + # if the PR is on the same repo, the coverage data can be reported as a comment + - if: github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository + name: Generate lcov report + run: cargo llvm-cov report --lcov --output-path lcov.info + --ignore-filename-regex "node/" + - if: github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository + name: Report code coverage + uses: romeovs/lcov-reporter-action@master + with: + lcov-file: lcov.info + pr-number: ${{ github.event.pull_request.number }} + delete-old-comments: true clippy: name: Clippy @@ -103,7 +103,4 @@ jobs: - name: Build run: cargo build - name: Run wallet test - run: | - ./target/debug/node-template --dev & - sleep 10 && - ./target/debug/tuxedo-template-wallet --endpoint http://localhost:9944 + run: ./wallet/test.sh diff --git a/wallet/src/cli.rs b/wallet/src/cli.rs index 7f72f500e..094ca94f0 100644 --- a/wallet/src/cli.rs +++ b/wallet/src/cli.rs @@ -15,22 +15,28 @@ use crate::{h256_from_string, keystore::SHAWN_PUB_KEY, output_ref_from_string, D #[command(about, version)] pub struct Cli { #[arg(long, short, default_value_t = DEFAULT_ENDPOINT.to_string())] - /// RPC endpoint of the node that this wallet will connect to + /// RPC endpoint of the node that this wallet will connect to. pub endpoint: String, #[arg(long, short)] - /// Path where the wallet data is stored. Wallet data is just keystore at the moment, - /// but will contain a local database of UTXOs in the future. - /// - /// Default value is platform specific - pub data_path: Option, + /// Path where the wallet data is stored. Default value is platform specific. + pub path: Option, - #[arg(long)] + #[arg(long, verbatim_doc_comment)] /// Skip the initial sync that the wallet typically performs with the node. - /// /// The wallet will use the latest data it had previously synced. pub no_sync: bool, + #[arg(long)] + /// A temporary directory will be created to store the configuration and will be deleted at the end of the process. + /// path will be ignored if this is set. + pub tmp: bool, + + #[arg(long, verbatim_doc_comment)] + /// Specify a development wallet instance, using a temporary directory (like --tmp). + /// The keystore will contain the development key Shawn. + pub dev: bool, + #[command(subcommand)] pub command: Option, } @@ -42,8 +48,8 @@ pub enum Command { AmoebaDemo, /// Verify that a particular coin exists. - /// /// Show its value and owner from both chain storage and the local database. + #[command(verbatim_doc_comment)] VerifyCoin { /// A hex-encoded output reference #[arg(value_parser = output_ref_from_string)] @@ -51,8 +57,9 @@ pub enum Command { }, /// Spend some coins. - /// - /// For now, all outputs in a single transaction go to the same recipient. FixMe: #62 + /// For now, all outputs in a single transaction go to the same recipient. + // FixMe: #62 + #[command(verbatim_doc_comment)] SpendCoins(SpendArgs), /// Insert a private key into the keystore to later use when signing transactions. @@ -65,7 +72,7 @@ pub enum Command { // sync_height: Option, }, - /// Generate a private key using either some or no password and insert into the keystore + /// Generate a private key using either some or no password and insert into the keystore. GenerateKey { /// Initialize a public/private key pair with a password password: Option, @@ -75,16 +82,18 @@ pub enum Command { ShowKeys, /// Remove a specific key from the keystore. - /// WARNING! This will permanently delete the private key information. Make sure your - /// keys are backed up somewhere safe. + /// WARNING! This will permanently delete the private key information. + /// Make sure your keys are backed up somewhere safe. + #[command(verbatim_doc_comment)] RemoveKey { /// The public key to remove #[arg(value_parser = h256_from_string)] pub_key: H256, }, - /// For each key tracked by the wallet, shows the sum of all UTXO values - /// owned by that key. This sum is sometimes known as the "balance". + /// For each key tracked by the wallet, shows the sum of all UTXO values owned by that key. + /// This sum is sometimes known as the "balance". + #[command(verbatim_doc_comment)] ShowBalance, /// Show the complete list of UTXOs known to the wallet. @@ -95,7 +104,7 @@ pub enum Command { pub struct SpendArgs { /// An input to be consumed by this transaction. This argument may be specified multiple times. /// They must all be coins. - #[arg(long, short, value_parser = output_ref_from_string)] + #[arg(long, short, verbatim_doc_comment, value_parser = output_ref_from_string)] pub input: Vec, // /// All inputs to the transaction will be from this same sender. @@ -105,14 +114,13 @@ pub struct SpendArgs { // https://docs.rs/clap/latest/clap/_derive/_cookbook/typed_derive/index.html // shows how to specify a custom parsing function - /// Hex encoded address (sr25519 pubkey) of the recipient - #[arg(long, short, value_parser = h256_from_string, default_value = SHAWN_PUB_KEY)] + /// Hex encoded address (sr25519 pubkey) of the recipient. + #[arg(long, short, verbatim_doc_comment, value_parser = h256_from_string, default_value = SHAWN_PUB_KEY)] pub recipient: H256, // The `action = Append` allows us to accept the same value multiple times. - /// An output amount. For the transaction to be valid, the outputs must add up to less than - /// the sum of the inputs. The wallet will not enforce this and will gladly send an invalid transaction - /// which will then e rejected by the node. - #[arg(long, short, action = Append)] + /// An output amount. For the transaction to be valid, the outputs must add up to less than the sum of the inputs. + /// The wallet will not enforce this and will gladly send an invalid which will then be rejected by the node. + #[arg(long, short, verbatim_doc_comment, action = Append)] pub output_amount: Vec, } diff --git a/wallet/src/keystore.rs b/wallet/src/keystore.rs index 4e2aaea34..ba6f7eb8f 100644 --- a/wallet/src/keystore.rs +++ b/wallet/src/keystore.rs @@ -26,9 +26,8 @@ pub const SHAWN_PHRASE: &str = /// The public key corresponding to the default seed above. pub const SHAWN_PUB_KEY: &str = "d2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67"; -/// Insert the example "Shawn" key into the keystore for -/// the current session only. This is convenient for ux when developing. -pub fn insert_default_key_for_this_session(keystore: &LocalKeystore) -> anyhow::Result<()> { +/// Insert the example "Shawn" key into the keystore for the current session only. +pub fn insert_development_key_for_this_session(keystore: &LocalKeystore) -> anyhow::Result<()> { keystore.sr25519_generate_new(KEY_TYPE, Some(SHAWN_PHRASE))?; Ok(()) } diff --git a/wallet/src/main.rs b/wallet/src/main.rs index b4ae4df86..3bb4ddb11 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -37,16 +37,24 @@ async fn main() -> anyhow::Result<()> { // Parse command line args let cli = Cli::parse(); + // If the user specified --tmp or --dev, then use a temporary directory. + let tmp = cli.tmp || cli.dev; + // Setup the data paths. - let data_path = cli.data_path.unwrap_or_else(default_data_path); + let data_path = match tmp { + true => temp_dir(), + _ => cli.path.unwrap_or_else(default_data_path), + }; let keystore_path = data_path.join("keystore"); let db_path = data_path.join("wallet_database"); // Setup the keystore let keystore = sc_keystore::LocalKeystore::open(keystore_path.clone(), None)?; - // Insert the example Shawn key so example transactions can be signed. - crate::keystore::insert_default_key_for_this_session(&keystore)?; + if cli.dev { + // Insert the example Shawn key so example transactions can be signed. + crate::keystore::insert_development_key_for_this_session(&keystore)?; + } // Setup jsonrpsee and endpoint-related information. // https://github.com/paritytech/jsonrpsee/blob/master/examples/examples/http.rs @@ -171,7 +179,20 @@ async fn main() -> anyhow::Result<()> { log::info!("No Wallet Command invoked. Exiting."); Ok(()) } + }?; + + if tmp { + // Cleanup the temporary directory. + std::fs::remove_dir_all(data_path.clone()).map_err(|e| { + log::warn!( + "Unable to remove temporary data directory at {}\nPlease remove it manually.", + data_path.to_string_lossy() + ); + e + })?; } + + Ok(()) } //TODO move to rpc.rs @@ -221,6 +242,15 @@ fn strip_0x_prefix(s: &str) -> &str { } } +/// Generate a plaform-specific temporary directory for the wallet +fn temp_dir() -> PathBuf { + // Since it is only used for testing purpose, we don't need a secure temp dir, just a unique one. + std::env::temp_dir().join(format!( + "tuxedo-wallet-{}", + std::time::UNIX_EPOCH.elapsed().unwrap().as_millis(), + )) +} + /// Generate the platform-specific default data path for the wallet fn default_data_path() -> PathBuf { // This uses the directories crate. diff --git a/wallet/test.sh b/wallet/test.sh new file mode 100755 index 000000000..dafece685 --- /dev/null +++ b/wallet/test.sh @@ -0,0 +1,6 @@ +# Integration tests for the Template Wallet. +# Requires a `cargo build` to be run before. + +./target/debug/node-template --dev & +sleep 20 && +./target/debug/tuxedo-template-wallet --dev