-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Submit (& sign) extrinsics from the runtime. #3514
Changes from 10 commits
51c3526
f4e7284
6451ed1
9a179d9
dd6b106
ebe3078
7222869
6883345
f74cfc4
2c23ee0
1516067
5ee8936
4f13af4
ddf540f
b14dedb
09611d3
17db4b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,12 +72,22 @@ impl<Address, Call, Signature, Extra: SignedExtension> Extrinsic | |
| { | ||
| type Call = Call; | ||
|
|
||
| type SignaturePayload = ( | ||
| Address, | ||
| Signature, | ||
| Extra, | ||
| ); | ||
|
|
||
| fn is_signed(&self) -> Option<bool> { | ||
| Some(self.signature.is_some()) | ||
| } | ||
|
|
||
| fn new_unsigned(function: Call) -> Option<Self> { | ||
| Some(UncheckedExtrinsic::new_unsigned(function)) | ||
| fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> { | ||
| Some(if let Some((address, signature, extra)) = signed_data { | ||
| UncheckedExtrinsic::new_signed(function, address, signature, extra) | ||
| } else { | ||
| UncheckedExtrinsic::new_unsigned(function) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -98,21 +108,18 @@ where | |
| fn check(self, lookup: &Lookup) -> Result<Self::Checked, &'static str> { | ||
| Ok(match self.signature { | ||
| Some((signed, signature, extra)) => { | ||
| let additional_signed = extra.additional_signed()?; | ||
| let raw_payload = (self.function, extra, additional_signed); | ||
| let signed = lookup.lookup(signed)?; | ||
| let raw_payload = SignedPayload::new(self.function, extra)?; | ||
| if !raw_payload.using_encoded(|payload| { | ||
| if payload.len() > 256 { | ||
| signature.verify(&blake2_256(payload)[..], &signed) | ||
| } else { | ||
| signature.verify(payload, &signed) | ||
| } | ||
| signature.verify(payload, &signed) | ||
| }) { | ||
| return Err(crate::BAD_SIGNATURE) | ||
| } | ||
|
|
||
| let (function, extra, _) = raw_payload.deconstruct(); | ||
| CheckedExtrinsic { | ||
| signed: Some((signed, raw_payload.1)), | ||
| function: raw_payload.0, | ||
| signed: Some((signed, extra)), | ||
| function, | ||
| } | ||
| } | ||
| None => CheckedExtrinsic { | ||
|
|
@@ -123,6 +130,67 @@ where | |
| } | ||
| } | ||
|
|
||
| /// A payload that has been signed for a unchecked extrinsics. | ||
| /// | ||
| /// Note that the payload that we sign to produce unchecked extrinsic signature | ||
| /// is going to be different than the `SignaturePayload` - so the thing the extrinsic | ||
| /// actually contains. | ||
| pub struct SignedPayload<Call, Extra: SignedExtension> { | ||
| raw_payload: ( | ||
|
||
| Call, | ||
| Extra, | ||
| Extra::AdditionalSigned, | ||
| ), | ||
| } | ||
|
|
||
| impl<Call, Extra> SignedPayload<Call, Extra> where | ||
| Call: Encode, | ||
| Extra: SignedExtension, | ||
| { | ||
| /// Create new `SignedPayload`. | ||
| /// | ||
| /// This function may fail if `additional_signed` of `Extra` is not available. | ||
| pub fn new(call: Call, extra: Extra) -> Result<Self, &'static str> { | ||
| let additional_signed = extra.additional_signed()?; | ||
| let raw_payload = (call, extra, additional_signed); | ||
| Ok(Self { raw_payload }) | ||
| } | ||
|
|
||
| /// Create new `SignedPayload` from raw components. | ||
| pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self { | ||
| Self { | ||
| raw_payload: (call, extra, additional_signed), | ||
| } | ||
| } | ||
|
|
||
| /// Get an encoded version of this payload. | ||
| /// | ||
| /// Payloads longer than 256 bytes are going to be `blake2_256`-hashed. | ||
| pub fn using_encoded<O>(&self, f: impl FnOnce(&[u8]) -> O) -> O { | ||
|
||
| self.raw_payload.using_encoded(|payload| { | ||
| if payload.len() > 256 { | ||
| f(&blake2_256(payload)[..]) | ||
| } else { | ||
| f(payload) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| /// Deconstruct the payload into it's components. | ||
| pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) { | ||
| self.raw_payload | ||
| } | ||
| } | ||
|
|
||
| impl<Call, Extra> Encode for SignedPayload<Call, Extra> where | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this implementation required?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The generic
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can also put documentation on top of a trait implementation. It will be shown in rustdocs as welll. |
||
| Call: Encode, | ||
| Extra: SignedExtension, | ||
| { | ||
| fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R { | ||
| self.using_encoded(f) | ||
| } | ||
| } | ||
|
|
||
| impl<Address, Call, Signature, Extra> Decode | ||
| for UncheckedExtrinsic<Address, Call, Signature, Extra> | ||
| where | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,7 @@ use sr_primitives::{ApplyResult, impl_opaque_keys, generic, create_runtime_str, | |
| use sr_primitives::transaction_validity::TransactionValidity; | ||
| use sr_primitives::weights::Weight; | ||
| use sr_primitives::traits::{ | ||
| BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, | ||
| self, BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, SaturatedConversion, | ||
| }; | ||
| use version::RuntimeVersion; | ||
| use elections::VoteIndex; | ||
|
|
@@ -48,6 +48,7 @@ use version::NativeVersion; | |
| use primitives::OpaqueMetadata; | ||
| use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; | ||
| use im_online::sr25519::{AuthorityId as ImOnlineId}; | ||
| use system::offchain::TransactionSubmitter; | ||
|
|
||
| #[cfg(any(feature = "std", test))] | ||
| pub use sr_primitives::BuildStorage; | ||
|
|
@@ -80,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { | |
| // implementation changes and behavior does not, then leave spec_version as | ||
| // is and increment impl_version. | ||
| spec_version: 154, | ||
| impl_version: 157, | ||
| impl_version: 158, | ||
| apis: RUNTIME_API_VERSIONS, | ||
| }; | ||
|
|
||
|
|
@@ -396,7 +397,7 @@ impl im_online::Trait for Runtime { | |
| type AuthorityId = ImOnlineId; | ||
| type Call = Call; | ||
| type Event = Event; | ||
| type UncheckedExtrinsic = UncheckedExtrinsic; | ||
| type SubmitTransaction = TransactionSubmitter<ImOnlineId, Runtime, UncheckedExtrinsic>; | ||
| type ReportUnresponsiveness = Offences; | ||
| type CurrentElectedSet = staking::CurrentElectedStashAccounts<Runtime>; | ||
| } | ||
|
|
@@ -424,6 +425,33 @@ impl finality_tracker::Trait for Runtime { | |
| type ReportLatency = ReportLatency; | ||
| } | ||
|
|
||
| impl system::offchain::CreateTransaction<Runtime, UncheckedExtrinsic> for Runtime { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is this being used?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We pass this as (see |
||
| type Signature = Signature; | ||
|
|
||
| fn create_transaction<F: system::offchain::Signer<AccountId, Self::Signature>>( | ||
| call: Call, | ||
| account: AccountId, | ||
| index: Index, | ||
| ) -> Option<(Call, <UncheckedExtrinsic as traits::Extrinsic>::SignaturePayload)> { | ||
| let period = 1 << 8; | ||
| let current_block = System::block_number().saturated_into::<u64>(); | ||
| let tip = 0; | ||
| let extra: SignedExtra = ( | ||
| system::CheckVersion::<Runtime>::new(), | ||
| system::CheckGenesis::<Runtime>::new(), | ||
| system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)), | ||
| system::CheckNonce::<Runtime>::from(index), | ||
| system::CheckWeight::<Runtime>::new(), | ||
| balances::TakeFees::<Runtime>::from(tip), | ||
| ); | ||
| let raw_payload = SignedPayload::new(call, extra).ok()?; | ||
| let signature = F::sign(account.clone(), &raw_payload)?; | ||
| let address = Indices::unlookup(account); | ||
| let (call, extra, _) = raw_payload.deconstruct(); | ||
| Some((call, (address, signature, extra))) | ||
| } | ||
| } | ||
|
|
||
| construct_runtime!( | ||
| pub enum Runtime where | ||
| Block = Block, | ||
|
|
@@ -475,6 +503,8 @@ pub type SignedExtra = ( | |
| ); | ||
| /// Unchecked extrinsic type as expected by this runtime. | ||
| pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>; | ||
| /// The payload being signed in transactions. | ||
| pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>; | ||
| /// Extrinsic type that has already been checked. | ||
| pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>; | ||
| /// Executive: handles dispatch to the various modules. | ||
|
|
@@ -609,3 +639,17 @@ impl_runtime_apis! { | |
| } | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use system::offchain::SubmitSignedTransaction; | ||
|
|
||
| fn is_submit_signed_transaction(_arg: impl SubmitSignedTransaction<Runtime, Call>) {} | ||
|
|
||
| #[test] | ||
| fn validate_bounds() { | ||
| let x = SubmitTransaction::default(); | ||
| is_submit_signed_transaction(x); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.