-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
336 lines (288 loc) · 9.16 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import fs from "fs";
import { LoggerFactory, WarpFactory } from "warp-contracts";
import createBrowserless from "browserless";
import getHTML from "html-get";
import Bundlr from "@bundlr-network/client";
import Arweave from "arweave";
// Spawn Chromium process once
const browserlessFactory = createBrowserless();
async function main() {
// let res = await (await fetch("https://node1.bundlr.network")).json();
// Warp and Arweave initialization
LoggerFactory.INST.logLevel("error");
const warp = WarpFactory.forMainnet();
const arweave = warp.arweave;
let wallet = JSON.parse(fs.readFileSync("../wallet.json").toString());
const walletAddress = await arweave.wallets.jwkToAddress(wallet);
// we setup the warp contracts
let archivor = warp
.contract("tD0FBLuXf2ZsnTRdwXRvfXbjPO0gBPpw3TnXzWYJ_cA")
.setEvaluationOptions({
internalWrites: true,
})
.connect(wallet);
var args = process.argv.slice(2);
if (args[0] === "order") {
let amount = 100;
let order = await archivor.writeInteraction({
function: "createOrder",
orderAction: {
website: "https://www.ethlisbon.org/",
amount_to_transfer: amount,
// 10 second
frequency: 10,
duration: 100,
},
});
console.log("order deployed");
console.log(order);
process.exit(0);
}
const bundlr = new Bundlr.default(
"http://node1.bundlr.network",
"arweave",
wallet
);
console.log(await bundlr.getLoadedBalance());
// now we fetch the latest state
let state = await reloadState(archivor);
console.log(state);
loop1: while (true) {
let orders = state.openOrders;
console.log("Init new round, there are ", orders.length, "orders");
// THIS IS THE UPLOADER LOOP
console.log("Entering the Uploader Loop");
loop2: for (let [orderID, order] of orders.entries()) {
// if it's not our epoch, we continue
if (order.next_upload_after > Date.now() / 1000) {
console.log("skip, nothing to do");
continue;
}
if (order.balance <= 0) {
console.log("balance is 0");
continue loop2;
}
// we upload
try {
// we do a drywrite first, to see if we can deploy it
// if not, this keeps going
let dw = await archivor.dryWrite({
function: "declareUpload",
declareUpload: {
txId: "example",
order_id: orderID,
},
});
if (dw.errorMessage) {
console.log("Dry write returned error, skipping");
continue;
}
console.log("Getting website source for ", order.website);
let data = await getWebpageSource(order.website);
let hash = Arweave.utils.bufferTob64Url(
await Arweave.crypto.hash(Buffer.from(data), "SHA-256")
);
const tags = [
{ name: "Content-Type", value: "text/html" },
{ name: "Deployer", value: walletAddress },
{ name: "App-Name", value: "archive-the-web" },
{
name: "Timestamp",
value: Math.round(Date.now() / 1000).toString(),
},
{ name: "Sig-Type", value: "arweave" },
{ name: "Sig", value: "" },
{ name: "Data-SHA256", value: hash },
{ name: "Original-URL", value: order.website },
];
console.log("Deploying to Bundlr");
const transaction = bundlr.createTransaction(data, { tags: tags });
await transaction.sign();
const id = (await transaction.upload()).data.id;
console.log("Deployed to Bundlr with id", id);
console.log("Declaring upload to contract");
// now we tell warp we uploaded
await archivor.writeInteraction({
function: "declareUpload",
declareUpload: {
txId: id,
order_id: orderID,
},
});
console.log("Upload declared");
} catch (e) {
console.log("error", e);
continue;
}
state = await reloadState(archivor);
// THIS IS THE VALIDATOR LOOP
// THIS IS THE VALIDATOR LOOP
// THIS IS THE VALIDATOR LOOP
// THIS IS THE VALIDATOR LOOP
// THIS IS THE VALIDATOR LOOP
console.log("Entering the Validator Loop");
for (let [orderID, order] of orders.entries()) {
let claims = order.claims;
for (let [claimID, claim] of claims.entries()) {
// check if we voted
if (claim.voted[walletAddress] || claim.claimed) {
continue;
}
if (claim.claim_release_timestamp < Date.now() / 1000) {
continue;
}
// we fetch the tx info using the tag
let txid = claim.claimer_tx;
// we do a graphql query
let res = await fetch("https://arweave.net/graphql", {
method: "post",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: `query {
transactions(ids: ["${txid}"], sort: HEIGHT_DESC) {
edges {
node {
id
tags {
name
value
}
block {
height
timestamp
}
parent { id }
bundledIn { id }
}
cursor
}
}
}`,
}),
});
let jsonResult = await res.json();
let tags = jsonResult.data.transactions.edges[0].node.tags;
let hashTag = tags.filter((obj) => {
return obj.name === "Data-SHA256";
})[0];
// now we get the website data
let source = await getWebpageSource(order.website);
// we hash it
let hash = Arweave.utils.bufferTob64Url(
await Arweave.crypto.hash(Buffer.from(source), "SHA-256")
);
// TODO check this
if (hash != hashTag.value) {
console.log(
"hashes from Website and Tag do not match",
hash,
hashTag.value
);
}
let res2 = await fetch(`https://arweave.net/${txid}`);
let dataOnArweave = await res2.text();
let hashDataOnArweave = Arweave.utils.bufferTob64Url(
await Arweave.crypto.hash(Buffer.from(dataOnArweave), "SHA-256")
);
// TODO check this
if (hash != hashDataOnArweave) {
console.log(
"hashes from data on arweave and Tag do not match",
hash,
hashDataOnArweave
);
}
await sleep(500);
}
}
state = await reloadState(archivor);
// THIS IS THE CLAIM LOOP
// THIS IS THE CLAIM LOOP
// THIS IS THE CLAIM LOOP
// THIS IS THE CLAIM LOOP
// THIS IS THE CLAIM LOOP
console.log("Entering the Claiming Loop");
loopOrderClaim: for (let [orderID, order] of orders.entries()) {
let claims = order.claims;
loopClaim: for (let [claimID, claim] of claims.entries()) {
// check if it was claimed
if (claim.claimed) {
continue loopClaim;
}
if (claim.claim_release_timestamp > Date.now() / 1000) {
continue loopClaim;
}
try {
let dw = await archivor.dryWrite({
function: "claimUpload",
claimUpload: {
order_id: orderID,
claim_index: claimID,
},
});
if (dw.errorMessage) {
console.log("error in dw claim, continuing");
continue loopClaim;
}
console.log(
"Claiming bounty for upload with order id",
orderID,
"and claimID",
claimID
);
await archivor.writeInteraction({
function: "claimUpload",
claimUpload: {
order_id: orderID,
claim_index: claimID,
},
});
} catch (e) {
console.log(e);
}
}
}
}
await sleep(30000);
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function getWebpageSource(url) {
try {
let content = await getContent(url);
return content.html;
} catch (e) {
console.log(e);
throw new Error(e);
}
}
const getContent = async (url) => {
// create a browser context inside Chromium process
const browserContext = browserlessFactory.createContext();
const getBrowserless = () => browserContext;
const result = await getHTML(url, { getBrowserless });
// close the browser context after it's used
await getBrowserless((browser) => browser.destroyContext());
return result;
};
async function reloadState(archivor) {
return (
await archivor.viewState({
function: "getState",
})
).state;
}
(async () => {
try {
process.on("exit", () => {
browserlessFactory.close();
});
await main();
} catch (e) {
// Deal with the fact the chain failed
console.log(e);
}
// `text` is not available here
})();