Skip to content

Commit

Permalink
Versioned TX (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
juchiast authored Nov 18, 2024
1 parent d1a6fe3 commit 70e4872
Show file tree
Hide file tree
Showing 10 changed files with 641 additions and 612 deletions.
2 changes: 1 addition & 1 deletion @space-operator/client/deno.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@space-operator/client",
"version": "0.8.0",
"version": "0.9.0",
"exports": "./src/mod.ts"
}
701 changes: 335 additions & 366 deletions @space-operator/client/deno.lock

Large diffs are not rendered by default.

23 changes: 15 additions & 8 deletions @space-operator/client/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { bs58, web3, Value, type IValue } from "./deps.ts";
import { bs58, web3, Value, type IValue, Buffer } from "./deps.ts";
import {
type ErrorBody,
type ISignatureRequest,
Expand Down Expand Up @@ -260,7 +260,9 @@ export class Client {
async signAndSubmitSignature(
req: SignatureRequest,
publicKey: web3.PublicKey,
signTransaction: (tx: web3.Transaction) => Promise<web3.Transaction>
signTransaction: (
tx: web3.VersionedTransaction
) => Promise<web3.VersionedTransaction>
) {
const requestedPublicKey = new web3.PublicKey(req.pubkey);
if (!publicKey.equals(requestedPublicKey)) {
Expand All @@ -270,17 +272,22 @@ export class Client {
}}\nwallet: ${publicKey.toBase58()}`
);
}

const tx = req.buildTransaction();
const signerPosition = tx.message.staticAccountKeys.findIndex((pk) =>
pk.equals(requestedPublicKey)
);
if (signerPosition === -1) {
throw new Error("pubkey is not in signers list");
}
this.logger("tx", tx);
const signedTx: web3.Transaction = await signTransaction(tx);
const signedTx: web3.VersionedTransaction = await signTransaction(tx);
this.logger("signedTx", signedTx);
const signature = signedTx.signatures.find(({ publicKey }) =>
publicKey.equals(requestedPublicKey)
)?.signature;
const signature = signedTx.signatures[signerPosition];
if (signature == null) throw new Error("signature is null");

const before = tx.serializeMessage();
const after = signedTx.serializeMessage();
const before = Buffer.from(tx.message.serialize());
const after = Buffer.from(signedTx.message.serialize());
const new_msg = before.equals(after) ? undefined : after.toString("base64");
await this.submitSignature({
id: req.id,
Expand Down
40 changes: 7 additions & 33 deletions @space-operator/client/src/types/ws.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FlowRunId, NodeId } from "./common.ts";
import { Value, Buffer, bs58, web3 } from "../deps.ts";
import type { FlowRunId, NodeId } from "./common.ts";
import { type Value, Buffer, bs58, web3 } from "../deps.ts";

export interface WsResponse<T> {
id: number;
Expand Down Expand Up @@ -154,53 +154,27 @@ export class SignatureRequest implements ISignatureRequest {
this.signatures = x.signatures;
}

buildTransaction(): web3.Transaction {
buildTransaction(): web3.VersionedTransaction {
const buffer = Buffer.from(this.message, "base64");
const solMsg = web3.Message.from(buffer);
const solMsg = web3.VersionedMessage.deserialize(buffer);

let sigs = undefined;
if (this.signatures) {
sigs = [];
const defaultSignature = bs58.encodeBase58(new Uint8Array(64));
for (let i = 0; i < solMsg.header.numRequiredSignatures; i++) {
const pubkey = solMsg.accountKeys[i].toBase58();
const pubkey = solMsg.staticAccountKeys[i].toBase58();
let signature = this.signatures.find(
(x) => x.pubkey === pubkey
)?.signature;
if (signature === undefined) {
signature = defaultSignature;
}
sigs.push(signature);
sigs.push(bs58.decodeBase58(signature));
}
}

return web3.Transaction.populate(solMsg, sigs);

// TODO: not sure if we still need this
// https://github.com/anza-xyz/wallet-adapter/issues/806
// https://github.com/solana-labs/solana/issues/21722

// const newTx = new Transaction();
// newTx.feePayer = tx.feePayer;
// newTx.recentBlockhash = tx.recentBlockhash;
// newTx.nonceInfo = tx.nonceInfo;

// solMsg.compiledInstructions.forEach((cIns) => {
// const init: TransactionInstructionCtorFields = {
// programId: solMsg.accountKeys[cIns.programIdIndex],

// keys: cIns.accountKeyIndexes.map((i) => {
// const x: AccountMeta = {
// pubkey: solMsg.accountKeys[i],
// isSigner: solMsg.isAccountSigner(i),
// isWritable: solMsg.isAccountWritable(i),
// };
// return x;
// }),
// data: Buffer.from(cIns.data),
// };
// newTx.add(new TransactionInstruction(init));
// });
return new web3.VersionedTransaction(solMsg, sigs);
}
}

Expand Down
41 changes: 8 additions & 33 deletions @space-operator/client/tests/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dotenv.loadSync({
});

const c = new client.Client({
// host: "http://localhost:8080",
host: "http://localhost:8080",
});

function keypair_from_env() {
Expand All @@ -31,46 +31,21 @@ const run = async () => {
inputs: new Value({
sender: keypair.publicKey,
}).M!,
output_instructions: true,
output_instructions: false,
fees: [["HuktZqYAXSeMz5hMtdEnvsJAXtapg24zXU2tkDnGgaSZ", 1000]],
});

// const req = await c.getSignatureRequest(result.flow_run_id, result.token);
const req = await c.getSignatureRequest(result.flow_run_id, result.token);

// const tx = req.buildTransaction();

// tx.sign(keypair);

// const sigResult = await c.submitSignature({
// id: req.id,
// signature: bs58.encodeBase58(tx.signature!),
// });

// console.log(sigResult);
await c.signAndSubmitSignature(req, keypair.publicKey, async (tx) => {
tx.sign([keypair]);
return tx;
});

const output = await c.getFlowOutput(result.flow_run_id, result.token);
return output;
};

const res = await Promise.all([
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
run(),
]);
const res = await Promise.all([run()]);

console.log(res);
4 changes: 2 additions & 2 deletions crates/cmds-solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ pub mod wormhole;
// pub mod xnft;
pub mod das;
pub mod governance;
pub mod jupiter;
pub mod memo;
pub mod pyth_price;
pub mod record;
pub mod spl;
pub mod spl_token_2022;
pub mod streamflow;
pub mod jupiter;

pub use error::{Error, Result};

Expand Down Expand Up @@ -88,7 +88,7 @@ pub mod tests {

#[test]
fn test_name_unique() {
let mut m = std::collections::HashSet::new();
let mut m = ::std::collections::HashSet::new();
let mut dup = false;
for CommandDescription { name, .. } in inventory::iter::<CommandDescription>() {
if !m.insert(name) {
Expand Down
5 changes: 4 additions & 1 deletion crates/flow/src/flow_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,11 +1122,13 @@ impl FlowGraph {
} else {
tracing::info!("executing instructions");
let config = self.tx_exec_config.clone();
let network = self.ctx.cfg.solana_client.cluster;
s.stop
.race(
std::pin::pin!(s.stop_shared.race(
std::pin::pin!(ins.execute(
&self.ctx.solana_client,
network,
self.ctx.signer.clone(),
Some(s.flow_run_id),
config,
Expand Down Expand Up @@ -1458,14 +1460,15 @@ impl FlowGraph {
fee_payer,
self.action_identity,
&self.ctx.solana_client,
self.ctx.cfg.solana_client.cluster,
self.ctx.signer.clone(),
Some(s.flow_run_id),
&self.tx_exec_config,
)
.await
{
Ok(tx) => {
let tx_bytes = bincode::serialize(&tx).unwrap();
let tx_bytes = tx.0.serialize();
let tx_base64 = BASE64_STANDARD.encode(&tx_bytes);
s.result
.output
Expand Down
44 changes: 41 additions & 3 deletions lib/flow-lib/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub mod env {
pub const TX_COMMITMENT_LEVEL: &str = "TX_COMMITMENT_LEVEL";
pub const WAIT_COMMITMENT_LEVEL: &str = "WAIT_COMMITMENT_LEVEL";
pub const EXECUTE_ON: &str = "EXECUTE_ON";
pub const DEVNET_LOOKUP_TABLE: &str = "DEVNET_LOOKUP_TABLE";
pub const MAINNET_LOOKUP_TABLE: &str = "MAINNET_LOOKUP_TABLE";
}

/// Get user's JWT, require
Expand Down Expand Up @@ -141,7 +143,9 @@ pub mod signer {
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as, DisplayFromStr, DurationSecondsWithFrac};
use solana_sdk::{pubkey::Pubkey, signature::Signature};
use solana_sdk::{
pubkey::Pubkey, signature::Signature, signer::presigner::Presigner as SdkPresigner,
};
use std::time::Duration;
use thiserror::Error as ThisError;

Expand Down Expand Up @@ -172,6 +176,12 @@ pub mod signer {
pub signature: Signature,
}

impl From<Presigner> for SdkPresigner {
fn from(value: Presigner) -> Self {
SdkPresigner::new(&value.pubkey, &value.signature)
}
}

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignatureRequest {
Expand Down Expand Up @@ -217,7 +227,10 @@ pub mod execute {
use serde::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as, DisplayFromStr};
use solana_client::client_error::ClientError;
use solana_sdk::{signature::Signature, signer::SignerError};
use solana_sdk::{
instruction::InstructionError, message::CompileError, sanitize::SanitizeError,
signature::Signature, signer::SignerError,
};
use std::sync::Arc;
use thiserror::Error as ThisError;

Expand Down Expand Up @@ -282,6 +295,12 @@ pub mod execute {
#[error(transparent)]
Signer(#[from] Arc<SignerError>),
#[error(transparent)]
CompileError(#[from] Arc<CompileError>),
#[error(transparent)]
InstructionError(#[from] Arc<InstructionError>),
#[error(transparent)]
SanitizeError(#[from] Arc<SanitizeError>),
#[error(transparent)]
Worker(Arc<BoxError>),
#[error(transparent)]
MailBox(#[from] actix::MailboxError),
Expand Down Expand Up @@ -318,6 +337,24 @@ pub mod execute {
}
}

impl From<CompileError> for Error {
fn from(value: CompileError) -> Self {
Error::CompileError(Arc::new(value))
}
}

impl From<InstructionError> for Error {
fn from(value: InstructionError) -> Self {
Error::InstructionError(Arc::new(value))
}
}

impl From<SanitizeError> for Error {
fn from(value: SanitizeError) -> Self {
Error::SanitizeError(Arc::new(value))
}
}

impl Error {
pub fn worker(e: BoxError) -> Self {
Error::Worker(Arc::new(e))
Expand All @@ -340,6 +377,7 @@ pub mod execute {
) -> Svc {
let rpc = ctx.solana_client.clone();
let signer = ctx.signer.clone();
let network = ctx.cfg.solana_client.cluster;
let handle = move |req: Request| {
let rpc = rpc.clone();
let signer = signer.clone();
Expand All @@ -348,7 +386,7 @@ pub mod execute {
Ok(Response {
signature: Some(
req.instructions
.execute(&rpc, signer, flow_run_id, config)
.execute(&rpc, network, signer, flow_run_id, config)
.await?,
),
})
Expand Down
Loading

0 comments on commit 70e4872

Please sign in to comment.