From 3b920ad471df150e2d871a3963f4677b3d1d05ed Mon Sep 17 00:00:00 2001 From: akildemir <34187742+akildemir@users.noreply.github.com> Date: Tue, 17 Jan 2023 00:17:54 +0300 Subject: [PATCH] Monerolib Improvements (#224) * convert AddressSpec subbaddress to tuple * add wallet-rpc tests * fix payment id decryption bug * run fmt * fix CI * use monero-rs wallet-rpc for tests * update the subaddress index type * fix wallet-rpc CI * fix monero-wallet-rpc CI actions * pull latest monero for CI * fix pr issues * detach monero wallet rpc Co-authored-by: Luke Parker --- .github/actions/monero-wallet-rpc/action.yml | 44 +++++++++ .github/actions/monero/action.yml | 2 +- .github/actions/test-dependencies/action.yml | 3 + .github/workflows/monero-tests.yaml | 4 +- coins/monero/Cargo.toml | 1 + coins/monero/src/wallet/mod.rs | 16 +++- .../monero/tests/wallet-rpc-compatibility.rs | 91 +++++++++++++++++++ 7 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 .github/actions/monero-wallet-rpc/action.yml create mode 100644 coins/monero/tests/wallet-rpc-compatibility.rs diff --git a/.github/actions/monero-wallet-rpc/action.yml b/.github/actions/monero-wallet-rpc/action.yml new file mode 100644 index 00000000..ebbd2171 --- /dev/null +++ b/.github/actions/monero-wallet-rpc/action.yml @@ -0,0 +1,44 @@ +name: monero-wallet-rpc +description: Spawns a Monero Wallet-RPC. + +inputs: + version: + description: "Version to download and run" + required: false + default: v0.18.1.2 + +runs: + using: "composite" + steps: + - name: Monero Wallet RPC Cache + id: cache-monero-wallet-rpc + uses: actions/cache@v3 + with: + path: monero-wallet-rpc + key: monero-wallet-rpc-${{ runner.os }}-${{ runner.arch }}-${{ inputs.version }} + + - name: Download the Monero Wallet RPC + if: steps.cache-monero-wallet-rpc.outputs.cache-hit != 'true' + # Calculates OS/ARCH to demonstrate it, yet then locks to linux-x64 due + # to the contained folder not following the same naming scheme and + # requiring further expansion not worth doing right now + shell: bash + run: | + RUNNER_OS=${{ runner.os }} + RUNNER_ARCH=${{ runner.arch }} + + RUNNER_OS=${RUNNER_OS,,} + RUNNER_ARCH=${RUNNER_ARCH,,} + + RUNNER_OS=linux + RUNNER_ARCH=x64 + + FILE=monero-$RUNNER_OS-$RUNNER_ARCH-${{ inputs.version }}.tar.bz2 + wget https://downloads.getmonero.org/cli/$FILE + tar -xvf $FILE + + mv monero-x86_64-linux-gnu-${{ inputs.version }}/monero-wallet-rpc monero-wallet-rpc + + - name: Monero Wallet RPC + shell: bash + run: ./monero-wallet-rpc --disable-rpc-login --rpc-bind-port 6061 --allow-mismatched-daemon-version --wallet-dir ./ --detach diff --git a/.github/actions/monero/action.yml b/.github/actions/monero/action.yml index 10ec5056..8985a548 100644 --- a/.github/actions/monero/action.yml +++ b/.github/actions/monero/action.yml @@ -5,7 +5,7 @@ inputs: version: description: "Version to download and run" required: false - default: v0.18.0.0 + default: v0.18.1.2 runs: using: "composite" diff --git a/.github/actions/test-dependencies/action.yml b/.github/actions/test-dependencies/action.yml index 9af81eef..9ce95eaf 100644 --- a/.github/actions/test-dependencies/action.yml +++ b/.github/actions/test-dependencies/action.yml @@ -29,3 +29,6 @@ runs: uses: ./.github/actions/monero with: version: ${{ inputs.monero-version }} + + - name: Run a Monero Wallet-RPC + uses: ./.github/actions/monero-wallet-rpc diff --git a/.github/workflows/monero-tests.yaml b/.github/workflows/monero-tests.yaml index d5c66acc..37595084 100644 --- a/.github/workflows/monero-tests.yaml +++ b/.github/workflows/monero-tests.yaml @@ -33,7 +33,7 @@ jobs: # Test against all supported protocol versions strategy: matrix: - version: [v0.17.3.2, v0.18.0.0] + version: [v0.17.3.2, v0.18.1.2] steps: - uses: actions/checkout@v3 @@ -50,7 +50,7 @@ jobs: - name: Run Integration Tests # Don't run if the the tests workflow also will - if: ${{ matrix.version != 'v0.18.0.0' }} + if: ${{ matrix.version != 'v0.18.1.2' }} run: | cargo test --package monero-serai --all-features --test '*' cargo test --package serai-processor monero diff --git a/coins/monero/Cargo.toml b/coins/monero/Cargo.toml index 6525ab25..50f95958 100644 --- a/coins/monero/Cargo.toml +++ b/coins/monero/Cargo.toml @@ -55,6 +55,7 @@ monero-generators = { path = "generators", version = "0.1" } [dev-dependencies] tokio = { version = "1", features = ["full"] } +monero-rpc = "0.3" frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.5", features = ["ed25519", "tests"] } diff --git a/coins/monero/src/wallet/mod.rs b/coins/monero/src/wallet/mod.rs index 11aaff41..383eb09a 100644 --- a/coins/monero/src/wallet/mod.rs +++ b/coins/monero/src/wallet/mod.rs @@ -60,13 +60,15 @@ pub(crate) fn shared_key( ) -> (u8, Scalar, [u8; 8]) { // 8Ra let mut output_derivation = (s * P).mul_by_cofactor().compress().to_bytes().to_vec(); + + let mut payment_id_xor = [0; 8]; + payment_id_xor + .copy_from_slice(&hash(&[output_derivation.as_ref(), [0x8d].as_ref()].concat())[.. 8]); + // || o write_varint(&o.try_into().unwrap(), &mut output_derivation).unwrap(); let view_tag = hash(&[b"view_tag".as_ref(), &output_derivation].concat())[0]; - let mut payment_id_xor = [0; 8]; - payment_id_xor - .copy_from_slice(&hash(&[output_derivation.as_ref(), [0x8d].as_ref()].concat())[.. 8]); // uniqueness || let shared_key = if let Some(uniqueness) = uniqueness { @@ -106,6 +108,14 @@ impl ViewPair { ViewPair { spend, view } } + pub fn spend(&self) -> EdwardsPoint { + self.spend + } + + pub fn view(&self) -> EdwardsPoint { + self.view.deref() * &ED25519_BASEPOINT_TABLE + } + fn subaddress_derivation(&self, index: SubaddressIndex) -> Scalar { hash_to_scalar(&Zeroizing::new( [ diff --git a/coins/monero/tests/wallet-rpc-compatibility.rs b/coins/monero/tests/wallet-rpc-compatibility.rs new file mode 100644 index 00000000..12353db8 --- /dev/null +++ b/coins/monero/tests/wallet-rpc-compatibility.rs @@ -0,0 +1,91 @@ +use std::{ + collections::{HashSet, HashMap}, + str::FromStr, +}; + +use rand_core::{RngCore, OsRng}; + +use monero_rpc::{ + monero::{Amount, Address}, + TransferOptions, +}; + +use monero_serai::{ + wallet::address::{Network, AddressSpec, SubaddressIndex}, + wallet::Scanner, +}; + +mod runner; + +async fn test_from_wallet_rpc_to_self(spec: AddressSpec) { + let wallet_rpc = + monero_rpc::RpcClientBuilder::new().build("http://127.0.0.1:6061").unwrap().wallet(); + let daemon_rpc = runner::rpc().await; + + // initialize wallet rpc + let address_resp = wallet_rpc.get_address(0, None).await; + let wallet_rpc_addr = if address_resp.is_ok() { + address_resp.unwrap().address + } else { + wallet_rpc.create_wallet("test_wallet".to_string(), None, "English".to_string()).await.unwrap(); + let addr = wallet_rpc.get_address(0, None).await.unwrap().address; + daemon_rpc.generate_blocks(&addr.to_string(), 70).await.unwrap(); + addr + }; + + // make an addr + let (_, view_pair, _) = runner::random_address(); + let addr = Address::from_str(&view_pair.address(Network::Mainnet, spec).to_string()[..]).unwrap(); + + // refresh & make a tx + wallet_rpc.refresh(None).await.unwrap(); + let tx = wallet_rpc + .transfer( + HashMap::from([(addr, Amount::ONE_XMR)]), + monero_rpc::TransferPriority::Default, + TransferOptions::default(), + ) + .await + .unwrap(); + let tx_hash: [u8; 32] = tx.tx_hash.0.try_into().unwrap(); + + // unlock it + runner::mine_until_unlocked(&daemon_rpc, &wallet_rpc_addr.to_string(), tx_hash).await; + + // create the scanner + let mut scanner = Scanner::from_view(view_pair, Some(HashSet::new())); + if let AddressSpec::Subaddress(index) = spec { + scanner.register_subaddress(index); + } + + // retrieve it and confirm + let tx = daemon_rpc.get_transaction(tx_hash).await.unwrap(); + let output = scanner.scan_transaction(&tx).not_locked().swap_remove(0); + + match spec { + AddressSpec::Subaddress(index) => assert_eq!(output.metadata.subaddress, Some(index)), + AddressSpec::Integrated(payment_id) => { + assert_eq!(output.metadata.payment_id, payment_id); + assert_eq!(output.metadata.subaddress, None); + } + _ => assert_eq!(output.metadata.subaddress, None), + } + assert_eq!(output.commitment().amount, 1000000000000); +} + +async_sequential!( + async fn test_receipt_of_wallet_rpc_tx_standard() { + test_from_wallet_rpc_to_self(AddressSpec::Standard).await; + } + + async fn test_receipt_of_wallet_rpc_tx_subaddress() { + test_from_wallet_rpc_to_self(AddressSpec::Subaddress(SubaddressIndex::new(0, 1).unwrap())) + .await; + } + + async fn test_receipt_of_wallet_rpc_tx_integrated() { + let mut payment_id = [0u8; 8]; + OsRng.fill_bytes(&mut payment_id); + test_from_wallet_rpc_to_self(AddressSpec::Integrated(payment_id)).await; + } +);