// This file is part of Gear.
// Copyright (C) 2022-2024 Gear Technologies Inc.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
use gear_core::ids::{prelude::CodeIdExt, CodeId, MessageId, ProgramId};
use gsdk::{
metadata::runtime_types::pallet_gear_voucher::internal::VoucherId, Api, Event, Result,
TxInBlock,
};
use sp_core::crypto::Ss58Codec;
use sp_runtime::AccountId32;
use utils::{alice_account_id, dev_node};
mod utils;
#[tokio::test]
async fn test_issue_voucher() -> Result<()> {
// arrange
let node = dev_node();
let signer = Api::new(node.ws().as_str())
.await?
.signer("//Alice", None)?;
let account_id = alice_account_id();
let voucher_initial_balance = 100_000_000_000_000;
// act
let tx = signer
.calls
.issue_voucher(
account_id.clone(),
voucher_initial_balance,
None,
false,
100,
)
.await?;
let voucher_id = get_issued_voucher_id(tx).await?;
let voucher_address = AccountId32::new(voucher_id.0).to_ss58check();
let voucher_balance = signer.api().get_balance(&voucher_address).await?;
// assert
assert_eq!(voucher_initial_balance, voucher_balance);
Ok(())
}
#[tokio::test]
async fn test_decline_revoke_voucher() -> Result<()> {
// arrange
let node = dev_node();
let signer = Api::new(node.ws().as_str())
.await?
.signer("//Alice", None)?;
let account_id = signer.account_id();
let voucher_initial_balance = 100_000_000_000_000;
// issue voucher
let tx = signer
.calls
.issue_voucher(account_id.clone(), voucher_initial_balance, None, true, 100)
.await?;
let voucher_id = get_issued_voucher_id(tx).await?;
let _voucher_address = AccountId32::new(voucher_id.0).to_ss58check();
// act
let tx = signer.calls.decline_voucher(voucher_id.clone()).await?;
let declined_id = get_declined_voucher_id(tx).await?;
let tx = signer
.calls
.revoke_voucher(account_id.clone(), voucher_id.clone())
.await?;
let revoked_id = get_revoked_voucher_id(tx).await?;
// assert
assert_eq!(voucher_id, declined_id);
assert_eq!(voucher_id, revoked_id);
Ok(())
}
#[tokio::test]
async fn test_upload_code_with_voucher() -> Result<()> {
// arrange
let node = dev_node();
let signer = Api::new(node.ws().as_str())
.await?
.signer("//Alice", None)?;
let account_id = signer.account_id();
let voucher_initial_balance = 100_000_000_000_000;
let expected_code_id = CodeId::generate(demo_messenger::WASM_BINARY);
// issue voucher
let tx = signer
.calls
.issue_voucher(account_id.clone(), voucher_initial_balance, None, true, 100)
.await?;
let voucher_id = get_issued_voucher_id(tx).await?;
let voucher_address = AccountId32::new(voucher_id.0).to_ss58check();
// account balance before upload code
let account_initial_balance = signer.rpc.get_balance().await?;
// act
let tx = signer
.calls
.upload_code_with_voucher(voucher_id, demo_messenger::WASM_BINARY.to_vec())
.await?;
let code_id = get_last_code_id(tx).await?;
let account_balance = signer.rpc.get_balance().await?;
let voucher_balance = signer.api().get_balance(&voucher_address).await?;
// assert
assert_eq!(expected_code_id, code_id);
// account balance not changed
assert_eq!(account_initial_balance, account_balance);
// voucher balance less then initial
assert!(voucher_balance < voucher_initial_balance);
Ok(())
}
#[tokio::test]
async fn test_send_message_with_voucher() -> Result<()> {
// arrange
let node = dev_node();
let signer = Api::new(node.ws().as_str())
.await?
.signer("//Alice", None)?;
let account_id = signer.account_id();
let voucher_initial_balance = 100_000_000_000_000;
// 1. issue voucher
let tx = signer
.calls
.issue_voucher(account_id.clone(), voucher_initial_balance, None, true, 100)
.await?;
let voucher_id = get_issued_voucher_id(tx).await?;
let voucher_address = AccountId32::new(voucher_id.0).to_ss58check();
// 2. upload code with voucher
let tx = signer
.calls
.upload_code_with_voucher(voucher_id.clone(), demo_messenger::WASM_BINARY.to_vec())
.await?;
let code_id = get_last_code_id(tx).await?;
// 3. calculate create gas and create program
let gas_info = signer
.rpc
.calculate_create_gas(None, code_id, vec![], 0, true, None)
.await?;
let tx = signer
.calls
.create_program(code_id, vec![], vec![], gas_info.min_limit, 0)
.await?;
let program_id = get_last_program_id(tx).await?;
// 4. calculate handle gas and send message with voucher
let account_before_balance = signer.rpc.get_balance().await?;
let voucher_before_balance = signer.api().get_balance(&voucher_address).await?;
let gas_info = signer
.rpc
.calculate_handle_gas(None, program_id, vec![], 0, true, None)
.await?;
let tx = signer
.calls
.send_message_with_voucher(
voucher_id.clone(),
program_id,
vec![],
gas_info.min_limit,
0,
false,
)
.await?;
let _message_id = get_last_message_id(tx).await?;
let account_after_balance = signer.rpc.get_balance().await?;
let voucher_after_balance = signer.api().get_balance(&voucher_address).await?;
// assert
// account balance remain unchanged
assert_eq!(account_before_balance, account_after_balance);
// voucher balance changed
assert!(voucher_before_balance > voucher_after_balance);
Ok(())
}
async fn get_issued_voucher_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::GearVoucher(
gsdk::metadata::runtime_types::pallet_gear_voucher::pallet::Event::VoucherIssued {
voucher_id,
..
},
) = event?.as_root_event::()?
{
dbg!(&voucher_id);
return Ok(voucher_id);
}
}
panic!("voucher not issued");
}
async fn get_last_code_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::Gear(
gsdk::metadata::runtime_types::pallet_gear::pallet::Event::CodeChanged {
id,
change:
gsdk::metadata::runtime_types::gear_common::event::CodeChangeKind::Active { .. },
},
) = event?.as_root_event::()?
{
return Ok(id.into());
}
}
panic!("code not uploaded");
}
async fn get_last_program_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::Gear(
gsdk::metadata::runtime_types::pallet_gear::pallet::Event::ProgramChanged {
id,
change:
gsdk::metadata::runtime_types::gear_common::event::ProgramChangeKind::ProgramSet {
..
},
},
) = event?.as_root_event::()?
{
return Ok(id.into());
}
}
panic!("program not created");
}
async fn get_last_message_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::Gear(
gsdk::metadata::runtime_types::pallet_gear::pallet::Event::MessageQueued { id, .. },
) = event?.as_root_event::()?
{
return Ok(id.into());
}
}
panic!("message not sent");
}
async fn get_declined_voucher_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::GearVoucher(
gsdk::metadata::runtime_types::pallet_gear_voucher::pallet::Event::VoucherDeclined {
voucher_id,
..
},
) = event?.as_root_event::()?
{
return Ok(voucher_id);
}
}
panic!("voucher not declined");
}
async fn get_revoked_voucher_id(tx: TxInBlock) -> Result {
for event in tx.wait_for_success().await?.iter() {
if let Event::GearVoucher(
gsdk::metadata::runtime_types::pallet_gear_voucher::pallet::Event::VoucherRevoked {
voucher_id,
..
},
) = event?.as_root_event::()?
{
return Ok(voucher_id);
}
}
panic!("voucher not revoked");
}