diff --git a/.eslintrc b/.eslintrc index b2dc13c9..41ce257f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,7 @@ "extends": ["eslint-config-airbnb-base"], "globals": {}, "rules": { + "indent": ["error", 2], "function-paren-newline": "off", "no-console": ["warn"], "no-useless-escape": 0, diff --git a/.prettierrc b/.prettierrc index 9f268861..f761fb6b 100644 --- a/.prettierrc +++ b/.prettierrc @@ -5,5 +5,6 @@ "useTabs": false, "printWidth": 120, "endOfLine": "lf", - "arrowParens": "avoid" + "arrowParens": "avoid", + "tabWidth": 2 } diff --git a/README.md b/README.md index bfc353dd..10f145a7 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,8 @@ You can skip 2.2 as 2.1 is enough now. In our dist directory, we supply two kinds of packages for different platforms, such as Node and Browser. | packages | usage | -| ---------------- | ------------------------------------------------------------ | +|------------------| ------------------------------------------------------------ | +| dist/aelf.esm.js | built as an ES Module, optimized for modern bundlers and tree-shaking. Designed for use in modern JavaScript environments like webpack, Rollup, and Vite. | | dist/aelf.cjs.js | built for node, remove node built-in modules such as crypto. | | dist/aelf.umd.js | built for browser, add some node built-in modules by webpack | @@ -70,6 +71,19 @@ if you want to use a bundle system such as webpack or rollup, and build your app #### For browser usage and use UMD +ESM +```javascript +// ✅ Recommended: Use "exports" to enable Tree Shaking +import wallet from 'aelf-sdk/wallet'; + +// ✅ Also supported: Directly import from the "src" directory +import wallet from 'aelf-sdk/src/wallet/index.js'; + +// ✅ Backward compatibility: Traditional import method +import AElf from 'aelf-sdk'; +const { wallet } = AElf; +``` + Webpack: ```javascript diff --git a/package.json b/package.json index 706984fc..0c2c2974 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aelf-sdk", - "version": "3.4.21", + "version": "3.5.0", "description": "aelf-sdk js library", "type": "module", "main": "dist/aelf.cjs", @@ -12,6 +12,34 @@ ".": { "import": "./dist/aelf.esm.js", "require": "./dist/aelf.cjs" + }, + "./src/*": { + "import": "./src/*", + "require": "./src/*" + }, + "./wallet": { + "import": "./src/wallet/index.js", + "require": "./src/wallet/index.js" + }, + "./utils": { + "import": "./src/util/utils.js", + "require": "./src/util/utils.js" + }, + "./chain": { + "import": "./src/chain/index.js", + "require": "./src/chain/index.js" + }, + "./sha256": { + "import": "./src/util/sha256.js", + "require": "./src/util/sha256.js" + }, + "./keyStore": { + "import": "./src/util/keyStore.js", + "require": "./src/util/keyStore.js" + }, + "./AElf": { + "import": "./src/AElf.js", + "require": "./src/AElf.js" } }, "scripts": { @@ -50,9 +78,6 @@ ], "author": "hzz780", "license": "MIT", - "devEngines": { - "node": "18.x || 20.x" - }, "engines": { "node": ">=18.18.0" }, @@ -64,7 +89,6 @@ "@babel/plugin-proposal-optional-chaining": "^7.21.0", "@babel/runtime": "^7.4.5", "@typescript-eslint/parser": "^5.47.1", - "assert": "^2.0.0", "babel-plugin-rewire": "^1.2.0", "bignumber.js": "^9.0.0", "bip39": "^3.0.2", @@ -138,9 +162,7 @@ "rimraf": "^5.0.0", "size-limit": "^8.1.2", "standard-version": "^9.5.0", - "unused-files-webpack-plugin": "^3.4.0", "webpack": "^5.80.0", - "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^5.0.2", "webpack-deadcode-plugin": "^0.1.17", "webpack-merge": "^5.8.0" diff --git a/src/AElf.js b/src/AElf.js new file mode 100644 index 00000000..07867be6 --- /dev/null +++ b/src/AElf.js @@ -0,0 +1,76 @@ +/** + * @file AElf-sdk index export + * @author atom-yang + */ +import * as protobuf from '@aelfqueen/protobufjs/light.js'; +import * as bloom from './util/bloom.js'; +import Chain from './chain/index.js'; +import RequestManager from './util/requestManage.js'; +import HttpProvider from './util/httpProvider.js'; +import wallet from './wallet/index.js'; +import * as utils from './util/utils.js'; +import * as proto from './util/proto.js'; +import * as transform from './util/transform.js'; +import * as transaction from './util/transaction.js'; +import Settings from './util/settings.js'; +import sha256 from './util/sha256.js'; + +/* eslint-disable no-underscore-dangle */ +export default class AElf { + constructor(provider) { + this._requestManager = new RequestManager(provider); + this.currentProvider = provider; + this.chain = new Chain(this._requestManager); + } + + static version = process.env.SDK_VERSION; + + static providers = { + HttpProvider + }; + + /** + * @type {protobuf} export protobufjs for developers + */ + static pbjs = protobuf; + + static pbUtils = proto; + + static wallet = wallet; + + static utils = { + ...utils, + ...bloom, + ...transaction, + sha256, + transform + }; + + providers = { + HttpProvider + }; + + settings = new Settings(); + + /** + * AElf-sdk version + * @type {{api: string}} + */ + version = { + api: process.env.SDK_VERSION + }; + + /** + * check the rpc node is work or not. + * @returns {boolean} whether can connect to the rpc. + */ + isConnected() { + return this.currentProvider && this.currentProvider.isConnected(); + } + + setProvider(provider) { + this._requestManager.setProvider(provider); + this.currentProvider = provider; + } +} +/* eslint-enable */ diff --git a/src/common/constants.js b/src/common/constants.js index 0f75979d..3393e1bd 100644 --- a/src/common/constants.js +++ b/src/common/constants.js @@ -183,22 +183,3 @@ export const CONGIG = { contractZero: 'AELF', defaultAccount: '0x04bb9c6c297ea90b1bc3e6af2c87d416583e' }; - -export const KEY_STORE_ERRORS = { - INVALID_PASSWORD: { - error: 200001, - errorMessage: 'Password Error' - }, - NOT_AELF_KEY_STORE: { - error: 200002, - errorMessage: 'Not a aelf key store' - }, - WRONG_VERSION: { - error: 200004, - errorMessage: 'The version is incorrect' - }, - WRONG_KEY_STORE_VERSION: { - error: 200005, - errorMessage: 'Not a V1 key store' - } -}; diff --git a/src/common/keyStoreConstants.js b/src/common/keyStoreConstants.js new file mode 100644 index 00000000..7ccb7474 --- /dev/null +++ b/src/common/keyStoreConstants.js @@ -0,0 +1,22 @@ +/** + * @file AElf-sdk constantsKeyStore + * @author hzz780 + */ +export const KEY_STORE_ERRORS = { + INVALID_PASSWORD: { + error: 200001, + errorMessage: 'Password Error' + }, + NOT_AELF_KEY_STORE: { + error: 200002, + errorMessage: 'Not a aelf key store' + }, + WRONG_VERSION: { + error: 200004, + errorMessage: 'The version is incorrect' + }, + WRONG_KEY_STORE_VERSION: { + error: 200005, + errorMessage: 'Not a V1 key store' + } +}; diff --git a/src/common/unitConstants.js b/src/common/unitConstants.js new file mode 100644 index 00000000..a7994172 --- /dev/null +++ b/src/common/unitConstants.js @@ -0,0 +1,42 @@ +/** + * @file AElf-sdk constants + * @author atom-yang + */ + +/** + * unsigned 256 int + */ +export const UNSIGNED_256_INT = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + +/** + * unit map + */ +export const UNIT_MAP = { + noether: '0', + wei: '1', + kwei: '1000', + Kwei: '1000', + babbage: '1000', + femtoether: '1000', + mwei: '1000000', + Mwei: '1000000', + lovelace: '1000000', + picoether: '1000000', + gwei: '1000000000', + Gwei: '1000000000', + shannon: '1000000000', + nanoether: '1000000000', + nano: '1000000000', + szabo: '1000000000000', + microether: '1000000000000', + micro: '1000000000000', + finney: '1000000000000000', + milliether: '1000000000000000', + milli: '1000000000000000', + ether: '1000000000000000000', + kether: '1000000000000000000000', + grand: '1000000000000000000000', + mether: '1000000000000000000000000', + gether: '1000000000000000000000000000', + tether: '1000000000000000000000000000000' +}; diff --git a/src/contract/contractMethod.js b/src/contract/contractMethod.js index a4b7fe30..d9b498f8 100644 --- a/src/contract/contractMethod.js +++ b/src/contract/contractMethod.js @@ -11,7 +11,7 @@ import { OUTPUT_TRANSFORMERS } from '../util/transform.js'; import { isBoolean, isFunction, isNumber, noop, uint8ArrayToHex, unpackSpecifiedTypeData } from '../util/utils.js'; -import wallet from '../wallet/index.js'; +import { signTransaction } from '../util/transaction.js'; export default class ContractMethod { constructor(chain, method, contractAddress, walletInstance, option) { @@ -88,7 +88,7 @@ export default class ContractMethod { handleTransaction(height, hash, encoded) { const rawTx = this.getRawTx(height, hash, encoded); - let tx = wallet.signTransaction(rawTx, this._wallet.keyPair); + let tx = signTransaction(rawTx, this._wallet.keyPair); tx = Transaction.encode(tx).finish(); // jest environment just go into Buffer branch diff --git a/src/index.js b/src/index.js index 152b0f92..a8732cbe 100644 --- a/src/index.js +++ b/src/index.js @@ -2,73 +2,7 @@ * @file AElf-sdk index export * @author atom-yang */ -import * as protobuf from '@aelfqueen/protobufjs/light.js'; -import * as bloom from './util/bloom.js'; -import Chain from './chain/index.js'; -import RequestManager from './util/requestManage.js'; -import HttpProvider from './util/httpProvider.js'; -import wallet from './wallet/index.js'; -import * as utils from './util/utils.js'; -import * as proto from './util/proto.js'; -import * as transform from './util/transform.js'; -import Settings from './util/settings.js'; -import sha256 from './util/sha256.js'; -/* eslint-disable no-underscore-dangle */ -export default class AElf { - constructor(provider) { - this._requestManager = new RequestManager(provider); - this.currentProvider = provider; - this.chain = new Chain(this._requestManager); - } +import AElf from './AElf.js'; - static version = process.env.SDK_VERSION; - - static providers = { - HttpProvider - }; - - /** - * @type {protobuf} export protobufjs for developers - */ - static pbjs = protobuf; - - static pbUtils = proto; - - static wallet = wallet; - - static utils = { - ...utils, - ...bloom, - sha256, - transform - }; - - providers = { - HttpProvider - }; - - settings = new Settings(); - - /** - * AElf-sdk version - * @type {{api: string}} - */ - version = { - api: process.env.SDK_VERSION - }; - - /** - * check the rpc node is work or not. - * @returns {boolean} whether can connect to the rpc. - */ - isConnected() { - return this.currentProvider && this.currentProvider.isConnected(); - } - - setProvider(provider) { - this._requestManager.setProvider(provider); - this.currentProvider = provider; - } -} -/* eslint-enable */ +export default AElf; diff --git a/src/util/keyStore.js b/src/util/keyStore.js index b245b147..e4d9bfa5 100644 --- a/src/util/keyStore.js +++ b/src/util/keyStore.js @@ -6,7 +6,7 @@ import scrypt from 'scryptsy'; import { createCipheriv, createDecipheriv } from 'browserify-cipher'; import randomBytes from 'randombytes'; import { keccak256 } from './keccak.js'; -import { KEY_STORE_ERRORS } from '../common/constants.js'; +import { KEY_STORE_ERRORS } from '../common/keyStoreConstants.js'; const AES_MODES = { 'aes-128-ecb': { diff --git a/src/util/transaction.js b/src/util/transaction.js new file mode 100644 index 00000000..84e971e5 --- /dev/null +++ b/src/util/transaction.js @@ -0,0 +1,83 @@ +import { Transaction } from './proto.js'; +import wallet from '../wallet/index.js'; +import { encodeAddress, OUTPUT_TRANSFORMERS, transform, transformArrayToMap } from './transform.js'; +import { unpackSpecifiedTypeData } from './utils.js'; +import sha256 from './sha256.js'; + +const { getSignature } = wallet; + +/** + * sign a transaction + * + * @alias module:AElf/wallet + * @param {Object} rawTxn rawTxn + * @param {Object} keyPair Any standard key pair + * @return {Object} wallet + * + * @Example + * const rawTxn = proto.getTransaction( + * 'ELF_65dDNxzcd35jESiidFXN5JV8Z7pCwaFnepuYQToNefSgqk9', + * 'ELF_65dDNxzcd35jESiidFXN5JV8Z7pCwaFnepuYQToNefSgqk9', + * 'test', + * [] + * ); + * const signWallet = aelf.wallet.signTransaction(rawTxn, wallet.keyPair); + */ +export const signTransaction = (rawTxn, keyPair) => { + let { params } = rawTxn; + if (params.length === 0) { + params = null; + } + // proto in proto.Transaction use proto2, but C# use proto3 + // proto3 will remove the default value key. + // The differences between proto2 and proto3: + // https://blog.csdn.net/huanggang982/article/details/77944174 + const ser = Transaction.encode(rawTxn).finish(); + const sig = getSignature(ser, keyPair); + return { + ...rawTxn, + params, + signature: sig + }; +}; + +export function deserializeTransaction(rawTx, paramsDataType) { + const { from, to, params, refBlockPrefix, signature, ...rest } = unpackSpecifiedTypeData({ + data: rawTx, + dataType: Transaction + }); + let methodParameters = unpackSpecifiedTypeData({ + data: params, + encoding: 'base64', + dataType: paramsDataType + }); + methodParameters = transform(paramsDataType, methodParameters, OUTPUT_TRANSFORMERS); + methodParameters = transformArrayToMap(paramsDataType, methodParameters); + + return { + from: encodeAddress(from.value), + to: encodeAddress(to.value), + params: methodParameters, + refBlockPrefix: Buffer.from(refBlockPrefix, 'base64').toString('hex'), + signature: Buffer.from(signature, 'base64').toString('hex'), + ...rest + }; +} + +/** + * + * Use rawTransaction to get transaction id + * @param {String} rawTx rawTransaction + * @return {String} string + * + * const txId = getTransactionId('0a220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd512220a2089ac786c8ad3b56f63a6f2767369a5273f801de2415b613c783cad3d148ce3ab18d5d3bb35220491cf6ba12a18537761704578616374546f6b656e73466f72546f6b656e73325008c0f7f27110bbe5947c1a09534752544553542d311a03454c4622220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd52a08088996ceb0061000320631323334353682f10441ec6ad50c4b210976ba0ba5c287ab6fabd0c444839e2505ecb1b5f52838095b290cb245ec1c97dade3bde6ac14c6892e526569e9b71240d3c120b1a6c8e41afba00'); + * console.log(txId); + * // => cf564f3169012cb173efcf5543b2a71b030b16fad3ddefe3e04a5c1e1bc0047d + */ +export function getTransactionId(rawTx) { + const hash = Buffer.from(rawTx.replace('0x', ''), 'hex'); + const decode = Transaction.decode(hash); + decode.signature = null; + const encode = Transaction.encode(decode).finish(); + return sha256(encode); +} diff --git a/src/util/utils.js b/src/util/utils.js index 11b29fcd..8c105de6 100644 --- a/src/util/utils.js +++ b/src/util/utils.js @@ -5,9 +5,7 @@ import BigNumber from 'bignumber.js'; import bs58 from 'bs58'; -import { UNIT_MAP, UNSIGNED_256_INT } from '../common/constants.js'; -import { Transaction } from './proto.js'; -import { OUTPUT_TRANSFORMERS, encodeAddress, transform, transformArrayToMap } from './transform.js'; +import { UNIT_MAP, UNSIGNED_256_INT } from '../common/unitConstants.js'; import sha256 from './sha256.js'; export const base58 = { @@ -376,27 +374,12 @@ export const unpackSpecifiedTypeData = ({ data, dataType, encoding = 'hex' }) => return result; }; +// eslint-disable-next-line no-unused-vars export function deserializeTransaction(rawTx, paramsDataType) { - const { from, to, params, refBlockPrefix, signature, ...rest } = unpackSpecifiedTypeData({ - data: rawTx, - dataType: Transaction - }); - let methodParameters = unpackSpecifiedTypeData({ - data: params, - encoding: 'base64', - dataType: paramsDataType - }); - methodParameters = transform(paramsDataType, methodParameters, OUTPUT_TRANSFORMERS); - methodParameters = transformArrayToMap(paramsDataType, methodParameters); - - return { - from: encodeAddress(from.value), - to: encodeAddress(to.value), - params: methodParameters, - refBlockPrefix: Buffer.from(refBlockPrefix, 'base64').toString('hex'), - signature: Buffer.from(signature, 'base64').toString('hex'), - ...rest - }; + console.error( + `deprecated method (>=3.5.0), + please use use utils/transaction.js deserializeTransaction` + ); } /** * @@ -422,12 +405,12 @@ export function getAuthorization(userName, password) { * console.log(txId); * // => cf564f3169012cb173efcf5543b2a71b030b16fad3ddefe3e04a5c1e1bc0047d */ +// eslint-disable-next-line no-unused-vars export function getTransactionId(rawTx) { - const hash = Buffer.from(rawTx.replace('0x', ''), 'hex'); - const decode = Transaction.decode(hash); - decode.signature = null; - const encode = Transaction.encode(decode).finish(); - return sha256(encode); + console.error( + `deprecated method (>=3.5.0), + please use utils/transaction.js getTransactionId` + ); } export function validateMulti(obj) { diff --git a/src/wallet/index.js b/src/wallet/index.js index 344b87ec..369d680a 100644 --- a/src/wallet/index.js +++ b/src/wallet/index.js @@ -9,9 +9,7 @@ import AES from 'crypto-js/aes.js'; import encUTF8 from 'crypto-js/enc-utf8.js'; import BN from 'bn.js'; import sha256 from '../util/sha256.js'; -import * as keyStore from '../util/keyStore.js'; import { encodeAddressRep, padLeft } from '../util/utils.js'; -import { Transaction } from '../util/proto.js'; // eslint-disable-next-line new-cap const ellipticEc = new elliptic.ec('secp256k1'); @@ -77,29 +75,29 @@ const _getWallet = (type, value, BIP44Path = "m/44'/1616'/0'/0/0", seedWithBuffe let keyPair = ''; let hdWallet; switch (type) { - case 'createNewWallet': - mnemonic = bip39.generateMnemonic(); - rootSeed = bip39.mnemonicToSeedSync(mnemonic).toString('hex'); - hdWallet = hdkey.fromMasterSeed(seedWithBuffer ? Buffer.from(rootSeed, 'hex') : rootSeed); - childWallet = hdWallet.derive(BIP44Path); - keyPair = ellipticEc.keyFromPrivate(childWallet.privateKey); - break; - case 'getWalletByMnemonic': - mnemonic = value; - rootSeed = bip39.mnemonicToSeedSync(mnemonic).toString('hex'); - hdWallet = hdkey.fromMasterSeed(seedWithBuffer ? Buffer.from(rootSeed, 'hex') : rootSeed); - childWallet = hdWallet.derive(BIP44Path); - keyPair = ellipticEc.keyFromPrivate(childWallet.privateKey); - break; - case 'getWalletByPrivateKey': - if (typeof value === 'string') { - keyPair = ellipticEc.keyFromPrivate(padLeft(value, 64, '0')); - } else { - keyPair = ellipticEc.keyFromPrivate(value); - } - break; - default: - throw new Error('not a valid method'); + case 'createNewWallet': + mnemonic = bip39.generateMnemonic(); + rootSeed = bip39.mnemonicToSeedSync(mnemonic).toString('hex'); + hdWallet = hdkey.fromMasterSeed(seedWithBuffer ? Buffer.from(rootSeed, 'hex') : rootSeed); + childWallet = hdWallet.derive(BIP44Path); + keyPair = ellipticEc.keyFromPrivate(childWallet.privateKey); + break; + case 'getWalletByMnemonic': + mnemonic = value; + rootSeed = bip39.mnemonicToSeedSync(mnemonic).toString('hex'); + hdWallet = hdkey.fromMasterSeed(seedWithBuffer ? Buffer.from(rootSeed, 'hex') : rootSeed); + childWallet = hdWallet.derive(BIP44Path); + keyPair = ellipticEc.keyFromPrivate(childWallet.privateKey); + break; + case 'getWalletByPrivateKey': + if (typeof value === 'string') { + keyPair = ellipticEc.keyFromPrivate(padLeft(value, 64, '0')); + } else { + keyPair = ellipticEc.keyFromPrivate(value); + } + break; + default: + throw new Error('not a valid method'); } // let mnemonic = bip39.generateMnemonic(); // let rootSeed = bip39.mnemonicToSeedHex(mnemonic); @@ -209,22 +207,28 @@ const getWalletByPrivateKey = privateKey => _getWallet('getWalletByPrivateKey', * ); * const signWallet = aelf.wallet.signTransaction(rawTxn, wallet.keyPair); */ +// eslint-disable-next-line no-unused-vars const signTransaction = (rawTxn, keyPair) => { - let { params } = rawTxn; - if (params.length === 0) { - params = null; - } - // proto in proto.Transaction use proto2, but C# use proto3 - // proto3 will remove the default value key. - // The differences between proto2 and proto3: - // https://blog.csdn.net/huanggang982/article/details/77944174 - const ser = Transaction.encode(rawTxn).finish(); - const sig = getSignature(ser, keyPair); - return { - ...rawTxn, - params, - signature: sig - }; + // let { params } = rawTxn; + // if (params.length === 0) { + // params = null; + // } + // // proto in proto.Transaction use proto2, but C# use proto3 + // // proto3 will remove the default value key. + // // The differences between proto2 and proto3: + // // https://blog.csdn.net/huanggang982/article/details/77944174 + // const ser = Transaction.encode(rawTxn).finish(); + // const sig = getSignature(ser, keyPair); + // return { + // ...rawTxn, + // params, + // signature: sig + // }; + console.error( + `deprecated method (>=3.5.0), + please use aelf.wallet.sign instead, + or use utils/transaction.js signTransaction` + ); }; /** @@ -281,6 +285,7 @@ export default { sign, verify, signTransaction, + getSignature, createNewWallet, getWalletByMnemonic, getWalletByPrivateKey, @@ -288,5 +293,21 @@ export default { ellipticEc, AESEncrypt, AESDecrypt, - keyStore + keyStore: { + getKeystore: () => { + console.error( + 'deprecated method (>=3.5.0), please use utils/keyStore.js getKeystore' + ); + }, + unlockKeystore: () => { + console.error( + 'deprecated method (>=3.5.0), please use utils/keyStore.js unlockKeystore' + ); + }, + checkPassword: () => { + console.error( + 'deprecated method (>=3.5.0), please use utils/keyStore.js checkPassword' + ); + } + } }; diff --git a/test/unit/util/transaction.test.js b/test/unit/util/transaction.test.js index 491bd247..77a766a0 100644 --- a/test/unit/util/transaction.test.js +++ b/test/unit/util/transaction.test.js @@ -1,11 +1,11 @@ import { unpackSpecifiedTypeData, - deserializeTransaction, getAuthorization, } from '../../../src/util/utils'; import AElf from '../../../src/index'; import tokenProto from './token.proto.json'; import { Transaction } from '../../../src/util/proto'; +import { getTransactionId, deserializeTransaction } from '../../../src/util/transaction.js'; describe('test deserializing transaction', () => { test('deserialize transaction', async () => { @@ -68,4 +68,10 @@ describe('test deserializing transaction', () => { const authorization = getAuthorization('test', 'pass'); expect(authorization).toEqual('Basic dGVzdDpwYXNz'); }); + test('test getTransactionId', () => { + const txId = getTransactionId( + '0a220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd512220a2089ac786c8ad3b56f63a6f2767369a5273f801de2415b613c783cad3d148ce3ab18d5d3bb35220491cf6ba12a18537761704578616374546f6b656e73466f72546f6b656e73325008c0f7f27110bbe5947c1a09534752544553542d311a03454c4622220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd52a08088996ceb0061000320631323334353682f10441ec6ad50c4b210976ba0ba5c287ab6fabd0c444839e2505ecb1b5f52838095b290cb245ec1c97dade3bde6ac14c6892e526569e9b71240d3c120b1a6c8e41afba00' + ); + expect(txId).toEqual('cf564f3169012cb173efcf5543b2a71b030b16fad3ddefe3e04a5c1e1bc0047d'); + }); }); diff --git a/test/unit/util/utils.test.js b/test/unit/util/utils.test.js index c7f3bfb3..a0789e1f 100644 --- a/test/unit/util/utils.test.js +++ b/test/unit/util/utils.test.js @@ -21,7 +21,13 @@ import { toTwosComplement, uint8ArrayToHex, setPath, - getTransactionId + deserializeTransaction, + getTransactionId, + getAuthorization, + validateMulti, + byteStringToHex, + noop, + unpackSpecifiedTypeData, } from '../../../src/util/utils'; describe('test utils', () => { @@ -190,10 +196,115 @@ describe('test utils', () => { new BigNumber('0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1') ); }); - test('test getTransactionId', () => { - const txId = getTransactionId( - '0a220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd512220a2089ac786c8ad3b56f63a6f2767369a5273f801de2415b613c783cad3d148ce3ab18d5d3bb35220491cf6ba12a18537761704578616374546f6b656e73466f72546f6b656e73325008c0f7f27110bbe5947c1a09534752544553542d311a03454c4622220a2071a4dc8cdf109bd72913c90c3fc666c78d080cdda0da7f3abbc7105c6b651fd52a08088996ceb0061000320631323334353682f10441ec6ad50c4b210976ba0ba5c287ab6fabd0c444839e2505ecb1b5f52838095b290cb245ec1c97dade3bde6ac14c6892e526569e9b71240d3c120b1a6c8e41afba00' + + test('test deprecated deserializeTransaction function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + deserializeTransaction('rawTx', 'paramsDataType'); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0),\n please use use utils/transaction.js deserializeTransaction' + ); + + consoleSpy.mockRestore(); + }); + + test('test deprecated getTransactionId function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + getTransactionId('rawTx'); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0),\n please use utils/transaction.js getTransactionId' + ); + + consoleSpy.mockRestore(); + }); + + test('test getAuthorization function', () => { + const result = getAuthorization('test', 'pass'); + expect(result).toBe('Basic dGVzdDpwYXNz'); + + const result2 = getAuthorization('user', 'password'); + expect(result2).toBe('Basic dXNlcjpwYXNzd29yZA=='); + }); + + test('test validateMulti function', () => { + const validObj = { + chain1: { chainUrl: 'http://test1.com', contractAddress: '0x123' }, + chain2: { chainUrl: 'http://test2.com', contractAddress: '0x456' } + }; + expect(validateMulti(validObj)).toBe(true); + + const invalidObj1 = { + chain1: { chainUrl: 'http://test1.com', contractAddress: '0x123' } + }; + expect(validateMulti(invalidObj1)).toBe(false); + + const invalidObj2 = { + chain1: { chainUrl: 'http://test1.com', contractAddress: '0x123' }, + chain2: { chainUrl: 'http://test2.com', contractAddress: '0x456' }, + chain3: { chainUrl: 'http://test3.com', contractAddress: '0x789' } + }; + expect(validateMulti(invalidObj2)).toBe(false); + + const invalidObj3 = { + chain1: { chainUrl: 'http://test1.com' }, + chain2: { chainUrl: 'http://test2.com', contractAddress: '0x456' } + }; + expect(validateMulti(invalidObj3)).toBe(false); + }); + + test('test byteStringToHex function', () => { + const result = byteStringToHex('hello'); + expect(result).toBe('68656c6c6f'); + + const result2 = byteStringToHex('test'); + expect(result2).toBe('74657374'); + + const result3 = byteStringToHex(''); + expect(result3).toBe(''); + }); + + test('test noop function', () => { + expect(noop()).toBeUndefined(); + expect(typeof noop).toBe('function'); + }); + + test('test unpackSpecifiedTypeData function', () => { + // Mock dataType with decode and toObject methods + const mockDataType = { + decode: jest.fn().mockReturnValue({ mockDecoded: true }), + toObject: jest.fn().mockReturnValue({ mockObject: true }) + }; + + const result = unpackSpecifiedTypeData({ + data: 'testdata', + dataType: mockDataType, + encoding: 'utf8' + }); + + expect(mockDataType.decode).toHaveBeenCalledWith(Buffer.from('testdata', 'utf8')); + expect(mockDataType.toObject).toHaveBeenCalledWith( + { mockDecoded: true }, + { + enums: String, + longs: String, + bytes: String, + defaults: true, + arrays: true, + objects: true, + oneofs: true + } ); - expect(txId).toEqual('cf564f3169012cb173efcf5543b2a71b030b16fad3ddefe3e04a5c1e1bc0047d'); + expect(result).toEqual({ mockObject: true }); + + // Test with default encoding + const result2 = unpackSpecifiedTypeData({ + data: 'testdata', + dataType: mockDataType + }); + + expect(mockDataType.decode).toHaveBeenCalledWith(Buffer.from('testdata', 'hex')); }); }); diff --git a/test/unit/wallet/index.test.js b/test/unit/wallet/index.test.js index a68c83b8..f954a69b 100644 --- a/test/unit/wallet/index.test.js +++ b/test/unit/wallet/index.test.js @@ -1,5 +1,6 @@ import { getTransaction } from '../../../src/util/proto'; import Wallet from '../../../src/wallet/index'; +import { signTransaction } from '../../../src/util/transaction.js'; describe('test wallet', () => { test('test create new wallet', () => { const result = Wallet.createNewWallet(); @@ -42,7 +43,7 @@ describe('test wallet', () => { ); const privateKey = '03bd0cea9730bcfc8045248fd7f4841ea19315995c44801a3dfede0ca872f808'; const wallet = Wallet.getWalletByPrivateKey(privateKey); - const signWallet = Wallet.signTransaction(rawTxn, wallet.keyPair); + const signWallet = signTransaction(rawTxn, wallet.keyPair); expect(signWallet).toHaveProperty('signature'); expect(signWallet.signature).toBeInstanceOf(Buffer); expect(signWallet.signature.toString('hex')).toBe( @@ -56,7 +57,7 @@ describe('test wallet', () => { ); const privateKeyNullParams = '03bd0cea9730bcfc8045248fd7f4841ea19315995c44801a3dfede0ca872f808'; const walletNullParams = Wallet.getWalletByPrivateKey(privateKeyNullParams); - const signWalletNullParams = Wallet.signTransaction(rawTxnNullParams, walletNullParams.keyPair); + const signWalletNullParams = signTransaction(rawTxnNullParams, walletNullParams.keyPair); expect(signWalletNullParams).toHaveProperty('signature'); expect(signWalletNullParams.signature).toBeInstanceOf(Buffer); expect(signWalletNullParams.signature.toString('hex')).toBe( @@ -95,4 +96,61 @@ describe('test wallet', () => { const isValidNoPubKey = Wallet.verify(signature.toString('hex'), msgHash); expect(isValidNoPubKey).toBe(true); }); + + test('test deprecated signTransaction function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + const rawTxn = getTransaction( + 'ELF_65dDNxzcd35jESiidFXN5JV8Z7pCwaFnepuYQToNefSgqk9', + 'ELF_65dDNxzcd35jESiidFXN5JV8Z7pCwaFnepuYQToNefSgqk9', + 'test', + ['hello', 'world'] + ); + const privateKey = '03bd0cea9730bcfc8045248fd7f4841ea19315995c44801a3dfede0ca872f808'; + const wallet = Wallet.getWalletByPrivateKey(privateKey); + + Wallet.signTransaction(rawTxn, wallet.keyPair); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0),\n please use aelf.wallet.sign instead,\n or use utils/transaction.js signTransaction' + ); + + consoleSpy.mockRestore(); + }); + + test('test deprecated keyStore.getKeystore function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + Wallet.keyStore.getKeystore(); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0), please use utils/keyStore.js getKeystore' + ); + + consoleSpy.mockRestore(); + }); + + test('test deprecated keyStore.unlockKeystore function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + Wallet.keyStore.unlockKeystore(); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0), please use utils/keyStore.js unlockKeystore' + ); + + consoleSpy.mockRestore(); + }); + + test('test deprecated keyStore.checkPassword function', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + Wallet.keyStore.checkPassword(); + + expect(consoleSpy).toHaveBeenCalledWith( + 'deprecated method (>=3.5.0), please use utils/keyStore.js checkPassword' + ); + + consoleSpy.mockRestore(); + }); }); diff --git a/types/index.d.ts b/types/index.d.ts index af4d5fda..db9ec633 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -5,6 +5,7 @@ import * as proto from './util/proto'; import * as utils from './util/utils'; import sha256 from './util/sha256'; import * as transform from './util/transform'; +import * as transaction from './util/transaction'; import { arrayToHex, padLeft, @@ -78,6 +79,8 @@ type TUtilsType = IUtils & sha256: typeof sha256; } & { transform: typeof transform; + } & { + transaction: typeof transaction; }; interface IVersion { api?: string; @@ -110,4 +113,5 @@ declare class AElf { currentProvider: HttpProvider; chain: Chain; } + export default AElf; diff --git a/types/util/transaction.d.ts b/types/util/transaction.d.ts new file mode 100644 index 00000000..e6b21b53 --- /dev/null +++ b/types/util/transaction.d.ts @@ -0,0 +1,16 @@ +import { TRawTransaction } from './proto'; +import { ec } from 'elliptic'; +import { TRawTx } from '../contract/contractMethod'; +import * as protobuf from '@aelfqueen/protobufjs/light'; + +interface SignatureObject { + signature: Uint8Array; +} +type SignTransaction = SignatureObject & TRawTx; + +export declare function signTransaction(rawTxn: TRawTransaction, keyPair: ec.KeyPair): SignTransaction; + +export declare function deserializeTransaction( + rawTx: ArrayBuffer | SharedArrayBuffer, + paramsDataType: protobuf.Type +): { [k: string]: any };