solana/programs/btc_spv/src/spv_state.rs

458 lines
13 KiB
Rust
Raw Normal View History

use crate::header_store::*;
use crate::utils::*;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
use std::{error, fmt};
pub type BitcoinTxHash = [u8; 32];
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct BlockHeader {
// Bitcoin network version
pub version: u32,
// Previous block's hash/digest
pub parent: [u8; 32],
// merkle Root of the block, proofEntry side should be None
pub merkle_root: ProofEntry,
// the blocktime associate with the block
pub time: u32,
// An encoded version of the target threshold this blocks header hash must be less than or equal to.
pub nbits: [u8; 4],
// block header's nonce
pub nonce: [u8; 4],
// Block hash
pub blockhash: [u8; 32],
}
impl BlockHeader {
pub fn new(header: &[u8; 80], blockhash: &[u8; 32]) -> Result<BlockHeader, SpvError> {
let mut va: [u8; 4] = [0; 4];
va.copy_from_slice(&header[0..4]);
let version = u32::from_le_bytes(va);
let mut ph: [u8; 32] = [0; 32];
ph.copy_from_slice(&header[4..36]);
let parent = ph;
// extract merkle root in internal byte order
let mut mrr: [u8; 32] = [0; 32];
mrr.copy_from_slice(&header[36..68]);
let merkle_root = ProofEntry {
hash: mrr,
side: EntrySide::Root,
};
// timestamp associate with the block
let mut bt: [u8; 4] = [0; 4];
bt.copy_from_slice(&header[68..72]);
let time = u32::from_le_bytes(bt);
// nbits field is an encoded version of the
let mut nb: [u8; 4] = [0; 4];
nb.copy_from_slice(&header[72..76]);
let nbits = nb;
let mut nn: [u8; 4] = [0; 4];
nn.copy_from_slice(&header[76..80]);
let nonce = nn;
let bh = BlockHeader {
version,
parent,
merkle_root,
time,
nbits,
nonce,
blockhash: *blockhash,
};
Ok(bh)
}
pub fn hexnew(header: &str, blockhash: &str) -> Result<BlockHeader, SpvError> {
if header.len() != 160 || blockhash.len() != 64 {
return Err(SpvError::InvalidBlockHeader);
}
match hex::decode(header) {
Ok(header) => {
let bhbytes = hex::decode(blockhash)?;
const SIZE: usize = 80;
let mut hh = [0; SIZE];
hh.copy_from_slice(&header[..header.len()]);
let mut bhb: [u8; 32] = [0; 32];
bhb.copy_from_slice(&bhbytes[..bhbytes.len()]);
Ok(BlockHeader::new(&hh, &bhb).unwrap())
}
Err(e) => Err(SpvError::InvalidBlockHeader),
}
}
pub fn difficulty(mut self) -> u32 {
// calculates difficulty from nbits
let standin: u32 = 123_456_789;
standin
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct BitcoinTransaction {
pub inputs: Vec<Input>,
pub inputs_num: u64,
//input utxos
pub outputs: Vec<Output>,
pub outputs_num: u64,
//output utxos
pub version: u32,
//bitcoin network version
pub lock_time: u32,
pub bytes_len: usize,
}
impl BitcoinTransaction {
pub fn new(txbytes: Vec<u8>) -> Self {
let mut ver: [u8; 4] = [0; 4];
ver.copy_from_slice(&txbytes[..4]);
let version = u32::from_le_bytes(ver);
let inputs_num: u64 = decode_variable_int(&txbytes[4..13]).unwrap();
let vinlen: usize = measure_variable_int(&txbytes[4..13]).unwrap();
let mut inputstart: usize = 4 + vinlen;
let mut inputs = Vec::new();
if inputs_num > 0 {
for i in 0..inputs_num {
let mut input = Input::new(txbytes[inputstart..].to_vec());
inputstart += input.bytes_len;
inputs.push(input);
}
inputs.to_vec();
}
let outputs_num: u64 = decode_variable_int(&txbytes[inputstart..9 + inputstart]).unwrap();
let voutlen: usize = measure_variable_int(&txbytes[inputstart..9 + inputstart]).unwrap();
let mut outputstart: usize = inputstart + voutlen;
let mut outputs = Vec::new();
for i in 0..outputs_num {
let mut output = Output::new(txbytes[outputstart..].to_vec());
outputstart += output.bytes_len;
outputs.push(output);
}
let mut lt: [u8; 4] = [0; 4];
lt.copy_from_slice(&txbytes[outputstart..4 + outputstart]);
let lock_time = u32::from_le_bytes(lt);
assert_eq!(inputs.len(), inputs_num as usize);
assert_eq!(outputs.len(), outputs_num as usize);
BitcoinTransaction {
inputs,
inputs_num,
outputs,
outputs_num,
version,
lock_time,
bytes_len: 4 + outputstart,
}
}
pub fn hexnew(hex: String) -> Result<BitcoinTransaction, SpvError> {
match hex::decode(&hex) {
Ok(txbytes) => Ok(BitcoinTransaction::new(txbytes)),
Err(e) => Err(SpvError::ParseError),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Input {
pub input_type: InputType,
// Type of the input
pub position: u32,
// position of the tx in its Block
pub txhash: BitcoinTxHash,
// hash of the transaction
pub script_length: u64,
// length of the spend script
pub script: Vec<u8>,
// script bytes
pub sequence: [u8; 4],
// length of the input in bytes
pub bytes_len: usize,
}
impl Input {
fn new(ibytes: Vec<u8>) -> Self {
let mut txhash: [u8; 32] = [0; 32];
txhash.copy_from_slice(&ibytes[..32]);
let mut tx_out_index: [u8; 4] = [0; 4];
tx_out_index.copy_from_slice(&ibytes[32..36]);
let position = u32::from_le_bytes(tx_out_index);
let script_length: u64 = decode_variable_int(&ibytes[36..45]).unwrap();
let script_length_len: usize = measure_variable_int(&ibytes[36..45]).unwrap();
let script_start = 36 + script_length_len; //checkc for correctness
let script_end = script_start + script_length as usize;
let input_end = script_end + 4;
let script: Vec<u8> = ibytes[script_start..script_length as usize].to_vec();
let mut sequence: [u8; 4] = [0; 4];
sequence.copy_from_slice(&ibytes[script_end..input_end]);
let input_type: InputType = InputType::NONE; // testing measure
Self {
input_type,
position,
txhash,
script_length,
script,
sequence,
bytes_len: input_end,
}
}
fn default() -> Self {
let txh: [u8; 32] = [0; 32];
let seq: [u8; 4] = [0; 4];
Self {
input_type: InputType::NONE,
position: 55,
txhash: txh,
script_length: 45,
script: txh.to_vec(),
sequence: seq,
bytes_len: 123,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum InputType {
LEGACY,
COMPATIBILITY,
WITNESS,
NONE,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Output {
pub output_type: OutputType,
// type of the output
pub value: u64,
// amount of btc in sats
pub script: Vec<u8>,
pub script_length: u64,
pub bytes_len: usize,
// payload: Option<Vec<u8>>,
// // data sent with the transaction (Op return)
}
impl Output {
fn new(obytes: Vec<u8>) -> Self {
let mut val: [u8; 8] = [0; 8];
val.copy_from_slice(&obytes[..8]);
let value: u64 = u64::from_le_bytes(val);
let script_start: usize = 8 + measure_variable_int(&obytes[8..17]).unwrap();
let script_length = decode_variable_int(&obytes[8..script_start]).unwrap();
let script_end: usize = script_start + script_length as usize;
let script = obytes[script_start..script_end].to_vec();
let output_type = OutputType::WPKH; // temporary hardcode
Self {
output_type,
value,
script,
script_length,
bytes_len: script_end,
}
}
fn default() -> Self {
let transaction_hash: [u8; 32] = [0; 32];
Self {
output_type: OutputType::WPKH,
value: 55,
script: transaction_hash.to_vec(),
script_length: 45,
bytes_len: 123,
}
}
}
#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum OutputType {
WPKH,
WSH,
OP_RETURN,
PKH,
SH,
NONSTANDARD,
// https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
}
pub type HeaderChain = Vec<BlockHeader>;
// a vector of BlockHeaders used as part of a Proof
// index 0 : the block header of the block prior to the proof Block
// index 1 : the block header of the proof block
// index 2-n* : the block headers for the confirmation chain
// (where n is the confirmations value from the proof request)
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ProofEntry {
// 32 byte merkle hashes
pub hash: [u8; 32],
// side of the merkle tree entry
pub side: EntrySide,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum EntrySide {
// Left side of the hash combination
Left,
// Right side of hash combination
Right,
// Root hash (neither side)
Root,
}
pub type MerkleProof = Vec<ProofEntry>;
// a vector of ProofEntries used as part of a Proof
// index 0 : a ProofEntry representing the txid
// indices 0-n : ProofEntries linking the txhash and the merkle root
// index n : a ProofEntry representing the merkel root for the block in question
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ClientRequestInfo {
// bitcoin transaction hash
pub txhash: BitcoinTxHash,
// confirmation count
pub confirmations: u8,
// fee paid for tx verification
pub fee: u64,
// required minimum difficulty for submitted blocks
pub difficulty: u64,
// expiration slot height
pub expiration: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ProofRequest {
pub owner: Pubkey,
// bitcoin transaction hash
pub txhash: BitcoinTxHash,
// confirmation count
pub confirmations: u8,
// fee paid for tx verification
pub fee: u64,
// minimum allowable difficulty
pub difficulty: u64,
// expiration slot height
pub expiration: u64,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Proof {
// the pubkey who submitted the proof in question, entitled to fees from any corresponding proof requests
pub submitter: Pubkey,
// merkle branch connecting txhash to block header merkle root
pub proof: MerkleProof,
// chain of bitcoin headers provifing context for the proof
pub headers: HeaderChain,
// transaction associated with the Proof
pub transaction: BitcoinTransaction,
// public key of the request this proof corresponds to
pub request: Pubkey,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AccountState {
// Request Account
Request(ClientRequestInfo),
// Verified Proof
Verification(Proof),
// Account holds a HeaderStore structure
Headers(HeaderAccountInfo),
// Account's data is Unallocated
Unallocated,
// Invalid
Invalid,
}
impl Default for AccountState {
fn default() -> Self {
AccountState::Unallocated
}
}
///Errors
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SpvError {
InvalidBlockHeader,
// blockheader is malformed or out of order
HeaderStoreError,
// header store write/read result is invalid
ParseError,
// other errors with parsing inputs
InvalidAccount,
}
impl error::Error for SpvError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
// temporary measure
None
}
}
impl From<HeaderStoreError> for SpvError {
fn from(e: HeaderStoreError) -> Self {
SpvError::HeaderStoreError
}
}
impl From<DecodeHexError> for SpvError {
fn from(e: DecodeHexError) -> Self {
SpvError::ParseError
}
}
impl From<hex::FromHexError> for SpvError {
fn from(e: hex::FromHexError) -> Self {
SpvError::ParseError
}
}
// impl fmt::Debug for SpvError {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
// match self {
// SpvError::InvalidBlockHeader => "BlockHeader is malformed or does not apply ".fmt(f),
// SpvError::HeaderStoreError => "Placeholder headerstore error debug text".fmt(f),
// SpvError::ParseError => "Error parsing blockheaders debug".fmt(f),
// }
// }
// }
impl fmt::Display for SpvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SpvError::InvalidBlockHeader => "BlockHeader is malformed or does not apply ".fmt(f),
SpvError::HeaderStoreError => "Placeholder headerstore error text".fmt(f),
SpvError::ParseError => "Error parsing blockheaders placceholder text".fmt(f),
SpvError::InvalidAccount => "Provided account is not usable or does not exist".fmt(f),
}
}
}