Make ExtrinsicParams more flexible, and introduce signed extensions (#1107)

* WIP new SignedExtensions

* Integrate new signex extension stuff, update docs, remove Static wrapper

* remove impl Into in tx_client to avoid type inference annoyances

* clippy and fix example

* Fix book links

* clippy

* book tweaks

* fmt: remove spaces

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>

* re-expose Era in utils, and tweak wasm-example

* clippy; remove useless conversion

---------

Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com>
This commit is contained in:
James Wilson
2023-08-08 09:37:06 +01:00
committed by GitHub
parent 9cfac4eec7
commit 9b86b55b56
20 changed files with 1106 additions and 624 deletions
+16 -25
View File
@@ -4,10 +4,8 @@ use js_sys::Promise;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::fmt::Write;
use subxt::ext::codec::Encode;
use subxt::tx::PartialExtrinsic;
use subxt::ext::codec::{Compact, Encode};
use subxt::{self, OnlineClient, PolkadotConfig};
use subxt::utils::AccountId32;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use yew::{AttrValue, Callback};
@@ -117,37 +115,30 @@ fn to_hex(bytes: impl AsRef<[u8]>) -> String {
format!("0x{}", hex::encode(bytes.as_ref()))
}
fn encode_to_hex<E: Encode>(input: &E) -> String {
fn encode_then_hex<E: Encode>(input: &E) -> String {
format!("0x{}", hex::encode(input.encode()))
}
/// this is used because numeric types (e.g. u32) are encoded as little-endian via scale (e.g. 9430 -> d6240000)
/// while we need a big-endian representation for the json (e.g. 9430 -> 000024d6).
fn encode_to_hex_reverse<E: Encode>(input: &E) -> String {
let mut bytes = input.encode();
bytes.reverse();
format!("0x{}", hex::encode(bytes))
}
/// communicates with JavaScript to obtain a signature for the `partial_extrinsic` via a browser extension (e.g. polkadot-js or Talisman)
///
/// Some parameters are hard-coded here and not taken from the partial_extrinsic itself (mortality_checkpoint, era, tip).
pub async fn extension_signature_for_partial_extrinsic(
partial_extrinsic: &PartialExtrinsic<PolkadotConfig, OnlineClient<PolkadotConfig>>,
pub async fn extension_signature_for_extrinsic(
call_data: &[u8],
api: &OnlineClient<PolkadotConfig>,
account_id: &AccountId32,
account_nonce: u64,
account_source: String,
account_address: String,
) -> Result<Vec<u8>, anyhow::Error> {
let spec_version = encode_to_hex_reverse(&api.runtime_version().spec_version);
let transaction_version = encode_to_hex_reverse(&api.runtime_version().transaction_version);
let mortality_checkpoint = encode_to_hex(&api.genesis_hash());
let era = encode_to_hex(&subxt::config::extrinsic_params::Era::Immortal);
let genesis_hash = encode_to_hex(&api.genesis_hash());
let method = to_hex(partial_extrinsic.call_data());
let nonce = api.tx().account_nonce(account_id).await?;
let nonce = encode_to_hex_reverse(&nonce);
let genesis_hash = encode_then_hex(&api.genesis_hash());
// These numbers aren't SCALE encoded; their bytes are just converted to hex:
let spec_version = to_hex(&api.runtime_version().spec_version.to_be_bytes());
let transaction_version = to_hex(&api.runtime_version().transaction_version.to_be_bytes());
let nonce = to_hex(&account_nonce.to_be_bytes());
// If you construct a mortal transaction, then this block hash needs to correspond
// to the block number passed to `Era::mortal()`.
let mortality_checkpoint = encode_then_hex(&api.genesis_hash());
let era = encode_then_hex(&subxt::utils::Era::Immortal);
let method = to_hex(call_data);
let signed_extensions: Vec<String> = api
.metadata()
.extrinsic()
@@ -155,7 +146,7 @@ pub async fn extension_signature_for_partial_extrinsic(
.iter()
.map(|e| e.identifier().to_string())
.collect();
let tip = encode_to_hex(&subxt::config::polkadot::PlainTip::new(0));
let tip = encode_then_hex(&Compact(0u128));
let payload = json!({
"specVersion": spec_version,