-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
REFACTOR: Simplified keeper bot #36
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e914c52
creating validator history accounts
coachchucksol fa1e385
all in one loop now
coachchucksol 4be1c38
loop done
coachchucksol 52eb0e9
removed unused imports
coachchucksol d5c7ddd
needs testing
coachchucksol 90626f5
need to refactor ixs
coachchucksol 8abb7d2
needs another lookthrough
coachchucksol fb1828e
looking good
coachchucksol 77cecf4
debugging
coachchucksol 1219a5a
cleaning up
coachchucksol 61b1bd0
cleaned up ready for pr
coachchucksol a7c288d
ran through fired everything
coachchucksol f9eb300
added readme
coachchucksol fc37ab1
linted
coachchucksol 8954675
not sure why linting is failing
coachchucksol 16cee52
little fixes
coachchucksol 28a4776
before emit removal
coachchucksol a1673c9
working on emit
coachchucksol 1b9ac08
emit is now at the end
coachchucksol f2ea52b
Merge remote-tracking branch 'origin/master' into christian/simple-ke…
coachchucksol b8d9ec5
removed duplicate emit functions
coachchucksol File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,6 @@ test-ledger | |
*/test-ledger | ||
.idea/ | ||
**/targets | ||
**/credentials | ||
**/config | ||
**/*.env |
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,5 @@ | ||
{ | ||
"cSpell.words": [ | ||
"datapoint" | ||
] | ||
} | ||
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,53 @@ | ||
## Keeper State | ||
The `KeeperState` keeps track of: | ||
- current epoch data | ||
- running tally of all operations successes and failures for the given epoch | ||
- all accounts fetched from the RPC that are needed downstream | ||
|
||
Note: All maps are key'd by the `vote_account` | ||
|
||
## Program | ||
|
||
### Initialize | ||
Gather all needed arguments, and initialize the global `KeeperState`. | ||
|
||
### Loop | ||
The forever loop consists of three parts: **Fetch**, **Fire** and **Emit**. There is only ever one **Fetch** and **Emit** section, and there can be several **Fire** sections. | ||
|
||
The **Fire** sections can run on independent cadences - say we want the Validator History functions to run every 300sec and we want to emit metrics every 60sec. | ||
|
||
The **Fetch** section is run _before_ and **Fire** section. | ||
The **Emit** section is *one tick* after any **Fire** section. | ||
|
||
#### Fetch | ||
The **Fetch** section is in charge of three operations: | ||
- Keeping track of the current epoch and resetting the runs and error counts for each operation | ||
- Creating any missing accounts needed for the downstream **Fires** to run | ||
- Fetching and updating all of the needed accounts needed downstream | ||
|
||
This is accomplished is by running three functions within the **Fetch** section | ||
- `pre_create_update` - Updates epoch, and fetches all needed accounts that are not dependant on any missing accounts. | ||
- `create_missing_accounts` - Creates the missing accounts, which can be determined by the accounts fetched in the previous step | ||
- `post_create_update` - Fetches any last accounts that needed the missing accounts | ||
|
||
Since this is run before every **FIRE** section, some accounts will be fetched that are not needed. This may seem wasteful but the simplicity of having a synchronized global is worth the cost. | ||
|
||
Notes: | ||
- The **Fetch** section is the only section that will mutate the `KeeperState`. | ||
- If anything in the **Fetch** section fails, no **Fires** will run | ||
|
||
#### Fire | ||
There are several **Fire** sections running at their own cadence. Before any **Fire** section is run, the **Fetch** section will be called. | ||
|
||
Each **Fire** is a call to `operations::operation_name::fire` which will fire off the operation and return the new count of runs and errors for that operation to be saved in the `KeeperState` | ||
|
||
Notes: | ||
- Each **Fire** is self contained, one should not be dependant on another. | ||
- No **Fire* will fetch any accounts, if there are needs for them, they should be added to the `KeeperState` | ||
|
||
|
||
#### Emit | ||
This section emits the state of the Keeper one tick after any operation has been called. This is because we want to emit a failure of any **Fetch** operation, which on failure advances the tick. | ||
|
||
|
||
|
This file was deleted.
Oops, something went wrong.
52 changes: 52 additions & 0 deletions
52
keepers/validator-keeper/src/entries/copy_vote_account_entry.rs
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,52 @@ | ||
use anchor_lang::{InstructionData, ToAccountMetas}; | ||
use keeper_core::{Address, UpdateInstruction}; | ||
use solana_sdk::instruction::Instruction; | ||
use solana_sdk::pubkey::Pubkey; | ||
use validator_history::Config; | ||
use validator_history::ValidatorHistory; | ||
|
||
pub struct CopyVoteAccountEntry { | ||
pub vote_account: Pubkey, | ||
pub validator_history_account: Pubkey, | ||
pub config_address: Pubkey, | ||
pub program_id: Pubkey, | ||
pub signer: Pubkey, | ||
} | ||
|
||
impl CopyVoteAccountEntry { | ||
pub fn new(vote_account: &Pubkey, program_id: &Pubkey, signer: &Pubkey) -> Self { | ||
let (validator_history_account, _) = Pubkey::find_program_address( | ||
&[ValidatorHistory::SEED, &vote_account.to_bytes()], | ||
program_id, | ||
); | ||
let (config_address, _) = Pubkey::find_program_address(&[Config::SEED], program_id); | ||
Self { | ||
vote_account: *vote_account, | ||
validator_history_account, | ||
config_address, | ||
program_id: *program_id, | ||
signer: *signer, | ||
} | ||
} | ||
} | ||
|
||
impl Address for CopyVoteAccountEntry { | ||
fn address(&self) -> Pubkey { | ||
self.validator_history_account | ||
} | ||
} | ||
|
||
impl UpdateInstruction for CopyVoteAccountEntry { | ||
fn update_instruction(&self) -> Instruction { | ||
Instruction { | ||
program_id: self.program_id, | ||
accounts: validator_history::accounts::CopyVoteAccount { | ||
validator_history_account: self.validator_history_account, | ||
vote_account: self.vote_account, | ||
signer: self.signer, | ||
} | ||
.to_account_metas(None), | ||
data: validator_history::instruction::CopyVoteAccount {}.data(), | ||
} | ||
} | ||
} |
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,159 @@ | ||
use anchor_lang::InstructionData; | ||
use anchor_lang::ToAccountMetas; | ||
use bytemuck::{bytes_of, Pod, Zeroable}; | ||
use keeper_core::Address; | ||
use solana_sdk::{ | ||
compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey, | ||
signature::Signature, | ||
}; | ||
|
||
use crate::{derive_validator_history_address, derive_validator_history_config_address}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct GossipEntry { | ||
pub vote_account: Pubkey, | ||
pub validator_history_account: Pubkey, | ||
pub config: Pubkey, | ||
pub signature: Signature, | ||
pub message: Vec<u8>, | ||
pub program_id: Pubkey, | ||
pub identity: Pubkey, | ||
pub signer: Pubkey, | ||
} | ||
|
||
impl GossipEntry { | ||
pub fn new( | ||
vote_account: &Pubkey, | ||
signature: &Signature, | ||
message: &[u8], | ||
program_id: &Pubkey, | ||
identity: &Pubkey, | ||
signer: &Pubkey, | ||
) -> Self { | ||
let validator_history_account = derive_validator_history_address(vote_account, program_id); | ||
let config = derive_validator_history_config_address(program_id); | ||
Self { | ||
vote_account: *vote_account, | ||
validator_history_account, | ||
config, | ||
signature: *signature, | ||
message: message.to_vec(), | ||
program_id: *program_id, | ||
identity: *identity, | ||
signer: *signer, | ||
} | ||
} | ||
} | ||
|
||
impl Address for GossipEntry { | ||
fn address(&self) -> Pubkey { | ||
self.validator_history_account | ||
} | ||
} | ||
|
||
impl GossipEntry { | ||
pub fn build_update_tx(&self, priority_fee: u64) -> Vec<Instruction> { | ||
let mut ixs = vec![ | ||
ComputeBudgetInstruction::set_compute_unit_limit(100_000), | ||
ComputeBudgetInstruction::set_compute_unit_price(priority_fee), | ||
build_verify_signature_ix( | ||
self.signature.as_ref(), | ||
self.identity.to_bytes(), | ||
&self.message, | ||
), | ||
]; | ||
|
||
ixs.push(Instruction { | ||
program_id: self.program_id, | ||
accounts: validator_history::accounts::CopyGossipContactInfo { | ||
validator_history_account: self.validator_history_account, | ||
vote_account: self.vote_account, | ||
instructions: solana_program::sysvar::instructions::id(), | ||
config: self.config, | ||
oracle_authority: self.signer, | ||
} | ||
.to_account_metas(None), | ||
data: validator_history::instruction::CopyGossipContactInfo {}.data(), | ||
}); | ||
ixs | ||
} | ||
} | ||
|
||
// CODE BELOW SLIGHTLY MODIFIED FROM | ||
// solana_sdk/src/ed25519_instruction.rs | ||
|
||
pub const PUBKEY_SERIALIZED_SIZE: usize = 32; | ||
pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; | ||
pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14; | ||
// bytemuck requires structures to be aligned | ||
pub const SIGNATURE_OFFSETS_START: usize = 2; | ||
pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START; | ||
|
||
#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)] | ||
#[repr(C)] | ||
pub struct Ed25519SignatureOffsets { | ||
signature_offset: u16, // offset to ed25519 signature of 64 bytes | ||
signature_instruction_index: u16, // instruction index to find signature | ||
public_key_offset: u16, // offset to public key of 32 bytes | ||
public_key_instruction_index: u16, // instruction index to find public key | ||
message_data_offset: u16, // offset to start of message data | ||
message_data_size: u16, // size of message data | ||
message_instruction_index: u16, // index of instruction data to get message data | ||
} | ||
|
||
// This code is modified from solana_sdk/src/ed25519_instruction.rs | ||
// due to that function requiring a keypair, and generating the signature within the function. | ||
// In our case we don't have the keypair, we just have the signature and pubkey. | ||
pub fn build_verify_signature_ix( | ||
signature: &[u8], | ||
pubkey: [u8; 32], | ||
message: &[u8], | ||
) -> Instruction { | ||
assert_eq!(pubkey.len(), PUBKEY_SERIALIZED_SIZE); | ||
assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE); | ||
|
||
let mut instruction_data = Vec::with_capacity( | ||
DATA_START | ||
.saturating_add(SIGNATURE_SERIALIZED_SIZE) | ||
.saturating_add(PUBKEY_SERIALIZED_SIZE) | ||
.saturating_add(message.len()), | ||
); | ||
|
||
let num_signatures: u8 = 1; | ||
let public_key_offset = DATA_START; | ||
let signature_offset = public_key_offset.saturating_add(PUBKEY_SERIALIZED_SIZE); | ||
let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE); | ||
|
||
// add padding byte so that offset structure is aligned | ||
instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0])); | ||
|
||
let offsets = Ed25519SignatureOffsets { | ||
signature_offset: signature_offset as u16, | ||
signature_instruction_index: u16::MAX, | ||
public_key_offset: public_key_offset as u16, | ||
public_key_instruction_index: u16::MAX, | ||
message_data_offset: message_data_offset as u16, | ||
message_data_size: message.len() as u16, | ||
message_instruction_index: u16::MAX, | ||
}; | ||
|
||
instruction_data.extend_from_slice(bytes_of(&offsets)); | ||
|
||
debug_assert_eq!(instruction_data.len(), public_key_offset); | ||
|
||
instruction_data.extend_from_slice(&pubkey); | ||
|
||
debug_assert_eq!(instruction_data.len(), signature_offset); | ||
|
||
instruction_data.extend_from_slice(signature); | ||
|
||
debug_assert_eq!(instruction_data.len(), message_data_offset); | ||
|
||
instruction_data.extend_from_slice(message); | ||
|
||
Instruction { | ||
program_id: solana_program::ed25519_program::id(), | ||
accounts: vec![], | ||
data: instruction_data, | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove this + add to .gitignore