Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impersonate Account Asset Transfer Fails: InvalidSignature #1507

Open
varun-doshi opened this issue Sep 13, 2024 · 2 comments
Open

Impersonate Account Asset Transfer Fails: InvalidSignature #1507

varun-doshi opened this issue Sep 13, 2024 · 2 comments
Assignees

Comments

@varun-doshi
Copy link

I'm trying to test the ImpersonateAccount feature which allows taking control of any account without its private key and run transactions on my fork.

However, it throws an error InvalidSignature when trying to transfer assets to another account. The exact error is as follows:

Decode error: Custom { kind: Other, error: \"Response errors; Invalid transaction data: Validity(InputInvalidSignature { index: 0 })\" }")

I've tried this with two setups:
Setup 1:

  • I use my own private key to create a UnlockedWallet and send my base_asset to another account. This runs successfully and generates a receipt.

Setup 2:

  • I try to impersonate my own account and send the base_asset to another account. This fails with the error given above.
  • For context, I've tried this with other accounts I've picked up from the explorer as well. But for better reproducibility, I've used this comparison of the same setup with same account. Only difference being, one use unlockedWallet and the other is impersonatedWallet.

The conclusion I come to is that it looks like some Signature verification fails when using the impersonated wallet.I may be wrong though.

Steps to reproduce

use std::{ops::Add, str::FromStr};
use dotenv::dotenv;

use fuels::{accounts::{impersonated_account, provider, wallet::Wallet}, client::PaginationRequest, crypto::SecretKey, prelude::*, types::{errors::transaction, ContractId, Identity}};



#[tokio::test]
async fn transfer_testnet_funds_from_impersonate(){
    
    let provider=Provider::connect("https://testnet.fuel.network/").await.unwrap();
    println!("Block before:{:?}",provider.latest_block_height().await.unwrap());
    
    let BASE_ASSET_ID=AssetId::from_str("0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07").unwrap();

    let my_address=Address::from_str("0xcf3600a55aa1974397559aebb24ddd6c8e9067c1a02a21c2531b8eac2bc7b7c7").unwrap();
    let bech_my_address=Bech32Address::from(my_address);

let receiver_address=Bech32Address::from_str("fuel18llu9z7mpfrqyclwm2d4d7w9z47gqjxztmg3dsayuh8w0zajfwus34ml3a").unwrap();
    let imper=ImpersonatedAccount::new(bech_my_address, Some(provider.clone()));
    let amt:u64=1;
    let mut tx_policies=TxPolicies::default().with_max_fee(900);

    
    println!("Balance of testnet account:{:?}",imper.get_spendable_resources(BASE_ASSET_ID, 1, None).await.unwrap());
    println!("Balance of base asset:{:?}",imper.get_asset_balance(&BASE_ASSET_ID).await.unwrap());

    //FAILS
    let transfer=imper.transfer(&receiver_address, amt,BASE_ASSET_ID , tx_policies).await.unwrap();
    println!("RECEIPT OF IMPERSONATE TRANSFER:{:?}",transfer.1[0]);
 
}

#[tokio::test]
async fn transfer_from_personal(){
    let provider=Provider::connect("https://testnet.fuel.network/").await.unwrap();
    println!("Block before:{:?}",provider.latest_block_height().await.unwrap());
    
    let BASE_ASSET_ID=AssetId::from_str("0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07").unwrap();
    let OTHER_ASSET_ID=AssetId::from_str("0x2a0d0ed9d2217ec7f32dcd9a1902ce2a66d68437aeff84e3a3cc8bebee0d2eea").unwrap();

    // fuel1eumqpf265xt58964nt4mynwadj8fqe7p5q4zrsjnrw82c278klrs53n24w
    dotenv().ok();
    let secret = match std::env::var("SECRET") {
        Ok(s) => s,
        Err(error) => panic!("❌ Cannot find .env file: {:#?}", error),
    };
    let secret_key=SecretKey::from_str(&secret).unwrap();
    let my_priv_acc=WalletUnlocked::new_from_private_key(secret_key, Some(provider.clone()));
    
    
    let receiver_address=Bech32Address::from_str("fuel18llu9z7mpfrqyclwm2d4d7w9z47gqjxztmg3dsayuh8w0zajfwus34ml3a").unwrap();
    let amt:u64=1;
    let mut tx_policies=TxPolicies::default().with_max_fee(900);

    
    println!("My balance:{:?}",provider.get_balances(&my_priv_acc.address()).await.unwrap());
    println!("Spendable resource:{:?}",my_priv_acc.get_spendable_resources(BASE_ASSET_ID, 1, None).await.unwrap());

    //PASSES
    let transfer=my_priv_acc.transfer(&receiver_address, amt,BASE_ASSET_ID , tx_policies).await.unwrap();
    println!("RECEIPT FROM TESTNET TRANSFER: {:?}",transfer.1[0]);
}

My current fuel setup:

Installed toolchains
--------------------
latest-aarch64-apple-darwin (default)

active toolchain
----------------
latest-aarch64-apple-darwin (default)
  forc : 0.63.1
    - forc-client
      - forc-deploy : 0.63.1
      - forc-run : 0.63.1
    - forc-crypto : 0.63.1
    - forc-debug : 0.63.1
    - forc-doc : 0.63.1
    - forc-fmt : 0.63.1
    - forc-lsp : 0.63.1
    - forc-tx : 0.63.1
    - forc-wallet : 0.9.0
  fuel-core : 0.34.0
  fuel-core-keygen : 0.34.0

fuels versions
--------------
forc : 0.66.1
forc-wallet : 0.66.0

My Cargo.toml:

[package]
name = "test-v2"
description = "A cargo-generate template for Rust + Sway integration testing."
version = "0.1.0"
edition = "2021"
authors = ["Varun Doshi <[email protected]>"]
license = "Apache-2.0"

[dev-dependencies]
fuels = { version = "0.66.0", features = ["fuel-core-lib"] }
tokio = { version = "1.12", features = ["rt", "macros"] }

[[test]]
harness = true
name = "integration_tests"
path = "tests/harness.rs"

[dependencies]
dotenv = "0.15.0"

@varun-doshi
Copy link
Author

varun-doshi commented Sep 13, 2024

I understand there is a method:

let node_config = NodeConfig {
        utxo_validation: false,
        ..Default::default()
    };

But from what I understand, this can only be set for a local node
What if I want to connect to a remote RPC. Can I set the NodeConfig for that provider? Currently, I do not see any functions to carry this out.

I would also like to know if it is possible to fork the testnet from a historic block. So connect to a remote RPC but at Block:900 (lets assume the current testnet block is 1000).

@hal3e hal3e self-assigned this Sep 16, 2024
@hal3e
Copy link
Contributor

hal3e commented Sep 17, 2024

@varun-doshi your observation is correct. You can not use the ImpersonateAccount without setting the utxo_validation to false. There is no way of setting this config to an already running node. That would be a huge vulnerability. So if you setup your local node and set the validation to false you can use the impersonated accounts.

I have talked with the client team and currently there is no easy way to fork the tesnet. The only way, for now, is to sync the whole testnet (can take a lot of space) and then rewind to a specific height.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants