-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from project-serum/armani/realize
Program interfaces
- Loading branch information
Showing
29 changed files
with
903 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cluster = "localnet" | ||
wallet = "~/.config/solana/id.json" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
[workspace] | ||
members = [ | ||
"programs/*" | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "counter-auth" | ||
version = "0.1.0" | ||
description = "Created with Anchor" | ||
edition = "2018" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] | ||
name = "counter_auth" | ||
|
||
[features] | ||
no-entrypoint = [] | ||
no-idl = [] | ||
cpi = ["no-entrypoint"] | ||
default = [] | ||
|
||
[dependencies] | ||
anchor-lang = { git = "https://github.com/project-serum/anchor" } | ||
counter = { path = "../counter", features = ["cpi"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[target.bpfel-unknown-unknown.dependencies.std] | ||
features = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
//! counter-auth is an example of a program *implementing* an external program | ||
//! interface. Here the `counter::Auth` trait, where we only allow a count | ||
//! to be incremented if it changes the counter from odd -> even or even -> odd. | ||
//! Creative, I know. :P. | ||
#![feature(proc_macro_hygiene)] | ||
|
||
use anchor_lang::prelude::*; | ||
use counter::Auth; | ||
|
||
#[program] | ||
pub mod counter_auth { | ||
use super::*; | ||
|
||
#[state] | ||
pub struct CounterAuth {} | ||
|
||
// TODO: remove this impl block after addressing | ||
// https://github.com/project-serum/anchor/issues/71. | ||
impl CounterAuth { | ||
pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> { | ||
Ok(Self {}) | ||
} | ||
} | ||
|
||
impl<'info> Auth<'info, Empty> for CounterAuth { | ||
fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult { | ||
if current % 2 == 0 { | ||
if new % 2 == 0 { | ||
return Err(ProgramError::Custom(50)); // Arbitrary error code. | ||
} | ||
} else { | ||
if new % 2 == 1 { | ||
return Err(ProgramError::Custom(60)); // Arbitrary error code. | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Accounts)] | ||
pub struct Empty {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "counter" | ||
version = "0.1.0" | ||
description = "Created with Anchor" | ||
edition = "2018" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] | ||
name = "counter" | ||
|
||
[features] | ||
no-entrypoint = [] | ||
no-idl = [] | ||
cpi = ["no-entrypoint"] | ||
default = [] | ||
|
||
[dependencies] | ||
anchor-lang = { git = "https://github.com/project-serum/anchor" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[target.bpfel-unknown-unknown.dependencies.std] | ||
features = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
//! counter is an example program that depends on an external interface | ||
//! that another program must implement. This allows our program to depend | ||
//! on another program, without knowing anything about it other than the fact | ||
//! that it implements the `Auth` trait. | ||
//! | ||
//! Here, we have a counter, where, in order to set the count, the `Auth` | ||
//! program must first approve the transaction. | ||
#![feature(proc_macro_hygiene)] | ||
|
||
use anchor_lang::prelude::*; | ||
|
||
#[program] | ||
pub mod counter { | ||
use super::*; | ||
|
||
#[state] | ||
pub struct Counter { | ||
pub count: u64, | ||
pub auth_program: Pubkey, | ||
} | ||
|
||
impl Counter { | ||
pub fn new(_ctx: Context<Empty>, auth_program: Pubkey) -> Result<Self> { | ||
Ok(Self { | ||
count: 0, | ||
auth_program, | ||
}) | ||
} | ||
|
||
#[access_control(SetCount::accounts(&self, &ctx))] | ||
pub fn set_count(&mut self, ctx: Context<SetCount>, new_count: u64) -> Result<()> { | ||
// Ask the auth program if we should approve the transaction. | ||
let cpi_program = ctx.accounts.auth_program.clone(); | ||
let cpi_ctx = CpiContext::new(cpi_program, Empty {}); | ||
auth::is_authorized(cpi_ctx, self.count, new_count)?; | ||
|
||
// Approved, so update. | ||
self.count = new_count; | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Accounts)] | ||
pub struct Empty {} | ||
|
||
#[derive(Accounts)] | ||
pub struct SetCount<'info> { | ||
auth_program: AccountInfo<'info>, | ||
} | ||
|
||
impl<'info> SetCount<'info> { | ||
// Auxiliary account validation requiring program inputs. As a convention, | ||
// we separate it from the business logic of the instruction handler itself. | ||
pub fn accounts(counter: &Counter, ctx: &Context<SetCount>) -> Result<()> { | ||
if ctx.accounts.auth_program.key != &counter.auth_program { | ||
return Err(ErrorCode::InvalidAuthProgram.into()); | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[interface] | ||
pub trait Auth<'info, T: Accounts<'info>> { | ||
fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> ProgramResult; | ||
} | ||
|
||
#[error] | ||
pub enum ErrorCode { | ||
#[msg("Invalid auth program.")] | ||
InvalidAuthProgram, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
const anchor = require('@project-serum/anchor'); | ||
const assert = require("assert"); | ||
|
||
describe("interface", () => { | ||
// Configure the client to use the local cluster. | ||
anchor.setProvider(anchor.Provider.env()); | ||
|
||
const counter = anchor.workspace.Counter; | ||
const counterAuth = anchor.workspace.CounterAuth; | ||
it("Is initialized!", async () => { | ||
await counter.state.rpc.new(counterAuth.programId); | ||
|
||
const stateAccount = await counter.state(); | ||
assert.ok(stateAccount.count.eq(new anchor.BN(0))); | ||
assert.ok(stateAccount.authProgram.equals(counterAuth.programId)); | ||
}); | ||
|
||
it("Should fail to go from even to event", async () => { | ||
await assert.rejects( | ||
async () => { | ||
await counter.state.rpc.setCount(new anchor.BN(4), { | ||
accounts: { | ||
authProgram: counterAuth.programId, | ||
}, | ||
}); | ||
}, | ||
(err) => { | ||
if (err.toString().split("custom program error: 0x32").length !== 2) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
); | ||
}); | ||
|
||
it("Shold succeed to go from even to odd", async () => { | ||
await counter.state.rpc.setCount(new anchor.BN(3), { | ||
accounts: { | ||
authProgram: counterAuth.programId, | ||
}, | ||
}); | ||
const stateAccount = await counter.state(); | ||
assert.ok(stateAccount.count.eq(new anchor.BN(3))); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.