Various simplifications re: Serai transactions

Removes PairSigner for the pair directly.

Resets spec_version to 1.

Defines the extrinsic without its length prefix, only prefixing during publish.
This commit is contained in:
Luke Parker 2023-11-22 23:44:24 -05:00
parent c6cf33e370
commit 372149c2cc
No known key found for this signature in database
9 changed files with 36 additions and 67 deletions

View file

@ -40,6 +40,8 @@ impl<'a> SeraiInInstructions<'a> {
}
pub fn execute_batch(batch: SignedBatch) -> Vec<u8> {
Serai::unsigned::<InInstructions, _>(&in_instructions::Call::<Runtime>::execute_batch { batch })
Serai::unsigned(&serai_runtime::RuntimeCall::InInstructions(
in_instructions::Call::<Runtime>::execute_batch { batch },
))
}
}

View file

@ -13,7 +13,6 @@ pub use sp_core::{
sr25519::{Public, Pair},
};
pub use subxt;
use subxt::{
error::Error as SubxtError,
config::{
@ -21,7 +20,6 @@ use subxt::{
substrate::{BlakeTwo256, SubstrateHeader},
extrinsic_params::BaseExtrinsicParams,
},
tx::Signer,
rpc::types::{ChainBlock, ChainBlockExtrinsic},
Config as SubxtConfig, OnlineClient,
};
@ -150,41 +148,32 @@ impl Serai {
Ok(Serai(OnlineClient::<SeraiConfig>::from_url(url).await.map_err(SeraiError::RpcError)?))
}
fn unsigned<P: 'static, C: Encode>(call: &C) -> Vec<u8> {
fn unsigned(call: &serai_runtime::RuntimeCall) -> Vec<u8> {
// TODO: Should Serai purge the old transaction code AND set this to 0/1?
const TRANSACTION_VERSION: u8 = 4;
const EXTRINSIC_FORMAT_VERSION: u8 = 4;
// Protocol version
let mut bytes = vec![TRANSACTION_VERSION];
// Pallet index
bytes.push(u8::try_from(PalletInfo::index::<P>().unwrap()).unwrap());
// Call
let mut bytes = vec![EXTRINSIC_FORMAT_VERSION];
bytes.extend(call.encode());
// Prefix the length
let mut complete_bytes = Compact(u32::try_from(bytes.len()).unwrap()).encode();
complete_bytes.extend(bytes);
complete_bytes
bytes
}
pub fn sign<S: Send + Sync + Signer<SeraiConfig>>(
pub fn sign(
&self,
signer: &S,
signer: &Pair,
call: &serai_runtime::RuntimeCall,
nonce: u32,
tip: Tip,
) -> Vec<u8> {
const SPEC_VERSION: u32 = 100;
const IMPL_VERSION: u32 = 1;
const TRANSACTION_VERSION: u8 = 4;
const SPEC_VERSION: u32 = 1;
const TX_VERSION: u32 = 1;
const EXTRINSIC_FORMAT_VERSION: u8 = 4;
let era = subxt::config::substrate::Era::Immortal;
let extra = (era, Compact(nonce), tip);
let genesis = self.0.genesis_hash();
let mortality_checkpoint = genesis;
let mut signature_payload =
(call, extra, SPEC_VERSION, IMPL_VERSION, genesis, mortality_checkpoint).encode();
(call, extra, SPEC_VERSION, TX_VERSION, genesis, mortality_checkpoint).encode();
if signature_payload.len() > 256 {
use subxt::config::Hasher;
signature_payload = BlakeTwo256::hash(&signature_payload).0.to_vec();
@ -192,19 +181,20 @@ impl Serai {
let signature = signer.sign(&signature_payload);
let signed = 1 << 7;
let extrinsic =
(signed + TRANSACTION_VERSION, signer.address(), signature, extra, call).encode();
let mut res = Compact(u32::try_from(extrinsic.len()).unwrap()).encode();
res.extend(&extrinsic);
res
(signed + EXTRINSIC_FORMAT_VERSION, signer.public(), signature, extra, call).encode()
}
pub async fn publish(&self, tx: &[u8]) -> Result<(), SeraiError> {
let mut length_prefixed = Compact(u32::try_from(tx.len()).unwrap()).encode();
length_prefixed.extend(tx);
self
.0
.rpc()
.deref()
.request::<String>("author_submitExtrinsic", subxt::rpc::rpc_params![hex::encode(tx)])
.request::<String>(
"author_submitExtrinsic",
subxt::rpc::rpc_params![hex::encode(length_prefixed)],
)
.await
// Drop the hash, which is the hash of the raw extrinsic, as extrinsics are allowed to share
// hashes and this hash is accordingly useless/unsafe
@ -386,23 +376,3 @@ impl<'a> TemporalSerai<'a> {
SeraiValidatorSets(self)
}
}
#[derive(Clone)]
pub struct PairSigner(Pair, <SeraiConfig as SubxtConfig>::AccountId);
impl PairSigner {
pub fn new(pair: Pair) -> Self {
let id = pair.public();
PairSigner(pair, id)
}
}
impl Signer<SeraiConfig> for PairSigner {
fn account_id(&self) -> &<SeraiConfig as SubxtConfig>::AccountId {
&self.1
}
fn address(&self) -> <SeraiConfig as SubxtConfig>::Address {
self.1.into()
}
fn sign(&self, payload: &[u8]) -> <SeraiConfig as SubxtConfig>::Signature {
self.0.sign(payload)
}
}

View file

@ -81,10 +81,8 @@ impl<'a> SeraiValidatorSets<'a> {
}
pub fn set_keys(network: NetworkId, key_pair: KeyPair, signature: Signature) -> Vec<u8> {
Serai::unsigned::<ValidatorSets, _>(&validator_sets::Call::<Runtime>::set_keys {
network,
key_pair,
signature,
})
Serai::unsigned(&serai_runtime::RuntimeCall::ValidatorSets(
validator_sets::Call::<Runtime>::set_keys { network, key_pair, signature },
))
}
}

View file

@ -20,7 +20,7 @@ use serai_client::{
primitives::{InInstruction, InInstructionWithBalance, Batch},
},
coins::{primitives::OutInstruction, CoinsEvent},
PairSigner, Serai, SeraiCoins,
Serai, SeraiCoins,
};
mod common;
@ -91,7 +91,7 @@ serai_test!(
serai,
&serai
.sign(
&PairSigner::new(pair),
&pair,
&SeraiCoins::burn_with_instruction(instruction.clone()),
0,
Default::default(),

View file

@ -1,6 +1,6 @@
use serai_runtime::primitives::{Coin, Amount};
use serai_client::{Serai, SeraiDex, PairSigner};
use serai_client::{Serai, SeraiDex};
use sp_core::{sr25519::Pair, Pair as PairTrait};
use crate::common::tx::publish_tx;
@ -17,7 +17,7 @@ pub async fn add_liquidity(
let address = pair.public();
let tx = serai.sign(
&PairSigner::new(pair),
&pair,
&SeraiDex::add_liquidity(coin, coin_amount, sri_amount, Amount(1), Amount(1), address.into()),
nonce,
Default::default(),
@ -39,7 +39,7 @@ pub async fn swap(
let address = pair.public();
let tx = serai.sign(
&PairSigner::new(pair),
&pair,
&SeraiDex::swap(from_coin, to_coin, amount_in, amount_out_min, address.into()),
nonce,
Default::default(),

View file

@ -34,7 +34,7 @@ pub async fn publish_tx(serai: &Serai, tx: &[u8]) -> [u8; 32] {
};
for transaction in block.transactions() {
if transaction.0 == tx[2 ..] {
if transaction.0 == tx {
return block.hash();
}
}

View file

@ -88,8 +88,7 @@ pub mod opaque {
pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("serai"),
impl_name: create_runtime_str!("core"),
// TODO: 1? Do we prefer some level of compatibility or our own path?
spec_version: 100,
spec_version: 1,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,

View file

@ -12,7 +12,7 @@ use ciphersuite::{group::GroupEncoding, Ciphersuite, Secp256k1};
use dkg::Participant;
use serai_client::{
PairTrait, PairSigner,
PairTrait,
primitives::{
NetworkId, Coin, Amount, Balance, BlockHash, SeraiAddress, ExternalAddress,
insecure_pair_from_name,
@ -211,7 +211,7 @@ async fn sign_test() {
let balance = Balance { coin: Coin::Serai, amount: Amount(1_000_000_000) };
serai
.publish(&serai.sign(
&PairSigner::new(insecure_pair_from_name("Ferdie")),
&insecure_pair_from_name("Ferdie"),
&SeraiCoins::transfer(address, balance),
0,
Default::default(),
@ -219,7 +219,7 @@ async fn sign_test() {
.await
.unwrap();
(PairSigner::new(pair), address)
(pair, address)
};
#[allow(clippy::inconsistent_digit_grouping)]

View file

@ -16,7 +16,7 @@ use serai_client::{
validator_sets::primitives::{Session, ValidatorSet},
in_instructions::primitives::Shorthand,
coins::primitives::{OutInstruction, OutInstructionWithBalance},
PairTrait, PairSigner, SeraiCoins,
PairTrait, SeraiCoins,
};
use crate::tests::*;
@ -249,7 +249,7 @@ async fn mint_and_burn_test() {
let balance = Balance { coin: Coin::Serai, amount: Amount(1_000_000_000) };
serai
.publish(&serai.sign(
&PairSigner::new(insecure_pair_from_name("Ferdie")),
&insecure_pair_from_name("Ferdie"),
&SeraiCoins::transfer(address, balance),
0,
Default::default(),
@ -257,7 +257,7 @@ async fn mint_and_burn_test() {
.await
.unwrap();
(PairSigner::new(pair), address)
(pair, address)
};
// Send in BTC