mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-25 11:45:47 +00:00
Add hash method to ExtrinsicDetails (#1676)
* Add hash method to ExtrinsicDetails * fix test
This commit is contained in:
@@ -3,15 +3,14 @@
|
|||||||
// see LICENSE for license details.
|
// see LICENSE for license details.
|
||||||
|
|
||||||
use crate::blocks::extrinsic_signed_extensions::ExtrinsicSignedExtensions;
|
use crate::blocks::extrinsic_signed_extensions::ExtrinsicSignedExtensions;
|
||||||
use crate::utils::strip_compact_prefix;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::{Config, Hasher},
|
||||||
error::{BlockError, Error, MetadataError},
|
error::{BlockError, Error, MetadataError},
|
||||||
Metadata,
|
Metadata,
|
||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use codec::Decode;
|
use codec::{Compact, CompactLen, Decode};
|
||||||
use scale_decode::DecodeAsType;
|
use scale_decode::DecodeAsType;
|
||||||
use subxt_metadata::PalletMetadata;
|
use subxt_metadata::PalletMetadata;
|
||||||
|
|
||||||
@@ -169,24 +168,28 @@ where
|
|||||||
const VERSION_MASK: u8 = 0b0111_1111;
|
const VERSION_MASK: u8 = 0b0111_1111;
|
||||||
const LATEST_EXTRINSIC_VERSION: u8 = 4;
|
const LATEST_EXTRINSIC_VERSION: u8 = 4;
|
||||||
|
|
||||||
// removing the compact encoded prefix:
|
// Wrap all of the bytes in Arc for easy sharing.
|
||||||
let bytes: Arc<[u8]> = strip_compact_prefix(extrinsic_bytes)?.1.into();
|
let bytes: Arc<[u8]> = Arc::from(extrinsic_bytes);
|
||||||
|
|
||||||
|
// The compact encoded length prefix.
|
||||||
|
let prefix = <Compact<u64>>::decode(&mut &*extrinsic_bytes)?;
|
||||||
|
let prefix_len = <Compact<u64>>::compact_len(&prefix.0);
|
||||||
|
|
||||||
// Extrinsic are encoded in memory in the following way:
|
// Extrinsic are encoded in memory in the following way:
|
||||||
// - first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
|
// - first byte: abbbbbbb (a = 0 for unsigned, 1 for signed, b = version)
|
||||||
// - signature: [unknown TBD with metadata].
|
// - signature: [unknown TBD with metadata].
|
||||||
// - extrinsic data
|
// - extrinsic data
|
||||||
let first_byte: u8 = Decode::decode(&mut &bytes[..])?;
|
let version_byte: u8 = Decode::decode(&mut &bytes[prefix_len..])?;
|
||||||
|
|
||||||
let version = first_byte & VERSION_MASK;
|
let version = version_byte & VERSION_MASK;
|
||||||
if version != LATEST_EXTRINSIC_VERSION {
|
if version != LATEST_EXTRINSIC_VERSION {
|
||||||
return Err(BlockError::UnsupportedVersion(version).into());
|
return Err(BlockError::UnsupportedVersion(version).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_signed = first_byte & SIGNATURE_MASK != 0;
|
let is_signed = version_byte & SIGNATURE_MASK != 0;
|
||||||
|
|
||||||
// Skip over the first byte which denotes the version and signing.
|
// Skip over the prefix and first byte which denotes the version and signing.
|
||||||
let cursor = &mut &bytes[1..];
|
let cursor = &mut &bytes[prefix_len + 1..];
|
||||||
|
|
||||||
let signed_details = is_signed
|
let signed_details = is_signed
|
||||||
.then(|| -> Result<SignedExtrinsicDetails, Error> {
|
.then(|| -> Result<SignedExtrinsicDetails, Error> {
|
||||||
@@ -248,6 +251,12 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate and return the hash of the extrinsic, based on the configured hasher.
|
||||||
|
pub fn hash(&self) -> T::Hash {
|
||||||
|
// Use hash(), not hash_of(), because we don't want to double encode the bytes.
|
||||||
|
T::Hasher::hash(&self.bytes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Is the extrinsic signed?
|
/// Is the extrinsic signed?
|
||||||
pub fn is_signed(&self) -> bool {
|
pub fn is_signed(&self) -> bool {
|
||||||
self.signed_details.is_some()
|
self.signed_details.is_some()
|
||||||
@@ -630,6 +639,40 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tx_hashes_line_up() {
|
||||||
|
let metadata = metadata();
|
||||||
|
let ids = ExtrinsicPartTypeIds::new(&metadata).unwrap();
|
||||||
|
|
||||||
|
let tx = crate::dynamic::tx(
|
||||||
|
"Test",
|
||||||
|
"TestCall",
|
||||||
|
vec![
|
||||||
|
Value::u128(10),
|
||||||
|
Value::bool(true),
|
||||||
|
Value::string("SomeValue"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Encoded TX ready to submit.
|
||||||
|
let tx_encoded = crate::tx::create_unsigned::<SubstrateConfig, _>(&tx, &metadata)
|
||||||
|
.expect("Valid dynamic parameters are provided");
|
||||||
|
|
||||||
|
// Extrinsic details ready to decode.
|
||||||
|
let extrinsic = ExtrinsicDetails::<SubstrateConfig>::decode_from(
|
||||||
|
1,
|
||||||
|
tx_encoded.encoded(),
|
||||||
|
metadata,
|
||||||
|
ids,
|
||||||
|
)
|
||||||
|
.expect("Valid extrinsic");
|
||||||
|
|
||||||
|
// Both of these types should produce the same bytes.
|
||||||
|
assert_eq!(tx_encoded.encoded(), extrinsic.bytes(), "bytes should eq");
|
||||||
|
// Both of these types should produce the same hash.
|
||||||
|
assert_eq!(tx_encoded.hash(), extrinsic.hash(), "hashes should eq");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn statically_decode_extrinsic() {
|
fn statically_decode_extrinsic() {
|
||||||
let metadata = metadata();
|
let metadata = metadata();
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See [`subxt_core::blocks::ExtrinsicDetails::hash()`].
|
||||||
|
pub fn hash(&self) -> T::Hash {
|
||||||
|
self.inner.hash()
|
||||||
|
}
|
||||||
|
|
||||||
/// See [`subxt_core::blocks::ExtrinsicDetails::is_signed()`].
|
/// See [`subxt_core::blocks::ExtrinsicDetails::is_signed()`].
|
||||||
pub fn is_signed(&self) -> bool {
|
pub fn is_signed(&self) -> bool {
|
||||||
self.inner.is_signed()
|
self.inner.is_signed()
|
||||||
|
|||||||
@@ -326,18 +326,18 @@ async fn transaction_v1_broadcast() {
|
|||||||
let api = ctx.client();
|
let api = ctx.client();
|
||||||
let rpc = ctx.unstable_rpc_methods().await;
|
let rpc = ctx.unstable_rpc_methods().await;
|
||||||
|
|
||||||
let tx = node_runtime::tx()
|
let tx_payload = node_runtime::tx()
|
||||||
.balances()
|
.balances()
|
||||||
.transfer_allow_death(bob_address.clone(), 10_001);
|
.transfer_allow_death(bob_address.clone(), 10_001);
|
||||||
|
|
||||||
let tx_bytes = ctx
|
let tx = ctx
|
||||||
.client()
|
.client()
|
||||||
.tx()
|
.tx()
|
||||||
.create_signed_offline(&tx, &dev::alice(), Default::default())
|
.create_signed_offline(&tx_payload, &dev::alice(), Default::default())
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.into_encoded();
|
|
||||||
|
|
||||||
let tx_hash = <SubstrateConfig as subxt::Config>::Hasher::hash(&tx_bytes[2..]);
|
let tx_hash = tx.hash();
|
||||||
|
let tx_bytes = tx.into_encoded();
|
||||||
|
|
||||||
// Subscribe to finalized blocks.
|
// Subscribe to finalized blocks.
|
||||||
let mut finalized_sub = api.blocks().subscribe_finalized().await.unwrap();
|
let mut finalized_sub = api.blocks().subscribe_finalized().await.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user