Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.

Commit 7c30e02

Browse files
author
Bradley Hart
committed
Merge branch 'release/21.0.x' into transaction-compression
2 parents 978847f + bef1888 commit 7c30e02

File tree

9 files changed

+86
-29
lines changed

9 files changed

+86
-29
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const api = new Api({ rpc, signatureProvider, textDecoder: new TextDecoder(), te
8383

8484
### Sending a transaction
8585

86-
`transact()` is used to sign and push transactions onto the blockchain with an optional configuration object parameter. This parameter can override the default value of `broadcast: true`, and can be used to fill TAPOS fields given `blocksBehind` and `expireSeconds`. Given no configuration options, transactions are expected to be unpacked with TAPOS fields (`expiration`, `ref_block_num`, `ref_block_prefix`) and will automatically be broadcast onto the chain.
86+
`transact()` is used to sign and push transactions onto the blockchain with an optional configuration object parameter. This parameter can override the default value of `broadcast: true`, and can be used to fill TAPOS fields given `expireSeconds` and either `blocksBehind` or `useLastIrreversible`. Given no configuration options, transactions are expected to be unpacked with TAPOS fields (`expiration`, `ref_block_num`, `ref_block_prefix`) and will automatically be broadcast onto the chain.
8787

8888
```js
8989
(async () => {

docs/01_technical-overview.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ The `Api` object is typically used when transacting on an EOSIO-based blockchain
1313
The typical use of the `Api` object is to call its [`transact` method](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-api.ts#L214-L254). This method performs a number of steps depending on the input passed to it:
1414

1515
* The `transact` method first checks if the **chainId** was set in the `Api` constructor, and if not, uses the [`JsonRpc` object's](#jsonrpc) [`get_info`](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-jsonrpc.ts#L101) method to retrieve the **chainId**.
16-
* The `transact` method then checks if the `blocksBehind` and `expiration` fields are set and well-formed in the [optional configuration object, as specified in *How to Submit a Transaction*](how-to-guides/01_how-to-submit-a-transaction.md#).
17-
* If so, the block *blocksBehind* the head block retrieved from [`JsonRpc`'s `get_info`](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-jsonrpc.ts#L101) is set as the reference block and the transaction header is serialized using this reference block and the `expiration` field.
16+
* The `transact` method then checks if the `expireSeconds` and either `blocksBehind` or `useLastIrreversible` fields are set and well-formed in the [optional configuration object, as specified in *How to Submit a Transaction*](how-to-guides/01_how-to-submit-a-transaction.md#).
17+
* If so, either the *last_irreversible_block_num* or the block *blocksBehind* the head block retrieved from [`JsonRpc`'s `get_info`](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-jsonrpc.ts#L101) is set as the reference block and the transaction header is serialized using this reference block and the `expireSeconds` field.
1818
* The `transact` method then checks if the appropriate TAPOS fields are present in the transaction ([they can either be specified directly in the transaction or in the optional configuration object](how-to-guides/01_how-to-submit-a-transaction.md#)) and throws an Error if not.
1919
* The necessary `abi`s for a transaction are then retrieved for the case when `transact` is expected to sign the transaction.
2020
* The `actions` are serialized using the `eosjs-serialize` `ser` object.

docs/how-to-guides/01_how-to-submit-a-transaction.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ The first parameter specifies the actions in the transaction, and their correspo
1818
}]
1919
}
2020
```
21-
The second parameter is an [optional configuration object parameter](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-api.ts#L215). This optional parameter can override the default values of `broadcast: true` and `sign: true`, and can be used to fill [TAPOS](https://eosio.stackexchange.com/questions/2362/what-is-transaction-as-proof-of-stake-tapos-and-when-would-a-smart-contract) fields with the specified `blocksBehind` and `expireSeconds` if necessary. These fields are required if the first parameter specified above does not itself contain the TAPOS fields `expiration`, `ref_block_num`, and `ref_block_prefix`. In this case it does not, so the fields are necessary.
21+
The second parameter is an [optional configuration object parameter](https://github.com/EOSIO/eosjs/blob/master/src/eosjs-api.ts#L215). This optional parameter can override the default values of `broadcast: true` and `sign: true`, and can be used to fill [TAPOS](https://eosio.stackexchange.com/questions/2362/what-is-transaction-as-proof-of-stake-tapos-and-when-would-a-smart-contract) fields with the specified `expireSeconds` and either `blocksBehind` or `useLastIrreversible` if necessary. A combination of these fields are required if the first parameter specified above does not itself contain the TAPOS fields `expiration`, `ref_block_num`, and `ref_block_prefix`. In this case it does not, so the fields are necessary.
2222
```javascript
2323
{
2424
blocksBehind: 3,

src/PublicKey.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class PublicKey {
4747
/** Export public key as `elliptic`-format public key */
4848
public toElliptic(): EC.KeyPair {
4949
return this.ec.keyPair({
50-
pub: new Buffer(this.key.data),
50+
pub: Buffer.from(this.key.data),
5151
});
5252
}
5353

src/eosjs-api-interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,6 @@ export interface TransactConfig {
7777
sign?: boolean;
7878
compression?: boolean;
7979
blocksBehind?: number;
80+
useLastIrreversible?: boolean;
8081
expireSeconds?: number;
8182
}

src/eosjs-api.ts

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
BinaryAbi,
1212
CachedAbi,
1313
SignatureProvider,
14-
TransactConfig,
14+
TransactConfig
1515
} from './eosjs-api-interfaces';
1616
import { JsonRpc } from './eosjs-jsonrpc';
1717
import {
@@ -265,33 +265,29 @@ export class Api {
265265
* * If both `blocksBehind` and `expireSeconds` are present,
266266
* then fetch the block which is `blocksBehind` behind head block,
267267
* use it as a reference for TAPoS, and expire the transaction `expireSeconds` after that block's time.
268+
* * If both `useLastIrreversible` and `expireSeconds` are present,
269+
* then fetch the last irreversible block, use it as a reference for TAPoS,
270+
* and expire the transaction `expireSeconds` after that block's time.
268271
* @returns node response if `broadcast`, `{signatures, serializedTransaction}` if `!broadcast`
269272
*/
270273
public async transact(
271274
transaction: any,
272-
{ broadcast = true, sign = true, compression, blocksBehind, expireSeconds }: TransactConfig = {}
273-
): Promise<any> {
275+
{ broadcast = true, sign = true, compression, blocksBehind, useLastIrreversible, expireSeconds }:
276+
TransactConfig = {}): Promise<any>
277+
{
274278
let info: GetInfoResult;
275279

280+
if (typeof blocksBehind === 'number' && useLastIrreversible) {
281+
throw new Error('Use either blocksBehind or useLastIrreversible');
282+
}
283+
276284
if (!this.chainId) {
277285
info = await this.rpc.get_info();
278286
this.chainId = info.chain_id;
279287
}
280288

281-
if (typeof blocksBehind === 'number' && expireSeconds) { // use config fields to generate TAPOS if they exist
282-
if (!info) {
283-
info = await this.rpc.get_info();
284-
}
285-
286-
const taposBlockNumber = info.head_block_num - blocksBehind;
287-
let refBlock: GetBlockHeaderStateResult | GetBlockResult;
288-
try {
289-
refBlock = await this.rpc.get_block_header_state(taposBlockNumber);
290-
} catch (error) {
291-
refBlock = await this.rpc.get_block(taposBlockNumber);
292-
}
293-
294-
transaction = { ...ser.transactionHeader(refBlock, expireSeconds), ...transaction };
289+
if ((typeof blocksBehind === 'number' || useLastIrreversible) && expireSeconds) {
290+
transaction = this.generateTapos(info, transaction, blocksBehind, useLastIrreversible, expireSeconds);
295291
}
296292

297293
if (!this.hasRequiredTaposFields(transaction)) {
@@ -356,6 +352,34 @@ export class Api {
356352
});
357353
}
358354

355+
private async generateTapos(
356+
info: GetInfoResult | undefined,
357+
transaction: any,
358+
blocksBehind: number | undefined,
359+
useLastIrreversible: boolean | undefined,
360+
expireSeconds: number
361+
) {
362+
if (!info) {
363+
info = await this.rpc.get_info();
364+
}
365+
366+
let taposBlockNumber: number;
367+
if (useLastIrreversible) {
368+
taposBlockNumber = info.last_irreversible_block_num;
369+
} else {
370+
taposBlockNumber = info.head_block_num - blocksBehind;
371+
}
372+
373+
let refBlock: GetBlockHeaderStateResult | GetBlockResult;
374+
try {
375+
refBlock = await this.rpc.get_block_header_state(taposBlockNumber);
376+
} catch (error) {
377+
refBlock = await this.rpc.get_block(taposBlockNumber);
378+
}
379+
380+
return { ...ser.transactionHeader(refBlock, expireSeconds), ...transaction };
381+
}
382+
359383
// eventually break out into TransactionValidator class
360384
private hasRequiredTaposFields({ expiration, ref_block_num, ref_block_prefix }: any): boolean {
361385
return !!(expiration && typeof(ref_block_num) === 'number' && typeof(ref_block_prefix) === 'number');

src/eosjs-jssig.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ function digestFromSerializedData(
2323
serializedContextFreeData?: Uint8Array,
2424
e = defaultEc) {
2525
const signBuf = Buffer.concat([
26-
new Buffer(chainId, 'hex'),
27-
new Buffer(serializedTransaction),
28-
new Buffer(
26+
Buffer.from(chainId, 'hex'),
27+
Buffer.from(serializedTransaction),
28+
Buffer.from(
2929
serializedContextFreeData ?
3030
new Uint8Array(e.hash().update(serializedContextFreeData).digest()) :
3131
new Uint8Array(32)

src/tests/node.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ describe('Node JS environment', () => {
99
transactionResponse = await tests.transactWithConfig({
1010
blocksBehind: 3,
1111
expireSeconds: 30
12-
}, 'transactWithConfig');
12+
}, 'transactWithBlocksBehind');
13+
expect(Object.keys(transactionResponse)).toContain('transaction_id');
14+
});
15+
16+
it('transacts with configuration object containing useLastIrreversible', async () => {
17+
transactionResponse = await tests.transactWithConfig({
18+
useLastIrreversible: 3,
19+
expireSeconds: 30
20+
}, 'transactWithUseLastIrreversible');
1321
expect(Object.keys(transactionResponse)).toContain('transaction_id');
1422
});
1523

src/tests/web.html

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
}]
3939
}, config);
4040

41-
const testTransactWithConfig = async (e) => {
41+
const testTransactWithConfigBlocksBehind = async (e) => {
4242
resultsLabel = e.target;
4343
resultsLabel.innerText = EXECUTING;
4444

@@ -47,7 +47,7 @@
4747
} catch (error) {
4848
resultsLabel.className = 'failed';
4949
resultsLabel.innerText = FAILED;
50-
console.error('Transact With Config Test Failure: ', error.message);
50+
console.error('Transact With Config Blocks Behind Test Failure: ', error.message);
5151
return false;
5252
}
5353

@@ -60,6 +60,29 @@
6060
resultsLabel.innerText = FAILED;
6161
return false;
6262
}
63+
64+
const testTransactWithConfigUseLastIrreversible = async (e) => {
65+
resultsLabel = e.target;
66+
resultsLabel.innerText = EXECUTING;
67+
68+
try {
69+
transactionResponse = await transactWithConfig({ useLastIrreversible: true, expireSeconds: 30 }, 'transactWithUseLastIrreversible');
70+
} catch (error) {
71+
resultsLabel.className = 'failed';
72+
resultsLabel.innerText = FAILED;
73+
console.error('Transact With Config Use Last Irreversible Test Failure: ', error.message);
74+
return false;
75+
}
76+
77+
if (transactionResponse.transaction_id) {
78+
resultsLabel.className = "success";
79+
resultsLabel.innerText = SUCCESS;
80+
return true;
81+
}
82+
resultsLabel.className = 'failed';
83+
resultsLabel.innerText = FAILED;
84+
return false;
85+
}
6386

6487
const transactWithoutConfig = async () => {
6588
const transactionResponse = await transactWithConfig({ blocksBehind: 3, expireSeconds: 30 }, 'transactWithoutConfig');
@@ -263,7 +286,8 @@ <h1>Web Build Integration Tests</h1>
263286
</div>
264287
</div>
265288
<div class='tests'>
266-
<div><h2>Transact with Configuration Parameter</h2><button onClick='testTransactWithConfig(event);'>Test</button></div>
289+
<div><h2>Transact with blocksBehind Configuration Parameter</h2><button onClick='testTransactWithConfigBlocksBehind(event);'>Test</button></div>
290+
<div><h2>Transact with useLastIrreversible Configuration Parameter</h2><button onClick='testTransactWithConfigUseLastIrreversible(event);'>Test</button></div>
267291
<div><h2>Transact with Manually Configured TAPOS</h2><button onClick='testTransactWithoutConfig(event);'>Test</button></div>
268292
<div><h2>Transact with Manually Configured TAPOS</h2><button onClick='testTransactWithCompression(event);'>Test</button></div>
269293
<div><h2>Transact without Broadcasting</h2><button onClick='testTransactWithoutBroadcast(event);'>Test</button></div>

0 commit comments

Comments
 (0)