Skip to content
Open
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
16 changes: 13 additions & 3 deletions src/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,14 +592,24 @@ export class TransactionBuilder {
memo: this.memo ? this.memo.toXDRObject() : null
};

// Allow building without explicit timebounds if ledgerbounds with maxLedger is set,
// since that provides an alternative upper bound on transaction validity.
const hasValidLedgerBounds =
this.ledgerbounds !== null && this.ledgerbounds.maxLedger > 0;

if (
this.timebounds === null ||
typeof this.timebounds.minTime === 'undefined' ||
typeof this.timebounds.maxTime === 'undefined'
) {
throw new Error(
'TimeBounds has to be set or you must call setTimeout(TimeoutInfinite).'
);
if (hasValidLedgerBounds) {
// Default to infinite timebounds when ledger bounds provide the constraint
this.timebounds = { minTime: 0, maxTime: 0 };
} else {
throw new Error(
'TimeBounds has to be set or you must call setTimeout(TimeoutInfinite).'
);
}
}

if (isValidDate(this.timebounds.minTime)) {
Expand Down
83 changes: 83 additions & 0 deletions test/unit/transaction_builder_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,89 @@ describe('TransactionBuilder', function () {
});
});

describe('ledgerbounds without timebounds', function () {
it('allows building with ledgerbounds.maxLedger > 0 and no setTimeout', function () {
let source = new StellarBase.Account(
'GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ',
'0'
);
let transaction = new StellarBase.TransactionBuilder(source, {
fee: 100,
networkPassphrase: StellarBase.Networks.TESTNET
})
.addOperation(
StellarBase.Operation.payment({
destination:
'GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2',
asset: StellarBase.Asset.native(),
amount: '1000'
})
)
.setLedgerbounds(0, 100)
.build();

expect(transaction.ledgerBounds.minLedger).to.equal(0);
expect(transaction.ledgerBounds.maxLedger).to.equal(100);
// timebounds defaults to infinite when only ledgerbounds are set
expect(transaction.timeBounds.minTime).to.equal('0');
expect(transaction.timeBounds.maxTime).to.equal('0');
});

it('still requires setTimeout when ledgerbounds.maxLedger is 0', function () {
let source = new StellarBase.Account(
'GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ',
'0'
);
let transactionBuilder = new StellarBase.TransactionBuilder(source, {
fee: 100
})
.addOperation(
StellarBase.Operation.payment({
destination:
'GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2',
asset: StellarBase.Asset.native(),
amount: '1000'
})
)
.setLedgerbounds(0, 0); // maxLedger of 0 means no upper bound

expect(() => transactionBuilder.build()).to.throw(
/TimeBounds has to be set/
);
});

it('allows combining ledgerbounds with explicit timebounds', function () {
let source = new StellarBase.Account(
'GCEZWKCA5VLDNRLN3RPRJMRZOX3Z6G5CHCGSNFHEYVXM3XOJMDS674JZ',
'0'
);
let transaction = new StellarBase.TransactionBuilder(source, {
fee: 100,
networkPassphrase: StellarBase.Networks.TESTNET,
timebounds: {
minTime: 0,
maxTime: 1000
},
ledgerbounds: {
minLedger: 0,
maxLedger: 100
}
})
.addOperation(
StellarBase.Operation.payment({
destination:
'GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2',
asset: StellarBase.Asset.native(),
amount: '1000'
})
)
.build();

expect(transaction.ledgerBounds.maxLedger).to.equal(100);
expect(transaction.timeBounds.maxTime).to.equal('1000');
});
});

describe('.buildFeeBumpTransaction', function () {
it('builds a fee bump transaction', function (done) {
const networkPassphrase = 'Standalone Network ; February 2017';
Expand Down
Loading