solana/programs/btc_spv/src/spv_state.rs

458 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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),
}
}
}