node_modules/digitalbits-base/src/transaction.js

  1. import {xdr, hash} from "./index";
  2. import {StrKey} from "./strkey";
  3. import {Operation} from "./operation";
  4. import {Network} from "./network";
  5. import {Memo} from "./memo";
  6. import map from "lodash/map";
  7. import each from "lodash/each";
  8. import isString from 'lodash/isString';
  9. import crypto from "crypto";
  10. let MIN_LEDGER = 0;
  11. let MAX_LEDGER = 0xFFFFFFFF; // max uint32
  12. /**
  13. * A new Transaction object is created from a transaction envelope or via {@link TransactionBuilder}.
  14. * Once a Transaction has been created from an envelope, its attributes and operations
  15. * should not be changed. You should only add signers (using {@link Transaction#sign}) to a Transaction object before
  16. * submitting to the network or forwarding on to additional signers.
  17. * @constructor
  18. * @param {string|xdr.TransactionEnvelope} envelope - The transaction envelope object or base64 encoded string.
  19. */
  20. export class Transaction {
  21. constructor(envelope) {
  22. if (typeof envelope === "string") {
  23. let buffer = new Buffer(envelope, "base64");
  24. envelope = xdr.TransactionEnvelope.fromXDR(buffer);
  25. }
  26. // since this transaction is immutable, save the tx
  27. this.tx = envelope.tx();
  28. this.source = StrKey.encodeEd25519PublicKey(envelope.tx().sourceAccount().ed25519());
  29. this.fee = this.tx.fee();
  30. this._memo = this.tx.memo();
  31. this.sequence = this.tx.seqNum().toString();
  32. let timeBounds = this.tx.timeBounds();
  33. if (timeBounds) {
  34. this.timeBounds = {
  35. minTime: timeBounds.minTime().toString(),
  36. maxTime: timeBounds.maxTime().toString()
  37. };
  38. }
  39. let operations = this.tx.operations() || [];
  40. this.operations = map(operations, op => {
  41. return Operation.fromXDRObject(op);
  42. });
  43. let signatures = envelope.signatures() || [];
  44. this.signatures = map(signatures, s => s);
  45. }
  46. get memo() {
  47. return Memo.fromXDRObject(this._memo);
  48. }
  49. set memo(value) {
  50. throw new Error("Transaction is immutable");
  51. }
  52. /**
  53. * Signs the transaction with the given {@link Keypair}.
  54. * @param {...Keypair} keypairs Keypairs of signers
  55. * @returns {void}
  56. */
  57. sign(...keypairs) {
  58. let txHash = this.hash();
  59. let newSigs = each(keypairs, kp => {
  60. let sig = kp.signDecorated(txHash);
  61. this.signatures.push(sig);
  62. });
  63. }
  64. /**
  65. * Add `hashX` signer preimage as signature.
  66. * @param {Buffer|String} preimage Preimage of hash used as signer
  67. * @returns {void}
  68. */
  69. signHashX(preimage) {
  70. if (isString(preimage)) {
  71. preimage = Buffer.from(preimage, "hex");
  72. }
  73. if (preimage.length > 64) {
  74. throw new Error('preimage cannnot be longer than 64 bytes');
  75. }
  76. let signature = preimage;
  77. let hash = crypto.createHash('sha256').update(preimage).digest();
  78. let hint = hash.slice(hash.length - 4);
  79. this.signatures.push(new xdr.DecoratedSignature({hint, signature}));
  80. }
  81. /**
  82. * Returns a hash for this transaction, suitable for signing.
  83. * @returns {Buffer}
  84. */
  85. hash() {
  86. return hash(this.signatureBase());
  87. }
  88. /**
  89. * Returns the "signature base" of this transaction, which is the value
  90. * that, when hashed, should be signed to create a signature that
  91. * validators on the DigitalBits Network will accept.
  92. *
  93. * It is composed of a 4 prefix bytes followed by the xdr-encoded form
  94. * of this transaction.
  95. * @returns {Buffer}
  96. */
  97. signatureBase() {
  98. if (Network.current() === null) {
  99. throw new Error("No network selected. Use `Network.use`, `Network.usePublicNetwork` or `Network.useTestNetwork` helper methods to select network.");
  100. }
  101. return Buffer.concat([
  102. Network.current().networkId(),
  103. xdr.EnvelopeType.envelopeTypeTx().toXDR(),
  104. this.tx.toXDR()
  105. ]);
  106. }
  107. /**
  108. * To envelope returns a xdr.TransactionEnvelope which can be submitted to the network.
  109. * @returns {xdr.TransactionEnvelope}
  110. */
  111. toEnvelope() {
  112. let tx = this.tx;
  113. let signatures = this.signatures;
  114. let envelope = new xdr.TransactionEnvelope({tx, signatures});
  115. return envelope;
  116. }
  117. }