-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathindex.js
154 lines (132 loc) · 4.83 KB
/
index.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
// QuantumLedger Project
// Database that stores the current state. Can be recreated by replaying the history.
var ledgerStateFile = 'ledgerState.txt';
// Stores every transaction ever applied. In correct consensus-order. Append-only.
var ledgerHistoryFile = 'ledgerHistory.txt';
var fs = require('fs');
var crypto = require('crypto');
var Datastore = require('nedb');
var createToken = require('./jwt').createToken;
var parseToken = require('./jwt').parseToken;
var safeEval = require('safe-eval')
var ledgerState = new Datastore({filename: ledgerStateFile});
ledgerState.ensureIndex({fieldName: '__publicKey'});
ledgerState.loadDatabase();
var count = 0;
function init() {
return new Promise(function(resolve, reject) {
var i;
// This is just to count the current ledger index
fs.createReadStream(ledgerHistoryFile)
.on('error', function(err) { reject(err) })
.on('data', function(chunk) {
for (i=0; i < chunk.length; ++i) {
if (chunk[i] == 10) count++;
}
})
.on('end', function() {
var ledger = {
index: count
}
resolve(ledger);
});
})
}
function serializeAndSign(tx, privateKey, passPhrase) {
if (!tx.publicKey) tx.publicKey = ''; // TODO: extract from privateKey
var payload = {
contract: tx.contract.toString(),
args: tx.args,
iss: tx.publicKey.toString()
}
return createToken(payload, privateKey, passPhrase);
}
function runContractInVm(promiseFn, globals) {
// TODO: Remove all possible code injection exploits, evil CPU locking code, etc. Into the rabbit hole and beyond.
var code = 'new Promise(' + promiseFn + ')';
// TODO: start new ACID Transaction
return safeEval(code, globals)
.then(function(result) {
// TODO: commit transaction
return Promise.resolve(result);
})
.catch(function(err) {
// TODO: rollback transaction
throw err;
});
}
// XXX: This is not final at all. TODO: Switch to postgres
function getLedgerInterface(database, publicKey) {
// Update variables of contract
var update = function(select, updates, callback) {
return new Promise(function(resolve, reject) {
if (!updates.$set) updates.$set = {}
delete updates.__publicKey;
delete updates.__type;
delete updates.__contract;
if (updates.$set) {
delete updates.$set.__publicKey;
delete updates.$set.__type;
delete updates.$set.__contract;
}
// XXX: there might be more vectors of changing private keys. switch to a different method.
select.__publicKey = publicKey;
database.update(select, updates, {upsert: true}, function(err, numUpdated) {
if (err) reject(err);
else resolve(numUpdated);
});
})
}
// Interact with another contract
var call = function(contractKey, args) {
return new Promise(function(resolve, reject) {
var contract = database.findOne({__publicKey: otherPublicKey, __type: 'contract'}, function(err, result) {
if (err) throw err;
var ledgerInterface = getLedgerInterface(ledgerState, otherPublicKey);
resolve(runContractInVm(result.__contract, Object.assign(args, {ledger: ledgerInterface, caller: publicKey})))
})
})
}
return {
update: update
}
}
// commitTransaction takes an array of serialized transactions. they can be parsed by parseToken.
function commitTransactions(serializedTransactions) {
var promises = serializedTransactions.map(function(serializedTransaction) {
return new Promise(function(resolve, reject) {
payload = parseToken(serializedTransaction); // Also verifies publicKey embedded in transaction Token
tx = {
contract: payload.contract, // contract is promise string, eg: "function(resolve, reject) { ... }"
publicKey: payload.iss,
args: payload.args
}
// Save contract in blockchain
ledgerState.update({__publicKey: tx.publicKey, __type: 'contract'}, {$set: {__contract: tx.contract}}, {upsert: true})
var ledgerInterface = getLedgerInterface(ledgerState, tx.publicKey);
runContractInVm(tx.contract, Object.assign(tx.args, {ledger: ledgerInterface, caller: null}))
.then(function(result) {
count++;
fs.appendFile(ledgerHistoryFile, serializedTransaction+'\n');
// console.log("@"+count+": " + encodedTx)
resolve(result);
})
.catch(reject)
})
})
// Apply all promised transactions
return Promise.all(promises)
.then(function() {
// TODO: things
})
.catch(function(err) {
console.error(err);
throw err;
})
}
module.exports = {
init: init,
commitTransactions: commitTransactions,
serializeAndSign: serializeAndSign,
templates: require('./templates')
}