forked from Colored-Coins/Transaction
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Transaction.js
178 lines (160 loc) · 5.29 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
var PROTOCOL = 0x4343
var VERSION = 0x02
var MAXBYTESIZE = 80
var OP_CODES = {
'issuance': {
'start': 0x00,
'end': 0x0f,
'encoder': require('cc-issuance-encoder')
},
'transfer': {
'start': 0x10,
'end': 0x1f,
'encoder': require('cc-transfer-encoder')
},
'burn': {
'start': 0x20,
'end': 0x2f,
'encoder': require('cc-transfer-encoder')
}
}
var encodingLookup = {}
for (var transactionType in OP_CODES) {
for (var j = OP_CODES[transactionType].start; j <= OP_CODES[transactionType].end; j++) {
encodingLookup[j] = {}
encodingLookup[j].encode = OP_CODES[transactionType].encoder.encode
encodingLookup[j].decode = OP_CODES[transactionType].encoder.decode
encodingLookup[j].type = transactionType
}
}
var paymentsInputToSkip = function (payments) {
var result = JSON.parse(JSON.stringify(payments))
result.sort(function (a, b) {
return a.input - b.input
})
for (var i = 0; i < result.length; i++) {
var skip = false
if (result[i + 1] && result[i + 1].input > result[i].input) {
skip = true
}
delete result[i].input
result[i].skip = skip
}
return result
}
var paymentsSkipToInput = function (payments) {
var paymentsDecoded = []
var input = 0
for (var i = 0; i < payments.length; i++) {
var paymentDecoded = payments[i].burn ? {burn: true} : {range: payments[i].range, output: payments[i].output}
paymentDecoded.input = input
paymentDecoded.percent = payments[i].percent
paymentDecoded.amount = payments[i].amount
paymentsDecoded.push(paymentDecoded)
if (payments[i].skip) input = input + 1
}
return paymentsDecoded
}
function Transaction (data) {
data = data || {}
this.type = data.type || 'transfer'
this.noRules = data.noRules || true
this.payments = data.payments || []
this.protocol = data.protocol || PROTOCOL
this.version = data.version || VERSION
this.lockStatus = data.lockStatus
this.aggregationPolicy = data.aggregationPolicy || 'aggregatable'
this.divisibility = data.divisibility
this.multiSig = data.multiSig || []
this.amount = data.amount
this.sha2 = data.sha2
this.torrentHash = data.torrentHash
}
Transaction.fromHex = function (op_return) {
if (!Buffer.isBuffer(op_return)) {
op_return = new Buffer(op_return, 'hex')
}
var decoder = encodingLookup[op_return[3]]
var rawData = decoder.decode(op_return)
rawData.type = decoder.type
rawData.payments = paymentsSkipToInput(rawData.payments)
return new Transaction(rawData)
}
Transaction.newTransaction = function (protocol, version) {
return new Transaction({protocol: protocol, version: version})
}
Transaction.prototype.addPayment = function (input, amount, output, range, percent) {
range = range || false
percent = percent || false
this.payments.push({input: input, amount: amount, output: output, range: range, percent: percent})
}
Transaction.prototype.addBurn = function (input, amount, percent) {
if (this.type === 'issuance') {
throw new Error('Can\'t add burn payment to an issuance transaction')
}
this.payments.push({input: input, amount: amount, percent: percent, burn: true})
this.type = 'burn'
}
/**
* @param {Number=} amount - the amount of units of the asset to issue. Integer.
* @param {Number=} divisibility - the divisibility of the asset to issue - how many decimal points can an asset unit have. Integer.
*/
Transaction.prototype.setAmount = function (amount, divisibility) {
if (typeof amount === 'undefined') throw new Error('Amount has to be defined')
this.type = 'issuance'
this.divisibility = divisibility || 0
this.amount = amount
}
Transaction.prototype.setLockStatus = function (lockStatus) {
this.lockStatus = lockStatus
this.type = 'issuance'
}
Transaction.prototype.setAggregationPolicy = function (aggregationPolicy) {
this.aggregationPolicy = aggregationPolicy || 'aggregatable'
this.type = 'issuance'
}
Transaction.prototype.allowRules = function () {
this.noRules = false
}
Transaction.prototype.shiftOutputs = function (shiftAmount) {
shiftAmount = shiftAmount || 1
this.payments.forEach(function (payment) {
payment.output += shiftAmount
})
}
Transaction.prototype.setHash = function (torrentHash, sha2) {
if (!torrentHash) throw new Error('Can\'t set hashes without the torrent hash')
if (!Buffer.isBuffer(torrentHash)) torrentHash = new Buffer(torrentHash, 'hex')
this.torrentHash = torrentHash
if (sha2) {
if (!Buffer.isBuffer(sha2)) sha2 = new Buffer(sha2, 'hex')
this.sha2 = sha2
}
}
Transaction.prototype.encode = function () {
var encoder = OP_CODES[this.type].encoder
this.payments = paymentsInputToSkip(this.payments)
var result = encoder.encode(this, MAXBYTESIZE)
this.payments = paymentsSkipToInput(this.payments)
return result
}
Transaction.prototype.toJson = function () {
var data = {}
data.payments = this.payments
data.protocol = this.protocol
data.version = this.version
data.type = this.type
if (this.type === 'issuance') {
data.lockStatus = this.lockStatus
data.aggregationPolicy = this.aggregationPolicy
data.divisibility = this.divisibility
data.amount = this.amount
}
data.multiSig = this.multiSig
if (this.torrentHash) {
data.torrentHash = this.torrentHash.toString('hex')
if (this.sha2) data.sha2 = this.sha2.toString('hex')
}
return data
}
module.exports = Transaction