Skip to content

Commit 9dd622a

Browse files
authored
anvil-polkadot: add eth_sendUnsignedTransaction rpc (#348)
* anvil-polkadot: add eth_sendUnsignedTransaction rpc Signed-off-by: Iulian Barbu <iulian.barbu@parity.io> * anvil-polkadot(tests): introduce a send unsigned tx Signed-off-by: Iulian Barbu <iulian.barbu@parity.io> --------- Signed-off-by: Iulian Barbu <iulian.barbu@parity.io>
1 parent af00f21 commit 9dd622a

File tree

3 files changed

+87
-9
lines changed

3 files changed

+87
-9
lines changed

crates/anvil-polkadot/src/api_server/server.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ impl ApiServer {
177177
}
178178
EthRequest::EthCall(call, block, _, _) => self.call(call, block).await.to_rpc_result(),
179179
EthRequest::EthSendTransaction(request) => {
180-
self.send_transaction(*request.clone()).await.to_rpc_result()
180+
self.send_transaction(*request.clone(), false).await.to_rpc_result()
181181
}
182182
EthRequest::EthGasPrice(()) => self.gas_price().await.to_rpc_result(),
183183
EthRequest::EthGetBlockByNumber(num, hydrated) => {
@@ -252,6 +252,10 @@ impl ApiServer {
252252
node_info!("eth_getLogs");
253253
self.get_logs(filter).await.to_rpc_result()
254254
}
255+
EthRequest::EthSendUnsignedTransaction(request) => {
256+
node_info!("eth_sendUnsignedTransaction");
257+
self.send_transaction(*request.clone(), true).await.to_rpc_result()
258+
}
255259
_ => Err::<(), _>(Error::RpcUnimplemented).to_rpc_result(),
256260
};
257261

@@ -546,6 +550,7 @@ impl ApiServer {
546550
async fn send_transaction(
547551
&self,
548552
transaction_req: WithOtherFields<TransactionRequest>,
553+
unsigned_tx: bool,
549554
) -> Result<H256> {
550555
node_info!("eth_sendTransaction");
551556
let mut transaction = convert_to_generic_transaction(transaction_req.clone().into_inner());
@@ -576,7 +581,7 @@ impl ApiServer {
576581
.try_into_unsigned()
577582
.map_err(|_| Error::ReviveRpc(EthRpcError::InvalidTransaction))?;
578583

579-
let payload = if self.impersonation_manager.is_impersonated(from) {
584+
let payload = if self.impersonation_manager.is_impersonated(from) || unsigned_tx {
580585
let mut fake_signature = [0; 65];
581586
fake_signature[12..32].copy_from_slice(from.as_bytes());
582587
tx.with_signature(fake_signature).signed_payload()

crates/anvil-polkadot/tests/it/impersonation.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,48 @@ async fn test_auto_impersonate(#[case] rpc_driven: bool) {
183183
format!("Account not found for address {}", dest_addr.to_string().to_lowercase()).as_str()
184184
));
185185
}
186+
187+
#[tokio::test(flavor = "multi_thread")]
188+
async fn test_send_unsigned_tx() {
189+
let anvil_node_config = AnvilNodeConfig::test_config();
190+
let substrate_node_config = SubstrateNodeConfig::new(&anvil_node_config);
191+
let mut node = TestNode::new(anvil_node_config.clone(), substrate_node_config).await.unwrap();
192+
193+
// Enable automine.
194+
unwrap_response::<()>(node.eth_rpc(EthRequest::SetAutomine(true)).await.unwrap()).unwrap();
195+
196+
// Create a random account.
197+
let alith_account = Account::from(subxt_signer::eth::dev::alith());
198+
let alith_addr = Address::from(ReviveAddress::new(alith_account.address()));
199+
let transfer_amount = U256::from(16e17);
200+
let dest_addr =
201+
transfer_to_unitialized_random_account(&mut node, alith_addr, transfer_amount).await;
202+
let dest_h160 = H160::from_slice(dest_addr.as_slice());
203+
204+
// Impersonate destination
205+
let transfer_amount = U256::from(1e11);
206+
let alith_balance = node.get_balance(alith_account.address(), None).await;
207+
let dest_balance = node.get_balance(dest_h160, None).await;
208+
let transaction =
209+
TransactionRequest::default().value(transfer_amount).from(dest_addr).to(alith_addr);
210+
let tx_hash = node
211+
.send_unsigned_transaction(
212+
transaction,
213+
Some(BlockWaitTimeout::new(2, Duration::from_secs(1))),
214+
)
215+
.await
216+
.unwrap();
217+
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
218+
let receipt_info = node.get_transaction_receipt(tx_hash).await;
219+
220+
// Assert on balances after second transfer.
221+
let alith_final_balance = node.get_balance(alith_account.address(), None).await;
222+
let dest_final_balance = node.get_balance(dest_h160, None).await;
223+
assert_eq!(alith_final_balance, alith_balance + transfer_amount);
224+
assert_eq!(
225+
dest_final_balance,
226+
dest_balance
227+
- transfer_amount
228+
- AlloyU256::from(receipt_info.effective_gas_price * receipt_info.gas_used).inner()
229+
);
230+
}

crates/anvil-polkadot/tests/it/utils.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,41 @@ impl TestNode {
120120
transaction: TransactionRequest,
121121
timeout: Option<BlockWaitTimeout>,
122122
) -> Result<H256, RpcError> {
123-
let tx_hash = unwrap_response::<H256>(
124-
self.eth_rpc(EthRequest::EthSendTransaction(Box::new(WithOtherFields::new(
125-
transaction,
126-
))))
127-
.await
128-
.unwrap(),
129-
)?;
123+
self.send_transaction_inner(transaction, timeout, false).await
124+
}
125+
126+
/// Execute an impersonated ethereum transaction.
127+
pub async fn send_unsigned_transaction(
128+
&mut self,
129+
transaction: TransactionRequest,
130+
timeout: Option<BlockWaitTimeout>,
131+
) -> Result<H256, RpcError> {
132+
self.send_transaction_inner(transaction, timeout, true).await
133+
}
134+
135+
async fn send_transaction_inner(
136+
&mut self,
137+
transaction: TransactionRequest,
138+
timeout: Option<BlockWaitTimeout>,
139+
unsigned: bool,
140+
) -> Result<H256, RpcError> {
141+
let tx_hash = if unsigned {
142+
unwrap_response::<H256>(
143+
self.eth_rpc(EthRequest::EthSendUnsignedTransaction(Box::new(
144+
WithOtherFields::new(transaction),
145+
)))
146+
.await
147+
.unwrap(),
148+
)?
149+
} else {
150+
unwrap_response::<H256>(
151+
self.eth_rpc(EthRequest::EthSendTransaction(Box::new(WithOtherFields::new(
152+
transaction,
153+
))))
154+
.await
155+
.unwrap(),
156+
)?
157+
};
130158

131159
if let Some(BlockWaitTimeout { block_number, timeout }) = timeout {
132160
self.wait_for_block_with_timeout(block_number, timeout).await.unwrap();

0 commit comments

Comments
 (0)