Skip to content

Commit

Permalink
Added boilerplate for a bunch of instructions. Tests passing.
Browse files Browse the repository at this point in the history
cargo check & cargo test-bpf both still pass.

Need to fill in actual processing & add tests for new functions.
  • Loading branch information
GabeDottl committed Apr 25, 2021
1 parent bff0f15 commit 44fa091
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub enum HeartTokenError {
InvalidInstruction,
#[error("Not Rent Exempt")]
NotRentExempt,
#[error("Invalid minter provided")]
InvalidMinter,
#[error("Not Implemented")]
NotImplemented,
}

impl From<HeartTokenError> for ProgramError {
Expand Down
171 changes: 164 additions & 7 deletions src/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::{HeartTokenError, EscrowError::InvalidInstruction};
use crate::state::{CredentialType, HeartToken, VerifiedCredential};
use crate::error::{EscrowError::InvalidInstruction, HeartTokenError};
use crate::state::{CredentialType, HeartToken, VerifiedCredential, MAX_REQUIRED_CREDENTIALS};
use solana_program::program_error::ProgramError;
use solana_program::{
instruction::{AccountMeta, Instruction},
Expand All @@ -9,6 +9,10 @@ use solana_program::{
use std::convert::TryInto;
use std::mem::size_of;

use std::iter::Chain;
use std::slice::Iter;


pub enum HeartTokenInstruction {
/// Creates a HeartToken.
///
Expand All @@ -17,11 +21,54 @@ pub enum HeartTokenInstruction {
///
/// 0. `[signer]` The account of the owner of the HeartToken.
/// 1. `[writable]` The HeartToken account, it will hold all necessary info about the HT.
/// 2. `[]` The rent sysvar
/// 2. `[signer]` The HeartToken minter
/// 3. `[]` The rent sysvar
CreateHeartToken {
// TODO: Make HeartTokenOwner a credential?
// heart_token_owner: Pubkey, // Wallet of holder.
// verified_credentials: Vec<VerifiedCredential>
// verified_credentials: Vec<VerifiedCredential>
},

/// Creates a new Claim type.
///
///
/// Accounts expected:
///
/// 3. `[]` The rent sysvar
CreateClaimType {
claim_check_program_id: Pubkey,
check_program_instruction_id: u8
// data_hash: [u8; DATA_HASH_SIZE]
// data aggregation? E.g. on_success_program_id
//
},
IssueClaim {
claim_type_id: Pubkey,
subject: Pubkey,
// data_hash
},
// RevokeClaim {
// claim_type_id: Pubkey,
// subject: Pubkey,
// },

/// Creates a new Claim type.
///
///
/// Accounts expected:
///
/// 3. `[]` The rent sysvar
CreateSimpleClaimCheck {
subject_required_credentials: [Pubkey; MAX_REQUIRED_CREDENTIALS],
issuer_required_credentials: [Pubkey; MAX_REQUIRED_CREDENTIALS]
},
ExecuteSimpleClaimCheck {
claim_type_id: Pubkey
}
// CreateHeartTokenContract {
// }
// SignWithHeartTokens {
// }
// RecoverHeartToken {
// heart_token_account: Pubkey, // The account/ID of the HeartToken
// heart_token_owner: Pubkey, // Wallet of holder.
Expand Down Expand Up @@ -49,6 +96,41 @@ impl HeartTokenInstruction {
// heart_token_owner: pubkey,
}
}
1 => {
let (claim_check_program_id, rest) = Self::unpack_pubkey(rest)?;
let check_program_instruction_id = *rest.get(0).unwrap();
Self::CreateClaimType {
claim_check_program_id,
check_program_instruction_id
}
}
2 => {
let (claim_type_id, rest) = Self::unpack_pubkey(rest)?;
let (subject, _rest) = Self::unpack_pubkey(rest)?;
Self::IssueClaim {
claim_type_id,
subject, // heart_token_owner: pubkey,
}
}
3 => {
if rest.len() == 2 * 32 * MAX_REQUIRED_CREDENTIALS {
let subject_required_credentials = [Pubkey::new_from_array([0u8; 32]); MAX_REQUIRED_CREDENTIALS];
let issuer_required_credentials = [Pubkey::new_from_array([0u8; 32]); MAX_REQUIRED_CREDENTIALS];
Self::CreateSimpleClaimCheck {
subject_required_credentials,
issuer_required_credentials
}
} else {
return Err(HeartTokenError::InvalidInstruction.into());
}
}
4 => {
let (pubkey, _rest) = Self::unpack_pubkey(rest)?;
Self::ExecuteSimpleClaimCheck {
claim_type_id: pubkey,
}
}

_ => return Err(HeartTokenError::InvalidInstruction.into()),
})
}
Expand All @@ -65,25 +147,100 @@ impl HeartTokenInstruction {
fn pack(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(size_of::<Self>());
match self {
&Self::CreateHeartToken { } => {
&Self::CreateHeartToken {} => {
buf.push(0);
// buf.extend_from_slice(heart_token_owner.as_ref());
}
&Self::CreateClaimType {
ref claim_check_program_id,
check_program_instruction_id
} => {
buf.push(1);
buf.extend_from_slice(claim_check_program_id.as_ref());
buf.push(check_program_instruction_id);
}
&Self::IssueClaim {
ref claim_type_id,
ref subject,
} => {
buf.push(2);
buf.extend_from_slice(claim_type_id.as_ref());
buf.extend_from_slice(subject.as_ref());
}
&Self::CreateSimpleClaimCheck {
ref subject_required_credentials,
ref issuer_required_credentials,
} => {
buf.push(3);
for key in subject_required_credentials.iter().chain(issuer_required_credentials.iter()) {
buf.extend_from_slice(key.as_ref());
}
}
&Self::ExecuteSimpleClaimCheck {
ref claim_type_id,
} => {
buf.push(4);
buf.extend_from_slice(claim_type_id.as_ref());
}
}
buf
}

pub fn create_claim_type(
heart_token_program_id: &Pubkey,
claim_check_program_id: &Pubkey,
check_program_instruction_id: u8
) -> Result<Instruction, ProgramError> {
let accounts = vec![
// AccountMeta::new_readonly(*claim_check_program_id, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let data = HeartTokenInstruction::CreateClaimType {
claim_check_program_id: *claim_check_program_id,
check_program_instruction_id
}
.pack();
Ok(Instruction {
program_id: *heart_token_program_id,
accounts,
data,
})
}

pub fn create_issue_claim(
heart_token_program_id: &Pubkey,
claim_type_id: &Pubkey,
subject: &Pubkey,
) -> Result<Instruction, ProgramError> {
let accounts = vec![
// AccountMeta::new_readonly(*claim_check_program_id, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let data = HeartTokenInstruction::IssueClaim {
claim_type_id: *claim_type_id,
subject: *subject,
}
.pack();
Ok(Instruction {
program_id: *heart_token_program_id,
accounts,
data,
})
}

pub fn create_heart_token(
heart_token_program_id: &Pubkey,
heart_token_owner: &Pubkey,
heart_token_account: &Pubkey
heart_token_account: &Pubkey,
heart_token_minter: &Pubkey,
) -> Result<Instruction, ProgramError> {
let accounts = vec![
AccountMeta::new_readonly(*heart_token_owner, true),
AccountMeta::new(*heart_token_account, false),
AccountMeta::new_readonly(*heart_token_minter, true),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let data = HeartTokenInstruction::CreateHeartToken { }.pack();
let data = HeartTokenInstruction::CreateHeartToken {}.pack();
Ok(Instruction {
program_id: *heart_token_program_id,
accounts,
Expand Down
82 changes: 76 additions & 6 deletions src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ use solana_program::{
sysvar::{rent::Rent, Sysvar},
};

use crate::{error::{HeartTokenError, EscrowError}, instruction::{EscrowInstruction, HeartTokenInstruction}, state::{HeartToken, Escrow}};
use std::str::FromStr;

use crate::{
error::{EscrowError, HeartTokenError},
instruction::{EscrowInstruction, HeartTokenInstruction},
state::{CredentialType, Escrow, HeartToken},
};

pub const GOD_PUBKEY_STR: &str = "5cjmBetNkWYa2ZKZTsTMreNZQNSpwyhDrTVsynJVKZ9C";// "5gREDw2KxWKTceSTCbtQSG32aSnHrxPUUNo1PERZBMTq";

pub struct Processor;
impl Processor {
Expand All @@ -22,11 +30,62 @@ impl Processor {
let instruction = HeartTokenInstruction::unpack(instruction_data)?;

match instruction {
HeartTokenInstruction::CreateHeartToken { } => {
HeartTokenInstruction::CreateHeartToken {} => {
msg!("Instruction: CreateHeartToken");
Self::process_create_heart_token(accounts, program_id)
}
HeartTokenInstruction::CreateClaimType { claim_check_program_id, check_program_instruction_id } => {
msg!("Instruction: CreateClaimType");
return Err(HeartTokenError::NotImplemented.into());
}
HeartTokenInstruction::IssueClaim { claim_type_id, subject } => {
msg!("Instruction: IssueClaim");
return Err(HeartTokenError::NotImplemented.into());
}
HeartTokenInstruction::CreateSimpleClaimCheck {subject_required_credentials, issuer_required_credentials} => {
msg!("Instruction: CreateSimpleClaimCheck");
return Err(HeartTokenError::NotImplemented.into());
}
HeartTokenInstruction::ExecuteSimpleClaimCheck {claim_type_id} => {
msg!("Instruction: ExecuteSimpleClaimCheck");
return Err(HeartTokenError::NotImplemented.into());
}
}
}

// fn process_create_simple_claim_check(accounts: &[AccountInfo],
// subject_required_credentials: &[Pubkey],
// issuer_required_credentials: &[Pubkey],
// program_id: &Pubkey) {
// let account_info_iter = &mut accounts.iter();
// let owner = next_account_info(account_info_iter)?;
// if account.owner != program_id {
// msg!("Invalid owner!");
// return Err(HeartTokenError::InvalidMinter.into());
// }
// }

fn check_minter(account: &AccountInfo, program_id: &Pubkey) -> ProgramResult {
msg!("Account: {}", account.key.to_string());
if account.key.to_string() == GOD_PUBKEY_STR {
return Ok(());
}
// Ensure minter account is owned by HeartToken program.
if account.owner != program_id {
msg!("Invalid owner!");
return Err(HeartTokenError::InvalidMinter.into());
}
// Check that minter has Minter credential.
let account_info = HeartToken::unpack_unchecked(&account.data.borrow())?;
if !account_info
.verified_credentials
.iter()
.any(|&vc| vc.type_ == CredentialType::HeartTokenMinter)
{
msg!("No Minter VC!");
return Err(HeartTokenError::InvalidMinter.into());
}
Ok(())
}

fn process_create_heart_token(
Expand All @@ -42,9 +101,16 @@ impl Processor {
}

let heart_token_account = next_account_info(account_info_iter)?;
let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
let heart_token_minter = next_account_info(account_info_iter)?;
msg!("pub key {}", *heart_token_minter.key);

if !rent.is_exempt(heart_token_account.lamports(), heart_token_account.data_len()) {
Processor::check_minter(heart_token_minter, program_id)?;

let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?;
if !rent.is_exempt(
heart_token_account.lamports(),
heart_token_account.data_len(),
) {
return Err(HeartTokenError::NotRentExempt.into());
}

Expand Down Expand Up @@ -84,7 +150,6 @@ impl Processor {
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();


let initializer = next_account_info(account_info_iter)?;
if !initializer.is_signer {
return Err(ProgramError::MissingRequiredSignature);
Expand Down Expand Up @@ -134,7 +199,12 @@ impl Processor {
)?;

msg!("Calling the token program to transfer token account ownership...");
msg!("Token program: {}. Transferring ownership {} -> {}", token_program.key, initializer.key, pda);
msg!(
"Token program: {}. Transferring ownership {} -> {}",
token_program.key,
initializer.key,
pda
);
invoke(
&owner_change_ix,
&[
Expand Down
Loading

0 comments on commit 44fa091

Please sign in to comment.