Skip to content

Commit

Permalink
Feature: clear pending offchain (#117)
Browse files Browse the repository at this point in the history
* add validation to write config + fix assert.fail test cases

* add function + test cases to clear pending offchain config

* gauntlet command to reset pending offchain config
  • Loading branch information
aalu1418 authored Jan 19, 2022
1 parent e7fc4c1 commit b56eac8
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 2 deletions.
19 changes: 19 additions & 0 deletions contracts/programs/ocr2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub mod ocr2 {
offchain_config.len() < config.pending_offchain_config.remaining_capacity(),
InvalidInput
);
require!(config.pending_offchain_config.version != 0, InvalidInput);
config.pending_offchain_config.extend(&offchain_config);
Ok(())
}
Expand Down Expand Up @@ -147,6 +148,24 @@ pub mod ocr2 {
Ok(())
}

#[access_control(owner(&ctx.accounts.state, &ctx.accounts.authority))]
pub fn reset_pending_offchain_config(ctx: Context<SetConfig>) -> ProgramResult {
let state = &mut *ctx.accounts.state.load_mut()?;
let config = &mut state.config;

// Require that at least some data was written
require!(
config.pending_offchain_config.version > 0
|| !config.pending_offchain_config.is_empty(),
InvalidInput
);

// reset staging area
config.pending_offchain_config.clear();
config.pending_offchain_config.version = 0;
Ok(())
}

#[access_control(owner(&ctx.accounts.state, &ctx.accounts.authority))]
pub fn set_config(
ctx: Context<SetConfig>,
Expand Down
73 changes: 71 additions & 2 deletions contracts/tests/ocr2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,19 +436,88 @@ describe('ocr2', async () => {
authority: owner.publicKey,
},
});
assert.fail("beginOffchainConfig shouldn't have succeeded!")
} catch {
// beginOffchainConfig should fail
return
}
assert.fail("beginOffchainConfig shouldn't have succeeded!")
});

it("Can't write offchain config if begin has not been called", async () => {
try {
await program.rpc.writeOffchainConfig(
Buffer.from([4, 5, 6]),
{
accounts: {
state: state.publicKey,
authority: owner.publicKey,
},
});
} catch {
// writeOffchainConfig should fail
return
}
assert.fail("writeOffchainConfig shouldn't have succeeded!")
});

it("ResetPendingOffchainConfig clears pending state", async () => {

await program.rpc.beginOffchainConfig(
new BN(2),
{
accounts: {
state: state.publicKey,
authority: owner.publicKey,
},
});
await program.rpc.writeOffchainConfig(
Buffer.from([4, 5, 6]),
{
accounts: {
state: state.publicKey,
authority: owner.publicKey,
},
});
let account = await program.account.state.fetch(state.publicKey);
assert.ok(account.config.pendingOffchainConfig.version != 0);
assert.ok(account.config.pendingOffchainConfig.len != 0);

await program.rpc.resetPendingOffchainConfig(
{
accounts: {
state: state.publicKey,
authority: owner.publicKey,
},
});
account = await program.account.state.fetch(state.publicKey);
assert.ok(account.config.pendingOffchainConfig.version == 0);
assert.ok(account.config.pendingOffchainConfig.len == 0);
})

it("Can't reset pending config if already in new state", async () => {
try {
await program.rpc.resetPendingOffchainConfig(
{
accounts: {
state: state.publicKey,
authority: owner.publicKey,
},
});
} catch {
// resetPendingOffchainConfig should fail
return
}
assert.fail("resetPendingOffchainConfig shouldn't have succeeded!")
});

it("Can't transmit a round if not the writer", async () => {
try {
await transmit(1, 1, new BN(1));
assert.fail("transmit() shouldn't have succeeded!");
} catch {
// transmit should fail
return
}
assert.fail("transmit() shouldn't have succeeded!");
});

it('Sets the cluster as the feed writer', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,22 @@
],
"args": []
},
{
"name": "resetPendingOffchainConfig",
"accounts": [
{
"name": "state",
"isMut": true,
"isSigner": false
},
{
"name": "authority",
"isMut": false,
"isSigner": true
}
],
"args": []
},
{
"name": "setConfig",
"accounts": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import BeginOffchainConfig from './offchainConfig/begin'
import CommitOffchainConfig from './offchainConfig/commit'
import SetOffchainConfigFlow from './offchainConfig/setOffchainConfig.flow'
import WriteOffchainConfig from './offchainConfig/write'
import ResetPendingOffchainConfig from './offchainConfig/resetPending'
import PayRemaining from './payRemaining'
import ReadState from './read'
import SetBillingAccessController from './setBillingAccessController'
Expand Down Expand Up @@ -31,6 +32,7 @@ export default [
BeginOffchainConfig,
WriteOffchainConfig,
CommitOffchainConfig,
ResetPendingOffchainConfig,
SetBillingAccessController,
SetRequesterAccessController,
// Inspection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Result } from '@chainlink/gauntlet-core'
import { logger, prompt } from '@chainlink/gauntlet-core/dist/utils'
import { SolanaCommand, TransactionResponse } from '@chainlink/gauntlet-solana'
import { PublicKey } from '@solana/web3.js'
import { CONTRACT_LIST, getContract } from '../../../../lib/contracts'

export default class ResetPendingOffchainConfig extends SolanaCommand {
static id = 'ocr2:reset_pending_offchain_config'
static category = CONTRACT_LIST.OCR_2

static examples = ['yarn gauntlet ocr2:reset_pending_offchain_config --network=devnet --state=[OCR2_STATE]']
constructor(flags, args) {
super(flags, args)

this.require(!!this.flags.state, 'Please provide flags with "state"')
}

execute = async () => {
const ocr2 = getContract(CONTRACT_LIST.OCR_2, '')
const address = ocr2.programId.toString()
const program = this.loadProgram(ocr2.idl, address)

const state = new PublicKey(this.flags.state)
const owner = this.wallet.payer

const info = await program.account.state.fetch(state)
console.log(info.config.pendingOffchainConfig)
this.require(
info.config.pendingOffchainConfig.version != 0 || info.config.pendingOffchainConfig.len != 0,
'pending offchain config version is already in reset state',
)

await prompt(`Reset pending offchain config?`)

const tx = await program.rpc.resetPendingOffchainConfig({
accounts: {
state: state,
authority: owner.publicKey,
},
})

logger.success(`Reset pending offchain config on tx ${tx}`)

return {
responses: [
{
tx: this.wrapResponse(tx, state.toString(), { state: state.toString() }),
contract: state.toString(),
},
],
} as Result<TransactionResponse>
}
}

0 comments on commit b56eac8

Please sign in to comment.