PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-cspr/src/lib
Просмотр файла: transaction.ts
import * as _ from 'lodash';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { CLPublicKey as PublicKey, DeployUtil, Keys, CLString, CLU512 } from 'casper-js-sdk';
import { BaseKey, BaseTransaction, TransactionType, InvalidTransactionError, SigningError } from '@bitgo/sdk-core';
import { KeyPair } from './keyPair';
import { CasperTransaction } from './ifaces';
import {
DELEGATE_FROM_ADDRESS,
DELEGATE_VALIDATOR,
OWNER_PREFIX,
SECP256K1_PREFIX,
TRANSACTION_TYPE,
} from './constants';
import {
getTransferAmount,
getTransferDestinationAddress,
getTransferId,
isValidPublicKey,
removeAlgoPrefixFromHexValue,
getDeployType,
getDelegatorAddress,
getValidatorAddress,
getDelegateAmount,
} from './utils';
export class Transaction extends BaseTransaction {
protected _type: TransactionType;
protected _deploy: DeployUtil.Deploy;
constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
}
/** @inheritdoc */
canSign(key: BaseKey): boolean {
return true;
}
sign(keyPair: KeyPair): void {
const keys = keyPair.getKeys();
if (!keys.prv) {
throw new SigningError('Missing private key');
}
if (
this._deploy.approvals.some(
(ap) => !ap.signer.startsWith(SECP256K1_PREFIX) || !isValidPublicKey(removeAlgoPrefixFromHexValue(ap.signer))
)
) {
throw new SigningError('Invalid deploy. Already signed with an invalid key');
}
const secpKeys = new Keys.Secp256K1(
Uint8Array.from(Buffer.from(keys.pub, 'hex')),
Uint8Array.from(Buffer.from(keys.prv, 'hex'))
);
const signedDeploy = DeployUtil.signDeploy(this._deploy, secpKeys);
this._signatures.push(signedDeploy.approvals[signedDeploy.approvals.length - 1].signature);
}
/**
* Add a signature to this transaction and to and its deploy
*
* @param {string} signature The signature to add, in string hex format
* @param {KeyPair} keyPair The key pair that created the signature
*/
addSignature(signature: string, keyPair: KeyPair): void {
const pub = keyPair.getKeys().pub;
const signatureBuffer = Uint8Array.from(Buffer.from(signature, 'hex'));
const pubKeyBuffer = Uint8Array.from(Buffer.from(pub, 'hex'));
const parsedPublicKey = Keys.Secp256K1.parsePublicKey(pubKeyBuffer, 'raw');
const pubKeyHex = Keys.Secp256K1.accountHex(parsedPublicKey);
if (removeAlgoPrefixFromHexValue(pubKeyHex) !== pub) {
throw new SigningError('Signer does not match signature');
}
const signedDeploy = DeployUtil.setSignature(
this._deploy,
signatureBuffer,
PublicKey.fromSecp256K1(parsedPublicKey)
);
const approval = _.last(signedDeploy.approvals) as DeployUtil.Approval;
if (removeAlgoPrefixFromHexValue(approval.signature) !== signature) {
throw new SigningError('Invalid signature');
}
this._signatures.push(signature);
}
/** @inheritdoc */
toBroadcastFormat(): string {
if (!this.casperTx) {
throw new InvalidTransactionError('Empty transaction');
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const txJson: any = DeployUtil.deployToJson(this.casperTx);
// The new casper lib is converting the TTL from miliseconds to another date format, in this case 1 day
// we need to leave it as ms for the HSM to be able to parse it
txJson.deploy.header.ttl = `${this.casperTx.header.ttl}ms`;
this.setOwnersInJson(txJson);
this.setTransfersFieldsInJson(txJson);
this.setDelegateFieldsInJson(txJson);
return JSON.stringify(txJson);
}
/** @inheritdoc */
toJson(): CasperTransaction {
const deployPayment = this._deploy.payment.asModuleBytes()?.getArgByName('amount') as CLU512;
if (!deployPayment) {
throw new InvalidTransactionError('Undefined fee');
}
const owner1Index = 0;
const owner2Index = 1;
const owner3Index = 2;
const sourcePublicKey = Buffer.from(this._deploy.header.account.value()).toString('hex');
const sourceAddress = new KeyPair({ pub: sourcePublicKey }).getAddress();
const result: CasperTransaction = {
hash: Buffer.from(this._deploy.hash).toString('hex'),
fee: { gasLimit: deployPayment.value().toString(), gasPrice: this._deploy.header.gasPrice.toString() },
from: sourceAddress,
startTime: new Date(this._deploy.header.timestamp).toISOString(),
expiration: this._deploy.header.ttl,
deployType: (this._deploy.session.getArgByName(TRANSACTION_TYPE) as CLString).value(),
};
const transactionType = getDeployType(this._deploy.session);
switch (transactionType) {
case TransactionType.Send:
result.to = getTransferDestinationAddress(this._deploy.session);
result.amount = getTransferAmount(this._deploy.session);
result.transferId = getTransferId(this._deploy.session);
break;
case TransactionType.WalletInitialization:
result.owner1 = (this.casperTx.session.getArgByName(OWNER_PREFIX + owner1Index) as CLString).value();
result.owner2 = (this.casperTx.session.getArgByName(OWNER_PREFIX + owner2Index) as CLString).value();
result.owner3 = (this.casperTx.session.getArgByName(OWNER_PREFIX + owner3Index) as CLString).value();
break;
case TransactionType.StakingLock:
result.fromDelegate = getDelegatorAddress(this.casperTx.session);
result.validator = getValidatorAddress(this.casperTx.session);
result.amount = getDelegateAmount(this.casperTx.session);
break;
case TransactionType.StakingUnlock:
result.fromDelegate = getDelegatorAddress(this.casperTx.session);
result.validator = getValidatorAddress(this.casperTx.session);
result.amount = getDelegateAmount(this.casperTx.session);
break;
}
return result;
}
/**
* Set the transaction type
*
* @param {TransactionType} transactionType The transaction type to be set
*/
setTransactionType(transactionType: TransactionType): void {
this._type = transactionType;
}
/**
* Retrieve signatures from the deploy instance and load them into the signatures list
*/
loadPreviousSignatures(): void {
if (this._deploy.approvals && this._deploy.approvals.length > 0) {
this._deploy.approvals.forEach((approval) => {
this._signatures.push(approval.signature);
});
}
}
/**
* Set owners inside a json representing a wallet initialization tx.
*
* @param {Record<string, any>} txJson json to modify
*/
setOwnersInJson(txJson: Record<string, any>): void {
if (getDeployType(this.casperTx.session) === TransactionType.WalletInitialization) {
const argName = 0;
const argValue = 1;
const owner0 = 0;
const owner1 = 1;
const owner2 = 2;
const ownersValues = new Map();
ownersValues.set(TRANSACTION_TYPE, (this.casperTx.session.getArgByName(TRANSACTION_TYPE) as CLString).value());
[owner0, owner1, owner2].forEach((index) => {
ownersValues.set(
OWNER_PREFIX + index,
(this.casperTx.session.getArgByName(OWNER_PREFIX + index) as CLString).value()
);
});
txJson['deploy']['session']['ModuleBytes']['args'].forEach((arg) => {
if (ownersValues.has(arg[argName])) {
arg[argValue]['parsed'] = ownersValues.get(arg[argName]);
}
});
}
}
/**
* Set transfer fields inside a json representing a transfer tx.
*
* @param {Record<string, any>} txJson json to modify
*/
setTransfersFieldsInJson(txJson: Record<string, any>): void {
if (getDeployType(this.casperTx.session) === TransactionType.Send) {
const argName = 0;
const argValue = 1;
const transferValues = new Map();
transferValues.set(TRANSACTION_TYPE, (this.casperTx.session.getArgByName(TRANSACTION_TYPE) as CLString).value());
transferValues.set('amount', getTransferAmount(this.casperTx.session));
transferValues.set('to_address', getTransferDestinationAddress(this.casperTx.session));
const transferId = getTransferId(this.casperTx.session);
if (transferId !== undefined) {
transferValues.set('id', transferId.toString());
}
txJson['deploy']['session']['Transfer']['args'].forEach((arg) => {
if (transferValues.has(arg[argName])) {
arg[argValue]['parsed'] = transferValues.get(arg[argName]);
}
});
}
}
/**
* Set delegate / undelegate fields inside a json representing the tx.
*
* @param {Record<string, any>} txJson json to modify
*/
setDelegateFieldsInJson(txJson: Record<string, any>): void {
if (
getDeployType(this.casperTx.session) === TransactionType.StakingLock ||
getDeployType(this.casperTx.session) === TransactionType.StakingUnlock
) {
const argName = 0;
const argValue = 1;
const delegateValues = new Map();
delegateValues.set(TRANSACTION_TYPE, (this.casperTx.session.getArgByName(TRANSACTION_TYPE) as CLString).value());
delegateValues.set('amount', getDelegateAmount(this.casperTx.session));
delegateValues.set(DELEGATE_FROM_ADDRESS, getDelegatorAddress(this.casperTx.session));
delegateValues.set(DELEGATE_VALIDATOR, getValidatorAddress(this.casperTx.session));
txJson.deploy.session.ModuleBytes.args.forEach((arg) => {
if (delegateValues.has(arg[argName])) {
arg[argValue]['parsed'] = delegateValues.get(arg[argName]);
}
});
}
}
get casperTx(): DeployUtil.Deploy {
return this._deploy;
}
set casperTx(deploy: DeployUtil.Deploy) {
this._deploy = deploy;
}
// endregion
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!