You don't have to, but if you do it, you have interesting features:
28 |
29 |
30 |
Immutability: Once you upload a file on the Blockchain it can't be edited.
31 |
Time proof: You can claim you had a file in a precise moment of the past. And the transaction is is your proof.
32 |
It's free: It doesn't cost EOS to upload a file, but you need enough EOS in staking in CPU and NET.
33 |
34 |
35 |
How does EOSfilestore work?
36 |
37 | EOSfilestore split the file in several connected action txs stored on EOS blockchain and it rebuild the file starting from the first one.
38 |
39 |
40 |
How many EOS do I need to upload XX kb?
41 |
42 | EOSfilestore has been tested mainly for small (under 1Mb files). NET quantity is predictable per transaction, but CPU is variable. You can test with a small file and then do a estimate with https://www.eosrp.io/#calc .
43 |
44 |
45 |
Are the files uploaded by EOSfilestore encrypted?
46 |
47 | No. EOS is a public blockchain so also the file content is readable by anyone. You can encrypt the file before the upload.
48 |
49 |
50 |
51 |
52 |
53 |
54 | >
55 | )
56 | }
57 | }
58 |
59 | export default InfoPage
--------------------------------------------------------------------------------
/src/containers/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
93 |
--------------------------------------------------------------------------------
/src/stores/file.tsx:
--------------------------------------------------------------------------------
1 | import { observable, action } from 'mobx'
2 | import { fetchTx, estimateBlob } from '../eosfilestore/core'
3 | import { notificationStore } from '.';
4 |
5 | import { doTx } from '../eosfilestore/core'
6 | import { userStore } from '../stores';
7 | import { splitString, createPayload } from '../eosfilestore/utils';
8 |
9 | // import { counterStore, routingStore } from '.'
10 |
11 | // interface
12 |
13 | class FileStore {
14 | @observable transactions: any = []
15 | @observable newTxid = ''
16 | @observable blob = ''
17 | @observable fileMetadata = {
18 | block_time: '...',
19 | cpu_usage_us: '...',
20 | net_usage_words: '...',
21 | num_txs: '...',
22 | upload_by: '...',
23 | }
24 | @observable isLoading = false
25 | @observable counter = Number(location.href.split(/\//)[4]) || 1
26 | @observable isOk = true
27 | @observable isErrorState = false
28 |
29 | @action setTxid(txid: string) {
30 | this.newTxid = txid
31 | // async start here
32 | }
33 |
34 | @action setBlob(blob: string, estimate?: boolean) {
35 | this.blob = blob
36 | const estData = estimateBlob(blob)
37 | if (estimate === true) {
38 | this.fileMetadata = {
39 | block_time: 'n/a',
40 | cpu_usage_us: estData.cpu_usage_us,
41 | net_usage_words: estData.net_usage_words,
42 | num_txs: estData.num_txs,
43 | upload_by: '...'
44 | }
45 | }
46 | // async start here
47 | }
48 |
49 | // NOTE: Upload should spin load until all tx uploaded
50 | @action async uploadFile() {
51 | const fileB64 = this.blob
52 | const chunks = splitString(fileB64)
53 | let nextTx: string | null = null
54 | let counter = 0
55 |
56 | notificationStore.push({ title: 'Scatter is opening..', message: 'You need to confirm all the windows to successfully upload the file.' })
57 |
58 | for (const chunk of chunks.reverse()) {
59 |
60 | try {
61 | const memo = createPayload(chunk, nextTx)
62 | const doneTx: any = await doTx(memo, userStore.account)
63 | const cpu = doneTx.processed.receipt.cpu_usage_us
64 | const net = doneTx.processed.receipt.net_usage_words
65 |
66 | // NOTE: needed for better cli formatting
67 | console.log(`${counter}. ${nextTx}
68 | ${doneTx.transaction_id} cpu: ${cpu} net:${net}`)
69 | nextTx = doneTx.transaction_id
70 | counter += 1
71 | } catch (err) {
72 | console.log(`eosfilestore ERROR: ${JSON.stringify(err, null, 2)}`)
73 | return
74 | }
75 |
76 | }
77 | if (nextTx) {
78 | fileStore.setTxid(nextTx)
79 | }
80 | console.log(`Done, uploaded in ${nextTx}`)
81 | // async start here
82 | }
83 |
84 | @action fetchData() {
85 | this.isLoading = true
86 | this.isErrorState = false
87 | this.blob = ''
88 | const context = this
89 | function callback(tx: any) {
90 | context.transactions.push(tx)
91 | }
92 | fetchTx(this.newTxid, callback).then(({ data, fileMetadata }) => {
93 | console.log('ddd', data)
94 | this.blob = data.slice(9)
95 | this.isLoading = false
96 | this.fileMetadata = fileMetadata
97 | console.log('f', fileMetadata)
98 | // window.open(data.slice(9,))
99 | // return false
100 | // const red = Base64.toByteArray(data)
101 | // window.open(data.slice(9,))
102 | // console.log(red)
103 | // exists()
104 | }).catch(e => {
105 | this.isLoading = false
106 | this.isErrorState = true
107 | notificationStore.push({ message: 'Error fetching tx via API' })
108 | console.error('ERROR: ', e)
109 | })
110 | }
111 |
112 | @action cleanTxid() {
113 | this.newTxid = ''
114 | this.blob = ''
115 | }
116 |
117 | @action reset() {
118 | this.blob = ''
119 | this.newTxid = ''
120 | this.transactions = []
121 | }
122 |
123 | }
124 | export const fileStore = new FileStore()
--------------------------------------------------------------------------------
/src/registerServiceWorker.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable:no-console
2 | // In production, we register a service worker to serve assets from local cache.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on the 'N+1' visit to a page, since previously
7 | // cached resources are updated in the background.
8 |
9 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
10 | // This link also includes instructions on opting out of this behavior.
11 |
12 | const isLocalhost = Boolean(
13 | window.location.hostname === 'localhost' ||
14 | // [::1] is the IPv6 localhost address.
15 | window.location.hostname === '[::1]' ||
16 | // 127.0.0.1/8 is considered localhost for IPv4.
17 | window.location.hostname.match(
18 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
19 | )
20 | );
21 |
22 | export default function register() {
23 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
24 | // The URL constructor is available in all browsers that support SW.
25 | const publicUrl = new URL(
26 | process.env.PUBLIC_URL!,
27 | window.location.toString()
28 | );
29 | if (publicUrl.origin !== window.location.origin) {
30 | // Our service worker won't work if PUBLIC_URL is on a different origin
31 | // from what our page is served on. This might happen if a CDN is used to
32 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
33 | return;
34 | }
35 |
36 | window.addEventListener('load', () => {
37 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
38 |
39 | if (isLocalhost) {
40 | // This is running on localhost. Lets check if a service worker still exists or not.
41 | checkValidServiceWorker(swUrl);
42 |
43 | // Add some additional logging to localhost, pointing developers to the
44 | // service worker/PWA documentation.
45 | navigator.serviceWorker.ready.then(() => {
46 | console.log(
47 | 'This web app is being served cache-first by a service ' +
48 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
49 | );
50 | });
51 | } else {
52 | // Is not local host. Just register service worker
53 | registerValidSW(swUrl);
54 | }
55 | });
56 | }
57 | }
58 |
59 | function registerValidSW(swUrl: string) {
60 | navigator.serviceWorker
61 | .register(swUrl)
62 | .then(registration => {
63 | registration.onupdatefound = () => {
64 | const installingWorker = registration.installing;
65 | if (installingWorker) {
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the old content will have been purged and
70 | // the fresh content will have been added to the cache.
71 | // It's the perfect time to display a 'New content is
72 | // available; please refresh.' message in your web app.
73 | console.log('New content is available; please refresh.');
74 | } else {
75 | // At this point, everything has been precached.
76 | // It's the perfect time to display a
77 | // 'Content is cached for offline use.' message.
78 | console.log('Content is cached for offline use.');
79 | }
80 | }
81 | };
82 | }
83 | };
84 | })
85 | .catch(error => {
86 | console.error('Error during service worker registration:', error);
87 | });
88 | }
89 |
90 | function checkValidServiceWorker(swUrl: string) {
91 | // Check if the service worker can be found. If it can't reload the page.
92 | fetch(swUrl)
93 | .then(response => {
94 | // Ensure service worker exists, and that we really are getting a JS file.
95 | if (
96 | response.status === 404 ||
97 | response.headers.get('content-type')!.indexOf('javascript') === -1
98 | ) {
99 | // No service worker found. Probably a different app. Reload the page.
100 | navigator.serviceWorker.ready.then(registration => {
101 | registration.unregister().then(() => {
102 | window.location.reload();
103 | });
104 | });
105 | } else {
106 | // Service worker found. Proceed as normal.
107 | registerValidSW(swUrl);
108 | }
109 | })
110 | .catch(() => {
111 | console.log(
112 | 'No internet connection found. App is running in offline mode.'
113 | );
114 | });
115 | }
116 |
117 | export function unregister() {
118 | if ('serviceWorker' in navigator) {
119 | navigator.serviceWorker.ready.then(registration => {
120 | registration.unregister();
121 | });
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/containers/eosfilestore.svg:
--------------------------------------------------------------------------------
1 |
2 |
153 |
--------------------------------------------------------------------------------
/src/containers/UploadPage.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | // import * as ReactDOM from 'react-dom';
3 | import { Icon, FileInput, Callout, Intent, Button } from "@blueprintjs/core";
4 | import { observer, inject } from 'mobx-react';
5 | import { fileStore } from '../stores';
6 | import {
7 | arrayBufferToBase64,
8 | // splitString,
9 | // createPayload
10 | } from '../eosfilestore/utils'
11 | // import { doTx } from '../eosfilestore/core'
12 | // import { userStore } from '../stores';
13 |
14 | // import { counterStore, routingStore } from '../stores'
15 |
16 | // gif b5407140a0f7b6365b2dcf1731f6ad50ee0502d052ecdb9da01700ecd398f759
17 | // a jpg 90edc50fac5a622820404e24f6839c7b9ca2bff73a3b1a11221caf85334aa6e6
18 | // sh 987c4c9995e717df08ce82f4b24a2f814749f92113e7458124f29607cad0b77d
19 | // welcome txt 9a9e4d3637cbecea36d7cf54d0cf8a7e8046f0b893a1d880800ec8312c7d9eb4
20 | @inject('userStore')
21 | @observer
22 | class UploadPage extends React.Component{
23 | componentDidMount() {
24 | // just in case it comes from DownloadPage
25 | fileStore.reset()
26 | }
27 |
28 | render() {
29 |
30 |
31 |
32 | return (
33 | <>
34 |
35 |
36 |
37 |
38 | Select the file to upload
39 |
40 |
41 | Please read Info & FAQ before the usage, and try it first with a small file of a few Kb.
42 |
108 | Congratulation! The file has been correctly downloaded from the EOS blockchain.
109 | Please open in new tab the link contained in the icon (via tap or right click)
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | Upload date: {fileStore.fileMetadata.block_time}
119 | Account: {fileStore.fileMetadata.upload_by}
120 | Total CPU: {fileStore.fileMetadata.cpu_usage_us}
121 | Total NET: {fileStore.fileMetadata.net_usage_words}
122 | Share direct link
123 |
124 |
125 |
126 |
127 |
File preview
128 |
129 | The preview supports only images and videos
130 |