diff --git a/bin/near-cli.js b/bin/near-cli.js index c343c471..072e2362 100644 --- a/bin/near-cli.js +++ b/bin/near-cli.js @@ -181,6 +181,17 @@ yargs // eslint-disable-line type: 'string', default: "44'/397'/0'/0'/1'" }) + .options('seedPhrase', { + desc: 'Seed phrase mnemonic', + type: 'string', + required: false + }) + .options('seedPath', { + desc: 'HD path derivation', + type: 'string', + default: "m/44'/397'/0'", + required: false + }) .option('walletUrl', { desc: 'Website for NEAR Wallet', type: 'string' @@ -212,6 +223,7 @@ yargs // eslint-disable-line .middleware(require('../middleware/print-options')) .middleware(require('../middleware/key-store')) .middleware(require('../middleware/ledger')) + .middleware(require('../middleware/seed-phrase')) .command(require('../commands/create-account').createAccountCommand) .command(require('../commands/create-account').createAccountCommandDeprecated) .command(viewAccount) diff --git a/commands/generate-key.js b/commands/generate-key.js index 344af8b2..2c576e4e 100644 --- a/commands/generate-key.js +++ b/commands/generate-key.js @@ -1,19 +1,23 @@ const KeyPair = require('near-api-js').KeyPair; const exitOnError = require('../utils/exit-on-error'); +const implicitAccountId = require('../utils/implicit-accountid'); module.exports = { - command: 'generate-key ', - desc: 'generate key ', + command: 'generate-key [account-id]', + desc: 'generate key or show key from Ledger', builder: (yargs) => yargs, handler: exitOnError(async (argv) => { let near = await require('../utils/connect')(argv); - if (!argv.accountId) { - return; - } if (argv.usingLedger) { - await argv.signer.getPublicKey(); - // NOTE: Command above already prints public key + if (argv.accountId) { + console.log('WARN: Account id is provided but ignored in case of using Ledger.'); + } + const publicKey = await argv.signer.getPublicKey(); + // NOTE: Command above already prints public key. + console.log(`Implicit account: ${implicitAccountId(publicKey.toString())}`); + // TODO: query all accounts with this public key here. + // TODO: check if implicit account exist, and if the key doen't match already. return; } @@ -24,8 +28,15 @@ module.exports = { return; } - const keyPair = KeyPair.fromRandom('ed25519'); - await keyStore.setKey(argv.networkId, argv.accountId, keyPair); - console.log(`Generated key pair with ${keyPair.publicKey} public key`); + // If key doesn't exist, create one and store in the keyStore. + // Otherwise, it's expected that both key and accountId are already provided in arguments. + if (!argv.publicKey) { + const keyPair = KeyPair.fromRandom('ed25519'); + argv.publicKey = keyPair.publicKey.toString(); + argv.accountId = argv.accountId || implicitAccountId(argv.publicKey); + await keyStore.setKey(argv.networkId, argv.accountId, keyPair); + } + + console.log(`Key pair with ${argv.publicKey} public key for an account "${argv.accountId}"`); }) }; \ No newline at end of file diff --git a/middleware/seed-phrase.js b/middleware/seed-phrase.js new file mode 100644 index 00000000..6e2bd06e --- /dev/null +++ b/middleware/seed-phrase.js @@ -0,0 +1,23 @@ +const { parseSeedPhrase } = require('near-seed-phrase'); +const { utils: { KeyPair }, InMemorySigner } = require('near-api-js'); +const { InMemoryKeyStore } = require('near-api-js/lib/key_stores'); + +const implicitAccountId = require('../utils/implicit-accountid'); + +// near ... --seedPhrase="phrase" --seedPath="m/44'/397'/0'" +// near generate-key --seedPhrase="phrase" +module.exports = async function useSeedPhrase({ seedPhrase, seedPath, publicKey, accountId }, yargs) { + if (!seedPhrase) { + return; + } + if (yargs.usingLedger) { + throw new Error('Can not use both --useLedgerKey and --seedPhrase at the same time'); + } + const result = parseSeedPhrase(seedPhrase, seedPath); + publicKey = result.publicKey; + let keyStore = new InMemoryKeyStore(); + accountId = accountId || implicitAccountId(publicKey); + await keyStore.setKey(yargs.networkId, accountId, KeyPair.fromString(result.secretKey)); + let signer = new InMemorySigner(keyStore); + return { signer, publicKey, accountId }; +}; diff --git a/package.json b/package.json index 4ecf0b1e..9c4dfc64 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "mixpanel": "^0.11.0", "ncp": "^2.0.0", "near-api-js": "^0.29.1", + "near-seed-phrase": "^0.1.0", "open": "^7.0.1", "rimraf": "^3.0.0", "stoppable": "^1.1.0", diff --git a/test/test_generate_key.sh b/test/test_generate_key.sh index 1a2c74ae..20288316 100755 --- a/test/test_generate_key.sh +++ b/test/test_generate_key.sh @@ -13,7 +13,7 @@ if [[ ! -f "${KEY_FILE}" ]]; then exit 1 fi -EXPECTED=".*Generated key pair with ed25519:.+ public key.*" +EXPECTED=".*Key pair with ed25519:.+ public key.*" if [[ ! "$RESULT" =~ $EXPECTED ]]; then echo FAILURE Unexpected output from near generate-key exit 1 diff --git a/utils/implicit-accountid.js b/utils/implicit-accountid.js new file mode 100644 index 00000000..304607d0 --- /dev/null +++ b/utils/implicit-accountid.js @@ -0,0 +1,5 @@ +const { decode } = require('bs58'); + +module.exports = (publicKey) => { + return decode(publicKey.replace('ed25519:', '')).toString('hex'); +}; diff --git a/yarn.lock b/yarn.lock index d08aeb57..1791bfff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -967,6 +967,25 @@ bindings@^1.4.0, bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bip39-light@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/bip39-light/-/bip39-light-1.0.7.tgz#06a72f251b89389a136d3f177f29b03342adc5ba" + integrity sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + +bip39@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.4.0.tgz#a0b8adbf163f53495f00f05d9ede7c25369ccf13" + integrity sha512-1++HywqIyPtWDo7gm4v0ylYbwkLvHkuwVSKbBlZBbTCP/mnkyrlARBny906VLAwxJbC5xw9EvuJasHFIZaIFMQ== + dependencies: + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + safe-buffer "^5.0.1" + unorm "^1.3.3" + bl@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" @@ -1171,6 +1190,14 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -1300,6 +1327,41 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + integrity sha1-rLniIaThe9sHbpBlfEK5PjcmzwY= + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2166,6 +2228,15 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -2286,7 +2357,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3209,6 +3280,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -3397,6 +3477,15 @@ near-api-js@^0.29.1: text-encoding-utf-8 "^1.0.2" tweetnacl "^1.0.1" +near-hd-key@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/near-hd-key/-/near-hd-key-1.0.0.tgz#b18fbbc8d5348a29d226ec8d546ba089afc42ce8" + integrity sha512-NknsGxGM1YivNUKdlhYl1pPWtRGOg8uBSJHd0j4B8mk0MxAeumIcorazp4V888sJleu/GptfPcGNlfiEf0zXaw== + dependencies: + bip39 "2.4.0" + create-hmac "1.1.6" + tweetnacl "^1.0.1" + near-ledger-js@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/near-ledger-js/-/near-ledger-js-0.1.2.tgz#7766e530428db4f62ec8055aba3c80956caa3825" @@ -3404,6 +3493,16 @@ near-ledger-js@^0.1.1: dependencies: bs58 "^4.0.1" +near-seed-phrase@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/near-seed-phrase/-/near-seed-phrase-0.1.0.tgz#8a9a842fc112759e86dfe65325a113a9dbd6a526" + integrity sha512-tkeb9LUi/2w1mHjP1xwTrTSQyaUBQRbYtgUnhUTJjFznXVRi4Ai+jifWhwPDvSjQ0IPqYmD8MuWRie3kY0yT1g== + dependencies: + bip39-light "^1.0.7" + bs58 "^4.0.1" + near-hd-key "^1.0.0" + tweetnacl "^1.0.2" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -3729,6 +3828,17 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" +pbkdf2@^3.0.9: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -3869,6 +3979,13 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +randombytes@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -3933,7 +4050,7 @@ readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -4106,6 +4223,14 @@ rimraf@^3.0.0: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -4118,7 +4243,7 @@ rxjs@^6.6.3: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4204,6 +4329,14 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -4756,7 +4889,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -tweetnacl@^1.0.1: +tweetnacl@^1.0.1, tweetnacl@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== @@ -4824,6 +4957,11 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +unorm@^1.3.3: + version "1.6.0" + resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" + integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"