fix: pay transaction fees from system accounts

This commit is contained in:
Michael Vines
2019-05-08 09:33:04 -07:00
parent 7bde7e3767
commit 48f0bcc2bf
10 changed files with 133 additions and 166 deletions

View File

@@ -3,8 +3,6 @@
import {Account} from './account';
import {PublicKey} from './publickey';
import {Loader} from './loader';
import {SystemProgram} from './system-program';
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
import type {Connection} from './connection';
/**
@@ -15,9 +13,7 @@ export class BpfLoader {
* Public key that identifies the BpfLoader
*/
static get programId(): PublicKey {
return new PublicKey(
'BPFLoader1111111111111111111111111111111111',
);
return new PublicKey('BPFLoader1111111111111111111111111111111111');
}
/**
@@ -27,26 +23,12 @@ export class BpfLoader {
* @param owner User account to load the program into
* @param elfBytes The entire ELF containing the BPF program
*/
static async load(
static load(
connection: Connection,
owner: Account,
payer: Account,
elf: Array<number>,
): Promise<PublicKey> {
const programAccount = new Account();
const transaction = SystemProgram.createAccount(
owner.publicKey,
programAccount.publicKey,
1 + Math.ceil(elf.length / Loader.chunkSize) + 1,
elf.length,
BpfLoader.programId,
);
await sendAndConfirmTransaction(connection, transaction, owner);
const loader = new Loader(connection, BpfLoader.programId);
await loader.load(programAccount, elf);
await loader.finalize(programAccount);
return programAccount.publicKey;
const program = new Account();
return Loader.load(connection, payer, program, BpfLoader.programId, elf);
}
}

View File

@@ -143,9 +143,7 @@ export class BudgetProgram {
* Public key that identifies the Budget program
*/
static get programId(): PublicKey {
return new PublicKey(
'Budget1111111111111111111111111111111111111',
);
return new PublicKey('Budget1111111111111111111111111111111111111');
}
/**

View File

@@ -626,7 +626,6 @@ export class Connection {
subscriptionId,
programId,
} = this._programAccountChangeSubscriptions[id];
console.log('program-id: ' + programId);
if (subscriptionId === null) {
try {
this._programAccountChangeSubscriptions[

View File

@@ -9,43 +9,46 @@ import {Transaction} from './transaction';
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
import {sleep} from './util/sleep';
import type {Connection} from './connection';
import {SystemProgram} from './system-program';
/**
* Program loader interface
*/
export class Loader {
/**
* @private
*/
connection: Connection;
/**
* @private
*/
programId: PublicKey;
/**
* Amount of program data placed in each load Transaction
*/
static get chunkSize(): number {
return 256;
return 229; // Keep program chunks under PACKET_DATA_SIZE
}
/**
* @param connection The connection to use
* @param programId Public key that identifies the loader
*/
constructor(connection: Connection, programId: PublicKey) {
Object.assign(this, {connection, programId});
}
/**
* Load program data
* Loads a generic program
*
* @param program Account to load the program info
* @param data Program data
* @param connection The connection to use
* @param payer System account that pays to load the program
* @param program Account to load the program into
* @param programId Public key that identifies the loader
* @param data Program octets
*/
async load(program: Account, data: Array<number>) {
static async load(
connection: Connection,
payer: Account,
program: Account,
programId: PublicKey,
data: Array<number>,
): Promise<PublicKey> {
{
const transaction = SystemProgram.createAccount(
payer.publicKey,
program.publicKey,
1,
data.length,
programId,
);
await sendAndConfirmTransaction(connection, transaction, payer);
}
const dataLayout = BufferLayout.struct([
BufferLayout.u32('instruction'),
BufferLayout.u32('offset'),
@@ -76,11 +79,11 @@ export class Loader {
const transaction = new Transaction().add({
keys: [{pubkey: program.publicKey, isSigner: true}],
programId: this.programId,
programId,
data,
});
transactions.push(
sendAndConfirmTransaction(this.connection, transaction, program),
sendAndConfirmTransaction(connection, transaction, payer, program),
);
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
@@ -100,29 +103,26 @@ export class Loader {
array = array.slice(chunkSize);
}
await Promise.all(transactions);
}
/**
* Finalize an account loaded with program data for execution
*
* @param program `load()`ed Account
*/
async finalize(program: Account) {
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
// Finalize the account loaded with program data for execution
{
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 1, // Finalize instruction
},
data,
);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 1, // Finalize instruction
},
data,
);
const transaction = new Transaction().add({
keys: [{pubkey: program.publicKey, isSigner: true}],
programId: this.programId,
data,
});
await sendAndConfirmTransaction(this.connection, transaction, program);
const transaction = new Transaction().add({
keys: [{pubkey: program.publicKey, isSigner: true}],
programId,
data,
});
await sendAndConfirmTransaction(connection, transaction, payer, program);
}
return program.publicKey;
}
}

View File

@@ -3,8 +3,6 @@
import {Account} from './account';
import {PublicKey} from './publickey';
import {Loader} from './loader';
import {SystemProgram} from './system-program';
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
import type {Connection} from './connection';
/**
@@ -15,41 +13,29 @@ export class NativeLoader {
* Public key that identifies the NativeLoader
*/
static get programId(): PublicKey {
return new PublicKey(
'NativeLoader1111111111111111111111111111111',
);
return new PublicKey('NativeLoader1111111111111111111111111111111');
}
/**
* Loads a native program
*
* @param connection The connection to use
* @param owner User account to load the program with
* @param payer System account that pays to load the program
* @param programName Name of the native program
*/
static async load(
static load(
connection: Connection,
owner: Account,
payer: Account,
programName: string,
): Promise<PublicKey> {
const bytes = [...Buffer.from(programName)];
const programAccount = new Account();
// Allocate memory for the program account
const transaction = SystemProgram.createAccount(
owner.publicKey,
programAccount.publicKey,
1 + 1 + 1,
bytes.length + 1,
const program = new Account();
return Loader.load(
connection,
payer,
program,
NativeLoader.programId,
bytes,
);
await sendAndConfirmTransaction(connection, transaction, owner);
const loader = new Loader(connection, NativeLoader.programId);
await loader.load(programAccount, bytes);
await loader.finalize(programAccount);
return programAccount.publicKey;
}
}

View File

@@ -243,7 +243,12 @@ export class Token {
programId,
data,
});
await sendAndConfirmTransaction(connection, transaction, tokenAccount);
await sendAndConfirmTransaction(
connection,
transaction,
owner,
tokenAccount,
);
return [token, initialAccountPublicKey];
}
@@ -299,7 +304,12 @@ export class Token {
programId: this.programId,
data,
});
await sendAndConfirmTransaction(this.connection, transaction, tokenAccount);
await sendAndConfirmTransaction(
this.connection,
transaction,
owner,
tokenAccount,
);
return tokenAccount.publicKey;
}

View File

@@ -92,7 +92,7 @@ export class Transaction {
signatures: Array<SignaturePubkeyPair> = [];
/**
* The first (primary) Transaction signature
* The first (payer) Transaction signature
*/
get signature(): Buffer | null {
if (this.signatures.length > 0) {
@@ -172,6 +172,14 @@ export class Transaction {
});
});
if (numRequiredSignatures > this.signatures.length) {
throw new Error(
`Insufficent signatures: expected ${numRequiredSignatures} but got ${
this.signatures.length
}`,
);
}
let keyCount = [];
shortvec.encodeLength(keyCount, keys.length);
@@ -252,7 +260,7 @@ export class Transaction {
]);
const transaction = {
numRequiredSignatures: Buffer.from([numRequiredSignatures]),
numRequiredSignatures: Buffer.from([this.signatures.length]),
keyCount: Buffer.from(keyCount),
keys: keys.map(key => new PublicKey(key).toBuffer()),
recentBlockhash: Buffer.from(bs58.decode(recentBlockhash)),