Adapt solana agent for v2
Change-Id: I81fc8b959f33a157371d1c59b1d5323dfc11f1ce
This commit is contained in:
parent
a341c2a5b5
commit
6d1b3d2651
|
@ -2,6 +2,7 @@ syntax = "proto3";
|
||||||
|
|
||||||
package agent.v1;
|
package agent.v1;
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
// TODO: documentation
|
// TODO: documentation
|
||||||
|
|
||||||
option go_package = "github.com/certusone/wormhole/bridge/pkg/proto/agent/v1;agentv1";
|
option go_package = "github.com/certusone/wormhole/bridge/pkg/proto/agent/v1;agentv1";
|
||||||
|
@ -15,10 +16,27 @@ message Empty {
|
||||||
}
|
}
|
||||||
|
|
||||||
message SubmitVAARequest {
|
message SubmitVAARequest {
|
||||||
bytes vaa = 1;
|
VAA vaa = 1;
|
||||||
bool skip_preflight = 2;
|
bool skip_preflight = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message VAA {
|
||||||
|
uint32 Version = 1;
|
||||||
|
google.protobuf.Timestamp Timestamp = 2;
|
||||||
|
uint32 Nonce = 3;
|
||||||
|
uint32 EmitterChain = 4;
|
||||||
|
bytes EmitterAddress = 5;
|
||||||
|
uint64 Sequence = 6;
|
||||||
|
bytes Payload = 7;
|
||||||
|
uint32 GuardianSetIndex = 8;
|
||||||
|
repeated Signature Signatures = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Signature{
|
||||||
|
uint32 GuardianIndex = 1;
|
||||||
|
bytes Signature = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message SubmitVAAResponse {
|
message SubmitVAAResponse {
|
||||||
string signature = 1;
|
string signature = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
name = "agent"
|
name = "agent"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"borsh",
|
||||||
"bridge",
|
"bridge",
|
||||||
"bs58",
|
"bs58",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
@ -46,6 +47,7 @@ dependencies = [
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha3",
|
||||||
"solana-client",
|
"solana-client",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
|
|
|
@ -14,7 +14,7 @@ solana-program = "=1.7.0"
|
||||||
solana-sdk = "=1.7.0"
|
solana-sdk = "=1.7.0"
|
||||||
solitaire = { path = "../../solitaire/program" }
|
solitaire = { path = "../../solitaire/program" }
|
||||||
solitaire-client = {path = "../../solitaire/client" }
|
solitaire-client = {path = "../../solitaire/client" }
|
||||||
bridge = { path = "../program", default-features = false, features = ["no-entrypoint"] }
|
bridge = { path = "../program", features = ["no-idl", "no-entrypoint", "client"] }
|
||||||
primitive-types = { version = "0.7.2" }
|
primitive-types = { version = "0.7.2" }
|
||||||
hex = "0.4.2"
|
hex = "0.4.2"
|
||||||
thiserror = "1.0.20"
|
thiserror = "1.0.20"
|
||||||
|
@ -26,10 +26,12 @@ log = "0.4.11"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.57"
|
serde_json = "1.0.57"
|
||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.4.3"
|
||||||
futures = "0.3.8"
|
futures = "0.3.8"
|
||||||
libc = "0.2.80"
|
libc = "0.2.80"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
borsh = "0.8.1"
|
||||||
|
sha3 = "0.9.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = { version = "0.3.0", features = ["prost"] }
|
tonic-build = { version = "0.3.0", features = ["prost"] }
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::compile_protos("../../proto/agent/v1/service.proto").unwrap();
|
tonic_build::compile_protos("../../../proto/agent/v1/service.proto").unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,80 @@
|
||||||
use std::{env, io::Write, mem::size_of, str::FromStr, fs};
|
|
||||||
use std::path::Path;
|
|
||||||
use libc;
|
use libc;
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
io::Write,
|
||||||
|
path::Path,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{Arg, App, SubCommand};
|
use clap::{
|
||||||
|
App,
|
||||||
|
Arg,
|
||||||
|
};
|
||||||
|
|
||||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{
|
||||||
|
LittleEndian,
|
||||||
|
WriteBytesExt,
|
||||||
|
};
|
||||||
use futures::stream::TryStreamExt;
|
use futures::stream::TryStreamExt;
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
client_error::ClientError, rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig,
|
client_error::ClientError,
|
||||||
|
rpc_client::RpcClient,
|
||||||
|
rpc_config::RpcSendTransactionConfig,
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
commitment_config::{
|
||||||
|
CommitmentConfig,
|
||||||
|
CommitmentLevel,
|
||||||
|
},
|
||||||
instruction::Instruction,
|
instruction::Instruction,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{read_keypair_file, write_keypair_file, Keypair, Signature, Signer},
|
signature::{
|
||||||
|
read_keypair_file,
|
||||||
|
Keypair,
|
||||||
|
Signature,
|
||||||
|
Signer,
|
||||||
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use tokio::net::UnixListener;
|
use tokio::net::UnixListener;
|
||||||
use tokio::sync::mpsc;
|
|
||||||
use tonic::{transport::Server, Code, Request, Response, Status};
|
|
||||||
|
|
||||||
use service::{
|
use tonic::{
|
||||||
agent_server::{Agent, AgentServer},
|
transport::Server,
|
||||||
Empty,SubmitVaaRequest, SubmitVaaResponse,
|
Code,
|
||||||
GetBalanceResponse, GetBalanceRequest,
|
Request,
|
||||||
|
Response,
|
||||||
|
Status,
|
||||||
};
|
};
|
||||||
use spl_bridge::{
|
|
||||||
instruction::{post_vaa, verify_signatures, VerifySigPayload, CHAIN_ID_SOLANA},
|
use borsh::BorshDeserialize;
|
||||||
state::{Bridge, GuardianSet, PostedMessage},
|
use bridge::{
|
||||||
vaa::VAA,
|
accounts::{
|
||||||
|
GuardianSet,
|
||||||
|
GuardianSetDerivationData,
|
||||||
|
},
|
||||||
|
instructions::{
|
||||||
|
hash_vaa,
|
||||||
|
post_vaa,
|
||||||
|
serialize_vaa,
|
||||||
|
verify_signatures,
|
||||||
|
},
|
||||||
|
types::GuardianSetData,
|
||||||
|
PostVAAData,
|
||||||
|
VerifySignaturesData,
|
||||||
|
};
|
||||||
|
use service::{
|
||||||
|
agent_server::{
|
||||||
|
Agent,
|
||||||
|
AgentServer,
|
||||||
|
},
|
||||||
|
GetBalanceRequest,
|
||||||
|
GetBalanceResponse,
|
||||||
|
SubmitVaaRequest,
|
||||||
|
SubmitVaaResponse,
|
||||||
|
};
|
||||||
|
use solitaire::{
|
||||||
|
processors::seeded::Seeded,
|
||||||
|
AccountState,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod socket;
|
mod socket;
|
||||||
|
@ -38,7 +84,6 @@ pub mod service {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AgentImpl {
|
pub struct AgentImpl {
|
||||||
url: String,
|
|
||||||
bridge: Pubkey,
|
bridge: Pubkey,
|
||||||
|
|
||||||
rpc_url: String,
|
rpc_url: String,
|
||||||
|
@ -46,7 +91,7 @@ pub struct AgentImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SignatureItem {
|
pub struct SignatureItem {
|
||||||
signature: [u8; 64 + 1],
|
signature: Vec<u8>,
|
||||||
key: [u8; 20],
|
key: [u8; 20],
|
||||||
index: u8,
|
index: u8,
|
||||||
}
|
}
|
||||||
|
@ -68,43 +113,46 @@ impl Agent for AgentImpl {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let rpc = RpcClient::new(rpc_url);
|
let rpc = RpcClient::new(rpc_url);
|
||||||
|
|
||||||
let mut vaa = match VAA::deserialize(&request.get_ref().vaa) {
|
let vaa = &request.get_ref().vaa.as_ref().unwrap();
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
let mut emitter_address = [0u8; 32];
|
||||||
return Err(Status::new(
|
emitter_address.copy_from_slice(vaa.emitter_address.as_slice());
|
||||||
Code::InvalidArgument,
|
let post_data = PostVAAData {
|
||||||
format!("could not parse VAA: {}", e),
|
version: vaa.version as u8,
|
||||||
));
|
guardian_set_index: vaa.guardian_set_index,
|
||||||
}
|
timestamp: vaa.timestamp.as_ref().unwrap().seconds as u32,
|
||||||
|
nonce: vaa.nonce,
|
||||||
|
emitter_chain: vaa.emitter_chain as u16,
|
||||||
|
emitter_address: emitter_address,
|
||||||
|
sequence: vaa.sequence,
|
||||||
|
payload: vaa.payload.clone(),
|
||||||
};
|
};
|
||||||
let verify_txs = pack_sig_verification_txs(&rpc, &bridge, &vaa, &key)?;
|
|
||||||
|
let verify_txs =
|
||||||
|
pack_sig_verification_txs(&rpc, &bridge, &post_data, &vaa.signatures, &key)?;
|
||||||
|
|
||||||
// Strip signatures
|
// Strip signatures
|
||||||
vaa.signatures = Vec::new();
|
let ix = post_vaa(bridge, key.pubkey(), post_data);
|
||||||
let ix = match post_vaa(&bridge, &key.pubkey(), vaa.serialize().unwrap()) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(Status::new(
|
|
||||||
Code::InvalidArgument,
|
|
||||||
format!("could not create post_vaa instruction: {}", e),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for mut tx in verify_txs {
|
for mut tx in verify_txs {
|
||||||
match sign_and_send(&rpc, &mut tx, vec![&key], request.skip_preflight) {
|
match sign_and_send(&rpc, &mut tx, vec![&key], request.get_ref().skip_preflight) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(Status::new(
|
return Err(Status::new(
|
||||||
Code::Internal,
|
Code::Internal,
|
||||||
format!("tx sending failed: {}", e),
|
format!("tx sending failed: {:?}", e),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut transaction2 = Transaction::new_with_payer(&[ix], Some(&key.pubkey()));
|
let mut transaction2 = Transaction::new_with_payer(&[ix], Some(&key.pubkey()));
|
||||||
match sign_and_send(&rpc, &mut transaction2, vec![&key], request.skip_preflight) {
|
match sign_and_send(
|
||||||
|
&rpc,
|
||||||
|
&mut transaction2,
|
||||||
|
vec![&key],
|
||||||
|
request.into_inner().skip_preflight,
|
||||||
|
) {
|
||||||
Ok(s) => Ok(Response::new(SubmitVaaResponse {
|
Ok(s) => Ok(Response::new(SubmitVaaResponse {
|
||||||
signature: s.to_string(),
|
signature: s.to_string(),
|
||||||
})),
|
})),
|
||||||
|
@ -114,13 +162,13 @@ impl Agent for AgentImpl {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join()
|
.join()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_balance(
|
async fn get_balance(
|
||||||
&self,
|
&self,
|
||||||
request: Request<GetBalanceRequest>,
|
_request: Request<GetBalanceRequest>,
|
||||||
) -> Result<Response<GetBalanceResponse>, Status> {
|
) -> Result<Response<GetBalanceResponse>, Status> {
|
||||||
// Hack to clone keypair
|
// Hack to clone keypair
|
||||||
let b = self.key.pubkey();
|
let b = self.key.pubkey();
|
||||||
|
@ -136,30 +184,32 @@ impl Agent for AgentImpl {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(Status::new(
|
return Err(Status::new(
|
||||||
Code::Internal,
|
Code::Internal,
|
||||||
format!("failed to fetch balance: {}", e),
|
format!("failed to fetch balance: {:?}", e),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Response::new(GetBalanceResponse {
|
Ok(Response::new(GetBalanceResponse { balance }))
|
||||||
balance,
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.join()
|
.join()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pack_sig_verification_txs<'a>(
|
fn pack_sig_verification_txs<'a>(
|
||||||
rpc: &RpcClient,
|
rpc: &RpcClient,
|
||||||
bridge: &Pubkey,
|
bridge: &Pubkey,
|
||||||
vaa: &VAA,
|
vaa: &PostVAAData,
|
||||||
|
signatures: &Vec<service::Signature>,
|
||||||
sender_keypair: &'a Keypair,
|
sender_keypair: &'a Keypair,
|
||||||
) -> Result<Vec<Transaction>, Status> {
|
) -> Result<Vec<Transaction>, Status> {
|
||||||
// Load guardian set
|
// Load guardian set
|
||||||
let bridge_key = Bridge::derive_bridge_id(bridge).unwrap();
|
let guardian_key = GuardianSet::<'_, { AccountState::Initialized }>::key(
|
||||||
let guardian_key =
|
&GuardianSetDerivationData {
|
||||||
Bridge::derive_guardian_set_id(bridge, &bridge_key, vaa.guardian_set_index).unwrap();
|
index: vaa.guardian_set_index,
|
||||||
|
},
|
||||||
|
bridge,
|
||||||
|
);
|
||||||
let guardian_account = rpc
|
let guardian_account = rpc
|
||||||
.get_account_with_commitment(
|
.get_account_with_commitment(
|
||||||
&guardian_key,
|
&guardian_key,
|
||||||
|
@ -171,78 +221,54 @@ fn pack_sig_verification_txs<'a>(
|
||||||
.value
|
.value
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let data = guardian_account.data;
|
let data = guardian_account.data;
|
||||||
let guardian_set: &GuardianSet = Bridge::unpack_immutable(data.as_slice()).unwrap();
|
let guardian_set: GuardianSetData = GuardianSetData::try_from_slice(data.as_slice()).unwrap();
|
||||||
|
|
||||||
// Map signatures to guardian set
|
// Map signatures to guardian set
|
||||||
let mut signature_items: Vec<SignatureItem> = Vec::new();
|
let mut signature_items: Vec<SignatureItem> = Vec::new();
|
||||||
for s in vaa.signatures.iter() {
|
for s in signatures.iter() {
|
||||||
let mut item = SignatureItem {
|
let mut item = SignatureItem {
|
||||||
signature: [0; 64 + 1],
|
signature: s.signature.clone(),
|
||||||
key: [0; 20],
|
key: [0; 20],
|
||||||
index: s.index,
|
index: s.guardian_index as u8,
|
||||||
};
|
};
|
||||||
|
item.key = guardian_set.keys[s.guardian_index as usize];
|
||||||
item.signature[0..32].copy_from_slice(&s.r);
|
|
||||||
item.signature[32..64].copy_from_slice(&s.s);
|
|
||||||
item.signature[64] = s.v;
|
|
||||||
item.key = guardian_set.keys[s.index as usize];
|
|
||||||
|
|
||||||
signature_items.push(item);
|
signature_items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vaa_hash = match vaa.body_hash() {
|
let vaa_body = serialize_vaa(vaa);
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(Status::new(
|
|
||||||
Code::InvalidArgument,
|
|
||||||
format!("could get vaa body hash: {}", e),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let vaa_body = match vaa.signature_body() {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(Status::new(
|
|
||||||
Code::InvalidArgument,
|
|
||||||
format!("could get vaa body: {}", e),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let signature_acc =
|
|
||||||
Bridge::derive_signature_id(&bridge, &bridge_key, &vaa_hash, guardian_set.index).unwrap();
|
|
||||||
|
|
||||||
let mut verify_txs: Vec<Transaction> = Vec::new();
|
let mut verify_txs: Vec<Transaction> = Vec::new();
|
||||||
for (tx_index, chunk) in signature_items.chunks(6).enumerate() {
|
for (_tx_index, chunk) in signature_items.chunks(6).enumerate() {
|
||||||
let mut secp_payload = Vec::new();
|
let mut secp_payload = Vec::new();
|
||||||
let mut signature_status = [-1i8; 20];
|
let mut signature_status = [-1i8; 19];
|
||||||
|
|
||||||
let data_offset = 1 + chunk.len() * 11;
|
let data_offset = 1 + chunk.len() * 11;
|
||||||
let message_offset = data_offset + chunk.len() * 85;
|
let message_offset = data_offset + chunk.len() * 85;
|
||||||
|
|
||||||
// 1 number of signatures
|
// 1 number of signatures
|
||||||
secp_payload.write_u8(chunk.len() as u8);
|
secp_payload.write_u8(chunk.len() as u8)?;
|
||||||
|
|
||||||
// Secp signature info description (11 bytes * n)
|
// Secp signature info description (11 bytes * n)
|
||||||
for (i, s) in chunk.iter().enumerate() {
|
for (i, s) in chunk.iter().enumerate() {
|
||||||
secp_payload.write_u16::<LittleEndian>((data_offset + 85 * i) as u16);
|
secp_payload.write_u16::<LittleEndian>((data_offset + 85 * i) as u16)?;
|
||||||
secp_payload.write_u8(0);
|
secp_payload.write_u8(0)?;
|
||||||
secp_payload.write_u16::<LittleEndian>((data_offset + 85 * i + 65) as u16);
|
secp_payload.write_u16::<LittleEndian>((data_offset + 85 * i + 65) as u16)?;
|
||||||
secp_payload.write_u8(0);
|
secp_payload.write_u8(0)?;
|
||||||
secp_payload.write_u16::<LittleEndian>(message_offset as u16);
|
secp_payload.write_u16::<LittleEndian>(message_offset as u16)?;
|
||||||
secp_payload.write_u16::<LittleEndian>(vaa_body.len() as u16);
|
secp_payload.write_u16::<LittleEndian>(vaa_body.len() as u16)?;
|
||||||
secp_payload.write_u8(0);
|
secp_payload.write_u8(0)?;
|
||||||
signature_status[s.index as usize] = i as i8;
|
signature_status[s.index as usize] = i as i8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write signatures and addresses
|
// Write signatures and addresses
|
||||||
for s in chunk.iter() {
|
for s in chunk.iter() {
|
||||||
secp_payload.write(&s.signature);
|
secp_payload.write(&s.signature)?;
|
||||||
secp_payload.write(&s.key);
|
secp_payload.write(&s.key)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write body
|
// Write body
|
||||||
secp_payload.write(&vaa_body);
|
secp_payload.write(&vaa_body)?;
|
||||||
|
|
||||||
let secp_ix = Instruction {
|
let secp_ix = Instruction {
|
||||||
program_id: solana_sdk::secp256k1_program::id(),
|
program_id: solana_sdk::secp256k1_program::id(),
|
||||||
|
@ -250,24 +276,25 @@ fn pack_sig_verification_txs<'a>(
|
||||||
accounts: vec![],
|
accounts: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload = VerifySigPayload {
|
let body_hash: [u8; 32] = hash_vaa(vaa);
|
||||||
|
|
||||||
|
let payload = VerifySignaturesData {
|
||||||
signers: signature_status,
|
signers: signature_status,
|
||||||
hash: vaa_hash,
|
hash: body_hash,
|
||||||
initial_creation: false,
|
initial_creation: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let verify_ix = match verify_signatures(
|
let verify_ix = match verify_signatures(
|
||||||
&bridge,
|
*bridge,
|
||||||
&signature_acc,
|
sender_keypair.pubkey(),
|
||||||
&sender_keypair.pubkey(),
|
|
||||||
vaa.guardian_set_index,
|
vaa.guardian_set_index,
|
||||||
&payload,
|
payload,
|
||||||
) {
|
) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(Status::new(
|
return Err(Status::new(
|
||||||
Code::InvalidArgument,
|
Code::InvalidArgument,
|
||||||
format!("could not create verify instruction: {}", e),
|
format!("could not create verify instruction: {:?}", e),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -307,40 +334,49 @@ fn sign_and_send(
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let matches = App::new("Wormhole Solana agent")
|
let matches = App::new("Wormhole Solana agent")
|
||||||
.arg(Arg::with_name("bridge")
|
.arg(
|
||||||
.long("bridge")
|
Arg::with_name("bridge")
|
||||||
.value_name("ADDRESS")
|
.long("bridge")
|
||||||
.help("Bridge address")
|
.value_name("ADDRESS")
|
||||||
.required(true)
|
.help("Bridge address")
|
||||||
.takes_value(true))
|
.required(true)
|
||||||
.arg(Arg::with_name("ws")
|
.takes_value(true),
|
||||||
.long("ws")
|
)
|
||||||
.value_name("URI")
|
.arg(
|
||||||
.help("PubSub Websocket URI (ws[s]://)")
|
Arg::with_name("ws")
|
||||||
.required(true)
|
.long("ws")
|
||||||
.takes_value(true))
|
.value_name("URI")
|
||||||
.arg(Arg::with_name("rpc")
|
.help("PubSub Websocket URI (ws[s]://)")
|
||||||
.long("rpc")
|
.required(true)
|
||||||
.value_name("URI")
|
.takes_value(true),
|
||||||
.help("RPC URI (http[s]://)")
|
)
|
||||||
.required(true)
|
.arg(
|
||||||
.takes_value(true))
|
Arg::with_name("rpc")
|
||||||
.arg(Arg::with_name("socket")
|
.long("rpc")
|
||||||
.long("socket")
|
.value_name("URI")
|
||||||
.value_name("FILE")
|
.help("RPC URI (http[s]://)")
|
||||||
.help("Path to agent socket")
|
.required(true)
|
||||||
.required(true)
|
.takes_value(true),
|
||||||
.takes_value(true))
|
)
|
||||||
.arg(Arg::with_name("keypair")
|
.arg(
|
||||||
.long("keypair")
|
Arg::with_name("socket")
|
||||||
.value_name("FILE")
|
.long("socket")
|
||||||
.help("Fee payer account key ")
|
.value_name("FILE")
|
||||||
.required(true)
|
.help("Path to agent socket")
|
||||||
.takes_value(true))
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("keypair")
|
||||||
|
.long("keypair")
|
||||||
|
.value_name("FILE")
|
||||||
|
.help("Fee payer account key ")
|
||||||
|
.required(true)
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let bridge = matches.value_of("bridge").unwrap();
|
let bridge = matches.value_of("bridge").unwrap();
|
||||||
let ws_url = matches.value_of("ws").unwrap();
|
|
||||||
let rpc_url = matches.value_of("rpc").unwrap();
|
let rpc_url = matches.value_of("rpc").unwrap();
|
||||||
let socket_path = matches.value_of("socket").unwrap();
|
let socket_path = matches.value_of("socket").unwrap();
|
||||||
let keypair = read_keypair_file(matches.value_of("keypair").unwrap()).unwrap();
|
let keypair = read_keypair_file(matches.value_of("keypair").unwrap()).unwrap();
|
||||||
|
@ -348,7 +384,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Agent using account: {}", keypair.pubkey());
|
println!("Agent using account: {}", keypair.pubkey());
|
||||||
|
|
||||||
let agent = AgentImpl {
|
let agent = AgentImpl {
|
||||||
url: ws_url.to_string(),
|
|
||||||
rpc_url: rpc_url.to_string(),
|
rpc_url: rpc_url.to_string(),
|
||||||
bridge: Pubkey::from_str(bridge).unwrap(),
|
bridge: Pubkey::from_str(bridge).unwrap(),
|
||||||
key: keypair,
|
key: keypair,
|
||||||
|
@ -359,7 +394,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
unsafe { libc::umask(0o0077) };
|
unsafe { libc::umask(0o0077) };
|
||||||
|
|
||||||
// Delete existing socket file and recreate it with restrictive permissions.
|
// Delete existing socket file and recreate it with restrictive permissions.
|
||||||
let mut path = Path::new(socket_path);
|
let path = Path::new(socket_path);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
fs::remove_file(path)?;
|
fs::remove_file(path)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,6 @@ pub struct PostVAAData {
|
||||||
// Header part
|
// Header part
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
pub guardian_set_index: u32,
|
pub guardian_set_index: u32,
|
||||||
pub signatures: Vec<Signature>,
|
|
||||||
|
|
||||||
// Body part
|
// Body part
|
||||||
pub timestamp: u32,
|
pub timestamp: u32,
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
use borsh::BorshSerialize;
|
use borsh::BorshSerialize;
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
borsh::try_from_slice_unchecked,
|
|
||||||
hash,
|
|
||||||
instruction::{
|
instruction::{
|
||||||
AccountMeta,
|
AccountMeta,
|
||||||
Instruction,
|
Instruction,
|
||||||
},
|
},
|
||||||
program_pack::Pack,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_instruction::{
|
|
||||||
self,
|
|
||||||
create_account,
|
|
||||||
},
|
|
||||||
system_program,
|
|
||||||
sysvar,
|
sysvar,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +26,6 @@ use crate::{
|
||||||
SignatureSet,
|
SignatureSet,
|
||||||
SignatureSetDerivationData,
|
SignatureSetDerivationData,
|
||||||
},
|
},
|
||||||
types::PostedMessage,
|
|
||||||
BridgeConfig,
|
BridgeConfig,
|
||||||
PostMessageData,
|
PostMessageData,
|
||||||
PostVAAData,
|
PostVAAData,
|
||||||
|
@ -124,7 +115,7 @@ pub fn verify_signatures(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
guardian_set_index: u32,
|
guardian_set_index: u32,
|
||||||
hash: [u8; 32],
|
data: VerifySignaturesData,
|
||||||
) -> solitaire::Result<Instruction> {
|
) -> solitaire::Result<Instruction> {
|
||||||
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
||||||
&GuardianSetDerivationData {
|
&GuardianSetDerivationData {
|
||||||
|
@ -134,15 +125,10 @@ pub fn verify_signatures(
|
||||||
);
|
);
|
||||||
|
|
||||||
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
||||||
&SignatureSetDerivationData { hash },
|
&SignatureSetDerivationData { hash: data.hash },
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Bridge with a single pre-existing signer.
|
|
||||||
// TODO: Get rid of this, exists to make testing easier for now.
|
|
||||||
let mut signers = [-1; 19];
|
|
||||||
signers[0] = 0;
|
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id,
|
program_id,
|
||||||
|
|
||||||
|
@ -155,40 +141,31 @@ pub fn verify_signatures(
|
||||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||||
],
|
],
|
||||||
|
|
||||||
data: crate::instruction::Instruction::VerifySignatures(VerifySignaturesData {
|
data: crate::instruction::Instruction::VerifySignatures(data).try_to_vec()?,
|
||||||
hash,
|
|
||||||
signers,
|
|
||||||
initial_creation: true,
|
|
||||||
})
|
|
||||||
.try_to_vec()?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_vaa(
|
pub fn post_vaa(program_id: Pubkey, payer: Pubkey, vaa: PostVAAData) -> Instruction {
|
||||||
program_id: Pubkey,
|
|
||||||
payer: Pubkey,
|
|
||||||
emitter: Pubkey,
|
|
||||||
guardian_set_index: u32,
|
|
||||||
vaa: PostVAAData,
|
|
||||||
) -> Instruction {
|
|
||||||
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
||||||
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
||||||
&GuardianSetDerivationData {
|
&GuardianSetDerivationData {
|
||||||
index: guardian_set_index,
|
index: vaa.guardian_set_index,
|
||||||
},
|
},
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
let signature_set = SignatureSet::<'_, { AccountState::Uninitialized }>::key(
|
||||||
&SignatureSetDerivationData { hash: hash_vaa(&vaa) },
|
&SignatureSetDerivationData {
|
||||||
|
hash: hash_vaa(&vaa),
|
||||||
|
},
|
||||||
&program_id,
|
&program_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = Message::<'_, { AccountState::MaybeInitialized }>::key(
|
let message = Message::<'_, { AccountState::MaybeInitialized }>::key(
|
||||||
&MessageDerivationData {
|
&MessageDerivationData {
|
||||||
emitter_key: emitter.to_bytes(),
|
emitter_key: vaa.emitter_address,
|
||||||
emitter_chain: 1,
|
emitter_chain: vaa.emitter_chain,
|
||||||
nonce: 0,
|
nonce: vaa.nonce,
|
||||||
payload: vaa.payload.clone(),
|
payload: vaa.payload.clone(),
|
||||||
},
|
},
|
||||||
&program_id,
|
&program_id,
|
||||||
|
@ -216,7 +193,7 @@ pub fn post_vaa(
|
||||||
|
|
||||||
// Convert a full VAA structure into the serialization of its unique components, this structure is
|
// Convert a full VAA structure into the serialization of its unique components, this structure is
|
||||||
// what is hashed and verified by Guardians.
|
// what is hashed and verified by Guardians.
|
||||||
fn serialize_vaa(vaa: &PostVAAData) -> Vec<u8> {
|
pub fn serialize_vaa(vaa: &PostVAAData) -> Vec<u8> {
|
||||||
use byteorder::{
|
use byteorder::{
|
||||||
BigEndian,
|
BigEndian,
|
||||||
WriteBytesExt,
|
WriteBytesExt,
|
||||||
|
@ -231,12 +208,13 @@ fn serialize_vaa(vaa: &PostVAAData) -> Vec<u8> {
|
||||||
v.write_u32::<BigEndian>(vaa.nonce).unwrap();
|
v.write_u32::<BigEndian>(vaa.nonce).unwrap();
|
||||||
v.write_u16::<BigEndian>(vaa.emitter_chain).unwrap();
|
v.write_u16::<BigEndian>(vaa.emitter_chain).unwrap();
|
||||||
v.write(&vaa.emitter_address).unwrap();
|
v.write(&vaa.emitter_address).unwrap();
|
||||||
|
v.write_u64::<BigEndian>(vaa.sequence).unwrap();
|
||||||
v.write(&vaa.payload).unwrap();
|
v.write(&vaa.payload).unwrap();
|
||||||
v.into_inner()
|
v.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash a VAA, this combines serialization and hashing.
|
// Hash a VAA, this combines serialization and hashing.
|
||||||
fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] {
|
pub fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] {
|
||||||
use sha3::Digest;
|
use sha3::Digest;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![feature(const_generics)]
|
#![feature(const_generics)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
use solana_program::msg;
|
use solana_program::msg;
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ mod helpers {
|
||||||
0,
|
0,
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
.unwrap()],
|
.unwrap()],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,13 +164,20 @@ mod helpers {
|
||||||
body_hash: [u8; 32],
|
body_hash: [u8; 32],
|
||||||
secret_key: SecretKey,
|
secret_key: SecretKey,
|
||||||
) {
|
) {
|
||||||
|
let mut signers = [-1; 19];
|
||||||
|
signers[0] = 0;
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
client,
|
client,
|
||||||
payer,
|
payer,
|
||||||
&[payer],
|
&[payer],
|
||||||
&[
|
&[
|
||||||
new_secp256k1_instruction(&secret_key, &body),
|
new_secp256k1_instruction(&secret_key, &body),
|
||||||
instructions::verify_signatures(*program, payer.pubkey(), 0, body_hash).unwrap(),
|
instructions::verify_signatures(*program, payer.pubkey(), 0, VerifySignaturesData {
|
||||||
|
hash: body_hash,
|
||||||
|
signers,
|
||||||
|
initial_creation: true,
|
||||||
|
}).unwrap(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -179,7 +186,6 @@ mod helpers {
|
||||||
client: &RpcClient,
|
client: &RpcClient,
|
||||||
program: &Pubkey,
|
program: &Pubkey,
|
||||||
payer: &Keypair,
|
payer: &Keypair,
|
||||||
emitter: &Pubkey,
|
|
||||||
vaa: PostVAAData,
|
vaa: PostVAAData,
|
||||||
) {
|
) {
|
||||||
execute(
|
execute(
|
||||||
|
@ -189,8 +195,6 @@ mod helpers {
|
||||||
&[instructions::post_vaa(
|
&[instructions::post_vaa(
|
||||||
*program,
|
*program,
|
||||||
payer.pubkey(),
|
payer.pubkey(),
|
||||||
*emitter,
|
|
||||||
0,
|
|
||||||
vaa,
|
vaa,
|
||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
|
|
|
@ -112,7 +112,6 @@ fn test_bridge_messages() {
|
||||||
client,
|
client,
|
||||||
program,
|
program,
|
||||||
payer,
|
payer,
|
||||||
&emitter.pubkey(),
|
|
||||||
vaa,
|
vaa,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -123,12 +122,11 @@ fn test_bridge_messages() {
|
||||||
/// is on the chain and creating a signature set for it.
|
/// is on the chain and creating a signature set for it.
|
||||||
fn guardian_sign_round(
|
fn guardian_sign_round(
|
||||||
emitter: &Keypair,
|
emitter: &Keypair,
|
||||||
data: Vec<u8>
|
data: Vec<u8>,
|
||||||
) -> (PostVAAData, Vec<u8>, [u8; 32], secp256k1::SecretKey) {
|
) -> (PostVAAData, Vec<u8>, [u8; 32], secp256k1::SecretKey) {
|
||||||
let mut vaa = PostVAAData {
|
let mut vaa = PostVAAData {
|
||||||
version: 0,
|
version: 0,
|
||||||
guardian_set_index: 0,
|
guardian_set_index: 0,
|
||||||
signatures: vec![],
|
|
||||||
|
|
||||||
// Body part
|
// Body part
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
|
@ -155,9 +153,9 @@ fn guardian_sign_round(
|
||||||
|
|
||||||
// Public Key: 0x1d72877eb2d898738afe94c6101152ede0435de9
|
// Public Key: 0x1d72877eb2d898738afe94c6101152ede0435de9
|
||||||
let secret_key = secp256k1::SecretKey::parse(&[
|
let secret_key = secp256k1::SecretKey::parse(&[
|
||||||
0x99, 0x70, 0x1c, 0x80, 0x5e, 0xf9, 0x38, 0xe1, 0x3f, 0x0e, 0x48, 0xf0, 0x9e, 0x2c, 0x32,
|
0x99, 0x70, 0x1c, 0x80, 0x5e, 0xf9, 0x38, 0xe1, 0x3f, 0x0e, 0x48, 0xf0, 0x9e, 0x2c, 0x32,
|
||||||
0x78, 0x91, 0xc1, 0xd8, 0x47, 0x29, 0xd1, 0x52, 0xf3, 0x01, 0xe7, 0xe6, 0x2c, 0xbf, 0x1f,
|
0x78, 0x91, 0xc1, 0xd8, 0x47, 0x29, 0xd1, 0x52, 0xf3, 0x01, 0xe7, 0xe6, 0x2c, 0xbf, 0x1f,
|
||||||
0x91, 0xc9
|
0x91, 0xc9
|
||||||
]).unwrap();
|
]).unwrap();
|
||||||
|
|
||||||
let public_key = secp256k1::PublicKey::from_secret_key(&secret_key);
|
let public_key = secp256k1::PublicKey::from_secret_key(&secret_key);
|
||||||
|
@ -181,9 +179,9 @@ fn guardian_sign_round(
|
||||||
let signature = sig.0.serialize();
|
let signature = sig.0.serialize();
|
||||||
vaa.signatures.push(Signature {
|
vaa.signatures.push(Signature {
|
||||||
index: 0,
|
index: 0,
|
||||||
r: signature[0..32].try_into().unwrap(),
|
r: signature[0..32].try_into().unwrap(),
|
||||||
s: signature[32..64].try_into().unwrap(),
|
s: signature[32..64].try_into().unwrap(),
|
||||||
v: sig.1.serialize(),
|
v: sig.1.serialize(),
|
||||||
});
|
});
|
||||||
|
|
||||||
(vaa, body, body_hash, secret_key)
|
(vaa, body, body_hash, secret_key)
|
||||||
|
|
Loading…
Reference in New Issue