Expand description
§ORAO VRF Callback
A crate to interact with the orao-vrf-cb smart contract on the Solana network.
Provides an interface to request verifiable randomness (Ed25519 Signature) on the Solana network and receive a CPI callback upon fulfill.
§Crate features
-
sdk(default) — use this feature to build an off-chain client -
cpi— use this feature to integrate your program with the oracle[dependencies.orao-solana-vrf-cb] version = "..." default-features = false features = ["cpi"]
§Integration
The integration process consists of the following steps:
- Write and deploy a client program. It must be able to invoke either
RequestorRequestAltinstruction via CPI. It might define a callback — in fact any program instruction could be called. - Register your program as a VRF client by sending the
Registerinstruction. - Fund a new client created on a previous step (just transfer some SOL to the
ClientPDA). - Now you are ready to perform a
Request/RequestAltinstruction CPI.
§Callback functionality
§Side Note
Due to historical reasons callback functionality is split in two parts with slightly different capabilities:
Callback— normal callbackCallbackAlt— ALT here stands for Address Lookup Tables. This type of callback is able to use Solana’s feature that allows developers to create a collection of related addresses to efficiently load more addresses in a single transaction. This is only possible for a request-level callback (see bellow).For the same reason there are two kinds of request accounts:
RequestAccount— request account created by theRequestinstruction.RequestAltAccount— request account created by theRequestAltinstruction.
§Account Order Convention
There is a convention that RequestAlt instruction follows:
If
nis the number of Lookup Tables used by a callback then the firstnaccounts in theContext::remaining_accountslist must be the corresponding Lookup Table accounts.
Namely the instruction accepts the RequestAltParams::num_lookup_tables parameter
and expects the extended list of accounts (see the “Accounts” section in RequestAlt docs)
There is a helper function that simplifies preparing Lookup Table accounts
for the RequestAlt instruction that is parse_lookup_tables so you are
encouraged to use it to prepare the CPI call. You also may follow the same
convention if you are using the Anchor Framework:
let mut cpi_call_remaining_accounts = ctx
.remaining_accounts
.get(..num_lookup_tables as usize)
.expect("call does not follow the convention")
.to_vec();
let lookup_tables = orao_vrf_cb::utils::parse_lookup_tables(&cpi_call_remaining_accounts)?;
// 1. prepare the callback using `CallbackAlt::compile_accounts`
// and `lookup_tables` obtained above
// 2. extend cpi_call_remaining_accounts with writable accounts if necessary
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts)
.with_signer(signers_seeds)
.with_remaining_accounts(cpi_call_remaining_accounts);
orao_vrf_cb::cpi::request_alt(
cpi_ctx,
RequestAltParams::new(seed)
.with_callback(Some(callback))
.with_num_lookup_tables(num_lookup_tables),
)?;§Callback
A callback is an instruction invoked via CPI as soon as the randomness request is fulfilled.
Note that callbacks are optional. You can go without a callback giving None during the
client registration. Also note, that there are two levels of callbacks:
- Client-level callback — defined either upon or after the client registration.
This callback will be used for every
Request(notRequestAlt) of this client if not overridden by the Request-level callback. Effective client level callback could be observed in theClient::callbackfield of the Client PDA and could be updated using theSetCallbackinstruction. - Request-level callback — sets a callback for the current request
RequestorRequestAlt(overrides the Client-level callback).
§Callback rules
-
callback instruction will be called as an instruction of the registered program
-
callback instruction will be called with the following list of accounts:
- The
ClientPDA (signer). - The state PDA (writable) (see
Client::state). - The
NetworkStatePDA (read-only). - The corresponding
RequestAccountPDA (read-only). - [ … zero or more remaining accounts (see bellow) ]
- The
§Remaining accounts of a callback
Use the following helpers to add remaining accounts to the callback:
- for a normal
Callback— useCallback::with_remaining_accountandCallback::with_remaining_accountshelpers. Or just directly extend theCallback::remaining_accountsfield. - for a
CallbackAlt— first create a vec ofRemainingAccounts and then use theCallbackAlt::compile_accountshelper.
There exists three kinds of remaining accounts:
- Arbitrary read-only account — it is possible to give an arbitrary
read-only account to a callback. Use the
RemainingAccount::readonlyconstructor to build one. - Arbitrary writable account — it is possible to give an arbitrary
writable account as long as it is authorized by the caller - i.e.
if it is given as writable to the corresponding
Request/RequestAltorRegisterinstruction. Use theRemainingAccount::arbitrary_writableconstructor. - Writable PDA — it is always possible to give a writable account as long
as it is a PDA of the client program - just provide the proper seeds so
that VRF is able to verify the address. Use the
RemainingAccount::writableconstructor.
§Callback faults
If Callback invocation is somehow fails then it is considered as a client misbehavior - well-written client’s callback should never fail.
There are two kinds of callback faults possible:
- On-chain fail — callback failed after the instruction was accepted by the network. This faulty call is visible on-chain and the logs could be easily inspected.
- Off-chain fail — callback failed before the instruction was accepted
by the network. This faulty call is not visible on-chain and appears as
a request that not get fulfilled (in fact it will be fulfilled after the
callback_deadlinereached). We’re working on making it easier to debug such a case.
In any case the oracle will continue trying to fulfill such a client’s request
with increasing interval but eventually will fulfill it without invoking the
Callback at all (see NetworkConfiguration::callback_deadline).
§Clients
Any program may register any number of clients as long as unique state PDA is used for every registration. Every registered client maintains a SOL balance used to pay request fee and rent (note that the rent is reimbursed upon the fulfill).
It is trivial to fund a client — you should transfer some SOL to its address.
The Withdraw instruction should be used to withdraw client funds but note
that you couldn’t withdraw past the Client PDA rent.
The client ownership might be transferred using the Transfer instruction.
Re-exports§
pub use constants::*;pub use instructions::*;
Modules§
- accounts
- An Anchor generated module, providing a set of structs
mirroring the structs deriving
Accounts, where each field is aPubkey. This is useful for specifying accounts for a client. - constants
- error
- events
- instruction
- An Anchor generated module containing the program’s set of
instructions, where each method handler in the
#[program]mod is associated with a struct defining the input arguments to the method. These should be used directly, when one wants to serialize Anchor instruction data, for example, when speciying instructions on a client. - instructions
- orao_
vrf_ cb - program
- Module representing the program.
- sdk
sdkand non-idl-build - Off-chain client module
- state
- utils
Constants§
- ID_
CONST - Const version of
ID
Statics§
- ID
- The static program ID
Functions§
- check_
id - Confirms that a given pubkey is equivalent to the program ID
- entry
- The Anchor codegen exposes a programming model where a user defines
a set of methods inside of a
#[program]module in a way similar to writing RPC request handlers. The macro then generates a bunch of code wrapping these user defined methods into something that can be executed on Solana. - id
- Returns the program ID
- id_
const - Const version of
ID - majority
- Helper that returns the majority for the given total value.
- quorum
- Helper that checks for Byzantine quorum.
- xor_
array - Helper that XORes
rintol.