Skip to content

Commit

Permalink
Forwarder contract test
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed Mar 15, 2024
1 parent d8297c0 commit b99ddf1
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 3 deletions.
8 changes: 6 additions & 2 deletions contracts/programs/keystone-forwarder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub const STATE_VERSION: u8 = 1;
pub struct State {
version: u8,
authority_nonce: u8,
owner: Pubkey,
}

#[account]
Expand Down Expand Up @@ -42,6 +43,7 @@ pub mod keystone_forwarder {
let state = &mut ctx.accounts.state;
state.version = STATE_VERSION;
state.authority_nonce = authority_nonce;
state.owner = ctx.accounts.owner.key();
Ok(())
}

Expand Down Expand Up @@ -107,7 +109,7 @@ pub struct Initialize<'info> {
#[account(
init,
payer = owner,
space = 8 + 1
space = 8 + 1 + 1 + 32
)]
pub state: Account<'info, State>,
#[account(mut)]
Expand Down Expand Up @@ -136,7 +138,9 @@ pub struct Report<'info> {
pub execution_state: Account<'info, ExecutionState>,

#[account(executable)]
// we don't use Program<> here since it can be any program, "executable" is enough
/// CHECK: We don't use Program<> here since it can be any program, "executable" is enough
pub receiver_program: UncheckedAccount<'info>,
// TODO: ensure receiver isn't forwarder itself?

// remaining_accounts... get passed to receiver as is
}
142 changes: 142 additions & 0 deletions contracts/tests/forwarder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import * as anchor from "@coral-xyz/anchor";
import { ProgramError, BN } from "@coral-xyz/anchor";
import { Keypair, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
import * as borsh from "borsh";

import { randomBytes, createHash } from "crypto";
import * as secp256k1 from "secp256k1";
import { keccak256 } from "ethereum-cryptography/keccak";

import { assert } from "chai";
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";

describe("ocr2", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.local();
anchor.setProvider(provider);

const forwarderProgram = anchor.workspace.KeystoneForwarder;

// Generate a new wallet keypair and airdrop SOL
const payer = Keypair.generate();

const owner = provider.wallet;

const state = Keypair.generate();
const transmitter = Keypair.generate();

let authorityNonce;
let authority: PublicKey;

let oracles = [];
const f = 6;
// NOTE: 17 is the most we can fit into one proposeConfig if we use a different payer
// if the owner == payer then we can fit 19
const n = 19; // min: 3 * f + 1;

let generateOracle = async () => {
let secretKey = randomBytes(32);
let transmitter = Keypair.generate();
return {
signer: {
secretKey,
publicKey: secp256k1.publicKeyCreate(secretKey, false).slice(1), // compressed = false, skip first byte (0x04)
},
transmitter,
};
};

it("Funds the payer", async () => {
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL * 1000),
"confirmed"
);

await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(transmitter.publicKey, LAMPORTS_PER_SOL * 1000),
"confirmed"
);
});

it("Initializes the forwarder", async() => {
await forwarderProgram.methods
.initialize()
.accounts({
state: state.publicKey,
owner: owner.publicKey,
})
.signers([state])
// .preInstructions([await program.account.state.createInstruction(state)])
.rpc();

let stateAccount = await forwarderProgram.account.state.fetch(state.publicKey);
authorityNonce = stateAccount.authorityNonce;
authority = PublicKey.createProgramAddressSync(
[
Buffer.from(anchor.utils.bytes.utf8.encode("forwarder")),
state.publicKey.toBuffer(),
Buffer.from([authorityNonce])
],
forwarderProgram.programId
);

console.log(`Generating ${n} oracles...`);
let futures = [];
for (let i = 0; i < n; i++) {
futures.push(generateOracle());
}
oracles = await Promise.all(futures);
});

// TODO: deploy mock receiver, forward the report there, assert on program log

it("Successfully receives a new, valid report", async () => {
const rawReport = Buffer.from([
// 32 byte workflow id
// 32 byte workflow execution id
// report data
]);

let hash = createHash("sha256")
.update(rawReport)
.digest();

let rawSignatures = [];
// for (let oracle of oracles.slice(0, f + 1)) {
// // sign with `f` + 1 oracles
// let { signature, recid } = secp256k1.ecdsaSign(
// hash,
// oracle.signer.secretKey
// );
// rawSignatures.push(...signature);
// rawSignatures.push(recid);
// }

let data = Buffer.concat([
Buffer.from([rawSignatures.length]),
Buffer.from(rawSignatures),
rawReport,
]);

const executionState = Keypair.generate();

await forwarderProgram.methods
.report(data)
.accounts({
state: state.publicKey,
authority: transmitter.publicKey,
forwarderAuthority: authority,
executionState: executionState.publicKey, // TODO: derive
receiverProgram: forwarderProgram.programId, // TODO:
})
.signers([transmitter])
.rpc();

// TODO: await until confirmation on all of these

});

it("Doesn't retransmit the same report", async () => {

});
})
2 changes: 1 addition & 1 deletion contracts/tests/ocr2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe("ocr2", () => {
const state = Keypair.generate();
// const stateSize = 8 + ;
const feed = Keypair.generate();
const payer = Keypair.generate();
const payer = Keypair.generate(); // TODO: both payer and fromWallet seem redundant, use payer
// const owner = Keypair.generate();
const owner = provider.wallet;
const mintAuthority = Keypair.generate();
Expand Down

0 comments on commit b99ddf1

Please sign in to comment.