UNPKG

hsd

Version:
353 lines (267 loc) 5.76 kB
/*! * claim.js - DNSSEC ownership proofs for hsd * Copyright (c) 2018, Christopher Jeffrey (MIT License). * https://github.com/handshake-org/hsd */ 'use strict'; const assert = require('bsert'); const bio = require('bufio'); const blake2b = require('bcrypto/lib/blake2b'); const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const rules = require('../covenants/rules'); const Ownership = require('../covenants/ownership'); const InvItem = require('./invitem'); const TX = require('./tx'); const Input = require('./input'); const Output = require('./output'); const {OwnershipProof} = Ownership; /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').Amount} AmountValue */ /** @typedef {import('../types').Rate} Rate */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('../protocol/network')} Network */ /* * Constants */ const EMPTY = Buffer.alloc(0); /** * Claim * @extends {bio.Struct} */ class Claim extends bio.Struct { constructor() { super(); this.blob = EMPTY; /** @type {Hash?} */ this._hash = null; this._data = null; } /** * @returns {this} */ refresh() { this._hash = null; this._data = null; return this; } /** * @returns {Hash} */ hash() { if (!this._hash) this._hash = blake2b.digest(this.blob); return this._hash; } /** * @returns {String} */ hashHex() { return this.hash().toString('hex'); } /** * @param {Network} network * @returns {Object} */ getData(network) { if (!this._data) { const proof = this.getProof(); if (!proof) return null; const data = proof.getData(network); if (!data) return null; this._data = data; } return this._data; } /** * @returns {Number} */ getSize() { return 2 + this.blob.length; } /** * @param {BufioWriter} bw * @returns {BufioWriter} */ write(bw) { bw.writeU16(this.blob.length); bw.writeBytes(this.blob); return bw; } /** * @param {Buffer} data * @returns {this} */ decode(data) { const br = bio.read(data); if (data.length > 2 + 10000) throw new Error('Proof too large.'); this.read(br); if (br.left() !== 0) throw new Error('Trailing data.'); return this; } /** * @param {bio.BufferReader} br * @returns {this} */ read(br) { const size = br.readU16(); if (size > 10000) throw new Error('Invalid claim size.'); this.blob = br.readBytes(size); return this; } /** * @returns {InvItem} */ toInv() { return new InvItem(InvItem.types.CLAIM, this.hash()); } /** * @returns {Number} */ getWeight() { return this.getSize(); } /** * @returns {Number} */ getVirtualSize() { const scale = consensus.WITNESS_SCALE_FACTOR; return (this.getWeight() + scale - 1) / scale | 0; } /** * @param {Number} [size] * @param {Number} [rate] * @returns {AmountValue} */ getMinFee(size, rate) { if (size == null) size = this.getVirtualSize(); return policy.getMinFee(size, rate); } /** * @param {Network} [network] * @returns {AmountValue} */ getFee(network) { const data = this.getData(network); assert(data); return data.fee; } /** * @param {Number} [size] * @param {Network} [network] * @returns {Rate} */ getRate(size, network) { const fee = this.getFee(network); if (size == null) size = this.getVirtualSize(); return policy.getRate(size, fee); } /** * @param {Network} network * @param {Number} height * @returns {TX} */ toTX(network, height) { const data = this.getData(network); assert(data); const tx = new TX(); tx.inputs.push(new Input()); tx.outputs.push(new Output()); const input = new Input(); input.witness.items.push(this.blob); const output = new Output(); output.value = data.value - data.fee; output.address.version = data.version; output.address.hash = data.hash; let flags = 0; if (data.weak) flags |= 1; output.covenant.setClaim( rules.hashName(data.name), height, Buffer.from(data.name, 'binary'), flags, data.commitHash, data.commitHeight ); tx.inputs.push(input); tx.outputs.push(output); tx.refresh(); return tx; } /** * @returns {OwnershipProof} */ getProof() { try { return this.toProof(); } catch (e) { return new OwnershipProof(); } } /** * @returns {OwnershipProof} */ toProof() { return OwnershipProof.decode(this.blob); } /** * @returns {Buffer} */ toBlob() { return this.blob; } /** * @returns {Object} */ getJSON() { const proof = this.getProof(); return proof.toJSON(); } /** * Inject properties from blob. * @param {Buffer} blob * @returns {this} */ fromBlob(blob) { assert(Buffer.isBuffer(blob)); this.blob = blob; return this; } /** * @param {OwnershipProof} proof * @returns {this} */ fromProof(proof) { assert(proof instanceof OwnershipProof); this.blob = proof.encode(); return this; } /** * Instantiate claim from raw proof. * @param {Buffer} blob * @returns {Claim} */ static fromBlob(blob) { return new this().fromBlob(blob); } /** * Instantiate claim from proof. * @param {OwnershipProof} proof * @returns {Claim} */ static fromProof(proof) { return new this().fromProof(proof); } } /* * Expose */ module.exports = Claim;