UNPKG

@stellar/stellar-base

Version:

Low-level support library for the Stellar network.

276 lines (275 loc) 15.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.authorizeEntry = authorizeEntry; exports.authorizeInvocation = authorizeInvocation; var _xdr = _interopRequireDefault(require("./xdr")); var _keypair = require("./keypair"); var _strkey = require("./strkey"); var _network = require("./network"); var _hashing = require("./hashing"); var _address = require("./address"); var _scval = require("./scval"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } /** * @async * @callback SigningCallback A callback for signing an XDR structure * representing all of the details necessary to authorize an invocation tree. * * @param {xdr.HashIdPreimage} preimage the entire authorization envelope * whose hash you should sign, so that you can inspect the entire structure * if necessary (rather than blindly signing a hash) * * @returns { * Promise<Uint8Array> | * Promise<{signature: Uint8Array, publicKey: string} * } the signature of the raw payload (which is the sha256 hash of the preimage * bytes, so `hash(preimage.toXDR())`) either naked, implying it is signed * by the key corresponding to the public key in the entry you pass to * {@link authorizeEntry} (decipherable from its * `credentials().address().address()`), or alongside an explicit `publicKey`. */ /** * Actually authorizes an existing authorization entry using the given the * credentials and expiration details, returning a signed copy. * * This "fills out" the authorization entry with a signature, indicating to the * {@link Operation.invokeHostFunction} its attached to that: * - a particular identity (i.e. signing {@link Keypair} or other signer) * - approving the execution of an invocation tree (i.e. a simulation-acquired * {@link xdr.SorobanAuthorizedInvocation} or otherwise built) * - on a particular network (uniquely identified by its passphrase, see * {@link Networks}) * - until a particular ledger sequence is reached. * * This one lets you pass a either a {@link Keypair} (or, more accurately, * anything with a `sign(Buffer): Buffer` method) or a callback function (see * {@link SigningCallback}) to handle signing the envelope hash. * * @param {xdr.SorobanAuthorizationEntry} entry an unsigned authorization entr * @param {Keypair | SigningCallback} signer either a {@link Keypair} instance * or a function which takes a {@link xdr.HashIdPreimageSorobanAuthorization} * input payload and returns EITHER * * (a) an object containing a `signature` of the hash of the raw payload bytes * as a Buffer-like and a `publicKey` string representing who just * created this signature, or * (b) just the naked signature of the hash of the raw payload bytes (where * the signing key is implied to be the address in the `entry`). * * The latter option (b) is JUST for backwards compatibility and will be * removed in the future. * @param {number} validUntilLedgerSeq the (exclusive) future ledger sequence * number until which this authorization entry should be valid (if * `currentLedgerSeq==validUntil`, this is expired)) * @param {string} [networkPassphrase] the network passphrase is incorprated * into the signature (see {@link Networks} for options) * * @returns {Promise<xdr.SorobanAuthorizationEntry>} a promise for an * authorization entry that you can pass along to * {@link Operation.invokeHostFunction} * * @note If using the `SigningCallback` variation, the signer is assumed to be * the entry's credential address unless you use the variant that returns * the object. * * @see authorizeInvocation * @example * import { * SorobanRpc, * Transaction, * Networks, * authorizeEntry * } from '@stellar/stellar-sdk'; * * // Assume signPayloadCallback is a well-formed signing callback. * // * // It might, for example, pop up a modal from a browser extension, send the * // transaction to a third-party service for signing, or just do simple * // signing via Keypair like it does here: * function signPayloadCallback(payload) { * return signer.sign(hash(payload.toXDR()); * } * * function multiPartyAuth( * server: SorobanRpc.Server, * // assume this involves multi-party auth * tx: Transaction, * ) { * return server * .simulateTransaction(tx) * .then((simResult) => { * tx.operations[0].auth.map(entry => * authorizeEntry( * entry, * signPayloadCallback, * currentLedger + 1000, * Networks.TESTNET); * )); * * return server.prepareTransaction(tx, simResult); * }) * .then((preppedTx) => { * preppedTx.sign(source); * return server.sendTransaction(preppedTx); * }); * } */ function authorizeEntry(_x, _x2, _x3) { return _authorizeEntry.apply(this, arguments); } /** * This builds an entry from scratch, allowing you to express authorization as a * function of: * - a particular identity (i.e. signing {@link Keypair} or other signer) * - approving the execution of an invocation tree (i.e. a simulation-acquired * {@link xdr.SorobanAuthorizedInvocation} or otherwise built) * - on a particular network (uniquely identified by its passphrase, see * {@link Networks}) * - until a particular ledger sequence is reached. * * This is in contrast to {@link authorizeEntry}, which signs an existing entry. * * @param {Keypair | SigningCallback} signer either a {@link Keypair} instance * (or anything with a `.sign(buf): Buffer-like` method) or a function which * takes a payload (a {@link xdr.HashIdPreimageSorobanAuthorization} * instance) input and returns the signature of the hash of the raw payload * bytes (where the signing key should correspond to the address in the * `entry`) * @param {number} validUntilLedgerSeq the (exclusive) future ledger sequence * number until which this authorization entry should be valid (if * `currentLedgerSeq==validUntilLedgerSeq`, this is expired)) * @param {xdr.SorobanAuthorizedInvocation} invocation the invocation tree that * we're authorizing (likely, this comes from transaction simulation) * @param {string} [publicKey] the public identity of the signer (when * providing a {@link Keypair} to `signer`, this can be omitted, as it just * uses {@link Keypair.publicKey}) * @param {string} [networkPassphrase] the network passphrase is incorprated * into the signature (see {@link Networks} for options, default: * {@link Networks.FUTURENET}) * * @returns {Promise<xdr.SorobanAuthorizationEntry>} a promise for an * authorization entry that you can pass along to * {@link Operation.invokeHostFunction} * * @see authorizeEntry */ function _authorizeEntry() { _authorizeEntry = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(entry, signer, validUntilLedgerSeq) { var networkPassphrase, clone, addrAuth, networkId, preimage, payload, signature, publicKey, sigResult, sigScVal, _args = arguments; return _regenerator().w(function (_context) { while (1) switch (_context.n) { case 0: networkPassphrase = _args.length > 3 && _args[3] !== undefined ? _args[3] : _network.Networks.FUTURENET; if (!(entry.credentials()["switch"]().value !== _xdr["default"].SorobanCredentialsType.sorobanCredentialsAddress().value)) { _context.n = 1; break; } return _context.a(2, entry); case 1: clone = _xdr["default"].SorobanAuthorizationEntry.fromXDR(entry.toXDR()); /** @type {xdr.SorobanAddressCredentials} */ addrAuth = clone.credentials().address(); addrAuth.signatureExpirationLedger(validUntilLedgerSeq); networkId = (0, _hashing.hash)(Buffer.from(networkPassphrase)); preimage = _xdr["default"].HashIdPreimage.envelopeTypeSorobanAuthorization(new _xdr["default"].HashIdPreimageSorobanAuthorization({ networkId: networkId, nonce: addrAuth.nonce(), invocation: clone.rootInvocation(), signatureExpirationLedger: addrAuth.signatureExpirationLedger() })); payload = (0, _hashing.hash)(preimage.toXDR()); if (!(typeof signer === 'function')) { _context.n = 3; break; } _context.n = 2; return signer(preimage); case 2: sigResult = _context.v; if (sigResult !== null && sigResult !== void 0 && sigResult.signature) { signature = Buffer.from(sigResult.signature); publicKey = sigResult.publicKey; } else { // if using the deprecated form, assume it's for the entry signature = Buffer.from(sigResult); publicKey = _address.Address.fromScAddress(addrAuth.address()).toString(); } _context.n = 4; break; case 3: signature = Buffer.from(signer.sign(payload)); publicKey = signer.publicKey(); case 4: if (_keypair.Keypair.fromPublicKey(publicKey).verify(payload, signature)) { _context.n = 5; break; } throw new Error("signature doesn't match payload"); case 5: // This structure is defined here: // https://soroban.stellar.org/docs/fundamentals-and-concepts/invoking-contracts-with-transactions#stellar-account-signatures // // Encoding a contract structure as an ScVal means the map keys are supposed // to be symbols, hence the forced typing here. sigScVal = (0, _scval.nativeToScVal)({ public_key: _strkey.StrKey.decodeEd25519PublicKey(publicKey), signature: signature }, { type: { public_key: ['symbol', null], signature: ['symbol', null] } }); addrAuth.signature(_xdr["default"].ScVal.scvVec([sigScVal])); return _context.a(2, clone); } }, _callee); })); return _authorizeEntry.apply(this, arguments); } function authorizeInvocation(signer, validUntilLedgerSeq, invocation) { var publicKey = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var networkPassphrase = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _network.Networks.FUTURENET; // We use keypairs as a source of randomness for the nonce to avoid mucking // with any crypto dependencies. Note that this just has to be random and // unique, not cryptographically secure, so it's fine. var kp = _keypair.Keypair.random().rawPublicKey(); var nonce = new _xdr["default"].Int64(bytesToInt64(kp)); var pk = publicKey || signer.publicKey(); if (!pk) { throw new Error("authorizeInvocation requires publicKey parameter"); } var entry = new _xdr["default"].SorobanAuthorizationEntry({ rootInvocation: invocation, credentials: _xdr["default"].SorobanCredentials.sorobanCredentialsAddress(new _xdr["default"].SorobanAddressCredentials({ address: new _address.Address(pk).toScAddress(), nonce: nonce, signatureExpirationLedger: 0, // replaced signature: _xdr["default"].ScVal.scvVec([]) // replaced })) }); return authorizeEntry(entry, signer, validUntilLedgerSeq, networkPassphrase); } function bytesToInt64(bytes) { // eslint-disable-next-line no-bitwise return bytes.subarray(0, 8).reduce(function (accum, b) { return accum << 8 | b; }, 0); }