Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hello world to program-examples #83

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 70 additions & 0 deletions a.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash -xe
build() {
declare -a ProjectDirs=(
"basics/account-data/native/program"
"basics/checking-accounts/native/program"
"basics/close-account/native/program"
"basics/counter/native/program"
"basics/create-account/native/program"
"basics/hello-solana/native/program"
"basics/pda-rent-payer/native/program"
"basics/processing-instructions/native/program"
"basics/program-derived-addresses/native/program"
"basics/realloc/native/program"
"basics/rent/native/program"
"basics/repository-layout/native/program"
"basics/transfer-sol/native/program"
)
for projectDir in "${ProjectDirs[@]}"; do
echo "
********
Building $projectDir
********"
cd $projectDir
if cargo-build-sbf --verbose; then
echo "Build succeeded for $projectDir."
else
failed=true
failed_builds+=($projectDir)
echo "Build failed for $projectDir. Continuing with the next program."
fi
cd - > /dev/null
done
}

run() {
solana -V
rustc -V
declare -a ProjectDirs=(
#"basics/account-data/native/"
#"basics/checking-accounts/native/"
#"basics/close-account/native/"
#"basics/counter/native/"
#"basics/create-account/native/"
"basics/hello-solana/native/"
#"basics/pda-rent-payer/native/"
#"basics/processing-instructions/native/"
#"basics/program-derived-addresses/native/"
#"basics/rent/native/"
#"basics/repository-layout/native/"
#"basics/transfer-sol/native/"
)
for projectDir in "${ProjectDirs[@]}"; do
echo "
********
Testing $projectDir
********"
cd $projectDir
pnpm install --frozen-lockfile
if (cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test); then
echo "Tests succeeded for $projectDir."
else
failed=true
failed_tests+=($projectDir)
echo "Tests failed for $projectDir. Continuing with the next program."
fi
cd - > /dev/null
done
}

run
7 changes: 7 additions & 0 deletions basics/hello-solana/move/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "hello"
version = "1.0.0"

[addresses]
hello = "0xba31"

Binary file not shown.
16 changes: 16 additions & 0 deletions basics/hello-solana/move/bin/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"program_id": "DozgQiYtGbdyniV2T74xMdmjZJvYDzoRFFqw7UR5MwPK",
"accounts": [
{
"key": "524HMdYYBy6TAn4dK5vCcjiTmT2sxV6Xoue5EXrz22Ca",
"owner": "BPFLoaderUpgradeab1e11111111111111111111111",
"is_signer": false,
"is_writable": true,
"lamports": 1000,
"data": [0, 0, 0, 3]
}
],
"instruction_data": [
11, 0, 0, 0, 0, 0, 0, 0,
104, 101, 108, 108, 111, 95, 95, 109, 97, 105, 110]
}
84 changes: 84 additions & 0 deletions basics/hello-solana/move/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/** WIP */
import * as fs from 'fs';
import * as solanaWeb3 from '@solana/web3.js';

async function startValidator() {
// TODO: Start a test-validator programmatically to have a self contained test.
// solana-test-validator -l test-ledger
}

async function deployProgramShell(programPath: string) {
const util = require('util');
const exec = util.promisify(require('child_process').exec);
try {
const { stdout, stderr } = await exec(`solana program deploy ${programPath}`);
console.log('stdout:', stdout);
console.log('stderr:', stderr);
return stdout;
} catch (e) {
console.error(e); // should contain code (exit code) and signal (that caused the termination).
return e;
}
}

// WIP: Function to deploy a Solana program programmatically.
async function deployProgram(
connection: solanaWeb3.Connection,
payer: solanaWeb3.Keypair,
programKeypair: solanaWeb3.Keypair,
programPath: string
): Promise<solanaWeb3.PublicKey> {
// solana program deploy programPath
// Load the program data
const programData = fs.readFileSync(programPath);

// Allocate space for the program data
const transaction = new solanaWeb3.Transaction();
const { feeCalculator } = await connection.getRecentBlockhash();
const programSpace = solanaWeb3.BpfLoader.getMinimumBalanceForRentExemption(
programData.length,
feeCalculator
);

// Create the program account
transaction.add(
solanaWeb3.SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: programKeypair.publicKey,
lamports: programSpace,
space: programData.length,
programId: solanaWeb3.BpfLoader.programId,
})
);

// Load the program
transaction.add(
solanaWeb3.BpfLoader.load(
connection,
payer,
programKeypair,
programData,
solanaWeb3.BpfLoader.programId
)
);

// Send the transaction
await solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
[payer, programKeypair]
);

return programKeypair.publicKey;
}

async function main() {
const connection = new solanaWeb3.Connection(solanaWeb3.clusterApiUrl('devnet'), 'confirmed');
const payer = solanaWeb3.Keypair.generate();
const programKeypair = solanaWeb3.Keypair.generate();
const programPath = 'bin/hello_solana_move_program.so';
const programId = await deployProgramShell(programPath);
console.log('Program deployed with', programId);
}

main().catch(console.error);
21 changes: 21 additions & 0 deletions basics/hello-solana/move/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts",
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
"deploy": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/deploy.ts"
},
"dependencies": {
"@solana/web3.js": "^1.47.3"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.1",
"@types/mocha": "^9.1.1",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"solana-bankrun": "^0.3.0",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
}
15 changes: 15 additions & 0 deletions basics/hello-solana/move/sources/lib.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module 0x10::debug {
native public fun print<T>(x: &T);
}

module hello::hello {
use 0x10::debug;
use 0x1::string;

public entry fun main() : u64 {
let rv = 0;
let s = string::utf8(b"Hello Solana");
debug::print(&s);
rv
}
}
45 changes: 45 additions & 0 deletions basics/hello-solana/move/tests/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Deploy a solana program by execing a shell.
// Returns stdout when the deploy was successful.
export async function deployProgramShell(programPath: string) {
const util = require('util');
const exec = util.promisify(require('child_process').exec);
try {
const { stdout, stderr } = await exec(`solana program deploy ${programPath}`);
if (stderr) {
return stderr;
}
return stdout;
} catch (e) {
console.error(e);
return e;
}
}

export function parseProgramID(programIdLog: string) : string {
// The string should of of the following form, else it is an error.
// Program Id: 5K4yQ8KW2CKsBVRUw2193GMnkKBKXDm6sdXnYY1cB4Hy
programIdLog = programIdLog.trim();
let templateString : string = 'Program Id: 5K4yQ8KW2CKsBVRUw2193GMnkKBKXDm6sdXnYY1cB4Hy';
let logLength = templateString.length;
if (programIdLog.length != logLength) {
console.log(`Different lenghts. Expected: ${logLength} vs. ${programIdLog.length}`);
return null;
}
if (!programIdLog.startsWith('Program Id:')){
console.log('program does not starts with.');
return null;
}
const programId = programIdLog.substring('Program Id: '.length);
return programId;
}

async function main() {
const programPath = 'bin/hello_solana_move_program.so';
const programIdLog = await deployProgramShell(programPath);
const programId = parseProgramID(programIdLog);
if (programId) {
console.log('Program deployed with', programId);
return 0;
}
return -1;
}
72 changes: 72 additions & 0 deletions basics/hello-solana/move/tests/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { describe, test } from 'node:test';
import { assert } from 'chai';
import {deployProgramShell, parseProgramID} from './deploy';

import {
Connection,
Keypair,
PublicKey,
sendAndConfirmTransaction,
Transaction,
TransactionInstruction,
} from '@solana/web3.js';

function createKeypairFromFile(path: string): Keypair {
return Keypair.fromSecretKey(
Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
)
};

function getInstructionData(path: string): Buffer {
// instruction_data (See: sui/external-crates/move/solana/move-mv-llvm-compiler/docs/Entrypoint.md)
let j = JSON.parse(require('fs').readFileSync(path, "utf-8"));
return j['instruction_data'];
}

async function deployProgram(programPath: string) : Promise<string> {
const programIdLog = await deployProgramShell(programPath);
const programId = await parseProgramID(programIdLog);
if (programId) {
console.log('Program deployed with', programId);
return programId;
}
console.log('Program could not be deployed');
return null;
}

describe("hello-solana", async () => {
// Loading these from local files for development
const connection = new Connection(`http://localhost:8899`, 'confirmed');
const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
// PublicKey of the deployed program.
const programPath = 'bin/hello_solana_move_program.so';
const programIdStr = await deployProgram(programPath);
const programId = new PublicKey(programIdStr);
const instructionData = getInstructionData('bin/input.json');

it("Say hello!", async () => {
// Set up transaction instructions first.
let ix = new TransactionInstruction({
keys: [
{pubkey: payer.publicKey, isSigner: true, isWritable: true}
],
programId,
data: instructionData,
});

// Send the transaction over RPC
let signature = await sendAndConfirmTransaction(
connection,
new Transaction().add(ix), // Add our instruction (you can add more than one)
[payer]
);

let transaction = await connection.getTransaction(signature, {commitment: "confirmed"});
console.log(transaction);
assert(transaction?.meta?.logMessages[0].startsWith(`Program ${programId}`));
// 'Hello Solana' as bytes
assert(transaction?.meta?.logMessages[1] === 'Program log: 0000000000000000000000000000000000000000000000000000000000000001::string::String { bytes: [72, 101, 108, 108, 111, 32, 83, 111, 108, 97, 110, 97], }');
assert(transaction?.meta?.logMessages[2] === `Program ${programId} consumed 5331 of 200000 compute units`);
assert(transaction?.meta?.logMessages[3] === `Program ${programId} success`);
});
});
10 changes: 10 additions & 0 deletions basics/hello-solana/move/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}
2 changes: 2 additions & 0 deletions basics/hello-solana/native/go.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so
solana program deploy ./program/target/so/program.so
20 changes: 20 additions & 0 deletions basics/hello-solana/native/kp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import base58
import json
import sys

#key_array = [4,182,130,247,119,117,227,207,112,73,170,126,222,197,244,99,215,107,255,202,33,43,36,17,104,111,157,246,196,192,174,95,240,23,238,206,118,215,154,238,229,96,11,37,156,123,51,223,5,231,17,117,86,136,103,14,75,95,175,132,148,54,1,46]

def print_keys_from_file(f):
key_array = json.load(open(f, 'r'))

secret_key = key_array[0:32]
public_key = key_array[32:64]

sk = base58.b58encode(bytes(secret_key))
pk = base58.b58encode(bytes(public_key))

print(pk)
print(sk)

if __name__ == "__main__":
print_keys_from_file(sys.argv[1])
Loading
Loading