fix: Squash budget bugs

This commit is contained in:
Tyera Eulberg 2019-05-24 16:07:16 -06:00 committed by Michael Vines
parent c99d8dd15c
commit 87c1becbef
6 changed files with 155 additions and 157 deletions

View File

@ -9,7 +9,6 @@ const solanaWeb3 = require('..');
const account1 = new solanaWeb3.Account();
const account2 = new solanaWeb3.Account();
const contractFunds = new solanaWeb3.Account();
const contractState = new solanaWeb3.Account();
let url;
@ -23,28 +22,20 @@ function showBalance() {
return Promise.all([
connection.getBalance(account1.publicKey),
connection.getBalance(account2.publicKey),
connection.getBalance(contractFunds.publicKey),
connection.getBalance(contractState.publicKey),
]).then(
([fromBalance, toBalance, contractFundsBalance, contractStateBalance]) => {
console.log(
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
);
console.log(
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
);
console.log(
`Contract Funds: ${
contractFunds.publicKey
} has a balance of ${contractFundsBalance}`,
);
console.log(
`Contract State: ${
contractState.publicKey
} has a balance of ${contractStateBalance}`,
);
},
);
]).then(([fromBalance, toBalance, contractStateBalance]) => {
console.log(
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
);
console.log(
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
);
console.log(
`Contract State: ${
contractState.publicKey
} has a balance of ${contractStateBalance}`,
);
});
}
function confirmTransaction(signature) {
@ -52,6 +43,10 @@ function confirmTransaction(signature) {
return connection.getSignatureStatus(signature).then(confirmation => {
if (confirmation && 'Ok' in confirmation) {
console.log('Transaction confirmed');
} else if (confirmation) {
throw new Error(
`Transaction was not confirmed (${JSON.stringify(confirmation.Err)})`,
);
} else {
throw new Error(`Transaction was not confirmed (${confirmation})`);
}
@ -68,36 +63,10 @@ function airDrop() {
showBalance()
.then(airDrop)
.then(showBalance)
.then(() => {
console.log(`\n== Creating account for the contract funds`);
const transaction = solanaWeb3.SystemProgram.createAccount(
account1.publicKey,
contractFunds.publicKey,
50, // number of lamports to transfer
0,
solanaWeb3.BudgetProgram.programId,
);
return connection.sendTransaction(transaction, account1);
})
.then(confirmTransaction)
.then(showBalance)
.then(() => {
console.log(`\n== Creating account for the contract state`);
const transaction = solanaWeb3.SystemProgram.createAccount(
account1.publicKey,
contractState.publicKey,
1, // account1 pays 1 lamport to hold the contract state
solanaWeb3.BudgetProgram.space,
solanaWeb3.BudgetProgram.programId,
);
return connection.sendTransaction(transaction, account1);
})
.then(confirmTransaction)
.then(showBalance)
.then(() => {
console.log(`\n== Initializing contract`);
const transaction = solanaWeb3.BudgetProgram.pay(
contractFunds.publicKey,
account1.publicKey,
contractState.publicKey,
account2.publicKey,
50,
@ -106,7 +75,11 @@ showBalance()
new Date('2050'),
),
);
return connection.sendTransaction(transaction, contractFunds);
return solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
account1,
);
})
.then(confirmTransaction)
.then(showBalance)
@ -118,7 +91,11 @@ showBalance()
account2.publicKey,
new Date('2050'),
);
return connection.sendTransaction(transaction, account1);
return solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
account1,
);
})
.then(confirmTransaction)
.then(showBalance)

View File

@ -8,7 +8,6 @@ const solanaWeb3 = require('..');
const account1 = new solanaWeb3.Account();
const account2 = new solanaWeb3.Account();
const contractFunds = new solanaWeb3.Account();
const contractState = new solanaWeb3.Account();
const approver1 = new solanaWeb3.Account();
@ -24,37 +23,34 @@ function showBalance() {
return Promise.all([
connection.getBalance(account1.publicKey),
connection.getBalance(account2.publicKey),
connection.getBalance(contractFunds.publicKey),
connection.getBalance(contractState.publicKey),
]).then(
([fromBalance, toBalance, contractFundsBalance, contractStateBalance]) => {
console.log(
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
);
console.log(
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
);
console.log(
`Contract Funds: ${
contractFunds.publicKey
} has a balance of ${contractFundsBalance}`,
);
console.log(
`Contract State: ${
contractState.publicKey
} has a balance of ${contractStateBalance}`,
);
},
);
]).then(([fromBalance, toBalance, contractStateBalance]) => {
console.log(
`Account1: ${account1.publicKey} has a balance of ${fromBalance}`,
);
console.log(
`Account2: ${account2.publicKey} has a balance of ${toBalance}`,
);
console.log(
`Contract State: ${
contractState.publicKey
} has a balance of ${contractStateBalance}`,
);
});
}
function confirmTransaction(signature) {
console.log('Confirming transaction:', signature);
return connection.getSignatureStatus(signature).then(confirmation => {
if (confirmation !== 'Confirmed') {
if (confirmation && 'Ok' in confirmation) {
console.log('Transaction confirmed');
} else if (confirmation) {
throw new Error(
`Transaction was not confirmed (${JSON.stringify(confirmation.Err)})`,
);
} else {
throw new Error(`Transaction was not confirmed (${confirmation})`);
}
console.log('Transaction confirmed');
});
}
@ -88,43 +84,21 @@ showBalance()
})
.then(confirmTransaction)
.then(showBalance)
.then(() => {
console.log(`\n== Creating account for the contract funds`);
const transaction = solanaWeb3.SystemProgram.createAccount(
account1.publicKey,
contractFunds.publicKey,
50, // number of lamports to transfer
0,
solanaWeb3.BudgetProgram.programId,
);
return connection.sendTransaction(transaction, account1);
})
.then(confirmTransaction)
.then(showBalance)
.then(() => {
console.log(`\n== Creating account for the contract state`);
const transaction = solanaWeb3.SystemProgram.createAccount(
account1.publicKey,
contractState.publicKey,
1, // account1 pays 1 lamport to hold the contract state
solanaWeb3.BudgetProgram.space,
solanaWeb3.BudgetProgram.programId,
);
return connection.sendTransaction(transaction, account1);
})
.then(confirmTransaction)
.then(showBalance)
.then(() => {
console.log(`\n== Initializing contract`);
const transaction = solanaWeb3.BudgetProgram.payOnBoth(
contractFunds.publicKey,
account1.publicKey,
contractState.publicKey,
account2.publicKey,
50,
solanaWeb3.BudgetProgram.signatureCondition(approver1.publicKey),
solanaWeb3.BudgetProgram.signatureCondition(approver2.publicKey),
);
return connection.sendTransaction(transaction, contractFunds);
return solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
account1,
);
})
.then(confirmTransaction)
.then(showBalance)
@ -135,7 +109,11 @@ showBalance()
contractState.publicKey,
account2.publicKey,
);
return connection.sendTransaction(transaction, approver1);
return solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
approver1,
);
})
.then(confirmTransaction)
.then(showBalance)
@ -146,7 +124,11 @@ showBalance()
contractState.publicKey,
account2.publicKey,
);
return connection.sendTransaction(transaction, approver2);
return solanaWeb3.sendAndConfirmTransaction(
connection,
transaction,
approver2,
);
})
.then(confirmTransaction)
.then(showBalance)

View File

@ -5851,7 +5851,7 @@
"dependencies": {
"marked": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
"dev": true
},
@ -6115,7 +6115,7 @@
},
"marked": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
"integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
"dev": true
},
@ -12399,7 +12399,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
@ -16878,7 +16878,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}

View File

@ -4,7 +4,7 @@ import * as BufferLayout from 'buffer-layout';
import {Transaction} from './transaction';
import {PublicKey} from './publickey';
import * as Layout from './layout';
import {SystemProgram} from './system-program';
/**
* Represents a condition that is met by executing a `applySignature()`
@ -114,20 +114,10 @@ function serializeCondition(condition: BudgetCondition) {
return data;
}
case 'signature': {
const dataLayout = BufferLayout.struct([
BufferLayout.u32('condition'),
Layout.publicKey('from'),
]);
const from = condition.from.toBuffer();
const data = Buffer.alloc(4 + from.length);
dataLayout.encode(
{
instruction: 1, // Signature
from,
},
data,
);
data.writeUInt32LE(1, 0); // Condition enum = Signature
from.copy(data, 4);
return data;
}
default:
@ -190,8 +180,8 @@ export class BudgetProgram {
pos += 4;
switch (conditions.length) {
case 0:
data.writeUInt32LE(0, pos); // Budget enum = Pay
case 0: {
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
pos += 4;
{
@ -199,17 +189,27 @@ export class BudgetProgram {
payment.copy(data, pos);
pos += payment.length;
}
const trimmedData = data.slice(0, pos);
return new Transaction().add({
const transaction = SystemProgram.createAccount(
from,
program,
amount,
trimmedData.length,
this.programId,
);
return transaction.add({
keys: [
{pubkey: from, isSigner: true, isDebitable: true},
{pubkey: to, isSigner: false, isDebitable: false},
{pubkey: program, isSigner: false, isDebitable: true},
],
programId: this.programId,
data: data.slice(0, pos),
data: trimmedData,
});
case 1:
data.writeUInt32LE(1, pos); // Budget enum = After
}
case 1: {
data.writeUInt32LE(1, pos); // BudgetExpr enum = After
pos += 4;
{
const condition = conditions[0];
@ -218,23 +218,32 @@ export class BudgetProgram {
conditionData.copy(data, pos);
pos += conditionData.length;
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
pos += 4;
const paymentData = serializePayment({amount, to});
paymentData.copy(data, pos);
pos += paymentData.length;
}
const trimmedData = data.slice(0, pos);
return new Transaction().add({
keys: [
{pubkey: from, isSigner: true, isDebitable: true},
{pubkey: program, isSigner: false, isDebitable: true},
{pubkey: to, isSigner: false, isDebitable: false},
],
const transaction = SystemProgram.createAccount(
from,
program,
amount,
trimmedData.length,
this.programId,
);
return transaction.add({
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
programId: this.programId,
data: data.slice(0, pos),
data: trimmedData,
});
}
case 2:
data.writeUInt32LE(2, pos); // Budget enum = Or
case 2: {
data.writeUInt32LE(2, pos); // BudgetExpr enum = Or
pos += 4;
for (let condition of conditions) {
@ -242,20 +251,29 @@ export class BudgetProgram {
conditionData.copy(data, pos);
pos += conditionData.length;
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
pos += 4;
const paymentData = serializePayment({amount, to});
paymentData.copy(data, pos);
pos += paymentData.length;
}
const trimmedData = data.slice(0, pos);
return new Transaction().add({
keys: [
{pubkey: from, isSigner: true, isDebitable: true},
{pubkey: program, isSigner: false, isDebitable: true},
{pubkey: to, isSigner: false, isDebitable: false},
],
const transaction = SystemProgram.createAccount(
from,
program,
amount,
trimmedData.length,
this.programId,
);
return transaction.add({
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
programId: this.programId,
data: data.slice(0, pos),
data: trimmedData,
});
}
default:
throw new Error(
@ -282,7 +300,7 @@ export class BudgetProgram {
data.writeUInt32LE(0, pos); // NewBudget instruction
pos += 4;
data.writeUInt32LE(3, pos); // Budget enum = And
data.writeUInt32LE(3, pos); // BudgetExpr enum = And
pos += 4;
for (let condition of [condition1, condition2]) {
@ -291,18 +309,27 @@ export class BudgetProgram {
pos += conditionData.length;
}
data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay
pos += 4;
const paymentData = serializePayment({amount, to});
paymentData.copy(data, pos);
pos += paymentData.length;
return new Transaction().add({
keys: [
{pubkey: from, isSigner: true, isDebitable: true},
{pubkey: program, isSigner: false, isDebitable: true},
{pubkey: to, isSigner: false, isDebitable: false},
],
const trimmedData = data.slice(0, pos);
const transaction = SystemProgram.createAccount(
from,
program,
amount,
trimmedData.length,
this.programId,
);
return transaction.add({
keys: [{pubkey: program, isSigner: false, isDebitable: true}],
programId: this.programId,
data: data.slice(0, pos),
data: trimmedData,
});
}

View File

@ -164,6 +164,8 @@ export class Transaction {
let numCreditOnlySignedAccounts = 0;
let numCreditOnlyUnsignedAccounts = 0;
const programIds = [];
this.instructions.forEach(instruction => {
instruction.keys.forEach(keySignerPair => {
const keyStr = keySignerPair.pubkey.toString();
@ -183,6 +185,12 @@ export class Transaction {
});
const programId = instruction.programId.toString();
if (!programIds.includes(programId)) {
programIds.push(programId);
}
});
programIds.forEach(programId => {
if (!keys.includes(programId)) {
keys.push(programId);
numCreditOnlyUnsignedAccounts += 1;

View File

@ -15,7 +15,8 @@ test('pay', () => {
to.publicKey,
123,
);
expect(transaction.keys).toHaveLength(2);
expect(transaction.instructions[0].keys).toHaveLength(2);
expect(transaction.instructions[1].keys).toHaveLength(2);
// TODO: Validate transaction contents more
transaction = BudgetProgram.pay(
@ -25,7 +26,8 @@ test('pay', () => {
123,
BudgetProgram.signatureCondition(from.publicKey),
);
expect(transaction.keys).toHaveLength(3);
expect(transaction.instructions[0].keys).toHaveLength(2);
expect(transaction.instructions[1].keys).toHaveLength(1);
// TODO: Validate transaction contents more
transaction = BudgetProgram.pay(
@ -36,7 +38,8 @@ test('pay', () => {
BudgetProgram.signatureCondition(from.publicKey),
BudgetProgram.timestampCondition(from.publicKey, new Date()),
);
expect(transaction.keys).toHaveLength(3);
expect(transaction.instructions[0].keys).toHaveLength(2);
expect(transaction.instructions[1].keys).toHaveLength(1);
// TODO: Validate transaction contents more
transaction = BudgetProgram.payOnBoth(
@ -47,7 +50,8 @@ test('pay', () => {
BudgetProgram.signatureCondition(from.publicKey),
BudgetProgram.timestampCondition(from.publicKey, new Date()),
);
expect(transaction.keys).toHaveLength(3);
expect(transaction.instructions[0].keys).toHaveLength(2);
expect(transaction.instructions[1].keys).toHaveLength(1);
// TODO: Validate transaction contents more
});