signature.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. import { ZeroHash } from "../constants/index.js";
  2. import { concat, dataLength, getBigInt, getBytes, getNumber, hexlify, toBeArray, isHexString, zeroPadValue, assertArgument, assertPrivate } from "../utils/index.js";
  3. // Constants
  4. const BN_0 = BigInt(0);
  5. const BN_1 = BigInt(1);
  6. const BN_2 = BigInt(2);
  7. const BN_27 = BigInt(27);
  8. const BN_28 = BigInt(28);
  9. const BN_35 = BigInt(35);
  10. const _guard = {};
  11. function toUint256(value) {
  12. return zeroPadValue(toBeArray(value), 32);
  13. }
  14. /**
  15. * A Signature @TODO
  16. *
  17. *
  18. * @_docloc: api/crypto:Signing
  19. */
  20. export class Signature {
  21. #r;
  22. #s;
  23. #v;
  24. #networkV;
  25. /**
  26. * The ``r`` value for a signautre.
  27. *
  28. * This represents the ``x`` coordinate of a "reference" or
  29. * challenge point, from which the ``y`` can be computed.
  30. */
  31. get r() { return this.#r; }
  32. set r(value) {
  33. assertArgument(dataLength(value) === 32, "invalid r", "value", value);
  34. this.#r = hexlify(value);
  35. }
  36. /**
  37. * The ``s`` value for a signature.
  38. */
  39. get s() { return this.#s; }
  40. set s(_value) {
  41. assertArgument(dataLength(_value) === 32, "invalid s", "value", _value);
  42. const value = hexlify(_value);
  43. assertArgument(parseInt(value.substring(0, 3)) < 8, "non-canonical s", "value", value);
  44. this.#s = value;
  45. }
  46. /**
  47. * The ``v`` value for a signature.
  48. *
  49. * Since a given ``x`` value for ``r`` has two possible values for
  50. * its correspondin ``y``, the ``v`` indicates which of the two ``y``
  51. * values to use.
  52. *
  53. * It is normalized to the values ``27`` or ``28`` for legacy
  54. * purposes.
  55. */
  56. get v() { return this.#v; }
  57. set v(value) {
  58. const v = getNumber(value, "value");
  59. assertArgument(v === 27 || v === 28, "invalid v", "v", value);
  60. this.#v = v;
  61. }
  62. /**
  63. * The EIP-155 ``v`` for legacy transactions. For non-legacy
  64. * transactions, this value is ``null``.
  65. */
  66. get networkV() { return this.#networkV; }
  67. /**
  68. * The chain ID for EIP-155 legacy transactions. For non-legacy
  69. * transactions, this value is ``null``.
  70. */
  71. get legacyChainId() {
  72. const v = this.networkV;
  73. if (v == null) {
  74. return null;
  75. }
  76. return Signature.getChainId(v);
  77. }
  78. /**
  79. * The ``yParity`` for the signature.
  80. *
  81. * See ``v`` for more details on how this value is used.
  82. */
  83. get yParity() {
  84. return (this.v === 27) ? 0 : 1;
  85. }
  86. /**
  87. * The [[link-eip-2098]] compact representation of the ``yParity``
  88. * and ``s`` compacted into a single ``bytes32``.
  89. */
  90. get yParityAndS() {
  91. // The EIP-2098 compact representation
  92. const yParityAndS = getBytes(this.s);
  93. if (this.yParity) {
  94. yParityAndS[0] |= 0x80;
  95. }
  96. return hexlify(yParityAndS);
  97. }
  98. /**
  99. * The [[link-eip-2098]] compact representation.
  100. */
  101. get compactSerialized() {
  102. return concat([this.r, this.yParityAndS]);
  103. }
  104. /**
  105. * The serialized representation.
  106. */
  107. get serialized() {
  108. return concat([this.r, this.s, (this.yParity ? "0x1c" : "0x1b")]);
  109. }
  110. /**
  111. * @private
  112. */
  113. constructor(guard, r, s, v) {
  114. assertPrivate(guard, _guard, "Signature");
  115. this.#r = r;
  116. this.#s = s;
  117. this.#v = v;
  118. this.#networkV = null;
  119. }
  120. [Symbol.for('nodejs.util.inspect.custom')]() {
  121. return `Signature { r: "${this.r}", s: "${this.s}", yParity: ${this.yParity}, networkV: ${this.networkV} }`;
  122. }
  123. /**
  124. * Returns a new identical [[Signature]].
  125. */
  126. clone() {
  127. const clone = new Signature(_guard, this.r, this.s, this.v);
  128. if (this.networkV) {
  129. clone.#networkV = this.networkV;
  130. }
  131. return clone;
  132. }
  133. /**
  134. * Returns a representation that is compatible with ``JSON.stringify``.
  135. */
  136. toJSON() {
  137. const networkV = this.networkV;
  138. return {
  139. _type: "signature",
  140. networkV: ((networkV != null) ? networkV.toString() : null),
  141. r: this.r, s: this.s, v: this.v,
  142. };
  143. }
  144. /**
  145. * Compute the chain ID from the ``v`` in a legacy EIP-155 transactions.
  146. *
  147. * @example:
  148. * Signature.getChainId(45)
  149. * //_result:
  150. *
  151. * Signature.getChainId(46)
  152. * //_result:
  153. */
  154. static getChainId(v) {
  155. const bv = getBigInt(v, "v");
  156. // The v is not an EIP-155 v, so it is the unspecified chain ID
  157. if ((bv == BN_27) || (bv == BN_28)) {
  158. return BN_0;
  159. }
  160. // Bad value for an EIP-155 v
  161. assertArgument(bv >= BN_35, "invalid EIP-155 v", "v", v);
  162. return (bv - BN_35) / BN_2;
  163. }
  164. /**
  165. * Compute the ``v`` for a chain ID for a legacy EIP-155 transactions.
  166. *
  167. * Legacy transactions which use [[link-eip-155]] hijack the ``v``
  168. * property to include the chain ID.
  169. *
  170. * @example:
  171. * Signature.getChainIdV(5, 27)
  172. * //_result:
  173. *
  174. * Signature.getChainIdV(5, 28)
  175. * //_result:
  176. *
  177. */
  178. static getChainIdV(chainId, v) {
  179. return (getBigInt(chainId) * BN_2) + BigInt(35 + v - 27);
  180. }
  181. /**
  182. * Compute the normalized legacy transaction ``v`` from a ``yParirty``,
  183. * a legacy transaction ``v`` or a legacy [[link-eip-155]] transaction.
  184. *
  185. * @example:
  186. * // The values 0 and 1 imply v is actually yParity
  187. * Signature.getNormalizedV(0)
  188. * //_result:
  189. *
  190. * // Legacy non-EIP-1559 transaction (i.e. 27 or 28)
  191. * Signature.getNormalizedV(27)
  192. * //_result:
  193. *
  194. * // Legacy EIP-155 transaction (i.e. >= 35)
  195. * Signature.getNormalizedV(46)
  196. * //_result:
  197. *
  198. * // Invalid values throw
  199. * Signature.getNormalizedV(5)
  200. * //_error:
  201. */
  202. static getNormalizedV(v) {
  203. const bv = getBigInt(v);
  204. if (bv === BN_0 || bv === BN_27) {
  205. return 27;
  206. }
  207. if (bv === BN_1 || bv === BN_28) {
  208. return 28;
  209. }
  210. assertArgument(bv >= BN_35, "invalid v", "v", v);
  211. // Otherwise, EIP-155 v means odd is 27 and even is 28
  212. return (bv & BN_1) ? 27 : 28;
  213. }
  214. /**
  215. * Creates a new [[Signature]].
  216. *
  217. * If no %%sig%% is provided, a new [[Signature]] is created
  218. * with default values.
  219. *
  220. * If %%sig%% is a string, it is parsed.
  221. */
  222. static from(sig) {
  223. function assertError(check, message) {
  224. assertArgument(check, message, "signature", sig);
  225. }
  226. ;
  227. if (sig == null) {
  228. return new Signature(_guard, ZeroHash, ZeroHash, 27);
  229. }
  230. if (typeof (sig) === "string") {
  231. const bytes = getBytes(sig, "signature");
  232. if (bytes.length === 64) {
  233. const r = hexlify(bytes.slice(0, 32));
  234. const s = bytes.slice(32, 64);
  235. const v = (s[0] & 0x80) ? 28 : 27;
  236. s[0] &= 0x7f;
  237. return new Signature(_guard, r, hexlify(s), v);
  238. }
  239. if (bytes.length === 65) {
  240. const r = hexlify(bytes.slice(0, 32));
  241. const s = bytes.slice(32, 64);
  242. assertError((s[0] & 0x80) === 0, "non-canonical s");
  243. const v = Signature.getNormalizedV(bytes[64]);
  244. return new Signature(_guard, r, hexlify(s), v);
  245. }
  246. assertError(false, "invalid raw signature length");
  247. }
  248. if (sig instanceof Signature) {
  249. return sig.clone();
  250. }
  251. // Get r
  252. const _r = sig.r;
  253. assertError(_r != null, "missing r");
  254. const r = toUint256(_r);
  255. // Get s; by any means necessary (we check consistency below)
  256. const s = (function (s, yParityAndS) {
  257. if (s != null) {
  258. return toUint256(s);
  259. }
  260. if (yParityAndS != null) {
  261. assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
  262. const bytes = getBytes(yParityAndS);
  263. bytes[0] &= 0x7f;
  264. return hexlify(bytes);
  265. }
  266. assertError(false, "missing s");
  267. })(sig.s, sig.yParityAndS);
  268. assertError((getBytes(s)[0] & 0x80) == 0, "non-canonical s");
  269. // Get v; by any means necessary (we check consistency below)
  270. const { networkV, v } = (function (_v, yParityAndS, yParity) {
  271. if (_v != null) {
  272. const v = getBigInt(_v);
  273. return {
  274. networkV: ((v >= BN_35) ? v : undefined),
  275. v: Signature.getNormalizedV(v)
  276. };
  277. }
  278. if (yParityAndS != null) {
  279. assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
  280. return { v: ((getBytes(yParityAndS)[0] & 0x80) ? 28 : 27) };
  281. }
  282. if (yParity != null) {
  283. switch (getNumber(yParity, "sig.yParity")) {
  284. case 0: return { v: 27 };
  285. case 1: return { v: 28 };
  286. }
  287. assertError(false, "invalid yParity");
  288. }
  289. assertError(false, "missing v");
  290. })(sig.v, sig.yParityAndS, sig.yParity);
  291. const result = new Signature(_guard, r, s, v);
  292. if (networkV) {
  293. result.#networkV = networkV;
  294. }
  295. // If multiple of v, yParity, yParityAndS we given, check they match
  296. assertError(sig.yParity == null || getNumber(sig.yParity, "sig.yParity") === result.yParity, "yParity mismatch");
  297. assertError(sig.yParityAndS == null || sig.yParityAndS === result.yParityAndS, "yParityAndS mismatch");
  298. return result;
  299. }
  300. }
  301. //# sourceMappingURL=signature.js.map