I have been trying to implement a transaction queue which will store extrinsic call data and later execute it.
Transaction Queue
This is how the queue looks like, which enqueue and dequeue call-data bytes (pallet u8, call u8, call params u8).
TransactionQueue: Vec<u8>
I tried to find an easy way to construct extrinsic from call bytes but failed. So I started to implement how subxt constructs extrinsic here
// dequeue transaction call data.
let maybe_call_data = store.dequeue_item(
SledQueueKey::from_substrate_chain_id(chain_id),
)?;
if let Some(call_data) = maybe_call_data {
let call_data = subxt::Encoded(call_data);
// This is the steps to create encoded extrinsic which can be executed by rpc client
let account_nonce = if let Some(nonce) = signer.nonce() {
nonce
} else {
client
.rpc()
.system_account_next_index(signer.account_id())
.map_err(anyhow::Error::from)
.await?
};
// 1.Construct our custom additional/extra params.
let additional_and_extra_params = {
// Obtain spec version and transaction version from the runtime version of the client.
let runtime = client
.rpc()
.runtime_version(None)
.map_err(anyhow::Error::from)
.await?;
subxt::PolkadotExtrinsicParams::<subxt::DefaultConfig>::new(
runtime.spec_version,
runtime.transaction_version,
account_nonce,
*client.genesis(),
Default::default(),
)
};
// 2. Construct signature. This is compatible with the Encode impl
// for SignedPayload (which is this payload of bytes that we'd like)
// to sign. See:
// https://github.com/paritytech/substrate/blob/9a6d706d8db00abb6ba183839ec98ecd9924b1f8/primitives/runtime/src/generic/unchecked_extrinsic.rs#L215)
let signature = {
let mut bytes = Vec::new();
call_data.encode_to(&mut bytes);
additional_and_extra_params.encode_extra_to(&mut bytes);
additional_and_extra_params
.encode_additional_to(&mut bytes);
if bytes.len() > 256 {
signer.sign(&blake2_256(&bytes))
} else {
signer.sign(&bytes)
}
};
// 3. Encode extrinsic, now that we have the parts we need. This is compatible
// with the Encode impl for UncheckedExtrinsic (protocol version 4).
let extrinsic = {
let mut encoded_inner = Vec::new();
// "is signed" + transaction protocol version (4)
(0b10000000 + 4u8).encode_to(&mut encoded_inner);
// from address for signature
signer.address().encode_to(&mut encoded_inner);
// the signature bytes
signature.encode_to(&mut encoded_inner);
// attach custom extra params
additional_and_extra_params
.encode_extra_to(&mut encoded_inner);
// and now, call data
call_data.encode_to(&mut encoded_inner);
// now, prefix byte length:
let len = Compact(
u32::try_from(encoded_inner.len())
.expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
// encoded extinsic
let encoded_extrinsic = subxt::Encoded(extrinsic);
// watch_extrinsic submits and returns transaction subscription
let result = client
.rpc()
.watch_extrinsic(&encoded_extrinsic)
.map_err(anyhow::Error::from)
.await;
let mut progress = match result {
Ok(d) => d,
Err(e) => {
tracing::debug!("extrinsic error : {:?}", e);
continue;
}
};
Error.
I tried to construct extrinsic as per the above code but it fails with code:1002, message: Verification Error: Runtime error
.
full error log
{
"timestamp": "2022-07-27T12:52:10.189639Z",
"level": "DEBUG",
"message": "extrinsic error : Rpc error: JSON-RPC call failed: RPC Call failed: code: 1002, message: Verification Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\nWASM backtrace:\n\n 0: 0x39248 - <unknown>!rust_begin_unwind\n, data: Some(RawValue(\"RuntimeApi(\\\"Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\\\\nWASM backtrace:\\\\n\\\\n 0: 0x39248 - <unknown>!rust_begin_unwind\\\\n\\\")\"))\n\nCaused by:\n 0: JSON-RPC call failed: RPC Call failed: code: 1002, message: Verification Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n WASM backtrace:\n \n 0: 0x39248 - <unknown>!rust_begin_unwind\n , data: Some(RawValue(\"RuntimeApi(\\\"Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\\\\nWASM backtrace:\\\\n\\\\n 0: 0x39248 - <unknown>!rust_begin_unwind\\\\n\\\")\"))\n 1: RPC Call failed: code: 1002, message: Verification Error: Runtime error: Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\n WASM backtrace:\n \n 0: 0x39248 - <unknown>!rust_begin_unwind\n , data: Some(RawValue(\"RuntimeApi(\\\"Execution failed: Execution aborted due to trap: wasm trap: wasm `unreachable` instruction executed\\\\nWASM backtrace:\\\\n\\\\n 0: 0x39248 - <unknown>!rust_begin_unwind\\\\n\\\")\"))",
"target": "webb_relayer::tx_queue::substrate_tx_queue",
"spans": [
{
"node": "1080",
"name": "run"
}
]
}
It would be great if anyone can help me understand what the error msg means and if there is a better way to solve this problem.
Any help or suggestion is most appreciated. Thank you