solana/web3.js/test/program-tests/system.test.ts

626 lines
19 KiB
TypeScript

import {Buffer} from 'buffer';
import {expect} from 'chai';
import {
Keypair,
Connection,
PublicKey,
StakeProgram,
SystemInstruction,
SystemProgram,
Transaction,
TransactionInstruction,
sendAndConfirmTransaction,
LAMPORTS_PER_SOL,
} from '../../src';
import {NONCE_ACCOUNT_LENGTH} from '../../src/nonce-account';
import {sleep} from '../../src/utils/sleep';
import {helpers} from '../mocks/rpc-http';
import {url} from '../url';
describe('SystemProgram', () => {
it('createAccount', () => {
const params = {
fromPubkey: Keypair.generate().publicKey,
newAccountPubkey: Keypair.generate().publicKey,
lamports: 123,
space: 0,
programId: SystemProgram.programId,
};
const transaction = new Transaction().add(
SystemProgram.createAccount(params),
);
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(
SystemInstruction.decodeCreateAccount(systemInstruction),
);
});
it('transfer', () => {
const params = {
fromPubkey: Keypair.generate().publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 123,
};
const transaction = new Transaction().add(SystemProgram.transfer(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
const decodedParams = {
...params,
lamports: BigInt(params.lamports),
};
expect(decodedParams).to.eql(
SystemInstruction.decodeTransfer(systemInstruction),
);
});
it('transferWithSeed', () => {
const params = {
fromPubkey: Keypair.generate().publicKey,
basePubkey: Keypair.generate().publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 123,
seed: '你好',
programId: Keypair.generate().publicKey,
};
const transaction = new Transaction().add(SystemProgram.transfer(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
const decodedParams = {
...params,
lamports: BigInt(params.lamports),
};
expect(decodedParams).to.eql(
SystemInstruction.decodeTransferWithSeed(systemInstruction),
);
});
it('allocate', () => {
const params = {
accountPubkey: Keypair.generate().publicKey,
space: 42,
};
const transaction = new Transaction().add(SystemProgram.allocate(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(SystemInstruction.decodeAllocate(systemInstruction));
});
it('allocateWithSeed', () => {
const params = {
accountPubkey: Keypair.generate().publicKey,
basePubkey: Keypair.generate().publicKey,
seed: '你好',
space: 42,
programId: Keypair.generate().publicKey,
};
const transaction = new Transaction().add(SystemProgram.allocate(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(
SystemInstruction.decodeAllocateWithSeed(systemInstruction),
);
});
it('assign', () => {
const params = {
accountPubkey: Keypair.generate().publicKey,
programId: Keypair.generate().publicKey,
};
const transaction = new Transaction().add(SystemProgram.assign(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(SystemInstruction.decodeAssign(systemInstruction));
});
it('assignWithSeed', () => {
const params = {
accountPubkey: Keypair.generate().publicKey,
basePubkey: Keypair.generate().publicKey,
seed: '你好',
programId: Keypair.generate().publicKey,
};
const transaction = new Transaction().add(SystemProgram.assign(params));
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(
SystemInstruction.decodeAssignWithSeed(systemInstruction),
);
});
it('createAccountWithSeed', () => {
const fromPubkey = Keypair.generate().publicKey;
const params = {
fromPubkey,
newAccountPubkey: Keypair.generate().publicKey,
basePubkey: fromPubkey,
seed: 'hi there',
lamports: 123,
space: 0,
programId: SystemProgram.programId,
};
const transaction = new Transaction().add(
SystemProgram.createAccountWithSeed(params),
);
expect(transaction.instructions).to.have.length(1);
const [systemInstruction] = transaction.instructions;
expect(params).to.eql(
SystemInstruction.decodeCreateWithSeed(systemInstruction),
);
});
it('createNonceAccount', () => {
const fromPubkey = Keypair.generate().publicKey;
const params = {
fromPubkey,
noncePubkey: Keypair.generate().publicKey,
authorizedPubkey: fromPubkey,
lamports: 123,
};
const transaction = new Transaction().add(
SystemProgram.createNonceAccount(params),
);
expect(transaction.instructions).to.have.length(2);
const [createInstruction, initInstruction] = transaction.instructions;
const createParams = {
fromPubkey: params.fromPubkey,
newAccountPubkey: params.noncePubkey,
lamports: params.lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
};
expect(createParams).to.eql(
SystemInstruction.decodeCreateAccount(createInstruction),
);
const initParams = {
noncePubkey: params.noncePubkey,
authorizedPubkey: fromPubkey,
};
expect(initParams).to.eql(
SystemInstruction.decodeNonceInitialize(initInstruction),
);
});
it('createNonceAccount with seed', () => {
const fromPubkey = Keypair.generate().publicKey;
const params = {
fromPubkey,
noncePubkey: Keypair.generate().publicKey,
authorizedPubkey: fromPubkey,
basePubkey: fromPubkey,
seed: 'hi there',
lamports: 123,
};
const transaction = new Transaction().add(
SystemProgram.createNonceAccount(params),
);
expect(transaction.instructions).to.have.length(2);
const [createInstruction, initInstruction] = transaction.instructions;
const createParams = {
fromPubkey: params.fromPubkey,
newAccountPubkey: params.noncePubkey,
basePubkey: fromPubkey,
seed: 'hi there',
lamports: params.lamports,
space: NONCE_ACCOUNT_LENGTH,
programId: SystemProgram.programId,
};
expect(createParams).to.eql(
SystemInstruction.decodeCreateWithSeed(createInstruction),
);
const initParams = {
noncePubkey: params.noncePubkey,
authorizedPubkey: fromPubkey,
};
expect(initParams).to.eql(
SystemInstruction.decodeNonceInitialize(initInstruction),
);
});
it('nonceAdvance', () => {
const params = {
noncePubkey: Keypair.generate().publicKey,
authorizedPubkey: Keypair.generate().publicKey,
};
const instruction = SystemProgram.nonceAdvance(params);
expect(params).to.eql(SystemInstruction.decodeNonceAdvance(instruction));
});
it('nonceWithdraw', () => {
const params = {
noncePubkey: Keypair.generate().publicKey,
authorizedPubkey: Keypair.generate().publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 123,
};
const transaction = new Transaction().add(
SystemProgram.nonceWithdraw(params),
);
expect(transaction.instructions).to.have.length(1);
const [instruction] = transaction.instructions;
expect(params).to.eql(SystemInstruction.decodeNonceWithdraw(instruction));
});
it('nonceAuthorize', () => {
const params = {
noncePubkey: Keypair.generate().publicKey,
authorizedPubkey: Keypair.generate().publicKey,
newAuthorizedPubkey: Keypair.generate().publicKey,
};
const transaction = new Transaction().add(
SystemProgram.nonceAuthorize(params),
);
expect(transaction.instructions).to.have.length(1);
const [instruction] = transaction.instructions;
expect(params).to.eql(SystemInstruction.decodeNonceAuthorize(instruction));
});
it('non-SystemInstruction error', () => {
const from = Keypair.generate();
const to = Keypair.generate();
const badProgramId = {
keys: [
{pubkey: from.publicKey, isSigner: true, isWritable: true},
{pubkey: to.publicKey, isSigner: false, isWritable: true},
],
programId: StakeProgram.programId,
data: Buffer.from([2, 0, 0, 0]),
};
expect(() => {
SystemInstruction.decodeInstructionType(
new TransactionInstruction(badProgramId),
);
}).to.throw();
const stakePubkey = Keypair.generate().publicKey;
const authorizedPubkey = Keypair.generate().publicKey;
const params = {stakePubkey, authorizedPubkey};
const transaction = StakeProgram.deactivate(params);
expect(() => {
SystemInstruction.decodeInstructionType(transaction.instructions[1]);
}).to.throw();
transaction.instructions[0].data[0] = 11;
expect(() => {
SystemInstruction.decodeInstructionType(transaction.instructions[0]);
}).to.throw();
});
if (process.env.TEST_LIVE) {
it('live Nonce actions', async () => {
const connection = new Connection(url, 'confirmed');
const nonceAccount = Keypair.generate();
const from = Keypair.generate();
await helpers.airdrop({
connection,
address: from.publicKey,
amount: 2 * LAMPORTS_PER_SOL,
});
const to = Keypair.generate();
const newAuthority = Keypair.generate();
await helpers.airdrop({
connection,
address: newAuthority.publicKey,
amount: LAMPORTS_PER_SOL,
});
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
NONCE_ACCOUNT_LENGTH,
);
let createNonceAccount = new Transaction().add(
SystemProgram.createNonceAccount({
fromPubkey: from.publicKey,
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: from.publicKey,
lamports: minimumAmount,
}),
);
await sendAndConfirmTransaction(
connection,
createNonceAccount,
[from, nonceAccount],
{preflightCommitment: 'confirmed'},
);
const nonceBalance = await connection.getBalance(nonceAccount.publicKey);
expect(nonceBalance).to.eq(minimumAmount);
const nonceQuery1 = await connection.getNonce(nonceAccount.publicKey);
if (nonceQuery1 === null) {
expect(nonceQuery1).not.to.be.null;
return;
}
const nonceQuery2 = await connection.getNonce(nonceAccount.publicKey);
if (nonceQuery2 === null) {
expect(nonceQuery2).not.to.be.null;
return;
}
expect(nonceQuery1.nonce).to.eq(nonceQuery2.nonce);
// Wait for blockhash to advance
await sleep(500);
const advanceNonce = new Transaction().add(
SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: from.publicKey,
}),
);
await sendAndConfirmTransaction(connection, advanceNonce, [from], {
preflightCommitment: 'confirmed',
});
const nonceQuery3 = await connection.getNonce(nonceAccount.publicKey);
if (nonceQuery3 === null) {
expect(nonceQuery3).not.to.be.null;
return;
}
expect(nonceQuery1.nonce).not.to.eq(nonceQuery3.nonce);
const nonce = nonceQuery3.nonce;
// Wait for blockhash to advance
await sleep(500);
const authorizeNonce = new Transaction().add(
SystemProgram.nonceAuthorize({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: from.publicKey,
newAuthorizedPubkey: newAuthority.publicKey,
}),
);
await sendAndConfirmTransaction(connection, authorizeNonce, [from], {
preflightCommitment: 'confirmed',
});
let transfer = new Transaction().add(
SystemProgram.transfer({
fromPubkey: from.publicKey,
toPubkey: to.publicKey,
lamports: minimumAmount,
}),
);
transfer.nonceInfo = {
nonce,
nonceInstruction: SystemProgram.nonceAdvance({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: newAuthority.publicKey,
}),
};
await sendAndConfirmTransaction(
connection,
transfer,
[from, newAuthority],
{
preflightCommitment: 'confirmed',
},
);
const toBalance = await connection.getBalance(to.publicKey);
expect(toBalance).to.eq(minimumAmount);
// Wait for blockhash to advance
await sleep(500);
const withdrawAccount = Keypair.generate();
const withdrawNonce = new Transaction().add(
SystemProgram.nonceWithdraw({
noncePubkey: nonceAccount.publicKey,
authorizedPubkey: newAuthority.publicKey,
lamports: minimumAmount,
toPubkey: withdrawAccount.publicKey,
}),
);
await sendAndConfirmTransaction(
connection,
withdrawNonce,
[newAuthority],
{
preflightCommitment: 'confirmed',
},
);
expect(await connection.getBalance(nonceAccount.publicKey)).to.eq(0);
const withdrawBalance = await connection.getBalance(
withdrawAccount.publicKey,
);
expect(withdrawBalance).to.eq(minimumAmount);
}).timeout(10 * 1000);
it('live withSeed actions', async () => {
const connection = new Connection(url, 'confirmed');
const baseAccount = Keypair.generate();
await helpers.airdrop({
connection,
address: baseAccount.publicKey,
amount: 2 * LAMPORTS_PER_SOL,
});
const basePubkey = baseAccount.publicKey;
const seed = 'hi there';
const programId = Keypair.generate().publicKey;
const createAccountWithSeedAddress = await PublicKey.createWithSeed(
basePubkey,
seed,
programId,
);
const space = 0;
const minimumAmount = await connection.getMinimumBalanceForRentExemption(
space,
);
// Test CreateAccountWithSeed
const createAccountWithSeedParams = {
fromPubkey: basePubkey,
newAccountPubkey: createAccountWithSeedAddress,
basePubkey,
seed,
lamports: minimumAmount,
space,
programId,
};
const createAccountWithSeedTransaction = new Transaction().add(
SystemProgram.createAccountWithSeed(createAccountWithSeedParams),
);
await sendAndConfirmTransaction(
connection,
createAccountWithSeedTransaction,
[baseAccount],
{preflightCommitment: 'confirmed'},
);
const createAccountWithSeedBalance = await connection.getBalance(
createAccountWithSeedAddress,
);
expect(createAccountWithSeedBalance).to.eq(minimumAmount);
// Test CreateAccountWithSeed where fromPubkey != basePubkey
const uniqueFromAccount = Keypair.generate();
const newBaseAccount = Keypair.generate();
const createAccountWithSeedAddress2 = await PublicKey.createWithSeed(
newBaseAccount.publicKey,
seed,
programId,
);
await helpers.airdrop({
connection,
address: uniqueFromAccount.publicKey,
amount: 2 * LAMPORTS_PER_SOL,
});
const createAccountWithSeedParams2 = {
fromPubkey: uniqueFromAccount.publicKey,
newAccountPubkey: createAccountWithSeedAddress2,
basePubkey: newBaseAccount.publicKey,
seed,
lamports: minimumAmount,
space,
programId,
};
const createAccountWithSeedTransaction2 = new Transaction().add(
SystemProgram.createAccountWithSeed(createAccountWithSeedParams2),
);
await sendAndConfirmTransaction(
connection,
createAccountWithSeedTransaction2,
[uniqueFromAccount, newBaseAccount],
{preflightCommitment: 'confirmed'},
);
const createAccountWithSeedBalance2 = await connection.getBalance(
createAccountWithSeedAddress2,
);
expect(createAccountWithSeedBalance2).to.eq(minimumAmount);
// Transfer to a derived address to prep for TransferWithSeed
const programId2 = Keypair.generate().publicKey;
const transferWithSeedAddress = await PublicKey.createWithSeed(
basePubkey,
seed,
programId2,
);
await sendAndConfirmTransaction(
connection,
new Transaction().add(
SystemProgram.transfer({
fromPubkey: baseAccount.publicKey,
toPubkey: transferWithSeedAddress,
lamports: 3 * minimumAmount,
}),
),
[baseAccount],
{preflightCommitment: 'confirmed'},
);
let transferWithSeedAddressBalance = await connection.getBalance(
transferWithSeedAddress,
);
expect(transferWithSeedAddressBalance).to.eq(3 * minimumAmount);
// Test TransferWithSeed
const programId3 = Keypair.generate();
const toPubkey = await PublicKey.createWithSeed(
basePubkey,
seed,
programId3.publicKey,
);
const transferWithSeedParams = {
fromPubkey: transferWithSeedAddress,
basePubkey,
toPubkey,
lamports: 2 * minimumAmount,
seed,
programId: programId2,
};
const transferWithSeedTransaction = new Transaction().add(
SystemProgram.transfer(transferWithSeedParams),
);
await sendAndConfirmTransaction(
connection,
transferWithSeedTransaction,
[baseAccount],
{preflightCommitment: 'confirmed'},
);
const toBalance = await connection.getBalance(toPubkey);
expect(toBalance).to.eq(2 * minimumAmount);
transferWithSeedAddressBalance = await connection.getBalance(
createAccountWithSeedAddress,
);
expect(transferWithSeedAddressBalance).to.eq(minimumAmount);
// Test AllocateWithSeed
const allocateWithSeedParams = {
accountPubkey: toPubkey,
basePubkey,
seed,
space: 10,
programId: programId3.publicKey,
};
const allocateWithSeedTransaction = new Transaction().add(
SystemProgram.allocate(allocateWithSeedParams),
);
await sendAndConfirmTransaction(
connection,
allocateWithSeedTransaction,
[baseAccount],
{preflightCommitment: 'confirmed'},
);
let account = await connection.getAccountInfo(toPubkey);
if (account === null) {
expect(account).not.to.be.null;
return;
}
expect(account.data).to.have.length(10);
// Test AssignWithSeed
const assignWithSeedParams = {
accountPubkey: toPubkey,
basePubkey,
seed,
programId: programId3.publicKey,
};
const assignWithSeedTransaction = new Transaction().add(
SystemProgram.assign(assignWithSeedParams),
);
await sendAndConfirmTransaction(
connection,
assignWithSeedTransaction,
[baseAccount],
{preflightCommitment: 'confirmed'},
);
account = await connection.getAccountInfo(toPubkey);
if (account === null) {
expect(account).not.to.be.null;
return;
}
expect(account.owner).to.eql(programId3.publicKey);
}).timeout(10 * 1000);
}
});