stellar-plus
Version:
beta version of stellar-plus, an all-in-one sdk for the Stellar blockchain
136 lines (135 loc) • 7.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SorobanAuthPipeline = void 0;
const tslib_1 = require("tslib");
const stellar_sdk_1 = require("@stellar/stellar-sdk");
const conveyor_belt_1 = require("../../../../stellar-plus/error/helpers/conveyor-belt");
const conveyor_belts_1 = require("../../../../stellar-plus/utils/pipeline/conveyor-belts");
const errors_1 = require("./errors");
const types_1 = require("./types");
const simulate_transaction_1 = require("../simulate-transaction");
class SorobanAuthPipeline extends conveyor_belts_1.ConveyorBelt {
constructor(plugins) {
super({
type: types_1.SorobanAuthPipelineType.id,
plugins: plugins || [],
});
}
process(item, itemId) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
var _a, _b;
const { transaction, simulation, signers, rpcHandler, additionalSorobanAuthToSign, additionalSignedSorobanAuth, } = item;
const authEntriesToSign = [
...this.removeSourceCredentialAuth((_a = simulation.result) === null || _a === void 0 ? void 0 : _a.auth), // Source credentials are handled in the classic signing pipeline
...(additionalSorobanAuthToSign || []), // Additional auth entries to sign can come from other simulations and more complex authorization use cases
];
if (authEntriesToSign.length === 0 && (!additionalSignedSorobanAuth || additionalSignedSorobanAuth.length < 1))
return transaction;
if (signers.length === 0)
throw errors_1.PSAError.noSignersProvided((0, conveyor_belt_1.extractConveyorBeltErrorMeta)(item, this.getMeta(itemId)));
const authEntries = additionalSignedSorobanAuth
? [...additionalSignedSorobanAuth]
: [];
for (const authEntry of authEntriesToSign) {
const requiredSigner = this.getRequiredSigner(authEntry, item, itemId);
const signer = signers.find((s) => s.getPublicKey() === requiredSigner);
if (!signer)
throw errors_1.PSAError.signerNotFound((0, conveyor_belt_1.extractConveyorBeltErrorMeta)(item, this.getMeta(itemId)), transaction, signers.map((s) => s.getPublicKey()), requiredSigner, authEntry);
const signedEntry = yield signer.signSorobanAuthEntry(authEntry, yield this.calculateExpirationLedgerFromTimeout(rpcHandler, transaction), transaction.networkPassphrase);
authEntries.push(signedEntry);
}
let updatedTransaction;
try {
updatedTransaction = this.updateTransaction(transaction, [
...this.getSourceCredentialAuth((_b = simulation.result) === null || _b === void 0 ? void 0 : _b.auth), // Reinject unsigned source credentials. These are signed in the classic signing pipeline
...authEntries,
], simulation.transactionData);
}
catch (error) {
throw errors_1.PSAError.couldntUpdateTransaction(error, (0, conveyor_belt_1.extractConveyorBeltErrorMeta)(item, this.getMeta(itemId)), transaction, authEntries);
}
let simulatedTransaction;
try {
simulatedTransaction = yield this.simulate(updatedTransaction, rpcHandler);
}
catch (error) {
throw errors_1.PSAError.couldntSimulateAuthorizedTransaction(error, (0, conveyor_belt_1.extractConveyorBeltErrorMeta)(item, this.getMeta(itemId)), updatedTransaction, authEntries);
}
return simulatedTransaction;
});
}
updateTransaction(transaction, authEntries, sorobanData) {
const op = transaction.toEnvelope().v1().tx().operations()[0];
const authorizedOperation = stellar_sdk_1.Operation.invokeHostFunction({
func: op.body().invokeHostFunctionOp().hostFunction(),
auth: authEntries,
});
const sourceAccount = new stellar_sdk_1.Account(transaction.source, (Number(transaction.sequence) - 1).toString());
const updatedTransaction = new stellar_sdk_1.TransactionBuilder(sourceAccount, {
fee: transaction.fee,
memo: transaction.memo,
networkPassphrase: transaction.networkPassphrase,
timebounds: transaction.timeBounds,
ledgerbounds: transaction.ledgerBounds,
minAccountSequence: transaction.minAccountSequence,
minAccountSequenceAge: transaction.minAccountSequenceAge,
minAccountSequenceLedgerGap: transaction.minAccountSequenceLedgerGap,
extraSigners: transaction.extraSigners,
sorobanData: sorobanData === null || sorobanData === void 0 ? void 0 : sorobanData.build(),
});
updatedTransaction.addOperation(authorizedOperation);
return updatedTransaction.build();
}
simulate(transaction, rpcHandler) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const simulateTransactionPipeline = new simulate_transaction_1.SimulateTransactionPipeline();
const simulateOutput = yield simulateTransactionPipeline.execute({
transaction: transaction,
rpcHandler,
});
return simulateOutput.assembledTransaction;
});
}
calculateExpirationLedgerFromTimeout(rpcHandler, transaction) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
var _a, _b, _c, _d;
// The signatures will be valid up to the same ledger as the transaction
if ((_a = transaction.ledgerBounds) === null || _a === void 0 ? void 0 : _a.maxLedger) {
return transaction.ledgerBounds.maxLedger;
}
const timeout = ((_b = transaction.timeBounds) === null || _b === void 0 ? void 0 : _b.maxTime) && Number((_c = transaction.timeBounds) === null || _c === void 0 ? void 0 : _c.maxTime) > 0
? Number((_d = transaction.timeBounds) === null || _d === void 0 ? void 0 : _d.maxTime) - Date.now() / 1000
: 600; // Arbitrary 10 min default
const ledger = yield rpcHandler.getLatestLedger();
const expirationLedger = (ledger.sequence + timeout / 5 + 1).toFixed(0);
return Number(expirationLedger);
});
}
//
// Source credentials are handled in the classic signing pipeline.
// This method removes them from the auth entries to be signed.
//
removeSourceCredentialAuth(authEntries) {
return authEntries
? authEntries.filter((entry) => {
const credentials = entry.credentials();
return credentials.switch() !== stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount();
})
: [];
}
getSourceCredentialAuth(authEntries) {
return authEntries
? authEntries.filter((entry) => {
const credentials = entry.credentials();
return credentials.switch() === stellar_sdk_1.xdr.SorobanCredentialsType.sorobanCredentialsSourceAccount();
})
: [];
}
getRequiredSigner(authEntry, item, itemId) {
if (authEntry.credentials().address().address().switch().name === 'scAddressTypeContract') {
throw errors_1.PSAError.contractAuthNotSupported(stellar_sdk_1.Address.contract(authEntry.credentials().address().address().contractId()).toString(), (0, conveyor_belt_1.extractConveyorBeltErrorMeta)(item, this.getMeta(itemId)));
}
return stellar_sdk_1.Address.account(authEntry.credentials().address().address().accountId().ed25519()).toString();
}
}
exports.SorobanAuthPipeline = SorobanAuthPipeline;