@leofcoin/lib 0.4.12 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib.js +76 -0
- package/package.json +20 -58
- package/rollup.config.js +11 -7
- package/src/addresses.json +6 -0
- package/src/bytecodes.json +6 -0
- package/src/lib.js +62 -0
- package/.travis.yml +0 -25
- package/README.md +0 -9
- package/deploy.js +0 -8
- package/index.js +0 -739
- package/src/block.js +0 -70
- package/src/chain.js +0 -350
- package/src/errors.js +0 -19
- package/src/hash.js +0 -60
- package/src/index.js +0 -3
- package/src/params.js +0 -18
- package/src/transaction.js +0 -205
- package/test/index.js +0 -106
package/src/transaction.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import Errors from './errors.js'
|
|
2
|
-
import Hash from './hash.js'
|
|
3
|
-
import { randomBytes } from 'crypto'
|
|
4
|
-
import MultiWallet from '@leofcoin/multi-wallet'
|
|
5
|
-
import { network, reward } from './params.js';
|
|
6
|
-
import * as ipldLfcTx from 'ipld-lfc-tx';
|
|
7
|
-
|
|
8
|
-
const { LFCTx, util } = ipldLfcTx
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @extends {Hash}
|
|
12
|
-
* @example
|
|
13
|
-
* const transaction = new Transaction()
|
|
14
|
-
*/
|
|
15
|
-
export default class Transaction extends Hash {
|
|
16
|
-
constructor() {
|
|
17
|
-
super()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create transaction
|
|
23
|
-
*
|
|
24
|
-
* @param inputs
|
|
25
|
-
* @param outputs
|
|
26
|
-
* @param reward
|
|
27
|
-
* @return {{id: string, reward: boolean, inputs: *, outputs: *, hash: string}}
|
|
28
|
-
*/
|
|
29
|
-
async newTransaction(inputs, outputs, reward = null) {
|
|
30
|
-
try {
|
|
31
|
-
const tx = new LFCTx({
|
|
32
|
-
id: randomBytes(32).toString('hex'),
|
|
33
|
-
time: Math.floor(new Date().getTime() / 1000),
|
|
34
|
-
reward,
|
|
35
|
-
outputs,
|
|
36
|
-
inputs
|
|
37
|
-
});
|
|
38
|
-
// const cid = await util.cid(tx.serialize())
|
|
39
|
-
// await global.ipfs.dag.put(tx, {format: util.codec, hashAlg: util.defaultHashAlg, version: 1, baseFormat: 'base58btc'})
|
|
40
|
-
return tx
|
|
41
|
-
} catch (e) {
|
|
42
|
-
throw e
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Create reward transaction for block mining
|
|
48
|
-
*
|
|
49
|
-
* @param {string} address
|
|
50
|
-
* @param {number} height
|
|
51
|
-
* @return {id: string, reward: boolean, inputs: *, outputs: *, hash: string}
|
|
52
|
-
*/
|
|
53
|
-
async createRewardTransaction(address, amount) {
|
|
54
|
-
return this.newTransaction([], [{index: 0, amount, address}], 'mined');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* validate transaction
|
|
59
|
-
*
|
|
60
|
-
* @param multihash
|
|
61
|
-
* @param transaction
|
|
62
|
-
* @param unspent
|
|
63
|
-
*/
|
|
64
|
-
async validateTransaction(multihash, transaction, unspent) {
|
|
65
|
-
if (!transaction.reward) delete transaction.reward
|
|
66
|
-
const outputs = transaction.outputs.map(o => {
|
|
67
|
-
// TODO: fix script
|
|
68
|
-
if (!o.script) delete o.script
|
|
69
|
-
return o
|
|
70
|
-
})
|
|
71
|
-
console.log(transaction);
|
|
72
|
-
transaction.outputs = outputs
|
|
73
|
-
if (!transaction.script) delete transaction.script
|
|
74
|
-
if (!ipldLfcTx.util.isValid(transaction)) return await ipldLfcTx.util.validate(transaction)
|
|
75
|
-
if (multihash !== await this.transactionHash(transaction)) throw this.TransactionError('Invalid transaction hash');
|
|
76
|
-
// TODO: versions should be handled here...
|
|
77
|
-
// Verify each input signature
|
|
78
|
-
|
|
79
|
-
if (transaction.inputs) {
|
|
80
|
-
transaction.inputs.forEach(input => {
|
|
81
|
-
const { signature, address } = input;
|
|
82
|
-
const hash = this.transactionInputHash(input);
|
|
83
|
-
|
|
84
|
-
let wallet = new MultiWallet(network);
|
|
85
|
-
wallet.fromAddress(address, null, network);
|
|
86
|
-
|
|
87
|
-
if (!wallet.verify(Buffer.from(signature, 'hex'), Buffer.from(hash, 'hex')))
|
|
88
|
-
throw this.TransactionError('Invalid input signature');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Check if inputs are in unspent list
|
|
92
|
-
transaction.inputs.forEach((input) => {
|
|
93
|
-
if (!unspent.find(out => out.tx === input.tx && out.index === input.index)) { throw this.TransactionError('Input has been already spent: ' + input.tx); }
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (transaction.reward === 'mined') {
|
|
98
|
-
// For reward transaction: check if reward output is correct
|
|
99
|
-
if (transaction.outputs.length !== 1) throw this.TransactionError('Reward transaction must have exactly one output');
|
|
100
|
-
if (transaction.outputs[0].amount !== reward) throw this.TransactionError(`Mining reward must be exactly: ${reward}`);
|
|
101
|
-
} else if (transaction.inputs) {
|
|
102
|
-
// For normal transaction: check if total output amount equals input amount
|
|
103
|
-
if (transaction.inputs.reduce((acc, input) => acc + input.amount, 0) !==
|
|
104
|
-
transaction.outputs.reduce((acc, output) => acc + output.amount, 0)) { throw this.TransactionError('Input and output amounts do not match'); }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* validate transactions list for current block
|
|
112
|
-
*
|
|
113
|
-
* @param {array} transactions
|
|
114
|
-
* @param unspent
|
|
115
|
-
*/
|
|
116
|
-
async validateTransactions(transactions, unspent) {
|
|
117
|
-
const _transactions = []
|
|
118
|
-
for (let tx of transactions) {
|
|
119
|
-
let multihash = tx.multihash
|
|
120
|
-
|
|
121
|
-
if (multihash) tx = await leofcoin.api.transaction.get(multihash);
|
|
122
|
-
else {
|
|
123
|
-
tx = await new LFCTx({...tx})
|
|
124
|
-
multihash = await util.cid(await tx.serialize())
|
|
125
|
-
multihash = multihash.toBaseEncodedString()
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
_transactions.push({multihash, value: tx.toJSON()})
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
for (const {value, multihash} of _transactions) {
|
|
133
|
-
// TODO: fix value.scrip
|
|
134
|
-
await this.validateTransaction(multihash, value, unspent)
|
|
135
|
-
}
|
|
136
|
-
} catch (e) {
|
|
137
|
-
throw e
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (_transactions.filter(({value}) => value.reward === 'mined').length !== 1)
|
|
141
|
-
throw this.TransactionError('Transactions cannot have more than one reward');
|
|
142
|
-
|
|
143
|
-
return true
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Verify signature
|
|
148
|
-
*
|
|
149
|
-
* @param {string} address - signer address
|
|
150
|
-
* @param {string} signature - signature to verify
|
|
151
|
-
* @param {string} hash - transaction hash
|
|
152
|
-
*/
|
|
153
|
-
verifySignature(address, signature, hash) {
|
|
154
|
-
const wallet = new MultiWallet(network);
|
|
155
|
-
return wallet.verify(signature, hash, address);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Create and sign input
|
|
160
|
-
*
|
|
161
|
-
* @param transaction Based on transaction id
|
|
162
|
-
* @param index Based on transaction output index
|
|
163
|
-
* @param amount
|
|
164
|
-
* @param wallet
|
|
165
|
-
* @return {Object} {transaction, index, amount, address}
|
|
166
|
-
*/
|
|
167
|
-
createInput(transaction, index, amount, wallet) {
|
|
168
|
-
const input = {
|
|
169
|
-
tx: transaction,
|
|
170
|
-
index,
|
|
171
|
-
amount,
|
|
172
|
-
address: wallet.address,
|
|
173
|
-
};
|
|
174
|
-
input.signature = wallet.sign(Buffer.from(this.transactionInputHash(input), 'hex')).toString('hex');
|
|
175
|
-
return input;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Create a transaction
|
|
180
|
-
*
|
|
181
|
-
* @param wallet
|
|
182
|
-
* @param toAddress
|
|
183
|
-
* @param amount
|
|
184
|
-
* @return {Object} {id, reward, inputs, outputs, hash,}
|
|
185
|
-
*/
|
|
186
|
-
async buildTransaction(wallet, toAddress, amount, unspent) {
|
|
187
|
-
let inputsAmount = 0;
|
|
188
|
-
// const unspent = await this.getUnspentForAddress(wallet.address);
|
|
189
|
-
const inputsRaw = unspent.filter(i => {
|
|
190
|
-
const more = inputsAmount < amount;
|
|
191
|
-
if (more) inputsAmount += i.amount;
|
|
192
|
-
return more;
|
|
193
|
-
});
|
|
194
|
-
if (inputsAmount < amount) throw this.TransactionError('Not enough funds');
|
|
195
|
-
// TODO: Add multiSigning
|
|
196
|
-
const inputs = inputsRaw.map(i => this.createInput(i.tx, i.index, i.amount, wallet));
|
|
197
|
-
// Send amount to destination address
|
|
198
|
-
const outputs = [{index: 0, amount, address: toAddress}];
|
|
199
|
-
// Send back change to my wallet
|
|
200
|
-
if (inputsAmount - amount > 0) {
|
|
201
|
-
outputs.push({index: 1, amount: inputsAmount - amount, address: wallet.address});
|
|
202
|
-
}
|
|
203
|
-
return this.newTransaction(inputs, outputs);
|
|
204
|
-
}
|
|
205
|
-
}
|
package/test/index.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
const test = require('tape');
|
|
2
|
-
const Peernet = require('@leofcoin/peernet');
|
|
3
|
-
const Chain = require('./../index');
|
|
4
|
-
|
|
5
|
-
const address = '8HFTVVfRMeL1sASrsdp6mvqDW9pvSD4AKySPEq3So16Wo1mpgY';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
(async () => {
|
|
9
|
-
await new Peernet()
|
|
10
|
-
|
|
11
|
-
const chain = new Chain()
|
|
12
|
-
|
|
13
|
-
chain.chain.push({
|
|
14
|
-
hash: 'zsNS6waLSmzg9BFR5CwJxJWpmT1ygXpXHuJ8Y4hTeqq8HRAVEcDfUnsMxEeFb7DLmaF3kPiUyEAfdX3dgPdTNLPHqCfvY6',
|
|
15
|
-
index: 1,
|
|
16
|
-
nonce: 263991677,
|
|
17
|
-
prevHash: 'zsNS6wZiHSc2QPHmjV8TMNn798b4Kp9jpjsBNeUkPhaJTza3GosWUgE72Jy3X9jKMrFCcDni7Pq4yXogQN4TcAfrPmTXFt',
|
|
18
|
-
time: 1590242832,
|
|
19
|
-
transactions: [{
|
|
20
|
-
id: 'f83349fbde4098c9be72be87468265d7ae43de8137fec514e3b81adbd8ff1c9d',
|
|
21
|
-
inputs: [],
|
|
22
|
-
outputs: [{
|
|
23
|
-
address: '8HFTVVfRMeL1sASrsdp6mvqDW9pvSD4AKySPEq3So16Wo1mpgY',
|
|
24
|
-
amount: 150,
|
|
25
|
-
index: 0
|
|
26
|
-
}],
|
|
27
|
-
reward: 'mined',
|
|
28
|
-
script: 'mined',
|
|
29
|
-
time: 1590242832
|
|
30
|
-
}]
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// test('block', tape => {
|
|
34
|
-
// tape.plan(1)
|
|
35
|
-
//
|
|
36
|
-
// tape.equals('block')
|
|
37
|
-
// })
|
|
38
|
-
const testChain = () => new Promise((resolve, reject) => {
|
|
39
|
-
test('chain', async tape => {
|
|
40
|
-
tape.plan(15)
|
|
41
|
-
|
|
42
|
-
let value = await chain.getTransactions()
|
|
43
|
-
tape.equals(value.length, 1, 'getTransactions')
|
|
44
|
-
|
|
45
|
-
value = await chain.getTransactions(false, 1)
|
|
46
|
-
tape.equals(value.length, 0, 'getTransactions after block 1')
|
|
47
|
-
|
|
48
|
-
value = await chain.getTransactionsForAddress(address)
|
|
49
|
-
tape.equals(value.length, 1, 'getTransactionsForAddress')
|
|
50
|
-
|
|
51
|
-
value = await chain.getTransactionsForAddress(address, 1)
|
|
52
|
-
tape.equals(value.length, 0, 'getTransactionsForAddress after block 1')
|
|
53
|
-
|
|
54
|
-
value = await chain.getUnspent(address)
|
|
55
|
-
tape.equals(value.length, 1, 'getUnspent')
|
|
56
|
-
|
|
57
|
-
value = await chain.getUnspentForAddress(address)
|
|
58
|
-
tape.equals(value.length, 1, 'getUnspentForAddress')
|
|
59
|
-
|
|
60
|
-
value = await chain.getBalanceForAddress(address)
|
|
61
|
-
tape.equals(value, 150, 'getBalanceForAddress')
|
|
62
|
-
|
|
63
|
-
value = await chain.getBalanceForAddress(address, 1)
|
|
64
|
-
tape.equals(value, 0, 'getBalanceForAddress after block 1')
|
|
65
|
-
|
|
66
|
-
value = await chain.nextBlockTransactions()
|
|
67
|
-
tape.equals(value.length, 0, 'nextBlockTransactions')
|
|
68
|
-
|
|
69
|
-
value = await chain.longestChain()
|
|
70
|
-
tape.equals(value.index, 0, 'longestChain')
|
|
71
|
-
|
|
72
|
-
value = await chain.lastBlock()
|
|
73
|
-
tape.equals(value.index, 0, 'lastBlock')
|
|
74
|
-
|
|
75
|
-
value = await chain.nextBlock('8HFTVVfRMeL1sASrsdp6mvqDW9pvSD4AKySPEq3So16Wo1mpgY')
|
|
76
|
-
tape.equals(value.transactions[0].reward, 'mined', 'nextBlock')
|
|
77
|
-
|
|
78
|
-
const hash = value.hash
|
|
79
|
-
|
|
80
|
-
value = await chain.blockHash(value)
|
|
81
|
-
tape.equals(value, hash, 'blockHash')
|
|
82
|
-
|
|
83
|
-
value = await chain.transactionHash(chain.chain[1].transactions[0])
|
|
84
|
-
tape.equals(value, 'z3vzxp8k4VbTshwYpcaQQwrD9h9AZLzQjo3Dvx8sLRUdziUfmnB', 'transactionHash')
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
value = await chain.validateBlock(chain.chain[0], chain.chain[1], await chain.difficulty(), await chain.getUnspentForAddress('8HFTVVfRMeL1sASrsdp6mvqDW9pvSD4AKySPEq3So16Wo1mpgY'))
|
|
89
|
-
tape.equals(value, true, 'validateBlock')
|
|
90
|
-
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.warn({e});
|
|
93
|
-
} finally {
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
setTimeout(function () {
|
|
98
|
-
resolve()
|
|
99
|
-
}, 200);
|
|
100
|
-
})
|
|
101
|
-
});
|
|
102
|
-
await testChain()
|
|
103
|
-
process.exit(0)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
})()
|