-
Notifications
You must be signed in to change notification settings - Fork 20
/
transaction.js
106 lines (97 loc) · 3.44 KB
/
transaction.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"use strict";
const utils = require('./utils.js');
// String constants mixed in before hashing.
const TX_CONST = "TX";
/**
* A transaction comes from a single account, specified by "address". For
* each account, transactions have an order established by the nonce. A
* transaction should not be accepted if the nonce has already been used.
* (Nonces are in increasing order, so it is easy to determine when a nonce
* has been used.)
*/
module.exports = class Transaction {
/**
* The constructor for a transaction includes an array of outputs, meaning
* that one transaction can pay multiple parties. An output is a pair of an
* amount of gold and the hash of a public key (also called the address),
* in the form:
* {amount, address}
*
* @constructor
* @param {Object} obj - The inputs and outputs of the transaction.
* @param obj.from - The address of the payer.
* @param obj.nonce - Number that orders the payer's transactions. For coinbase
* transactions, this should be the block height.
* @param obj.pubKey - Public key associated with the specified from address.
* @param obj.sig - Signature of the transaction. This field may be omitted.
* @param {Array} [obj.outputs] - An array of the outputs.
* @param [obj.fee] - The amount of gold offered as a transaction fee.
* @param [obj.data] - Object with any additional properties desired for the transaction.
*/
constructor({from, nonce, pubKey, sig, outputs, fee=0, data={}}) {
this.from = from;
this.nonce = nonce;
this.pubKey = pubKey;
this.sig = sig;
this.fee = fee;
this.outputs = [];
if (outputs) outputs.forEach(({amount, address}) => {
if (typeof amount !== 'number') {
amount = parseInt(amount, 10);
}
this.outputs.push({amount, address});
});
this.data = data;
}
/**
* A transaction's ID is derived from its contents.
*/
get id() {
return utils.hash(TX_CONST + JSON.stringify({
from: this.from,
nonce: this.nonce,
pubKey: this.pubKey,
outputs: this.outputs,
fee: this.fee,
data: this.data }));
}
/**
* Signs a transaction and stores the signature in the transaction.
*
* @param privKey - The key used to sign the signature. It should match the
* public key included in the transaction.
*/
sign(privKey) {
this.sig = utils.sign(privKey, this.id);
}
/**
* Determines whether the signature of the transaction is valid
* and if the from address matches the public key.
*
* @returns {Boolean} - Validity of the signature and from address.
*/
validSignature() {
return this.sig !== undefined &&
utils.addressMatchesKey(this.from, this.pubKey) &&
utils.verifySignature(this.pubKey, this.id, this.sig);
}
/**
* Verifies that there is currently sufficient gold for the transaction.
*
* @param {Block} block - Block used to check current balances
*
* @returns {boolean} - True if there are sufficient funds for the transaction,
* according to the balances from the specified block.
*/
sufficientFunds(block) {
return this.totalOutput() <= block.balances.get(this.from);
}
/**
* Calculates the total value of all outputs, including the transaction fee.
*
* @returns {Number} - Total amount of gold given out with this transaction.
*/
totalOutput() {
return this.outputs.reduce( (totalValue, {amount}) => totalValue + amount, this.fee);
}
};