alloy-rs / alloy

Transports, Middleware, and Networks for the Alloy project
https://alloy.rs
Apache License 2.0
587 stars 213 forks source link

[Bug] Signatures for legacy TXs following EIP-155 don't work as expected #539

Open kayabaNerve opened 5 months ago

kayabaNerve commented 5 months ago

Component

consensus, eips, genesis

What version of Alloy are you on?

037dd4b20ec8533d6b6d5cf5e9489bbb182c18c6

Operating System

Linux

Describe the bug

pub async fn send(
  provider: &RootProvider<SimpleRequest>,
  wallet: &k256::ecdsa::SigningKey,
  mut tx: TxLegacy,
) -> Option<TransactionReceipt> {
  let verifying_key = *wallet.verifying_key().as_affine();
  let address = Address::from(address(&verifying_key.into()));
  dbg!(address);

  let chain_id = provider.get_chain_id().await.unwrap();
  tx.chain_id = Some(chain_id);
  tx.nonce = provider.get_transaction_count(address, None).await.unwrap();
  // 100 gwei
  tx.gas_price = 100_000_000_000u128;
  dbg!(&tx);

  let sig = wallet.sign_prehash_recoverable(tx.signature_hash().as_ref()).unwrap();
  let tx = tx.into_signed(sig.into());
  assert_eq!(address, tx.recover_signer().unwrap());
  assert!(
    dbg!(provider.get_balance(address, None).await.unwrap()) >
      dbg!(((U256::from(tx.tx().gas_price) * U256::from(tx.tx().gas_limit)) + tx.tx().value))
  );

  let mut bytes = vec![];
  use alloy_rlp::Encodable;
  alloy_consensus::TxEnvelope::from(tx).encode(&mut bytes);
  /*
  tx.encode_with_signature_fields(&sig.into(), &mut bytes);
  */

  let pending_tx = dbg!(provider.send_raw_transaction(&bytes).await).ok()?;
  dbg!(pending_tx.get_receipt().await).ok()
}

I have the following function to manually sign, encode, and publish a transaction. It's part of my tests which run against an AnvilInstance spawned via alloy (Foundry from April 15th, 2024, just updated while debugging this).

This will pass both asserts (expected signer, has enough balance) and error with insufficient funds. Setting chain_id to None causes the TX to be published without issue.

I'm unsure if the signature hash isn't respecting EIP-155, or if the signature v isn't being encoded correctly when published, or if the fact I'm creating the signature from an ecdsa signature (as in crate, I know all TX signatures are ECDSA) causes the v to be malformed. This could also be an issue with Anvil, yet I'd assume it's with alloy.

I also confirmed the address I expect on my end, and recovered, matches the first address returned by AnvilInstance::addresses.

prestwich commented 4 months ago

would you mind letting me know if you're still encountering this issue? we had a few fixes to signature parity logic that could have affected this