Redo primitives, abi

Consolidates all primitives into a single crate. We didn't benefit from its
fragmentation. I'm hesitant to say the new internal-organization is better (it
may be just as clunky), but it's at least in a single crate (not spread out
over micro-crates).

The ABI is the most distinct. We now entirely own it. Block header hashes don't
directly commit to any BABE data (avoiding potentially ~4 KB headers upon
session changes), and are hashed as borsh (a more widely used codec than
SCALE). There are still Substrate variants, using SCALE and with the BABE data,
but they're prunable from a protocol design perspective.

Defines a transaction as a Vec of Calls, allowing atomic operations.
This commit is contained in:
Luke Parker
2025-02-12 03:41:50 -05:00
parent dd95494d9c
commit 2d8f70036a
52 changed files with 2340 additions and 2217 deletions

250
substrate/abi/src/block.rs Normal file
View File

@@ -0,0 +1,250 @@
use alloc::vec::Vec;
use borsh::{BorshSerialize, BorshDeserialize};
use crate::{primitives::BlockHash, Transaction};
/// A V1 header for a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct HeaderV1 {
/// The index of this block on the blockchain.
///
/// The genesis block has number 0.
pub number: u64,
/// The block this header builds upon.
pub parent_hash: BlockHash,
/// The root of a Merkle tree commiting to the transactions within this block.
// TODO: Review the format of this defined by Substrate
pub transactions_root: [u8; 32],
/// A commitment to the consensus data used to justify adding this block to the blockchain.
pub consensus_commitment: [u8; 32],
}
/// A header for a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Header {
/// A version 1 header.
V1(HeaderV1),
}
impl Header {
/// Get the hash of the header.
pub fn number(&self) -> u64 {
match self {
Header::V1(HeaderV1 { number, .. }) => *number,
}
}
/// Get the hash of the header.
pub fn parent_hash(&self) -> BlockHash {
match self {
Header::V1(HeaderV1 { parent_hash, .. }) => *parent_hash,
}
}
/// Get the hash of the header.
pub fn transactions_root(&self) -> [u8; 32] {
match self {
Header::V1(HeaderV1 { transactions_root, .. }) => *transactions_root,
}
}
/// Get the hash of the header.
pub fn hash(&self) -> BlockHash {
BlockHash(sp_core::blake2_256(&borsh::to_vec(self).unwrap()))
}
}
/// A block.
///
/// This does not guarantee consistency. The header's `transactions_root` may not match the
/// contained transactions.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Block {
/// The block's header.
pub header: Header,
/// The block's transactions.
pub transactions: Vec<Transaction>,
}
#[cfg(feature = "substrate")]
mod substrate {
use scale::{Encode, Decode};
use scale_info::TypeInfo;
use sp_core::H256;
use sp_runtime::{
generic::Digest,
traits::{Header as HeaderTrait, HeaderProvider, Block as BlockTrait},
};
use super::*;
/// The consensus data for a V1 header.
///
/// This is not considered part of the protocol proper and may be pruned in the future. It's
/// solely considered used for consensus now.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
pub struct ConsensusV1 {
/// The state root.
state_root: H256,
/// The consensus digests.
digest: Digest,
}
/// A V1 header for a block, as needed by Substrate.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
pub struct SubstrateHeaderV1 {
number: u64,
parent_hash: H256,
transactions_root: H256,
consensus: ConsensusV1,
}
/// A header for a block, as needed by Substrate.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
pub enum SubstrateHeader {
/// A version 1 header.
V1(SubstrateHeaderV1),
}
impl From<&SubstrateHeader> for Header {
fn from(header: &SubstrateHeader) -> Header {
match header {
SubstrateHeader::V1(header) => Header::V1(HeaderV1 {
number: header.number,
parent_hash: BlockHash(header.parent_hash.0),
transactions_root: header.transactions_root.0,
consensus_commitment: sp_core::blake2_256(&header.consensus.encode()),
}),
}
}
}
/// A block, as needed by Substrate.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, sp_runtime::Serialize)]
pub struct SubstrateBlock {
header: SubstrateHeader,
#[serde(skip)] // This makes this unsafe to deserialize, but we don't impl `Deserialize`
transactions: Vec<Transaction>,
}
impl HeaderTrait for SubstrateHeader {
type Number = u64;
type Hash = H256;
type Hashing = sp_runtime::traits::BlakeTwo256;
fn new(
number: Self::Number,
extrinsics_root: Self::Hash,
state_root: Self::Hash,
parent_hash: Self::Hash,
digest: Digest,
) -> Self {
SubstrateHeader::V1(SubstrateHeaderV1 {
number,
parent_hash,
transactions_root: extrinsics_root,
consensus: ConsensusV1 { state_root, digest },
})
}
fn number(&self) -> &Self::Number {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { number, .. }) => number,
}
}
fn set_number(&mut self, number: Self::Number) {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { number: existing, .. }) => {
*existing = number;
}
}
}
fn extrinsics_root(&self) -> &Self::Hash {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => transactions_root,
}
}
fn set_extrinsics_root(&mut self, extrinsics_root: Self::Hash) {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => {
*transactions_root = extrinsics_root;
}
}
}
fn state_root(&self) -> &Self::Hash {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.state_root,
}
}
fn set_state_root(&mut self, state_root: Self::Hash) {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
consensus.state_root = state_root;
}
}
}
fn parent_hash(&self) -> &Self::Hash {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash, .. }) => parent_hash,
}
}
fn set_parent_hash(&mut self, parent_hash: Self::Hash) {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash: existing, .. }) => {
*existing = parent_hash;
}
}
}
fn digest(&self) -> &Digest {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.digest,
}
}
fn digest_mut(&mut self) -> &mut Digest {
match self {
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &mut consensus.digest,
}
}
fn hash(&self) -> H256 {
H256::from(Header::from(self).hash().0)
}
}
impl HeaderProvider for SubstrateBlock {
type HeaderT = SubstrateHeader;
}
impl BlockTrait for SubstrateBlock {
type Extrinsic = Transaction;
type Header = SubstrateHeader;
type Hash = H256;
fn header(&self) -> &Self::Header {
&self.header
}
fn extrinsics(&self) -> &[Self::Extrinsic] {
&self.transactions
}
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
(self.header, self.transactions)
}
fn new(header: Self::Header, transactions: Vec<Self::Extrinsic>) -> Self {
Self { header, transactions }
}
fn encode_from(header: &Self::Header, transactions: &[Self::Extrinsic]) -> Vec<u8> {
let header = header.encode();
let transactions = transactions.encode();
let mut block = header;
block.extend(transactions);
block
}
fn hash(&self) -> Self::Hash {
self.header.hash()
}
}
}
#[cfg(feature = "substrate")]
pub use substrate::*;