Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a04abf
working version with pubkeys
Aug 2, 2024
21314e3
pubkeys with vecs for values
Aug 2, 2024
ded5cc0
static space allocation, testing with unique and repeating votes
Aug 2, 2024
0227927
grow space for extra data
Aug 2, 2024
23632b4
now with simulated pubkeys
Aug 3, 2024
3f69c2e
static space allocation, testing with unique and repeating votes and …
Aug 3, 2024
8b30814
static space allocation, testing with unique and repeating votes and …
Aug 3, 2024
e1a1f4d
PDA for each block_id, then vec->vec
Aug 3, 2024
e6544b5
static space allocation, testing with unique and repeating votes and …
Aug 3, 2024
c1bd3c9
better counting method
Aug 3, 2024
f53cbf1
server side code
Aug 3, 2024
53e2f27
truncate finger print string to 64 bit, adjust storage as bytes, chan…
Aug 3, 2024
5644364
optimization
Aug 3, 2024
ba8236c
pda query
Aug 5, 2024
064ca64
count works
Aug 5, 2024
9ddaf1a
out of memory experiment
Aug 5, 2024
af01252
working vote account writer function
Aug 7, 2024
f08f856
working vote account writer function, fetch isn't working
Aug 7, 2024
e98f426
user accounts pdas
Aug 8, 2024
fc46065
some sample commented out code with some structure
Aug 8, 2024
2ca2571
working schema for user account pda
Aug 8, 2024
9e1b397
working schema for user account pda
Aug 8, 2024
61ebae6
single user_pda used, must send data to multiple
Aug 9, 2024
37bc8b7
changes
lbelyaev Aug 17, 2024
db0e5cb
changes
lbelyaev Aug 17, 2024
5e022d3
ready for tests
lbelyaev Aug 17, 2024
6de7257
added README.md
lbelyaev Aug 17, 2024
2efc46d
amended README.md
lbelyaev Aug 17, 2024
060c46c
amended README.md
lbelyaev Aug 17, 2024
6547faf
amended README.md
lbelyaev Aug 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[toolchain]
anchor_version = "0.30.1"

[features]
resolution = true
Expand Down
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# XEN Blocks Voting Program

## Installation

### JS Modules

```npm i```

### Rust Modules

```anchor build```

#### After First Time Build

1. Run `anchor keys list`
2. Copy program ID
3. Replace Program ID in `./programs/grow_space/src/lib.rs` with the copied string
4. Run `anchor build` again or proceed with tests

## Configuration

This project requires configured connection to Xolana network and a funded main wallet.

Config is set in `Anchor.toml` file.

## Funding Xolana wallet

```solana airdrop 2```

Note: airdrop faucet is rate-limited to prevent abuse.

## Running tests

```anchor test```

This is a long-running integration test which creates N worker wallets, fund them from the main wallet, and then
repeatedly attempts to invoke Program Instruction to append voting data for the current block, as well as reward winning
voters for the previous block.

After the tests are complete, the script prints out stats info about block-voting PDAs and user accounting PDAs.

## TODOs

- make global auto-incrementing counter for BlockId
46 changes: 46 additions & 0 deletions app/dist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const anchor = require('@coral-xyz/anchor');
const { PublicKey } = require('@solana/web3.js');
const { BN } = require('bn.js');

// Get input from command line arguments
const block_id = parseInt(process.argv[2]);

if (isNaN(block_id)) {
console.error('Please provide a valid block_id.');
process.exit(1);
}

const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.GrowSpace;

async function distributeSolToBlock(block_id) {
const uniqueId = new BN(block_id);

const [pda, bump] = await PublicKey.findProgramAddress(
[Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)],
program.programId
);

const [distributionPda] = await PublicKey.findProgramAddress(
[Buffer.from("distribution_account")],
program.programId
);

try {
// Distribute SOL to all pubkeys associated with the block_id
const txHash = await program.methods.distributeSol(uniqueId).accounts({
pdaAccount: pda,
distributionAccount: distributionPda,
systemProgram: anchor.web3.SystemProgram.programId,
}).rpc();

console.log(`Distributed SOL. Transaction hash: ${txHash}`);
} catch (err) {
console.error("Failed to distribute SOL", err);
}
}

// Run the distributeSolToBlock function with the provided block_id
distributeSolToBlock(block_id);

33 changes: 33 additions & 0 deletions app/read_logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {workspace, web3, AnchorProvider, setProvider,} from "@coral-xyz/anchor";
import type {Program} from "@coral-xyz/anchor";
import {GrowSpace} from "../target/types/grow_space";

async function main() {

const provider = AnchorProvider.env();
setProvider(provider);
const connection = provider.connection;

const program = workspace.GrowSpace as Program<GrowSpace>;

let transactionList = await connection.getSignaturesForAddress(
program.programId,
{limit: 10},
'confirmed'
);

let signatureList = transactionList.map(transaction => transaction.signature);
let transactionDetails = await connection.getParsedTransactions(signatureList, {
maxSupportedTransactionVersion: 0,
commitment: 'confirmed'
});

transactionDetails.forEach((transaction, i) => {
const date = new Date(transaction.blockTime * 1000);
console.log(`Transaction No: ${i + 1}`);
console.log(`Messages:`);
transaction.meta.logMessages.forEach(console.log)
})
}

main().catch(console.log)
142 changes: 142 additions & 0 deletions app/server_vote_receiver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const express = require('express');
const bodyParser = require('body-parser');
const anchor = require('@coral-xyz/anchor');
const { PublicKey } = require('@solana/web3.js');
const { BN } = require('bn.js');

const app = express();
app.use(bodyParser.json());

const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.GrowSpace;

// Function to check if a PDA account already exists
async function pdaExists(pda) {
const accountInfo = await provider.connection.getAccountInfo(pda);
return accountInfo !== null;
}

// Endpoint to append data and initialize PDA if needed
app.post('/', async (req, res) => {
const { first_block_id, final_hash, pubkey } = req.body;
console.log(req.body);
const block_id = first_block_id;
console.log(block_id + " " + pubkey + "\n");

const uniqueId = new BN(block_id);
const pubkeyObj = new PublicKey(pubkey);

// define prev block_id
const prevuniqueId = new BN(block_id - 100);

const [prev_pda, prev_bump] = await PublicKey.findProgramAddress(
[Buffer.from("pda_account"), prevuniqueId.toArrayLike(Buffer, "le", 8)],
program.programId
);

// Derive the PDA for the main account
const [pda, bump] = await PublicKey.findProgramAddress(
[Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)],
program.programId
);

// Derive the PDA for the user account
const [user_account_pda, user_account_pda_bump] = await PublicKey.findProgramAddress(
[Buffer.from("user_account_pda"), pubkeyObj.toBuffer()],
program.programId
);

try {
// Check if the pda_account already exists
const pdaExistsFlag = await pdaExists(pda);

if (!pdaExistsFlag) {
// Initialize the pda_account if it does not exist
try {
const tx = await program.methods.initializePda(uniqueId, pubkeyObj).accounts({
pdaAccount: pda,
payer: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([provider.wallet.payer])
.rpc();
console.log("Initialization transaction hash:", tx);
console.log("Initialized PDA account public key:", pda.toString(), "with bump:", bump);
console.log("Initialized PDA for user:", user_account_pda.toString());
} catch (err) {
throw new Error(`Failed to initialize PDA: ${err.message}`);
}
} else {
console.log("PDA already initialized, proceeding to append data.");
}

try {
console.log("Attempting to append data...");

// Append the data
const tx2 = await program.methods.appendData(uniqueId, final_hash, pubkeyObj).accounts({
pdaAccount: pda,
prevPdaAccount: prev_pda,
userAccountPda: user_account_pda, // Uses init_if_needed so no explicit initialization required
payer: provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.signers([provider.wallet.payer])
.rpc();

console.log("Transaction successful, transaction hash:", tx2);
res.status(200).json({ message: "Appended data", pda: pda.toString(), txHash: tx2 });

} catch (err) {
console.error("Failed to append data, error details:", err);
res.status(500).json({ error: "Failed to append data", details: err.toString() });
}

} catch (err) {
console.error("Failed to process request, error details:", err);
res.status(500).json({ error: "Failed to process request", details: err.toString() });
}
});

// Endpoint to fetch and display data
app.get('/fetch_data/:block_id', async (req, res) => {
const block_id = parseInt(req.params.block_id);
if (isNaN(block_id)) {
return res.status(400).json({ error: "Invalid block_id" });
}

const uniqueId = new BN(block_id);

const [pda] = await PublicKey.findProgramAddress(
[Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)],
program.programId
);

try {
console.log(`Fetching data for block_id: ${block_id}, PDA: ${pda.toString()}`);
const account = await program.account.pdaAccount.fetch(pda);

const blockInfo = {
blockId: block_id,
entries: account.blockIds.map(entry => ({
blockId: entry.blockId.toString(),
finalHashes: entry.finalHashes.map(hashEntry => ({
finalHash: Buffer.from(hashEntry.finalHash).toString('utf8'), // Convert finalHash bytes to string
count: parseInt(hashEntry.count, 10),
pubkeys: hashEntry.pubkeys.map(pubkey => pubkey.toString())
}))
}))
};

res.status(200).json(blockInfo);
} catch (err) {
res.status(500).json({ error: "Failed to fetch data", details: err.toString() });
}
});

const PORT = 4444;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

72 changes: 72 additions & 0 deletions app/test_agg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const anchor = require('@coral-xyz/anchor');
const { PublicKey, Transaction } = require('@solana/web3.js');
const { BN } = require('bn.js');
const { ComputeBudgetProgram } = require('@solana/web3.js');

// Get input from command line arguments
const block_id = parseInt(process.argv[2]);

if (isNaN(block_id)) {
console.error('Please provide a valid block_id.');
process.exit(1);
}

const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.GrowSpace;

async function aggregatePubkeyCounts(blockId) {
const uniqueId = new BN(blockId - 100);

const uniqueIdBuffer = Buffer.alloc(8);
uniqueIdBuffer.writeBigUInt64LE(BigInt(uniqueId.toString()));

const [pda] = await PublicKey.findProgramAddress(
[Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)],
program.programId
);

const [voterAccountingPda] = await PublicKey.findProgramAddress(
[Buffer.from("accounting")],
program.programId
);

try {
const voterAccounting = await program.account.voterAccounting.fetch(voterAccountingPda);
const pdaAccount = await program.account.pdaAccount.fetch(pda);
const transaction = new Transaction();

transaction.add(
ComputeBudgetProgram.setComputeUnitLimit({
units: 1400000,
})
);

transaction.add(
await program.methods.aggregatePubkeyCounts(uniqueId).accounts({
pdaAccount: pda,
voterAccounting: voterAccountingPda,
systemProgram: anchor.web3.SystemProgram.programId,
}).instruction()
);

console.log(`Sending transaction for block ID: ${uniqueId}`);
const txId = await provider.sendAndConfirm(transaction);
console.log(`Transaction sent with ID: ${txId}`);

// Fetch the updated voter accounting data after the transaction
console.log(`Fetching updated voter accounting data for PDA: ${voterAccountingPda.toString()}`);
const updatedVoterAccounting = await program.account.voterAccounting.fetch(voterAccountingPda);
//console.log("Updated Voter Accounting Data:", updatedVoterAccounting);

updatedVoterAccounting.pubkeyCounts.forEach((entry, index) => {
console.log(`Updated Record ${index + 1}: Pubkey = ${entry.user.toString()}, Credit = ${entry.credit.toString()}, Debit = ${entry.debit.toString()}, inBlock = ${entry.inblock.toString()}`);
});

} catch (err) {
console.error(`Failed to aggregate pubkey counts for block ID: ${uniqueId}`, err);
}
}

aggregatePubkeyCounts(block_id);

36 changes: 36 additions & 0 deletions fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { GrowSpace } from "../target/types/grow_space";
import { assert } from "chai";

describe("grow_space_combined", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);

const program = anchor.workspace.GrowSpace as Program<GrowSpace>;

// Static PDA account
const pda = new anchor.web3.PublicKey("69E5vfTiBshW7R28t3ber33wfBHyAbubTvXL3iPWsfcz");

it("Fetches and displays data from the static PDA", async () => {
try {
const account = await program.account.pdaAccount.fetch(pda);

// Print and count the Block IDs
console.log("Block IDs stored in the PDA:");
account.blockIds.forEach((entry: any, index: number) => {
console.log(`Index ${index}: Block ID ${entry.blockId.toString()}, Final Hashes: ${entry.finalHashes.map((hashEntry: any) => `${hashEntry.finalHash} (count: ${hashEntry.count})`).join(", ")}`);
});

// Verify that the values are unique Block IDs
const blockIdsSet = new Set(account.blockIds.map((entry: any) => entry.blockId.toString()));
assert.equal(blockIdsSet.size, account.blockIds.length, "Block IDs should be unique");

console.log("Total unique block IDs added: ", account.blockIds.length);
} catch (err) {
console.error("Failed to fetch data from the PDA:", err);
}
});
});

Loading