Exercise – Logging

After learning how logging works, perform the following actions: 

  1. Log the value of greeting_account.counter just before and just after the increment operation
  2. Change the increment operation in the following way: 
    1. if the counter is 0, add 1 to it 
    2. otherwise, multiply the counter by 2 
  3. Verify your solution by compiling the code, deploying it, and running it

:one: Log the value of greeting_account.counter just before and just after the increment operation

Within the main program (main.ts file), I added an additional reportGreetings() call, before calling sayHello() -> so that the structure is:

main.ts

...
  // Find out how many times that account has been greeted BEFORE the incrementor
  await reportGreetings(true);

  // Say hello to an account
  await sayHello();

  // Find out how many times that account has been greeted AFTER the incrementor
  await reportGreetings(false);
...

But it’s useful to differentiate whether the reportGreetings() call is coming before the incrementor or after the incrementor, in the logs.
To do this, the reportGreetings() function should be changed.

The change can be as simple as adding a single boolean argument to the reportGreetings() function to follow the path where if it is before the incrementor, it says so in the logs, and if not then it says after incrementing.

The additional change in the function body is at the final console.log part where we can simple tack on to the end ${beforeIncrement ? "Before the incrementor" : "After the incrementor"}.

So the revised reportGreetings() function looks like this:

hello_world.ts

export async function reportGreetings(beforeIncrement: boolean): Promise<void> {
  const accountInfo = await connection.getAccountInfo(greetedPubkey);
  if (accountInfo === null) {
    throw 'Error: cannot find the greeted account';
  }
  const greeting = borsh.deserialize(
    GreetingSchema,
    GreetingAccount,
    accountInfo.data,
  );
  console.log(
    greetedPubkey.toBase58(),
    'has been greeted',
    greeting.counter,
    'time(s)',
    `${beforeIncrement ? "Before the incrementor" : "After the incrementor"}`
  );
}

And in the main program looks like this:

main.ts

async function main() {
  console.log("Let's say hello to a Solana account...");

  // Establish connection to the cluster
  await establishConnection();

  // Determine who pays for the fees
  await establishPayer();

  // Check if the program has been deployed
  await checkProgram();

  // THIS WAS ADDED
  // Find out how many times that account has been greeted BEFORE the incrementor
  await reportGreetings(true);

  // Say hello to an account
  await sayHello();

  // THIS WAS CHANGED
  // Find out how many times that account has been greeted AFTER the incrementor
  await reportGreetings(false);

  console.log('Success');
}

EDIT
The question specification was not to make different logs on the client, but rather to add system logging, so instead you should need to go to the smart contract located at /src/program-rust/src/lib.rs and in the process_instruction() public function, 4 lines before the end of the function, you should find greeting_account.counter += 1;.
At this point, you should add a msg!("Greeted {} time(s)!", greeting_account.counter); before and after it to have a counter log before and after the increment.

:two: Change the increment operation to: (If the counter is 0, add 1 to it…otherwise, multiply the counter by 2 )

This one is a little harder…

Within the file src/program-rust/src/lib.rs, the entrypoint!(process_instruction); calls the public function process_instruction() which does a whole bunch, including processing the counter increment and storing it.

...
// Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    greeting_account.counter += 1;

    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
...

This can be modified to do a check before the counter increment, such that:

...
    // Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    if greeting_account.counter == 0 {
        greeting_account.counter += 1;
    } else {
        greeting_account.counter *= 2;
    }
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
...

Doing the check to see if greeting_account.counter != 0 will then double the count and store the number each time the program is run.

1 Like
  1. Log the value of greeting_account.counter just before and just after the increment operation

lib.rs:

    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    msg!("Counter before {}: ", greeting_account.counter);    
        greeting_account.counter += 1;
    msg!("Counter after {}: ", greeting_account.counter);    
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
  1. Change the increment operation in the following way:
    if the counter is 0, add 1 to it
    otherwise, multiply the counter by 2

lib.rs:

 let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    msg!("Counter before {}: ", greeting_account.counter);    
    if greeting_account.counter == 0{
        greeting_account.counter += 1;
    } else {
    greeting_account.counter *= 2;
    }
    msg!("Counter after {}: ", greeting_account.counter);    
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
2 Likes

main.ts

/**
 * Hello world
 */

import {
  establishConnection,
  establishPayer,
  checkProgram,
  sayHello,
  reportGreetings,
} from './hello_world';

async function main() {
  console.log("Let's say hello to a Solana account...");

  // Establish connection to the cluster
  await establishConnection();
  
  // Determine who pays for the fees
  await establishPayer();
  
  // Check if the program has been deployed
  await checkProgram();
  
  await reportGreetings();//1
  
  // Say hello to an account
  await sayHello();

  // Find out how many times that account has been greeted
  await reportGreetings();//2

  console.log('Success');
}

main().then(
  () => process.exit(),
  err => {
    console.error(err);
    process.exit(-1);
  },
);

lib.rs

use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
};

/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    /// number of greetings
    pub counter: u32,
}

// Declare and export the program's entrypoint
entrypoint!(process_instruction);

// Program entrypoint's implementation
pub fn process_instruction(
    program_id: &Pubkey, // Public key of the account the hello world program was loaded into
    accounts: &[AccountInfo], // The account to say hello to
    _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
    msg!("Hello World Rust program entrypoint");

    // Iterating accounts is safer than indexing
    let accounts_iter = &mut accounts.iter();

    // Get the account to say hello to
    let account = next_account_info(accounts_iter)?;

    // The account must be owned by the program in order to modify its data
    if account.owner != program_id {
        msg!("Greeted account does not have the correct program id");
        return Err(ProgramError::IncorrectProgramId);
    }

    // Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    if greeting_account.counter==0 {greeting_account.counter += 1;}
    else {greeting_account.counter *=2}
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

    msg!("Greeted {} time(s)!", greeting_account.counter);

    Ok(())
}

// Sanity tests
#[cfg(test)]
mod test {
    use super::*;
    use solana_program::clock::Epoch;
    use std::mem;

    #[test]
    fn test_sanity() {
        let program_id = Pubkey::default();
        let key = Pubkey::default();
        let mut lamports = 0;
        let mut data = vec![0; mem::size_of::<u32>()];
        let owner = Pubkey::default();
        let account = AccountInfo::new(
            &key,
            false,
            true,
            &mut lamports,
            &mut data,
            &owner,
            false,
            Epoch::default(),
        );
        let instruction_data: Vec<u8> = Vec::new();

        let accounts = vec![account];

        assert_eq!(
            GreetingAccount::try_from_slice(&accounts[0].data.borrow())
                .unwrap()
                .counter,
            0
        );
        process_instruction(&program_id, &accounts, &instruction_data).unwrap();
        assert_eq!(
            GreetingAccount::try_from_slice(&accounts[0].data.borrow())
                .unwrap()
                .counter,
            1
        );
        process_instruction(&program_id, &accounts, &instruction_data).unwrap();
        assert_eq!(
            GreetingAccount::try_from_slice(&accounts[0].data.borrow())
                .unwrap()
                .counter,
            2
        );
    }
}

1 Like
// Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    msg!("Counter before: {}", greeting_account.counter);

    greeting_account.counter += 1;
    
    msg!("Counter after: {}", greeting_account.counter);

    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

    msg!("Greeted {} time(s)!", greeting_account.counter);
// Increment and store the number of times the account has been greeted
    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    msg!("Counter before: {}", greeting_account.counter);

    if greeting_account.counter == 0 {
        greeting_account.counter += 1;
    } else {
        greeting_account.counter *= 2;
    }
    msg!("Counter after: {}", greeting_account.counter);

    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

    msg!("Greeted {} time(s)!", greeting_account.counter);

compiling the code
:white_check_mark: npm run build:program-rust
deploying the code
:ballot_box_with_check: solana program deploy dist/program/helloworld.so
running the code
:heavy_check_mark: npm start

Program log: Counter before: 0
Program log: Counter after: 1
Program log: Greeted 1 time(s)!

:heavy_check_mark: npm start

Program log: Counter before: 1
Program log: Counter after: 2
Program log: Greeted 2 time(s)!

:heavy_check_mark: npm start

Program log: Counter before: 2
Program log: Counter after: 4
Program log: Greeted 4 time(s)!

:heavy_check_mark: npm start

Program log: Counter before: 4
Program log: Counter after: 8
Program log: Greeted 8 time(s)!
1 Like

this is my code, i try use a one global variable on blockchain, but solana program destabilizes me (I think in solidity method)
my first try is comment

lib.rs

/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    /// number of greetings
    pub counter: u32,
    // previs number greetings
    //-->pub previs_counter: u32,
}

// Declare and export the program's entrypoint
entrypoint!(process_instruction);

// Program entrypoint's implementation
pub fn process_instruction(
    program_id: &Pubkey, // Public key of the account the hello world program was loaded into
    accounts: &[AccountInfo], // The account to say hello to
    _instruction_data: &[u8], // Ignored, all helloworld instructions are hellos
) -> ProgramResult {
    msg!("Hello World Rust program entrypoint");

    // Iterating accounts is safer than indexing
    let accounts_iter = &mut accounts.iter();

    // Get the account to say hello to
    let account = next_account_info(accounts_iter)?;

    // The account must be owned by the program in order to modify its data
    if account.owner != program_id {
        msg!("Greeted account does not have the correct program id");
        return Err(ProgramError::IncorrectProgramId);
    }

    let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
    let mut previus: u32;
    match greeting_account.counter {
        0 => {
            //greeting_account.previs_counter = greeting_account.counter;
            previus = greeting_account.counter;
            greeting_account.counter += 1;
        }
        _ => {
            //greeting_account.previs_counter = greeting_account.counter;
            previus = greeting_account.counter;
            greeting_account.counter *= 2;
        }
    }
    greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;

    msg!(
        "Greeted {} time(s)!, previs counter is {}",
        greeting_account.counter,
        previus //greeting_account.previs_counter
    );

    Ok(())
}

client:

class GreetingAccount {
  counter = 0;
  previs_counter = 0;
  constructor(fields: {counter: number,previs_counter: number } | undefined = undefined) {
    if (fields) {
      this.counter = fields.counter;
      this.previs_counter = fields.previs_counter;
    }
  }
}

/**
 * Borsh schema definition for greeting accounts
 */
const GreetingSchema = new Map([
  [GreetingAccount, {kind: 'struct', fields: [['counter', 'u32'],['previs_counter','u32']]}],
]);

this return “SERIALIZE or DESERIALIZE error”