can submit value

This commit is contained in:
De Facto 2021-02-15 16:17:45 +08:00
parent d445491cb3
commit 39476b0aec
7 changed files with 125 additions and 133 deletions

View File

@ -81,7 +81,7 @@ Next we create a new oracle to the feed we've created previously, and set its ow
``` ```
yarn solink add-oracle \ yarn solink add-oracle \
--feedAddress 3aTBom2uodyWkuVPiUkwCZ2HiFywdUx9tp7su7U2H4Nx \ --aggregatorAddress 3aTBom2uodyWkuVPiUkwCZ2HiFywdUx9tp7su7U2H4Nx \
--oracleName solink-test \ --oracleName solink-test \
--oracleOwner FosLwbttPgkEDv36VJLU3wwXcBSSoUGkh7dyZPsXNtT4 --oracleOwner FosLwbttPgkEDv36VJLU3wwXcBSSoUGkh7dyZPsXNtT4

View File

@ -3,6 +3,7 @@
use crate::processor::Processor; use crate::processor::Processor;
use solana_program::{ use solana_program::{
msg,
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
}; };
@ -14,11 +15,12 @@ fn process_instruction<'a>(
accounts: &'a [AccountInfo<'a>], accounts: &'a [AccountInfo<'a>],
instruction_data: &[u8], instruction_data: &[u8],
) -> ProgramResult { ) -> ProgramResult {
if let Err(error) = Processor::process(program_id, accounts, instruction_data) { msg!("call aggregator yo");
// catch the error so we can print it // if let Err(error) = Processor::process(program_id, accounts, instruction_data) {
// error.print::<Error>(); // // catch the error so we can print it
// msg!("{:?}", error); // // error.print::<Error>();
return Err(error); // // msg!("{:?}", error);
} // return Err(error);
// }
Ok(()) Ok(())
} }

View File

@ -53,7 +53,8 @@ mod tests {
#[test] #[test]
fn test_decode_instruction() -> ProgramResult { fn test_decode_instruction() -> ProgramResult {
let input = hex::decode("004254433a5553442020202020202020202020202020202020202020202020202002010c010100000000000000").map_err(|_| ProgramError::InvalidInstructionData)?; let mut input = hex::decode("004254433a5553442020202020202020202020202020202020202020202020202002010c010100000000000000").map_err(|_| ProgramError::InvalidInstructionData)?;
let mut input = hex::decode("02534f4c494e4b2d54455354202020202020202020202020202020202020202020").map_err(|_| ProgramError::InvalidInstructionData)?;
let inx = Instruction::try_from_slice(&input) let inx = Instruction::try_from_slice(&input)
.map_err(|_| ProgramError::InvalidInstructionData)?; .map_err(|_| ProgramError::InvalidInstructionData)?;

View File

@ -7,6 +7,7 @@ use crate::{
}; };
use solana_program::{ use solana_program::{
msg,
account_info::AccountInfo, account_info::AccountInfo,
clock::Clock, clock::Clock,
entrypoint::ProgramResult, entrypoint::ProgramResult,
@ -112,9 +113,11 @@ impl<'a> AddOracleContext<'a> {
fn process(&self) -> ProgramResult { fn process(&self) -> ProgramResult {
// Note: there can in fact be more oracles than max_submissions // Note: there can in fact be more oracles than max_submissions
let aggregator = Aggregator::load_initialized(self.aggregator)?; let aggregator = Aggregator::load_initialized(self.aggregator)?;
msg!("loaded aggregator");
aggregator.authorize(self.aggregator_owner)?; aggregator.authorize(self.aggregator_owner)?;
let mut oracle = Oracle::init_uninitialized(self.oracle)?; let mut oracle = Oracle::init_uninitialized(self.oracle)?;
msg!("loaded oracle");
oracle.is_initialized = true; oracle.is_initialized = true;
oracle.description = self.description; oracle.description = self.description;
oracle.owner = self.oracle_owner.into(); oracle.owner = self.oracle_owner.into();

View File

@ -29,6 +29,7 @@ import { decodeOracleInfo } from "./utils"
import { schema } from "./schema" import { schema } from "./schema"
import * as encoding from "./schema" import * as encoding from "./schema"
import { deserialize, serialize } from "borsh" import { deserialize, serialize } from "borsh"
import { conn } from "./context"
export const AggregatorLayout = BufferLayout.struct([]) export const AggregatorLayout = BufferLayout.struct([])
@ -57,15 +58,11 @@ interface InitializeInstructionParams extends InitializeParams {
} }
interface AddOracleParams { interface AddOracleParams {
owner: PublicKey
description: string
aggregator: PublicKey aggregator: PublicKey
// To prove you are the aggregator owner
aggregatorOwner: Account aggregatorOwner: Account
}
interface AddOracleInstructionParams extends AddOracleParams { oracleOwner: PublicKey
oracle: PublicKey description: string
} }
interface RemoveOracleParams { interface RemoveOracleParams {
@ -78,15 +75,17 @@ interface RemoveOracleParams {
interface RemoveOracleInstructionParams extends RemoveOracleParams {} interface RemoveOracleInstructionParams extends RemoveOracleParams {}
interface SubmitParams { interface SubmitParams {
aggregator: PublicKey accounts: {
oracle: PublicKey aggregator: { write: PublicKey }
// The oracle"s index roundSubmissions: { write: PublicKey }
submission: bigint answerSubmissions: { write: PublicKey }
// oracle owner oracle: { write: PublicKey }
owner: Account oracle_owner: Account
} }
interface SubmitInstructionParams extends SubmitParams {} round_id: BigInt
value: BigInt
}
interface WithdrawParams { interface WithdrawParams {
aggregator: PublicKey aggregator: PublicKey
@ -114,8 +113,6 @@ export default class FluxAggregator extends BaseProgram {
const answer_submissions = new Account() const answer_submissions = new Account()
const round_submissions = new Account() const round_submissions = new Account()
console.log({ config: params.config, program: this.programID.toString() })
const input = encoding.Initialize.serialize({ config: params.config }) const input = encoding.Initialize.serialize({ config: params.config })
await this.sendTx( await this.sendTx(
@ -158,6 +155,10 @@ export default class FluxAggregator extends BaseProgram {
public async addOracle(params: AddOracleParams): Promise<Account> { public async addOracle(params: AddOracleParams): Promise<Account> {
const oracle = new Account() const oracle = new Account()
const input = encoding.AddOracle.serialize({
description: params.description,
})
await this.sendTx( await this.sendTx(
[ [
await this.sys.createRentFreeAccountInstruction({ await this.sys.createRentFreeAccountInstruction({
@ -165,10 +166,13 @@ export default class FluxAggregator extends BaseProgram {
space: encoding.Oracle.size, space: encoding.Oracle.size,
programID: this.programID, programID: this.programID,
}), }),
this.addOracleInstruction({ this.instruction(input, [
...params, SYSVAR_RENT_PUBKEY,
oracle: oracle.publicKey, params.aggregator,
}), params.aggregatorOwner, // signed
oracle.publicKey,
params.oracleOwner,
]),
], ],
[this.account, oracle, params.aggregatorOwner] [this.account, oracle, params.aggregatorOwner]
) )
@ -181,32 +185,6 @@ export default class FluxAggregator extends BaseProgram {
return decodeOracleInfo(info) return decodeOracleInfo(info)
} }
private addOracleInstruction(
params: AddOracleInstructionParams
): TransactionInstruction {
const { oracle, owner, description, aggregator, aggregatorOwner } = params
const layout = BufferLayout.struct([
BufferLayout.u8("instruction"),
BufferLayout.blob(32, "description"),
])
return this.instructionEncode(
layout,
{
instruction: 1, // add oracle instruction
description: Buffer.from(description),
},
[
{ write: oracle },
owner,
SYSVAR_CLOCK_PUBKEY,
{ write: aggregator },
aggregatorOwner,
]
)
}
public async removeOracle(params: RemoveOracleParams): Promise<void> { public async removeOracle(params: RemoveOracleParams): Promise<void> {
await this.sendTx( await this.sendTx(
[this.removeOracleInstruction(params)], [this.removeOracleInstruction(params)],
@ -239,29 +217,18 @@ export default class FluxAggregator extends BaseProgram {
} }
public async submit(params: SubmitParams): Promise<void> { public async submit(params: SubmitParams): Promise<void> {
const input = encoding.Submit.serialize(params)
let auths = [
SYSVAR_CLOCK_PUBKEY,
...Object.values(params.accounts),
]
await this.sendTx( await this.sendTx(
[this.submitInstruction(params)], [
[this.account, params.owner] this.instruction(input, auths),
) ],
} [this.account, params.accounts.oracle_owner]
private submitInstruction(
params: SubmitInstructionParams
): TransactionInstruction {
const { aggregator, oracle, submission, owner } = params
const layout = BufferLayout.struct([
BufferLayout.u8("instruction"),
uint64("submission"),
])
return this.instructionEncode(
layout,
{
instruction: 3, // submit instruction
submission: u64LEBuffer(submission),
},
[{ write: aggregator }, SYSVAR_CLOCK_PUBKEY, { write: oracle }, owner]
) )
} }

View File

@ -18,12 +18,7 @@ import dotenv from "dotenv"
import FluxAggregator from "./FluxAggregator" import FluxAggregator from "./FluxAggregator"
import { import { decodeAggregatorInfo, sleep } from "./utils"
decodeAggregatorInfo,
walletFromEnv,
openDeployer,
sleep,
} from "./utils"
import * as feed from "./feed" import * as feed from "./feed"
import { AggregatorConfig } from "./schema" import { AggregatorConfig } from "./schema"
@ -39,43 +34,7 @@ const FLUX_AGGREGATOR_SO = path.resolve(
const network = (process.env.NETWORK || "local") as NetworkName const network = (process.env.NETWORK || "local") as NetworkName
const conn = solana.connect(network) const conn = solana.connect(network)
class AppContext { import { AppContext } from "./context"
static readonly AGGREGATOR_PROGRAM = "aggregatorProgram"
static async forAdmin() {
const deployer = await openDeployer()
const admin = await walletFromEnv("ADMIN_MNEMONIC", conn)
return new AppContext(deployer, admin)
}
static async forOracle() {
const deployer = await openDeployer()
const wallet = await walletFromEnv("ORACLE_MNEMONIC", conn)
return new AppContext(deployer, wallet)
}
constructor(public deployer: Deployer, public wallet: Wallet) {}
get aggregatorProgramID() {
return this.aggregatorProgramAccount.publicKey
}
get aggregator() {
return new FluxAggregator(this.wallet, this.aggregatorProgramID)
}
get aggregatorProgramAccount() {
const program = this.deployer.account(AppContext.AGGREGATOR_PROGRAM)
if (program == null) {
throw new Error(`flux aggregator program is not yet deployed`)
}
return program
}
}
function color(s, c = "black", b = false): string { function color(s, c = "black", b = false): string {
// 30m Black, 31m Red, 32m Green, 33m Yellow, 34m Blue, 35m Magenta, 36m Cyanic, 37m White // 30m Black, 31m Red, 32m Green, 33m Yellow, 34m Blue, 35m Magenta, 36m Cyanic, 37m White
@ -226,7 +185,7 @@ cli
cli cli
.command("add-oracle") .command("add-oracle")
.description("add an oracle to aggregator") .description("add an oracle to aggregator")
.option("--feedAddress <string>", "feed address") .option("--aggregatorAddress <string>", "aggregator address")
.option("--oracleName <string>", "oracle name") .option("--oracleName <string>", "oracle name")
.option("--oracleOwner <string>", "oracle owner address") .option("--oracleOwner <string>", "oracle owner address")
.action(async (opts) => { .action(async (opts) => {
@ -236,8 +195,8 @@ cli
log("add oracle...") log("add oracle...")
const oracle = await aggregator.addOracle({ const oracle = await aggregator.addOracle({
owner: new PublicKey(oracleOwner), oracleOwner: new PublicKey(oracleOwner),
description: oracleName.substr(0, 32).padEnd(32), description: oracleName,
aggregator: new PublicKey(feedAddress), aggregator: new PublicKey(feedAddress),
aggregatorOwner: wallet.account, aggregatorOwner: wallet.account,
}) })

View File

@ -2,7 +2,7 @@ import { PublicKey, Account } from "solray"
import BN from "bn.js" import BN from "bn.js"
import { deserialize, serialize } from "borsh" import { deserialize, serialize } from "borsh"
const MAX_ORACLES = 12 const MAX_ORACLES = 13
const boolMapper = { const boolMapper = {
encode: boolToInt, encode: boolToInt,
@ -28,7 +28,7 @@ const pubkeyMapper = {
// support strings that can be contained in at most 32 bytes // support strings that can be contained in at most 32 bytes
const str32Mapper = { const str32Mapper = {
encode: (str: String) => { encode: (str: String) => {
str = str.substr(0, 32).toUpperCase().padEnd(32) str = str.substr(0, 32).padEnd(32)
return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes return Buffer.from(str, "utf8").slice(0, 32) // truncate at 32 bytes
}, },
@ -50,7 +50,11 @@ abstract class Serialization {
} }
public serialize(): Buffer { public serialize(): Buffer {
return Buffer.from(serialize(schema, this)) let buf = Buffer.from(serialize(schema, this))
if (buf.length == 0) {
throw new Error("serialized buffer is 0. something wrong with schema")
}
return buf
} }
constructor(data) { constructor(data) {
@ -66,7 +70,7 @@ class Submission {
public static schema = { public static schema = {
kind: "struct", kind: "struct",
fields: [ fields: [
["time", "u64"], ["updated_at", "u64"],
["value", "u64"], ["value", "u64"],
["oracle", [32], pubkeyMapper], ["oracle", [32], pubkeyMapper],
], ],
@ -104,7 +108,7 @@ export class Submissions extends Serialization {
kind: "struct", kind: "struct",
fields: [ fields: [
["isInitialized", "u8", boolMapper], ["isInitialized", "u8", boolMapper],
["submissions", [MAX_ORACLES, Submission]], ["submissions", [Submission, MAX_ORACLES]],
], ],
} }
} }
@ -134,7 +138,8 @@ export class Aggregator extends Serialization {
public static size = 189 public static size = 189
public config!: AggregatorConfig public config!: AggregatorConfig
// public submissions!: Submission[] public roundSubmissions!: PublicKey
public answerSubmissions!: PublicKey
public static schema = { public static schema = {
kind: "struct", kind: "struct",
@ -143,9 +148,9 @@ export class Aggregator extends Serialization {
["isInitialized", "u8", boolMapper], ["isInitialized", "u8", boolMapper],
["owner", [32], pubkeyMapper], ["owner", [32], pubkeyMapper],
["round", Round], ["round", Round],
["round_submissions", [32], pubkeyMapper], ["roundSubmissions", [32], pubkeyMapper],
["answer", Answer], ["answer", Answer],
["answer_submissions", [32], pubkeyMapper], ["answerSubmissions", [32], pubkeyMapper],
], ],
} }
} }
@ -170,13 +175,51 @@ export class Initialize extends InstructionSerialization {
} }
} }
export class Configure extends InstructionSerialization {
public static schema = {
kind: "struct",
fields: [["config", AggregatorConfig]],
}
}
export class AddOracle extends InstructionSerialization {
public static schema = {
kind: "struct",
fields: [["description", [32], str32Mapper]],
}
}
export class RemoveOracle extends InstructionSerialization {
public static schema = {
kind: "struct",
fields: [],
}
}
export class Submit extends InstructionSerialization {
public static schema = {
kind: "struct",
fields: [
["round_id", "u64"],
["value", "u64"],
],
}
}
export class Instruction extends Serialization { export class Instruction extends Serialization {
public enum!: string public enum!: string
public static schema = { public static schema = {
kind: "enum", kind: "enum",
field: "enum", field: "enum",
values: [[Initialize.name, Initialize]], values: [
[Initialize.name, Initialize],
[Configure.name, Configure],
[AddOracle.name, AddOracle],
[RemoveOracle.name, RemoveOracle],
[Submit.name, Submit],
],
} }
public constructor(prop: { [key: string]: any }) { public constructor(prop: { [key: string]: any }) {
@ -215,8 +258,20 @@ function boolToInt(t: boolean) {
} }
} }
export class Oracle { export class Oracle extends Serialization {
public static size = 113 public static size = 113
public static schema = {
kind: "struct",
fields: [
["description", [32], str32Mapper],
["isInitialized", "u8", boolMapper],
["withdrawable", "u64"],
["allow_start_round", "u64"],
["aggregator", [32], pubkeyMapper],
["owner", [32], pubkeyMapper],
],
}
} }
// if there is optional or variable length items, what is: borsh_utils::get_packed_len::<Submission>()? // if there is optional or variable length items, what is: borsh_utils::get_packed_len::<Submission>()?
@ -225,11 +280,16 @@ export class Oracle {
export const schema = new Map([ export const schema = new Map([
[Aggregator, Aggregator.schema], [Aggregator, Aggregator.schema],
[Oracle, Oracle.schema],
[Round, Round.schema], [Round, Round.schema],
[Answer, Answer.schema], [Answer, Answer.schema],
[AggregatorConfig, AggregatorConfig.schema], [AggregatorConfig, AggregatorConfig.schema],
[Submissions, Submissions.schema], [Submissions, Submissions.schema],
[Submission, Submission.schema], [Submission, Submission.schema],
[Initialize, Initialize.schema],
[Instruction, Instruction.schema], [Instruction, Instruction.schema],
[Initialize, Initialize.schema],
[AddOracle, AddOracle.schema],
[Submit, Submit.schema],
] as any) as any ] as any) as any