mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +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:
Generated
+5
@@ -3731,9 +3731,14 @@ version = "1.0.0"
|
||||
dependencies = [
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"node-primitives 1.0.0",
|
||||
"node-runtime 1.0.0",
|
||||
"parity-codec 3.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"schnorrkel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sr-primitives 1.0.0",
|
||||
"substrate-bip39 0.2.1 (git+https://github.com/paritytech/substrate-bip39)",
|
||||
"substrate-primitives 1.0.0",
|
||||
"tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
@@ -215,7 +215,9 @@ pub trait Derive: Sized {
|
||||
/// Derive a child key from a series of given junctions.
|
||||
///
|
||||
/// Will be `None` for public keys if there are any hard junctions in there.
|
||||
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, _path: Iter) -> Option<Self> { None }
|
||||
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, _path: Iter) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -266,11 +268,19 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
|
||||
let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?;
|
||||
let re_junction = Regex::new(r"/(/?[^/]+)")
|
||||
.expect("constructed from known-good static value; qed");
|
||||
let path = re_junction.captures_iter(&cap["path"])
|
||||
.map(|f| DeriveJunction::from(&f[1]));
|
||||
Self::from_ss58check(cap.name("ss58").map(|r| r.as_str()).unwrap_or(DEV_ADDRESS))?
|
||||
.derive(path)
|
||||
.ok_or(PublicError::InvalidPath)
|
||||
let addr = Self::from_ss58check(
|
||||
cap.name("ss58")
|
||||
.map(|r| r.as_str())
|
||||
.unwrap_or(DEV_ADDRESS)
|
||||
)?;
|
||||
if cap["path"].is_empty() {
|
||||
Ok(addr)
|
||||
} else {
|
||||
let path = re_junction.captures_iter(&cap["path"])
|
||||
.map(|f| DeriveJunction::from(&f[1]));
|
||||
addr.derive(path)
|
||||
.ok_or(PublicError::InvalidPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
substrate-primitives = { version = "*", path = "../core/primitives" }
|
||||
node-runtime = { version = "*", path = "../node/runtime" }
|
||||
node-primitives = { version = "*", path = "../node/primitives" }
|
||||
sr-primitives = { version = "*", path = "../core/sr-primitives" }
|
||||
rand = "0.6"
|
||||
clap = { version = "~2.32", features = ["yaml"] }
|
||||
tiny-bip39 = "0.6.0"
|
||||
@@ -13,6 +16,8 @@ rustc-hex = "2.0"
|
||||
substrate-bip39 = { git = "https://github.com/paritytech/substrate-bip39" }
|
||||
schnorrkel = "0.1"
|
||||
hex = "0.3"
|
||||
hex-literal = "0.1"
|
||||
parity-codec = "3.2"
|
||||
|
||||
[features]
|
||||
bench = []
|
||||
|
||||
@@ -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