Various token bridge and solitaire improvements

Derive message accounts from nonce and data vs sequence because sequence can lead to collision with parallel tx submission

Change-Id: I82d5b3a3c7fd96b5a6c74933c773a32e1c58bdd4
This commit is contained in:
Hendrik Hofstadt 2021-06-18 14:34:31 +02:00
parent c3fa835196
commit 5eb7d0b7d0
13 changed files with 173 additions and 93 deletions

View File

@ -57,6 +57,8 @@ dependencies = [
"solana-sdk", "solana-sdk",
"solitaire", "solitaire",
"solitaire-client", "solitaire-client",
"spl-token",
"token-bridge",
] ]
[[package]] [[package]]
@ -70,9 +72,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.40" version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
[[package]] [[package]]
name = "arrayref" name = "arrayref"
@ -1146,7 +1148,7 @@ dependencies = [
"http", "http",
"indexmap", "indexmap",
"slab", "slab",
"tokio 1.6.1", "tokio 1.6.2",
"tokio-util", "tokio-util",
"tracing", "tracing",
] ]
@ -1304,7 +1306,7 @@ dependencies = [
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"socket2 0.4.0", "socket2 0.4.0",
"tokio 1.6.1", "tokio 1.6.2",
"tower-service", "tower-service",
"tracing", "tracing",
"want", "want",
@ -1320,7 +1322,7 @@ dependencies = [
"hyper", "hyper",
"log", "log",
"rustls", "rustls",
"tokio 1.6.1", "tokio 1.6.2",
"tokio-rustls", "tokio-rustls",
"webpki", "webpki",
] ]
@ -1387,9 +1389,9 @@ dependencies = [
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.3.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9"
[[package]] [[package]]
name = "itertools" name = "itertools"
@ -1466,9 +1468,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.96" version = "0.2.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc" checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
[[package]] [[package]]
name = "libloading" name = "libloading"
@ -1611,9 +1613,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.7.11" version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -1780,9 +1782,9 @@ checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.7.2" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
dependencies = [ dependencies = [
"parking_lot 0.11.1", "parking_lot 0.11.1",
] ]
@ -2275,7 +2277,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"tokio 1.6.1", "tokio 1.6.2",
"tokio-rustls", "tokio-rustls",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
@ -2722,7 +2724,7 @@ dependencies = [
"solana-version", "solana-version",
"solana-vote-program", "solana-vote-program",
"thiserror", "thiserror",
"tokio 1.6.1", "tokio 1.6.2",
"tungstenite", "tungstenite",
"url", "url",
] ]
@ -2787,7 +2789,7 @@ dependencies = [
"solana-version", "solana-version",
"spl-memo", "spl-memo",
"thiserror", "thiserror",
"tokio 1.6.1", "tokio 1.6.2",
] ]
[[package]] [[package]]
@ -2875,7 +2877,7 @@ dependencies = [
"solana-clap-utils", "solana-clap-utils",
"solana-logger", "solana-logger",
"solana-version", "solana-version",
"tokio 1.6.1", "tokio 1.6.2",
"url", "url",
] ]
@ -3426,6 +3428,22 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "token-bridge"
version = "0.1.0"
dependencies = [
"borsh",
"bridge",
"byteorder",
"primitive-types",
"rocksalt",
"sha3",
"solana-program",
"solitaire",
"solitaire-client",
"spl-token",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "0.1.22" version = "0.1.22"
@ -3452,15 +3470,15 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.6.1" version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a38d31d7831c6ed7aad00aa4c12d9375fd225a6dd77da1d25b707346319a975" checksum = "aea337f72e96efe29acc234d803a5981cd9a2b6ed21655cd7fc21cfe021e8ec7"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes 1.0.1", "bytes 1.0.1",
"libc", "libc",
"memchr", "memchr",
"mio 0.7.11", "mio 0.7.13",
"num_cpus", "num_cpus",
"once_cell", "once_cell",
"parking_lot 0.11.1", "parking_lot 0.11.1",
@ -3560,7 +3578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [ dependencies = [
"rustls", "rustls",
"tokio 1.6.1", "tokio 1.6.2",
"webpki", "webpki",
] ]
@ -3661,7 +3679,7 @@ dependencies = [
"futures-sink", "futures-sink",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tokio 1.6.1", "tokio 1.6.2",
] ]
[[package]] [[package]]

View File

@ -9,7 +9,8 @@ crate-type = ["cdylib", "lib"]
name = "bridge" name = "bridge"
[features] [features]
no-entrypoint = ["solitaire/no-entrypoint", "solitaire-client"] no-entrypoint = ["solitaire/no-entrypoint"]
client = ["solitaire-client"]
no-idl = [] no-idl = []
cpi = ["no-entrypoint"] cpi = ["no-entrypoint"]
default = [] default = []

View File

@ -12,8 +12,10 @@ use solitaire::{
AccountState, AccountState,
Data, Data,
Derive, Derive,
Info,
}; };
pub type FeeCollector<'a> = Derive<Info<'a>, "fee_collector">;
pub type Bridge<'a, const State: AccountState> = Derive<Data<'a, BridgeData, { State }>, "Bridge">; pub type Bridge<'a, const State: AccountState> = Derive<Data<'a, BridgeData, { State }>, "Bridge">;
pub type GuardianSet<'b, const State: AccountState> = Data<'b, types::GuardianSetData, { State }>; pub type GuardianSet<'b, const State: AccountState> = Data<'b, types::GuardianSetData, { State }>;
@ -30,6 +32,24 @@ impl<'b, const State: AccountState> Seeded<&GuardianSetDerivationData>
} }
} }
pub type Claim<'b, const State: AccountState> = Data<'b, types::ClaimData, { State }>;
pub struct ClaimDerivationData {
pub emitter_address: [u8; 32],
pub emitter_chain: u16,
pub sequence: u64,
}
impl<'b, const State: AccountState> Seeded<&ClaimDerivationData> for Claim<'b, { State }> {
fn seeds(data: &ClaimDerivationData) -> Vec<Vec<u8>> {
return vec![
data.emitter_address.to_vec(),
data.emitter_chain.to_be_bytes().to_vec(),
data.sequence.to_be_bytes().to_vec(),
];
}
}
pub type SignatureSet<'b, const State: AccountState> = Data<'b, types::SignatureSet, { State }>; pub type SignatureSet<'b, const State: AccountState> = Data<'b, types::SignatureSet, { State }>;
pub struct SignaturesSetDerivationData { pub struct SignaturesSetDerivationData {
@ -48,15 +68,21 @@ pub type Message<'b, const State: AccountState> = Data<'b, PostedMessage, { Stat
pub struct MessageDerivationData { pub struct MessageDerivationData {
pub emitter_key: [u8; 32], pub emitter_key: [u8; 32],
pub sequence: u64, pub emitter_chain: u16,
pub nonce: u32,
pub payload: Vec<u8>,
} }
impl<'b, const State: AccountState> Seeded<&MessageDerivationData> for Message<'b, { State }> { impl<'b, const State: AccountState> Seeded<&MessageDerivationData> for Message<'b, { State }> {
fn seeds(data: &MessageDerivationData) -> Vec<Vec<u8>> { fn seeds(data: &MessageDerivationData) -> Vec<Vec<u8>> {
vec![ let mut seeds = vec![
data.emitter_key.to_vec(), data.emitter_key.to_vec(),
data.sequence.to_be_bytes().to_vec(), data.emitter_chain.to_be_bytes().to_vec(),
] data.nonce.to_be_bytes().to_vec(),
];
seeds.append(&mut data.payload.chunks(32).map(|v| v.to_vec()).collect());
seeds
} }
} }

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
accounts::{ accounts::{
Bridge, Bridge,
FeeCollector,
Message, Message,
MessageDerivationData, MessageDerivationData,
Sequence, Sequence,
@ -28,15 +29,6 @@ use solitaire::{
type UninitializedMessage<'b> = Message<'b, { AccountState::Uninitialized }>; type UninitializedMessage<'b> = Message<'b, { AccountState::Uninitialized }>;
impl<'a> From<&PostMessage<'a>> for MessageDerivationData {
fn from(accs: &PostMessage<'a>) -> Self {
MessageDerivationData {
emitter_key: accs.emitter.key.to_bytes(),
sequence: accs.sequence.sequence,
}
}
}
impl<'a> From<&PostMessage<'a>> for SequenceDerivationData<'a> { impl<'a> From<&PostMessage<'a>> for SequenceDerivationData<'a> {
fn from(accs: &PostMessage<'a>) -> Self { fn from(accs: &PostMessage<'a>) -> Self {
SequenceDerivationData { SequenceDerivationData {
@ -45,12 +37,9 @@ impl<'a> From<&PostMessage<'a>> for SequenceDerivationData<'a> {
} }
} }
pub type FeeAccount<'a> = Derive<Info<'a>, "Fees">;
#[derive(FromAccounts)] #[derive(FromAccounts)]
pub struct PostMessage<'b> { pub struct PostMessage<'b> {
pub bridge: Bridge<'b, { AccountState::Initialized }>, pub bridge: Bridge<'b, { AccountState::Initialized }>,
pub fee_vault: FeeAccount<'b>,
/// Account to store the posted message /// Account to store the posted message
pub message: UninitializedMessage<'b>, pub message: UninitializedMessage<'b>,
@ -65,17 +54,13 @@ pub struct PostMessage<'b> {
pub payer: Signer<Info<'b>>, pub payer: Signer<Info<'b>>,
/// Account to collect tx fee /// Account to collect tx fee
pub fee_collector: Derive<Info<'b>, "fee_collector">, pub fee_collector: FeeCollector<'b>,
/// Instruction reflection account (special sysvar)
pub instruction_acc: Info<'b>,
pub clock: Sysvar<'b, Clock>, pub clock: Sysvar<'b, Clock>,
} }
impl<'b> InstructionContext<'b> for PostMessage<'b> { impl<'b> InstructionContext<'b> for PostMessage<'b> {
fn verify(&self, program_id: &Pubkey) -> Result<()> { fn verify(&self, program_id: &Pubkey) -> Result<()> {
self.message.verify_derivation(program_id, &self.into())?;
self.sequence.verify_derivation(program_id, &self.into())?; self.sequence.verify_derivation(program_id, &self.into())?;
Ok(()) Ok(())
} }
@ -87,8 +72,6 @@ pub struct PostMessageData {
pub nonce: u32, pub nonce: u32,
/// message payload /// message payload
pub payload: Vec<u8>, pub payload: Vec<u8>,
/// Emitter address
pub emitter: Pubkey,
} }
pub fn post_message( pub fn post_message(
@ -96,6 +79,15 @@ pub fn post_message(
accs: &mut PostMessage, accs: &mut PostMessage,
data: PostMessageData, data: PostMessageData,
) -> Result<()> { ) -> Result<()> {
let msg_derivation = MessageDerivationData {
emitter_key: accs.emitter.key.to_bytes(),
emitter_chain: 1,
nonce: data.nonce,
payload: data.payload.clone(),
};
accs.message
.verify_derivation(ctx.program_id, &msg_derivation)?;
// Fee handling // Fee handling
let fee = transfer_fee(); let fee = transfer_fee();
if accs if accs
@ -115,18 +107,18 @@ pub fn post_message(
.create(&(&*accs).into(), ctx, accs.payer.key, Exempt)?; .create(&(&*accs).into(), ctx, accs.payer.key, Exempt)?;
} }
// Create message account
accs.message
.create(&(&*accs).into(), ctx, accs.payer.key, Exempt)?;
// Initialize transfer // Initialize transfer
accs.message.submission_time = accs.clock.unix_timestamp as u32; accs.message.submission_time = accs.clock.unix_timestamp as u32;
accs.message.emitter_chain = 1; accs.message.emitter_chain = 1;
accs.message.emitter_address = accs.emitter.key.to_bytes(); accs.message.emitter_address = accs.emitter.key.to_bytes();
accs.message.nonce = data.nonce; accs.message.nonce = data.nonce;
accs.message.payload = data.payload.clone(); accs.message.payload = data.payload;
accs.message.sequence = accs.sequence.sequence; accs.message.sequence = accs.sequence.sequence;
// Create message account
accs.message
.create(&msg_derivation, ctx, accs.payer.key, Exempt)?;
// Bump sequence number // Bump sequence number
accs.sequence.sequence += 1; accs.sequence.sequence += 1;

View File

@ -49,15 +49,6 @@ impl<'a> From<&PostVAA<'a>> for SignaturesSetDerivationData {
} }
} }
impl From<&PostVAAData> for MessageDerivationData {
fn from(data: &PostVAAData) -> Self {
MessageDerivationData {
emitter_key: data.emitter_address,
sequence: data.sequence,
}
}
}
impl From<&PostVAAData> for GuardianSetDerivationData { impl From<&PostVAAData> for GuardianSetDerivationData {
fn from(data: &PostVAAData) -> Self { fn from(data: &PostVAAData) -> Self {
GuardianSetDerivationData { GuardianSetDerivationData {
@ -131,8 +122,14 @@ pub struct PostVAAData {
} }
pub fn post_vaa(ctx: &ExecutionContext, accs: &mut PostVAA, vaa: PostVAAData) -> Result<()> { pub fn post_vaa(ctx: &ExecutionContext, accs: &mut PostVAA, vaa: PostVAAData) -> Result<()> {
let msg_derivation = MessageDerivationData {
emitter_key: vaa.emitter_address,
emitter_chain: vaa.emitter_chain,
nonce: vaa.nonce,
payload: vaa.payload.clone(),
};
accs.message accs.message
.verify_derivation(ctx.program_id, &(&vaa).into())?; .verify_derivation(ctx.program_id, &msg_derivation)?;
accs.guardian_set accs.guardian_set
.verify_derivation(ctx.program_id, &(&vaa).into())?; .verify_derivation(ctx.program_id, &(&vaa).into())?;
// Verify any required invariants before we process the instruction. // Verify any required invariants before we process the instruction.
@ -166,14 +163,13 @@ pub fn post_vaa(ctx: &ExecutionContext, accs: &mut PostVAA, vaa: PostVAAData) ->
// If the VAA originates from another chain we need to create the account and populate all fields // If the VAA originates from another chain we need to create the account and populate all fields
if vaa.emitter_chain != 1 { if vaa.emitter_chain != 1 {
accs.message
.create(&(&vaa).into(), ctx, accs.payer.key, Exempt)?;
accs.message.nonce = vaa.nonce; accs.message.nonce = vaa.nonce;
accs.message.emitter_chain = vaa.emitter_chain; accs.message.emitter_chain = vaa.emitter_chain;
accs.message.emitter_address = vaa.emitter_address; accs.message.emitter_address = vaa.emitter_address;
accs.message.sequence = vaa.sequence; accs.message.sequence = vaa.sequence;
accs.message.payload = vaa.payload; accs.message.payload = vaa.payload;
accs.message
.create(&msg_derivation, ctx, accs.payer.key, Exempt)?;
} }
// Store VAA data in associated message. // Store VAA data in associated message.

View File

@ -1,6 +1,7 @@
// #![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))] // #![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]
#![feature(const_generics)] #![feature(const_generics)]
#![allow(warnings)] #![allow(warnings)]
// Salt contains the framework definition, single file for now but to be extracted into a cargo // Salt contains the framework definition, single file for now but to be extracted into a cargo
// package as soon as possible. // package as soon as possible.
pub mod accounts; pub mod accounts;

View File

@ -22,9 +22,12 @@ use solitaire::{
}, },
SolitaireError, SolitaireError,
}; };
use std::io::{ use std::{
Cursor, io::{
Read, Cursor,
Read,
},
str::FromStr,
}; };
#[derive(Default, BorshSerialize, BorshDeserialize)] #[derive(Default, BorshSerialize, BorshDeserialize)]
@ -101,7 +104,7 @@ impl Owned for SignatureSet {
} }
} }
#[derive(Default, BorshSerialize, BorshDeserialize)] #[derive(Default, BorshSerialize, BorshDeserialize, Clone)]
pub struct PostedMessage { pub struct PostedMessage {
/// Header of the posted VAA /// Header of the posted VAA
pub vaa_version: u8, pub vaa_version: u8,
@ -133,7 +136,9 @@ pub struct PostedMessage {
impl Owned for PostedMessage { impl Owned for PostedMessage {
fn owner(&self) -> AccountOwner { fn owner(&self) -> AccountOwner {
AccountOwner::This AccountOwner::Other(
Pubkey::from_str("96RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE").unwrap(),
) // TODO key of the bridge
} }
} }

View File

@ -1,4 +1,8 @@
use crate::{ use crate::{
accounts::{
Claim,
ClaimDerivationData,
},
types::{ types::{
ClaimData, ClaimData,
PostedMessage, PostedMessage,
@ -115,21 +119,13 @@ impl<'b, T: DeserializePayload> PayloadMessage<'b, T> {
} }
} }
data_wrapper!(Claim, ClaimData, AccountState::Uninitialized);
impl<'b, T: DeserializePayload> Seeded<&ClaimableVAA<'b, T>> for Claim<'b> {
fn seeds(_accs: &ClaimableVAA<'b, T>) -> Vec<Vec<u8>> {
return vec![];
}
}
#[derive(FromAccounts)] #[derive(FromAccounts)]
pub struct ClaimableVAA<'b, T: DeserializePayload> { pub struct ClaimableVAA<'b, T: DeserializePayload> {
// Signed message for the transfer // Signed message for the transfer
pub message: PayloadMessage<'b, T>, // TODO use bridge type here that does verifications pub message: PayloadMessage<'b, T>, // TODO use bridge type here that does verifications
// Claim account to prevent double spending // Claim account to prevent double spending
pub claim: Claim<'b>, pub claim: Claim<'b, { AccountState::Uninitialized }>,
} }
impl<'b, T: DeserializePayload> Deref for ClaimableVAA<'b, T> { impl<'b, T: DeserializePayload> Deref for ClaimableVAA<'b, T> {
@ -144,7 +140,14 @@ impl<'b, T: DeserializePayload> InstructionContext<'b> for ClaimableVAA<'b, T> {
// Do the Posted Message verification // Do the Posted Message verification
// Verify that the claim account is derived correctly // Verify that the claim account is derived correctly
self.claim.verify_derivation(program_id, self)?; self.claim.verify_derivation(
program_id,
&ClaimDerivationData {
emitter_address: self.message.meta().emitter_address,
emitter_chain: self.message.meta().emitter_chain,
sequence: self.message.meta().sequence,
},
)?;
Ok(()) Ok(())
} }
@ -160,7 +163,16 @@ impl<'b, T: DeserializePayload> ClaimableVAA<'b, T> {
return Err(VAAAlreadyExecuted.into()); return Err(VAAAlreadyExecuted.into());
} }
self.claim.create(self, ctx, payer, Exempt)?; self.claim.create(
&ClaimDerivationData {
emitter_address: self.message.meta().emitter_address,
emitter_chain: self.message.meta().emitter_chain,
sequence: self.message.meta().sequence,
},
ctx,
payer,
Exempt,
)?;
self.claim.claimed = true; self.claim.claimed = true;
Ok(()) Ok(())

View File

@ -81,6 +81,7 @@ macro_rules! solitaire {
} }
use instruction::solitaire; use instruction::solitaire;
#[cfg(not(feature = "no-entrypoint"))]
solana_program::entrypoint!(solitaire); solana_program::entrypoint!(solitaire);
} }
} }
@ -153,10 +154,14 @@ macro_rules! pack_type {
impl BorshDeserialize for $name { impl BorshDeserialize for $name {
fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> { fn deserialize(buf: &mut &[u8]) -> std::io::Result<Self> {
Ok($name( let acc = $name(
solana_program::program_pack::Pack::unpack(buf) solana_program::program_pack::Pack::unpack(buf)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?,
)) );
// We need to clear the buf to show to Borsh that we've read all data
*buf = &buf[..0];
Ok(acc)
} }
} }

View File

@ -22,6 +22,8 @@ use borsh::{
BorshSerialize, BorshSerialize,
}; };
use solana_program::{ use solana_program::{
entrypoint::ProgramResult,
instruction::Instruction,
program::invoke_signed, program::invoke_signed,
pubkey::Pubkey, pubkey::Pubkey,
}; };
@ -82,6 +84,10 @@ pub trait Seeded<I> {
seeds seeds
} }
fn self_bumped_seeds(&self, accs: I, program_id: &Pubkey) -> Vec<Vec<u8>> {
Self::bumped_seeds(accs, program_id)
}
fn verify_derivation<'a, 'b: 'a>(&'a self, program_id: &'a Pubkey, accs: I) -> Result<()> fn verify_derivation<'a, 'b: 'a>(&'a self, program_id: &'a Pubkey, accs: I) -> Result<()>
where where
Self: Keyed<'a, 'b>, Self: Keyed<'a, 'b>,
@ -147,3 +153,15 @@ impl<'a, const Seed: &'static str, T> Seeded<Option<()>> for Derive<T, Seed> {
vec![Seed.as_bytes().to_vec()] vec![Seed.as_bytes().to_vec()]
} }
} }
pub fn invoke_seeded<I, T: Seeded<I>>(
instruction: &Instruction,
context: &ExecutionContext,
seeded_acc: &T,
accs: I,
) -> ProgramResult {
let seeds = seeded_acc.self_bumped_seeds(accs, context.program_id);
let s: Vec<&[u8]> = seeds.iter().map(|item| item.as_slice()).collect();
let seed_slice = s.as_slice();
invoke_signed(instruction, context.accounts, &[seed_slice])
}

View File

@ -19,6 +19,7 @@ use proc_macro2::{
use quote::{ use quote::{
quote, quote,
quote_spanned, quote_spanned,
ToTokens,
}; };
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
use syn::{ use syn::{
@ -162,9 +163,12 @@ fn generate_fields(name: &syn::Ident, data: &Data) -> TokenStream2 {
let recurse = fields.named.iter().map(|f| { let recurse = fields.named.iter().map(|f| {
// Field name, to assign to. // Field name, to assign to.
let name = &f.ident; let name = &f.ident;
let name_string =
format!("Peeling: {}", name.to_token_stream().to_string());
let ty = &f.ty; let ty = &f.ty;
quote! { quote! {
solana_program::msg!(#name_string);
let #name: #ty = solitaire::Peel::peel(&mut solitaire::Context::new( let #name: #ty = solitaire::Peel::peel(&mut solitaire::Context::new(
pid, pid,
iter, iter,

View File

@ -51,11 +51,11 @@ pub fn generate_to_instruction(
quote! { quote! {
/// Solitaire-generated client-side #name representation /// Solitaire-generated client-side #name representation
#[cfg(feature = "no-entrypoint")] #[cfg(feature = "client")]
#client_struct_decl #client_struct_decl
/// Solitaire-generatied ToInstruction implementation /// Solitaire-generatied ToInstruction implementation
#[cfg(feature = "no-entrypoint")] #[cfg(feature = "client")]
impl #impl_generics solitaire_client::ToInstruction for #client_struct_name { impl #impl_generics solitaire_client::ToInstruction for #client_struct_name {
fn to_ix( fn to_ix(
self, self,

View File

@ -156,16 +156,18 @@ where
{ {
fn wrap(a: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox> { fn wrap(a: &AccEntry) -> StdResult<Vec<AccountMeta>, ErrBox> {
if let AccEntry::Sysvar(k) = a { if let AccEntry::Sysvar(k) = a {
if Var::check_id(k) { if Var::check_id(k) {
Ok(vec![AccountMeta::new_readonly(k.clone(), false)]) Ok(vec![AccountMeta::new_readonly(k.clone(), false)])
} else { } else {
Err(format!("{} does not point at sysvar {}", k, std::any::type_name::<Var>()).into()) Err(format!(
} "{} does not point at sysvar {}",
k,
std::any::type_name::<Var>()
)
.into())
}
} else { } else {
Err(format!( Err(format!("{} must be passed as Sysvar", std::any::type_name::<Self>()).into())
"{} must be passed as Sysvar",
std::any::type_name::<Self>()
).into())
} }
} }
} }