PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-cspr/src
Просмотр файла: cspr.ts
/**
* @prettier
*/
import * as CsprLib from './lib';
import { ECPair } from '@bitgo/secp256k1';
import BigNumber from 'bignumber.js';
import { BaseCoin as StaticsBaseCoin, CoinFamily, coins } from '@bitgo/statics';
import {
BaseCoin,
BitGoBase,
InvalidAddressError,
InvalidTransactionError,
KeyPair,
MultisigType,
multisigTypes,
ParsedTransaction,
ParseTransactionOptions,
SignedTransaction,
SignTransactionOptions as BaseSignTransactionOptions,
TransactionExplanation,
TransactionPrebuild as BaseTransactionPrebuild,
TransactionType,
UnexpectedAddressError,
VerifyAddressOptions,
VerifyTransactionOptions,
} from '@bitgo/sdk-core';
interface SignTransactionOptions extends BaseSignTransactionOptions {
txPrebuild: TransactionPrebuild;
prv: string;
}
export interface TransactionPrebuild extends BaseTransactionPrebuild {
txHex: string;
}
export interface TransactionFee {
gasLimit: string;
gasPrice: string;
}
export interface ExplainTransactionOptions {
txHex?: string;
halfSigned?: {
txHex: string;
};
feeInfo: TransactionFee;
}
interface SupplementGenerateWalletOptions {
rootPrivateKey?: string;
}
interface TransactionOutput {
address: string;
amount: string;
coin: string;
}
interface TransactionOperation {
type: number;
amount: string;
coin: string;
validator: string;
}
interface CsprVerifyAddressOptions extends VerifyAddressOptions {
rootAddress: string;
}
export class Cspr extends BaseCoin {
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
super(bitgo);
if (!staticsCoin) {
throw new Error('missing required constructor parameter staticsCoin');
}
this._staticsCoin = staticsCoin;
}
static createInstance(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>): BaseCoin {
return new Cspr(bitgo, staticsCoin);
}
getChain(): string {
return this._staticsCoin.name;
}
getFamily(): CoinFamily {
return this._staticsCoin.family;
}
getFullName(): string {
return this._staticsCoin.fullName;
}
getBaseFactor(): string | number {
return Math.pow(10, this._staticsCoin.decimalPlaces);
}
/** {@inheritDoc } **/
supportsMultisig(): boolean {
return true;
}
/** inherited doc */
getDefaultMultisigType(): MultisigType {
return multisigTypes.onchain;
}
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
// TODO: Implement when available on the SDK.
return true;
}
/**
* Check if address is valid, then make sure it matches the root address.
*
* @param {VerifyAddressOptions} params address and rootAddress to verify
*/
async isWalletAddress(params: CsprVerifyAddressOptions): Promise<boolean> {
const { address, rootAddress } = params;
if (!this.isValidAddress(address)) {
throw new InvalidAddressError(`invalid address: ${address}`);
}
if (!this.isValidAddress(rootAddress)) {
throw new InvalidAddressError('wallet root address is not valid');
}
const newAddressDetails = CsprLib.Utils.getAddressDetails(address);
const rootAddressDetails = CsprLib.Utils.getAddressDetails(rootAddress);
if (newAddressDetails.address.toLowerCase() !== rootAddressDetails.address.toLowerCase()) {
throw new UnexpectedAddressError(`address validation failure: ${newAddressDetails.address} vs ${rootAddress}`);
}
return true;
}
/**
* Generate Casper key pair - BitGo xpub format
*
* @param {Buffer} seed - Seed from which the new keypair should be generated, otherwise a random seed is used
* @returns {Object} object with generated xpub and xprv
*/
generateKeyPair(seed?: Buffer): KeyPair {
const keyPair = seed ? new CsprLib.KeyPair({ seed }) : new CsprLib.KeyPair();
const keys = keyPair.getExtendedKeys();
if (!keys.xprv) {
throw new Error('Missing xprv in key generation.');
}
return {
pub: keys.xpub,
prv: keys.xprv,
};
}
isValidPub(pub: string): boolean {
// TODO(STLX-1344): Validate using account-lib when available
// return accountLib.Cspr.Utils.isValidPublicKey(pub);
try {
new CsprLib.KeyPair({ pub });
return true;
} catch (e) {
return false;
}
}
/**
* Return boolean indicating whether input is valid private key for the coin
*
* @param prv the prv to be checked
* @returns is it valid?
*/
isValidPrv(prv: string): boolean {
// TODO(STLX-1345): Validate using account-lib when available
// return accountLib.Cspr.Utils.isValidPrivateKey(prv);
try {
new CsprLib.KeyPair({ prv });
return true;
} catch (e) {
return false;
}
}
/**
* Return boolean indicating whether input is valid CSPR address
*
* @param address the pub to be checked
* @returns true if the address is valid
*/
isValidAddress(address: string): boolean {
try {
const addressDetails = CsprLib.Utils.getAddressDetails(address);
return address === CsprLib.Utils.normalizeAddress(addressDetails);
} catch (e) {
return false;
}
}
/**
* Assemble keychain and half-sign prebuilt transaction
*
* @param {SignTransactionOptions} params data required to rebuild and sign the transaction
* @param {TransactionPrebuild} params.txPrebuild prebuild object returned by platform
* @param {String} params.prv user prv used to sign the transaction
* @returns Bluebird<SignedTransaction>
*/
async signTransaction(params: SignTransactionOptions): Promise<SignedTransaction> {
const txBuilder = this.getBuilder().from(params.txPrebuild.txHex);
const key = params.prv;
txBuilder.sign({ key });
const transaction: any = await txBuilder.build();
if (!transaction) {
throw new InvalidTransactionError('Error while trying to build transaction');
}
const response = {
txHex: transaction.toBroadcastFormat(),
};
return transaction.signature.length >= 2 ? response : { halfSigned: response };
}
async parseTransaction(params: ParseTransactionOptions): Promise<ParsedTransaction> {
return {};
}
/**
* Extend walletParams with extra params required for generating a Casper wallet
*
* Casper wallets have three three keys, user, backup and bitgo.
* Initially, we need a root prv to generate the account, which must be distinct from all three keychains on the wallet.
* If a root private key is not provided, a random one is generated.
* The root public key is the basis for the wallet root address.
*/
async supplementGenerateWallet(
walletParams: SupplementGenerateWalletOptions
): Promise<SupplementGenerateWalletOptions> {
if (walletParams.rootPrivateKey) {
if (!this.isValidPrv(walletParams.rootPrivateKey) || walletParams.rootPrivateKey.length !== 64) {
throw new Error('rootPrivateKey needs to be a hexadecimal private key string');
}
} else {
const keyPair = ECPair.makeRandom();
if (!keyPair.privateKey) {
throw new Error('no privateKey');
}
walletParams.rootPrivateKey = keyPair.privateKey.toString('hex');
}
return walletParams;
}
/**
* Sign message with private key
*
* @param key
* @param message
*/
async signMessage(key: KeyPair, message: string | Buffer): Promise<Buffer> {
const keyPair = new CsprLib.KeyPair({ prv: key.prv });
const messageHex = typeof message === 'string' ? message : message.toString('hex');
const signatureData = CsprLib.Utils.signMessage(keyPair, messageHex);
return Buffer.from(signatureData.signature);
}
/**
* Explain a Casper transaction from Raw Tx
*
* @param {ExplainTransactionOptions} params given explain transaction params
* @param {String} params.txHex raw transaction
* @param {String} params.halfSigned.txHex raw half signed transaction
* @param {TransactionFee} fee fee information
* @returns Bluebird<TransactionExplanation>
*/
async explainTransaction(params: ExplainTransactionOptions): Promise<TransactionExplanation> {
const txHex = params.txHex || (params.halfSigned && params.halfSigned.txHex);
if (!txHex || !params.feeInfo) {
throw new Error('missing explain tx parameters');
}
const txBuilder = this.getBuilder().from(txHex);
const tx: any = await txBuilder.build();
if (!tx) {
throw new InvalidTransactionError('Error while trying to build transaction');
}
const id = Buffer.from(tx.casperTx.hash).toString('hex');
const amount = CsprLib.Utils.getTransferAmount(tx.casperTx.session);
let transferId;
const outputs: TransactionOutput[] = [];
const operations: TransactionOperation[] = [];
switch (tx.type) {
case TransactionType.Send: {
transferId = CsprLib.Utils.getTransferId(tx.casperTx.session);
const toAddress = CsprLib.Utils.getTransferDestinationAddress(tx._deploy.session);
outputs.push({
address: toAddress,
amount,
coin: this.getChain(),
});
break;
}
case TransactionType.StakingLock: {
const validator = CsprLib.Utils.getValidatorAddress(tx._deploy.session);
operations.push({
type: TransactionType.StakingLock,
amount,
coin: this.getChain(),
validator: validator,
});
break;
}
case TransactionType.StakingUnlock: {
const validator = CsprLib.Utils.getValidatorAddress(tx._deploy.session);
operations.push({
type: TransactionType.StakingUnlock,
amount,
coin: this.getChain(),
validator: validator,
});
break;
}
default: {
throw new InvalidTransactionError('Error while trying to get transaction type');
}
}
const outputAmount = outputs
.reduce((acumulator, output) => {
const currentValue = new BigNumber(output.amount);
return acumulator.plus(currentValue);
}, new BigNumber(0))
.toFixed(0);
const displayOrder = [
'id',
'outputAmount',
'changeAmount',
'outputs',
'changeOutputs',
'transferId',
'fee',
'operations',
];
return {
displayOrder,
id,
outputs,
outputAmount,
changeOutputs: [], // account based does not use change outputs
changeAmount: '0', // account base does not make change
transferId,
fee: params.feeInfo,
operations,
} as any;
}
private getBuilder(): CsprLib.TransactionBuilderFactory {
return new CsprLib.TransactionBuilderFactory(coins.get(this.getChain()));
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!