Bridge Converted to Solitaire
Change-Id: I6223c5d51d6bda7f3581339a93f9519725a337b9
This commit is contained in:
parent
2202a38c65
commit
32b2f11def
|
@ -1,3 +1,5 @@
|
||||||
mod initialize;
|
mod initialize;
|
||||||
|
mod post_vaa;
|
||||||
|
|
||||||
pub use initialize::*;
|
pub use initialize::*;
|
||||||
|
pub use post_vaa::*;
|
||||||
|
|
|
@ -7,39 +7,16 @@ type Bridge<'a> = Derive<Data<'a, BridgeData, Uninitialized>, "Bridge">;
|
||||||
|
|
||||||
#[derive(FromAccounts, ToAccounts)]
|
#[derive(FromAccounts, ToAccounts)]
|
||||||
pub struct Initialize<'b> {
|
pub struct Initialize<'b> {
|
||||||
pub payer: Payer<'b>,
|
|
||||||
pub guardian_set: GuardianSet<'b>,
|
|
||||||
pub bridge: Bridge<'b>,
|
pub bridge: Bridge<'b>,
|
||||||
pub transfer: Transfer<'b>,
|
pub guardian_set: GuardianSet<'b>,
|
||||||
|
pub payer: Payer<'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b> InstructionContext<'b> for Initialize<'b> {
|
impl<'b> InstructionContext<'b> for Initialize<'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromAccounts, ToAccounts)]
|
|
||||||
pub struct Transfer<'b> {
|
|
||||||
pub mint: Data<'b, Test, Initialized>,
|
|
||||||
pub from: Data<'b, Test, Initialized>,
|
|
||||||
pub to: Data<'b, Test, Initialized>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b> InstructionContext<'b> for Transfer<'b> {
|
|
||||||
fn verify(&self) -> Result<()> {
|
|
||||||
return if self.mint.mint == self.from.mint {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(SolitaireError::InvalidDerive(*self.mint.0.key).into())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(BorshDeserialize, BorshSerialize)]
|
|
||||||
pub struct Test {
|
|
||||||
mint: Pubkey,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize(
|
pub fn initialize(
|
||||||
ctx: &ExecutionContext,
|
_ctx: &ExecutionContext,
|
||||||
accs: &mut Initialize,
|
accs: &mut Initialize,
|
||||||
config: BridgeConfig,
|
config: BridgeConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
use solitaire::*;
|
||||||
|
|
||||||
|
use borsh::{BorshDeserialize, BorshSerialize};
|
||||||
|
use byteorder::{BigEndian, WriteBytesExt};
|
||||||
|
use sha3::Digest;
|
||||||
|
use solana_program::{self, sysvar::clock::Clock};
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
types::{self, Bridge},
|
||||||
|
Error,
|
||||||
|
VAA_TX_FEE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIN_BRIDGE_BALANCE: u64 = (((solana_program::rent::ACCOUNT_STORAGE_OVERHEAD
|
||||||
|
+ std::mem::size_of::<Bridge>() as u64)
|
||||||
|
* solana_program::rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR) as f64
|
||||||
|
* solana_program::rent::DEFAULT_EXEMPTION_THRESHOLD) as u64;
|
||||||
|
|
||||||
|
type GuardianSet<'b> = Derive<Data<'b, types::GuardianSet>, "GuardianSet">;
|
||||||
|
type SignatureSet<'b> = Derive<Data<'b, types::SignatureSet>, "Signatures">;
|
||||||
|
type Message<'b> = Derive<Data<'b, types::PostedMessage>, "Message">;
|
||||||
|
|
||||||
|
#[derive(FromAccounts)]
|
||||||
|
pub struct PostVAA<'b> {
|
||||||
|
/// Required by Anchor for associated accounts.
|
||||||
|
pub system_program: Info<'b>,
|
||||||
|
|
||||||
|
/// Required by Anchor for associated accounts.
|
||||||
|
pub rent: Info<'b>,
|
||||||
|
|
||||||
|
/// Clock used for timestamping.
|
||||||
|
pub clock: Sysvar<Info<'b>, Clock>,
|
||||||
|
|
||||||
|
/// State struct, derived by #[state], used for associated accounts.
|
||||||
|
pub state: Info<'b>,
|
||||||
|
|
||||||
|
/// Information about the current guardian set.
|
||||||
|
pub guardian_set: GuardianSet<'b>,
|
||||||
|
|
||||||
|
/// Bridge Info
|
||||||
|
pub bridge_info: Info<'b>,
|
||||||
|
|
||||||
|
/// Claim Info
|
||||||
|
pub claim: Info<'b>,
|
||||||
|
|
||||||
|
/// Signature Info
|
||||||
|
pub signature_set: SignatureSet<'b>,
|
||||||
|
|
||||||
|
/// Account used to pay for auxillary instructions.
|
||||||
|
pub payer: Info<'b>,
|
||||||
|
|
||||||
|
/// Message the VAA is associated with.
|
||||||
|
pub message: Message<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> InstructionContext<'b> for PostVAA<'b> {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct Signature {
|
||||||
|
pub index: u8,
|
||||||
|
pub r: [u8; 32],
|
||||||
|
pub s: [u8; 32],
|
||||||
|
pub v: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ForeignAddress = [u8; 32];
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct PostVAAData {
|
||||||
|
// Header part
|
||||||
|
pub version: u8,
|
||||||
|
pub guardian_set_index: u32,
|
||||||
|
pub signatures: Vec<Signature>,
|
||||||
|
|
||||||
|
// Body part
|
||||||
|
pub timestamp: u32,
|
||||||
|
pub nonce: u32,
|
||||||
|
pub emitter_chain: u8,
|
||||||
|
pub emitter_address: ForeignAddress,
|
||||||
|
pub payload: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_vaa(
|
||||||
|
_ctx: &ExecutionContext,
|
||||||
|
accs: &mut PostVAA,
|
||||||
|
vaa: PostVAAData
|
||||||
|
) -> Result<()> {
|
||||||
|
// Verify any required invariants before we process the instruction.
|
||||||
|
check_active(&accs.guardian_set, &accs.clock)?;
|
||||||
|
check_valid_sigs(&accs.guardian_set, &accs.signature_set)?;
|
||||||
|
check_integrity(&vaa, &accs.signature_set)?;
|
||||||
|
|
||||||
|
// Count the numnber of signatures currently present.
|
||||||
|
let signature_count: usize = accs
|
||||||
|
.signature_set
|
||||||
|
.signatures
|
||||||
|
.iter()
|
||||||
|
.filter(|v| v.iter().filter(|v| **v != 0).count() != 0)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// Calculate how many signatures are required to reach consensus. This calculation is in
|
||||||
|
// expanded form to ease auditing.
|
||||||
|
let required_consensus_count = {
|
||||||
|
let len = accs.guardian_set.keys.len();
|
||||||
|
// Fixed point number transformation with one decimal to deal with rounding.
|
||||||
|
let len = (len * 10) / 3;
|
||||||
|
// Multiplication by two to get a 2/3 quorum.
|
||||||
|
let len = len * 2;
|
||||||
|
// Division by 10+1 to bring the number back into range.
|
||||||
|
len / (10 + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
if signature_count < required_consensus_count {
|
||||||
|
return Err(Error::PostVAAConsensusFailed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store VAA data in associated message.
|
||||||
|
accs.message.vaa_version = vaa.version;
|
||||||
|
accs.message.vaa_time = vaa.timestamp;
|
||||||
|
accs.message.vaa_signature_account = *accs.signature_set.pubkey();
|
||||||
|
|
||||||
|
// If the bridge has enough balance, refund the SOL to the transaction payer.
|
||||||
|
if VAA_TX_FEE + MIN_BRIDGE_BALANCE < accs.state.to_account_info().lamports() {
|
||||||
|
transfer_sol(
|
||||||
|
&ctx.accounts.state.to_account_info(),
|
||||||
|
&ctx.accounts.payer,
|
||||||
|
VAA_TX_FEE,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// // Claim the VAA
|
||||||
|
// ctx.accounts.claim.vaa_time = ctx.accounts.clock.unix_timestamp as u32;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transfer_sol(sender: &Info, recipient: &Info, amount: u64) -> Result<()> {
|
||||||
|
// let mut payer_balance = sender.try_borrow_mut_lamports()?;
|
||||||
|
// **payer_balance = payer_balance
|
||||||
|
// .checked_sub(amount)
|
||||||
|
// .ok_or(ProgramError::InsufficientFunds)?;
|
||||||
|
// let mut recipient_balance = recipient.try_borrow_mut_lamports()?;
|
||||||
|
// **recipient_balance = recipient_balance
|
||||||
|
// .checked_add(amount)
|
||||||
|
// .ok_or(ProgramError::InvalidArgument)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A guardian set must not have expired.
|
||||||
|
#[inline(always)]
|
||||||
|
fn check_active<'r>(guardian_set: &GuardianSet, clock: &Sysvar<Info<'r>, Clock>) -> Result<()> {
|
||||||
|
// if guardian_set.expiration_time != 0
|
||||||
|
// && (guardian_set.expiration_time as i64) < clock.unix_timestamp
|
||||||
|
// {
|
||||||
|
// return Err(ErrorCode::PostVAAGuardianSetExpired.into());
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The signatures in this instruction must be from the right guardian set.
|
||||||
|
#[inline(always)]
|
||||||
|
fn check_valid_sigs<'r>(
|
||||||
|
guardian_set: &GuardianSet,
|
||||||
|
signatures: &SignatureSet<'r>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// if sig_info.guardian_set_index != guardian_set.index {
|
||||||
|
// return Err(ErrorCode::PostVAAGuardianSetMismatch.into());
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn check_integrity<'r>(
|
||||||
|
vaa: &PostVAAData,
|
||||||
|
signatures: &SignatureSet<'r>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// // Serialize the VAA body into an array of bytes.
|
||||||
|
// let body = {
|
||||||
|
// let mut v = Cursor::new(Vec::new());
|
||||||
|
// v.write_u32::<BigEndian>(vaa.timestamp)?;
|
||||||
|
// v.write_u32::<BigEndian>(vaa.nonce)?;
|
||||||
|
// v.write_u8(vaa.emitter_chain)?;
|
||||||
|
// v.write(&vaa.emitter_address)?;
|
||||||
|
// v.write(&vaa.payload)?;
|
||||||
|
// v.into_inner()
|
||||||
|
// };
|
||||||
|
// // Hash this body, which is expected to be the same as the hash currently stored in the
|
||||||
|
// // signature account, binding that set of signatures to this VAA.
|
||||||
|
// let body_hash: [u8; 32] = {
|
||||||
|
// let mut h = sha3::Keccak256::default();
|
||||||
|
// h.write(body.as_slice())
|
||||||
|
// .map_err(|_| ProgramError::InvalidArgument);
|
||||||
|
// h.finalize().into()
|
||||||
|
// };
|
||||||
|
// if signatures.hash != body_hash {
|
||||||
|
// return Err(ProgramError::InvalidAccountData.into());
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -5,11 +5,30 @@
|
||||||
mod api;
|
mod api;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
use solitaire::*;
|
||||||
|
|
||||||
use api::{initialize, Initialize};
|
use api::{initialize, Initialize};
|
||||||
|
use api::{post_vaa, PostVAA, PostVAAData};
|
||||||
use types::BridgeConfig;
|
use types::BridgeConfig;
|
||||||
|
|
||||||
use solitaire::*;
|
const VAA_TX_FEE: u64 = 0;
|
||||||
|
const MAX_LEN_GUARDIAN_KEYS: u64 = 0;
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
InvalidSysVar,
|
||||||
|
InsufficientFees,
|
||||||
|
PostVAAGuardianSetExpired,
|
||||||
|
PostVAAGuardianSetMismatch,
|
||||||
|
PostVAAConsensusFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Error> for SolitaireError {
|
||||||
|
fn from(e: Error) -> SolitaireError {
|
||||||
|
SolitaireError::Custom(e as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
solitaire! {
|
solitaire! {
|
||||||
Initialize(BridgeConfig) => initialize,
|
Initialize(BridgeConfig) => initialize,
|
||||||
|
PostVAA(PostVAAData) => post_vaa,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use borsh::{BorshDeserialize, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSerialize};
|
||||||
|
use solana_program::pubkey::Pubkey;
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, BorshSerialize, BorshDeserialize)]
|
#[derive(Default, Clone, Copy, BorshSerialize, BorshDeserialize)]
|
||||||
pub struct Index(u8);
|
pub struct Index(u8);
|
||||||
|
@ -45,3 +46,79 @@ pub struct BridgeData {
|
||||||
/// Bridge configuration, which is set once upon initialization.
|
/// Bridge configuration, which is set once upon initialization.
|
||||||
pub config: BridgeConfig,
|
pub config: BridgeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct Bridge {
|
||||||
|
/// The current guardian set index, used to decide which signature sets to accept.
|
||||||
|
pub guardian_set_index: Index,
|
||||||
|
|
||||||
|
/// Bridge configuration, which is set once upon initialization.
|
||||||
|
pub config: BridgeConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct SignatureSet {
|
||||||
|
/// Signatures of validators
|
||||||
|
pub signatures: Vec<[u8; 32]>,
|
||||||
|
|
||||||
|
/// Hash of the data
|
||||||
|
pub hash: [u8; 32],
|
||||||
|
|
||||||
|
/// Index of the guardian set
|
||||||
|
pub guardian_set_index: Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct GuardianSet {
|
||||||
|
/// Index of this guardian set.
|
||||||
|
pub index: Index,
|
||||||
|
|
||||||
|
/// Public key hashes of the guardian set
|
||||||
|
pub keys: Vec<[u8; 20]>,
|
||||||
|
|
||||||
|
/// Creation time
|
||||||
|
pub creation_time: u32,
|
||||||
|
|
||||||
|
/// Expiration time when VAAs issued by this set are no longer valid
|
||||||
|
pub expiration_time: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct PostedMessage {
|
||||||
|
/// Header of the posted VAA
|
||||||
|
pub vaa_version: u8,
|
||||||
|
|
||||||
|
/// Time the vaa was submitted
|
||||||
|
pub vaa_time: u32,
|
||||||
|
|
||||||
|
/// Account where signatures are stored
|
||||||
|
pub vaa_signature_account: Pubkey,
|
||||||
|
|
||||||
|
/// Time the posted message was created
|
||||||
|
pub submission_time: u32,
|
||||||
|
|
||||||
|
/// Unique nonce for this message
|
||||||
|
pub nonce: u32,
|
||||||
|
|
||||||
|
/// Emitter of the message
|
||||||
|
pub emitter_chain: Chain,
|
||||||
|
|
||||||
|
/// Emitter of the message
|
||||||
|
pub emitter_address: [u8; 32],
|
||||||
|
|
||||||
|
/// Message payload
|
||||||
|
pub payload: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub enum Chain {
|
||||||
|
Unknown,
|
||||||
|
Solana = 1u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Chain {
|
||||||
|
fn default() -> Self {
|
||||||
|
Chain::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
use solana_program::{program_error::ProgramError, pubkey::Pubkey};
|
||||||
|
|
||||||
|
/// Quality of life Result type for the Solitaire stack.
|
||||||
|
pub type Result<T> = std::result::Result<T, SolitaireError>;
|
||||||
|
|
||||||
|
/// Quality of life type alias for wrapping up boxed errors.
|
||||||
|
pub type ErrBox = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
/// There are several places in Solitaire that might fail, we want descriptive errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SolitaireError {
|
||||||
|
/// The AccountInfo parser expected a Signer, but the account did not sign.
|
||||||
|
InvalidSigner(Pubkey),
|
||||||
|
|
||||||
|
/// The AccountInfo parser expected a Sysvar, but the key was invalid.
|
||||||
|
InvalidSysvar(Pubkey),
|
||||||
|
|
||||||
|
/// The AccountInfo parser tried to derive the provided key, but it did not match.
|
||||||
|
InvalidDerive(Pubkey),
|
||||||
|
|
||||||
|
/// The instruction payload itself could not be deserialized.
|
||||||
|
InstructionDeserializeFailed,
|
||||||
|
|
||||||
|
/// An IO error was captured, wrap it up and forward it along.
|
||||||
|
IoError(std::io::Error),
|
||||||
|
|
||||||
|
/// An solana program error
|
||||||
|
ProgramError(ProgramError),
|
||||||
|
|
||||||
|
Custom(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ProgramError> for SolitaireError {
|
||||||
|
fn from(e: ProgramError) -> Self {
|
||||||
|
SolitaireError::ProgramError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for SolitaireError {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
SolitaireError::IoError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ProgramError> for SolitaireError {
|
||||||
|
fn into(self) -> ProgramError {
|
||||||
|
if let SolitaireError::ProgramError(e) = self {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
ProgramError::Custom(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,88 +13,37 @@ pub use rocksalt::*;
|
||||||
// - Client generation incomplete.
|
// - Client generation incomplete.
|
||||||
|
|
||||||
// We need a few Solana things in scope in order to properly abstract Solana.
|
// We need a few Solana things in scope in order to properly abstract Solana.
|
||||||
pub use solana_program::{
|
use solana_program::{
|
||||||
account_info::AccountInfo,
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint,
|
entrypoint,
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
instruction::AccountMeta,
|
||||||
|
program::invoke_signed,
|
||||||
|
program_error::ProgramError,
|
||||||
|
program_pack::Pack,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
rent::Rent,
|
||||||
system_instruction,
|
system_instruction,
|
||||||
system_program,
|
system_program,
|
||||||
sysvar::{self, SysvarId},
|
sysvar::{self, SysvarId},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Later on we will define types that don't actually contain data, PhantomData will help us.
|
|
||||||
pub use std::marker::PhantomData;
|
|
||||||
|
|
||||||
// We'll need these traits to make any wrappers we define more ergonomic for users.
|
|
||||||
pub use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
// Borsh is Solana's goto serialization target, so we'll need this if we want to do any
|
|
||||||
// serialization on the users behalf.
|
|
||||||
pub use borsh::{BorshDeserialize, BorshSerialize};
|
|
||||||
use solana_program::{
|
|
||||||
account_info::next_account_info,
|
|
||||||
instruction::AccountMeta,
|
|
||||||
program::invoke_signed,
|
|
||||||
program_error::ProgramError,
|
|
||||||
program_pack::Pack,
|
|
||||||
rent::Rent,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
io::{ErrorKind, Write},
|
io::{ErrorKind, Write},
|
||||||
|
marker::PhantomData,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
slice::Iter,
|
slice::Iter,
|
||||||
string::FromUtf8Error,
|
string::FromUtf8Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// There are several places in Solitaire that might fail, we want descriptive errors.
|
use borsh::{BorshDeserialize, BorshSerialize};
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum SolitaireError {
|
|
||||||
/// The AccountInfo parser expected a Signer, but the account did not sign.
|
|
||||||
InvalidSigner(Pubkey),
|
|
||||||
|
|
||||||
/// The AccountInfo parser expected a Sysvar, but the key was invalid.
|
pub use crate::{
|
||||||
InvalidSysvar(Pubkey),
|
error::{ErrBox, Result, SolitaireError},
|
||||||
|
seeded::Creatable,
|
||||||
|
};
|
||||||
|
|
||||||
/// The AccountInfo parser tried to derive the provided key, but it did not match.
|
mod error;
|
||||||
InvalidDerive(Pubkey),
|
|
||||||
|
|
||||||
/// The instruction payload itself could not be deserialized.
|
|
||||||
InstructionDeserializeFailed,
|
|
||||||
|
|
||||||
/// An IO error was captured, wrap it up and forward it along.
|
|
||||||
IoError(std::io::Error),
|
|
||||||
|
|
||||||
/// An solana program error
|
|
||||||
ProgramError(ProgramError),
|
|
||||||
|
|
||||||
Custom(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ProgramError> for SolitaireError {
|
|
||||||
fn from(e: ProgramError) -> Self {
|
|
||||||
SolitaireError::ProgramError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for SolitaireError {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
SolitaireError::IoError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<ProgramError> for SolitaireError {
|
|
||||||
fn into(self) -> ProgramError {
|
|
||||||
if let SolitaireError::ProgramError(e) = self {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
ProgramError::Custom(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Quality of life Result type for the Solitaire stack.
|
|
||||||
pub type Result<T> = std::result::Result<T, SolitaireError>;
|
|
||||||
pub type ErrBox = Box<dyn std::error::Error>;
|
|
||||||
|
|
||||||
pub trait Persist {
|
pub trait Persist {
|
||||||
fn persist(self);
|
fn persist(self);
|
||||||
|
@ -561,7 +510,12 @@ macro_rules! solitaire {
|
||||||
mod instruction {
|
mod instruction {
|
||||||
use super::*;
|
use super::*;
|
||||||
use borsh::{BorshDeserialize, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSerialize};
|
||||||
use solitaire::{Persist, FromAccounts, Result};
|
use solana_program::{
|
||||||
|
account_info::AccountInfo,
|
||||||
|
entrypoint::ProgramResult,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
use solitaire::{FromAccounts, Persist, Result};
|
||||||
|
|
||||||
/// Generated:
|
/// Generated:
|
||||||
/// This Instruction contains a 1-1 mapping for each enum variant to function call. The
|
/// This Instruction contains a 1-1 mapping for each enum variant to function call. The
|
||||||
|
@ -602,7 +556,8 @@ macro_rules! solitaire {
|
||||||
/// can be matched to the Instruction found above.
|
/// can be matched to the Instruction found above.
|
||||||
mod client {
|
mod client {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_program::instruction::Instruction;
|
use borsh::BorshSerialize;
|
||||||
|
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||||
|
|
||||||
/// Generated from Instruction Field
|
/// Generated from Instruction Field
|
||||||
$(pub(crate) fn $fn(pid: &Pubkey, accounts: $row, ix_data: $kind) -> std::result::Result<Instruction, ErrBox> {
|
$(pub(crate) fn $fn(pid: &Pubkey, accounts: $row, ix_data: $kind) -> std::result::Result<Instruction, ErrBox> {
|
||||||
|
@ -615,7 +570,7 @@ macro_rules! solitaire {
|
||||||
}
|
}
|
||||||
|
|
||||||
use instruction::solitaire;
|
use instruction::solitaire;
|
||||||
entrypoint!(solitaire);
|
solana_program::entrypoint!(solitaire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#![feature(const_generics)]
|
||||||
|
#![feature(const_generics_defaults)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
// #![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
mod messages;
|
||||||
|
mod types;
|
||||||
|
mod vaa;
|
||||||
|
|
||||||
|
use api::{initialize, Initialize};
|
||||||
|
|
||||||
|
use solitaire::*;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub enum TokenBridgeError {
|
||||||
|
InvalidPayload,
|
||||||
|
Unknown(String),
|
||||||
|
InvalidMint,
|
||||||
|
WrongAccountOwner,
|
||||||
|
InvalidUTF8String,
|
||||||
|
AlreadyExecuted,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Error> From<T> for TokenBridgeError {
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
return TokenBridgeError::Unknown(t.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<SolitaireError> for TokenBridgeError {
|
||||||
|
fn into(self) -> SolitaireError {
|
||||||
|
SolitaireError::Custom(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
solitaire! {
|
||||||
|
Initialize(Pubkey) => initialize,
|
||||||
|
}
|
|
@ -85,12 +85,12 @@ pub fn derive_from_accounts(input: TokenStream) -> TokenStream {
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
/// Macro generated implementation of FromAccounts by Solitaire.
|
/// Macro generated implementation of FromAccounts by Solitaire.
|
||||||
impl #combined_impl_g solitaire::FromAccounts #peel_type_g for #name #type_g {
|
impl #combined_impl_g solitaire::FromAccounts #peel_type_g for #name #type_g {
|
||||||
fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &'c mut std::slice::Iter<'a, AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<(Self, Vec<solana_program::pubkey::Pubkey>)> {
|
fn from<DataType>(pid: &'a solana_program::pubkey::Pubkey, iter: &'c mut std::slice::Iter<'a, solana_program::account_info::AccountInfo<'b>>, data: &'a DataType) -> solitaire::Result<(Self, Vec<solana_program::pubkey::Pubkey>)> {
|
||||||
#from_method
|
#from_method
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #combined_impl_g Peel<'a, 'b, 'c> for #name #type_g {
|
impl #combined_impl_g solitaire::Peel<'a, 'b, 'c> for #name #type_g {
|
||||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized {
|
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> solitaire::Result<Self> where Self: Sized {
|
||||||
let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data).map(|v| v.0)?;
|
let v: #name #type_g = FromAccounts::from(ctx.this, ctx.iter, ctx.data).map(|v| v.0)?;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Merge similar crates together to avoid multiple use statements.
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
|
||||||
|
# Consistency in formatting makes tool based searching/editing better.
|
||||||
|
empty_item_single_line = false
|
||||||
|
|
||||||
|
# Easier editing when arbitrary mixed use statements do not collapse.
|
||||||
|
imports_layout = "HorizontalVertical"
|
||||||
|
|
||||||
|
# Default rustfmt formatting of match arms with branches is awful.
|
||||||
|
match_arm_leading_pipes = "Preserve"
|
Loading…
Reference in New Issue