use super::apply::{BlockError, TxError};
use super::genesis::Genesis;
use super::header::{BlockId, Header, HeaderError};
use super::select::{Checkpoint, CheckpointError};
use super::state::Id;
use super::tx::Tx;
use super::u256::U256;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Tip {
pub height: u64,
pub id: BlockId,
pub chainwork: U256,
pub header: Header,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Block {
pub header: Header,
pub body: Vec<u8>,
}
impl Block {
pub fn id(&self) -> BlockId {
self.header.id()
}
pub fn encode(&self) -> Vec<u8> {
let mut out = self.header.encode().to_vec();
out.extend_from_slice(&self.body);
out
}
pub fn decode(b: &[u8]) -> Result<Block, HeaderError> {
let header = Header::decode_raw(b.get(..168).ok_or(HeaderError::Length(b.len()))?)?;
Ok(Block { header, body: b[168..].to_vec() })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum HeaderVerdict {
Accepted,
Known,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BlockVerdict {
Extended,
Reorganized { old_tip: BlockId, depth: u64 },
Stored,
NeedBodies(Vec<BlockId>),
Known,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChainError {
Header(HeaderError),
UnknownParent(BlockId),
InvalidParent(BlockId),
Height { expected: u64, actual: u64 },
Time { mtp: u64, actual: u64 },
FutureTime { time: u64, now: u64 },
Bits { expected: u32, actual: u32 },
Pow,
RuleSetUnknown(u64),
DeepFork { fork_height: u64, tip_height: u64 },
Checkpoint { height: u64, expected: BlockId },
CheckpointRejected(CheckpointError),
Block(BlockError),
Rejected(BlockId),
Genesis,
Store(String),
}
impl std::fmt::Display for ChainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ChainError::Header(e) => write!(f, "header: {e}"),
ChainError::UnknownParent(_) => write!(f, "unknown parent"),
ChainError::InvalidParent(_) => write!(f, "parent is invalid"),
ChainError::Height { expected, actual } => write!(f, "height {actual}, expected {expected}"),
ChainError::Time { mtp, actual } => write!(f, "time {actual} is not above the median time past {mtp}"),
ChainError::FutureTime { time, now } => write!(f, "time {time} is more than the drift ahead of {now}"),
ChainError::Bits { expected, actual } => write!(f, "bits {actual:#010x}, ASERT expects {expected:#010x}"),
ChainError::Pow => write!(f, "block id does not meet the target"),
ChainError::RuleSetUnknown(h) => write!(f, "no rule set is known for height {h}; this release stops here"),
ChainError::DeepFork { fork_height, tip_height } => write!(f, "fork at {fork_height} is beyond MAX_REORG from the tip {tip_height}"),
ChainError::Checkpoint { height, .. } => write!(f, "chain excludes the checkpoint at {height}"),
ChainError::CheckpointRejected(e) => write!(f, "checkpoint: {e}"),
ChainError::Block(e) => write!(f, "block: {e}"),
ChainError::Rejected(_) => write!(f, "previously rejected block"),
ChainError::Genesis => write!(f, "genesis block mismatch"),
ChainError::Store(e) => write!(f, "store: {e}"),
}
}
}
impl std::error::Error for ChainError {}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxInfo {
pub txid: Id,
pub tx: Tx,
pub size: usize,
pub fee: u64,
pub account: Id,
pub nonce: u64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Template {
pub header: Header,
pub target: U256,
pub body: Vec<u8>,
pub txids: Vec<Id>,
pub fees: u64,
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
impl Template {
pub fn json(&self, chain_name: &str, expires: u64) -> String {
format!(
"{{\"chain\":{},\"height\":{},\"prev\":\"{}\",\"header\":\"{}\",\"bits\":\"{:08x}\",\"target\":\"{}\",\"body\":\"{}\",\"expires\":{}}}",
serde_json::to_string(chain_name).unwrap_or_else(|_| "\"\"".into()),
self.header.height,
hex(&self.header.prev),
hex(&self.header.encode()),
self.header.bits,
hex(&self.target.to_le_bytes()),
hex(&self.body),
expires
)
}
}
impl Tip {
pub fn json(&self) -> String {
format!("{{\"height\":{},\"id\":\"{}\",\"chainwork\":\"{}\"}}", self.height, hex(&self.id), self.chainwork.to_decimal())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
Block { header: Header, body: Vec<u8>, state_root: Id },
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Status {
pub chain: Id,
pub name: String,
pub rule_set: u16,
pub height: u64,
pub tip: BlockId,
pub chainwork: U256,
pub bits: u32,
pub next_bits: u32,
pub genesis_block: BlockId,
pub genesis_time: u64,
pub state_root: Id,
pub supply: u64,
pub conserved: bool,
pub subsidy: u64,
pub neurons: u64,
pub particles: u64,
pub axons: u64,
pub checkpoint: Option<u64>,
pub finalized: u64,
}
pub trait ChainApi {
fn chain_id(&self) -> Id;
fn genesis(&self) -> &Genesis;
fn tip(&self) -> Tip;
fn status(&self) -> Status;
fn header(&self, id: &BlockId) -> Option<Header>;
fn header_at(&self, height: u64) -> Option<Header>;
fn headers(&self, from: u64, count: usize) -> Vec<Header>;
fn block(&self, id: &BlockId) -> Option<Block>;
fn blocks(&self, from: u64, count: usize) -> Vec<Block>;
fn chainwork(&self, id: &BlockId) -> Option<U256>;
fn locator(&self) -> Vec<BlockId>;
fn ancestor_at(&self, id: &BlockId, height: u64) -> Option<BlockId>;
fn balance(&self, owner: &Id, token: &Id) -> u64;
fn nonce(&self, neuron: &Id) -> u64;
fn supply(&self, token: &Id) -> u64;
fn finalized_height(&self) -> u64;
fn accept_header(&mut self, header: &Header, now: u64) -> Result<HeaderVerdict, ChainError>;
fn accept_block(&mut self, block: &Block, now: u64) -> Result<BlockVerdict, ChainError>;
fn accept_checkpoint(&mut self, cp: &Checkpoint) -> Result<bool, ChainError>;
fn checkpoints(&self) -> Vec<(u64, BlockId)>;
fn validate_tx(&mut self, tx_bytes: &[u8], pending: &[Tx]) -> Result<TxInfo, TxError>;
fn header_template(&mut self, to: Id, time: u64, mempool: &[Vec<u8>]) -> Result<Template, ChainError>;
fn take_events(&mut self) -> Vec<Event>;
}