Source: covenants/bitfield.js

  1. 'use strict';
  2. const assert = require('bsert');
  3. const AirdropProof = require('../primitives/airdropproof');
  4. const {TREE_LEAVES} = AirdropProof;
  5. /**
  6. * Field
  7. */
  8. class Field {
  9. constructor(size = 0) {
  10. assert((size >>> 0) === size);
  11. this.size = size;
  12. this.field = Buffer.alloc((size + 7) >>> 3, 0x00);
  13. this.dirty = false;
  14. }
  15. set(i, val) {
  16. assert((i >>> 0) === i);
  17. assert(i < this.size);
  18. assert((val >>> 0) === val);
  19. assert(val === 0 || val === 1);
  20. if (val)
  21. this.field[i >>> 3] |= 1 << (7 - (i & 7));
  22. else
  23. this.field[i >>> 3] &= ~(1 << (7 - (i & 7)));
  24. this.dirty = true;
  25. return this;
  26. }
  27. get(i) {
  28. assert((i >>> 0) === i);
  29. if (i >= this.size)
  30. return 1;
  31. return (this.field[i >>> 3] >> (7 - (i & 7))) & 1;
  32. }
  33. isSpent(i) {
  34. return Boolean(this.get(i));
  35. }
  36. spend(i) {
  37. return this.set(i, 1);
  38. }
  39. unspend(i) {
  40. return this.set(i, 0);
  41. }
  42. encode() {
  43. this.dirty = false;
  44. return this.field;
  45. }
  46. decode(data) {
  47. assert(Buffer.isBuffer(data));
  48. this.field = data;
  49. this.dirty = false;
  50. return this;
  51. }
  52. static decode(size, data) {
  53. return new this(size).decode(data);
  54. }
  55. }
  56. /**
  57. * BitField
  58. */
  59. class BitField extends Field {
  60. constructor() {
  61. super(TREE_LEAVES);
  62. }
  63. static decode(data) {
  64. return new this().decode(data);
  65. }
  66. }
  67. /**
  68. * BitView
  69. */
  70. class BitView {
  71. constructor() {
  72. this.bits = new Map();
  73. }
  74. spend(field, tx) {
  75. assert(field instanceof Field);
  76. assert(tx && tx.isCoinbase());
  77. for (let i = 1; i < tx.inputs.length; i++) {
  78. const input = tx.inputs[i];
  79. const output = tx.output(i);
  80. const {witness} = input;
  81. assert(output && witness.items.length === 1);
  82. const {covenant} = output;
  83. if (!covenant.isNone())
  84. continue;
  85. const proof = AirdropProof.decode(witness.items[0]);
  86. const index = proof.position();
  87. if (!this.bits.has(index))
  88. this.bits.set(index, field.get(index));
  89. if (this.bits.get(index) !== 0)
  90. return false;
  91. this.bits.set(index, 1);
  92. }
  93. return true;
  94. }
  95. undo(tx) {
  96. assert(tx && tx.isCoinbase());
  97. for (let i = 1; i < tx.inputs.length; i++) {
  98. const input = tx.inputs[i];
  99. const output = tx.output(i);
  100. const {witness} = input;
  101. assert(output && witness.items.length === 1);
  102. const {covenant} = output;
  103. if (!covenant.isNone())
  104. continue;
  105. const proof = AirdropProof.decode(witness.items[0]);
  106. const index = proof.position();
  107. this.bits.set(index, 0);
  108. }
  109. return this;
  110. }
  111. commit(field) {
  112. assert(field instanceof Field);
  113. for (const [bit, spent] of this.bits)
  114. field.set(bit, spent);
  115. return field.dirty;
  116. }
  117. }
  118. /*
  119. * Expose
  120. */
  121. exports.Field = Field;
  122. exports.BitField = BitField;
  123. exports.BitView = BitView;