sp_block_producer_metadata/
lib.rs

1//! # Block Producer Metadata Primitives
2//!
3//! This crate contains primitive types and logic used by the Block Producer Metadata feature
4//! of the Partner Chain Toolkit. This feature allows Partner Chain block producers to save
5//! information about themselves on-chain. The format of this metadata is left generic for each
6//! Partner Chain builder to define. Metadata is associated with the cross-chain public key of
7//! the block producer.
8//!
9//! Currently the only code defined in this crate is the [MetadataSignedMessage] type describing
10//! the message that is signed and submitted by the block producer together with each change in
11//! their on-chain metadata to prove that they are the owner of the public keys associated with
12//! the metadata.
13#![cfg_attr(not(feature = "std"), no_std)]
14#![deny(missing_docs)]
15
16use parity_scale_codec::{Decode, Encode};
17use sidechain_domain::*;
18use sp_api;
19extern crate alloc;
20
21/// Message signed to authorize modification of a block producer's on-chain metadata
22#[derive(Debug, Clone, Encode)]
23pub struct MetadataSignedMessage<Metadata, AccountId> {
24	/// Cross-chain public key
25	pub cross_chain_pub_key: CrossChainPublicKey,
26	/// Block producer's metadata. [None] signifies a deletion of metadata from the chain.
27	pub metadata: Option<Metadata>,
28	/// Genesis UTXO of the Partner Chain that the metadata will be submitted to
29	pub genesis_utxo: UtxoId,
30	/// UNIX epoch time in seconds before which a metadata transaction must be executed on-chain
31	/// to be valid. This value is mapped to a Partner Chain slot which loses precision for
32	/// chains with block times above 1 second.
33	pub valid_before: u64,
34	/// Account ID that will be used to set the metadata and own it on-chain
35	pub owner: AccountId,
36}
37
38impl<M: Encode, AccountId: Encode> MetadataSignedMessage<M, AccountId> {
39	/// Encodes this message using SCALE codec and signs it
40	pub fn sign_with_key(&self, skey: &k256::SecretKey) -> CrossChainSignature {
41		use k256::Secp256k1;
42		use k256::ecdsa::hazmat::DigestPrimitive;
43		use k256::ecdsa::*;
44		use k256::sha2::Digest;
45		let data = self.encode();
46		let digest = <Secp256k1 as DigestPrimitive>::Digest::new_with_prefix(data);
47
48		let (sig, _recid) = SigningKey::from(skey).sign_digest_recoverable(digest.clone()).unwrap();
49		CrossChainSignature(sig.to_vec())
50	}
51
52	/// Verifies a signature of this message against the given public key
53	pub fn verify_signature(
54		&self,
55		vkey: &CrossChainPublicKey,
56		signature: CrossChainSignature,
57	) -> Result<(), k256::ecdsa::signature::Error> {
58		signature.verify(vkey, &self.encode())
59	}
60}
61
62#[cfg(test)]
63mod tests {
64	use super::*;
65	use hex_literal::hex;
66
67	#[test]
68	fn round_trip() {
69		let message = MetadataSignedMessage {
70			cross_chain_pub_key: CrossChainPublicKey(vec![1; 32]),
71			metadata: Some("metadata".to_string()),
72			genesis_utxo: UtxoId::new([2; 32], 0),
73			valid_before: 100_000_000_000,
74			owner: 1000,
75		};
76
77		// Alice cross-chain key
78		let skey = k256::SecretKey::from_slice(&hex!(
79			"cb6df9de1efca7a3998a8ead4e02159d5fa99c3e0d4fd6432667390bb4726854"
80		))
81		.unwrap();
82		let vkey = skey.public_key();
83
84		let signature = message.sign_with_key(&skey);
85
86		assert!(message.verify_signature(&vkey.into(), signature).is_ok());
87	}
88}
89
90sp_api::decl_runtime_apis! {
91	/// Runtime API for accessing metadata of block producers
92	pub trait BlockProducerMetadataApi<Metadata>
93	where Metadata:Decode
94	{
95		/// Retrieves the metadata for a given SPO public key if it exists.
96		fn get_metadata_for(
97			cross_chain_pub_key: &CrossChainPublicKey,
98		) -> Option<Metadata>;
99	}
100}