Source: script/sigcache.js

  1. /*!
  2. * sigcache.js - signature cache for bcoin
  3. * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
  4. * https://github.com/bcoin-org/bcoin
  5. */
  6. 'use strict';
  7. const assert = require('bsert');
  8. const {BufferMap} = require('buffer-map');
  9. const secp256k1 = require('bcrypto/lib/secp256k1');
  10. /**
  11. * Signature cache.
  12. * @alias module:script.SigCache
  13. * @property {Number} size
  14. * @property {Hash[]} keys
  15. * @property {Object} valid
  16. */
  17. class SigCache {
  18. /**
  19. * Create a signature cache.
  20. * @constructor
  21. * @param {Number} [size=10000]
  22. */
  23. constructor(size) {
  24. if (size == null)
  25. size = 10000;
  26. assert((size >>> 0) === size);
  27. this.size = size;
  28. this.keys = [];
  29. this.valid = new BufferMap();
  30. }
  31. /**
  32. * Resize the sigcache.
  33. * @param {Number} size
  34. */
  35. resize(size) {
  36. assert((size >>> 0) === size);
  37. this.size = size;
  38. this.keys.length = 0;
  39. this.valid.clear();
  40. }
  41. /**
  42. * Add item to the sigcache.
  43. * Potentially evict a random member.
  44. * @param {Hash} msg - Sig hash.
  45. * @param {Buffer} sig
  46. * @param {Buffer} key
  47. */
  48. add(msg, sig, key) {
  49. if (this.size === 0)
  50. return;
  51. this.valid.set(msg, new SigCacheEntry(sig, key));
  52. if (this.keys.length >= this.size) {
  53. const i = Math.floor(Math.random() * this.keys.length);
  54. const k = this.keys[i];
  55. this.valid.delete(k);
  56. this.keys[i] = msg;
  57. } else {
  58. this.keys.push(msg);
  59. }
  60. }
  61. /**
  62. * Test whether the sig exists.
  63. * @param {Hash} msg - Sig hash.
  64. * @param {Buffer} sig
  65. * @param {Buffer} key
  66. * @returns {Boolean}
  67. */
  68. has(msg, sig, key) {
  69. const entry = this.valid.get(msg);
  70. if (!entry)
  71. return false;
  72. return entry.equals(sig, key);
  73. }
  74. /**
  75. * Verify a signature, testing
  76. * it against the cache first.
  77. * @param {Buffer} msg
  78. * @param {Buffer} sig
  79. * @param {Buffer} key
  80. * @returns {Boolean}
  81. */
  82. verify(msg, sig, key) {
  83. if (this.size === 0)
  84. return secp256k1.verifyDER(msg, sig, key);
  85. if (this.has(msg, sig, key))
  86. return true;
  87. const result = secp256k1.verifyDER(msg, sig, key);
  88. if (!result)
  89. return false;
  90. this.add(msg, sig, key);
  91. return true;
  92. }
  93. }
  94. /**
  95. * Signature Cache Entry
  96. * @ignore
  97. * @property {Buffer} sig
  98. * @property {Buffer} key
  99. */
  100. class SigCacheEntry {
  101. /**
  102. * Create a cache entry.
  103. * @constructor
  104. * @param {Buffer} sig
  105. * @param {Buffer} key
  106. */
  107. constructor(sig, key) {
  108. this.sig = Buffer.from(sig);
  109. this.key = Buffer.from(key);
  110. }
  111. /**
  112. * Compare an entry to a sig and key.
  113. * @param {Buffer} sig
  114. * @param {Buffer} key
  115. * @returns {Boolean}
  116. */
  117. equals(sig, key) {
  118. return this.sig.equals(sig) && this.key.equals(key);
  119. }
  120. }
  121. /*
  122. * Expose
  123. */
  124. module.exports = SigCache;