Source: primitives/claim.js

/*!
 * 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 {types} = rules;
const {OwnershipProof} = Ownership;

/*
 * Constants
 */

const EMPTY = Buffer.alloc(0);

/**
 * Claim
 * @extends {bufio.Struct}
 */

class Claim extends bio.Struct {
  constructor() {
    super();

    this.blob = EMPTY;

    this._hash = null;
    this._data = null;
  }

  refresh() {
    this._hash = null;
    this._data = null;
    return this;
  }

  hash() {
    if (!this._hash)
      this._hash = blake2b.digest(this.blob);

    return this._hash;
  }

  hashHex() {
    return this.hash().toString('hex');
  }

  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;
  }

  getSize() {
    return 2 + this.blob.length;
  }

  write(bw) {
    bw.writeU16(this.blob.length);
    bw.writeBytes(this.blob);
    return bw;
  }

  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;
  }

  read(br) {
    const size = br.readU16();

    if (size > 10000)
      throw new Error('Invalid claim size.');

    this.blob = br.readBytes(size);

    return this;
  }

  toInv() {
    return new InvItem(InvItem.types.CLAIM, this.hash());
  }

  getWeight() {
    return this.getSize();
  }

  getVirtualSize() {
    const scale = consensus.WITNESS_SCALE_FACTOR;
    return (this.getWeight() + scale - 1) / scale | 0;
  }

  getMinFee(size, rate) {
    if (size == null)
      size = this.getVirtualSize();

    return policy.getMinFee(size, rate);
  }

  getFee(network) {
    const data = this.getData(network);
    assert(data);
    return data.fee;
  }

  getRate(size, network) {
    const fee = this.getFee(network);

    if (size == null)
      size = this.getVirtualSize();

    return policy.getRate(size, fee);
  }

  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.type = types.CLAIM;
    output.covenant.pushHash(rules.hashName(data.name));
    output.covenant.pushU32(height);
    output.covenant.pushString(data.name);
    output.covenant.pushU8(flags);
    output.covenant.pushHash(data.commitHash);
    output.covenant.pushU32(data.commitHeight);

    tx.inputs.push(input);
    tx.outputs.push(output);

    tx.refresh();

    return tx;
  }

  getProof() {
    try {
      return this.toProof();
    } catch (e) {
      return new OwnershipProof();
    }
  }

  toProof() {
    return OwnershipProof.decode(this.blob);
  }

  toBlob() {
    return this.blob;
  }

  getJSON() {
    const proof = this.getProof();
    return proof.toJSON();
  }

  fromBlob(blob) {
    assert(Buffer.isBuffer(blob));
    this.blob = blob;
    return this;
  }

  fromProof(proof) {
    assert(proof instanceof OwnershipProof);
    this.blob = proof.encode();
    return this;
  }

  static fromBlob(blob) {
    return new this().fromBlob(blob);
  }

  static fromProof(proof) {
    return new this().fromProof(proof);
  }
}

/*
 * Expose
 */

module.exports = Claim;