Fix protocol compatibility and increase VAA test coverage

This commit is contained in:
Hendrik Hofstadt 2020-08-05 12:28:44 +02:00
parent a1ba223361
commit dece1a6a90
14 changed files with 546 additions and 253 deletions

View File

@ -63,6 +63,8 @@ type (
}
BodyTransfer struct {
// Nonce is a user given unique nonce for this transfer
Nonce uint32
// SourceChain is the id of the chain the transfer was initiated from
SourceChain ChainID
// TargetChain is the id of the chain the transfer is directed to
@ -106,7 +108,7 @@ func ParseVAA(data []byte) (*VAA, error) {
}
v.Version = data[0]
if v.Version < supportedVAAVersion {
if v.Version != supportedVAAVersion {
return nil, fmt.Errorf("unsupported VAA version: %d", v.Version)
}
@ -259,6 +261,10 @@ func (v *VAA) serializeBody() ([]byte, error) {
func parseBodyTransfer(r io.Reader) (*BodyTransfer, error) {
b := &BodyTransfer{}
if err := binary.Read(r, binary.BigEndian, &b.Nonce); err != nil {
return nil, fmt.Errorf("failed to read nonce: %w", err)
}
if err := binary.Read(r, binary.BigEndian, &b.SourceChain); err != nil {
return nil, fmt.Errorf("failed to read source chain: %w", err)
}
@ -294,6 +300,7 @@ func (v *BodyTransfer) getActionID() Action {
func (v *BodyTransfer) serialize() ([]byte, error) {
buf := new(bytes.Buffer)
MustWrite(buf, binary.BigEndian, v.Nonce)
MustWrite(buf, binary.BigEndian, v.SourceChain)
MustWrite(buf, binary.BigEndian, v.TargetChain)
buf.Write(v.TargetAddress[:])

View File

@ -20,7 +20,7 @@ func TestSerializeDeserialize(t *testing.T) {
{
name: "BodyTransfer",
vaa: &VAA{
Version: 8,
Version: 1,
GuardianSetIndex: 9,
Signature: &Signature{
Sig: [32]byte{2, 8},
@ -28,6 +28,7 @@ func TestSerializeDeserialize(t *testing.T) {
},
Timestamp: time.Unix(2837, 0),
Payload: &BodyTransfer{
Nonce: 38,
SourceChain: 2,
TargetChain: 1,
TargetAddress: Address{2, 1, 3},
@ -42,7 +43,7 @@ func TestSerializeDeserialize(t *testing.T) {
{
name: "GuardianSetUpdate",
vaa: &VAA{
Version: 8,
Version: 1,
GuardianSetIndex: 9,
Signature: &Signature{
Sig: [32]byte{2, 8},

View File

@ -177,6 +177,7 @@ Size: `75 byte`
Payload:
```
uint32 nonce
uint8 source_chain
uint8 target_chain
[32]uint8 target_address

104
solana/bridge/Cargo.lock generated
View File

@ -34,10 +34,10 @@ dependencies = [
]
[[package]]
name = "arrayref"
version = "0.3.6"
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
[[package]]
name = "assert_matches"
@ -99,6 +99,16 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitvec"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c"
dependencies = [
"either",
"radium",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
@ -158,6 +168,12 @@ dependencies = [
"serde",
]
[[package]]
name = "byte-slice-cast"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3"
[[package]]
name = "byte-tools"
version = "0.3.1"
@ -304,6 +320,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-mac"
version = "0.7.0"
@ -437,6 +459,18 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
[[package]]
name = "fixed-hash"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c"
dependencies = [
"byteorder",
"rand",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -717,6 +751,15 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "impl-codec"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53"
dependencies = [
"parity-scale-codec",
]
[[package]]
name = "indexmap"
version = "1.5.0"
@ -1014,6 +1057,18 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "parity-scale-codec"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34d38aeaffc032ec69faa476b3caaca8d4dd7f3f798137ff30359e5c7869ceb6"
dependencies = [
"arrayvec",
"bitvec",
"byte-slice-cast",
"serde",
]
[[package]]
name = "parking_lot"
version = "0.9.0"
@ -1094,6 +1149,17 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "primitive-types"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8"
dependencies = [
"fixed-hash",
"impl-codec",
"uint",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.18"
@ -1148,6 +1214,12 @@ dependencies = [
"proc-macro2 1.0.19",
]
[[package]]
name = "radium"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
[[package]]
name = "rand"
version = "0.7.3"
@ -1280,6 +1352,12 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc-hex"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -1565,6 +1643,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
@ -1967,6 +2051,18 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "uint"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "429ffcad8c8c15f874578c7337d156a3727eb4a1c2374c0ae937ad9a9b748c80"
dependencies = [
"byteorder",
"crunchy",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "unicase"
version = "2.6.0"
@ -2216,11 +2312,11 @@ dependencies = [
name = "wormhole-bridge"
version = "0.1.0"
dependencies = [
"arrayref",
"byteorder",
"hex",
"num-derive 0.2.5",
"num-traits",
"primitive-types",
"rand",
"remove_dir_all",
"sha3",

View File

@ -22,10 +22,10 @@ remove_dir_all = "=0.5.0"
solana-sdk = { version = "=1.2.17", default-features = false, optional = true }
spl-token = { package = "spl-token", version = "1.0.6", default-features = false, optional = true }
thiserror = "1.0"
arrayref = "0.3.6"
byteorder = "1.3.4"
zerocopy = "0.3.0"
sha3 = "0.9.1"
primitive-types = "0.7.2"
[dev-dependencies]
rand = { version = "0.7.0" }

View File

@ -6,7 +6,6 @@ use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use solana_sdk::{
decode_error::DecodeError,
info,
program_error::{PrintProgramError, ProgramError},
};
use thiserror::Error;
@ -117,37 +116,3 @@ impl<T> DecodeError<T> for Error {
"Swap Error"
}
}
impl PrintProgramError for Error {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
{
match self {
Error::ExpectedToken => info!("Error: ExpectedToken"),
Error::ExpectedAccount => info!("Error: ExpectedAccount"),
Error::ExpectedBridge => info!("Error: ExpectedBridge"),
Error::ExpectedGuardianSet => info!("Error: ExpectedGuardianSet"),
Error::UninitializedState => info!("Error: State is unititialized"),
Error::InvalidProgramAddress => info!("Error: InvalidProgramAddress"),
Error::InvalidVAAFormat => info!("Error: InvalidVAAFormat"),
Error::InvalidVAAAction => info!("Error: InvalidVAAAction"),
Error::InvalidVAASignature => info!("Error: InvalidVAASignature"),
Error::AlreadyExists => info!("Error: AlreadyExists"),
Error::InvalidDerivedAccount => info!("Error: InvalidDerivedAccount"),
Error::TokenMintMismatch => info!("Error: TokenMintMismatch"),
Error::WrongMintOwner => info!("Error: WrongMintOwner"),
Error::WrongTokenAccountOwner => info!("Error: WrongTokenAccountOwner"),
Error::ParseFailed => info!("Error: ParseFailed"),
Error::GuardianSetExpired => info!("Error: GuardianSetExpired"),
Error::VAAExpired => info!("Error: VAAExpired"),
Error::VAAClaimed => info!("Error: VAAClaimed"),
Error::WrongBridgeOwner => info!("Error: WrongBridgeOwner"),
Error::OldGuardianSet => info!("Error: OldGuardianSet"),
Error::GuardianIndexNotIncreasing => info!("Error: GuardianIndexNotIncreasing"),
Error::ExpectedTransferOutProposal => info!("Error: ExpectedTransferOutProposal"),
Error::VAAProposalMismatch => info!("Error: VAAProposalMismatch"),
Error::SameChainTransfer => info!("Error: SameChainTransfer"),
}
}
}

View File

@ -0,0 +1,49 @@
#![cfg(feature = "program")]
use std::io;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use solana_sdk::{
decode_error::DecodeError,
info,
program_error::{PrintProgramError, ProgramError},
};
use thiserror::Error;
use crate::error;
use crate::error::*;
impl PrintProgramError for Error {
fn print<E>(&self)
where
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
{
match self {
Error::ExpectedToken => info!("Error: ExpectedToken"),
Error::ExpectedAccount => info!("Error: ExpectedAccount"),
Error::ExpectedBridge => info!("Error: ExpectedBridge"),
Error::ExpectedGuardianSet => info!("Error: ExpectedGuardianSet"),
Error::UninitializedState => info!("Error: State is unititialized"),
Error::InvalidProgramAddress => info!("Error: InvalidProgramAddress"),
Error::InvalidVAAFormat => info!("Error: InvalidVAAFormat"),
Error::InvalidVAAAction => info!("Error: InvalidVAAAction"),
Error::InvalidVAASignature => info!("Error: InvalidVAASignature"),
Error::AlreadyExists => info!("Error: AlreadyExists"),
Error::InvalidDerivedAccount => info!("Error: InvalidDerivedAccount"),
Error::TokenMintMismatch => info!("Error: TokenMintMismatch"),
Error::WrongMintOwner => info!("Error: WrongMintOwner"),
Error::WrongTokenAccountOwner => info!("Error: WrongTokenAccountOwner"),
Error::ParseFailed => info!("Error: ParseFailed"),
Error::GuardianSetExpired => info!("Error: GuardianSetExpired"),
Error::VAAExpired => info!("Error: VAAExpired"),
Error::VAAClaimed => info!("Error: VAAClaimed"),
Error::WrongBridgeOwner => info!("Error: WrongBridgeOwner"),
Error::OldGuardianSet => info!("Error: OldGuardianSet"),
Error::GuardianIndexNotIncreasing => info!("Error: GuardianIndexNotIncreasing"),
Error::ExpectedTransferOutProposal => info!("Error: ExpectedTransferOutProposal"),
Error::VAAProposalMismatch => info!("Error: VAAProposalMismatch"),
Error::SameChainTransfer => info!("Error: SameChainTransfer"),
}
}
}

View File

@ -1,5 +1,4 @@
#![allow(clippy::too_many_arguments)]
//! Instruction types
use std::io::Write;
@ -132,7 +131,6 @@ impl BridgeInstruction {
Self::EvictExecutedVAA() => {
output[0] = 4;
}
_ => panic!(""),
}
Ok(output)
}

View File

@ -1,10 +1,9 @@
#[macro_use]
extern crate arrayref;
#[macro_use]
extern crate zerocopy;
pub mod entrypoint;
pub mod error;
pub mod error_program;
pub mod instruction;
pub mod processor;
pub mod state;

View File

@ -1,4 +1,5 @@
//! Program instruction processing logic
#![cfg(feature = "program")]
use std::io::Write;
use std::mem::size_of;
@ -553,7 +554,7 @@ impl Bridge {
b.target_chain,
b.target_address,
b.source_address,
b.ref_block,
b.nonce,
)?;
if expected_proposal != *proposal_info.key {
return Err(Error::InvalidDerivedAccount.into());
@ -611,6 +612,158 @@ pub fn invoke_signed<'a>(
}
}
/// Implementation of actions
impl Bridge {
/// Burn a wrapped asset from account
pub fn wrapped_burn(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
authority: &Pubkey,
token_account: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let all_signers: Vec<&Pubkey> = accounts
.iter()
.filter_map(|item| if item.is_signer { Some(item.key) } else { None })
.collect();
let ix = spl_token::instruction::burn(
token_program_id,
token_account,
authority,
all_signers.as_slice(),
amount,
)?;
invoke_signed(&ix, accounts, &[])
}
/// Mint a wrapped asset to account
pub fn wrapped_mint_to(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
bridge: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let ix = spl_token::instruction::mint_to(
token_program_id,
mint,
destination,
bridge,
&[],
amount,
)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Transfer tokens from a caller
pub fn token_transfer_caller(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
source: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let all_signers: Vec<&Pubkey> = accounts
.iter()
.filter_map(|item| if item.is_signer { Some(item.key) } else { None })
.collect();
let ix = spl_token::instruction::transfer(
token_program_id,
source,
destination,
authority,
all_signers.as_slice(),
amount,
)?;
invoke_signed(&ix, accounts, &[])
}
/// Transfer tokens from a custody account
pub fn token_transfer_custody(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
bridge: &Pubkey,
source: &Pubkey,
destination: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let ix = spl_token::instruction::transfer(
token_program_id,
source,
destination,
bridge,
&[],
amount,
)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a new account
pub fn create_custody_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program: &Pubkey,
bridge: &Pubkey,
account: &Pubkey,
mint: &Pubkey,
payer: &Pubkey,
) -> Result<(), ProgramError> {
Self::create_account::<Mint>(
program_id,
accounts,
mint,
payer,
Self::derive_custody_seeds(bridge, mint),
)?;
let ix = spl_token::instruction::initialize_account(token_program, account, mint, bridge)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a mint for a wrapped asset
pub fn create_wrapped_mint(
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program: &Pubkey,
mint: &Pubkey,
bridge: &Pubkey,
payer: &Pubkey,
asset: &AssetMeta,
) -> Result<(), ProgramError> {
Self::create_account::<Mint>(
program_id,
accounts,
mint,
payer,
Self::derive_wrapped_asset_seeds(bridge, asset.chain, asset.address),
)?;
let ix =
spl_token::instruction::initialize_mint(token_program, mint, None, Some(bridge), 0, 8)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a new account
pub fn create_account<T: Sized>(
program_id: &Pubkey,
accounts: &[AccountInfo],
new_account: &Pubkey,
payer: &Pubkey,
seeds: Vec<Vec<u8>>,
) -> Result<(), ProgramError> {
let size = size_of::<T>();
let ix = create_account(
payer,
new_account,
Rent::default().minimum_balance(size as usize),
size as u64,
program_id,
);
let s: Vec<_> = seeds.iter().map(|item| item.as_slice()).collect();
invoke_signed(&ix, accounts, &[s.as_slice()])
}
}
#[cfg(test)]
mod tests {
use solana_sdk::{

View File

@ -11,14 +11,13 @@ use solana_sdk::clock::Clock;
use solana_sdk::hash::hash;
#[cfg(not(target_arch = "bpf"))]
use solana_sdk::instruction::Instruction;
use solana_sdk::log::sol_log;
#[cfg(target_arch = "bpf")]
use solana_sdk::program::invoke_signed;
use solana_sdk::rent::Rent;
use solana_sdk::system_instruction::{create_account, SystemInstruction};
use solana_sdk::sysvar::Sysvar;
use solana_sdk::{
account_info::next_account_info, account_info::AccountInfo, entrypoint::ProgramResult, info,
account_info::next_account_info, account_info::AccountInfo, entrypoint::ProgramResult,
program_error::ProgramError, pubkey::bs58, pubkey::Pubkey,
};
use spl_token::state::Mint;
@ -27,12 +26,11 @@ use crate::instruction::BridgeInstruction::*;
use crate::instruction::{
BridgeInstruction, ForeignAddress, GuardianKey, TransferOutPayload, CHAIN_ID_SOLANA, VAA_BODY,
};
#[cfg(not(target_arch = "bpf"))]
use crate::processor::invoke_signed;
use crate::syscalls::{sol_verify_schnorr, RawKey, SchnorrifyInput};
use crate::vaa::{BodyTransfer, BodyUpdateGuardianSet, VAABody, VAA};
use crate::{error::Error, instruction::unpack};
use primitive_types::U256;
use zerocopy::AsBytes;
/// fee rate as a ratio
@ -73,7 +71,7 @@ impl IsInitialized for GuardianSet {
#[derive(Clone, Copy)]
pub struct TransferOutProposal {
/// amount to transfer
pub amount: u64,
pub amount: U256,
/// chain id to transfer to
pub to_chain_id: u8,
/// address on the foreign chain to transfer to
@ -219,158 +217,6 @@ impl Bridge {
}
}
/// Implementation of actions
impl Bridge {
/// Burn a wrapped asset from account
pub fn wrapped_burn(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
authority: &Pubkey,
token_account: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let all_signers: Vec<&Pubkey> = accounts
.iter()
.filter_map(|item| if item.is_signer { Some(item.key) } else { None })
.collect();
let ix = spl_token::instruction::burn(
token_program_id,
token_account,
authority,
all_signers.as_slice(),
amount,
)?;
invoke_signed(&ix, accounts, &[])
}
/// Mint a wrapped asset to account
pub fn wrapped_mint_to(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
bridge: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let ix = spl_token::instruction::mint_to(
token_program_id,
mint,
destination,
bridge,
&[],
amount,
)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Transfer tokens from a caller
pub fn token_transfer_caller(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
source: &Pubkey,
destination: &Pubkey,
authority: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let all_signers: Vec<&Pubkey> = accounts
.iter()
.filter_map(|item| if item.is_signer { Some(item.key) } else { None })
.collect();
let ix = spl_token::instruction::transfer(
token_program_id,
source,
destination,
authority,
all_signers.as_slice(),
amount,
)?;
invoke_signed(&ix, accounts, &[])
}
/// Transfer tokens from a custody account
pub fn token_transfer_custody(
accounts: &[AccountInfo],
token_program_id: &Pubkey,
bridge: &Pubkey,
source: &Pubkey,
destination: &Pubkey,
amount: u64,
) -> Result<(), ProgramError> {
let ix = spl_token::instruction::transfer(
token_program_id,
source,
destination,
bridge,
&[],
amount,
)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a new account
pub fn create_custody_account(
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program: &Pubkey,
bridge: &Pubkey,
account: &Pubkey,
mint: &Pubkey,
payer: &Pubkey,
) -> Result<(), ProgramError> {
Self::create_account::<Mint>(
program_id,
accounts,
mint,
payer,
Self::derive_custody_seeds(bridge, mint),
)?;
let ix = spl_token::instruction::initialize_account(token_program, account, mint, bridge)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a mint for a wrapped asset
pub fn create_wrapped_mint(
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program: &Pubkey,
mint: &Pubkey,
bridge: &Pubkey,
payer: &Pubkey,
asset: &AssetMeta,
) -> Result<(), ProgramError> {
Self::create_account::<Mint>(
program_id,
accounts,
mint,
payer,
Self::derive_wrapped_asset_seeds(bridge, asset.chain, asset.address),
)?;
let ix =
spl_token::instruction::initialize_mint(token_program, mint, None, Some(bridge), 0, 8)?;
invoke_signed(&ix, accounts, &[&[&bridge.to_bytes()[..32]][..]])
}
/// Create a new account
pub fn create_account<T: Sized>(
program_id: &Pubkey,
accounts: &[AccountInfo],
new_account: &Pubkey,
payer: &Pubkey,
seeds: Vec<Vec<u8>>,
) -> Result<(), ProgramError> {
let size = size_of::<T>();
let ix = create_account(
payer,
new_account,
Rent::default().minimum_balance(size as usize),
size as u64,
program_id,
);
let s: Vec<_> = seeds.iter().map(|item| item.as_slice()).collect();
invoke_signed(&ix, accounts, &[s.as_slice()])
}
}
/// Implementation of derivations
impl Bridge {
/// Calculates derived seeds for a guardian set
@ -404,7 +250,7 @@ impl Bridge {
target_chain: u8,
target_address: ForeignAddress,
user: ForeignAddress,
slot: u64,
slot: u32,
) -> Vec<Vec<u8>> {
vec![
"transfer".as_bytes().to_vec(),
@ -498,7 +344,7 @@ impl Bridge {
target_chain: u8,
target_address: ForeignAddress,
user: ForeignAddress,
slot: u64,
slot: u32,
) -> Result<Pubkey, Error> {
Self::derive_key(
program_id,

View File

@ -10,11 +10,16 @@ pub struct SchnorrifyInput {
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub struct RawKey {
pub x: [u8; 32],
pub y: [u8; 32],
pub y_parity: bool,
}
impl SchnorrifyInput {
pub fn new(pub_key: RawKey, message: [u8; 32], signature: [u8; 32], addr: [u8; 20]) -> SchnorrifyInput {
pub fn new(
pub_key: RawKey,
message: [u8; 32],
signature: [u8; 32],
addr: [u8; 20],
) -> SchnorrifyInput {
SchnorrifyInput {
message,
addr,
@ -29,9 +34,7 @@ impl SchnorrifyInput {
/// @param input - Input for signature verification
#[inline]
pub fn sol_verify_schnorr(input: &SchnorrifyInput) -> bool {
let res = unsafe {
sol_verify_ethschnorr(input as *const _ as *const u8)
};
let res = unsafe { sol_verify_ethschnorr(input as *const _ as *const u8) };
res == 1
}

View File

@ -3,14 +3,14 @@ use std::io::{Cursor, Read, Write};
use std::mem::size_of;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use primitive_types::U256;
use sha3::Digest;
use solana_sdk::program_error::ProgramError;
use crate::error::Error;
use crate::error::Error::InvalidVAAFormat;
use crate::instruction::unpack;
use crate::state::AssetMeta;
use crate::syscalls::{RawKey, SchnorrifyInput, sol_verify_schnorr};
use crate::syscalls::{sol_verify_schnorr, RawKey, SchnorrifyInput};
use crate::vaa::VAABody::UpdateGuardianSet;
pub type ForeignAddress = [u8; 32];
@ -42,16 +42,20 @@ impl VAA {
pub fn verify(&self, guardian_key: &RawKey) -> bool {
let body = match self.signature_body() {
Ok(v) => { v }
Err(_) => { return false; }
Ok(v) => v,
Err(_) => {
return false;
}
};
let mut h = sha3::Keccak256::default();
if let Err(_) = h.write(body.as_slice()) { return false; };
if let Err(_) = h.write(body.as_slice()) {
return false;
};
let hash = h.finalize().into();
let schnorr_input = SchnorrifyInput::new(*guardian_key, hash,
self.signature_sig, self.signature_addr);
let schnorr_input =
SchnorrifyInput::new(*guardian_key, hash, self.signature_sig, self.signature_addr);
sol_verify_schnorr(&schnorr_input)
}
@ -68,6 +72,7 @@ impl VAA {
v.write_u8(payload.action_id())?;
let payload_data = payload.serialize()?;
v.write_u8(payload_data.len() as u8)?;
v.write(payload_data.as_slice())?;
Ok(v.into_inner())
@ -124,14 +129,13 @@ impl VAABody {
fn deserialize(data: &Vec<u8>) -> Result<VAABody, Error> {
let mut payload_data = Cursor::new(data);
let action = payload_data.read_u8()?;
let length = payload_data.read_u8()?;
let payload = match action {
0x01 => {
VAABody::UpdateGuardianSet(BodyUpdateGuardianSet::deserialize(&mut payload_data)?)
}
0x10 => {
VAABody::Transfer(BodyTransfer::deserialize(&mut payload_data)?)
}
0x10 => VAABody::Transfer(BodyTransfer::deserialize(&mut payload_data)?),
_ => {
return Err(Error::InvalidVAAAction);
}
@ -142,12 +146,8 @@ impl VAABody {
fn serialize(&self) -> Result<Vec<u8>, Error> {
match self {
VAABody::Transfer(b) => {
b.serialize()
}
VAABody::UpdateGuardianSet(b) => {
b.serialize()
}
VAABody::Transfer(b) => b.serialize(),
VAABody::UpdateGuardianSet(b) => b.serialize(),
}
}
}
@ -160,37 +160,45 @@ pub struct BodyUpdateGuardianSet {
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct BodyTransfer {
pub ref_block: u64,
pub nonce: u32,
pub source_chain: u8,
pub source_address: ForeignAddress,
pub target_chain: u8,
pub target_address: ForeignAddress,
pub asset: AssetMeta,
pub amount: u64,
pub amount: U256,
}
impl BodyUpdateGuardianSet {
fn deserialize(data: &mut Cursor<&Vec<u8>>) -> Result<BodyUpdateGuardianSet, Error> {
let new_index = data.read_u32::<BigEndian>()?;
let mut new_key_x: [u8; 32] = [0; 32];
data.read(&mut new_key_x)?;
let mut new_key_y: [u8; 32] = [0; 32];
data.read(&mut new_key_y)?;
let new_key_y_parity = match data.read_u8()? {
0 => false,
1 => true,
_ => return Err(InvalidVAAFormat),
};
let new_index = data.read_u32::<BigEndian>()?;
Ok(BodyUpdateGuardianSet {
new_index,
new_key: RawKey {
x: new_key_x,
y: new_key_y,
y_parity: new_key_y_parity,
},
})
}
fn serialize(&self) -> Result<Vec<u8>, Error> {
let mut v: Cursor<Vec<u8>> = Cursor::new(Vec::new());
v.write_u32::<BigEndian>(self.new_index)?;
v.write(&self.new_key.x)?;
v.write(&self.new_key.y)?;
v.write_u8({
match self.new_key.y_parity {
false => 0,
true => 1,
}
})?;
v.write_u32::<BigEndian>(self.new_index)?;
Ok(v.into_inner())
}
@ -198,26 +206,26 @@ impl BodyUpdateGuardianSet {
impl BodyTransfer {
fn deserialize(data: &mut Cursor<&Vec<u8>>) -> Result<BodyTransfer, Error> {
let ref_block = data.read_u64::<BigEndian>()?;
let nonce = data.read_u32::<BigEndian>()?;
let source_chain = data.read_u8()?;
let mut source_address: ForeignAddress = ForeignAddress::default();
data.read(&mut source_address)?;
let target_chain = data.read_u8()?;
let mut target_address: ForeignAddress = ForeignAddress::default();
data.read(&mut target_address)?;
data.read_exact(&mut target_address)?;
let token_chain = data.read_u8()?;
let mut token_address: ForeignAddress = ForeignAddress::default();
data.read(&mut token_address)?;
let amount = data.read_u64::<BigEndian>()?;
data.read_exact(&mut token_address)?;
let mut am_data: [u8; 32] = [0; 32];
data.read_exact(&mut am_data)?;
let amount = U256::from_big_endian(&am_data);
Ok(BodyTransfer {
ref_block,
nonce,
source_chain,
source_address,
target_chain,
target_address,
asset: AssetMeta {
address: target_address,
address: token_address,
chain: token_chain,
},
amount,
@ -226,12 +234,16 @@ impl BodyTransfer {
fn serialize(&self) -> Result<Vec<u8>, Error> {
let mut v: Cursor<Vec<u8>> = Cursor::new(Vec::new());
v.write_u32::<BigEndian>(self.nonce)?;
v.write_u8(self.source_chain)?;
v.write_u8(self.target_chain)?;
v.write(&self.target_address)?;
v.write_u8(self.asset.chain)?;
v.write(&self.asset.address)?;
v.write_u64::<BigEndian>(self.amount)?;
let mut am_data: [u8; 32] = [0; 32];
self.amount.to_big_endian(&mut am_data);
v.write(&am_data[..])?;
Ok(v.into_inner())
}
@ -242,11 +254,12 @@ mod tests {
use std::io::Write;
use hex;
use primitive_types::U256;
use crate::error::Error;
use crate::state::AssetMeta;
use crate::syscalls::RawKey;
use crate::vaa::{BodyTransfer, BodyUpdateGuardianSet, VAA, VAABody};
use crate::vaa::{BodyTransfer, BodyUpdateGuardianSet, VAABody, VAA};
#[test]
fn serialize_deserialize_vaa_transfer() {
@ -257,16 +270,15 @@ mod tests {
signature_addr: [9; 20],
timestamp: 83,
payload: Some(VAABody::Transfer(BodyTransfer {
ref_block: 28,
nonce: 28,
source_chain: 1,
source_address: [9; 32],
target_chain: 2,
target_address: [1; 32],
asset: AssetMeta {
address: [2; 32],
chain: 8,
},
amount: 4,
amount: U256::from(3),
})),
};
@ -287,7 +299,7 @@ mod tests {
new_index: 29,
new_key: RawKey {
x: [2; 32],
y: [3; 32],
y_parity: true,
},
})),
};
@ -296,4 +308,71 @@ mod tests {
let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap();
assert_eq!(vaa, parsed_vaa)
}
#[test]
fn parse_given_guardian_set_update() {
let vaa = VAA {
version: 1,
guardian_set_index: 9,
signature_sig: [
2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
],
signature_addr: [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
timestamp: 2837,
payload: Some(VAABody::UpdateGuardianSet(BodyUpdateGuardianSet {
new_index: 2,
new_key: RawKey {
x: [
34, 23, 130, 103, 189, 101, 144, 104, 196, 19, 115, 119, 37, 80, 123, 46,
218, 191, 167, 75, 3, 40, 130, 168, 218, 203, 128, 99, 120, 238, 102, 1,
],
y_parity: true,
},
})),
};
let data = hex::decode("01000000090208000000000000000000000000000000000000000000000000000000000000010203040000000000000000000000000000000000000b15012522178267bd659068c413737725507b2edabfa74b032882a8dacb806378ee66010100000002").unwrap();
let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap();
assert_eq!(vaa, parsed_vaa);
let rec_data = parsed_vaa.serialize().unwrap();
assert_eq!(data, rec_data);
}
#[test]
fn parse_given_transfer() {
let vaa = VAA {
version: 1,
guardian_set_index: 9,
signature_sig: [
2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
],
signature_addr: [1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
timestamp: 2837,
payload: Some(VAABody::Transfer(BodyTransfer {
nonce: 38,
source_chain: 2,
target_chain: 1,
target_address: [
2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
],
asset: AssetMeta {
address: [
9, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
chain: 9,
},
amount: U256::from(29),
})),
};
let data = hex::decode("01000000090208000000000000000000000000000000000000000000000000000000000000010203040000000000000000000000000000000000000b1510670000002602010201030000000000000000000000000000000000000000000000000000000000090902040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d").unwrap();
let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap();
assert_eq!(vaa, parsed_vaa);
let rec_data = parsed_vaa.serialize().unwrap();
assert_eq!(data, rec_data);
}
}

98
solana/cli/Cargo.lock generated
View File

@ -127,6 +127,16 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitvec"
version = "0.17.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c"
dependencies = [
"either",
"radium",
]
[[package]]
name = "blake2b_simd"
version = "0.5.10"
@ -197,6 +207,12 @@ dependencies = [
"serde",
]
[[package]]
name = "byte-slice-cast"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3"
[[package]]
name = "byte-tools"
version = "0.3.1"
@ -426,6 +442,12 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-mac"
version = "0.7.0"
@ -597,6 +619,18 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da"
[[package]]
name = "fixed-hash"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c"
dependencies = [
"byteorder",
"rand",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -913,6 +947,15 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "impl-codec"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53"
dependencies = [
"parity-scale-codec",
]
[[package]]
name = "indexmap"
version = "1.5.0"
@ -1323,6 +1366,18 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "parity-scale-codec"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34d38aeaffc032ec69faa476b3caaca8d4dd7f3f798137ff30359e5c7869ceb6"
dependencies = [
"arrayvec",
"bitvec",
"byte-slice-cast",
"serde",
]
[[package]]
name = "parking_lot"
version = "0.9.0"
@ -1433,6 +1488,17 @@ version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "primitive-types"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8"
dependencies = [
"fixed-hash",
"impl-codec",
"uint",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.18"
@ -1487,6 +1553,12 @@ dependencies = [
"proc-macro2 1.0.19",
]
[[package]]
name = "radium"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac"
[[package]]
name = "rand"
version = "0.7.3"
@ -1683,6 +1755,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hex"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
[[package]]
name = "rustc_version"
version = "0.2.3"
@ -2241,6 +2319,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
@ -2698,6 +2782,18 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
[[package]]
name = "uint"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "429ffcad8c8c15f874578c7337d156a3727eb4a1c2374c0ae937ad9a9b748c80"
dependencies = [
"byteorder",
"crunchy",
"rustc-hex",
"static_assertions",
]
[[package]]
name = "unicase"
version = "2.6.0"
@ -2965,10 +3061,10 @@ dependencies = [
name = "wormhole-bridge"
version = "0.1.0"
dependencies = [
"arrayref",
"byteorder",
"num-derive 0.2.5",
"num-traits",
"primitive-types",
"remove_dir_all",
"sha3",
"solana-sdk",