mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 19:21:13 +00:00
Subkey can construct and sign transfer txs (#2109)
* First effort * Fix for encoding * !fixed subkey xfer creation (still brittle because of double-hardcoded genesis_hash (#2221) * CLI genesis hash * Add test * Slightly nicer text * Fix Elm hash * Update lock file
This commit is contained in:
@@ -40,6 +40,30 @@ subcommands:
|
||||
long: hex
|
||||
help: The message on STDIN is hex-encoded data
|
||||
takes_value: false
|
||||
- transfer:
|
||||
about: Author and sign a Node balances::Transfer transaction with a given (secret) key
|
||||
args:
|
||||
- from:
|
||||
index: 1
|
||||
required: true
|
||||
help: The signing secret key URI.
|
||||
- to:
|
||||
index: 2
|
||||
required: true
|
||||
help: The destination account public key URI.
|
||||
- amount:
|
||||
index: 3
|
||||
required: true
|
||||
help: The number of units to transfer.
|
||||
- index:
|
||||
index: 4
|
||||
required: true
|
||||
help: The signing account's transaction index.
|
||||
- genesis:
|
||||
short: g
|
||||
long: genesis
|
||||
help: The genesis hash or a recognised chain identifier (dev, elm, alex).
|
||||
takes_value: true
|
||||
- verify:
|
||||
about: Verify a signature for a message, provided on STDIN, with a given (public or secret) key
|
||||
args:
|
||||
|
||||
@@ -20,14 +20,19 @@ extern crate test;
|
||||
|
||||
extern crate substrate_bip39;
|
||||
extern crate rustc_hex;
|
||||
#[macro_use] extern crate hex_literal;
|
||||
|
||||
use std::io::{stdin, Read};
|
||||
use clap::load_yaml;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use substrate_bip39::mini_secret_from_entropy;
|
||||
use bip39::{Mnemonic, Language, MnemonicType};
|
||||
use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, crypto::Ss58Codec};
|
||||
use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, crypto::Ss58Codec, blake2_256};
|
||||
use parity_codec::{Encode, Decode, Compact};
|
||||
use sr_primitives::generic::Era;
|
||||
use schnorrkel::keys::MiniSecretKey;
|
||||
use node_primitives::{Balance, Index, Hash};
|
||||
use node_runtime::{Call, UncheckedExtrinsic, BalancesCall};
|
||||
|
||||
mod vanity;
|
||||
|
||||
@@ -173,6 +178,54 @@ fn execute<C: Crypto<Seed=[u8; 32]>>(matches: clap::ArgMatches) where
|
||||
let sig = pair.sign(&message);
|
||||
println!("{}", hex::encode(&sig));
|
||||
}
|
||||
("transfer", Some(matches)) => {
|
||||
let signer = matches.value_of("from")
|
||||
.expect("parameter is required; thus it can't be None; qed");
|
||||
let signer = Sr25519::pair_from_suri(signer, password);
|
||||
|
||||
let to = matches.value_of("to")
|
||||
.expect("parameter is required; thus it can't be None; qed");
|
||||
let to = sr25519::Public::from_string(to).ok().or_else(||
|
||||
sr25519::Pair::from_string(to, password).ok().map(|p| p.public())
|
||||
).expect("Invalid 'to' URI; expecting either a secret URI or a public URI.");
|
||||
|
||||
let amount = matches.value_of("amount")
|
||||
.expect("parameter is required; thus it can't be None; qed");
|
||||
let amount = str::parse::<Balance>(amount)
|
||||
.expect("Invalid 'amount' parameter; expecting an integer.");
|
||||
|
||||
let index = matches.value_of("index")
|
||||
.expect("parameter is required; thus it can't be None; qed");
|
||||
let index = str::parse::<Index>(index)
|
||||
.expect("Invalid 'amount' parameter; expecting an integer.");
|
||||
|
||||
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
|
||||
|
||||
let genesis_hash: Hash = match matches.value_of("genesis").unwrap_or("alex") {
|
||||
"elm" => hex!["10c08714a10c7da78f40a60f6f732cf0dba97acfb5e2035445b032386157d5c3"].into(),
|
||||
"alex" => hex!["dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025b"].into(),
|
||||
h => hex::decode(h).ok().and_then(|x| Decode::decode(&mut &x[..])).expect("Invalid genesis hash or unrecognised chain identifier"),
|
||||
};
|
||||
|
||||
println!("Using a genesis hash of {}", HexDisplay::from(&genesis_hash.as_ref()));
|
||||
|
||||
let era = Era::immortal();
|
||||
let raw_payload = (Compact(index), function, era, genesis_hash);
|
||||
let signature = raw_payload.using_encoded(|payload| if payload.len() > 256 {
|
||||
signer.sign(&blake2_256(payload)[..])
|
||||
} else {
|
||||
println!("Signing {}", HexDisplay::from(&payload));
|
||||
signer.sign(payload)
|
||||
});
|
||||
let extrinsic = UncheckedExtrinsic::new_signed(
|
||||
index,
|
||||
raw_payload.1,
|
||||
signer.public().into(),
|
||||
signature.into(),
|
||||
era,
|
||||
);
|
||||
println!("0x{}", hex::encode(&extrinsic.encode()));
|
||||
}
|
||||
("verify", Some(matches)) => {
|
||||
let sig_data = matches.value_of("sig")
|
||||
.expect("signature parameter is required; thus it can't be None; qed");
|
||||
@@ -218,3 +271,22 @@ fn main() {
|
||||
fn print_usage(matches: &clap::ArgMatches) {
|
||||
println!("{}", matches.usage());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Hash, Decode};
|
||||
#[test]
|
||||
fn should_work() {
|
||||
let s = "0123456789012345678901234567890123456789012345678901234567890123";
|
||||
|
||||
let d1: Hash = hex::decode(s).ok().and_then(|x| Decode::decode(&mut &x[..])).unwrap();
|
||||
|
||||
let d2: Hash = {
|
||||
let mut gh: [u8; 32] = Default::default();
|
||||
gh.copy_from_slice(hex::decode(s).unwrap().as_ref());
|
||||
Hash::from(gh)
|
||||
};
|
||||
|
||||
assert_eq!(d1, d2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user