UNPKG

@kazeblockchain/krypton-js

Version:

Javascript libraries for kaze wallet using https://github.com/kazechainio/kazewallet/blob/master/js/wallet.js as the original source.

213 lines (197 loc) 7.02 kB
import { num2hexstring, num2VarInt, reverseHex, Fixed8 } from '../utils' import { getScriptHashFromAddress, getPublicKeysFromVerificationScript, getSignaturesFromInvocationScript, getSigningThresholdFromVerificationScript, verifySignature } from '../wallet' import { ASSET_ID } from '../consts' /** * @typedef TransactionInput * @property {string} prevHash - Transaction hash, Uint256 * @property {number} prevIndex - Index of the coin in the previous transaction, Uint16 */ /** * Serializes a TransactionInput. * @param {TransactionInput} input * @return {string} */ export const serializeTransactionInput = (input) => { return reverseHex(input.prevHash) + reverseHex(num2hexstring(input.prevIndex, 2)) } /** * Deserializes a stream of hexstring into a TransactionInput. * @param {StringStream} stream * @return {TransactionInput} */ export const deserializeTransactionInput = (stream) => { const prevHash = reverseHex(stream.read(32)) const prevIndex = parseInt(reverseHex(stream.read(2)), 16) return { prevHash, prevIndex } } /** * @typedef TransactionOutput * @property {string} assetId - assetId, Uint256 * @property {number|Fixed8} value - value of output, Fixed8 * @property {string} scriptHash - Uint160 */ export const TransactionOutput = (input) => { return { assetId: input.assetId, value: new Fixed8(input.value), scriptHash: input.scriptHash } } /** * Serializes a TransactionOutput. * @param {TransactionOutput} output * @return {string} */ export const serializeTransactionOutput = (output) => { const value = new Fixed8(output.value).toReverseHex() return reverseHex(output.assetId) + value + reverseHex(output.scriptHash) } /** * Deserializes a stream into a TransactionOutput. * @param {StringStream} stream * @return {TransactionOutput} */ export const deserializeTransactionOutput = (stream) => { const assetId = reverseHex(stream.read(32)) const value = Fixed8.fromReverseHex(stream.read(8)) const scriptHash = reverseHex(stream.read(20)) return { assetId, value, scriptHash } } /** * A helper method to create a TransactionOutput using human-friendly inputs. * @param {string} assetSym - The Symbol of the asset to send. Typically KAZE or STREAM. * @param {number|Fixed8} val - The value to send. * @param {string} address - The address to send the asset to. * @return {TransactionOutput} */ export const createTransactionOutput = (assetSym, val, address) => { const assetId = ASSET_ID[assetSym] const scriptHash = getScriptHashFromAddress(address) const value = new Fixed8(val) return { assetId, value, scriptHash } } /** * @typedef TransactionAttribute * @property {number} usage - Identifying byte * @property {string} data - Data */ const maxTransactionAttributeSize = 65535 /** * Serializes a TransactionAttribute. * @param {TransactionAttribute} attr * @return {string} */ export const serializeTransactionAttribute = (attr) => { if (attr.data.length > maxTransactionAttributeSize) throw new Error() let out = num2hexstring(attr.usage) if (attr.usage === 0x81) { out += num2hexstring(attr.data.length / 2) } else if (attr.usage === 0x90 || attr.usage >= 0xf0) { out += num2VarInt(attr.data.length / 2) } if (attr.usage === 0x02 || attr.usage === 0x03) { out += attr.data.substr(2, 64) } else { out += attr.data } return out } /** * Deserializes a stream into a TransactionAttribute * @param {StringStream} stream * @return {TransactionAttribute} */ export const deserializeTransactionAttribute = (stream) => { const attr = { usage: parseInt(stream.read(1), 16) } if (attr.usage === 0x00 || attr.usage === 0x30 || (attr.usage >= 0xa1 && attr.usage <= 0xaf)) { attr.data = stream.read(32) } else if (attr.usage === 0x02 || attr.usage === 0x03) { attr.data = num2hexstring(attr.usage) + stream.read(32) } else if (attr.usage === 0x20) { attr.data = stream.read(20) } else if (attr.usage === 0x81) { attr.data = stream.read(parseInt(stream.read(1), 16)) } else if (attr.usage === 0x90 || attr.usage >= 0xf0) { attr.data = stream.readVarBytes() } else { throw new Error() } return attr } /** * @typedef Witness * @property {string} invocationScript - This data is stored as is (Little Endian) * @property {string} verificationScript - This data is stored as is (Little Endian) */ /** * Serializes a Witness. * @param {Witness} witness * @return {string} */ export const serializeWitness = (witness) => { const invoLength = num2VarInt(witness.invocationScript.length / 2) const veriLength = num2VarInt(witness.verificationScript.length / 2) return invoLength + witness.invocationScript + veriLength + witness.verificationScript } /** * Deserializes a stream into a Witness * @param {StringStream} stream * @return {Witness} */ export const deserializeWitness = (stream) => { const invocationScript = stream.readVarBytes() const verificationScript = stream.readVarBytes() return { invocationScript, verificationScript } } export const Witness = { buildMultiSig: function (tx, sigs, acctOrVerificationScript) { const verificationScript = typeof acctOrVerificationScript === 'string' ? acctOrVerificationScript : acctOrVerificationScript.contract.script const publicKeys = getPublicKeysFromVerificationScript(verificationScript) const orderedSigs = Array(publicKeys.length).fill('') sigs.forEach(element => { if (typeof element === 'string') { const position = publicKeys.findIndex(key => verifySignature(tx, element, key)) if (position === -1) { throw new Error(`Invalid signature given: ${element}`) } orderedSigs[position] = element } else if (typeof element === 'object') { const keys = getPublicKeysFromVerificationScript( element.verificationScript ) if (keys.length !== 1) { throw new Error('Given witness contains more than 1 public key!') } const position = publicKeys.indexOf(keys[0]) orderedSigs[position] = getSignaturesFromInvocationScript( element.invocationScript )[0] } else { throw new Error('Unable to process given signature') } }) const signingThreshold = getSigningThresholdFromVerificationScript( verificationScript ) const validSigs = orderedSigs.filter(s => s !== '') if (validSigs.length < signingThreshold) { throw new Error( `Insufficient signatures: expected ${signingThreshold} but got ${ validSigs.length } instead` ) } return { invocationScript: validSigs .slice(0, signingThreshold) .map(s => '40' + s) .join(''), verificationScript } } }