PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-avaxp/src/lib
Просмотр файла: atomicTransactionBuilder.ts
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { BN, Buffer as BufferAvax } from 'avalanche';
import utils from './utils';
import { DeprecatedTransactionBuilder } from './deprecatedTransactionBuilder';
import {
SECPTransferInput,
SECPTransferOutput,
SelectCredentialClass,
TransferableInput,
TransferableOutput,
} from 'avalanche/dist/apis/platformvm';
import { Credential } from 'avalanche/dist/common';
import { BuildTransactionError } from '@bitgo/sdk-core';
import { SECP256K1_Transfer_Output } from './iface';
/**
* Cross-chain transactions (export and import) are atomic operations.
*/
export abstract class AtomicTransactionBuilder extends DeprecatedTransactionBuilder {
protected _externalChainId: BufferAvax;
constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
this.transaction._fee.fee = this.fixedFee;
}
/**
* The internal chain is the one set for the coin in coinConfig.network. The external chain is the other chain involved.
* The external chain id is the source on import and the destination on export.
*
* @param {string} chainId - id of the external chain
*/
externalChainId(chainId: string | Buffer): this {
const newTargetChainId = typeof chainId === 'string' ? utils.cb58Decode(chainId) : BufferAvax.from(chainId);
this.validateChainId(newTargetChainId);
this._externalChainId = newTargetChainId;
return this;
}
/**
* Fee is fix for AVM atomic tx.
*
* @returns network.txFee
* @protected
*/
protected get fixedFee(): string {
return this.transaction._network.txFee;
}
// region utxo engine
/**
* Threshold must be 2 and since output always get reordered we want to make sure we can always add signatures in the correct location
* To find the correct location for the signature, we use the output's addresses to create the signatureIdx in the order that we desire
* 0: user key, 1: hsm key, 2: recovery key
* @protected
*/
protected createInputOutput(amount: BN): {
inputs: TransferableInput[];
outputs: TransferableOutput[];
credentials: Credential[];
} {
const inputs: TransferableInput[] = [];
const outputs: TransferableOutput[] = [];
// amount spent so far
let currentTotal: BN = new BN(0);
// delegating and validating have no fees
const totalTarget = amount.clone();
const credentials: Credential[] = [];
/*
A = user key
B = hsm key
C = backup key
bitgoAddresses = bitgo addresses [ A, B, C ]
utxo.addresses = IMS addresses [ B, C, A ]
utxo.addressesIndex = [ 2, 0, 1 ]
we pick 0, 1 for non-recovery
we pick 1, 2 for recovery
*/
this.transaction._utxos.forEach((utxo) => {
// in WP, output.addressesIndex is empty, so fill it
if (!utxo.addressesIndex || utxo.addressesIndex.length === 0) {
const utxoAddresses: BufferAvax[] = utxo.addresses.map((a) => utils.parseAddress(a));
utxo.addressesIndex = this.transaction._fromAddresses.map((a) => utxoAddresses.findIndex((u) => a.equals(u)));
}
// in OVC, output.addressesIndex is defined correctly from the previous iteration
});
// validate the utxos
this.transaction._utxos.forEach((utxo) => {
if (!utxo) {
throw new BuildTransactionError('Utxo is undefined');
}
// addressesIndex should never have a mismatch
if (utxo.addressesIndex?.includes(-1)) {
throw new BuildTransactionError('Addresses are inconsistent: ' + utxo.txid);
}
if (utxo.threshold !== this.transaction._threshold) {
throw new BuildTransactionError('Threshold is inconsistent');
}
});
this.transaction._utxos.forEach((utxo, i) => {
if (utxo.outputID === SECP256K1_Transfer_Output) {
const txidBuf = utils.cb58Decode(utxo.txid);
const amt: BN = new BN(utxo.amount);
const outputidx = utils.outputidxNumberToBuffer(utxo.outputidx);
const addressesIndex = utxo.addressesIndex ?? [];
// either user (0) or recovery (2)
const firstIndex = this.recoverSigner ? 2 : 0;
const bitgoIndex = 1;
currentTotal = currentTotal.add(amt);
const secpTransferInput = new SECPTransferInput(amt);
// if user/backup > bitgo
if (addressesIndex[bitgoIndex] < addressesIndex[firstIndex]) {
secpTransferInput.addSignatureIdx(addressesIndex[bitgoIndex], this.transaction._fromAddresses[bitgoIndex]);
secpTransferInput.addSignatureIdx(addressesIndex[firstIndex], this.transaction._fromAddresses[firstIndex]);
credentials.push(
SelectCredentialClass(
secpTransferInput.getCredentialID(), // 9
['', this.transaction._fromAddresses[firstIndex].toString('hex')].map(utils.createSig)
)
);
} else {
secpTransferInput.addSignatureIdx(addressesIndex[firstIndex], this.transaction._fromAddresses[firstIndex]);
secpTransferInput.addSignatureIdx(addressesIndex[bitgoIndex], this.transaction._fromAddresses[bitgoIndex]);
credentials.push(
SelectCredentialClass(
secpTransferInput.getCredentialID(),
[this.transaction._fromAddresses[firstIndex].toString('hex'), ''].map(utils.createSig)
)
);
}
const input: TransferableInput = new TransferableInput(
txidBuf,
outputidx,
this.transaction._assetId,
secpTransferInput
);
inputs.push(input);
}
});
if (currentTotal.lt(totalTarget)) {
throw new BuildTransactionError(
`Utxo outputs get ${currentTotal.toString()} and ${totalTarget.toString()} is required`
);
} else if (currentTotal.gt(totalTarget)) {
outputs.push(
new TransferableOutput(
this.transaction._assetId,
new SECPTransferOutput(
currentTotal.sub(totalTarget),
this.transaction._fromAddresses,
this.transaction._locktime,
this.transaction._threshold
)
)
);
}
return {
inputs,
outputs,
credentials,
};
}
// endregion
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!