PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo-express/node_modules/prova-lib/src
Просмотр файла: transactionBuilder.js
const Address = require('./address');
const ADMIN = require('./admin');
const bitcoin = require('bitcoinjs-lib');
const ECPair = require('./ecPair');
const HDNode = require('./hdNode');
const OPS = require('./ops');
const script = require('./script');
const networks = require('./networks');
const Transaction = require('./transaction');
const TransactionBuilder = bitcoin.TransactionBuilder;
TransactionBuilder.ADMIN = ADMIN;
const expandInput = (scriptSig) => {
const scriptSigChunks = bitcoin.script.decompile(scriptSig);
const pubKeys = scriptSigChunks.filter((_, index) => { return index % 2 === 0; });
const signatures = scriptSigChunks.filter((_, index) => { return index % 2 === 1; });
return {
pubKeys: pubKeys,
signatures: signatures
};
};
const buildInput = (input) => {
const interlace = [];
input.pubKeys.forEach((pubKey, index) => {
interlace.push(pubKey, input.signatures[index]);
});
const scriptType = bitcoin.script.types.P2WSH;
const script = bitcoin.script.compile(interlace);
const witness = [];
return {
type: scriptType,
script: script,
witness: witness
}
};
const determineKeyUpdateOpCode = (operation, keyType) => {
const keyTypes = ADMIN.KEY_TYPES;
switch (keyType) {
case keyTypes.ROOT.ISSUANCE_KEY:
if (operation == ADMIN.OPERATIONS.ADD_KEY) {
return OPS.ADMIN_OP_ISSUEKEYADD;
} else if (operation == ADMIN.OPERATIONS.REVOKE_KEY) {
return OPS.ADMIN_OP_ISSUEKEYREVOKE;
}
break;
case keyTypes.ROOT.PROVISIONING_KEY:
if (operation == ADMIN.OPERATIONS.ADD_KEY) {
return OPS.ADMIN_OP_PROVISIONKEYADD;
} else if (operation == ADMIN.OPERATIONS.REVOKE_KEY) {
return OPS.ADMIN_OP_PROVISIONKEYREVOKE;
}
break;
case keyTypes.PROVISIONING.VALIDATOR_KEY:
if (operation == ADMIN.OPERATIONS.ADD_KEY) {
return OPS.ADMIN_OP_VALIDATEKEYADD;
} else if (operation == ADMIN.OPERATIONS.REVOKE_KEY) {
return OPS.ADMIN_OP_VALIDATEKEYREVOKE;
}
break;
case keyTypes.PROVISIONING.ACCOUNT_SERVICE_PROVIDER_KEY:
if (operation == ADMIN.OPERATIONS.ADD_KEY) {
return OPS.ADMIN_OP_ASPKEYADD;
} else if (operation == ADMIN.OPERATIONS.REVOKE_KEY) {
return OPS.ADMIN_OP_ASPKEYREVOKE;
}
break;
}
throw new Error('invalid admin key type and operation combination');
};
const oldFromTransaction = TransactionBuilder.fromTransaction;
TransactionBuilder.fromTransaction = function(transaction, network = networks.rmg) {
return oldFromTransaction(transaction, network);
};
/**
* Admin Outputs can be either thread continuation outputs, or operation outputs
* Creates an output delineating the thread
* Operation outputs are broken down as follows:
* - Issue and revoke keys (Root thread)
* - Provisioning keys (can issue and revoke validator and ASP keys)
* - Issuing keys (can issue and destroy funds)
* - Provision non-root keys (Provisioning thread)
* - Validator keys (like mining)
* - Account Service Provider keys
* - Issue funds (Issuing thread)
*/
TransactionBuilder.prototype.addAdminThreadOutput = function(thread) {
const permissibleThreads = [
ADMIN.THREADS.ROOT,
ADMIN.THREADS.PROVISIONING,
ADMIN.THREADS.ISSUANCE,
];
if (permissibleThreads.indexOf(thread) === -1) {
throw new Error('invalid admin thread');
}
const outputScript = script.compile([script.encodeNumber(thread), OPS.OP_CHECKTHREAD]);
this.tx.addOutput(outputScript, 0);
};
/**
* Determined the admin thread output index
* @return {number}
*/
TransactionBuilder.prototype.getAdminThreadOutputIndex = function() {
for (let i = 0; i < this.tx.outs.length; i++) {
const { script } = this.tx.outs[i];
if (script && script.length === 2 && script[1] === OPS.OP_CHECKTHREAD) {
return i;
}
}
return -1;
};
/**
* Determined the admin thread output index
* @return {number}
*/
TransactionBuilder.prototype.getAdminThreadOutputIndex = function() {
for (let i = 0; i < this.tx.outs.length; i++) {
const { script } = this.tx.outs[i];
if (script && script.length === 2 && script[1] === OPS.OP_CHECKTHREAD) {
return i;
}
}
return -1;
};
/**
* Auto-infers the thread based on keyType
* @param operation (whether it's to provision or to revoke)
* @param keyType
* @param publicKey
* @param keyID
*/
TransactionBuilder.prototype.addKeyUpdateOutput = function(operation, keyType, publicKey, keyID) {
const permissibleOperations = [
ADMIN.OPERATIONS.ADD_KEY,
ADMIN.OPERATIONS.REVOKE_KEY
];
if (permissibleOperations.indexOf(operation) === -1) {
throw new Error('invalid admin key operation');
}
let opCode = determineKeyUpdateOpCode(operation, keyType);
let bufferLength = 34;
if (keyType === ADMIN.KEY_TYPES.PROVISIONING.ACCOUNT_SERVICE_PROVIDER_KEY) {
// we add four bytes for the key id
bufferLength += 4;
if (!keyID) {
throw new Error('key id must not be empty for ASP key provisioning');
}
}
let publicKeyBuffer;
if (Buffer.isBuffer(publicKey)) {
publicKeyBuffer = publicKey;
} else if (publicKey instanceof ECPair) {
publicKeyBuffer = publicKey.getPublicKeyBuffer();
} else if (publicKey instanceof HDNode) {
publicKeyBuffer = publicKey.getPublicKeyBuffer();
} else if (typeof publicKey === 'string') {
if (publicKey.startsWith('xpub')) {
const hdNode = HDNode.fromBase58(publicKey, this.network);
publicKeyBuffer = hdNode.getPublicKeyBuffer();
} else {
const ecPair = ECPair.fromPublicKeyBuffer(Buffer.from(publicKey, 'hex'), this.network);
publicKeyBuffer = ecPair.getPublicKeyBuffer();
}
} else {
throw new Error('publicKey needs to be hex or xpub String, or instance of Buffer, ECPair, or HDNode');
}
const subScript = Buffer.alloc(bufferLength);
subScript[0] = opCode;
publicKeyBuffer.copy(subScript, 1);
// the buffer length is longer by 4 bytes, totaling 38, for the key id
if (bufferLength === 38 && keyID) {
subScript.writeUInt32LE(keyID, 34);
}
const scripts = [OPS.OP_RETURN, subScript];
const scriptBuffer = script.compile(scripts);
this.tx.addOutput(scriptBuffer, 0);
};
TransactionBuilder.prototype.addFundDestructionOutput = function(amount) {
this.tx.addOutput(script.compile([OPS.OP_RETURN]), amount);
};
TransactionBuilder.prototype.__addInputUnsafe = function(txHash, vout, options) {
if (Transaction.isCoinbaseHash(txHash)) {
throw new Error('coinbase inputs not supported')
}
const prevTxOut = txHash.toString('hex') + ':' + vout;
if (this.prevTxMap[prevTxOut] !== undefined) {
throw new Error('Duplicate TxOut: ' + prevTxOut);
}
let input = {};
// derive what we can from the scriptSig
if (options.script !== undefined) {
input = expandInput(options.script, options.witness);
}
// if an input value was given, retain it
if (options.value !== undefined) {
input.value = options.value;
}
// derive what we can from the previous transactions output script
if (!input.prevOutScript && options.prevOutScript) {
let prevOutType;
if (!input.pubKeys && !input.signatures) {
const expanded = expandOutput(options.prevOutScript);
if (expanded.pubKeys) {
input.pubKeys = expanded.pubKeys;
input.signatures = expanded.signatures;
}
prevOutType = expanded.scriptType;
}
input.prevOutScript = options.prevOutScript;
input.prevOutType = prevOutType || bitcoin.script.classifyOutput(options.prevOutScript);
}
input.pubKeys = input.pubKeys || [];
input.signatures = input.signatures || [];
const vin = this.tx.addInput(txHash, vout, options.sequence, options.scriptSig);
this.inputs[vin] = input;
this.prevTxMap[prevTxOut] = vin;
return vin;
};
TransactionBuilder.prototype.addOutput = function(scriptPubKey, value) {
if (!this.__canModifyOutputs()) {
throw new Error('No, this would invalidate signatures')
}
// Attempt to get a script if it's a base58 address string
if (typeof scriptPubKey === 'string') {
scriptPubKey = Address.fromBase58(scriptPubKey).toScript();
} else if (scriptPubKey instanceof Address) {
scriptPubKey = scriptPubKey.toScript();
}
return this.tx.addOutput(scriptPubKey, value)
};
TransactionBuilder.prototype.signWithTx = function(vin, keyPair, prevOutTx) {
// ready to sign
if (!(prevOutTx instanceof Transaction)) {
throw new Error('prevOutTx needs to be instance of Transaction');
}
const currentIn = this.tx.ins[vin];
const referencedTxId = Buffer.from(currentIn.hash).reverse().toString('hex');
const actualTxId = prevOutTx.getId();
if (referencedTxId !== actualTxId) {
throw new Error('unexpected prevOutTx with id ' + actualTxId + ', as opposed to expected ' + referencedTxId);
}
const previousOutputIndex = currentIn.index;
const prevOut = prevOutTx.outs[previousOutputIndex];
const hashScript = prevOut.script;
const witnessValue = prevOut.value;
return this.sign(vin, keyPair, hashScript, witnessValue);
};
TransactionBuilder.prototype.sign = function(vin, keyPair, redeemScript, redeemValue) {
if (!(keyPair instanceof ECPair)) {
throw new Error('keyPair needs to be instance of ECPair');
}
if (keyPair.network !== this.network) {
throw new Error('Inconsistent network');
}
if (!this.inputs[vin]) {
throw new Error('No input at index: ' + vin);
}
const hashType = Transaction.SIGHASH_ALL;
const input = this.inputs[vin];
const kpPubKey = keyPair.getPublicKeyBuffer();
const signatureHash = this.tx.hashForWitnessV0(vin, redeemScript, redeemValue, hashType);
const signature = keyPair.sign(signatureHash).toScriptSignature(hashType);
input.pubKeys.push(kpPubKey);
input.signatures.push(signature);
};
TransactionBuilder.prototype.__build = function(allowIncomplete) {
if (!allowIncomplete) {
if (!this.tx.ins.length) {
throw new Error('Transaction has no inputs');
}
if (!this.tx.outs.length) {
throw new Error('Transaction has no outputs');
}
}
const tx = this.tx.clone();
// Create script signatures from inputs
this.inputs.forEach((input, i) => {
const isIncompleteAztec = ((input.pubKeys.length !== input.signatures.length) || input.signatures.length < 2);
if (isIncompleteAztec && !allowIncomplete) {
throw new Error('Transaction is not complete');
}
const result = buildInput(input, allowIncomplete);
tx.setInputScript(i, result.script);
tx.setWitness(i, result.witness);
});
if (!allowIncomplete) {
// do not rely on this, its merely a last resort
if (this.__overMaximumFees(tx.byteLength())) {
throw new Error('Transaction has absurd fees');
}
}
return tx;
};
module.exports = TransactionBuilder;
Выполнить команду
Для локальной разработки. Не используйте в интернете!