Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Ledger sign_tx can not work anymore. always show LedgerError(BadRetcode(BadKeyHandle)) #1189

Closed
lolieatapple opened this issue Apr 28, 2022 · 14 comments · Fixed by #1198
Closed
Labels
bug Something isn't working

Comments

@lolieatapple
Copy link

Version
ethers-signers = { version = "0.6.2", features = ["ledger"]}
ethers-core = "^0.6"

cargo tree | grep ethers
├── ethers-core v0.6.3
├── ethers-signers v0.6.2
│ ├── ethers-core v0.6.3 (*)

Platform
Darwin molindeMacBook-Pro-2.local 21.1.0 Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:01 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T6000 arm64

Description
Ledger sign_tx can not work anymore. always show LedgerError(BadRetcode(BadKeyHandle))

I tried this code:

pub async fn signs_ledger() -> Result<(),()> {
  let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await;
  match ledger {
    Ok(wallet) => {
      println!("Ledger: {:?}", wallet);

      let data = hex::decode("095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();

      let tx_req = TransactionRequest::new()
        .to("0x2ed7afa17473e17ac59908f088b4371d28585476".parse::<Address>().unwrap())
        .gas(1000000)
        .gas_price(400e9 as u64)
        .nonce(5)
        .data(data)
        .value(ethers_core::utils::parse_ether(100).unwrap())
        .into();
      
      println!("tx_req {:?}", tx_req);
      
      let tx = wallet.sign_tx(&tx_req).await;
      match tx {
        Ok(tx_ret) => println!("txRet {:?}", tx_ret),
        Err(e) => println!("Err {:?}", e),
      }
    },
    Err(e) => {
      println!("ERROR {:?}", e.to_string());
    }
  }
  
  Ok(())
}

I expected to see this happen:

Ledger: LedgerEthereum { transport: Mutex { is_locked: false, has_waiters: false }, derivation: LedgerLive(0), chain_id: 1, address: 0x0242e3f6ced3817bd4cbc80b66f59bf2970c157c }
tx_req Legacy(TransactionRequest { from: None, to: Some(Address(0x2ed7afa17473e17ac59908f088b4371d28585476)), gas: Some(1000000), gas_price: Some(400000000000), value: Some(100000000000000000000), data: Some(Bytes(b"\t^\xa7\xb3\0\0\0\0\0\0\0\0\0\0\0\0z%\rV0\xb4\xcfS\x979\xdf,]\xac\xb4\xc6Y\xf2H\x8d\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")), nonce: Some(5) })
Err LedgerError(BadRetcode(BadKeyHandle))

Instead, this happened: LedgerError(BadRetcode(BadKeyHandle))

@lolieatapple lolieatapple added the bug Something isn't working label Apr 28, 2022
@gakonst
Copy link
Owner

gakonst commented Apr 28, 2022

Do you get the same bug if you use ethers = { git = "https://github.com/gakonst/ethers-rs" }?

@lolieatapple
Copy link
Author

but how to get ethers-signers from ethers?

@lolieatapple
Copy link
Author

I will try it now. thanks for reply.

@gakonst
Copy link
Owner

gakonst commented Apr 28, 2022

You can access all sub-packages via: ethers::signers, ethers::providers etc

@lolieatapple
Copy link
Author

I tried it, still error.
My cargo.toml:

[dependencies]
anyhow = "1.0"
rand = "0.8"
ethers = { git = "https://github.com/gakonst/ethers-rs", features = ["ledger"] }
tokio = { version = "1.0", features = ["full"] }
hex = "0.4.3"
[features]
default = ["ledger"]
ledger = [ "ethers/ledger" ]

my code:

use ethers::{prelude::*, utils::parse_ether};

pub async fn signs_ledger() -> Result<(),()> {
  let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await;
  match ledger {
    Ok(wallet) => {
      println!("Ledger: {:?}", wallet);

      let data = hex::decode("095ea7b30000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();

      let tx_req = TransactionRequest::new()
        .to("0x2ed7afa17473e17ac59908f088b4371d28585476".parse::<Address>().unwrap())
        .gas(1000000)
        .gas_price(400e9 as u64)
        .nonce(5)
        .data(data)
        .value(parse_ether(100).unwrap())
        .into();
      
      println!("tx_req {:?}", tx_req);
      
      let tx = wallet.sign_tx(&tx_req).await;
      match tx {
        Ok(tx_ret) => println!("txRet {:?}", tx_ret),
        Err(e) => println!("Err {:?}", e),
      }
    },
    Err(e) => {
      println!("ERROR {:?}", e.to_string());
    }
  }
  
  Ok(())
}

The error output:

Ledger: LedgerEthereum { transport: Mutex { is_locked: false, has_waiters: false }, derivation: LedgerLive(0), chain_id: 1, address: 0x0242e3f6ced3817bd4cbc80b66f59bf2970c157c }
tx_req Legacy(TransactionRequest { from: None, to: Some(Address(0x2ed7afa17473e17ac59908f088b4371d28585476)), gas: Some(1000000), gas_price: Some(400000000000), value: Some(100000000000000000000), data: Some(Bytes(b"\t^\xa7\xb3\0\0\0\0\0\0\0\0\0\0\0\0z%\rV0\xb4\xcfS\x979\xdf,]\xac\xb4\xc6Y\xf2H\x8d\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")), nonce: Some(5), chain_id: None })
Err LedgerError(BadRetcode(BadKeyHandle))

Still have the BadKeyHandle message...

@lolieatapple
Copy link
Author

OMG. I found a way it could work
Use your example code to modify as:

async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect over websockets
    let provider = Provider::<Http>::try_from("https://gwan-ssl.wandevs.org:46891").unwrap();
    let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await?;
    let client = ethers::middleware::SignerMiddleware::new(provider, ledger);

    // Create and broadcast a transaction (ENS enabled!)
    // (this will require confirming the tx on the device)
    let tx = TransactionRequest::new().to("0x7521EDa00E2Ce05aC4a9d8353d096CCB970d5188".parse::<Address>().unwrap()).value(parse_ether(10)?);
    let pending_tx = client.send_transaction(tx, None).await?;

    // Get the receipt
    let _receipt = pending_tx.confirmations(3).await?;
    Ok(())
}

@lolieatapple
Copy link
Author

it pop ledger, but still failed:

Error: MiddlewareError(JsonRpcClientError(JsonRpcError(JsonRpcError { code: -32000, message: "invalid sender", data: None })))

@gakonst
Copy link
Owner

gakonst commented Apr 28, 2022

You are setting the chain id to 1 in let ledger = Ledger::new(HDPath::LedgerLive(0), 1).await?;, when it should be the value of the Chain ID of the network you are connecting to. Try doing provider.get_chainid() and using its output instead of 1

@lolieatapple
Copy link
Author

lolieatapple commented Apr 28, 2022

Thanks for reply. but it still not work.

I use ganache-cli ran a node with chain_id 999. and fill 999 in code. but it still error.
I first deposit my ledger, and then run this code:

    let provider = Provider::<Http>::try_from("http://127.0.0.1:8545").unwrap();
    let chain_id = provider.get_chainid().await.ok();
    println!("chain_id: {:?}", chain_id.unwrap());
    let ledger = Ledger::new(HDPath::LedgerLive(15), 999).await?;
    println!("addr: {:?}", ledger.address());
    println!("balance: {:?}", provider.get_balance(ledger.address(), None).await.ok().unwrap());

    let client = ethers::middleware::SignerMiddleware::new(provider, ledger);

    // Create and broadcast a transaction (ENS enabled!)
    // (this will require confirming the tx on the device)
    let tx = TransactionRequest::new()
        .to("0x7521EDa00E2Ce05aC4a9d8353d096CCB970d5188".parse::<Address>().unwrap())
        .value(parse_ether(0.001)?);
    println!("tx {:?}", tx);

    let pending_tx = client.send_transaction(tx, None).await?;

and it returns:

chain_id: 999
addr: 0x7521eda00e2ce05ac4a9d8353d096ccb970d5188
balance: 11000000000000000000
tx TransactionRequest { from: None, to: Some(Address(0x7521eda00e2ce05ac4a9d8353d096ccb970d5188)), gas: None, gas_price: None, value: Some(1000000000000000), data: None, nonce: None, chain_id: None }
Error: MiddlewareError(JsonRpcClientError(JsonRpcError(JsonRpcError { code: -32000, message: "sender doesn't have enough funds to send tx. The upfront cost is: 1420000000000000 and the sender's account only has: 0", data: Some(Object({"name": String("Error"), "stack": String("Error: sender doesn't have enough funds to send tx. The upfront cost is: 1420000000000000 and the sender's account only has: 0\n    at t.<anonymous> (/Users/molin/.config/yarn/global/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2008456)\n    at /Users/molin/.config/yarn/global/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2007756\n    at Object.next (/Users/molin/.config/yarn/global/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2007861)\n    at c (/Users/molin/.config/yarn/global/node_modules/ganache-cli/build/ganache-core.node.cli.js:55:2006575)\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)")})) })))

I have enought balance, and I could get it from provider, but it throw errror says I have 0 balance.

It usually because the rpc server didn't recover the correct address from tx signature.

@gakonst
Copy link
Owner

gakonst commented Apr 28, 2022

Ah I think I know the bug, this was introduced in the refactor of RLP.

cc @Rjected, we need this logic in every signer, because otherwise a transaction that does not have a set chain id in Ledger ends up getting signed w/o EIP155

@Rjected
Copy link
Contributor

Rjected commented Apr 29, 2022

Ah I think I know the bug, this was introduced in the refactor of RLP.

cc @Rjected, we need this logic in every signer, because otherwise a transaction that does not have a set chain id in Ledger ends up getting signed w/o EIP155

Ah yeah, that makes sense. Should be easy to add that to the rest of the signers. It seems like the trezor signer uses the signer's chain in sign_tx. Adding similar logic in the ledger sign_tx method would be a bit redundant, but since we expose it, it might be a good idea anyways. What do you think?

@gakonst
Copy link
Owner

gakonst commented Apr 29, 2022

I think let's do it like in the private key? If there's a chain id specified in the transaction use that, else use the signer's chain id?

@lolieatapple
Copy link
Author

when will it be merged? :)

@lolieatapple
Copy link
Author

Great, it is good now! Thanks!!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants