@orao-network/solana-vrf-cb
Version:
ORAO Verifiable Random Function with Callback for Solana.
319 lines (318 loc) • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemainingAccount = exports.Callback = exports.Fulfilled = exports.ValidatedLookupAccount = exports.ValidatedRemainingAccount = exports.ValidatedCallbackAlt = exports.ValidatedCallback = exports.PendingAlt = exports.Pending = exports.RequestAccount = exports.RequestAltAccount = exports.Response = exports.Client = exports.NetworkConfiguration = exports.NetworkState = void 0;
const _1 = require(".");
const sha2_1 = require("@noble/hashes/sha2");
/**
* On-chain VRF state.
*/
class NetworkState {
constructor(bump, config, numRequests, numRegistered, numTerminated) {
this.bump = bump;
this.config = config;
this.numRequests = numRequests;
this.numRegistered = numRegistered;
this.numTerminated = numTerminated;
}
/** See {@link networkStateAddress} */
static findAddress(vrf_id = _1.PROGRAM_ID) {
return (0, _1.networkStateAddress)(vrf_id);
}
/** See {@link networkStateAddress} */
static createAddress(bump, vrf_id = _1.PROGRAM_ID) {
return (0, _1.networkStateAddress)(bump, vrf_id);
}
}
exports.NetworkState = NetworkState;
/**
* On-chain VRF configuration.
*/
class NetworkConfiguration {
constructor(authority, treasury, requestFee, callbackDeadline, fulfillAuthorities) {
this.authority = authority;
this.treasury = treasury;
this.requestFee = requestFee;
this.callbackDeadline = callbackDeadline;
this.fulfillAuthorities = fulfillAuthorities;
}
}
exports.NetworkConfiguration = NetworkConfiguration;
/**
* Registered client PDA.
*
* This PDA is created by the Callback VRF upon new client registration.
*
* Note that the balance of this PDA is used to pay request fees and rent.
*/
class Client {
constructor(bump, owner, program, state, numRequests, callback) {
this.bump = bump;
this.owner = owner;
this.program = program;
this.state = state;
this.numRequests = numRequests;
this.callback = callback;
}
}
exports.Client = Client;
/**
* Response of a single fulfill authority.
*/
class Response {
constructor(pubkey, randomness) {
this.pubkey = pubkey;
this.randomness = Array.isArray(randomness) ? new Uint8Array(randomness) : randomness;
}
static fromRawData(data) {
return new Response(data.pubkey, data.randomness);
}
}
exports.Response = Response;
/**
* A PDA allocated for every randomness request with Address Lookup Tables support.
*
* Holds request metadata and state.
*/
class RequestAltAccount {
constructor(bump, slot, client, seed, state) {
this.bump = bump;
this.slot = slot;
this.client = client;
this.seed = Array.isArray(seed) ? new Uint8Array(seed) : seed;
this.state = state;
}
/** See {@link requestAccountAddress} */
static findAddress(client, seed, vrf_id = _1.PROGRAM_ID) {
return (0, _1.requestAltAccountAddress)(client, seed, vrf_id);
}
/** See {@link requestAccountAddress} */
static createAddress(client, seed, bump, vrf_id = _1.PROGRAM_ID) {
return (0, _1.requestAltAccountAddress)(client, seed, bump, vrf_id);
}
static fromRawAccount(accountData) {
let state = "pending" in accountData.state
? new PendingAlt(accountData.state.pending["0"].responses.map(Response.fromRawData), accountData.state.pending["0"].callback
? ValidatedCallbackAlt.fromRawData(accountData.state.pending["0"].callback)
: null, accountData.state.pending["0"].lookupTables)
: new Fulfilled(accountData.state.fulfilled["0"].randomness, accountData.state.fulfilled["0"].responses
? accountData.state.fulfilled["0"].responses.map(Response.fromRawData)
: null);
return new RequestAltAccount(accountData.bump, accountData.slot, accountData.client, accountData.seed, state);
}
/** Returns the request seed */
getSeed() {
return this.seed;
}
/** Returns the {@link Client} PDA address */
getClient() {
return this.client;
}
/** Returns pending state (or `null` if this request was fulfilled) */
getPending() {
return "randomness" in this.state ? null : this.state;
}
/** Returns fulfilled state (or `null` if this request is still pending) */
getFulfilled() {
return "randomness" in this.state ? this.state : null;
}
}
exports.RequestAltAccount = RequestAltAccount;
/**
* A PDA allocated for every randomness request.
*
* Holds request metadata and state.
*/
class RequestAccount {
constructor(bump, slot, client, seed, state) {
this.bump = bump;
this.slot = slot;
this.client = client;
this.seed = Array.isArray(seed) ? new Uint8Array(seed) : seed;
this.state = state;
}
/** See {@link requestAccountAddress} */
static findAddress(client, seed, vrf_id = _1.PROGRAM_ID) {
return (0, _1.requestAccountAddress)(client, seed, vrf_id);
}
/** See {@link requestAccountAddress} */
static createAddress(client, seed, bump, vrf_id = _1.PROGRAM_ID) {
return (0, _1.requestAccountAddress)(client, seed, bump, vrf_id);
}
static fromRawAccount(accountData) {
let state = "pending" in accountData.state
? new Pending(accountData.state.pending["0"].responses.map(Response.fromRawData), accountData.state.pending["0"].callback
? ValidatedCallback.fromRawData(accountData.state.pending["0"].callback)
: null, accountData.state.pending["0"].callbackOverride)
: new Fulfilled(accountData.state.fulfilled["0"].randomness, accountData.state.fulfilled["0"].responses
? accountData.state.fulfilled["0"].responses.map(Response.fromRawData)
: null);
return new RequestAccount(accountData.bump, accountData.slot, accountData.client, accountData.seed, state);
}
/** Returns the request seed */
getSeed() {
return this.seed;
}
/** Returns the {@link Client} PDA address */
getClient() {
return this.client;
}
/** Returns pending state (or `null` if this request was fulfilled) */
getPending() {
return "randomness" in this.state ? null : this.state;
}
/** Returns fulfilled state (or `null` if this request is still pending) */
getFulfilled() {
return "randomness" in this.state ? this.state : null;
}
}
exports.RequestAccount = RequestAccount;
/** Represents a state of a pending randomness request {@link RequestAccount.state } */
class Pending {
constructor(responses, callback, callbackOverride) {
this.responses = responses;
this.callback = callback;
this.callbackOverride = callbackOverride;
}
isFulfilledBy(key) {
return this.responses.find((response) => response.pubkey.equals(key)) !== undefined;
}
}
exports.Pending = Pending;
/** Represents a state of a pending randomness request {@link RequestAltAccount.state } */
class PendingAlt {
constructor(responses, callback, lookupTables) {
this.responses = responses;
this.callback = callback;
this.lookupTables = lookupTables;
}
isFulfilledBy(key) {
return this.responses.find((response) => response.pubkey.equals(key)) !== undefined;
}
}
exports.PendingAlt = PendingAlt;
/**
* This is a validated callback stored on-chain (see {@link Callback}).
*/
class ValidatedCallback {
constructor(remainingAccounts, data) {
this.remainingAccounts = remainingAccounts;
this.data = Array.isArray(data) ? new Uint8Array(data) : data;
}
static fromRawData(data) {
return new ValidatedCallback(data.remainingAccounts.map(ValidatedRemainingAccount.fromRawData), Array.isArray(data.data) ? new Uint8Array(data.data) : data.data);
}
}
exports.ValidatedCallback = ValidatedCallback;
/**
* This is a validated callback stored on-chain (see {@link Callback}).
*/
class ValidatedCallbackAlt {
constructor(accountsHash, remainingAccounts, data) {
this.accountsHash = Array.isArray(accountsHash)
? new Uint8Array(accountsHash)
: accountsHash;
this.remainingAccounts = remainingAccounts;
this.data = Array.isArray(data) ? new Uint8Array(data) : data;
}
static fromRawData(data) {
return new ValidatedCallbackAlt(Array.isArray(data.accountsHash)
? new Uint8Array(data.accountsHash)
: data.accountsHash, data.remainingAccounts.map((x) => {
return "lookup" in x
? ValidatedLookupAccount.fromRawData(x.lookup[0])
: ValidatedRemainingAccount.fromRawData(x.plain[0]);
}), Array.isArray(data.data) ? new Uint8Array(data.data) : data.data);
}
/**
* Resolves lookup accounts back to plain accounts (see {@link compileAccounts}).
*
* @param lookupTables - the list of lookup tables given upon compilation
*/
decompile(lookupTables) {
const accountsHashData = Buffer.alloc(32 * this.remainingAccounts.length);
const output = [];
for (const account of this.remainingAccounts) {
if ("tableIndex" in account) {
let table = lookupTables[account.tableIndex];
if (!table) {
throw new Error("Table index out of bounds");
}
let address = table.state.addresses[account.addressIndex];
if (!table) {
throw new Error("Address index out of bounds");
}
output.push({ pubkey: address, isWritable: account.isWritable });
}
else {
output.push(account);
}
}
for (let i = 0; i < output.length; i++) {
output[i].pubkey.toBuffer().copy(accountsHashData, 32 * i);
}
let expectedHash = Buffer.from((0, sha2_1.sha256)(accountsHashData));
if (!expectedHash.equals(this.accountsHash)) {
throw new Error(`accountsHash mismatch ${expectedHash.toString("hex")} != ${Buffer.from(this.accountsHash).toString("hex")} `);
}
return output;
}
}
exports.ValidatedCallbackAlt = ValidatedCallbackAlt;
/** This is a validated remaining account stored on-chain (see {@link RemainingAccount}) */
class ValidatedRemainingAccount {
constructor(pubkey, isWritable) {
this.pubkey = pubkey;
this.isWritable = isWritable;
}
static fromRawData(data) {
return new ValidatedRemainingAccount(data.pubkey, data.isWritable);
}
}
exports.ValidatedRemainingAccount = ValidatedRemainingAccount;
/** This is a validated remaining account stored on-chain (see {@link RemainingAccount}) */
class ValidatedLookupAccount {
constructor(tableIndex, addressIndex, isWritable) {
this.tableIndex = tableIndex;
this.addressIndex = addressIndex;
this.isWritable = isWritable;
}
static fromRawData(data) {
return new ValidatedLookupAccount(data.tableIndex, data.addressIndex, data.isWritable);
}
}
exports.ValidatedLookupAccount = ValidatedLookupAccount;
/** Represents a state of a fulfilled randomness request {@link RequestAccount.state } */
class Fulfilled {
constructor(randomness, responses) {
this.randomness = Array.isArray(randomness) ? new Uint8Array(randomness) : randomness;
this.responses = responses;
}
}
exports.Fulfilled = Fulfilled;
/**
* A callback definition.
*
* This structure is used to define client-level or request-level callbacks:
*
* 1. _client-level callback_ — defined upon the client registration and couldn't be avoided, but
* you can override it with the _request-level callback_. Additionally You can update the
* _client-level callback_ using the `SetCallback` instruction (see {@link SetCallbackBuilder}).
* 2. _request-level callback_ — overrides the _client-level callback_ (even if it is not defined).
*/
class Callback {
constructor(data, remainingAccounts = []) {
this.data = data;
this.remainingAccounts = remainingAccounts;
}
}
exports.Callback = Callback;
/**
* An account to add to the callback invocation (see {@link Callback.remainingAccounts})
*/
class RemainingAccount {
constructor(pubkey, seeds) {
this.pubkey = pubkey;
this.seeds = seeds;
}
}
exports.RemainingAccount = RemainingAccount;