@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/src/block.js DELETED
@@ -1,70 +0,0 @@
1
- import Transaction from './transaction'
2
- import ipldLfc from 'ipld-lfc';
3
-
4
- const { LFCNode, util } = ipldLfc
5
-
6
-
7
- /**
8
- * @extends {Transaction}
9
- * @example
10
- * const block = new Block()
11
- */
12
- export default class Block extends Transaction {
13
- constructor() {
14
- super()
15
- }
16
-
17
- getDifficulty(hash) {
18
- return parseInt(hash.substring(0, 8), 16);
19
- }
20
-
21
- goodBlock(block, difficulty){
22
- return new Promise(async (resolve, reject) => {
23
- block.hash = await this.blockHash(block);
24
- if (parseInt(block.hash.substring(0, 8), 16) >= difficulty) {
25
- block.nonce++
26
- block = await this.goodBlock(block, difficulty)
27
- }
28
- resolve(block)
29
- })
30
- }
31
-
32
- async validateBlock(previousBlock, block, difficulty, unspent) {
33
- const valid = await ipldLfc.util.isValid(block)
34
- if (!valid) return await ipldLfc.util.validate(block)
35
- // console.log(block, previousBlock);
36
- if (previousBlock.index + 1 !== block.index) return this.BlockError('index');
37
- if (!previousBlock.hash) {
38
- previousBlock = await new LFCNode(previousBlock)
39
- previousBlock = previousBlock.toJSON()
40
- previousBlock.hash = await util.cid(util.serialize(previousBlock))
41
- previousBlock.hash = previousBlock.hash.toBaseEncodedString()
42
- }
43
- console.log(await this.blockHash(block), block.hash);
44
- if (previousBlock.hash !== block.prevHash) throw this.BlockError('prevhash');
45
- if (await this.blockHash(block) !== block.hash) return this.BlockError('hash');
46
- if (this.getDifficulty(block.hash) > difficulty) return this.BlockError('difficulty');
47
-
48
-
49
- return this.validateTransactions(block.transactions, unspent);
50
- }
51
-
52
- /**
53
- * Create a new genesis block
54
- */
55
- async newGenesisDAGNode(difficulty = 1, address = Buffer.alloc(32).toString('hex')) {
56
- let block = {
57
- index: 0,
58
- prevHash: Buffer.alloc(47).toString('hex'),
59
- time: Math.floor(new Date().getTime() / 1000),
60
- transactions: [
61
- // ms.unspent(network, [], wallet.account()).create(index: 0, amount: consensusSubsidy(0), address)
62
- ],
63
- nonce: 0
64
- }
65
- block.hash = await blockHash(block);
66
- block = await this.goodBlock(block, difficulty)
67
- const node = new LFCNode(block)
68
- return node;
69
- }
70
- }
package/src/chain.js DELETED
@@ -1,350 +0,0 @@
1
- import { GENESISBLOCK, genesisCID, consensusSubsidyInterval, reward } from './params'
2
- import Block from './block'
3
-
4
- const invalidTransactions = {};
5
-
6
- globalThis.chain = globalThis.chain || [
7
- GENESISBLOCK
8
- ];
9
-
10
- globalThis.mempool = globalThis.mempool || [];
11
- globalThis.blockHashSet = globalThis.blockHashSet || [];
12
-
13
- const filterPeers = (peers, localPeer) => {
14
- const set = []
15
- return peers.reduce((p, c) => {
16
- if (set.indexOf(c.peer) === -1 && c.peer !== localPeer) {
17
- set.push(c.peer)
18
- p.push(c)
19
- }
20
- return p
21
- }, [])
22
- }
23
-
24
- /**
25
- * @extends {Block}
26
- * @example
27
- * const chain = new Chain()
28
- */
29
- export default class Chain extends Block {
30
- constructor() {
31
- super()
32
- }
33
-
34
- get chain() { return globalThis.chain }
35
-
36
- get mempool() { return globalThis.mempool }
37
-
38
- get blockHashSet() { return globalThis.blockHashSet }
39
-
40
- // TODO: needs 3 nodes running
41
- invalidTransaction(data) {
42
- // console.log(data.data.toString());
43
- data = JSON.parse(data.data.toString());
44
- if (!invalidTransactions[data.tx]) invalidTransactions[data.tx] = 0;
45
- ++invalidTransactions[data.tx]
46
- const count = invalidTransactions[data.tx];
47
- if (count === 3) {
48
- const memIndex = mempool.indexOf(data)
49
- mempool.splice(memIndex, 1)
50
- delete invalidTransactions[data.tx];
51
- }
52
- }
53
-
54
- /**
55
- * @param {number} height
56
- */
57
- consensusSubsidy(height) {
58
- // console.log(height);
59
- const quarterlings = height / consensusSubsidyInterval;
60
- if (quarterlings >= 256) {
61
- return 0;
62
- }
63
- //subsidy is lowered by 12.5 %, approx every year
64
- const minus = quarterlings >= 1 ? (quarterlings * (reward / 256)) : 0;
65
- return reward - minus;
66
- }
67
-
68
- async getTransactions(withMempool = true, index = 0) {
69
- const _chain = [...chain];
70
- if (index === chain.length - 1) return []
71
- _chain.slice(index + 1, chain.length - 1)
72
- let transactions = _chain.reduce((transactions, block) => [ ...transactions, ...block.transactions], []);
73
- if (withMempool) transactions = transactions.concat(mempool);
74
- let _transactions = []
75
- // TODO: deprecated, get/put should be handled in core
76
- for (const tx of transactions) {
77
- const {multihash} = tx
78
- if (multihash) {
79
- let value;
80
- if (leofcoin.api.transaction) value = await leofcoin.api.transaction.get(multihash)
81
- _transactions.push(value)
82
- } else {
83
- _transactions.push(tx)
84
- }
85
-
86
- }
87
- return _transactions
88
- };
89
-
90
- async getTransactionsForAddress(address, index = 0) {
91
- const transactions = await this.getTransactions(false, index);
92
- return transactions.filter(tx => tx.inputs.find(i => i.address === address) ||
93
- tx.outputs.find(o => o.address === address));
94
- };
95
-
96
- /**
97
- *
98
- * @param {string} withMempool - with or without mempool inclusion
99
- * @param {number} index - block height to start from
100
- */
101
- async getUnspent(withMempool = false, index = 0) {
102
- const transactions = await this.getTransactions(withMempool, index);
103
- // Find all inputs with their tx ids
104
- const inputs = transactions.reduce((inputs, tx) => inputs.concat(tx.inputs), []);
105
-
106
- // Find all outputs with their tx ids
107
- const outputs = transactions.reduce((outputs, tx) =>
108
- outputs.concat(tx.outputs.map(output => Object.assign({}, output, {tx: tx.id}))), []);
109
-
110
- // Figure out which outputs are unspent
111
- const unspent = outputs.filter(output =>
112
- typeof inputs.find(input => input.tx === output.tx && input.index === output.index && input.amount === output.amount) === 'undefined');
113
- return unspent;
114
- }
115
-
116
- /**
117
- * @param {String} address - wallet address
118
- * @param {number} index - block height to start from
119
- */
120
- async getUnspentForAddress(address = null, index = 0) {
121
- const unspent = await this.getUnspent(true, index)
122
- return unspent.filter(u => u.address === address);
123
- }
124
-
125
- /**
126
- * @param {String} address - wallet address
127
- * @param {Number} index - Block index to get balance after
128
- */
129
- async getBalanceForAddress(address = null, index) {
130
- // debug(`Getting balance for ${address}`)
131
- const unspent = await this.getUnspentForAddress(address, index)
132
- const amount = unspent.reduce((acc, u) => acc + u.amount , 0);
133
- // debug(`Got ${amount} for ${address}`)
134
- return amount
135
- }
136
-
137
- /**
138
- * @deprecated Use getTransactionsForAddress(addr, index) instead
139
- *
140
- * @param {string} address - wallet address
141
- * @param {number} index - block height to start from
142
- *
143
- * @return {number} balance
144
- */
145
- async getBalanceForAddressAfter(address = null, index = 0) {
146
- // debug(`Getting balance for ${address} @${index}`)
147
- const unspent = await this.getUnspentForAddress(address, index)
148
- const amount = unspent.reduce((acc, u) => acc + u.amount , 0);
149
- // debug(`Got ${amount} for ${address} @${index}`)
150
- return amount
151
- }
152
-
153
- median(array) {
154
- array.sort((a,b) => a - b)
155
-
156
- var half = Math.floor(array.length / 2);
157
-
158
- if(array.length % 2)
159
- return array[half];
160
- else
161
- return (array[half - 1] + array[half]) / 2.0;
162
- }
163
-
164
- difficulty() {
165
- // TODO: lower difficulty when transactionpool contain more then 500 tx ?
166
- // TODO: raise difficulty when pool is empty
167
-
168
- // or
169
-
170
- // TODO: implement iTX (instant transaction)
171
- // iTX is handled by multiple peers, itx is chained together by their hashes
172
- // by handlng a tx as itx the block well be converted into a iRootBlock
173
- // this results into smaller chains (tangles, tails) which should improve
174
- // resolving transactions, wallet amounts etc ...
175
- const start = chain.length >= 128 ? (chain.length - 128) : 0;
176
- const blocks = chain.slice(start, (chain.length - 1)).reverse();
177
- const stamps = [];
178
- for (var i = 0; i < blocks.length; i++) {
179
- if (blocks[i + 1]) {
180
- stamps.push(blocks[i].time - blocks[i + 1].time);
181
- }
182
- }
183
- if (stamps.length === 0) {
184
- stamps.push(10);
185
- }
186
- let blocksMedian = this.median(stamps) || 10;
187
- const offset = blocksMedian / 10
188
- // offset for quick recovery
189
- if (blocksMedian < 10) {
190
- blocksMedian -= (offset / 2);
191
- } else if (blocksMedian > 10) {
192
- blocksMedian += (offset * 2);
193
- }
194
- if (blocksMedian < 0) blocksMedian = -blocksMedian
195
- console.log(`Average Block Time: ${blocksMedian}`);
196
- console.log(`Difficulty: ${10 / blocksMedian}`);
197
- return (10000 / (10 / blocksMedian));
198
- };//10000
199
-
200
-
201
- /**
202
- * Get the transactions for the next Block
203
- *
204
- * @return {object} transactions
205
- */
206
- async nextBlockTransactions() {
207
-
208
- try {
209
- const unspent = await this.getUnspent(false);
210
- return mempool.filter(async transaction => {
211
- const multihash = transaction.multihash
212
- const value = await leofcoin.api.transaction.get(multihash)
213
- try {
214
- await this.validateTransaction(multihash, value.toJSON(), unspent);
215
- return transaction
216
- } catch (e) {
217
- globalThis.pubsub.publish('invalid-transaction', Buffer.from(JSON.stringify(transaction)));
218
- console.error(e);
219
- }
220
- });
221
- } catch (e) {
222
- throw new Error(e)
223
- }
224
- }
225
-
226
- longestChain() {
227
- return new Promise(async (resolve, reject) => {
228
- try {
229
- // TODO: peernet.id => peer.id
230
- let peers = await filterPeers([...peernet.peers], peernet.id)
231
- let set = []
232
- const request = new globalThis.peernet.protos['peernet-request']({
233
- request: 'lastBlock'
234
- })
235
-
236
- for (const peer of peers) {
237
- const to = peernet._getPeerId(peer.id)
238
- if (to) {
239
- const node = await peernet.prepareMessage(to, request.encoded)
240
- let response = await peer.request(node.encoded)
241
- const proto = new globalThis.peernet.protos['peernet-message'](Buffer.from(response.data))
242
- response = new globalThis.peernet.protos['peernet-response'](Buffer.from(proto.decoded.data))
243
- const block = JSON.parse(response.decoded.response)
244
- set.push({peer, block})
245
- }
246
- }
247
- let localIndex
248
- let localHash
249
- try {
250
- const index = await chainStore.get('localIndex')
251
- const hash = await chainStore.get('localBlock')
252
- localIndex = Number(index.toString())
253
- localHash = hash.toString()
254
- } catch (e) {
255
- localIndex = 0;
256
- localHash = genesisCID;
257
- await chainStore.put('localIndex', 0)
258
- await chainStore.put('localBlock', genesisCID)
259
- }
260
-
261
- set = set.reduce((p, c) => {
262
- if (Number(c.height) > Number(p.height)) {
263
- c.seen = 1
264
- return c
265
- } else if (Number(c.height) === Number(p.height)) {
266
- p.seen += 1
267
- }
268
- return p
269
- }, { height: localIndex, hash: localHash, seen: 0})
270
-
271
- // temp
272
- // if (_blocks.seen < 2) {
273
- // _blocks = history[_blocks.block.index - 1]
274
- //
275
- // }
276
- // const localIndex = await chainStore.get('localIndex')
277
- // const localHash = await chainStore.get('localBlock')
278
- return resolve({index: set.height, hash: set.hash})
279
-
280
- } catch (e) {
281
- console.warn(e);
282
- // debug(e)
283
- reject(e)
284
- }
285
- })
286
- }
287
-
288
- /**
289
- * Resolve latest block from the longest chain.
290
- * @return Promise(resolve(block))
291
- */
292
- lastBlock() {
293
- return new Promise(async (resolve, reject) => {
294
- const result = await this.longestChain();
295
-
296
- resolve(result); // retrieve links
297
- });
298
- }
299
-
300
- /**
301
- * @param {String} address
302
- * @return {Function} newBlock
303
- */
304
- async nextBlock(address) {
305
- let transactions;
306
- let previousBlock;
307
- try {
308
- previousBlock = await this.lastBlock()
309
-
310
- if (previousBlock.index > chain.length - 1) {
311
- await leofcoin.api.chain.sync()
312
- previousBlock = await this.lastBlock()
313
- }
314
- if (!previousBlock.index) previousBlock = chain[chain.length - 1]
315
- transactions = await this.nextBlockTransactions();
316
- } catch (e) {
317
- previousBlock = GENESISBLOCK
318
- previousBlock.hash = genesisCID
319
- transactions = await this.nextBlockTransactions();
320
- } finally {
321
- // console.log(transactions, previousBlock, address);
322
- return await this.newBlock({transactions, previousBlock, address});
323
- }
324
- }
325
-
326
- /**
327
- * Create new block
328
- *
329
- * @param {Array} transactions - transactions
330
- * @param {Object} previousBlock - previousBlock
331
- * @param {String} address - address
332
- * @return {Object} index, prevHash, time, transactions, nonce
333
- */
334
- async newBlock({transactions = [], previousBlock, address}) {
335
- const index = previousBlock.index + 1
336
- const minedTx = await this.createRewardTransaction(address, this.consensusSubsidy(index))
337
- transactions.push(minedTx.toJSON());
338
- this.data = {
339
- index,
340
- prevHash: previousBlock.hash,
341
- time: Math.floor(new Date().getTime() / 1000),
342
- transactions,
343
- nonce: 0
344
- }
345
-
346
- this.data.hash = await this.blockHash({...this.data});
347
-
348
- return this.data;
349
- }
350
- }
package/src/errors.js DELETED
@@ -1,19 +0,0 @@
1
- const invalid = (name, text) => new Error(`Invalid ${name}: ${text}`);
2
- // TODO: show notification
3
- /**
4
- * @example
5
- * const errors = new Errors()
6
- */
7
- export default class Errors {
8
- BlockError(text) {
9
- return invalid('block', text)
10
- }
11
-
12
- TransactionError(text) {
13
- return invalid('transaction', text)
14
- }
15
-
16
- MinerWarning(text) {
17
- new Error(`warning @Miner: ${text}`)
18
- }
19
- }
package/src/hash.js DELETED
@@ -1,60 +0,0 @@
1
- import IPLDLFCTx from 'ipld-lfc-tx'
2
- import ipldLfc from 'ipld-lfc'
3
- import CID from 'cids'
4
- import Errors from './errors'
5
- import cryptoJs from 'crypto-js';
6
-
7
- const { LFCNode, util } = ipldLfc
8
- const {SHA256} = cryptoJs
9
-
10
- const _SHA256 = (object) => {
11
- return SHA256(JSON.stringify(object)).toString();
12
- }
13
-
14
- /**
15
- * @extends {Validator}
16
- * @example
17
- * const hash = new Hash()
18
- */
19
- export default class Hash extends Errors {
20
- constructor() {
21
- super()
22
- }
23
-
24
- hashFromMultihash(multihash) {
25
- const cid = new CID(multihash.replace('/ipfs/', ''))
26
- return cid.multihash.slice(cid.prefix.length / 2).toString('hex')
27
- }
28
-
29
- multihashFromHash(hash) {
30
- const cid = new CID(1, 'leofcoin-block', Buffer.from(`1d40${hash}`, 'hex'), 'base58btc');
31
- return cid.toBaseEncodedString();
32
- }
33
-
34
- async blockHash(block) {
35
- const cid = await util.cid(await util.serialize(await new LFCNode({...block})))
36
- return cid.toBaseEncodedString();
37
- }
38
-
39
- /**
40
- * Generate transaction hash
41
- *
42
- * @param {object} transaction {id, type, inputs, outputs}
43
- */
44
- async transactionHash(transaction) {
45
- const tx = await new IPLDLFCTx.LFCTx(transaction)
46
- const cid = await IPLDLFCTx.util.cid(await tx.serialize())
47
- return cid.toBaseEncodedString()
48
- }
49
-
50
- /**
51
- * Generate transaction input hash
52
- *
53
- * @param {object} transactionInput {transaction, index, amount, address}
54
- */
55
- transactionInputHash(transactionInput) {
56
- const {tx, index, amount, address} = transactionInput;
57
- return _SHA256({tx, index, amount, address});
58
- }
59
-
60
- }
package/src/index.js DELETED
@@ -1,3 +0,0 @@
1
- import Chain from './chain'
2
-
3
- export default Chain
package/src/params.js DELETED
@@ -1,18 +0,0 @@
1
- export const reward = 150;
2
- export const consensusSubsidyInterval = 52500;
3
- export const consensusSubsidyPercentage = 12.5; // quarterlings
4
- export const genesisCID = 'zsNS6wZiHSc2QPHmjV8TMNn798b4Kp9jpjsBNeUkPhaJTza3GosWUgE72Jy3X9jKMrFCcDni7Pq4yXogQN4TcAfrPmTXFt';
5
- export const GENESISBS58 = 'EKBqhKsFG14thpzEKUbbb94WG9a1p9sm82YQ2YwK6wkme4C1df2dMwggnDgh74zZsBx2UbWi1qDQLysrrU3YKJ6V73sLtASepYieNpx8Fwjxyjh6xWNYToTNbNkp1geiSWq6fne7Npf37HCZVmi';
6
- export const GENESISBLOCK = {
7
- index: 0,
8
- prevHash: Buffer.alloc(47).toString('hex'),
9
- time: 1590240964,
10
- transactions: [],
11
- nonce: 1077701
12
- }
13
-
14
- /**
15
- * network (hardcoded for now)
16
- * @dev change to 'leofcoin:olivia' for testnet
17
- */
18
- export const network = 'leofcoin'