Skip to content

Commit

Permalink
Switch to true bitmap to save rent over Vec<bool>
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass committed Aug 29, 2023
1 parent 048dbfb commit 470e081
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 10 deletions.
76 changes: 76 additions & 0 deletions packages/helium-admin-cli/src/close-lazy-transaction-markers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as anchor from "@coral-xyz/anchor";
import { blockKey, init as initLazy, lazyTransactionsKey } from "@helium/lazy-transactions-sdk";
import os from "os";
import yargs from "yargs/yargs";
import { bulkSendTransactions, chunks } from "@helium/spl-utils";
import { Transaction } from "@solana/web3.js";

export async function run(args: any = process.argv) {
const yarg = yargs(args).options({
wallet: {
alias: "k",
describe: "Anchor wallet keypair",
default: `${os.homedir()}/.config/solana/id.json`,
},
url: {
alias: "u",
default: "http://127.0.0.1:8899",
describe: "The solana url",
},
name: {
default: "nJWGUMOK",
describe: "The lazy transactions instance name"
}
});
const argv = await yarg.argv;
process.env.ANCHOR_WALLET = argv.wallet;
process.env.ANCHOR_PROVIDER_URL = argv.url;
anchor.setProvider(anchor.AnchorProvider.local(argv.url));
const provider = anchor.getProvider() as anchor.AnchorProvider;
const lazyProgram = await initLazy(provider);
const ltKey = lazyTransactionsKey(argv.name)[0];
const lt = await lazyProgram.account.lazyTransactionsV0.fetch(ltKey);

const blocks = await lazyProgram.account.block.all();
const blocksByKey = new Set(blocks.map((b) => b.publicKey.toString()));
const allIndices = new Array(1 << lt.maxDepth).fill(0).map((_, i) => i);
const blockIndices = allIndices.filter((bi) =>
blocksByKey.has(blockKey(ltKey, bi)[0].toBase58())
);
if (lt.executed.length !== 1 << lt.maxDepth) {
await lazyProgram.methods
.reinitializeExecutedTransactionsV0()
.accounts({
lazyTransactions: ltKey,
})
.rpc({ skipPreflight: true });
}
const instructions = await Promise.all(
blockIndices.map((bi) =>
lazyProgram.methods
.closeMarkerV0({
index: bi,
})
.accounts({
refund: provider.wallet.publicKey,
lazyTransactions: ltKey,
authority: provider.wallet.publicKey,
})
.instruction()
)
);

console.log(`${blocks.length} blocks to close`);
const txns = chunks(instructions, 10).map(chunk => {
const tx = new Transaction({
feePayer: provider.wallet.publicKey,
})
tx.add(...chunk)
return tx
})

await bulkSendTransactions(provider, txns, (status) => {
console.log(`Sending ${status.currentBatchProgress} / ${status.currentBatchSize} batch. ${status.totalProgress} / ${txns.length}`)
})
console.log("Done")
}
11 changes: 11 additions & 0 deletions packages/lazy-transactions-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,14 @@ export function compileNoMerkle(

return compiledTransactions
}

export function isExecuted(
executed: Buffer,
index: number,
): boolean {
const byteIndex = Math.floor(index / 8);
const bitIndex = index % 8;
const byte = executed[byteIndex];
const mask = 1 << bitIndex;
return (byte & mask) !== 0;
}
3 changes: 2 additions & 1 deletion packages/migration-service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LazyTransactions } from "@helium/idls/lib/types/lazy_transactions";
import {
blockKey,
init,
isExecuted,
lazySignerKey,
lazyTransactionsKey,
} from "@helium/lazy-transactions-sdk";
Expand Down Expand Up @@ -146,7 +147,7 @@ async function getTransactions(
},
idx
) => {
const hasRun = executed[idx];
const hasRun = isExecuted(executed, idx);
const compiledTx = decompress(compiled);
const block = blockKey(lazyTransactions, id)[0];
const signers = decompressSigners(signersRaw);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::state::*;
use crate::{state::*, util::set_executed};
use anchor_lang::prelude::*;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
Expand Down Expand Up @@ -28,7 +28,7 @@ pub struct CloseMarkerV0<'info> {
}

pub fn handler(ctx: Context<CloseMarkerV0>, args: CloseMarkerArgsV0) -> Result<()> {
ctx.accounts.lazy_transactions.executed[args.index as usize] = true;
set_executed(&mut ctx.accounts.lazy_transactions.executed, args.index);

Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::canopy::fill_in_proof_from_canopy;
use crate::error::ErrorCode;
use crate::util::{is_executed, set_executed};
use crate::{merkle_proof::verify, state::*};
use anchor_lang::{prelude::*, solana_program, solana_program::instruction::Instruction};

Expand Down Expand Up @@ -30,8 +31,9 @@ pub struct ExecuteTransactionV0<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(
mut,
has_one = canopy,
constraint = lazy_transactions.executed[args.index as usize] == false @ ErrorCode::TransactionAlreadyExecuted,
constraint = !is_executed(&lazy_transactions.executed, args.index) @ ErrorCode::TransactionAlreadyExecuted,
)]
pub lazy_transactions: Account<'info, LazyTransactionsV0>,
/// CHECK: Verified by has one
Expand All @@ -55,7 +57,7 @@ pub struct ExecuteTransactionV0<'info> {
}

pub fn handler(ctx: Context<ExecuteTransactionV0>, args: ExecuteTransactionArgsV0) -> Result<()> {
ctx.accounts.lazy_transactions.executed[args.index as usize] = true;
set_executed(&mut ctx.accounts.lazy_transactions.executed, args.index);

let largest_acct_idx: usize = (*args
.instructions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{canopy::check_canopy_bytes, id, state::*};
use crate::{canopy::check_canopy_bytes, id, state::*, util::get_bitmap_len};
use anchor_lang::prelude::*;

#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
Expand Down Expand Up @@ -50,7 +50,7 @@ pub fn handler(
canopy: ctx.accounts.canopy.key(),
max_depth: args.max_depth,
bump_seed: ctx.bumps["lazy_transactions"],
executed: vec![false; 1 << args.max_depth],
executed: vec![0; get_bitmap_len(args.max_depth)],
});

Ok(())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use crate::state::*;
use crate::util::get_bitmap_len;
use anchor_lang::prelude::*;
use shared_utils::resize_to_fit;

#[derive(Accounts)]
pub struct ReinitializeExecutedTransactionsV0<'info> {
#[account(mut)]
pub payer: Signer<'info>,
pub authority: Signer<'info>,
#[account(
mut,
has_one = authority
)]
pub lazy_transactions: Box<Account<'info, LazyTransactionsV0>>,
pub system_program: Program<'info, System>,
}

pub fn handler(ctx: Context<ReinitializeExecutedTransactionsV0>) -> Result<()> {
ctx.accounts.lazy_transactions.executed =
vec![0; get_bitmap_len(ctx.accounts.lazy_transactions.max_depth)];

resize_to_fit(
&ctx.accounts.payer.to_account_info(),
&ctx.accounts.system_program.to_account_info(),
&ctx.accounts.lazy_transactions,
)?;
Ok(())
}
1 change: 1 addition & 0 deletions programs/lazy-transactions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod error;
pub mod instructions;
pub mod merkle_proof;
pub mod state;
pub mod util;

pub use instructions::*;
pub use state::*;
Expand Down
3 changes: 2 additions & 1 deletion programs/lazy-transactions/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub struct LazyTransactionsV0 {
pub authority: Pubkey,
pub canopy: Pubkey,
pub bump_seed: u8,
pub executed: Vec<bool>,
// Bitmap of executed transactions
pub executed: Vec<u8>,
}

#[account]
Expand Down
17 changes: 17 additions & 0 deletions programs/lazy-transactions/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pub fn get_bitmap_len(max_depth: u32) -> usize {
let num_txns = 1 << max_depth;
// Div ceil, https://stackoverflow.com/questions/72442853/how-would-you-perform-ceiling-division
((num_txns + 8 - 1) / 8) as usize
}

pub fn is_executed(bitmap: &Vec<u8>, index: u32) -> bool {
let byte = bitmap[index as usize / 8];
let bit = 1 << (index % 8);
byte & bit != 0
}

pub fn set_executed(bitmap: &mut Vec<u8>, index: u32) {
let byte = &mut bitmap[index as usize / 8];
let bit = 1 << (index % 8);
*byte |= bit;
}
5 changes: 3 additions & 2 deletions tests/lazy-transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,12 @@ describe("lazy-transactions", () => {
})
.accounts({ lazyTransactions })
.remainingAccounts(accounts)
.rpc({ skipPreflight: true });
.rpc();

throw new Error("Should have failed");
} catch (e: any) {
expect(e.toString()).to.not.include("Should have failed");
console.log(e.toString())
expect(e.toString()).to.include("Transaction has already been executed");
}

/// Attempt to close the canopy
Expand Down

0 comments on commit 470e081

Please sign in to comment.