Skip to content

Commit

Permalink
Merge pull request #293 from tronprotocol/release_v4.4.0
Browse files Browse the repository at this point in the history
Release v4.4.0
  • Loading branch information
unicornonea authored Sep 27, 2022
2 parents 2d3b0d1 + 4aca621 commit 2f4b001
Show file tree
Hide file tree
Showing 15 changed files with 460 additions and 12 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ In order to contribute you can

## Recent History

__4.4.0__
- Support `createRandom` and `fromMnemonic` function
- Add `tronWeb.utils.message` lib, which includes `hashMessage`, `signMessage` and `verifyMessage`
- Add `signMessageV2` and `verifyMessageV2` in `tronWeb.trx` lib which can support plain text signature and verification
- Add `size` filter for event watch

__4.3.0__
- Support `_signTypedData`

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tronweb",
"version": "4.3.0",
"version": "4.4.0",
"description": "JavaScript SDK that encapsulates the TRON HTTP API",
"main": "dist/TronWeb.node.js",
"scripts": {
Expand Down
16 changes: 14 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Plugin from 'lib/plugin';
import Event from 'lib/event';
import SideChain from 'lib/sidechain';
import { keccak256 } from 'utils/ethersUtils';
import { ADDRESS_PREFIX } from 'utils/address';
import { ADDRESS_PREFIX, TRON_BIP39_PATH_INDEX_0 } from 'utils/address';

const DEFAULT_VERSION = '3.5.0';

Expand Down Expand Up @@ -83,7 +83,7 @@ export default class TronWeb extends EventEmitter {
'sha3', 'toHex', 'toUtf8', 'fromUtf8',
'toAscii', 'fromAscii', 'toDecimal', 'fromDecimal',
'toSun', 'fromSun', 'toBigNumber', 'isAddress',
'createAccount', 'address', 'version'
'createAccount', 'address', 'version', 'createRandom', 'fromMnemonic'
].forEach(key => {
this[key] = TronWeb[key];
});
Expand Down Expand Up @@ -431,6 +431,18 @@ export default class TronWeb extends EventEmitter {
return account;
}

static createRandom(options) {
const account = utils.accounts.generateRandom(options);

return account
}

static fromMnemonic(mnemonic, path = TRON_BIP39_PATH_INDEX_0, wordlist = 'en') {
const account = utils.accounts.generateAccountWithMnemonic(mnemonic, path, wordlist);

return account
}

async isConnected(callback = false) {
if (!callback)
return this.injectPromise(this.isConnected);
Expand Down
5 changes: 5 additions & 0 deletions src/lib/contract/method.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@ export default class Method {
blockNumber: 'latest',
filters: options.filters
}

if(options.size) {
params.size = options.size;
}

if (options.resourceNode) {
if (/full/i.test(options.resourceNode))
params.onlyUnconfirmed = true
Expand Down
59 changes: 57 additions & 2 deletions src/lib/trx.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import TronWeb from 'index';
import utils from 'utils';
import {keccak256, toUtf8Bytes, recoverAddress, SigningKey} from 'utils/ethersUtils';
import {ADDRESS_PREFIX} from 'utils/address';
import { keccak256, toUtf8Bytes, recoverAddress, SigningKey } from 'utils/ethersUtils';
import { ADDRESS_PREFIX } from 'utils/address';
import Validator from "../paramValidator";
import injectpromise from 'injectpromise';

Expand Down Expand Up @@ -647,6 +647,27 @@ export default class Trx {
return base58Address == TronWeb.address.fromHex(address);
}

verifyMessageV2(message = false, signature = false, options = {}, callback = false) {
if (utils.isFunction(options)) {
callback = options;
options = {};
}

if (!callback)
return this.injectPromise(this.verifyMessageV2, message, signature, options);

try {
const base58Address = Trx.verifyMessageV2(message, signature);
callback(null, base58Address);
} catch(ex) {
callback(ex);
}
}

static verifyMessageV2(message, signature) {
return utils.message.verifyMessage(message, signature);
}

verifyTypedData(domain, types, value, signature, address = this.tronWeb.defaultAddress.base58, callback = false) {
if (utils.isFunction(address)) {
callback = address;
Expand Down Expand Up @@ -763,6 +784,40 @@ export default class Trx {
return signatureHex
}

/**
* sign message v2 for verified header length
*
* @param {message to be signed, should be Bytes or string} message
* @param {privateKey for signature} privateKey
* @param {reserved} options
* @param {callback function} callback
*/
signMessageV2(message, privateKey = this.tronWeb.defaultPrivateKey, options = {}, callback = false) {
if (utils.isFunction(options)) {
callback = options;
options = {};
}

if (utils.isFunction(privateKey)) {
callback = privateKey;
privateKey = this.tronWeb.defaultPrivateKey;
}

if (!callback)
return this.injectPromise(this.signMessageV2, message, privateKey);

try {
const signatureHex = Trx.signMessageV2(message, privateKey);
return callback(null, signatureHex);
} catch (ex) {
callback(ex);
}
}

static signMessageV2(message, privateKey) {
return utils.message.signMessage(message, privateKey);
}

_signTypedData(domain, types, value, privateKey = this.tronWeb.defaultPrivateKey, callback = false) {
if (utils.isFunction(privateKey)) {
callback = privateKey;
Expand Down
48 changes: 47 additions & 1 deletion src/utils/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import {
getBase58CheckAddress,
genPriKey,
getAddressFromPriKey,
getPubKeyFromPriKey
getPubKeyFromPriKey,
pkToAddress,
} from './crypto';
import {ethersWallet} from './ethersUtils'
import {TRON_BIP39_PATH_INDEX_0} from './address'
import utils from './index';

const INVALID_TRON_PATH_ERROR_MSG = 'Invalid tron path provided';

export function generateAccount() {
const priKeyBytes = genPriKey();
Expand All @@ -23,3 +29,43 @@ export function generateAccount() {
}
}
}

export function generateRandom(options) {
if(!utils.isObject(options)) { options = {}; }
if(!options.path) {
options.path = TRON_BIP39_PATH_INDEX_0;
}
if(!String(options.path).match(/^m\/44\'\/195\'/)) {
throw new Error(INVALID_TRON_PATH_ERROR_MSG);
}

const account = ethersWallet.createRandom(options);

const result = {
mnemonic: account.mnemonic,
privateKey: account.privateKey,
publicKey: account.publicKey,
address: pkToAddress(account.privateKey.replace(/^0x/, ''))
}

return result;
}

export function generateAccountWithMnemonic(mnemonic, path, wordlist = 'en') {
if(!path) {
path = TRON_BIP39_PATH_INDEX_0;
}
if(!String(path).match(/^m\/44\'\/195\'/)) {
throw new Error(INVALID_TRON_PATH_ERROR_MSG);
}
const account = ethersWallet.fromMnemonic(mnemonic, path, wordlist);

const result = {
mnemonic: account.mnemonic,
privateKey: account.privateKey,
publicKey: account.publicKey,
address: pkToAddress(account.privateKey.replace(/^0x/, ''))
}

return result;
}
3 changes: 3 additions & 0 deletions src/utils/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ export const ADDRESS_SIZE = 34;
export const ADDRESS_PREFIX = "41";
export const ADDRESS_PREFIX_BYTE = 0x41;
export const ADDRESS_PREFIX_REGEX = /^(41)/;

export const TRON_BIP39_PATH_PREFIX = "m/44'/195'";
export const TRON_BIP39_PATH_INDEX_0 = TRON_BIP39_PATH_PREFIX + "/0'/0/0";
11 changes: 9 additions & 2 deletions src/utils/ethersUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { utils } from "ethers";
import { utils, Wallet as ethersWallet } from "ethers";

const keccak256 = utils.keccak256;
const sha256 = utils.sha256;
Expand All @@ -12,6 +12,9 @@ const FormatTypes = utils.FormatTypes;
const arrayify = utils.arrayify;
const splitSignature = utils.splitSignature;
const joinSignature = utils.joinSignature;
const concat = utils.concat;
const id = utils.id;
const isValidMnemonic = utils.isValidMnemonic;

export {
keccak256,
Expand All @@ -25,5 +28,9 @@ export {
FormatTypes,
splitSignature,
joinSignature,
arrayify
arrayify,
ethersWallet,
concat,
id,
isValidMnemonic
};
2 changes: 2 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as bytes from './bytes';
import * as crypto from './crypto';
import * as code from './code';
import * as abi from './abi';
import * as message from './message'
import * as ethersUtils from './ethersUtils';
import {TypedDataEncoder as _TypedDataEncoder} from './typedData'

Expand Down Expand Up @@ -151,6 +152,7 @@ export default {
bytes,
crypto,
abi,
message,
_TypedDataEncoder,
ethersUtils
};
40 changes: 40 additions & 0 deletions src/utils/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { keccak256, toUtf8Bytes, concat, recoverAddress, SigningKey, joinSignature } from 'utils/ethersUtils';
import { ADDRESS_PREFIX } from 'utils/address';
import { getBase58CheckAddress } from 'utils/crypto';
import { hexStr2byteArray } from 'utils/code';

export const TRON_MESSAGE_PREFIX = "\x19TRON Signed Message:\n";

export function hashMessage(message) {
if (typeof(message) === "string") {
message = toUtf8Bytes(message);
}

return keccak256(concat([
toUtf8Bytes(TRON_MESSAGE_PREFIX),
toUtf8Bytes(String(message.length)),
message
]));
}

export function signMessage(message, privateKey) {
if(!privateKey.match(/^0x/)) {
privateKey = '0x' + privateKey;
}

const signingKey = new SigningKey(privateKey);
const messageDigest = hashMessage(message);
const signature = signingKey.signDigest(messageDigest);

return joinSignature(signature)
}

export function verifyMessage(message, signature) {
if(!signature.match(/^0x/)) {
signature = '0x' + signature;
}
const recovered = recoverAddress(hashMessage(message), signature);
const base58Address = getBase58CheckAddress(hexStr2byteArray(recovered.replace(/^0x/, ADDRESS_PREFIX)));

return base58Address;
}
35 changes: 35 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,41 @@ describe('TronWeb Instance', function () {
});
});


describe("#createRandom", function () {
it("should create a random mnemonic and the zero index account", async function () {
const tronWeb = tronWebBuilder.createInstance();

const newAccount = await tronWeb.createRandom();
assert.equal(newAccount.privateKey.substring(2).length, 64);
assert.equal(newAccount.publicKey.substring(2).length, 130);
let address = tronWeb.address.fromPrivateKey(newAccount.privateKey.substring(2));
assert.equal(address, newAccount.address);
address = tronWeb.address.fromPrivateKey(newAccount.privateKey.substring(2), true);
assert.equal(address, newAccount.address);
assert.equal(tronWeb.address.toHex(address), tronWeb.address.toHex(newAccount.address));
});
});

describe("#fromMnemonic", function () {
it("should generate the zero index account of the passed mnemonic phrase", async function () {
const tronWeb = tronWebBuilder.createInstance();

const accountCreated = await tronWeb.createRandom()

const newAccount = await tronWeb.fromMnemonic(accountCreated.mnemonic.phrase);

assert.equal(newAccount.privateKey.substring(2).length, 64);
assert.equal(newAccount.publicKey.substring(2).length, 130);
let address = tronWeb.address.fromPrivateKey(newAccount.privateKey.substring(2));
assert.equal(address, newAccount.address);
address = tronWeb.address.fromPrivateKey(newAccount.privateKey.substring(2), true);
assert.equal(address, newAccount.address);
assert.equal(tronWeb.address.toHex(address), tronWeb.address.toHex(newAccount.address));
});
});


describe("#isConnected", function () {
it("should verify that tronWeb is connected to nodes and event server", async function () {

Expand Down
25 changes: 21 additions & 4 deletions test/lib/trx.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ const _ = require('lodash');
const tronWebBuilder = require('../helpers/tronWebBuilder');
const assertEqualHex = require('../helpers/assertEqualHex');
const TronWeb = tronWebBuilder.TronWeb;
const config = require('../helpers/config');
const waitChainData = require('../helpers/waitChainData');
const {
ADDRESS_BASE58,
PRIVATE_KEY,
getTokenOptions,
SIGNED_HEX_TRANSACTION
SIGNED_HEX_TRANSACTION,
FULL_NODE_API
} = require('../helpers/config');
const testRevertContract = require('../fixtures/contracts').testRevert;

Expand Down Expand Up @@ -1936,7 +1936,24 @@ describe('TronWeb.trx', function () {

});
});

describe("#signMessageV2", async function() {
tests.forEach(function(test) {
it('signs a message "' + test.name + '"', async function () {
const tronWeb = new TronWeb({ fullHost: FULL_NODE_API }, test.privateKey)
const signature = await tronWeb.trx.signMessageV2(test.message);
assert.equal(signature, test.signature, 'computes message signature');
});
});
});



describe("#verifyMessageV2", async function() {
tests.forEach(function(test) {
it('signs a message "' + test.name + '"', async function () {
const tronWeb = new TronWeb({ fullHost: FULL_NODE_API }, test.privateKey)
const address = await tronWeb.trx.verifyMessageV2(test.message, test.signature);
assert.equal(address, test.address, 'verifies message signature');
});
});
});
});
Loading

0 comments on commit 2f4b001

Please sign in to comment.