Skip to content

Commit

Permalink
fix(tx-builder): limit ttl to 3 in case tx built internally
Browse files Browse the repository at this point in the history
  • Loading branch information
davidyuk committed Mar 4, 2024
1 parent 0751244 commit 08d14c2
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 14 deletions.
5 changes: 5 additions & 0 deletions src/aens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export async function aensRevoke(
options: AensRevokeOptions,
): ReturnType<typeof sendTransaction> {
const nameRevokeTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.NameRevokeTx,
nameId: name,
Expand Down Expand Up @@ -96,6 +97,7 @@ export async function aensUpdate(
}

const nameUpdateTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.NameUpdateTx,
version: hasRawPointers ? 2 : 1,
Expand Down Expand Up @@ -150,6 +152,7 @@ export async function aensTransfer(
options: AensTransferOptions,
): ReturnType<typeof sendTransaction> {
const nameTransferTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.NameTransferTx,
nameId: name,
Expand Down Expand Up @@ -267,6 +270,7 @@ export async function aensClaim(
options: AensClaimOptions,
): Promise<AensClaimReturnType> {
const claimTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.NameClaimTx,
accountId: options.onAccount.address,
Expand Down Expand Up @@ -321,6 +325,7 @@ Awaited<ReturnType<typeof sendTransaction>> & {
const commitmentId = commitmentHash(name, salt);

const preclaimTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.NamePreclaimTx,
accountId: options.onAccount.address,
Expand Down
2 changes: 2 additions & 0 deletions src/contract/Contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class Contract<M extends ContractMethodsBase> {
const ownerId = opt.onAccount.address;
if (this.$options.bytecode == null) throw new IllegalArgumentError('Can\'t deploy without bytecode');
const tx = await buildTxAsync({
_isInternalBuild: true,
...opt,
tag: Tag.ContractCreateTx,
gasLimit: opt.gasLimit ?? await this._estimateGas('init', params, opt),
Expand Down Expand Up @@ -379,6 +380,7 @@ class Contract<M extends ContractMethodsBase> {
if (top != null) throw new IllegalArgumentError('Can\'t handle `top` option in on-chain contract call');
if (contractId == null) throw new MissingContractAddressError('Can\'t call contract without address');
const tx = await buildTxAsync({
_isInternalBuild: true,
...opt,
tag: Tag.ContractCallTx,
gasLimit: opt.gasLimit ?? await this._estimateGas(fn, params, opt),
Expand Down
1 change: 1 addition & 0 deletions src/contract/ga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export async function createGeneralizedAccount(
});

const tx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.GaAttachTx,
onNode,
Expand Down
4 changes: 4 additions & 0 deletions src/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export async function postQueryToOracle(
const senderId = options.onAccount.address;

const oracleQueryTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.OracleQueryTx,
oracleId,
Expand Down Expand Up @@ -174,6 +175,7 @@ Awaited<ReturnType<typeof sendTransaction>> & Awaited<ReturnType<typeof getOracl
> {
const oracleId = encode(decode(options.onAccount.address), Encoding.OracleAddress);
const oracleExtendTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.OracleExtendTx,
oracleId,
Expand Down Expand Up @@ -206,6 +208,7 @@ export async function respondToQuery(
> {
const oracleId = encode(decode(options.onAccount.address), Encoding.OracleAddress);
const oracleRespondTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.OracleResponseTx,
oracleId,
Expand Down Expand Up @@ -290,6 +293,7 @@ export async function registerOracle(
> {
const accountId = options.onAccount.address;
const oracleRegisterTx = await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.OracleRegisterTx,
accountId,
Expand Down
21 changes: 18 additions & 3 deletions src/spend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export async function spend(
): ReturnType<typeof sendTransaction> {
return sendTransaction(
await buildTxAsync({
_isInternalBuild: true,
...options,
tag: Tag.SpendTx,
senderId: options.onAccount.address,
Expand Down Expand Up @@ -78,15 +79,25 @@ export async function transferFunds(
const desiredAmount = balance.times(fraction).integerValue(BigNumber.ROUND_HALF_UP);
const { fee } = unpackTx(
await buildTxAsync({
...options, tag: Tag.SpendTx, senderId, recipientId, amount: desiredAmount,
_isInternalBuild: true,
...options,
tag: Tag.SpendTx,
senderId,
recipientId,
amount: desiredAmount,
}),
Tag.SpendTx,
);
// Reducing of the amount may reduce transaction fee, so this is not completely accurate
const amount = desiredAmount.plus(fee).gt(balance) ? balance.minus(fee) : desiredAmount;
return sendTransaction(
await buildTxAsync({
...options, tag: Tag.SpendTx, senderId, recipientId, amount,
_isInternalBuild: true,
...options,
tag: Tag.SpendTx,
senderId,
recipientId,
amount,
}),
options,
);
Expand All @@ -109,7 +120,11 @@ export async function payForTransaction(
): ReturnType<typeof sendTransaction> {
return sendTransaction(
await buildTxAsync({
...options, tag: Tag.PayingForTx, payerId: options.onAccount.address, tx: transaction,
_isInternalBuild: true,
...options,
tag: Tag.PayingForTx,
payerId: options.onAccount.address,
tx: transaction,
}),
options,
);
Expand Down
8 changes: 7 additions & 1 deletion src/tx/builder/field-types/nonce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export default function genNonceField<SenderKey extends string>(senderKey: Sende
value: number | undefined,
params: {},
// TODO: replace `string` with AddressEncodings
options: { [key in SenderKey]: string } & { strategy?: NextNonceStrategy; onNode?: Node },
options: { [key in SenderKey]: string } & {
strategy?: NextNonceStrategy;
onNode?: Node;
_isInternalBuild?: boolean;
},
) => Promise<number>;
deserialize: (value: Buffer) => number;
senderKey: string;
Expand All @@ -21,6 +25,8 @@ export default function genNonceField<SenderKey extends string>(senderKey: Sende

async prepare(value, params, options) {
if (value != null) return value;
// TODO: uncomment the below line
// if (options._isInternalBuild === true) return 0;
const { onNode, strategy } = options;
const senderId = options[senderKey];
const requirement = 'provided (or provide `nonce` instead)';
Expand Down
9 changes: 6 additions & 3 deletions src/tx/builder/field-types/ttl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,17 @@ export default {
value: number | undefined,
params: {},
// TODO: { absoluteTtl: true } | { absoluteTtl: false, onNode: Node }
{ onNode, absoluteTtl, ...options }: {
{
onNode, absoluteTtl, _isInternalBuild, ...options
}: {
onNode?: Node;
absoluteTtl?: boolean;
_isInternalBuild?: boolean;
} & Parameters<typeof _getPollInterval>[1],
) {
if (absoluteTtl !== true && value !== 0 && value != null) {
if (absoluteTtl !== true && value !== 0 && (value != null || _isInternalBuild === true)) {
if (onNode == null) throw new ArgumentError('onNode', 'provided', onNode);
value += await getHeight({ ...options, onNode, cached: true });
value = (value ?? 3) + await getHeight({ ...options, onNode, cached: true });
}
return value;
},
Expand Down
9 changes: 7 additions & 2 deletions test/integration/aens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ describe('Aens', () => {
assertNotNull(claimed.tx);
assertNotNull(claimed.blockHeight);
assertNotNull(claimed.signatures);
expect(claimed.tx.fee).to.satisfy((fee: bigint) => fee >= 16860000000000n);
expect(claimed.tx.fee).to.satisfy((fee: bigint) => fee < 17000000000000n);
expect(claimed).to.be.eql({
tx: {
fee: 16860000000000n,
fee: claimed.tx.fee,
nonce: claimed.tx.nonce,
accountId: aeSdk.address,
name: n,
ttl: claimed.tx.ttl,
nameSalt: 0,
nameFee: 500000000000000n,
version: 2,
Expand Down Expand Up @@ -76,13 +79,15 @@ describe('Aens', () => {
expect(claimed.extendTtl).to.be.a('function');
assertNotNull(claimed.tx);
assertNotNull(claimed.signatures);
expect(claimed.tx.fee).to.be.oneOf([16940000000000n, 16960000000000n]);
expect(claimed.tx.fee).to.satisfy((fee: bigint) => fee >= 16960000000000n);
expect(claimed.tx.fee).to.satisfy((fee: bigint) => fee < 17100000000000n);
expect(claimed).to.be.eql({
tx: {
fee: claimed.tx.fee,
nonce: claimed.tx.nonce,
accountId: aeSdk.address,
name: n,
ttl: claimed.tx.ttl,
nameSalt: claimed.tx.nameSalt,
nameFee: 300000000000000n,
version: 2,
Expand Down
4 changes: 3 additions & 1 deletion test/integration/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ describe('Node Chain', () => {
expect(getCount()).to.be.equal(2); // nonce, post tx
await aeSdk.poll(hash);

await aeSdk.getHeight({ cached: false });
getCount = bindRequestCounter(aeSdk.api);
hash = (await aeSdk.spend(100, publicKey, { waitMined: false })).hash;
expect(getCount()).to.be.equal(5); // nonce, validator(acc, height, status), post tx
Expand All @@ -140,6 +141,7 @@ describe('Node Chain', () => {
const txPostRetry = '/v3/transactions?int-as-string=true&__sdk-retry=';
it('multiple spends from one account', async () => {
const { nextNonce } = await aeSdk.api.getAccountNextNonce(aeSdk.address);
await aeSdk.getHeight({ cached: false });
const getCount = bindRequestCounter(aeSdk.api);
const spends = await Promise.all(accounts.map(async (account, idx) => aeSdk.spend(
Math.floor(Math.random() * 1000 + 1e15),
Expand All @@ -160,7 +162,7 @@ describe('Node Chain', () => {
);
transactions.push(...spends.map(({ hash }) => hash));
const txPostCount = accounts.length;
expect(getCount()).to.be.equal(txPostCount);
expect(getCount()).to.be.equal(txPostCount + 1); // height for relative ttl
});

it('ensure transactions mined', async () => Promise.all(transactions.map(async (hash) => aeSdkWithoutAccount.poll(hash))));
Expand Down
7 changes: 4 additions & 3 deletions test/integration/contract-aci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ describe('Contract instance', () => {
expect(res.decodedResult).to.be.equal(2n);
ensureEqual<Tag.SignedTx>(res.tx.tag, Tag.SignedTx);
ensureEqual<Tag.ContractCallTx>(res.tx.encodedTx.tag, Tag.ContractCallTx);
expect(res.tx.encodedTx.fee).to.be.equal('182000000000000');
expect(res.tx.encodedTx.fee).to.satisfy((fee: string) => +fee >= 182000000000000);
expect(res.tx.encodedTx.fee).to.satisfy((fee: string) => +fee < 184000000000000);
});

it('calls without waitMined and get result later', async () => {
Expand Down Expand Up @@ -561,7 +562,7 @@ describe('Contract instance', () => {

it('validates gas limit for contract calls', async () => {
await expect(contract.setKey(4, { gasLimit: 7e6 }))
.to.be.rejectedWith(IllegalArgumentError, 'Gas limit 7000000 must be less or equal to 5818100');
.to.be.rejectedWith(IllegalArgumentError, 'Gas limit 7000000 must be less or equal to 58');
});

it('sets maximum possible gas limit for dry-run contract calls', async () => {
Expand All @@ -570,7 +571,7 @@ describe('Contract instance', () => {
const { gasLimit } = tx;
expect(gasLimit).to.be.equal(5817980);
await expect(contract.intFn(4, { gasLimit: gasLimit + 1 }))
.to.be.rejectedWith(IllegalArgumentError, 'Gas limit 5817981 must be less or equal to 5817980');
.to.be.rejectedWith(IllegalArgumentError, 'Gas limit 5817981 must be less or equal to 58');
await expect(contract.intFn(4, { gasLimit: gasLimit + 1, gasMax: 6e6 + 1 }))
.to.be.rejectedWith('v3/dry-run error: Over the gas limit');
});
Expand Down
1 change: 1 addition & 0 deletions test/integration/paying-for.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('Paying for transaction of another account', () => {
Object.assign(aeSdkNotPayingFee._options, {
waitMined: false,
innerTx: true,
ttl: 0,
});
const contract: TestContract = await aeSdkNotPayingFee.initializeContract({ sourceCode });
const { rawTx: contractDeployTx, address } = await contract.$deploy([42]);
Expand Down
2 changes: 1 addition & 1 deletion test/integration/~execution-cost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('Execution cost', () => {
});

it('calculates execution cost for spend tx', async () => {
const { rawTx } = await aeSdk.spend(100, aeSdk.address);
const { rawTx } = await aeSdk.spend(100, aeSdk.address, { ttl: 0 });
const expectedCost = 16660000000000n;
expect(getExecutionCostBySignedTx(rawTx, networkId)).to.equal(expectedCost);
expect(getExecutionCost(buildTx(unpackTx(rawTx, Tag.SignedTx).encodedTx)))
Expand Down

0 comments on commit 08d14c2

Please sign in to comment.