forked from cerc-io/ipld-eth-server
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5fc0e7c44 | ||
|
|
6027e7dc3d | ||
|
|
d21a6e65a7 | ||
|
|
dc9081e271 | ||
|
|
1d57f75dcf | ||
|
|
c098e07d51 | ||
|
|
add6afadd7 | ||
|
|
e917ccb973 | ||
|
|
065e70185a | ||
|
|
dbcb2c0cf7 | ||
|
|
9758770579 | ||
|
|
1dee766014 | ||
|
|
0d28234804 | ||
|
|
f4b7b92dd8 | ||
|
|
d645f52e87 | ||
|
|
f4591a6beb | ||
|
|
5830df44a4 | ||
|
|
558599dd32 | ||
|
|
3fd2af61e8 | ||
|
|
2817d847ef | ||
|
|
578afaf314 | ||
|
|
611fc2c11e | ||
|
|
d842a5e796 | ||
|
|
a532e17e21 | ||
|
|
39354b2114 | ||
|
|
52b5c99760 | ||
|
|
c4876739a1 | ||
|
|
6175fc02c4 | ||
|
|
15d037b8fd | ||
|
|
30b07b8669 | ||
|
|
e2d591b3f7 | ||
|
|
fb340aca18 | ||
|
|
4d4e5eb029 | ||
|
|
7b0b613b92 | ||
|
|
74752d15aa | ||
|
|
943e2ba8e2 | ||
|
|
2b8e5ad05a | ||
|
|
1137436ac0 | ||
|
|
b9fbd2da53 | ||
|
|
96ac218218 | ||
|
|
77b7bcc94c | ||
|
|
77490e8b4b | ||
|
|
18299c76e2 | ||
|
|
0ab55ef9d8 | ||
|
|
e2bcc06f8a | ||
|
|
449d23757e | ||
|
|
ba0a5f0718 | ||
|
|
3dc1bf6955 | ||
|
|
2d4f4ad43c | ||
|
|
9f3e4f1102 | ||
|
|
6b8e48a20e | ||
|
|
fe083c12f9 | ||
|
|
78e9fbd248 | ||
|
|
421987ff72 | ||
|
|
0101c4791a | ||
|
|
5fb1cc0696 | ||
|
|
b7d6152238 | ||
|
|
63c1d22240 | ||
|
|
3ea98656e6 | ||
|
|
3320df264f | ||
|
|
bb14c01529 | ||
|
|
e2ccd3ffdb | ||
|
|
8dc31e4ca5 | ||
|
|
fb6fdfcc13 | ||
|
|
fbd4a5ca6a | ||
|
|
dfb66eb67b | ||
|
|
9b0ded692b | ||
|
|
44e7f0570f | ||
|
|
d6b84b8335 | ||
|
|
1541c62ddc | ||
|
|
4a550af756 | ||
|
|
1413533a04 | ||
|
|
c250235714 | ||
|
|
ff5472ccf5 | ||
|
|
059664ffc9 | ||
|
|
65da8cafe7 | ||
|
|
475ca3e6d1 | ||
|
|
680a86aee2 | ||
|
|
d345e6b954 | ||
|
|
9db0fb219a | ||
|
|
96162e1853 | ||
|
|
ef77688145 | ||
|
|
3bd1e518c6 | ||
|
|
43c254b5f6 | ||
|
|
eceaa0aecb | ||
|
|
b3be8aaa05 | ||
|
|
e53edb4d81 | ||
|
|
86802b25e6 | ||
|
|
35e625695e | ||
|
|
02872ce7e1 | ||
|
|
7bb1f444b5 | ||
|
|
3764662fe3 | ||
|
|
2d98d741b2 | ||
|
|
79c3078c36 | ||
|
|
5cc3871f45 | ||
|
|
2f4ed18231 | ||
|
|
290157a202 | ||
|
|
f2e853e9c9 | ||
|
|
8960cde4f7 | ||
|
|
649fd54a9f | ||
|
|
00031e2b83 | ||
|
|
060e3430c1 | ||
|
|
aa9f78a028 | ||
|
|
1d05938b25 | ||
|
|
bcf15c186d | ||
|
|
657abbb7ec | ||
|
|
f4a20de5ae | ||
|
|
834e2bcff4 | ||
|
|
e570a2c9d4 | ||
|
|
716cc3663f | ||
|
|
c8b7b30292 | ||
|
|
eb9c49a3e9 | ||
|
|
a5fc994c79 | ||
|
|
8b7ecee4ec | ||
|
|
7bd7cd3aab | ||
|
|
f15a0e872a | ||
|
|
1c208e5946 | ||
|
|
1aded75645 | ||
|
|
f81c440418 | ||
|
|
b2d5345c02 | ||
|
|
ba5d6f9b9f | ||
|
|
57bdcca43c | ||
|
|
1d4b37aca9 | ||
|
|
6ded3a6c85 | ||
|
|
2d5bd2defc | ||
|
|
c72dc273ba | ||
|
|
d47ba24373 | ||
|
|
7eddf396a5 | ||
|
|
1e2b09045a | ||
|
|
64a33e910a | ||
|
|
e7225317d4 | ||
|
|
93e7eb35c5 | ||
|
|
36b714b6df | ||
|
|
b934f6c963 | ||
|
|
f814b64543 | ||
|
|
823c57384c | ||
|
|
77b1c25bc1 | ||
|
|
b4005eff39 | ||
|
|
e5c5422edc | ||
|
|
be875c0100 | ||
|
|
aad318c67b | ||
|
|
9d2a30596e | ||
|
|
8c2a71b16f | ||
|
|
330a083749 | ||
|
|
25aa4634e9 | ||
|
|
e3e8700d34 | ||
|
|
fb360d8562 | ||
|
|
f0c5ff8077 | ||
|
|
94aefafd7c | ||
|
|
2fbf97da9d | ||
|
|
5f85a7ecf6 | ||
|
|
ea7379728e | ||
|
|
48fb5bcd27 | ||
|
|
ef3b043f97 | ||
|
|
5173edf563 | ||
|
|
ca273a026d | ||
|
|
0f765df12c | ||
|
|
5d46e02509 | ||
|
|
642e08a04b | ||
|
|
8643a7f3b6 | ||
|
|
aa2f8bd0a5 | ||
|
|
f33cc3f34b | ||
|
|
48f70d4ddf | ||
|
|
808f1b5662 | ||
|
|
5094b975fc | ||
|
|
9018e551ba | ||
|
|
4dde90447e | ||
|
|
cacd9953ee | ||
|
|
68d8e68144 | ||
|
|
8099f726c3 | ||
|
|
2e81f8d31a | ||
|
|
076903b174 | ||
|
|
da844b0b83 | ||
|
|
1412f5a7f5 | ||
|
|
5dec3d145d | ||
|
|
33ac5978f5 | ||
|
|
e52284b9ba | ||
|
|
ac66759665 | ||
|
|
308ccb5d8c | ||
|
|
7843312815 | ||
|
|
5ff05225c2 | ||
|
|
0785507a7d | ||
|
|
358575335b | ||
|
|
1b37e66d9d | ||
|
|
9e082e8a6a | ||
|
|
df09709ec5 | ||
|
|
aa395278bf | ||
|
|
7ef132c400 | ||
|
|
56ce8bdb41 | ||
|
|
1178940047 | ||
|
|
6f837f5e88 | ||
|
|
a005e0d04a | ||
|
|
74a90fd124 | ||
|
|
5a1f599a4d | ||
|
|
614ea3bba1 | ||
|
|
0b9f29c300 | ||
|
|
e26fa12af0 | ||
|
|
bf26198458 | ||
|
|
4036d9d6a0 | ||
|
|
5be205ffa6 | ||
|
|
c16ac026db | ||
|
|
e912fa6c87 | ||
|
|
67df8dea77 | ||
|
|
6880611436 | ||
|
|
3a666df294 | ||
|
|
40c3aff597 | ||
|
|
83fd76bc8a | ||
|
|
b789ab69e6 | ||
|
|
8fe273fb7b | ||
|
|
2c997921cb | ||
|
|
8b31d12716 | ||
|
|
d2db5d0ac9 | ||
|
|
03dbe5dd22 | ||
|
|
825b0762cd | ||
|
|
9ebc785d98 | ||
|
|
35c8f3561a | ||
|
|
d79cc90cb2 | ||
|
|
069646905e | ||
|
|
89faa85871 | ||
|
|
e72995605d | ||
|
|
90d685a763 | ||
|
|
0bbb7a30d1 | ||
|
|
2244d1869f | ||
|
|
13d09e15c1 | ||
|
|
905585b212 | ||
|
|
f2efbb5d01 | ||
|
|
3fa33fb767 | ||
|
|
23a21c14f3 | ||
|
|
1d4d0cbc15 | ||
|
|
b76bdc51f2 | ||
|
|
1235ec1f97 | ||
|
|
230e782e6c | ||
|
|
5356cd50bb | ||
|
|
4c81ca4d54 | ||
|
|
e7cdd6247e | ||
|
|
723c7c6244 | ||
|
|
4baea2923c | ||
|
|
b1bb646ad5 | ||
|
|
8ccdfd4835 | ||
|
|
b5099a5051 | ||
|
|
2db0ce971d | ||
|
|
1dc46640cf | ||
|
|
34393ffb3f | ||
|
|
b83c0371d9 | ||
|
|
5ebe2243d8 | ||
|
|
671a324294 | ||
|
|
3108957e5f | ||
|
|
15e044403d | ||
|
|
d702cb720c | ||
|
|
31a9017c4f | ||
|
|
79efaeb089 | ||
|
|
e033eabc28 | ||
|
|
1921c06b02 | ||
|
|
1fba4d0858 | ||
|
|
d248a1050e | ||
|
|
a31c7e9ced | ||
|
|
35719b5180 | ||
|
|
c6c070005b | ||
|
|
bc59aa4ed6 | ||
|
|
6434a7279d | ||
|
|
7f32c907fd | ||
|
|
1a208546d1 | ||
|
|
313c5fe427 | ||
|
|
db0f024088 | ||
|
|
a834e55b9f | ||
|
|
8562abd180 | ||
|
|
563832422c | ||
|
|
37f4a2d603 | ||
|
|
b454b61777 | ||
|
|
a59bd06a37 | ||
|
|
2a98a30ee9 | ||
|
|
fb3b19854b | ||
|
|
4a98b7942f | ||
|
|
e1236b4072 | ||
|
|
eba868ff00 | ||
|
|
0ded7bcd62 | ||
|
|
eb2f3c2209 | ||
|
|
0167f884b4 | ||
|
|
2bc3267cce | ||
|
|
59f767b9e1 | ||
|
|
234fff83c5 | ||
|
|
92650f0981 | ||
|
|
8f055fcead | ||
|
|
58a5f06094 | ||
|
|
2dc8ace69b | ||
|
|
184603bf49 | ||
|
|
afe8b3c999 | ||
|
|
deb64b8701 | ||
|
|
722f7f3665 | ||
|
|
25f9c6c9e3 | ||
|
|
e252229b8a | ||
|
|
62e1378e0c | ||
|
|
b8fec5e4e3 | ||
|
|
b7675316b4 | ||
|
|
a2d249ca9d | ||
|
|
4fbde836d4 | ||
|
|
65808998b3 | ||
|
|
11b5efbfe3 | ||
|
|
3fff2896aa | ||
|
|
f7c4a6736d | ||
|
|
6c055a9e12 | ||
|
|
7be070fcea | ||
|
|
2800e6df36 | ||
|
|
f6ab9382b2 | ||
|
|
031043130e | ||
|
|
4505382590 | ||
|
|
b4e16c4af5 | ||
|
|
f0d2741dea | ||
|
|
267de00f99 | ||
|
|
5c0e5592ab | ||
|
|
9c6182c356 | ||
|
|
6672ecf547 | ||
|
|
f315988507 | ||
|
|
6869330bd3 | ||
|
|
2931edc317 | ||
|
|
4166fb24dd | ||
|
|
e1de3afdfc | ||
|
|
2ff88de859 | ||
|
|
d06dddbfaa | ||
|
|
f574407bb6 | ||
|
|
8111f4ec5e | ||
|
|
3fb8e13979 | ||
|
|
deb0315a49 | ||
|
|
66d695d93b | ||
|
|
a8a8fe4ac2 | ||
|
|
9831badc2a | ||
|
|
8e166f2a56 | ||
|
|
639c561fdf | ||
|
|
9226e53e0b | ||
|
|
0cc90a1f80 | ||
|
|
18f47b7b6a | ||
|
|
045d78be25 | ||
|
|
87252f42b9 | ||
|
|
e637024cce | ||
|
|
7cd66b8775 | ||
|
|
7cbad1d89f | ||
|
|
963479fdc0 | ||
|
|
b09b8a8735 | ||
|
|
70fcc22a00 | ||
|
|
a577811e0a | ||
|
|
8c4a4d6587 | ||
|
|
ee244ac6f5 | ||
|
|
dc06991605 | ||
|
|
1b4a901892 | ||
|
|
9de631f3a7 | ||
|
|
34f8e16c11 | ||
|
|
36533f7c3f | ||
|
|
20ce0ab852 | ||
|
|
9bb2f27a69 | ||
|
|
ba4e79fc63 | ||
|
|
c21c069b69 | ||
|
|
eea0b0bdca | ||
|
|
a596966b7c | ||
|
|
37e2673a8d | ||
|
|
dc30099a7c | ||
|
|
3466a51b4d | ||
|
|
8bbd65eabd | ||
|
|
749db40b96 | ||
|
|
2b151c2ba3 | ||
|
|
3897b288af | ||
|
|
b96a1ee1c6 | ||
|
|
f83e996ab8 | ||
|
|
4fa19be90a | ||
|
|
2b798e00e0 | ||
|
|
3f9b034c4c | ||
|
|
13d503b851 | ||
|
|
c568fedd89 | ||
|
|
3d6c973f6e | ||
|
|
ce91b0d9e6 | ||
|
|
b9f3b9f946 | ||
|
|
5ac76eee74 | ||
|
|
222252f89a | ||
|
|
666ea1c325 | ||
|
|
d76be4962b | ||
|
|
1883a11ab1 | ||
|
|
63dabbb051 | ||
|
|
d496dad33c | ||
|
|
cb819fa9a6 | ||
|
|
66a4e20b20 | ||
|
|
3693ed905f | ||
|
|
b6a2a278ce | ||
|
|
edc0bdf668 | ||
|
|
1b3786338f | ||
|
|
58c23c6632 | ||
|
|
38f6847ff2 | ||
|
|
7e38764618 | ||
|
|
4e40e892d2 | ||
|
|
bcd6d14fcd | ||
|
|
258035833b | ||
|
|
4b61c87b55 | ||
|
|
1373fe83a1 | ||
|
|
b13eefbce7 | ||
|
|
3005a1ad50 | ||
|
|
750754caa7 | ||
|
|
e093ad351f | ||
|
|
33e7651096 | ||
|
|
baea9740c1 | ||
|
|
4f8b057726 | ||
|
|
65e762f80b | ||
|
|
dc8d52a58d | ||
|
|
f2547b5a42 | ||
|
|
62e454b7fe | ||
|
|
ca4451d914 | ||
|
|
f51f5c64a0 | ||
|
|
987abd4b2e | ||
|
|
a3b55bfd56 | ||
|
|
ee77fc6521 | ||
|
|
85aba29cf2 | ||
|
|
2c66afbcd7 | ||
|
|
a188e1dd79 | ||
|
|
92d153b010 | ||
|
|
d7a82e353b | ||
|
|
69ad521482 | ||
|
|
d30b4ea4f2 | ||
|
|
51915f7506 | ||
|
|
ebfb4965c7 | ||
|
|
b0fff9a1dd | ||
|
|
2c092e8d04 | ||
|
|
af6190ea09 | ||
|
|
0f95267e84 | ||
|
|
f0bcf930f4 | ||
|
|
0558792282 | ||
|
|
62cca62432 | ||
|
|
7092d33338 | ||
|
|
11a65c525c | ||
|
|
3d57f6030d | ||
|
|
b124bea974 | ||
|
|
f0a7a7d999 | ||
|
|
7d6629e5b4 | ||
|
|
d761f88b30 | ||
|
|
948246cf83 | ||
|
|
e11f2c8c59 | ||
|
|
95fa6280c5 | ||
|
|
fd126c7bea | ||
|
|
f292888195 | ||
|
|
adf91df23a | ||
|
|
ede6ad1c8f | ||
|
|
ee13d5c1df | ||
|
|
eb2b3a0263 | ||
|
|
5088574437 | ||
|
|
d61f4f3cb4 | ||
|
|
1a455741ce | ||
|
|
180c2d963e | ||
|
|
2598704690 | ||
|
|
e2909797fc | ||
|
|
79765c7998 | ||
|
|
9c6549258e | ||
|
|
b4cdf76905 | ||
|
|
622ea44a6f | ||
|
|
d947c8f30a | ||
|
|
481988cb08 | ||
|
|
4580f0fc2e | ||
|
|
eba52442ec | ||
|
|
7ddf728db2 | ||
|
|
829a581727 | ||
|
|
fa03716cb2 | ||
|
|
bd3e841e21 | ||
|
|
8dc9ddf65e | ||
|
|
7d1b334bda | ||
|
|
ade1429ce3 | ||
|
|
5d1ba5903c | ||
|
|
a49f5d7582 | ||
|
|
436d9b9cb1 | ||
|
|
f7d520c933 | ||
|
|
e1a0d894a2 | ||
|
|
ba81766f6c | ||
|
|
b96f6c4d31 | ||
|
|
c5cfa85f4c | ||
|
|
702e839f5b | ||
|
|
ca354de229 |
@@ -0,0 +1,25 @@
|
||||
name: Docker Compose Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
- name: Tag docker image
|
||||
run: docker tag vulcanize/ipld-eth-server docker.pkg.github.com/vulcanize/ipld-eth-server/ipld-eth-server:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Login
|
||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login https://docker.pkg.github.com -u vulcanize --password-stdin
|
||||
- name: Docker Push
|
||||
run: docker push docker.pkg.github.com/vulcanize/ipld-eth-server/ipld-eth-server:${{steps.vars.outputs.sha}}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
name: Docker Build
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Publish Docker image
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
jobs:
|
||||
push_to_registries:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: |
|
||||
echo ::set-output name=sha::$(echo ${GITHUB_SHA:0:7})
|
||||
echo ::set-output name=tag::$(echo ${GITHUB_REF#refs/tags/})
|
||||
- name: Docker Login to Github Registry
|
||||
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login https://docker.pkg.github.com -u vulcanize --password-stdin
|
||||
- name: Docker Pull
|
||||
run: docker pull docker.pkg.github.com/vulcanize/ipld-eth-server/ipld-eth-server:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Login to Docker Registry
|
||||
run: echo ${{ secrets.VULCANIZEJENKINS_PAT }} | docker login -u vulcanizejenkins --password-stdin
|
||||
- name: Tag docker image
|
||||
run: docker tag docker.pkg.github.com/vulcanize/ipld-eth-server/ipld-eth-server:${{steps.vars.outputs.sha}} vulcanize/ipld-eth-server:${{steps.vars.outputs.tag}}
|
||||
- name: Docker Push to Docker Hub
|
||||
run: docker push vulcanize/ipld-eth-server:${{steps.vars.outputs.tag}}
|
||||
|
||||
+2
-1
@@ -7,7 +7,7 @@ Vagrantfile
|
||||
vagrant*.sh
|
||||
.vagrant
|
||||
test_scripts/
|
||||
vulcanizedb
|
||||
ipld-eth-server
|
||||
postgraphile/build/
|
||||
postgraphile/node_modules/
|
||||
postgraphile/package-lock.json
|
||||
@@ -16,3 +16,4 @@ db/migrations/20*.sql
|
||||
plugins/*.so
|
||||
postgraphile/*.toml
|
||||
postgraphile/schema.graphql
|
||||
vulcanizedb.pem
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
dist: trusty
|
||||
language: go
|
||||
go:
|
||||
- 1.11
|
||||
services:
|
||||
- postgresql
|
||||
addons:
|
||||
postgresql: '9.6'
|
||||
go_import_path: github.com/vulcanize/vulcanizedb
|
||||
before_install:
|
||||
- make installtools
|
||||
- bash ./scripts/install-postgres-10.sh
|
||||
- curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
- echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
- sudo apt-get update && sudo apt-get install yarn
|
||||
before_script:
|
||||
- sudo -u postgres createdb vulcanize_private
|
||||
- make migrate NAME=vulcanize_private
|
||||
- cd postgraphile && yarn
|
||||
script:
|
||||
- yarn test
|
||||
- cd ../
|
||||
- make test
|
||||
- make integrationtest
|
||||
notifications:
|
||||
email: false
|
||||
env:
|
||||
matrix:
|
||||
secure: GA4GiMHy868UD/C+59jRUOdJxxdLSMauNapPA31AODUMv0lcEOXg6TBJkv6Vn3OOrLHYYSpwCGiGQjF86U2vpKWzD1mSITalPfP/kNpFnrHVNf2oPpfrQs8ccs9gnFpbCNLDy0w2+55pgnrd5hvdnlsTEUS1lHWSKEZmWsSqUeS/dw22IS0+nUja0Pc3pHaWlJz2R502uEKIixjq1fOX65Z/9r1BrMqklOwWrABK0GwgYcDZwc+b3iovyDPkDa3sT/Ddz/srkcYi/Y182zdLdrknj3MVR3GiW5iSqIVm6gr427SFXF4EQVwkEBwAGoo83wRhCgCAslYTRZZ5aleb1HUtAWzuiMAd/JlAYb2jc6VJa19+rQ/5wd7Bann3hB5bY2uV+b02qFK3qWe6uqZLBSESWjFEyWJVoaXgpH0QDc0JG1R9UFxLSVa8NM3P/jgTnUgnAvdW36mTC4dg73Ha9bYdeLpuAZr6k8iTh5qp0CazKAazsdJ+ECNt6q3fkgsI/V5ZwCFfQwlauq+PiBaZsQqr7eeP19klipE3vGf6BCBRvCBdubzSgGZPIRkxOQREc24j/gzBjBNGeqoU2uOp8sXMEG5idfIyHbMU+n2Yi6IRKKPe5lwjgWEsqLkkoM2REMPIEPffRQz0f+mD48HWP0H4KjKHvubKRCE0v2ONlM4=
|
||||
+35
-18
@@ -1,24 +1,41 @@
|
||||
FROM golang:alpine as builder
|
||||
RUN apk --update --no-cache add make git g++
|
||||
FROM golang:1.13-alpine as builder
|
||||
|
||||
# Build statically linked vDB binary (wonky path because of Dep)
|
||||
RUN mkdir -p /go/src/github.com/vulcanize/vulcanizedb
|
||||
ADD . /go/src/github.com/vulcanize/vulcanizedb
|
||||
WORKDIR /go/src/github.com/vulcanize/vulcanizedb
|
||||
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' .
|
||||
RUN apk --update --no-cache add make git g++ linux-headers
|
||||
# DEBUG
|
||||
RUN apk add busybox-extras
|
||||
|
||||
# Build migration tool
|
||||
RUN go get -u -d github.com/pressly/goose/cmd/goose
|
||||
WORKDIR /go/src/github.com/pressly/goose/cmd/goose
|
||||
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -tags='no_mysql no_sqlite' -o goose
|
||||
# Build ipld-eth-server
|
||||
WORKDIR /go/src/github.com/vulcanize/ipld-eth-server
|
||||
ADD . .
|
||||
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
|
||||
|
||||
# Second stage
|
||||
# Copy migration tool
|
||||
WORKDIR /
|
||||
ARG GOOSE_VER="v2.6.0"
|
||||
ADD https://github.com/pressly/goose/releases/download/${GOOSE_VER}/goose-linux64 ./goose
|
||||
RUN chmod +x ./goose
|
||||
|
||||
# app container
|
||||
FROM alpine
|
||||
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/vulcanizedb /app/vulcanizedb
|
||||
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/environments/staging.toml /app/environments/
|
||||
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/dockerfiles/startup_script.sh /app/
|
||||
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/db/migrations/* /app/
|
||||
COPY --from=builder /go/src/github.com/pressly/goose/cmd/goose/goose /app/goose
|
||||
|
||||
ARG USER="vdm"
|
||||
ARG CONFIG_FILE="./environments/example.toml"
|
||||
|
||||
RUN adduser -Du 5000 $USER
|
||||
WORKDIR /app
|
||||
CMD ["./startup_script.sh"]
|
||||
RUN chown $USER /app
|
||||
USER $USER
|
||||
|
||||
# chown first so dir is writable
|
||||
# note: using $USER is merged, but not in the stable release yet
|
||||
COPY --chown=5000:5000 --from=builder /go/src/github.com/vulcanize/ipld-eth-server/$CONFIG_FILE config.toml
|
||||
COPY --chown=5000:5000 --from=builder /go/src/github.com/vulcanize/ipld-eth-server/entrypoint.sh .
|
||||
|
||||
|
||||
# keep binaries immutable
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipld-eth-server/ipld-eth-server ipld-eth-server
|
||||
COPY --from=builder /goose goose
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipld-eth-server/db/migrations migrations/vulcanizedb
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipld-eth-server/environments environments
|
||||
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
|
||||
Generated
-563
@@ -1,563 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:48a213e9dc4880bbbd6999309a476fa4d3cc67560aa7127154cf8ea95bd464c2"
|
||||
name = "github.com/allegro/bigcache"
|
||||
packages = [
|
||||
".",
|
||||
"queue",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f31987a23e44c5121ef8c8b2f2ea2e8ffa37b068"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a313376bcbcce8ae8bddb8089a7293e0473a0f8e9e3710d6244e09e81875ccf0"
|
||||
name = "github.com/aristanetworks/goarista"
|
||||
packages = ["monotime"]
|
||||
pruneopts = ""
|
||||
revision = "ff33da284e760fcdb03c33d37a719e5ed30ba844"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c6bf1ac7bbc0fe51637bf54d5a88ff79b171b3b42dbc665dec98303c862d8662"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = ""
|
||||
revision = "cff30e1d23fc9e800b2b5b4b41ef1817dda07e9f"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5d47691333460db6ac83ced03c79b4bdb9aff3e322be24affb7855bed8affc6c"
|
||||
name = "github.com/dave/jennifer"
|
||||
packages = ["jen"]
|
||||
pruneopts = ""
|
||||
revision = "14e399b6b5e8456c66c45c955fc27b568bacb5c9"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:aaeffbff5bd24654cb4c190ed75d6c7b57b4f5d6741914c1a7a6bb7447e756c5"
|
||||
name = "github.com/deckarep/golang-set"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "cbaa98ba5575e67703b32b4b19f73c91f3c4159e"
|
||||
version = "v1.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:90d36f5b581e95e00ced808cd48824ed6c320c25887828cce461bdef4cb7bc7c"
|
||||
name = "github.com/ethereum/go-ethereum"
|
||||
packages = [
|
||||
".",
|
||||
"accounts",
|
||||
"accounts/abi",
|
||||
"accounts/abi/bind",
|
||||
"accounts/keystore",
|
||||
"common",
|
||||
"common/bitutil",
|
||||
"common/hexutil",
|
||||
"common/math",
|
||||
"common/mclock",
|
||||
"common/prque",
|
||||
"core/rawdb",
|
||||
"core/types",
|
||||
"crypto",
|
||||
"crypto/ecies",
|
||||
"crypto/secp256k1",
|
||||
"ethclient",
|
||||
"ethdb",
|
||||
"ethdb/leveldb",
|
||||
"ethdb/memorydb",
|
||||
"event",
|
||||
"log",
|
||||
"metrics",
|
||||
"p2p",
|
||||
"p2p/discover",
|
||||
"p2p/discv5",
|
||||
"p2p/enode",
|
||||
"p2p/enr",
|
||||
"p2p/nat",
|
||||
"p2p/netutil",
|
||||
"params",
|
||||
"rlp",
|
||||
"rpc",
|
||||
"trie",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "cd79bc61a983d6482579d12cdd239b37bbfa12ef"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd"
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a01080d20c45c031c13f3828c56e58f4f51d926a482ad10cc0316225097eb7ea"
|
||||
name = "github.com/go-stack/stack"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3dd078fda7500c341bc26cfbc6c6a34614f295a2457149fc1045cab767cbcf18"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
pruneopts = ""
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2a5888946cdbc8aa360fd43301f9fc7869d663f60d5eedae7d4e6e5e4f06f2bf"
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5247b135b5492aa232a731acdcb52b08f32b874cb398f21ab460396eadbe866b"
|
||||
name = "github.com/google/uuid"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:9c776d7d9c54b7ed89f119e449983c3f24c0023e75001d6092442412ebca6b94"
|
||||
name = "github.com/hashicorp/golang-lru"
|
||||
packages = [
|
||||
".",
|
||||
"simplelru",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "0fb14efe8c47ae851c0034ed7a448854d3d34cf3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d14365c51dd1d34d5c79833ec91413bfbb166be978724f15701e17080dc06dec"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
"hcl/ast",
|
||||
"hcl/parser",
|
||||
"hcl/printer",
|
||||
"hcl/scanner",
|
||||
"hcl/strconv",
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b3c5b95e56c06f5aa72cb2500e6ee5f44fcd122872d4fec2023a488e561218bc"
|
||||
name = "github.com/hpcloud/tail"
|
||||
packages = [
|
||||
".",
|
||||
"ratelimiter",
|
||||
"util",
|
||||
"watch",
|
||||
"winfile",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "a30252cb686a21eb2d0b98132633053ec2f7f1e5"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b6e4cc26365c004808649862e22069de09594a9222143399a7a04904e9f7018c"
|
||||
name = "github.com/huin/goupnp"
|
||||
packages = [
|
||||
".",
|
||||
"dcps/internetgateway1",
|
||||
"dcps/internetgateway2",
|
||||
"httpu",
|
||||
"scpd",
|
||||
"soap",
|
||||
"ssdp",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "1395d1447324cbea88d249fbfcfd70ea878fdfca"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:76f836364ae83ed811c415aa92e1209ce49de9f62aad85b85fca749a8b96a110"
|
||||
name = "github.com/jackpal/go-nat-pmp"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c9cfead9f2a36ddf3daa40ba269aa7f4bbba6b62"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:617ee2434b77e911fa26b678730be9a617f75243b194eadc8201c8ac860844aa"
|
||||
name = "github.com/jmoiron/sqlx"
|
||||
packages = [
|
||||
".",
|
||||
"reflectx",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "0dae4fefe7c0e190f7b5a78dac28a1c82cc8d849"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a874e3ddfb9db2b42bd8c85b6875407c702fa868eed20634ff489bc896ccfd3"
|
||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "5c8c8bd35d3832f5d134ae1e1e375b69a4d25242"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:29145d7af4adafd72a79df5e41456ac9e232d5a28c1cd4dacf3ff008a217fc10"
|
||||
name = "github.com/lib/pq"
|
||||
packages = [
|
||||
".",
|
||||
"oid",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:961dc3b1d11f969370533390fdf203813162980c858e1dabe827b60940c909a5"
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c2353362d570a7bfa228149c62842019201cfb71"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:096a8a9182648da3d00ff243b88407838902b6703fc12657f76890e08d1899bf"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5219b4506253ccc598f9340677162a42d6a78f340a4cc6df2d62db4d0593c4e9"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a7fd918fb5bd2188436785c0424f8a50b4addfedf37a2b14d796be2a927b8007"
|
||||
name = "github.com/onsi/ginkgo"
|
||||
packages = [
|
||||
".",
|
||||
"config",
|
||||
"internal/codelocation",
|
||||
"internal/containernode",
|
||||
"internal/failer",
|
||||
"internal/leafnodes",
|
||||
"internal/remote",
|
||||
"internal/spec",
|
||||
"internal/spec_iterator",
|
||||
"internal/specrunner",
|
||||
"internal/suite",
|
||||
"internal/testingtproxy",
|
||||
"internal/writer",
|
||||
"reporters",
|
||||
"reporters/stenographer",
|
||||
"reporters/stenographer/support/go-colorable",
|
||||
"reporters/stenographer/support/go-isatty",
|
||||
"types",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "3774a09d95489ccaa16032e0770d08ea77ba6184"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3ecd0a37c4a90c12a97e31c398cdbc173824351aa891898ee178120bfe71c478"
|
||||
name = "github.com/onsi/gomega"
|
||||
packages = [
|
||||
".",
|
||||
"format",
|
||||
"ghttp",
|
||||
"internal/assertion",
|
||||
"internal/asyncassertion",
|
||||
"internal/oraclematcher",
|
||||
"internal/testingtsupport",
|
||||
"matchers",
|
||||
"matchers/support/goraph/bipartitegraph",
|
||||
"matchers/support/goraph/edge",
|
||||
"matchers/support/goraph/node",
|
||||
"matchers/support/goraph/util",
|
||||
"types",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "7615b9433f86a8bdf29709bf288bc4fd0636a369"
|
||||
version = "v1.4.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a5484d4fa43127138ae6e7b2299a6a52ae006c7f803d98d717f60abf3e97192e"
|
||||
name = "github.com/pborman/uuid"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "adf5a7427709b9deb95d29d3fa8a2bf9cfd388f1"
|
||||
version = "v1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:894aef961c056b6d85d12bac890bf60c44e99b46292888bfa66caf529f804457"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7143292549152d009ca9e9c493b74736a2ebd93f921bea8a4b308d7cc5edc6b3"
|
||||
name = "github.com/rjeczalik/notify"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "0f065fa99b48b842c3fd3e2c8b194c6f2b69f6b8"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:78c9cf43ddeacd0e472f412082227a0fac2ae107ee60e9112156f9371f9912cf"
|
||||
name = "github.com/rs/cors"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "3fb1b69b103a84de38a19c3c6ec073dd6caa4d3f"
|
||||
version = "v1.5.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9d57e200ef5ccc4217fe0a34287308bac652435e7c6513f6263e0493d2245c56"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "bcd833dfe83d3cebad139e4a29ed79cb2318bf95"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d0431c2fd72e39ee43ea7742322abbc200c3e704c9102c5c3c2e2e667095b0ca"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d0b38ba6da419a6d4380700218eeec8623841d44a856bb57369c172fbf692ab4"
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a1403cc8a94b8d7956ee5e9694badef0e7b051af289caad1cf668331e3ffa4f6"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "ef82de70bb3f60c65fb8eebacbb2d122ef517385"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9ceffa4ab5f7195ecf18b3a7fff90c837a9ed5e22e66d18069e4bccfe1f52aa0"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0a52bcb568386d98f4894575d53ce3e456f56471de6897bb8b9de13c33d9340e"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "9a97c102cda95a86cec2345a6f09f55a939babf5"
|
||||
version = "v1.0.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ac25ea6cc1156aca9611411274b4a0bdd83a623845df6985aab508253955cc66"
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "8fb642006536c8d3760c99d4fa2389f5e2205631"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ce5194e5afac308cc34e500cab45b4ce88a0742d689e3cf7e37b607ad76bed2f"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
packages = [
|
||||
"leveldb",
|
||||
"leveldb/cache",
|
||||
"leveldb/comparer",
|
||||
"leveldb/errors",
|
||||
"leveldb/filter",
|
||||
"leveldb/iterator",
|
||||
"leveldb/journal",
|
||||
"leveldb/memdb",
|
||||
"leveldb/opt",
|
||||
"leveldb/storage",
|
||||
"leveldb/table",
|
||||
"leveldb/util",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:59b49c47c11a48f1054529207f65907c014ecf5f9a7c0d9c0f1616dec7b062ed"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"pbkdf2",
|
||||
"scrypt",
|
||||
"sha3",
|
||||
"ssh/terminal",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "ff983b9c42bc9fbf91556e191cc8efb585c16908"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fbdbb6cf8db3278412c9425ad78b26bb8eb788181f26a3ffb3e4f216b314f86a"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"html",
|
||||
"html/atom",
|
||||
"html/charset",
|
||||
"websocket",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["errgroup"]
|
||||
pruneopts = ""
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:70d519d5cddeb60ceda2db88c24c340b1b2d7efb25ab54bacb38f57ea1998df7"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "d641721ec2dead6fe5ca284096fe4b1fcd49e427"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"encoding",
|
||||
"encoding/charmap",
|
||||
"encoding/htmlindex",
|
||||
"encoding/internal",
|
||||
"encoding/internal/identifier",
|
||||
"encoding/japanese",
|
||||
"encoding/korean",
|
||||
"encoding/simplifiedchinese",
|
||||
"encoding/traditionalchinese",
|
||||
"encoding/unicode",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"internal/utf8internal",
|
||||
"language",
|
||||
"runes",
|
||||
"transform",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd"
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
source = "gopkg.in/fsnotify/fsnotify.v1"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
digest = "1:4f830ee018eb8c56d0def653ad7c9a1d2a053f0cef2ac6b2200f73b98fa6a681"
|
||||
name = "gopkg.in/natefinch/npipe.v2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6"
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
digest = "1:a96d16bd088460f2e0685d46c39bcf1208ba46e0a977be2df49864ec7da447dd"
|
||||
name = "gopkg.in/tomb.v1"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "dd632973f1e7218eb1089048e0798ec9ae7dceb8"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/dave/jennifer/jen",
|
||||
"github.com/ethereum/go-ethereum",
|
||||
"github.com/ethereum/go-ethereum/accounts/abi",
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind",
|
||||
"github.com/ethereum/go-ethereum/common",
|
||||
"github.com/ethereum/go-ethereum/common/hexutil",
|
||||
"github.com/ethereum/go-ethereum/core/rawdb",
|
||||
"github.com/ethereum/go-ethereum/core/types",
|
||||
"github.com/ethereum/go-ethereum/crypto",
|
||||
"github.com/ethereum/go-ethereum/ethclient",
|
||||
"github.com/ethereum/go-ethereum/ethdb",
|
||||
"github.com/ethereum/go-ethereum/p2p",
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5",
|
||||
"github.com/ethereum/go-ethereum/rlp",
|
||||
"github.com/ethereum/go-ethereum/rpc",
|
||||
"github.com/hashicorp/golang-lru",
|
||||
"github.com/hpcloud/tail",
|
||||
"github.com/jmoiron/sqlx",
|
||||
"github.com/lib/pq",
|
||||
"github.com/mitchellh/go-homedir",
|
||||
"github.com/onsi/ginkgo",
|
||||
"github.com/onsi/gomega",
|
||||
"github.com/onsi/gomega/ghttp",
|
||||
"github.com/sirupsen/logrus",
|
||||
"github.com/spf13/cobra",
|
||||
"github.com/spf13/viper",
|
||||
"golang.org/x/net/context",
|
||||
"golang.org/x/sync/errgroup",
|
||||
"gopkg.in/tomb.v1",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[override]]
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
source = "gopkg.in/fsnotify/fsnotify.v1"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/pressly/sup"
|
||||
version = "0.5.3"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/onsi/ginkgo"
|
||||
version = "1.4.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/jmoiron/sqlx"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "0.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ethereum/go-ethereum"
|
||||
revision = "cd79bc61a983d6482579d12cdd239b37bbfa12ef"
|
||||
@@ -3,11 +3,6 @@ BASE = $(GOPATH)/src/$(PACKAGE)
|
||||
PKGS = go list ./... | grep -v "^vendor/"
|
||||
|
||||
# Tools
|
||||
## Dependency management
|
||||
DEP = $(BIN)/dep
|
||||
$(BIN)/dep:
|
||||
go get -u github.com/golang/dep/cmd/dep
|
||||
|
||||
## Testing library
|
||||
GINKGO = $(BIN)/ginkgo
|
||||
$(BIN)/ginkgo:
|
||||
@@ -32,7 +27,7 @@ $(BIN)/gometalinter.v2:
|
||||
|
||||
|
||||
.PHONY: installtools
|
||||
installtools: | $(LINT) $(GOOSE) $(GINKGO) $(DEP)
|
||||
installtools: | $(LINT) $(GOOSE) $(GINKGO)
|
||||
echo "Installing tools"
|
||||
|
||||
.PHONY: metalint
|
||||
@@ -46,26 +41,6 @@ metalint: | $(METALINT)
|
||||
lint:
|
||||
$(LINT) $$($(PKGS)) | grep -v -E "exported (function)|(var)|(method)|(type).*should have comment or be unexported"
|
||||
|
||||
.PHONY: test
|
||||
test: | $(GINKGO) $(LINT)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
$(GINKGO) -r --skipPackage=integration_tests,integration
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest: | $(GINKGO) $(LINT)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
$(GINKGO) -r integration_test/
|
||||
|
||||
.PHONY: dep
|
||||
dep: | $(DEP)
|
||||
$(DEP) ensure
|
||||
|
||||
build: dep
|
||||
go fmt ./...
|
||||
go build
|
||||
|
||||
#Database
|
||||
HOST_NAME = localhost
|
||||
PORT = 5432
|
||||
@@ -73,6 +48,36 @@ NAME =
|
||||
USER = postgres
|
||||
CONNECT_STRING=postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(NAME)?sslmode=disable
|
||||
|
||||
#Test
|
||||
TEST_DB = vulcanize_testing
|
||||
TEST_CONNECT_STRING = postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(TEST_DB)?sslmode=disable
|
||||
|
||||
.PHONY: test
|
||||
test: | $(GINKGO) $(LINT)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
dropdb --if-exists $(TEST_DB)
|
||||
createdb $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" up
|
||||
$(GOOSE) -dir db/migrations postgres "$(TEST_CONNECT_STRING)" reset
|
||||
make migrate NAME=$(TEST_DB)
|
||||
$(GINKGO) -r --skipPackage=integration_tests,integration
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest: | $(GINKGO) $(LINT)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
dropdb --if-exists $(TEST_DB)
|
||||
createdb $(TEST_DB)
|
||||
$(GOOSE) -dir db/migrations "$(TEST_CONNECT_STRING)" up
|
||||
$(GOOSE) -dir db/migrations "$(TEST_CONNECT_STRING)" reset
|
||||
make migrate NAME=$(TEST_DB)
|
||||
$(GINKGO) -r integration_test/
|
||||
|
||||
build:
|
||||
go fmt ./...
|
||||
GO111MODULE=on go build
|
||||
|
||||
# Parameter checks
|
||||
## Check that DB variables are provided
|
||||
.PHONY: checkdbvars
|
||||
@@ -80,7 +85,6 @@ checkdbvars:
|
||||
test -n "$(HOST_NAME)" # $$HOST_NAME
|
||||
test -n "$(PORT)" # $$PORT
|
||||
test -n "$(NAME)" # $$NAME
|
||||
test -n "$(USER)" # $$USER
|
||||
@echo $(CONNECT_STRING)
|
||||
|
||||
## Check that the migration variable (id/timestamp) is provided
|
||||
@@ -97,41 +101,36 @@ checkmigname:
|
||||
## Rollback the last migration
|
||||
.PHONY: rollback
|
||||
rollback: $(GOOSE) checkdbvars
|
||||
cd db/migrations;\
|
||||
$(GOOSE) postgres "$(CONNECT_STRING)" down
|
||||
$(GOOSE) -dir db/migrations postgres "$(CONNECT_STRING)" down
|
||||
pg_dump -O -s $(CONNECT_STRING) > db/schema.sql
|
||||
|
||||
|
||||
## Rollbackt to a select migration (id/timestamp)
|
||||
## Rollback to a select migration (id/timestamp)
|
||||
.PHONY: rollback_to
|
||||
rollback_to: $(GOOSE) checkmigration checkdbvars
|
||||
cd db/migrations;\
|
||||
$(GOOSE) postgres "$(CONNECT_STRING)" down-to "$(MIGRATION)"
|
||||
$(GOOSE) -dir db/migrations postgres "$(CONNECT_STRING)" down-to "$(MIGRATION)"
|
||||
|
||||
## Apply all migrations not already run
|
||||
.PHONY: migrate
|
||||
migrate: $(GOOSE) checkdbvars
|
||||
cd db/migrations;\
|
||||
$(GOOSE) postgres "$(CONNECT_STRING)" up
|
||||
$(GOOSE) -dir db/migrations postgres "$(CONNECT_STRING)" up
|
||||
pg_dump -O -s $(CONNECT_STRING) > db/schema.sql
|
||||
|
||||
## Create a new migration file
|
||||
.PHONY: new_migration
|
||||
new_migration: $(GOOSE) checkmigname
|
||||
cd db/migrations;\
|
||||
$(GOOSE) create $(NAME) sql
|
||||
$(GOOSE) -dir db/migrations create $(NAME) sql
|
||||
|
||||
## Check which migrations are applied at the moment
|
||||
.PHONY: migration_status
|
||||
migration_status: $(GOOSE) checkdbvars
|
||||
cd db/migrations;\
|
||||
$(GOOSE) postgres "$(CONNECT_STRING)" status
|
||||
$(GOOSE) -dir db/migrations postgres "$(CONNECT_STRING)" status
|
||||
|
||||
# Convert timestamped migrations to versioned (to be run in CI);
|
||||
# merge timestamped files to prevent conflict
|
||||
.PHONY: version_migrations
|
||||
version_migrations:
|
||||
cd db/migrations; $(GOOSE) fix
|
||||
$(GOOSE) -dir db/migrations fix
|
||||
|
||||
# Import a psql schema to the database
|
||||
.PHONY: import
|
||||
@@ -139,25 +138,7 @@ import:
|
||||
test -n "$(NAME)" # $$NAME
|
||||
psql $(NAME) < db/schema.sql
|
||||
|
||||
|
||||
# Docker actions
|
||||
## Rinkeby docker environment
|
||||
RINKEBY_COMPOSE_FILE=dockerfiles/rinkeby/docker-compose.yml
|
||||
|
||||
.PHONY: rinkeby_env_up
|
||||
rinkeby_env_up:
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) up -d geth
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) up --build migrations
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) up -d --build vulcanizedb
|
||||
|
||||
.PHONY: rinkeby_env_deploy
|
||||
rinkeby_env_deploy:
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) up -d --build vulcanizedb
|
||||
|
||||
.PHONY: dev_env_migrate
|
||||
rinkeby_env_migrate:
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) up --build migrations
|
||||
|
||||
.PHONY: rinkeby_env_down
|
||||
rinkeby_env_down:
|
||||
docker-compose -f $(RINKEBY_COMPOSE_FILE) down
|
||||
## Build docker image
|
||||
.PHONY: docker-build
|
||||
docker-build:
|
||||
docker build -t vulcanize/ipld-eth-server .
|
||||
@@ -1,140 +1,102 @@
|
||||
# Vulcanize DB
|
||||
# ipld-eth-server
|
||||
|
||||
[](https://travis-ci.org/vulcanize/vulcanizedb)
|
||||
[](https://goreportcard.com/report/github.com/vulcanize/vulcanizedb)
|
||||
|
||||
> Vulcanize DB is a set of tools that make it easier for developers to write application-specific indexes and caches for dapps built on Ethereum.
|
||||
[](https://goreportcard.com/report/github.com/vulcanize/ipld-eth-server)
|
||||
|
||||
> ipld-eth-server is the server backend for indexed ETH IPLD objects
|
||||
|
||||
## Table of Contents
|
||||
1. [Background](../staging/README.md#background)
|
||||
1. [Dependencies](../staging/README.md#dependencies)
|
||||
1. [Install](../staging/README.md#install)
|
||||
1. [Usage](../staging/README.md#usage)
|
||||
1. [Tests](../staging/README.md#tests)
|
||||
1. [API](../staging/README.md#API)
|
||||
1. [Contributing](../staging/README.md#contributing)
|
||||
1. [License](../staging/README.md#license)
|
||||
|
||||
1. [Background](#background)
|
||||
1. [Install](#install)
|
||||
1. [Usage](#usage)
|
||||
1. [Contributing](#contributing)
|
||||
1. [License](#license)
|
||||
|
||||
## Background
|
||||
The same data structures and encodings that make Ethereum an effective and trust-less distributed virtual machine
|
||||
complicate data accessibility and usability for dApp developers. VulcanizeDB improves Ethereum data accessibility by
|
||||
providing a suite of tools to ease the extraction and transformation of data into a more useful state.
|
||||
NOTE: WIP
|
||||
|
||||
ipld-eth-server is used to service queries against the indexed Ethereum IPLD objects indexed by [ipld-eth-indexer](https://github.com/vulcanize/ipld-eth-indexer).
|
||||
|
||||
It exposes standard Ethereum JSON RPC endpoints on top of the database, in some cases these endpoints can leverage the unique indexes to improve query performance.
|
||||
Additional, unique endpoints are exposed which utilize the new indexes and state diff data objects.
|
||||
|
||||
|
||||
## Dependencies
|
||||
- Go 1.11+
|
||||
- Postgres 10.6
|
||||
- Ethereum Node
|
||||
- [Go Ethereum](https://ethereum.github.io/go-ethereum/downloads/) (1.8.23+)
|
||||
- [Parity 1.8.11+](https://github.com/paritytech/parity/releases)
|
||||
Minimal build dependencies
|
||||
* Go (1.13)
|
||||
* Git
|
||||
* GCC compiler
|
||||
* This repository
|
||||
|
||||
External dependency
|
||||
* Postgres database populated by [ipld-eth-indexer](https://github.com/vulcanize/ipld-eth-indexer)
|
||||
|
||||
## Install
|
||||
1. [Building the project](../staging/README.md#building-the-project)
|
||||
1. [Setting up the database](../staging/README.md#setting-up-the-database)
|
||||
1. [Configuring a synced Ethereum node](../staging/README.md#configuring-a-synced-ethereum-node)
|
||||
Start by downloading ipld-eth-server and moving into the repo:
|
||||
|
||||
### Building the project
|
||||
Download the codebase to your local `GOPATH` via:
|
||||
`GO111MODULE=off go get -d github.com/vulcanize/ipld-eth-server`
|
||||
|
||||
`go get github.com/vulcanize/vulcanizedb`
|
||||
`cd $GOPATH/src/github.com/vulcanize/ipld-eth-server`
|
||||
|
||||
Move to the project directory and use [golang/dep](https://github.com/golang/dep) to install the dependencies:
|
||||
|
||||
`cd $GOPATH/src/github.com/vulcanize/vulcanizedb`
|
||||
|
||||
`dep ensure`
|
||||
|
||||
Once the dependencies have been successfully installed, build the executable with:
|
||||
Then, build the binary:
|
||||
|
||||
`make build`
|
||||
|
||||
If you are running into issues at this stage, ensure that `GOPATH` is defined in your shell.
|
||||
If necessary, `GOPATH` can be set in `~/.bashrc` or `~/.bash_profile`, depending upon your system.
|
||||
It can be additionally helpful to add `$GOPATH/bin` to your shell's `$PATH`.
|
||||
|
||||
### Setting up the database
|
||||
1. Install Postgres
|
||||
1. Create a superuser for yourself and make sure `psql --list` works without prompting for a password.
|
||||
1. `createdb vulcanize_public`
|
||||
1. `cd $GOPATH/src/github.com/vulcanize/vulcanizedb`
|
||||
1. Run the migrations: `make migrate HOST_NAME=localhost NAME=vulcanize_public PORT=5432`
|
||||
- There is an optional var `USER=username` if the database user is not the default user `postgres`
|
||||
- To rollback a single step: `make rollback NAME=vulcanize_public`
|
||||
- To rollback to a certain migration: `make rollback_to MIGRATION=n NAME=vulcanize_public`
|
||||
- To see status of migrations: `make migration_status NAME=vulcanize_public`
|
||||
|
||||
* See below for configuring additional environments
|
||||
|
||||
In some cases (such as recent Ubuntu systems), it may be necessary to overcome failures of password authentication from localhost. To allow access on Ubuntu, set localhost connections via hostname, ipv4, and ipv6 from peer/md5 to trust in: /etc/postgresql/<version>/pg_hba.conf
|
||||
|
||||
(It should be noted that trusted auth should only be enabled on systems without sensitive data in them: development and local test databases)
|
||||
|
||||
### Configuring a synced Ethereum node
|
||||
- To use a local Ethereum node, copy `environments/public.toml.example` to
|
||||
`environments/public.toml` and update the `ipcPath` and `levelDbPath`.
|
||||
- `ipcPath` should match the local node's IPC filepath:
|
||||
- For Geth:
|
||||
- The IPC file is called `geth.ipc`.
|
||||
- The geth IPC file path is printed to the console when you start geth.
|
||||
- The default location is:
|
||||
- Mac: `<full home path>/Library/Ethereum`
|
||||
- Linux: `<full home path>/ethereum/geth.ipc`
|
||||
|
||||
- For Parity:
|
||||
- The IPC file is called `jsonrpc.ipc`.
|
||||
- The default location is:
|
||||
- Mac: `<full home path>/Library/Application\ Support/io.parity.ethereum/`
|
||||
- Linux: `<full home path>/local/share/io.parity.ethereum/`
|
||||
|
||||
- `levelDbPath` should match Geth's chaindata directory path.
|
||||
- The geth LevelDB chaindata path is printed to the console when you start geth.
|
||||
- The default location is:
|
||||
- Mac: `<full home path>/Library/Ethereum/geth/chaindata`
|
||||
- Linux: `<full home path>/ethereum/geth/chaindata`
|
||||
- `levelDbPath` is irrelevant (and `coldImport` is currently unavailable) if only running parity.
|
||||
|
||||
|
||||
## Usage
|
||||
Usage is broken up into two processes:
|
||||
After building the binary, run as
|
||||
|
||||
### Data syncing
|
||||
To provide data for transformations, raw Ethereum data must first be synced into vDB.
|
||||
This is accomplished through the use of the `headerSync`, `sync`, or `coldImport` commands.
|
||||
These commands are described in detail [here](../staging/documentation/sync.md).
|
||||
`./ipld-eth-server serve --config=<the name of your config file.toml>`
|
||||
|
||||
### Data transformation
|
||||
Contract watchers use the raw data that has been synced into Postgres to filter out and apply transformations to specific data of interest.
|
||||
### Configuration
|
||||
|
||||
There is a built-in `contractWatcher` command which provides generic transformation of most contract data.
|
||||
The `contractWatcher` command is described further [here](../staging/documentation/contractWatcher.md).
|
||||
Below is the set of parameters for the ipld-eth-server command, in .toml form, with the respective environmental variables commented to the side.
|
||||
The corresponding CLI flags can be found with the `./ipld-eth-server serve --help` command.
|
||||
|
||||
In many cases a custom transformer or set of transformers will need to be written to provide complete or more comprehensive coverage or to optimize other aspects of the output for a specific end-use.
|
||||
In this case we have provided the `compose`, `execute`, and `composeAndExecute` commands for running custom transformers from external repositories.
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "postgres" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
Usage of the `compose`, `execute`, and `composeAndExecute` commands is described further [here](../staging/documentation/composeAndExecute.md).
|
||||
[log]
|
||||
level = "info" # $LOGRUS_LEVEL
|
||||
|
||||
Documentation on how to build custom transformers to work with these commands can be found [here](../staging/documentation/transformers.md).
|
||||
[server]
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SERVER_IPC_PATH
|
||||
wsPath = "127.0.0.1:8081" # $SERVER_WS_PATH
|
||||
httpPath = "127.0.0.1:8082" # $SERVER_HTTP_PATH
|
||||
```
|
||||
|
||||
## Tests
|
||||
- Replace the empty `ipcPath` in the `environments/infura.toml` with a path to a full node's eth_jsonrpc endpoint (e.g. local geth node ipc path or infura url)
|
||||
- Note: integration tests require configuration with an archival node
|
||||
- `createdb vulcanize_private` will create the test db
|
||||
- `make migrate NAME=vulcanize_private` will run the db migrations
|
||||
- `make test` will run the unit tests and skip the integration tests
|
||||
- `make integrationtest` will run just the integration tests
|
||||
The `database` fields are for connecting to a Postgres database that has been/is being populated by [ipld-eth-indexer](https://github.com/vulcanize/ipld-eth-indexer).
|
||||
The `server` fields set the paths for exposing the ipld-eth-server endpoints
|
||||
|
||||
## API
|
||||
[Postgraphile](https://www.graphile.org/postgraphile/) is used to expose GraphQL endpoints for our database schemas, this is described in detail [here](../staging/postgraphile/README.md).
|
||||
|
||||
### Endpoints
|
||||
#### IPLD subscription
|
||||
TODO: Port the IPLD RPC subscription endpoints after the decoupling
|
||||
|
||||
#### Ethereum JSON-RPC
|
||||
ipld-eth-server currently recapitulates portions of the Ethereum JSON-RPC api standard.
|
||||
|
||||
The currently supported standard endpoints are:
|
||||
`eth_blockNumber`
|
||||
`eth_getLogs`
|
||||
`eth_getHeaderByNumber`
|
||||
`eth_getBlockByNumber`
|
||||
`eth_getBlockByHash`
|
||||
`eth_getTransactionByHash`
|
||||
|
||||
TODO: Add the rest of the standard endpoints add unique endpoints (e.g. getSlice)
|
||||
|
||||
### Testing
|
||||
`make test` will run the unit tests
|
||||
`make test` setups a clean `vulcanize_testing` db
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome! For more on this, please see [here](../staging/documentation/contributing.md).
|
||||
|
||||
Small note: If editing the Readme, please conform to the [standard-readme specification](https://github.com/RichardLitt/standard-readme).
|
||||
Contributions are welcome!
|
||||
|
||||
VulcanizeDB follows the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct).
|
||||
|
||||
## License
|
||||
[AGPL-3.0](../staging/LICENSE) © Vulcanize Inc
|
||||
[AGPL-3.0](LICENSE) © Vulcanize Inc
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/crypto"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/ethereum"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/cold_import"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/converters/cold_db"
|
||||
vulcCommon "github.com/vulcanize/vulcanizedb/pkg/geth/converters/common"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
var coldImportCmd = &cobra.Command{
|
||||
Use: "coldImport",
|
||||
Short: "Sync vulcanize from a cold instance of LevelDB.",
|
||||
Long: `Populate core vulcanize db data directly out of LevelDB, rather than over rpc calls. For example:
|
||||
|
||||
./vulcanizedb coldImport -s 0 -e 5000000
|
||||
|
||||
Geth must be synced over all of the desired blocks and must not be running in order to execute this command.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
coldImport()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(coldImportCmd)
|
||||
coldImportCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "BlockNumber for first block to cold import.")
|
||||
coldImportCmd.Flags().Int64VarP(&endingBlockNumber, "ending-block-number", "e", 5500000, "BlockNumber for last block to cold import.")
|
||||
coldImportCmd.Flags().BoolVarP(&syncAll, "all", "a", false, "Option to sync all missing blocks.")
|
||||
}
|
||||
|
||||
func coldImport() {
|
||||
// init eth db
|
||||
ethDBConfig := ethereum.CreateDatabaseConfig(ethereum.Level, levelDbPath)
|
||||
ethDB, err := ethereum.CreateDatabase(ethDBConfig)
|
||||
if err != nil {
|
||||
log.Fatal("Error connecting to ethereum db: ", err)
|
||||
}
|
||||
mostRecentBlockNumberInDb := ethDB.GetHeadBlockNumber()
|
||||
if syncAll {
|
||||
startingBlockNumber = 0
|
||||
endingBlockNumber = mostRecentBlockNumberInDb
|
||||
}
|
||||
if endingBlockNumber < startingBlockNumber {
|
||||
log.Fatal("Ending block number must be greater than starting block number for cold import.")
|
||||
}
|
||||
if endingBlockNumber > mostRecentBlockNumberInDb {
|
||||
log.Fatal("Ending block number is greater than most recent block in db: ", mostRecentBlockNumberInDb)
|
||||
}
|
||||
|
||||
// init pg db
|
||||
genesisBlock := ethDB.GetBlockHash(0)
|
||||
reader := fs.FsReader{}
|
||||
parser := crypto.EthPublicKeyParser{}
|
||||
nodeBuilder := cold_import.NewColdImportNodeBuilder(reader, parser)
|
||||
coldNode, err := nodeBuilder.GetNode(genesisBlock, levelDbPath)
|
||||
if err != nil {
|
||||
log.Fatal("Error getting node: ", err)
|
||||
}
|
||||
pgDB := utils.LoadPostgres(databaseConfig, coldNode)
|
||||
|
||||
// init cold importer deps
|
||||
blockRepository := repositories.NewBlockRepository(&pgDB)
|
||||
receiptRepository := repositories.ReceiptRepository{DB: &pgDB}
|
||||
transactionConverter := cold_db.NewColdDbTransactionConverter()
|
||||
blockConverter := vulcCommon.NewBlockConverter(transactionConverter)
|
||||
|
||||
// init and execute cold importer
|
||||
coldImporter := cold_import.NewColdImporter(ethDB, blockRepository, receiptRepository, blockConverter)
|
||||
err = coldImporter.Execute(startingBlockNumber, endingBlockNumber, coldNode.ID)
|
||||
if err != nil {
|
||||
log.Fatal("Error executing cold import: ", err)
|
||||
}
|
||||
}
|
||||
-187
@@ -1,187 +0,0 @@
|
||||
// Copyright © 2019 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||
p2 "github.com/vulcanize/vulcanizedb/pkg/plugin"
|
||||
)
|
||||
|
||||
// composeCmd represents the compose command
|
||||
var composeCmd = &cobra.Command{
|
||||
Use: "compose",
|
||||
Short: "Composes transformer initializer plugin",
|
||||
Long: `This command needs a config .toml file of form:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
user = "vulcanize"
|
||||
password = "vulcanize"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[exporter]
|
||||
home = "github.com/vulcanize/vulcanizedb"
|
||||
name = "exampleTransformerExporter"
|
||||
save = false
|
||||
transformerNames = [
|
||||
"transformer1",
|
||||
"transformer2",
|
||||
"transformer3",
|
||||
"transformer4",
|
||||
]
|
||||
[exporter.transformer1]
|
||||
path = "path/to/transformer1"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer2]
|
||||
path = "path/to/transformer2"
|
||||
type = "eth_contract"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer3]
|
||||
path = "path/to/transformer3"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer4]
|
||||
path = "path/to/transformer4"
|
||||
type = "eth_storage"
|
||||
repository = "github.com/account2/repo2"
|
||||
migrations = "to/db/migrations"
|
||||
rank = "1"
|
||||
|
||||
|
||||
Note: If any of the plugin transformer need additional
|
||||
configuration variables include them in the .toml file as well
|
||||
|
||||
This information is used to write and build a go plugin with a transformer
|
||||
set composed from the transformer imports specified in the config file
|
||||
This plugin is loaded and the set of transformer initializers is exported
|
||||
from it and loaded into and executed over by the appropriate watcher.
|
||||
|
||||
The type of watcher that the transformer works with is specified using the
|
||||
type variable for each transformer in the config. Currently there are watchers
|
||||
of event data from an eth node (eth_event) and storage data from an eth node
|
||||
(eth_storage), and a more generic interface for accepting contract_watcher pkg
|
||||
based transformers which can perform both event watching and public method
|
||||
polling (eth_contract).
|
||||
|
||||
Transformers of different types can be ran together in the same command using a
|
||||
single config file or in separate command instances using different config files
|
||||
|
||||
Specify config location when executing the command:
|
||||
./vulcanizedb compose --config=./environments/config_name.toml`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
compose()
|
||||
},
|
||||
}
|
||||
|
||||
func compose() {
|
||||
// Build plugin generator config
|
||||
prepConfig()
|
||||
|
||||
// Generate code to build the plugin according to the config file
|
||||
log.Info("generating plugin")
|
||||
generator, err := p2.NewGenerator(genConfig, databaseConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = generator.GenerateExporterPlugin()
|
||||
if err != nil {
|
||||
log.Debug("generating plugin failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
// TODO: Embed versioning info in the .so files so we know which version of vulcanizedb to run them with
|
||||
_, pluginPath, err := genConfig.GetPluginPaths()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("Composed plugin %s", pluginPath)
|
||||
log.Info("plugin .so file output to", pluginPath)
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(composeCmd)
|
||||
}
|
||||
|
||||
func prepConfig() {
|
||||
log.Info("configuring plugin")
|
||||
names := viper.GetStringSlice("exporter.transformerNames")
|
||||
transformers := make(map[string]config.Transformer)
|
||||
for _, name := range names {
|
||||
transformer := viper.GetStringMapString("exporter." + name)
|
||||
p, pOK := transformer["path"]
|
||||
if !pOK || p == "" {
|
||||
log.Fatal(name, "transformer config is missing `path` value")
|
||||
}
|
||||
r, rOK := transformer["repository"]
|
||||
if !rOK || r == "" {
|
||||
log.Fatal(name, "transformer config is missing `repository` value")
|
||||
}
|
||||
m, mOK := transformer["migrations"]
|
||||
if !mOK || m == "" {
|
||||
log.Fatal(name, "transformer config is missing `migrations` value")
|
||||
}
|
||||
mr, mrOK := transformer["rank"]
|
||||
if !mrOK || mr == "" {
|
||||
log.Fatal(name, "transformer config is missing `rank` value")
|
||||
}
|
||||
rank, err := strconv.ParseUint(mr, 10, 64)
|
||||
if err != nil {
|
||||
log.Fatal(name, "migration `rank` can't be converted to an unsigned integer")
|
||||
}
|
||||
t, tOK := transformer["type"]
|
||||
if !tOK {
|
||||
log.Fatal(name, "transformer config is missing `type` value")
|
||||
}
|
||||
transformerType := config.GetTransformerType(t)
|
||||
if transformerType == config.UnknownTransformerType {
|
||||
log.Fatal(errors.New(`unknown transformer type in exporter config accepted types are "eth_event", "eth_storage"`))
|
||||
}
|
||||
|
||||
transformers[name] = config.Transformer{
|
||||
Path: p,
|
||||
Type: transformerType,
|
||||
RepositoryPath: r,
|
||||
MigrationPath: m,
|
||||
MigrationRank: rank,
|
||||
}
|
||||
}
|
||||
|
||||
genConfig = config.Plugin{
|
||||
Transformers: transformers,
|
||||
FilePath: "$GOPATH/src/github.com/vulcanize/vulcanizedb/plugins",
|
||||
FileName: viper.GetString("exporter.name"),
|
||||
Save: viper.GetBool("exporter.save"),
|
||||
Home: viper.GetString("exporter.home"),
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
// Copyright © 2019 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"plugin"
|
||||
syn "sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||
p2 "github.com/vulcanize/vulcanizedb/pkg/plugin"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/plugin/helpers"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
// composeAndExecuteCmd represents the composeAndExecute command
|
||||
var composeAndExecuteCmd = &cobra.Command{
|
||||
Use: "composeAndExecute",
|
||||
Short: "Composes, loads, and executes transformer initializer plugin",
|
||||
Long: `This command needs a config .toml file of form:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
user = "vulcanize"
|
||||
password = "vulcanize"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[exporter]
|
||||
home = "github.com/vulcanize/vulcanizedb"
|
||||
name = "exampleTransformerExporter"
|
||||
save = false
|
||||
transformerNames = [
|
||||
"transformer1",
|
||||
"transformer2",
|
||||
"transformer3",
|
||||
"transformer4",
|
||||
]
|
||||
[exporter.transformer1]
|
||||
path = "path/to/transformer1"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer2]
|
||||
path = "path/to/transformer2"
|
||||
type = "eth_contract"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "2"
|
||||
[exporter.transformer3]
|
||||
path = "path/to/transformer3"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer4]
|
||||
path = "path/to/transformer4"
|
||||
type = "eth_storage"
|
||||
repository = "github.com/account2/repo2"
|
||||
migrations = "to/db/migrations"
|
||||
rank = "1"
|
||||
|
||||
|
||||
Note: If any of the plugin transformer need additional
|
||||
configuration variables include them in the .toml file as well
|
||||
|
||||
This information is used to write and build a go plugin with a transformer
|
||||
set composed from the transformer imports specified in the config file
|
||||
This plugin is loaded and the set of transformer initializers is exported
|
||||
from it and loaded into and executed over by the appropriate watcher.
|
||||
|
||||
The type of watcher that the transformer works with is specified using the
|
||||
type variable for each transformer in the config. Currently there are watchers
|
||||
of event data from an eth node (eth_event) and storage data from an eth node
|
||||
(eth_storage), and a more generic interface for accepting contract_watcher pkg
|
||||
based transformers which can perform both event watching and public method
|
||||
polling (eth_contract).
|
||||
|
||||
Transformers of different types can be ran together in the same command using a
|
||||
single config file or in separate command instances using different config files
|
||||
|
||||
Specify config location when executing the command:
|
||||
./vulcanizedb composeAndExecute --config=./environments/config_name.toml`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
composeAndExecute()
|
||||
},
|
||||
}
|
||||
|
||||
func composeAndExecute() {
|
||||
// Build plugin generator config
|
||||
prepConfig()
|
||||
|
||||
// Generate code to build the plugin according to the config file
|
||||
log.Info("generating plugin")
|
||||
generator, err := p2.NewGenerator(genConfig, databaseConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = generator.GenerateExporterPlugin()
|
||||
if err != nil {
|
||||
log.Debug("generating plugin failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Get the plugin path and load the plugin
|
||||
_, pluginPath, err := genConfig.GetPluginPaths()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !genConfig.Save {
|
||||
defer helpers.ClearFiles(pluginPath)
|
||||
}
|
||||
log.Info("linking plugin", pluginPath)
|
||||
plug, err := plugin.Open(pluginPath)
|
||||
if err != nil {
|
||||
log.Debug("linking plugin failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load the `Exporter` symbol from the plugin
|
||||
log.Info("loading transformers from plugin")
|
||||
symExporter, err := plug.Lookup("Exporter")
|
||||
if err != nil {
|
||||
log.Debug("loading Exporter symbol failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Assert that the symbol is of type Exporter
|
||||
exporter, ok := symExporter.(Exporter)
|
||||
if !ok {
|
||||
log.Debug("plugged-in symbol not of type Exporter")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Use the Exporters export method to load the EventTransformerInitializer, StorageTransformerInitializer, and ContractTransformerInitializer sets
|
||||
ethEventInitializers, ethStorageInitializers, ethContractInitializers := exporter.Export()
|
||||
|
||||
// Setup bc and db objects
|
||||
blockChain := getBlockChain()
|
||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||
|
||||
// Execute over transformer sets returned by the exporter
|
||||
// Use WaitGroup to wait on both goroutines
|
||||
var wg syn.WaitGroup
|
||||
if len(ethEventInitializers) > 0 {
|
||||
ew := watcher.NewEventWatcher(&db, blockChain)
|
||||
ew.AddTransformers(ethEventInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthEvents(&ew, &wg)
|
||||
}
|
||||
|
||||
if len(ethStorageInitializers) > 0 {
|
||||
tailer := fs.FileTailer{Path: storageDiffsPath}
|
||||
storageFetcher := fetcher.NewCsvTailStorageFetcher(tailer)
|
||||
sw := watcher.NewStorageWatcher(storageFetcher, &db)
|
||||
sw.AddTransformers(ethStorageInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthStorage(&sw, &wg)
|
||||
}
|
||||
|
||||
if len(ethContractInitializers) > 0 {
|
||||
gw := watcher.NewContractWatcher(&db, blockChain)
|
||||
gw.AddTransformers(ethContractInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthContract(&gw, &wg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(composeAndExecuteCmd)
|
||||
composeAndExecuteCmd.Flags().BoolVarP(&recheckHeadersArg, "recheck-headers", "r", false, "whether to re-check headers for watched events")
|
||||
composeAndExecuteCmd.Flags().DurationVarP(&queueRecheckInterval, "queue-recheck-interval", "q", 5*time.Minute, "how often to recheck queued storage diffs")
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
st "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
ft "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/transformer"
|
||||
lt "github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
// contractWatcherCmd represents the contractWatcher command
|
||||
var contractWatcherCmd = &cobra.Command{
|
||||
Use: "contractWatcher",
|
||||
Short: "Watches events at the provided contract address using fully synced vDB",
|
||||
Long: `Uses input contract address and event filters to watch events
|
||||
|
||||
Expects an ethereum node to be running
|
||||
Expects an archival node synced into vulcanizeDB
|
||||
Requires a .toml config file:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[contract]
|
||||
network = ""
|
||||
addresses = [
|
||||
"contractAddress1",
|
||||
"contractAddress2"
|
||||
]
|
||||
[contract.contractAddress1]
|
||||
abi = 'ABI for contract 1'
|
||||
startingBlock = 982463
|
||||
[contract.contractAddress2]
|
||||
abi = 'ABI for contract 2'
|
||||
events = [
|
||||
"event1",
|
||||
"event2"
|
||||
]
|
||||
eventArgs = [
|
||||
"arg1",
|
||||
"arg2"
|
||||
]
|
||||
methods = [
|
||||
"method1",
|
||||
"method2"
|
||||
]
|
||||
methodArgs = [
|
||||
"arg1",
|
||||
"arg2"
|
||||
]
|
||||
startingBlock = 4448566
|
||||
piping = true
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
contractWatcher()
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
mode string
|
||||
)
|
||||
|
||||
func contractWatcher() {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
blockChain := getBlockChain()
|
||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||
|
||||
var t st.ContractTransformer
|
||||
con := config.ContractConfig{}
|
||||
con.PrepConfig()
|
||||
switch mode {
|
||||
case "header":
|
||||
t = lt.NewTransformer(con, blockChain, &db)
|
||||
case "full":
|
||||
t = ft.NewTransformer(con, blockChain, &db)
|
||||
default:
|
||||
log.Fatal("Invalid mode")
|
||||
}
|
||||
|
||||
err := t.Init()
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Failed to initialized transformer\r\nerr: %v\r\n", err))
|
||||
}
|
||||
|
||||
for range ticker.C {
|
||||
err = t.Execute()
|
||||
if err != nil {
|
||||
log.Error("Execution error for transformer:", t.GetConfig().Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(contractWatcherCmd)
|
||||
contractWatcherCmd.Flags().StringVarP(&mode, "mode", "o", "header", "'header' or 'full' mode to work with either header synced or fully synced vDB (default is header)")
|
||||
}
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
// Copyright © 2019 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"plugin"
|
||||
syn "sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/fetcher"
|
||||
storageUtils "github.com/vulcanize/vulcanizedb/libraries/shared/storage/utils"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/watcher"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/fs"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
// executeCmd represents the execute command
|
||||
var executeCmd = &cobra.Command{
|
||||
Use: "execute",
|
||||
Short: "executes a precomposed transformer initializer plugin",
|
||||
Long: `This command needs a config .toml file of form:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
user = "vulcanize"
|
||||
password = "vulcanize"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[exporter]
|
||||
name = "exampleTransformerExporter"
|
||||
|
||||
Note: If any of the plugin transformer need additional
|
||||
configuration variables include them in the .toml file as well
|
||||
|
||||
The exporter.name is the name (without extension) of the plugin to be loaded.
|
||||
The plugin file needs to be located in the /plugins directory and this command assumes
|
||||
the db migrations remain from when the plugin was composed. Additionally, the plugin
|
||||
must have been composed by the same version of vulcanizedb or else it will not be compatible.
|
||||
|
||||
Specify config location when executing the command:
|
||||
./vulcanizedb execute --config=./environments/config_name.toml`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
execute()
|
||||
},
|
||||
}
|
||||
|
||||
func execute() {
|
||||
// Build plugin generator config
|
||||
prepConfig()
|
||||
|
||||
// Get the plugin path and load the plugin
|
||||
_, pluginPath, err := genConfig.GetPluginPaths()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Executing plugin %s", pluginPath)
|
||||
log.Info("linking plugin", pluginPath)
|
||||
plug, err := plugin.Open(pluginPath)
|
||||
if err != nil {
|
||||
log.Debug("linking plugin failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Load the `Exporter` symbol from the plugin
|
||||
log.Info("loading transformers from plugin")
|
||||
symExporter, err := plug.Lookup("Exporter")
|
||||
if err != nil {
|
||||
log.Debug("loading Exporter symbol failed")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Assert that the symbol is of type Exporter
|
||||
exporter, ok := symExporter.(Exporter)
|
||||
if !ok {
|
||||
log.Debug("plugged-in symbol not of type Exporter")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Use the Exporters export method to load the EventTransformerInitializer, StorageTransformerInitializer, and ContractTransformerInitializer sets
|
||||
ethEventInitializers, ethStorageInitializers, ethContractInitializers := exporter.Export()
|
||||
|
||||
// Setup bc and db objects
|
||||
blockChain := getBlockChain()
|
||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||
|
||||
// Execute over transformer sets returned by the exporter
|
||||
// Use WaitGroup to wait on both goroutines
|
||||
var wg syn.WaitGroup
|
||||
if len(ethEventInitializers) > 0 {
|
||||
ew := watcher.NewEventWatcher(&db, blockChain)
|
||||
ew.AddTransformers(ethEventInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthEvents(&ew, &wg)
|
||||
}
|
||||
|
||||
if len(ethStorageInitializers) > 0 {
|
||||
tailer := fs.FileTailer{Path: storageDiffsPath}
|
||||
storageFetcher := fetcher.NewCsvTailStorageFetcher(tailer)
|
||||
sw := watcher.NewStorageWatcher(storageFetcher, &db)
|
||||
sw.AddTransformers(ethStorageInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthStorage(&sw, &wg)
|
||||
}
|
||||
|
||||
if len(ethContractInitializers) > 0 {
|
||||
gw := watcher.NewContractWatcher(&db, blockChain)
|
||||
gw.AddTransformers(ethContractInitializers)
|
||||
wg.Add(1)
|
||||
go watchEthContract(&gw, &wg)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(executeCmd)
|
||||
executeCmd.Flags().BoolVarP(&recheckHeadersArg, "recheck-headers", "r", false, "whether to re-check headers for watched events")
|
||||
executeCmd.Flags().DurationVarP(&queueRecheckInterval, "queue-recheck-interval", "q", 5*time.Minute, "how often to recheck queued storage diffs")
|
||||
}
|
||||
|
||||
type Exporter interface {
|
||||
Export() ([]transformer.EventTransformerInitializer, []transformer.StorageTransformerInitializer, []transformer.ContractTransformerInitializer)
|
||||
}
|
||||
|
||||
func watchEthEvents(w *watcher.EventWatcher, wg *syn.WaitGroup) {
|
||||
defer wg.Done()
|
||||
// Execute over the EventTransformerInitializer set using the watcher
|
||||
log.Info("executing event transformers")
|
||||
var recheck constants.TransformerExecution
|
||||
if recheckHeadersArg {
|
||||
recheck = constants.HeaderRecheck
|
||||
} else {
|
||||
recheck = constants.HeaderMissing
|
||||
}
|
||||
ticker := time.NewTicker(pollingInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
w.Execute(recheck)
|
||||
}
|
||||
}
|
||||
|
||||
func watchEthStorage(w *watcher.StorageWatcher, wg *syn.WaitGroup) {
|
||||
defer wg.Done()
|
||||
// Execute over the StorageTransformerInitializer set using the storage watcher
|
||||
log.Info("executing storage transformers")
|
||||
ticker := time.NewTicker(pollingInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
errs := make(chan error)
|
||||
rows := make(chan storageUtils.StorageDiffRow)
|
||||
w.Execute(rows, errs, queueRecheckInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func watchEthContract(w *watcher.ContractWatcher, wg *syn.WaitGroup) {
|
||||
defer wg.Done()
|
||||
// Execute over the ContractTransformerInitializer set using the contract watcher
|
||||
log.Info("executing contract_watcher transformers")
|
||||
ticker := time.NewTicker(pollingInterval)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
w.Execute()
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/history"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
// headerSyncCmd represents the headerSync command
|
||||
var headerSyncCmd = &cobra.Command{
|
||||
Use: "headerSync",
|
||||
Short: "Syncs VulcanizeDB with local ethereum node's block headers",
|
||||
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
||||
Postgres with block headers.
|
||||
|
||||
./vulcanizedb headerSync --starting-block-number 0 --config public.toml
|
||||
|
||||
Expects ethereum node to be running and requires a .toml config:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
headerSync()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(headerSyncCmd)
|
||||
headerSyncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
||||
}
|
||||
|
||||
func backFillAllHeaders(blockchain core.BlockChain, headerRepository datastore.HeaderRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
||||
populated, err := history.PopulateMissingHeaders(blockchain, headerRepository, startingBlockNumber)
|
||||
if err != nil {
|
||||
// TODO Lots of possible errors in the call stack above. If errors occur, we still put
|
||||
// 0 in the channel, triggering another round
|
||||
log.Error("backfillAllHeaders: Error populating headers: ", err)
|
||||
}
|
||||
missingBlocksPopulated <- populated
|
||||
}
|
||||
|
||||
func headerSync() {
|
||||
ticker := time.NewTicker(pollingInterval)
|
||||
defer ticker.Stop()
|
||||
blockChain := getBlockChain()
|
||||
validateArgs(blockChain)
|
||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||
|
||||
headerRepository := repositories.NewHeaderRepository(&db)
|
||||
validator := history.NewHeaderValidator(blockChain, headerRepository, validationWindow)
|
||||
missingBlocksPopulated := make(chan int)
|
||||
go backFillAllHeaders(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
window, err := validator.ValidateHeaders()
|
||||
if err != nil {
|
||||
log.Error("headerSync: ValidateHeaders failed: ", err)
|
||||
}
|
||||
log.Info(window.GetString())
|
||||
case n := <-missingBlocksPopulated:
|
||||
if n == 0 {
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
go backFillAllHeaders(blockChain, headerRepository, missingBlocksPopulated, startingBlockNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateArgs(blockChain *geth.BlockChain) {
|
||||
lastBlock, err := blockChain.LastBlock()
|
||||
if err != nil {
|
||||
log.Error("validateArgs: Error getting last block: ", err)
|
||||
}
|
||||
if lastBlock.Int64() == 0 {
|
||||
log.Fatal("geth initial: state sync not finished")
|
||||
}
|
||||
if startingBlockNumber > lastBlock.Int64() {
|
||||
log.Fatal("starting block number > current block number")
|
||||
}
|
||||
}
|
||||
+47
-75
@@ -20,125 +20,97 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/client"
|
||||
vRpc "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/node"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
databaseConfig config.Database
|
||||
genConfig config.Plugin
|
||||
ipc string
|
||||
levelDbPath string
|
||||
queueRecheckInterval time.Duration
|
||||
startingBlockNumber int64
|
||||
storageDiffsPath string
|
||||
syncAll bool
|
||||
endingBlockNumber int64
|
||||
recheckHeadersArg bool
|
||||
)
|
||||
|
||||
const (
|
||||
pollingInterval = 7 * time.Second
|
||||
validationWindow = 15
|
||||
cfgFile string
|
||||
subCommand string
|
||||
logWithCommand log.Entry
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "vulcanizedb",
|
||||
PersistentPreRun: database,
|
||||
Use: "ipld-eth-server",
|
||||
PersistentPreRun: initFuncs,
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
log.Info("----- Starting vDB -----")
|
||||
log.Info("----- Starting IPFS blockchain watcher -----")
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func database(cmd *cobra.Command, args []string) {
|
||||
ipc = viper.GetString("client.ipcpath")
|
||||
levelDbPath = viper.GetString("client.leveldbpath")
|
||||
storageDiffsPath = viper.GetString("filesystem.storageDiffsPath")
|
||||
databaseConfig = config.Database{
|
||||
Name: viper.GetString("database.name"),
|
||||
Hostname: viper.GetString("database.hostname"),
|
||||
Port: viper.GetInt("database.port"),
|
||||
User: viper.GetString("database.user"),
|
||||
Password: viper.GetString("database.password"),
|
||||
func initFuncs(cmd *cobra.Command, args []string) {
|
||||
logfile := viper.GetString("logfile")
|
||||
if logfile != "" {
|
||||
file, err := os.OpenFile(logfile,
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err == nil {
|
||||
log.Infof("Directing output to %s", logfile)
|
||||
log.SetOutput(file)
|
||||
} else {
|
||||
log.SetOutput(os.Stdout)
|
||||
log.Info("Failed to log to file, using default stdout")
|
||||
}
|
||||
} else {
|
||||
log.SetOutput(os.Stdout)
|
||||
}
|
||||
viper.Set("database.config", databaseConfig)
|
||||
if err := logLevel(); err != nil {
|
||||
log.Fatal("Could not set log level: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func logLevel() error {
|
||||
viper.BindEnv("log.level", "LOGRUS_LEVEL")
|
||||
lvl, err := log.ParseLevel(viper.GetString("log.level"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.SetLevel(lvl)
|
||||
if lvl > log.InfoLevel {
|
||||
log.SetReportCaller(true)
|
||||
}
|
||||
log.Info("Log level set to ", lvl.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
// When searching for env variables, replace dots in config keys with underscores
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
||||
rootCmd.PersistentFlags().String("logfile", "", "file path for logging")
|
||||
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
rootCmd.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
rootCmd.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
rootCmd.PersistentFlags().String("database-user", "", "database user")
|
||||
rootCmd.PersistentFlags().String("database-password", "", "database password")
|
||||
rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file")
|
||||
rootCmd.PersistentFlags().String("client-levelDbPath", "", "location of levelDb chaindata")
|
||||
rootCmd.PersistentFlags().String("filesystem-storageDiffsPath", "", "location of storage diffs csv file")
|
||||
rootCmd.PersistentFlags().String("exporter-name", "exporter", "name of exporter plugin")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
|
||||
|
||||
viper.BindPFlag("logfile", rootCmd.PersistentFlags().Lookup("logfile"))
|
||||
viper.BindPFlag("database.name", rootCmd.PersistentFlags().Lookup("database-name"))
|
||||
viper.BindPFlag("database.port", rootCmd.PersistentFlags().Lookup("database-port"))
|
||||
viper.BindPFlag("database.hostname", rootCmd.PersistentFlags().Lookup("database-hostname"))
|
||||
viper.BindPFlag("database.user", rootCmd.PersistentFlags().Lookup("database-user"))
|
||||
viper.BindPFlag("database.password", rootCmd.PersistentFlags().Lookup("database-password"))
|
||||
viper.BindPFlag("client.ipcPath", rootCmd.PersistentFlags().Lookup("client-ipcPath"))
|
||||
viper.BindPFlag("client.levelDbPath", rootCmd.PersistentFlags().Lookup("client-levelDbPath"))
|
||||
viper.BindPFlag("filesystem.storageDiffsPath", rootCmd.PersistentFlags().Lookup("filesystem-storageDiffsPath"))
|
||||
viper.BindPFlag("exporter.fileName", rootCmd.PersistentFlags().Lookup("exporter-name"))
|
||||
viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
viper.SetConfigFile(cfgFile)
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
} else {
|
||||
log.Fatal(fmt.Sprintf("Couldn't read config file: %s", err.Error()))
|
||||
}
|
||||
} else {
|
||||
noConfigError := "No config file passed with --config flag"
|
||||
fmt.Println("Error: ", noConfigError)
|
||||
log.Fatal(noConfigError)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
log.Printf("Using config file: %s\n\n", viper.ConfigFileUsed())
|
||||
} else {
|
||||
invalidConfigError := "Couldn't read config file"
|
||||
fmt.Println("Error: ", invalidConfigError)
|
||||
log.Fatal(invalidConfigError)
|
||||
os.Exit(1)
|
||||
log.Warn("No config file passed with --config flag")
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockChain() *geth.BlockChain {
|
||||
rawRpcClient, err := rpc.Dial(ipc)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, ipc)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
vdbEthClient := client.NewEthClient(ethClient)
|
||||
vdbNode := node.MakeNode(rpcClient)
|
||||
transactionConverter := vRpc.NewRpcTransactionConverter(ethClient)
|
||||
return geth.NewBlockChain(vdbEthClient, rpcClient, vdbNode, transactionConverter)
|
||||
}
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright © 2020 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
|
||||
s "github.com/vulcanize/ipld-eth-server/pkg/serve"
|
||||
v "github.com/vulcanize/ipld-eth-server/version"
|
||||
)
|
||||
|
||||
// serveCmd represents the serve command
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "serve chain data from PG-IPFS",
|
||||
Long: `This command configures a VulcanizeDB ipld-eth-server.
|
||||
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
serve()
|
||||
},
|
||||
}
|
||||
|
||||
func serve() {
|
||||
logWithCommand.Infof("running ipld-eth-server version: %s", v.VersionWithMeta)
|
||||
|
||||
var forwardPayloadChan chan eth.ConvertedPayload
|
||||
wg := new(sync.WaitGroup)
|
||||
logWithCommand.Debug("loading server configuration variables")
|
||||
serverConfig, err := s.NewConfig()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Infof("server config: %+v", serverConfig)
|
||||
logWithCommand.Debug("initializing new server service")
|
||||
server, err := s.NewServer(serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
logWithCommand.Info("starting up server servers")
|
||||
forwardPayloadChan = make(chan eth.ConvertedPayload, s.PayloadChanBufferSize)
|
||||
server.Serve(wg, forwardPayloadChan)
|
||||
if err := startServers(server, serverConfig); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
shutdown := make(chan os.Signal)
|
||||
signal.Notify(shutdown, os.Interrupt)
|
||||
<-shutdown
|
||||
server.Stop()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func startServers(server s.Server, settings *s.Config) error {
|
||||
logWithCommand.Info("starting up IPC server")
|
||||
_, _, err := rpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logWithCommand.Info("starting up WS server")
|
||||
_, _, err = rpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb"}, nil, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logWithCommand.Info("starting up HTTP server")
|
||||
_, _, err = rpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"eth"}, nil, nil, rpc.HTTPTimeouts{})
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
|
||||
// flags for all config variables
|
||||
serveCmd.PersistentFlags().String("server-ws-path", "", "vdb server ws path")
|
||||
serveCmd.PersistentFlags().String("server-http-path", "", "vdb server http path")
|
||||
serveCmd.PersistentFlags().String("server-ipc-path", "", "vdb server ipc path")
|
||||
|
||||
serveCmd.PersistentFlags().String("eth-ws-path", "", "ws url for ethereum node")
|
||||
serveCmd.PersistentFlags().String("eth-http-path", "", "http url for ethereum node")
|
||||
serveCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
serveCmd.PersistentFlags().String("eth-client-name", "", "eth client name")
|
||||
serveCmd.PersistentFlags().String("eth-genesis-block", "", "eth genesis block hash")
|
||||
serveCmd.PersistentFlags().String("eth-network-id", "", "eth network id")
|
||||
|
||||
// and their bindings
|
||||
viper.BindPFlag("server.wsPath", serveCmd.PersistentFlags().Lookup("server-ws-path"))
|
||||
viper.BindPFlag("server.httpPath", serveCmd.PersistentFlags().Lookup("server-http-path"))
|
||||
viper.BindPFlag("server.ipcPath", serveCmd.PersistentFlags().Lookup("server-ipc-path"))
|
||||
|
||||
viper.BindPFlag("ethereum.wsPath", serveCmd.PersistentFlags().Lookup("eth-ws-path"))
|
||||
viper.BindPFlag("ethereum.httpPath", serveCmd.PersistentFlags().Lookup("eth-http-path"))
|
||||
viper.BindPFlag("ethereum.nodeID", serveCmd.PersistentFlags().Lookup("eth-node-id"))
|
||||
viper.BindPFlag("ethereum.clientName", serveCmd.PersistentFlags().Lookup("eth-client-name"))
|
||||
viper.BindPFlag("ethereum.genesisBlock", serveCmd.PersistentFlags().Lookup("eth-genesis-block"))
|
||||
viper.BindPFlag("ethereum.networkID", serveCmd.PersistentFlags().Lookup("eth-network-id"))
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright © 2019 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
w "github.com/vulcanize/ipld-eth-server/pkg/serve"
|
||||
)
|
||||
|
||||
// subscribeCmd represents the subscribe command
|
||||
var subscribeCmd = &cobra.Command{
|
||||
Use: "subscribe",
|
||||
Short: "This command is used to subscribe to the eth ipfs watcher data stream with the provided filters",
|
||||
Long: `This command is for demo and testing purposes and is used to subscribe to the watcher with the provided subscription configuration parameters.
|
||||
It does not do anything with the data streamed from the watcher other than unpack it and print it out for demonstration purposes.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
subscribe()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(subscribeCmd)
|
||||
}
|
||||
|
||||
func subscribe() {
|
||||
// Prep the subscription config/filters to be sent to the server
|
||||
ethSubConfig, err := eth.NewEthSubscriptionConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create a new rpc client and a subscription streamer with that client
|
||||
rpcClient, err := getRPCClient()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
subClient := client.NewClient(rpcClient)
|
||||
|
||||
// Buffered channel for reading subscription payloads
|
||||
payloadChan := make(chan w.SubscriptionPayload, 20000)
|
||||
|
||||
// Subscribe to the watcher service with the given config/filter parameters
|
||||
sub, err := subClient.Stream(payloadChan, *ethSubConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("awaiting payloads")
|
||||
// Receive response payloads and print out the results
|
||||
for {
|
||||
select {
|
||||
case payload := <-payloadChan:
|
||||
if payload.Err != "" {
|
||||
logWithCommand.Error(payload.Err)
|
||||
continue
|
||||
}
|
||||
var ethData eth2.IPLDs
|
||||
if err := rlp.DecodeBytes(payload.Data, ðData); err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
var header types.Header
|
||||
err = rlp.Decode(bytes.NewBuffer(ethData.Header.Data), &header)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Header number %d, hash %s\n", header.Number.Int64(), header.Hash().Hex())
|
||||
fmt.Printf("header: %v\n", header)
|
||||
for _, trxRlp := range ethData.Transactions {
|
||||
var trx types.Transaction
|
||||
buff := bytes.NewBuffer(trxRlp.Data)
|
||||
stream := rlp.NewStream(buff, 0)
|
||||
err := trx.DecodeRLP(stream)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Transaction with hash %s\n", trx.Hash().Hex())
|
||||
fmt.Printf("trx: %v\n", trx)
|
||||
}
|
||||
for _, rctRlp := range ethData.Receipts {
|
||||
var rct types.Receipt
|
||||
buff := bytes.NewBuffer(rctRlp.Data)
|
||||
stream := rlp.NewStream(buff, 0)
|
||||
err = rct.DecodeRLP(stream)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Receipt with block hash %s, trx hash %s\n", rct.BlockHash.Hex(), rct.TxHash.Hex())
|
||||
fmt.Printf("rct: %v\n", rct)
|
||||
for _, l := range rct.Logs {
|
||||
if len(l.Topics) < 1 {
|
||||
logWithCommand.Error(fmt.Sprintf("log only has %d topics", len(l.Topics)))
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Log for block hash %s, trx hash %s, address %s, and with topic0 %s\n",
|
||||
l.BlockHash.Hex(), l.TxHash.Hex(), l.Address.Hex(), l.Topics[0].Hex())
|
||||
fmt.Printf("log: %v\n", l)
|
||||
}
|
||||
}
|
||||
// This assumes leafs only
|
||||
for _, stateNode := range ethData.StateNodes {
|
||||
var acct state.Account
|
||||
err = rlp.DecodeBytes(stateNode.IPLD.Data, &acct)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Account for key %s, and root %s, with balance %s\n",
|
||||
stateNode.StateLeafKey.Hex(), acct.Root.Hex(), acct.Balance.String())
|
||||
fmt.Printf("state account: %+v\n", acct)
|
||||
}
|
||||
for _, storageNode := range ethData.StorageNodes {
|
||||
fmt.Printf("Storage for state key %s ", storageNode.StateLeafKey.Hex())
|
||||
fmt.Printf("with storage key %s\n", storageNode.StorageLeafKey.Hex())
|
||||
var i []interface{}
|
||||
err := rlp.DecodeBytes(storageNode.IPLD.Data, &i)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
continue
|
||||
}
|
||||
// if a value node
|
||||
if len(i) == 1 {
|
||||
valueBytes, ok := i[0].([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("Storage leaf key: %s, and value hash: %s\n",
|
||||
storageNode.StorageLeafKey.Hex(), common.BytesToHash(valueBytes).Hex())
|
||||
}
|
||||
}
|
||||
case err = <-sub.Err():
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getRPCClient() (*rpc.Client, error) {
|
||||
vulcPath := viper.GetString("watcher.ethSubscription.wsPath")
|
||||
if vulcPath == "" {
|
||||
vulcPath = "ws://127.0.0.1:8080" // default to and try the default ws url if no path is provided
|
||||
}
|
||||
return rpc.Dial(vulcPath)
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/history"
|
||||
"github.com/vulcanize/vulcanizedb/utils"
|
||||
)
|
||||
|
||||
// syncCmd represents the sync command
|
||||
var syncCmd = &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Syncs VulcanizeDB with local ethereum node",
|
||||
Long: `Syncs VulcanizeDB with local ethereum node. Populates
|
||||
Postgres with blocks, transactions, receipts, and logs.
|
||||
|
||||
./vulcanizedb sync --starting-block-number 0 --config public.toml
|
||||
|
||||
Expects ethereum node to be running and requires a .toml config:
|
||||
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sync()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(syncCmd)
|
||||
|
||||
syncCmd.Flags().Int64VarP(&startingBlockNumber, "starting-block-number", "s", 0, "Block number to start syncing from")
|
||||
}
|
||||
|
||||
func backFillAllBlocks(blockchain core.BlockChain, blockRepository datastore.BlockRepository, missingBlocksPopulated chan int, startingBlockNumber int64) {
|
||||
populated, err := history.PopulateMissingBlocks(blockchain, blockRepository, startingBlockNumber)
|
||||
if err != nil {
|
||||
log.Error("backfillAllBlocks: error in populateMissingBlocks: ", err)
|
||||
}
|
||||
missingBlocksPopulated <- populated
|
||||
}
|
||||
|
||||
func sync() {
|
||||
ticker := time.NewTicker(pollingInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
blockChain := getBlockChain()
|
||||
lastBlock, err := blockChain.LastBlock()
|
||||
if err != nil {
|
||||
log.Error("sync: Error getting last block: ", err)
|
||||
}
|
||||
if lastBlock.Int64() == 0 {
|
||||
log.Fatal("geth initial: state sync not finished")
|
||||
}
|
||||
if startingBlockNumber > lastBlock.Int64() {
|
||||
log.Fatal("sync: starting block number > current block number")
|
||||
}
|
||||
|
||||
db := utils.LoadPostgres(databaseConfig, blockChain.Node())
|
||||
blockRepository := repositories.NewBlockRepository(&db)
|
||||
validator := history.NewBlockValidator(blockChain, blockRepository, validationWindow)
|
||||
missingBlocksPopulated := make(chan int)
|
||||
go backFillAllBlocks(blockChain, blockRepository, missingBlocksPopulated, startingBlockNumber)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
window, err := validator.ValidateBlocks()
|
||||
if err != nil {
|
||||
log.Error("sync: error in validateBlocks: ", err)
|
||||
}
|
||||
log.Info(window.GetString())
|
||||
case <-missingBlocksPopulated:
|
||||
go backFillAllBlocks(blockChain, blockRepository, missingBlocksPopulated, startingBlockNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2020 Vulcanize, Inc
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
v "github.com/vulcanize/ipld-eth-server/version"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints the version of ipld-eth-server",
|
||||
Long: `Use this command to fetch the version of ipld-eth-server
|
||||
|
||||
Usage: ./ipld-eth-server version`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
logWithCommand.Infof("ipld-eth-server version: %s", v.VersionWithMeta)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE public.blocks (
|
||||
id SERIAL PRIMARY KEY,
|
||||
difficulty BIGINT,
|
||||
extra_data VARCHAR,
|
||||
gas_limit BIGINT,
|
||||
gas_used BIGINT,
|
||||
hash VARCHAR(66),
|
||||
miner VARCHAR(42),
|
||||
nonce VARCHAR(20),
|
||||
"number" BIGINT,
|
||||
parent_hash VARCHAR(66),
|
||||
reward NUMERIC,
|
||||
uncles_reward NUMERIC,
|
||||
"size" VARCHAR,
|
||||
"time" BIGINT,
|
||||
is_final BOOLEAN,
|
||||
uncle_hash VARCHAR(66)
|
||||
);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.blocks;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS public.blocks (
|
||||
key TEXT UNIQUE NOT NULL,
|
||||
data BYTEA NOT NULL
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.blocks;
|
||||
@@ -1,18 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE full_sync_transactions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
block_id INTEGER NOT NULL REFERENCES blocks(id) ON DELETE CASCADE,
|
||||
gas_limit NUMERIC,
|
||||
gas_price NUMERIC,
|
||||
hash VARCHAR(66),
|
||||
input_data BYTEA,
|
||||
nonce NUMERIC,
|
||||
raw BYTEA,
|
||||
tx_from VARCHAR(66),
|
||||
tx_index INTEGER,
|
||||
tx_to VARCHAR(66),
|
||||
"value" NUMERIC
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE full_sync_transactions;
|
||||
+1
-1
@@ -3,7 +3,7 @@ CREATE TABLE nodes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_name VARCHAR,
|
||||
genesis_block VARCHAR(66),
|
||||
network_id NUMERIC,
|
||||
network_id VARCHAR,
|
||||
node_id VARCHAR(128),
|
||||
CONSTRAINT node_uc UNIQUE (genesis_block, network_id, node_id)
|
||||
);
|
||||
@@ -1,6 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX number_index ON blocks (number);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX number_index;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- +goose Up
|
||||
CREATE SCHEMA eth;
|
||||
|
||||
-- +goose Down
|
||||
DROP SCHEMA eth;
|
||||
@@ -1,10 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE watched_contracts
|
||||
(
|
||||
contract_id SERIAL PRIMARY KEY,
|
||||
contract_abi json,
|
||||
contract_hash VARCHAR(66) UNIQUE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE watched_contracts;
|
||||
@@ -0,0 +1,23 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.header_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
block_number BIGINT NOT NULL,
|
||||
block_hash VARCHAR(66) NOT NULL,
|
||||
parent_hash VARCHAR(66) NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
td NUMERIC NOT NULL,
|
||||
node_id INTEGER NOT NULL REFERENCES nodes (id) ON DELETE CASCADE,
|
||||
reward NUMERIC NOT NULL,
|
||||
state_root VARCHAR(66) NOT NULL,
|
||||
tx_root VARCHAR(66) NOT NULL,
|
||||
receipt_root VARCHAR(66) NOT NULL,
|
||||
uncle_root VARCHAR(66) NOT NULL,
|
||||
bloom BYTEA NOT NULL,
|
||||
timestamp NUMERIC NOT NULL,
|
||||
times_validated INTEGER NOT NULL DEFAULT 1,
|
||||
UNIQUE (block_number, block_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.header_cids;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.uncle_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES eth.header_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
block_hash VARCHAR(66) NOT NULL,
|
||||
parent_hash VARCHAR(66) NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
reward NUMERIC NOT NULL,
|
||||
UNIQUE (header_id, block_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.uncle_cids;
|
||||
@@ -1,11 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE blocks
|
||||
ADD COLUMN node_id INTEGER NOT NULL,
|
||||
ADD CONSTRAINT node_fk
|
||||
FOREIGN KEY (node_id)
|
||||
REFERENCES nodes (id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE blocks
|
||||
DROP COLUMN node_id;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.transaction_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES eth.header_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
tx_hash VARCHAR(66) NOT NULL,
|
||||
index INTEGER NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
dst VARCHAR(66) NOT NULL,
|
||||
src VARCHAR(66) NOT NULL,
|
||||
deployment BOOL NOT NULL,
|
||||
tx_data BYTEA,
|
||||
UNIQUE (header_id, tx_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.transaction_cids;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.receipt_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tx_id INTEGER NOT NULL REFERENCES eth.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
contract VARCHAR(66),
|
||||
contract_hash VARCHAR(66),
|
||||
topic0s VARCHAR(66)[],
|
||||
topic1s VARCHAR(66)[],
|
||||
topic2s VARCHAR(66)[],
|
||||
topic3s VARCHAR(66)[],
|
||||
log_contracts VARCHAR(66)[],
|
||||
UNIQUE (tx_id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.receipt_cids;
|
||||
@@ -1,19 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
block_number BIGINT,
|
||||
address VARCHAR(66),
|
||||
tx_hash VARCHAR(66),
|
||||
index BIGINT,
|
||||
topic0 VARCHAR(66),
|
||||
topic1 VARCHAR(66),
|
||||
topic2 VARCHAR(66),
|
||||
topic3 VARCHAR(66),
|
||||
data TEXT,
|
||||
CONSTRAINT log_uc UNIQUE (block_number, index)
|
||||
);
|
||||
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE logs;
|
||||
@@ -1,7 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE blocks
|
||||
ADD CONSTRAINT node_id_block_number_uc UNIQUE (number, node_id);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE blocks
|
||||
DROP CONSTRAINT node_id_block_number_uc;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.state_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES eth.header_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
state_leaf_key VARCHAR(66),
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
state_path BYTEA,
|
||||
node_type INTEGER NOT NULL,
|
||||
diff BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
UNIQUE (header_id, state_path)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.state_cids;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX block_id_index ON full_sync_transactions (block_id);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX block_id_index;
|
||||
@@ -0,0 +1,15 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.storage_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
state_id INTEGER NOT NULL REFERENCES eth.state_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
storage_leaf_key VARCHAR(66),
|
||||
cid TEXT NOT NULL,
|
||||
mh_key TEXT NOT NULL REFERENCES public.blocks (key) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
storage_path BYTEA,
|
||||
node_type INTEGER NOT NULL,
|
||||
diff BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
UNIQUE (state_id, storage_path)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.storage_cids;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX node_id_index ON blocks (node_id);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX node_id_index;
|
||||
@@ -0,0 +1,13 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.state_accounts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
state_id INTEGER NOT NULL REFERENCES eth.state_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
balance NUMERIC NOT NULL,
|
||||
nonce INTEGER NOT NULL,
|
||||
code_hash BYTEA NOT NULL,
|
||||
storage_root VARCHAR(66) NOT NULL,
|
||||
UNIQUE (state_id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.state_accounts;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX tx_to_index ON full_sync_transactions(tx_to);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX tx_to_index;
|
||||
@@ -0,0 +1,6 @@
|
||||
-- +goose Up
|
||||
COMMENT ON TABLE public.nodes IS E'@name NodeInfo';
|
||||
COMMENT ON TABLE eth.transaction_cids IS E'@name EthTransactionCids';
|
||||
COMMENT ON TABLE eth.header_cids IS E'@name EthHeaderCids';
|
||||
COMMENT ON COLUMN public.nodes.node_id IS E'@name ChainNodeID';
|
||||
COMMENT ON COLUMN eth.header_cids.node_id IS E'@name EthNodeID';
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX tx_from_index ON full_sync_transactions(tx_from);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX tx_from_index;
|
||||
@@ -1,16 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE full_sync_receipts
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
transaction_id INTEGER NOT NULL REFERENCES full_sync_transactions (id) ON DELETE CASCADE,
|
||||
contract_address VARCHAR(42),
|
||||
cumulative_gas_used NUMERIC,
|
||||
gas_used NUMERIC,
|
||||
state_root VARCHAR(66),
|
||||
status INTEGER,
|
||||
tx_hash VARCHAR(66)
|
||||
);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE full_sync_receipts;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE INDEX transaction_id_index ON full_sync_receipts (transaction_id);
|
||||
|
||||
-- +goose Down
|
||||
DROP INDEX transaction_id_index;
|
||||
@@ -1,23 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE logs
|
||||
DROP CONSTRAINT log_uc;
|
||||
|
||||
ALTER TABLE logs
|
||||
ADD COLUMN receipt_id INT;
|
||||
|
||||
ALTER TABLE logs
|
||||
ADD CONSTRAINT receipts_fk
|
||||
FOREIGN KEY (receipt_id)
|
||||
REFERENCES full_sync_receipts (id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE logs
|
||||
DROP CONSTRAINT receipts_fk;
|
||||
|
||||
ALTER TABLE logs
|
||||
DROP COLUMN receipt_id;
|
||||
|
||||
ALTER TABLE logs
|
||||
ADD CONSTRAINT log_uc UNIQUE (block_number, index);
|
||||
@@ -1,16 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE log_filters (
|
||||
id SERIAL,
|
||||
name VARCHAR NOT NULL CHECK (name <> ''),
|
||||
from_block BIGINT CHECK (from_block >= 0),
|
||||
to_block BIGINT CHECK (from_block >= 0),
|
||||
address VARCHAR(66),
|
||||
topic0 VARCHAR(66),
|
||||
topic1 VARCHAR(66),
|
||||
topic2 VARCHAR(66),
|
||||
topic3 VARCHAR(66),
|
||||
CONSTRAINT name_uc UNIQUE (name)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE log_filters;
|
||||
@@ -1,34 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE VIEW block_stats AS
|
||||
SELECT
|
||||
max(block_number) AS max_block,
|
||||
min(block_number) AS min_block
|
||||
FROM logs;
|
||||
|
||||
CREATE VIEW watched_event_logs AS
|
||||
SELECT
|
||||
log_filters.name,
|
||||
logs.id,
|
||||
block_number,
|
||||
logs.address,
|
||||
tx_hash,
|
||||
index,
|
||||
logs.topic0,
|
||||
logs.topic1,
|
||||
logs.topic2,
|
||||
logs.topic3,
|
||||
data,
|
||||
receipt_id
|
||||
FROM log_filters
|
||||
CROSS JOIN block_stats
|
||||
JOIN logs ON logs.address = log_filters.address
|
||||
AND logs.block_number >= coalesce(log_filters.from_block, block_stats.min_block)
|
||||
AND logs.block_number <= coalesce(log_filters.to_block, block_stats.max_block)
|
||||
WHERE (log_filters.topic0 = logs.topic0 OR log_filters.topic0 ISNULL)
|
||||
AND (log_filters.topic1 = logs.topic1 OR log_filters.topic1 ISNULL)
|
||||
AND (log_filters.topic2 = logs.topic2 OR log_filters.topic2 ISNULL)
|
||||
AND (log_filters.topic3 = logs.topic3 OR log_filters.topic3 ISNULL);
|
||||
|
||||
-- +goose Down
|
||||
DROP VIEW watched_event_logs;
|
||||
DROP VIEW block_stats;
|
||||
@@ -1,14 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE log_filters
|
||||
DROP CONSTRAINT log_filters_from_block_check1;
|
||||
|
||||
ALTER TABLE log_filters
|
||||
ADD CONSTRAINT log_filters_to_block_check CHECK (to_block >= 0);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE log_filters
|
||||
DROP CONSTRAINT log_filters_to_block_check;
|
||||
|
||||
ALTER TABLE log_filters
|
||||
ADD CONSTRAINT log_filters_from_block_check1 CHECK (to_block >= 0);
|
||||
@@ -1,43 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE public.nodes RENAME TO eth_nodes;
|
||||
|
||||
ALTER TABLE public.eth_nodes RENAME COLUMN node_id TO eth_node_id;
|
||||
|
||||
ALTER TABLE public.eth_nodes DROP CONSTRAINT node_uc;
|
||||
ALTER TABLE public.eth_nodes
|
||||
ADD CONSTRAINT eth_node_uc UNIQUE (genesis_block, network_id, eth_node_id);
|
||||
|
||||
ALTER TABLE public.blocks RENAME COLUMN node_id TO eth_node_id;
|
||||
|
||||
ALTER TABLE public.blocks DROP CONSTRAINT node_id_block_number_uc;
|
||||
ALTER TABLE public.blocks
|
||||
ADD CONSTRAINT eth_node_id_block_number_uc UNIQUE (number, eth_node_id);
|
||||
|
||||
ALTER TABLE public.blocks DROP CONSTRAINT node_fk;
|
||||
ALTER TABLE public.blocks
|
||||
ADD CONSTRAINT node_fk
|
||||
FOREIGN KEY (eth_node_id) REFERENCES eth_nodes (id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE public.eth_nodes
|
||||
RENAME TO nodes;
|
||||
|
||||
ALTER TABLE public.nodes
|
||||
RENAME COLUMN eth_node_id TO node_id;
|
||||
|
||||
ALTER TABLE public.nodes
|
||||
DROP CONSTRAINT eth_node_uc;
|
||||
ALTER TABLE public.nodes
|
||||
ADD CONSTRAINT node_uc UNIQUE (genesis_block, network_id, node_id);
|
||||
|
||||
ALTER TABLE public.blocks RENAME COLUMN eth_node_id TO node_id;
|
||||
|
||||
ALTER TABLE public.blocks DROP CONSTRAINT eth_node_id_block_number_uc;
|
||||
ALTER TABLE public.blocks
|
||||
ADD CONSTRAINT node_id_block_number_uc UNIQUE (number, node_id);
|
||||
|
||||
ALTER TABLE public.blocks DROP CONSTRAINT node_fk;
|
||||
ALTER TABLE public.blocks
|
||||
ADD CONSTRAINT node_fk
|
||||
FOREIGN KEY (node_id) REFERENCES nodes (id) ON DELETE CASCADE;
|
||||
@@ -1,44 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE full_sync_receipts
|
||||
ADD COLUMN block_id INT;
|
||||
|
||||
UPDATE full_sync_receipts
|
||||
SET block_id = (
|
||||
SELECT block_id FROM full_sync_transactions WHERE full_sync_transactions.id = full_sync_receipts.transaction_id
|
||||
);
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
ALTER COLUMN block_id SET NOT NULL;
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
ADD CONSTRAINT blocks_fk
|
||||
FOREIGN KEY (block_id)
|
||||
REFERENCES blocks (id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
DROP COLUMN transaction_id;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE full_sync_receipts
|
||||
ADD COLUMN transaction_id INT;
|
||||
|
||||
CREATE INDEX transaction_id_index ON full_sync_receipts (transaction_id);
|
||||
|
||||
UPDATE full_sync_receipts
|
||||
SET transaction_id = (
|
||||
SELECT id FROM full_sync_transactions WHERE full_sync_transactions.hash = full_sync_receipts.tx_hash
|
||||
);
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
ALTER COLUMN transaction_id SET NOT NULL;
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
ADD CONSTRAINT transaction_fk
|
||||
FOREIGN KEY (transaction_id)
|
||||
REFERENCES full_sync_transactions (id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE full_sync_receipts
|
||||
DROP COLUMN block_id;
|
||||
@@ -1,16 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE blocks
|
||||
ADD COLUMN eth_node_fingerprint VARCHAR(128);
|
||||
|
||||
UPDATE blocks
|
||||
SET eth_node_fingerprint = (
|
||||
SELECT eth_node_id FROM eth_nodes WHERE eth_nodes.id = blocks.eth_node_id
|
||||
);
|
||||
|
||||
ALTER TABLE blocks
|
||||
ALTER COLUMN eth_node_fingerprint SET NOT NULL;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE blocks
|
||||
DROP COLUMN eth_node_fingerprint;
|
||||
@@ -1,17 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE public.headers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hash VARCHAR(66),
|
||||
block_number BIGINT,
|
||||
raw JSONB,
|
||||
block_timestamp NUMERIC,
|
||||
eth_node_id INTEGER NOT NULL REFERENCES eth_nodes (id) ON DELETE CASCADE,
|
||||
eth_node_fingerprint VARCHAR(128)
|
||||
);
|
||||
|
||||
-- Index is removed when table is
|
||||
CREATE INDEX headers_block_number ON public.headers (block_number);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.headers;
|
||||
@@ -1,8 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE public.checked_headers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER UNIQUE NOT NULL REFERENCES headers (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.checked_headers;
|
||||
@@ -1,12 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE public.queued_storage (
|
||||
id SERIAL PRIMARY KEY,
|
||||
block_height BIGINT,
|
||||
block_hash BYTEA,
|
||||
contract BYTEA,
|
||||
storage_key BYTEA,
|
||||
storage_value BYTEA
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.queued_storage;
|
||||
@@ -1,19 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE header_sync_transactions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
||||
hash VARCHAR(66),
|
||||
gas_limit NUMERIC,
|
||||
gas_price NUMERIC,
|
||||
input_data BYTEA,
|
||||
nonce NUMERIC,
|
||||
raw BYTEA,
|
||||
tx_from VARCHAR(44),
|
||||
tx_index INTEGER,
|
||||
tx_to VARCHAR(44),
|
||||
"value" NUMERIC,
|
||||
UNIQUE (header_id, hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE header_sync_transactions;
|
||||
@@ -1,18 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE header_sync_receipts(
|
||||
id SERIAL PRIMARY KEY,
|
||||
transaction_id INTEGER NOT NULL REFERENCES header_sync_transactions(id) ON DELETE CASCADE,
|
||||
header_id INTEGER NOT NULL REFERENCES headers(id) ON DELETE CASCADE,
|
||||
contract_address VARCHAR(42),
|
||||
cumulative_gas_used NUMERIC,
|
||||
gas_used NUMERIC,
|
||||
state_root VARCHAR(66),
|
||||
status INTEGER,
|
||||
tx_hash VARCHAR(66),
|
||||
rlp BYTEA,
|
||||
UNIQUE(header_id, transaction_id)
|
||||
);
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE header_sync_receipts;
|
||||
@@ -1,16 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE public.uncles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
hash VARCHAR(66) NOT NULL,
|
||||
block_id INTEGER NOT NULL REFERENCES blocks (id) ON DELETE CASCADE,
|
||||
reward NUMERIC NOT NULL,
|
||||
miner VARCHAR(42) NOT NULL,
|
||||
raw JSONB,
|
||||
block_timestamp NUMERIC,
|
||||
eth_node_id INTEGER NOT NULL REFERENCES eth_nodes (id) ON DELETE CASCADE,
|
||||
eth_node_fingerprint VARCHAR(128),
|
||||
UNIQUE (block_id, hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE public.uncles;
|
||||
+429
-692
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
dapptools:
|
||||
restart: unless-stopped
|
||||
image: vulcanize/dapptools:v0.29.0-statediff-0.0.2
|
||||
ports:
|
||||
- "127.0.0.1:8545:8545"
|
||||
- "127.0.0.1:8546:8546"
|
||||
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:10.12-alpine
|
||||
environment:
|
||||
POSTGRES_USER: "vdbm"
|
||||
POSTGRES_DB: "vulcanize_public"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
volumes:
|
||||
- vdb_db_eth_server:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:8077:5432"
|
||||
|
||||
eth-indexer:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- dapptools
|
||||
image: vulcanize/ipld-eth-indexer:v0.3.0-alpha
|
||||
environment:
|
||||
DATABASE_NAME: vulcanize_public
|
||||
DATABASE_HOSTNAME: db
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: vdbm
|
||||
DATABASE_PASSWORD: password
|
||||
ETH_WS_PATH: "dapptools:8546"
|
||||
ETH_HTTP_PATH: "dapptools:8545"
|
||||
ETH_CHAIN_ID: 4
|
||||
ETH_NETWORK_ID: 4
|
||||
VDB_COMMAND: sync
|
||||
|
||||
eth-server:
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
- golang:1.13-alpine
|
||||
environment:
|
||||
VDB_COMMAND: "serve"
|
||||
DATABASE_NAME: "vulcanize_public"
|
||||
DATABASE_HOSTNAME: "db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
SERVER_WS_PATH: "0.0.0.0:8081"
|
||||
SERVER_HTTP_PATH: "0.0.0.0:8082"
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
- "127.0.0.1:8081:8081"
|
||||
|
||||
graphql:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
image: vulcanize/postgraphile:v1.0.1
|
||||
environment:
|
||||
- PG_HOST=db
|
||||
- PG_PORT=5432
|
||||
- PG_DATABASE=vulcanize_public
|
||||
- PG_USER=vdbm
|
||||
- PG_PASSWORD=password
|
||||
- SCHEMA=public,eth
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000"
|
||||
|
||||
volumes:
|
||||
vdb_db_eth_server:
|
||||
@@ -1,32 +0,0 @@
|
||||
S
|
||||
`Dockerfile` will build an alpine image containing:
|
||||
- vDB as a binary with runtime deps statically linked: `/app/vulcanizedb`
|
||||
- The migration tool goose: `/app/goose`
|
||||
- Two services for running `headerSync` and `continuousLogSync`, started with the default configuration `environments/staging.toml`.
|
||||
|
||||
By default, vDB is configured towards the Kovan deploy. The configuration values can be overridden using environment variables, using the same hierarchical naming pattern but in CAPS and using underscores. For example, the contract address for the `Pit` can be set with the variable `CONTRACT_ADDRESS_PIT="0x123..."`.
|
||||
|
||||
## To use the container:
|
||||
1. Setup a postgres database with superuser `vulcanize`
|
||||
2. Set the env variables `DATABASE_NAME`, `DATABASE_HOSTNAME`,
|
||||
`DATABASE_PORT`, `DATABASE_USER` & `DATABASE_PASSWORD`
|
||||
3. Run the DB migrations:
|
||||
* `./goose postgres "postgresql://$(DATABASE_USER):$(DATABASE_PASSWORD)@$(DATABASE_HOSTNAME):$(DATABASE_PORT)/$(DATABASE_NAME)?sslmode=disable"
|
||||
e`
|
||||
4. Set `CLIENT_IPCPATH` to a node endpoint
|
||||
5. Set the contract variables:
|
||||
* `CONTRACT_ADDRESS_[CONTRACT NAME]=0x123...`
|
||||
* `CONTRACT_ABI_[CONTRACT NAME]="ABI STRING"`
|
||||
* `CONTRACT_DEPLOYMENT-BLOCK_[CONTRACT NAME]=0` (doesn't really matter on a short chain, just avoids long unnecessary searching)
|
||||
6. Start the `headerSync` and `continuousLogSync` services:
|
||||
* `./vulcanizedb headerSync --config environments/staging.toml`
|
||||
* `./vulcanizedb continuousLogSync --config environments/staging.toml`
|
||||
|
||||
### Automated
|
||||
The steps above have been rolled into a script: `/app/startup_script.sh`, which just assumes the DB env variables have been set, and defaults the rest to Kovan according to `environments/staging.toml`. This can be called with something like:
|
||||
|
||||
`docker run -d -e DATABASE_NAME=vulcanize_public -e DATABASE_HOSTNAME=localhost -e DATABASE_PORT=5432 -e DATABASE_USER=vulcanize -e DATABASE_PASSWORD=vulcanize m0ar/images:vDB`
|
||||
|
||||
### Logging
|
||||
When running, vDB services log to `/app/vulcanizedb.log`.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
FROM golang:1.10.3-alpine3.7
|
||||
|
||||
RUN apk add --no-cache make gcc musl-dev
|
||||
|
||||
ADD . /go/src/github.com/vulcanize/vulcanizedb
|
||||
WORKDIR /go/src/github.com/vulcanize/vulcanizedb
|
||||
RUN go build -o /app main.go
|
||||
|
||||
ENTRYPOINT ["/app"]
|
||||
@@ -1,9 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanizedb"
|
||||
hostname = "postgres"
|
||||
port = 5432
|
||||
user = "postgres"
|
||||
password = "postgres"
|
||||
|
||||
[client]
|
||||
ipcPath = "/geth/geth.ipc"
|
||||
@@ -1,63 +0,0 @@
|
||||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
vulcanizedb:
|
||||
build:
|
||||
context: ./../../
|
||||
dockerfile: dockerfiles/rinkeby/Dockerfile
|
||||
container_name: rinkeby_vulcanizedb
|
||||
command: "sync --starting-block-number 0 --config /config.toml"
|
||||
volumes:
|
||||
- "./config.toml:/config.toml"
|
||||
- "vulcanizedb_geth_data:/geth"
|
||||
networks:
|
||||
vulcanizedb_net:
|
||||
|
||||
migrations:
|
||||
image: migrate/migrate:v3.3.0
|
||||
container_name: rinkeby_vulcanizedb_migrations
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
command: -database postgresql://postgres:postgres@postgres:5432/vulcanizedb?sslmode=disable -path /migrations up
|
||||
volumes:
|
||||
- ./../../db/migrations:/migrations
|
||||
networks:
|
||||
vulcanizedb_net:
|
||||
|
||||
postgres:
|
||||
image: postgres:9.6.5-alpine
|
||||
container_name: rinkeby_vulcanizedb_postgres
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: vulcanizedb
|
||||
POSTGRES_PASSWORD: postgres
|
||||
volumes:
|
||||
- "vulcanizedb_db_data:/var/lib/postgresql/data"
|
||||
networks:
|
||||
vulcanizedb_net:
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
|
||||
geth:
|
||||
image: ethereum/client-go:v1.8.11
|
||||
container_name: rinkeby_vulcanizedb_geth
|
||||
cpus: 0.3
|
||||
hostname: eth
|
||||
command: '--rinkeby --rpc --rpcaddr="0.0.0.0" --rpcvhosts="geth"'
|
||||
volumes:
|
||||
- "vulcanizedb_geth_data:/root/.ethereum/rinkeby"
|
||||
networks:
|
||||
vulcanizedb_net:
|
||||
|
||||
volumes:
|
||||
vulcanizedb_geth_data:
|
||||
vulcanizedb_db_data:
|
||||
|
||||
networks:
|
||||
vulcanizedb_net:
|
||||
driver: bridge
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Runs the migrations and starts the headerSync and continuousLogSync services
|
||||
|
||||
# Exit if the variable tests fail
|
||||
set -e
|
||||
|
||||
# Check the database variables are set
|
||||
test $DATABASE_NAME
|
||||
test $DATABASE_HOSTNAME
|
||||
test $DATABASE_PORT
|
||||
test $DATABASE_USER
|
||||
test $DATABASE_PASSWORD
|
||||
|
||||
# Construct the connection string for postgres
|
||||
CONNECT_STRING=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable
|
||||
echo "Connecting with: $CONNECT_STRING"
|
||||
|
||||
set +e
|
||||
|
||||
# Run the DB migrations
|
||||
./goose postgres "$CONNECT_STRING" up
|
||||
if [ $? -eq 0 ]; then
|
||||
# Fire up the services
|
||||
./vulcanizedb headerSync --config environments/staging.toml &
|
||||
./vulcanizedb continuousLogSync --config environments/staging.toml &
|
||||
else
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
fi
|
||||
wait
|
||||
@@ -0,0 +1,258 @@
|
||||
## ipld-eth-server APIs
|
||||
We can expose a number of different APIs for remote access to ipld-eth-server data
|
||||
|
||||
|
||||
### Table of Contents
|
||||
1. [Postgraphile](#postgraphile)
|
||||
1. [RPC Subscription Interface](#rpc-subscription-interface)
|
||||
1. [Native API Recapitulation](#native-api-recapitulation)
|
||||
|
||||
|
||||
### Postgraphile
|
||||
ipld-eth-server stores all processed data in Postgres using PG-IPFS, this includes all of the IPLD objects.
|
||||
[Postgraphile](https://www.graphile.org/postgraphile/) can be used to expose GraphQL endpoints for the Postgres tables.
|
||||
|
||||
e.g.
|
||||
|
||||
`postgraphile --plugins @graphile/pg-pubsub --subscriptions --simple-subscriptions -c postgres://localhost:5432/vulcanize_public?sslmode=disable -s public,btc,eth -a -j`
|
||||
|
||||
|
||||
This will stand up a Postgraphile server on the public, eth, and btc schemas- exposing GraphQL endpoints for all of the tables contained under those schemas.
|
||||
All of their data can then be queried with standard [GraphQL](https://graphql.org) queries.
|
||||
|
||||
|
||||
### RPC Subscription Interface
|
||||
A direct, real-time subscription to the data being processed by ipld-eth-server can be established over WS or IPC through the [Stream](../pkg/serve/api.go#L53) RPC method.
|
||||
This method is not chain-specific and each chain-type supports it, it is accessed under the "vdb" namespace rather than a chain-specific namespace. An interface for
|
||||
subscribing to this endpoint is provided [here](../pkg/client/client.go).
|
||||
|
||||
When subscribing to this endpoint, the subscriber provides a set of RLP-encoded subscription parameters. These parameters will be chain-specific, and are used
|
||||
by ipld-eth-server to filter and return a requested subset of chain data to the subscriber. (e.g. [BTC](../pkg/btc/subscription_config.go), [ETH](../../pkg/eth/subscription_config.go)).
|
||||
|
||||
#### Ethereum RPC Subscription
|
||||
An example of how to subscribe to a real-time Ethereum data feed from ipld-eth-server using the `Stream` RPC method is provided below
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/watch"
|
||||
)
|
||||
|
||||
config, _ := eth.NewEthSubscriptionConfig()
|
||||
rlpConfig, _ := rlp.EncodeToBytes(config)
|
||||
vulcPath := viper.GetString("watcher.ethSubscription.path")
|
||||
rpcClient, _ := rpc.Dial(vulcPath)
|
||||
subClient := client.NewClient(rpcClient)
|
||||
payloadChan := make(chan watch.SubscriptionPayload, 20000)
|
||||
subscription, _ := subClient.Stream(payloadChan, rlpConfig)
|
||||
for {
|
||||
select {
|
||||
case payload := <- payloadChan:
|
||||
// do something with the subscription payload
|
||||
case err := <- subscription.Err():
|
||||
// do something with the subscription error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The .toml file being used to fill the Ethereum subscription config would look something like this:
|
||||
|
||||
```toml
|
||||
[watcher]
|
||||
[watcher.ethSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[watcher.ethSubscription.headerFilter]
|
||||
off = false
|
||||
uncles = false
|
||||
[watcher.ethSubscription.txFilter]
|
||||
off = false
|
||||
src = []
|
||||
dst = []
|
||||
[watcher.ethSubscription.receiptFilter]
|
||||
off = false
|
||||
contracts = []
|
||||
topic0s = []
|
||||
topic1s = []
|
||||
topic2s = []
|
||||
topic3s = []
|
||||
[watcher.ethSubscription.stateFilter]
|
||||
off = false
|
||||
addresses = []
|
||||
intermediateNodes = false
|
||||
[watcher.ethSubscription.storageFilter]
|
||||
off = true
|
||||
addresses = []
|
||||
storageKeys = []
|
||||
intermediateNodes = false
|
||||
```
|
||||
|
||||
These configuration parameters are broken down as follows:
|
||||
|
||||
`ethSubscription.wsPath` is used to define the watcher ws url OR ipc endpoint to subscribe to
|
||||
|
||||
`ethSubscription.historicalData` specifies whether or not ipld-eth-server should look up historical data in its cache and
|
||||
send that to the subscriber, if this is set to `false` then only newly synced/incoming data is streamed
|
||||
|
||||
`ethSubscription.historicalDataOnly` will tell ipld-eth-server to only send historical data with the specified range and
|
||||
not stream forward syncing data
|
||||
|
||||
`ethSubscription.startingBlock` is the starting block number for the range to receive data in
|
||||
|
||||
`ethSubscription.endingBlock` is the ending block number for the range to receive data in;
|
||||
setting to 0 means the process will continue streaming indefinitely.
|
||||
|
||||
`ethSubscription.headerFilter` has two sub-options: `off` and `uncles`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any headers to the subscriber
|
||||
- setting `uncles` to true tells ipld-eth-server to send uncles in addition to normal headers.
|
||||
|
||||
`ethSubscription.txFilter` has three sub-options: `off`, `src`, and `dst`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any transactions to the subscriber
|
||||
- `src` and `dst` are string arrays which can be filled with ETH addresses to filter transactions for,
|
||||
if they have any addresses then ipld-eth-server will only send transactions that were sent or received by the addresses contained
|
||||
in `src` and `dst`, respectively.
|
||||
|
||||
`ethSubscription.receiptFilter` has four sub-options: `off`, `topics`, `contracts` and `matchTxs`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any receipts to the subscriber
|
||||
- `topic0s` is a string array which can be filled with event topics to filter for,
|
||||
if it has any topics then ipld-eth-server will only send receipts that contain logs which have that topic0.
|
||||
- `contracts` is a string array which can be filled with contract addresses to filter for, if it contains any contract addresses the watcher will
|
||||
only send receipts that correspond to one of those contracts.
|
||||
- `matchTrxs` is a bool which when set to true any receipts that correspond to filtered for transactions will be sent by the watcher, regardless of whether or not the receipt satisfies the `topics` or `contracts` filters.
|
||||
|
||||
`ethSubscription.stateFilter` has three sub-options: `off`, `addresses`, and `intermediateNodes`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any state data to the subscriber
|
||||
- `addresses` is a string array which can be filled with ETH addresses to filter state for,
|
||||
if it has any addresses then ipld-eth-server will only send state leafs (accounts) corresponding to those account addresses.
|
||||
- By default ipld-eth-server only sends along state leafs, to receive branch and extension nodes as well `intermediateNodes` can be set to `true`.
|
||||
|
||||
`ethSubscription.storageFilter` has four sub-options: `off`, `addresses`, `storageKeys`, and `intermediateNodes`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any storage data to the subscriber
|
||||
- `addresses` is a string array which can be filled with ETH addresses to filter storage for,
|
||||
if it has any addresses then ipld-eth-server will only send storage nodes from the storage tries at those state addresses.
|
||||
- `storageKeys` is another string array that can be filled with storage keys to filter storage data for. It is important to note that the storage keys need to be the actual keccak256 hashes, whereas
|
||||
the addresses in the `addresses` fields are pre-hashed ETH addresses.
|
||||
- By default ipld-eth-server only sends along storage leafs, to receive branch and extension nodes as well `intermediateNodes` can be set to `true`.
|
||||
|
||||
### Bitcoin RPC Subscription:
|
||||
An example of how to subscribe to a real-time Bitcoin data feed from ipld-eth-server using the `Stream` RPC method is provided below
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/btc"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/watch"
|
||||
)
|
||||
|
||||
config, _ := btc.NewBtcSubscriptionConfig()
|
||||
rlpConfig, _ := rlp.EncodeToBytes(config)
|
||||
vulcPath := viper.GetString("watcher.btcSubscription.path")
|
||||
rpcClient, _ := rpc.Dial(vulcPath)
|
||||
subClient := client.NewClient(rpcClient)
|
||||
payloadChan := make(chan watch.SubscriptionPayload, 20000)
|
||||
subscription, _ := subClient.Stream(payloadChan, rlpConfig)
|
||||
for {
|
||||
select {
|
||||
case payload := <- payloadChan:
|
||||
// do something with the subscription payload
|
||||
case err := <- subscription.Err():
|
||||
// do something with the subscription error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The .toml file being used to fill the Bitcoin subscription config would look something like this:
|
||||
|
||||
```toml
|
||||
[watcher]
|
||||
[watcher.btcSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[watcher.btcSubscription.headerFilter]
|
||||
off = false
|
||||
[watcher.btcSubscription.txFilter]
|
||||
off = false
|
||||
segwit = false
|
||||
witnessHashes = []
|
||||
indexes = []
|
||||
pkScriptClass = []
|
||||
multiSig = false
|
||||
addresses = []
|
||||
```
|
||||
|
||||
These configuration parameters are broken down as follows:
|
||||
|
||||
`btcSubscription.wsPath` is used to define the ipld-eth-server ws url OR ipc endpoint to subscribe to
|
||||
|
||||
`btcSubscription.historicalData` specifies whether or not ipld-eth-server should look up historical data in its cache and
|
||||
send that to the subscriber, if this is set to `false` then ipld-eth-server only streams newly synced/incoming data
|
||||
|
||||
`btcSubscription.historicalDataOnly` will tell ipld-eth-server to only send historical data with the specified range and
|
||||
not stream forward syncing data
|
||||
|
||||
`btcSubscription.startingBlock` is the starting block number for the range to receive data in
|
||||
|
||||
`btcSubscription.endingBlock` is the ending block number for the range to receive data in;
|
||||
setting to 0 means the process will continue streaming indefinitely.
|
||||
|
||||
`btcSubscription.headerFilter` has one sub-option: `off`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to
|
||||
not send any headers to the subscriber.
|
||||
- Additional header-filtering options will be added in the future.
|
||||
|
||||
`btcSubscription.txFilter` has seven sub-options: `off`, `segwit`, `witnessHashes`, `indexes`, `pkScriptClass`, `multiSig`, and `addresses`.
|
||||
|
||||
- Setting `off` to true tells ipld-eth-server to not send any transactions to the subscriber.
|
||||
- Setting `segwit` to true tells ipld-eth-server to only send segwit transactions.
|
||||
- `witnessHashes` is a string array that can be filled with witness hash string; if it contains any hashes ipld-eth-server will only send transactions that contain one of those hashes.
|
||||
- `indexes` is an int64 array that can be filled with tx index numbers; if it contains any integers ipld-eth-server will only send transactions at those indexes (e.g. `[0]` will send only coinbase transactions)
|
||||
- `pkScriptClass` is an uint8 array that can be filled with pk script class numbers; if it contains any integers ipld-eth-server will only send transactions that have at least one tx output with one of the specified pkscript classes;
|
||||
possible class types are 0 through 8 as defined [here](https://github.com/btcsuite/btcd/blob/master/txscript/standard.go#L52).
|
||||
- Setting `multisig` to true tells ipld-eth-server to send only multi-sig transactions- to send only transaction that have at least one tx output that requires more than one signature to spend.
|
||||
- `addresses` is a string array that can be filled with btc address strings; if it contains any addresses ipld-eth-server will only send transactions that have at least one tx output with at least one of the provided addresses.
|
||||
|
||||
|
||||
### Native API Recapitulation:
|
||||
In addition to providing novel Postgraphile and RPC-Subscription endpoints, we are working towards complete recapitulation of the
|
||||
standard chain APIs. This will allow direct compatibility with software that already makes use of the standard interfaces.
|
||||
|
||||
#### Ethereum JSON-RPC API
|
||||
ipld-eth-server currently faithfully recapitulates portions of the Ethereum JSON-RPC api standard.
|
||||
|
||||
The currently supported endpoints include:
|
||||
`eth_blockNumber`
|
||||
`eth_getLogs`
|
||||
`eth_getHeaderByNumber`
|
||||
`eth_getBlockByNumber`
|
||||
`eth_getBlockByHash`
|
||||
`eth_getTransactionByHash`
|
||||
|
||||
Additional endpoints will be added in the near future, with the immediate goal of recapitulating the largest set of "eth_" endpoints which can be provided as a service.
|
||||
|
||||
#### Bitcoin JSON-RPC API:
|
||||
In the near future, the standard Bitcoin JSON-RPC interfaces will be implemented.
|
||||
@@ -1,160 +0,0 @@
|
||||
# composeAndExecute
|
||||
The `composeAndExecute` command is used to compose and execute over an arbitrary set of custom transformers.
|
||||
This is accomplished by generating a Go pluggin which allows the `vulcanizedb` binary to link to external transformers, so
|
||||
long as they abide by one of the standard [interfaces](../staging/libraries/shared/transformer).
|
||||
|
||||
Additionally, there are separate `compose` and `execute` commands to allow pre-building and linking to a pre-built .so file.
|
||||
|
||||
**NOTE:**
|
||||
1. It is necessary that the .so file was built with the same exact dependencies that are present in the execution environment,
|
||||
i.e. we need to `compose` and `execute` the plugin .so file with the same exact version of vulcanizeDB.
|
||||
1. The plugin migrations are run during the plugin's composition. As such, if `execute` is used to run a prebuilt .so in a different
|
||||
environment than the one it was composed in then the migrations for that plugin will first need to be manually ran against that environment's Postgres database.
|
||||
|
||||
These commands require Go 1.11+ and use [Go plugins](https://golang.org/pkg/plugin/) which only work on Unix-based systems.
|
||||
There is also an ongoing [conflict](https://github.com/golang/go/issues/20481) between Go plugins and the use vendored dependencies which
|
||||
imposes certain limitations on how the plugins are built.
|
||||
|
||||
## Commands
|
||||
The `compose` and `composeAndExecute` commands assume you are in the vulcanizdb directory located at your system's `$GOPATH`,
|
||||
and that all of the transformer repositories for building the plugin are present at their `$GOPATH` directories.
|
||||
|
||||
The `execute` command does not require the plugin transformer dependencies be located in their
|
||||
`$GOPATH` directories, instead it expects a prebuilt .so file (of the name specified in the config file)
|
||||
to be in `$GOPATH/src/github.com/vulcanize/vulcanizedb/plugins/` and, as noted above, also expects the plugin
|
||||
db migrations to have already been ran against the database.
|
||||
|
||||
compose:
|
||||
|
||||
`./vulcanizedb compose --config=./environments/config_name.toml`
|
||||
|
||||
execute:
|
||||
|
||||
`./vulcanizedb execute --config=./environments/config_name.toml`
|
||||
|
||||
composeAndExecute:
|
||||
|
||||
`./vulcanizedb composeAndExecute --config=./environments/config_name.toml`
|
||||
|
||||
## Flags
|
||||
|
||||
The `compose` and `composeAndExecute` commands can be passed optional flags to specify the operation of the watchers:
|
||||
|
||||
- `--recheck-headers`/`-r` - specifies whether to re-check headers for events after the header has already been queried for watched logs.
|
||||
Can be useful for redundancy if you suspect that your node is not always returning all desired logs on every query.
|
||||
Argument is expected to be a boolean: e.g. `-r=true`.
|
||||
Defaults to `false`.
|
||||
|
||||
- `query-recheck-interval`/`-q` - specifies interval for re-checking storage diffs that haven been queued for later processing
|
||||
(by default, the storage watched queues storage diffs if transformer execution fails, on the assumption that subsequent data derived from the event transformers may enable us to decode storage keys that we don't recognize right now).
|
||||
Argument is expected to be a duration (integer measured in nanoseconds): e.g. `-q=10m30s` (for 10 minute, 30 second intervals).
|
||||
Defaults to `5m` (5 minutes).
|
||||
|
||||
## Configuration
|
||||
A .toml config file is specified when executing the commands.
|
||||
The config provides information for composing a set of transformers from external repositories:
|
||||
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
user = "vulcanize"
|
||||
password = "vulcanize"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[exporter]
|
||||
home = "github.com/vulcanize/vulcanizedb"
|
||||
name = "exampleTransformerExporter"
|
||||
save = false
|
||||
transformerNames = [
|
||||
"transformer1",
|
||||
"transformer2",
|
||||
"transformer3",
|
||||
"transformer4",
|
||||
]
|
||||
[exporter.transformer1]
|
||||
path = "path/to/transformer1"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer2]
|
||||
path = "path/to/transformer2"
|
||||
type = "eth_contract"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer3]
|
||||
path = "path/to/transformer3"
|
||||
type = "eth_event"
|
||||
repository = "github.com/account/repo"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
[exporter.transformer4]
|
||||
path = "path/to/transformer4"
|
||||
type = "eth_storage"
|
||||
repository = "github.com/account2/repo2"
|
||||
migrations = "to/db/migrations"
|
||||
rank = "1"
|
||||
```
|
||||
- `home` is the name of the package you are building the plugin for, in most cases this is github.com/vulcanize/vulcanizedb
|
||||
- `name` is the name used for the plugin files (.so and .go)
|
||||
- `save` indicates whether or not the user wants to save the .go file instead of removing it after .so compilation. Sometimes useful for debugging/trouble-shooting purposes.
|
||||
- `transformerNames` is the list of the names of the transformers we are composing together, so we know how to access their submaps in the exporter map
|
||||
- `exporter.<transformerName>`s are the sub-mappings containing config info for the transformers
|
||||
- `repository` is the path for the repository which contains the transformer and its `TransformerInitializer`
|
||||
- `path` is the relative path from `repository` to the transformer's `TransformerInitializer` directory (initializer package).
|
||||
- Transformer repositories need to be cloned into the user's $GOPATH (`go get`)
|
||||
- `type` is the type of the transformer; indicating which type of watcher it works with (for now, there are only two options: `eth_event` and `eth_storage`)
|
||||
- `eth_storage` indicates the transformer works with the [storage watcher](../../staging/libraries/shared/watcher/storage_watcher.go)
|
||||
that fetches state and storage diffs from an ETH node (instead of, for example, from IPFS)
|
||||
- `eth_event` indicates the transformer works with the [event watcher](../../staging/libraries/shared/watcher/event_watcher.go)
|
||||
that fetches event logs from an ETH node
|
||||
- `eth_contract` indicates the transformer works with the [contract watcher](../staging/libraries/shared/watcher/contract_watcher.go)
|
||||
that is made to work with [contract_watcher pkg](../../staging/pkg/contract_watcher)
|
||||
based transformers which work with either a header or full sync vDB to watch events and poll public methods ([example1](https://github.com/vulcanize/account_transformers/tree/master/transformers/account/light), [example2](https://github.com/vulcanize/ens_transformers/tree/working/transformers/domain_records))
|
||||
- `migrations` is the relative path from `repository` to the db migrations directory for the transformer
|
||||
- `rank` determines the order that migrations are ran, with lower ranked migrations running first
|
||||
- this is to help isolate any potential conflicts between transformer migrations
|
||||
- start at "0"
|
||||
- use strings
|
||||
- don't leave gaps
|
||||
- transformers with identical migrations/migration paths should share the same rank
|
||||
- Note: If any of the imported transformers need additional config variables those need to be included as well
|
||||
|
||||
This information is used to write and build a Go plugin which exports the configured transformers.
|
||||
These transformers are loaded onto their specified watchers and executed.
|
||||
|
||||
Transformers of different types can be run together in the same command using a single config file or in separate instances using different config files
|
||||
|
||||
The general structure of a plugin .go file, and what we would see built with the above config is shown below
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
interface1 "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
transformer1 "github.com/account/repo/path/to/transformer1"
|
||||
transformer2 "github.com/account/repo/path/to/transformer2"
|
||||
transformer3 "github.com/account/repo/path/to/transformer3"
|
||||
transformer4 "github.com/account2/repo2/path/to/transformer4"
|
||||
)
|
||||
|
||||
type exporter string
|
||||
|
||||
var Exporter exporter
|
||||
|
||||
func (e exporter) Export() []interface1.EventTransformerInitializer, []interface1.StorageTransformerInitializer, []interface1.ContractTransformerInitializer {
|
||||
return []interface1.TransformerInitializer{
|
||||
transformer1.TransformerInitializer,
|
||||
transformer3.TransformerInitializer,
|
||||
}, []interface1.StorageTransformerInitializer{
|
||||
transformer4.StorageTransformerInitializer,
|
||||
}, []interface1.ContractTransformerInitializer{
|
||||
transformer2.TransformerInitializer,
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,160 +0,0 @@
|
||||
# contractWatcher
|
||||
The `contractWatcher` command is a built-in generic contract watcher. It can watch any and all events for a given contract provided the contract's ABI is available.
|
||||
It also provides some state variable coverage by automating polling of public methods, with some restrictions:
|
||||
1. The method must have 2 or less arguments
|
||||
1. The method's arguments must all be of type address or bytes32 (hash)
|
||||
1. The method must return a single value
|
||||
|
||||
This command operates in two modes- `header` and `full`- which require a header or full-synced vulcanizeDB, respectively.
|
||||
|
||||
This command requires the contract ABI be available on Etherscan if it is not provided in the config file by the user.
|
||||
|
||||
If method polling is turned on we require an archival node at the ETH ipc endpoint in our config, whether or not we are operating in `header` or `full` mode.
|
||||
Otherwise we only need to connect to a full node.
|
||||
|
||||
## Configuration
|
||||
This command takes a config of the form:
|
||||
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "/Users/user/Library/Ethereum/geth.ipc"
|
||||
|
||||
[contract]
|
||||
network = ""
|
||||
addresses = [
|
||||
"contractAddress1",
|
||||
"contractAddress2"
|
||||
]
|
||||
[contract.contractAddress1]
|
||||
abi = 'ABI for contract 1'
|
||||
startingBlock = 982463
|
||||
[contract.contractAddress2]
|
||||
abi = 'ABI for contract 2'
|
||||
events = [
|
||||
"event1",
|
||||
"event2"
|
||||
]
|
||||
eventArgs = [
|
||||
"arg1",
|
||||
"arg2"
|
||||
]
|
||||
methods = [
|
||||
"method1",
|
||||
"method2"
|
||||
]
|
||||
methodArgs = [
|
||||
"arg1",
|
||||
"arg2"
|
||||
]
|
||||
startingBlock = 4448566
|
||||
piping = true
|
||||
````
|
||||
|
||||
- The `contract` section defines which contracts we want to watch and with which conditions.
|
||||
- `network` is only necessary if the ABIs are not provided and wish to be fetched from Etherscan.
|
||||
- Empty or nil string indicates mainnet
|
||||
- "ropsten", "kovan", and "rinkeby" indicate their respective networks
|
||||
- `addresses` lists the contract addresses we are watching and is used to load their individual configuration parameters
|
||||
- `contract.<contractAddress>` are the sub-mappings which contain the parameters specific to each contract address
|
||||
- `abi` is the ABI for the contract; if none is provided the application will attempt to fetch one from Etherscan using the provided address and network
|
||||
- `events` is the list of events to watch
|
||||
- If this field is omitted or no events are provided then by defualt ALL events extracted from the ABI will be watched
|
||||
- If event names are provided then only those events will be watched
|
||||
- `eventArgs` is the list of arguments to filter events with
|
||||
- If this field is omitted or no eventArgs are provided then by default watched events are not filtered by their argument values
|
||||
- If eventArgs are provided then only those events which emit at least one of these values as an argument are watched
|
||||
- `methods` is the list of methods to poll
|
||||
- If this is omitted or no methods are provided then by default NO methods are polled
|
||||
- If method names are provided then those methods will be polled, provided
|
||||
1) Method has two or less arguments
|
||||
1) Arguments are all of address or hash types
|
||||
1) Method returns a single value
|
||||
- `methodArgs` is the list of arguments to limit polling methods to
|
||||
- If this field is omitted or no methodArgs are provided then by default methods will be polled with every combination of the appropriately typed values that have been collected from watched events
|
||||
- If methodArgs are provided then only those values will be used to poll methods
|
||||
- `startingBlock` is the block we want to begin watching the contract, usually the deployment block of that contract
|
||||
- `piping` is a boolean flag which indicates whether or not we want to pipe return method values forward as arguments to subsequent method calls
|
||||
|
||||
At the very minimum, for each contract address an ABI and a starting block number need to be provided (or just the starting block if the ABI can be reliably fetched from Etherscan).
|
||||
With just this information we will be able to watch all events at the contract, but with no additional filters and no method polling.
|
||||
|
||||
## Output
|
||||
|
||||
Transformed events and polled method results are committed to Postgres in schemas and tables generated according to the contract abi.
|
||||
|
||||
Schemas are created for each contract using the naming convention `<sync-type>_<lowercase contract-address>`
|
||||
Under this schema, tables are generated for watched events as `<lowercase event name>_event` and for polled methods as `<lowercase method name>_method`
|
||||
The 'method' and 'event' identifiers are tacked onto the end of the table names to prevent collisions between methods and events of the same lowercase name
|
||||
|
||||
## Example:
|
||||
|
||||
Modify `./environments/example.toml` to replace the empty `ipcPath` with a path that points to an ethjson_rpc endpoint (e.g. a local geth node ipc path or an Infura url).
|
||||
This endpoint should be for an archival eth node if we want to perform method polling as this configuration is currently set up to do. To work with a non-archival full node,
|
||||
remove the `balanceOf` method from the `0x8dd5fbce2f6a956c3022ba3663759011dd51e73e` (TrueUSD) contract.
|
||||
|
||||
If you are operating a header sync vDB, run:
|
||||
|
||||
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=header`
|
||||
|
||||
If instead you are operating a full sync vDB and provided an archival node IPC path, run in full mode:
|
||||
|
||||
`./vulcanizedb contractWatcher --config=./environments/example.toml --mode=full`
|
||||
|
||||
This will run the contractWatcher and configures it to watch the contracts specified in the config file. Note that
|
||||
by default we operate in `header` mode but the flag is included here to demonstrate its use.
|
||||
|
||||
The example config we link to in this example watches two contracts, the ENS Registry (0x314159265dD8dbb310642f98f50C066173C1259b) and TrueUSD (0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E).
|
||||
|
||||
Because the ENS Registry is configured with only an ABI and a starting block, we will watch all events for this contract and poll none of its methods. Note that the ENS Registry is an example
|
||||
of a contract which does not have its ABI available over Etherscan and must have it included in the config file.
|
||||
|
||||
The TrueUSD contract is configured with two events (`Transfer` and `Mint`) and a single method (`balanceOf`), as such it will watch these two events and use any addresses it collects emitted from them
|
||||
to poll the `balanceOf` method with those addresses at every block. Note that we do not provide an ABI for TrueUSD as its ABI can be fetched from Etherscan.
|
||||
|
||||
For the ENS contract, it produces and populates a schema with four tables"
|
||||
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newowner_event`
|
||||
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newresolver_event`
|
||||
`header_0x314159265dd8dbb310642f98f50c066173c1259b.newttl_event`
|
||||
`header_0x314159265dd8dbb310642f98f50c066173c1259b.transfer_event`
|
||||
|
||||
For the TrusUSD contract, it produces and populates a schema with three tables:
|
||||
|
||||
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event`
|
||||
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.mint_event`
|
||||
`header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method`
|
||||
|
||||
Column ids and types for these tables are generated based on the event and method argument names and types and method return types, resulting in tables such as:
|
||||
|
||||
Table "header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event"
|
||||
|
||||
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
|
||||
|:----------:|:---------------------:|:---------:|:--------:|:--------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
||||
| id | integer | | not null | nextval('header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.transfer_event_id_seq'::regclass) | plain | | |
|
||||
| header_id | integer | | not null | | plain | | |
|
||||
| token_name | character varying(66) | | not null | | extended | | |
|
||||
| raw_log | jsonb | | | | extended | | |
|
||||
| log_idx | integer | | not null | | plain | | |
|
||||
| tx_idx | integer | | not null | | plain | | |
|
||||
| from_ | character varying(66) | | not null | | extended | | |
|
||||
| to_ | character varying(66) | | not null | | extended | | |
|
||||
| value_ | numeric | | not null | | main | | |
|
||||
|
||||
|
||||
Table "header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method"
|
||||
|
||||
| Column | Type | Collation | Nullable | Default | Storage | Stats target | Description |
|
||||
|:----------:|:---------------------:|:---------:|:--------:|:----------------------------------------------------------------------------------------------:|:--------:|:------------:|:-----------:|
|
||||
| id | integer | | not null | nextval('header_0x8dd5fbce2f6a956c3022ba3663759011dd51e73e.balanceof_method_id_seq'::regclass) | plain | | |
|
||||
| token_name | character varying(66) | | not null | | extended | | |
|
||||
| block | integer | | not null | | plain | | |
|
||||
| who_ | character varying(66) | | not null | | extended | | |
|
||||
| returned | numeric | | not null | | main | | |
|
||||
|
||||
The addition of '_' after table names is to prevent collisions with reserved Postgres words.
|
||||
|
||||
Also notice that the contract address used for the schema name has been down-cased.
|
||||
@@ -1,11 +0,0 @@
|
||||
# Contribution guidelines
|
||||
|
||||
Contributions are welcome! In addition to core contributions, developers are encouraged to build their own custom transformers which
|
||||
can be run together with other custom transformers using the [composeAndExeucte](../../staging/documentation/composeAndExecute.md) command.
|
||||
|
||||
## Creating a new migration file
|
||||
1. `make new_migration NAME=add_columnA_to_table1`
|
||||
- This will create a new timestamped migration file in `db/migrations`
|
||||
1. Write the migration code in the created file, under the respective `goose` pragma
|
||||
- Goose automatically runs each migration in a transaction; don't add `BEGIN` and `COMMIT` statements.
|
||||
1. Core migrations should be committed in their `goose fix`ed form.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Syncing commands
|
||||
These commands are used to sync raw Ethereum data into Postgres.
|
||||
|
||||
## headerSync
|
||||
Syncs VulcanizeDB with the configured Ethereum node, populating only block headers.
|
||||
This command is useful when you want a minimal baseline from which to track targeted data on the blockchain (e.g. individual smart contract storage values or event logs).
|
||||
1. Start Ethereum node
|
||||
1. In a separate terminal start VulcanizeDB:
|
||||
- `./vulcanizedb headerSync --config <config.toml> --starting-block-number <block-number>`
|
||||
|
||||
## sync
|
||||
Syncs VulcanizeDB with the configured Ethereum node, populating blocks, transactions, receipts, and logs.
|
||||
This command is useful when you want to maintain a broad cache of what's happening on the blockchain.
|
||||
1. Start Ethereum node (**if fast syncing your Ethereum node, wait for initial sync to finish**)
|
||||
1. In a separate terminal start VulcanizeDB:
|
||||
- `./vulcanizedb sync --config <config.toml> --starting-block-number <block-number>`
|
||||
|
||||
## coldImport
|
||||
Sync VulcanizeDB from the LevelDB underlying a Geth node.
|
||||
1. Assure node is not running, and that it has synced to the desired block height.
|
||||
1. Start vulcanize_db
|
||||
- `./vulcanizedb coldImport --config <config.toml>`
|
||||
1. Optional flags:
|
||||
- `--starting-block-number <block number>`/`-s <block number>`: block number to start syncing from
|
||||
- `--ending-block-number <block number>`/`-e <block number>`: block number to sync to
|
||||
- `--all`/`-a`: sync all missing blocks
|
||||
@@ -1,40 +0,0 @@
|
||||
# Custom transformers
|
||||
When the capabilities of the generic `contractWatcher` are not sufficient, custom transformers tailored to a specific
|
||||
purpose can be leveraged.
|
||||
|
||||
Individual transformers can be composed together from any number of external repositories and executed as a single process using
|
||||
the `compose` and `execute` commands or the `composeAndExecute` command.
|
||||
|
||||
## Writing custom transformers
|
||||
For help with writing different types of custom transformers for the `composeAndExecute` set of commands, please see the below:
|
||||
|
||||
Storage Transformers
|
||||
* [Guide](../../staging/libraries/shared/factories/storage/README.md)
|
||||
* [Example](../../staging/libraries/shared/factories/storage/EXAMPLE.md)
|
||||
|
||||
Event Transformers
|
||||
* [Guide](../../staging/libraries/shared/factories/event/README.md)
|
||||
* [Example 1](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registar)
|
||||
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/registry)
|
||||
* [Example 3](https://github.com/vulcanize/ens_transformers/tree/master/transformers/resolver)
|
||||
|
||||
Contract Transformers
|
||||
* [Example 1](https://github.com/vulcanize/account_transformers)
|
||||
* [Example 2](https://github.com/vulcanize/ens_transformers/tree/master/transformers/domain_records)
|
||||
|
||||
## Preparing custom transformers to work as part of a plugin
|
||||
To plug in an external transformer we need to:
|
||||
|
||||
1. Create a package that exports a variable `TransformerInitializer`, `StorageTransformerInitializer`, or `ContractTransformerInitializer` that are of type [TransformerInitializer](../staging/libraries/shared/transformer/event_transformer.go#L33)
|
||||
or [StorageTransformerInitializer](../../staging/libraries/shared/transformer/storage_transformer.go#L31),
|
||||
or [ContractTransformerInitializer](../../staging/libraries/shared/transformer/contract_transformer.go#L31), respectively
|
||||
2. Design the transformers to work in the context of their [event](../staging/libraries/shared/watcher/event_watcher.go#L83),
|
||||
[storage](../../staging/libraries/shared/watcher/storage_watcher.go#L53),
|
||||
or [contract](../../staging/libraries/shared/watcher/contract_watcher.go#L68) watcher execution modes
|
||||
3. Create db migrations to run against vulcanizeDB so that we can store the transformer output
|
||||
* Do not `goose fix` the transformer migrations, this is to ensure they are always ran after the core vulcanizedb migrations which are kept in their fixed form
|
||||
* Specify migration locations for each transformer in the config with the `exporter.transformer.migrations` fields
|
||||
* If the base vDB migrations occupy this path as well, they need to be in their `goose fix`ed form
|
||||
as they are [here](../../staging/db/migrations)
|
||||
|
||||
To update a plugin repository with changes to the core vulcanizedb repository, run `dep ensure` to update its dependencies.
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Runs the db migrations and starts the watcher services
|
||||
|
||||
# Construct the connection string for postgres
|
||||
VDB_PG_CONNECT=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable
|
||||
|
||||
# Run the DB migrations
|
||||
echo "Connecting with: $VDB_PG_CONNECT"
|
||||
echo "Running database migrations"
|
||||
./goose -dir migrations/vulcanizedb postgres "$VDB_PG_CONNECT" up
|
||||
rv=$?
|
||||
|
||||
if [ $rv != 0 ]; then
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "Beginning the ipld-eth-server process"
|
||||
|
||||
echo running: ./ipld-eth-server ${VDB_COMMAND} --config=config.toml
|
||||
./ipld-eth-server ${VDB_COMMAND} --config=config.toml
|
||||
rv=$?
|
||||
|
||||
if [ $rv != 0 ]; then
|
||||
echo "ipld-eth-server startup failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,38 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = ""
|
||||
|
||||
[exporter]
|
||||
home = "github.com/vulcanize/vulcanizedb"
|
||||
name = "accountTransformerExporter"
|
||||
save = false
|
||||
transformerNames = [
|
||||
"account"
|
||||
]
|
||||
[exporter.account]
|
||||
path = "transformers/account/light/initializer"
|
||||
type = "eth_contract"
|
||||
repository = "github.com/vulcanize/account_transformers"
|
||||
migrations = "db/migrations"
|
||||
rank = "0"
|
||||
|
||||
[token]
|
||||
addresses = [
|
||||
"0x58b6A8A3302369DAEc383334672404Ee733aB239",
|
||||
"0x862Da0A691bb0b74038377295f8fF523D0493eB4",
|
||||
]
|
||||
[token.equivalents]
|
||||
0x0000000000085d4780B73119b644AE5ecd22b376 = [
|
||||
"0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E"
|
||||
]
|
||||
0x58b6A8A3302369DAEc383334672404Ee733aB239 = [
|
||||
"0x8e306b005773bee6bA6A6e8972Bc79D766cC15c8"
|
||||
]
|
||||
|
||||
[account]
|
||||
start = 0
|
||||
addresses = []
|
||||
+11
-23
@@ -1,26 +1,14 @@
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "postgres" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[client]
|
||||
ipcPath = ""
|
||||
[log]
|
||||
level = "info" # $LOGRUS_LEVEL
|
||||
|
||||
[contract]
|
||||
network = ""
|
||||
addresses = [
|
||||
"0x314159265dD8dbb310642f98f50C066173C1259b",
|
||||
"0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E"
|
||||
]
|
||||
[contract.0x314159265dD8dbb310642f98f50C066173C1259b]
|
||||
abi = '[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}]'
|
||||
startingBlock = 3327417
|
||||
[contract.0x8dd5fbCe2F6a956C3022bA3663759011Dd51e73E]
|
||||
events = [
|
||||
"Transfer",
|
||||
"Issue"
|
||||
]
|
||||
methods = [
|
||||
"balanceOf"
|
||||
]
|
||||
startingBlock = 5197514
|
||||
[server]
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SERVER_IPC_PATH
|
||||
wsPath = "127.0.0.1:8081" # $SERVER_WS_PATH
|
||||
httpPath = "127.0.0.1:8082" # $SERVER_HTTP_PATH
|
||||
@@ -1,7 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = ""
|
||||
@@ -1,7 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_private"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "http://127.0.0.1:7545"
|
||||
@@ -1,8 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_public"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = <local node's IPC filepath>
|
||||
levelDbPath = <local node's LevelDB chaindata filepath>
|
||||
@@ -0,0 +1,30 @@
|
||||
[watcher]
|
||||
[watcher.ethSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[watcher.ethSubscription.headerFilter]
|
||||
off = false
|
||||
uncles = false
|
||||
[watcher.ethSubscription.txFilter]
|
||||
off = false
|
||||
src = []
|
||||
dst = []
|
||||
[watcher.ethSubscription.receiptFilter]
|
||||
off = false
|
||||
contracts = []
|
||||
topic0s = []
|
||||
topic1s = []
|
||||
topic2s = []
|
||||
topic3s = []
|
||||
[watcher.ethSubscription.stateFilter]
|
||||
off = false
|
||||
addresses = []
|
||||
intermediateNodes = false
|
||||
[watcher.ethSubscription.storageFilter]
|
||||
off = true
|
||||
addresses = []
|
||||
storageKeys = []
|
||||
intermediateNodes = false
|
||||
@@ -0,0 +1,7 @@
|
||||
[database]
|
||||
name = "vulcanize_testing"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "http://127.0.0.1:8545"
|
||||
@@ -0,0 +1,23 @@
|
||||
module github.com/vulcanize/ipld-eth-server
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/ethereum/go-ethereum v1.9.11
|
||||
github.com/ipfs/go-cid v0.0.5
|
||||
github.com/ipfs/go-ipfs-blockstore v1.0.0
|
||||
github.com/ipfs/go-ipfs-ds-help v1.0.0
|
||||
github.com/ipfs/go-ipld-format v0.2.0
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/lib/pq v1.5.2
|
||||
github.com/multiformats/go-multihash v0.0.13
|
||||
github.com/onsi/ginkgo v1.12.1
|
||||
github.com/onsi/gomega v1.10.1
|
||||
github.com/sirupsen/logrus v1.6.0
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/vulcanize/ipld-eth-indexer v0.2.0-alpha
|
||||
github.com/vulcanize/pg-ipfs-ethdb v0.0.1-alpha
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.9.11 => github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.5
|
||||
@@ -1,62 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/client"
|
||||
vRpc "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/node"
|
||||
"github.com/vulcanize/vulcanizedb/test_config"
|
||||
)
|
||||
|
||||
var _ = Describe("Rewards calculations", func() {
|
||||
|
||||
It("calculates a block reward for a real block", func() {
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := vRpc.NewRpcTransactionConverter(ethClient)
|
||||
blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
block, err := blockChain.GetBlockByNumber(1071819)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block.Reward).To(Equal("5313550000000000000"))
|
||||
})
|
||||
|
||||
It("calculates an uncle reward for a real block", func() {
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := vRpc.NewRpcTransactionConverter(ethClient)
|
||||
blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
block, err := blockChain.GetBlockByNumber(1071819)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block.UnclesReward).To(Equal("6875000000000000000"))
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,110 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/client"
|
||||
rpc2 "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/node"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/testing"
|
||||
"github.com/vulcanize/vulcanizedb/test_config"
|
||||
)
|
||||
|
||||
var _ = Describe("Reading contracts", func() {
|
||||
|
||||
Describe("Getting a contract attribute", func() {
|
||||
It("retrieves the event log for a specific block and contract", func() {
|
||||
expectedLogZero := core.Log{
|
||||
BlockNumber: 4703824,
|
||||
TxHash: "0xf896bfd1eb539d881a1a31102b78de9f25cd591bf1fe1924b86148c0b205fd5d",
|
||||
Address: "0xd26114cd6ee289accf82350c8d8487fedb8a0c07",
|
||||
Topics: core.Topics{
|
||||
0: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
|
||||
1: "0x000000000000000000000000fbb1b73c4f0bda4f67dca266ce6ef42f520fbb98",
|
||||
2: "0x000000000000000000000000d26114cd6ee289accf82350c8d8487fedb8a0c07",
|
||||
},
|
||||
Index: 19,
|
||||
Data: "0x0000000000000000000000000000000000000000000000000c7d713b49da0000"}
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
|
||||
blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
contract := testing.SampleContract()
|
||||
|
||||
logs, err := blockChain.GetLogs(contract, big.NewInt(4703824), nil)
|
||||
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(logs)).To(Equal(3))
|
||||
Expect(logs[0]).To(Equal(expectedLogZero))
|
||||
})
|
||||
|
||||
It("returns and empty log array when no events for a given block / contract combo", func() {
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
|
||||
blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
|
||||
logs, err := blockChain.GetLogs(core.Contract{Hash: "0x123"}, big.NewInt(4703824), nil)
|
||||
|
||||
Expect(err).To(BeNil())
|
||||
Expect(len(logs)).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Fetching Contract data", func() {
|
||||
It("returns the correct attribute for a real contract", func() {
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
|
||||
blockChain := geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
|
||||
contract := testing.SampleContract()
|
||||
var balance = new(big.Int)
|
||||
|
||||
args := make([]interface{}, 1)
|
||||
args[0] = common.HexToHash("0xd26114cd6ee289accf82350c8d8487fedb8a0c07")
|
||||
|
||||
err = blockChain.FetchContractData(contract.Abi, "0xd26114cd6ee289accf82350c8d8487fedb8a0c07", "balanceOf", args, &balance, 5167471)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
expected := new(big.Int)
|
||||
expected.SetString("10897295492887612977137", 10)
|
||||
Expect(balance).To(Equal(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,322 +0,0 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/full/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||
)
|
||||
|
||||
var _ = Describe("contractWatcher full transformer", func() {
|
||||
var db *postgres.DB
|
||||
var err error
|
||||
var blockChain core.BlockChain
|
||||
var blockRepository repositories.BlockRepository
|
||||
var ensAddr = strings.ToLower(constants.EnsContractAddress)
|
||||
var tusdAddr = strings.ToLower(constants.TusdContractAddress)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
BeforeEach(func() {
|
||||
db, blockChain = test_helpers.SetupDBandBC()
|
||||
blockRepository = *repositories.NewBlockRepository(db)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
test_helpers.TearDown(db)
|
||||
})
|
||||
|
||||
Describe("Init", func() {
|
||||
It("Initializes transformer's contract objects", func() {
|
||||
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
|
||||
Expect(c.StartingBlock).To(Equal(int64(6194633)))
|
||||
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
||||
Expect(c.Name).To(Equal("TrueUSD"))
|
||||
Expect(c.Address).To(Equal(tusdAddr))
|
||||
})
|
||||
|
||||
It("Fails to initialize if first and most recent blocks cannot be fetched from vDB", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("Does nothing if watched events are unset", func() {
|
||||
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Events = nil
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no filters created"))
|
||||
|
||||
_, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Execute", func() {
|
||||
BeforeEach(func() {
|
||||
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.TransferBlock2)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("Transforms watched contract data into custom repositories", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
log := test_helpers.TransferLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.transfer_event WHERE block = 6194634", tusdAddr)).StructScan(&log)
|
||||
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654eee"))
|
||||
Expect(log.Block).To(Equal(int64(6194634)))
|
||||
Expect(log.From).To(Equal("0x000000000000000000000000000000000000Af21"))
|
||||
Expect(log.To).To(Equal("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391"))
|
||||
Expect(log.Value).To(Equal("1097077688018008265106216665536940668749033598146"))
|
||||
})
|
||||
|
||||
It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
b, ok := c.EmittedAddrs[common.HexToAddress("0x000000000000000000000000000000000000Af21")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843bCE061BA391")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843b1234567890")]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x")]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[""]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x09THISE21a5IS5cFAKE1D82fAND43bCE06MADEUP")]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
|
||||
It("Polls given methods using generated token holder address", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.BalanceOf{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x000000000000000000000000000000000000Af21' AND block = '6194634'", tusdAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Balance).To(Equal("0"))
|
||||
Expect(res.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843bCE061BA391' AND block = '6194634'", tusdAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Balance).To(Equal("0"))
|
||||
Expect(res.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6194634'", tusdAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("Fails if initialization has not been done", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("transformer has no initialized contracts to work with"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Execute- against ENS registry contract", func() {
|
||||
BeforeEach(func() {
|
||||
_, insertErr := blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := blockRepository.CreateOrUpdateBlock(mocks.NewOwnerBlock2)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("Transforms watched contract data into custom repositories", func() {
|
||||
t := transformer.NewTransformer(test_helpers.ENSConfig, blockChain, db)
|
||||
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
log := test_helpers.NewOwnerLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(log.Tx).To(Equal("0x135391a0962a63944e5908e6fedfff90fb4be3e3290a21017861099bad654bbb"))
|
||||
Expect(log.Block).To(Equal(int64(6194635)))
|
||||
Expect(log.Node).To(Equal("0x0000000000000000000000000000000000000000000000000000c02aaa39b223"))
|
||||
Expect(log.Label).To(Equal("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391"))
|
||||
Expect(log.Owner).To(Equal("0x000000000000000000000000000000000000Af21"))
|
||||
})
|
||||
|
||||
It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, ok := t.Contracts[ensAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(c.EmittedHashes)).To(Equal(3))
|
||||
|
||||
b, ok := c.EmittedHashes[common.HexToHash("0x0000000000000000000000000000000000000000000000000000c02aaa39b223")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedHashes[common.HexToHash("0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
// Doesn't keep track of address since it wouldn't be used in calling the 'owner' method
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x000000000000000000000000000000000000Af21")]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
|
||||
It("Polls given methods using generated token holder address", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.Owner{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x0000000000000000000000000000000000000000000000000000c02aaa39b223' AND block = '6194636'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("It does not perist events if they do not pass the emitted arg filter", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.EventArgs = map[string][]string{
|
||||
ensAddr: {"fake_filter_value"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
||||
})
|
||||
|
||||
It("If a method arg filter is applied, only those arguments are used in polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.MethodArgs = map[string][]string{
|
||||
ensAddr: {"0x0000000000000000000000000000000000000000000000000000c02aaa39b223"},
|
||||
}
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.Owner{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x0000000000000000000000000000000000000000000000000000c02aaa39b223' AND block = '6194636'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x9dd48110dcc444fdc242510c09bbbbe21a5975cac061d82f7b843bce061ba391' AND block = '6194636'", ensAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,445 +0,0 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/config"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/header/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers/mocks"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories"
|
||||
)
|
||||
|
||||
var _ = Describe("contractWatcher headerSync transformer", func() {
|
||||
var db *postgres.DB
|
||||
var err error
|
||||
var blockChain core.BlockChain
|
||||
var headerRepository repositories.HeaderRepository
|
||||
var headerID int64
|
||||
var ensAddr = strings.ToLower(constants.EnsContractAddress)
|
||||
var tusdAddr = strings.ToLower(constants.TusdContractAddress)
|
||||
|
||||
BeforeEach(func() {
|
||||
db, blockChain = test_helpers.SetupDBandBC()
|
||||
headerRepository = repositories.NewHeaderRepository(db)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
test_helpers.TearDown(db)
|
||||
})
|
||||
|
||||
Describe("Init", func() {
|
||||
It("Initializes transformer's contract objects", func() {
|
||||
_, insertErr := headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
|
||||
// TODO: Fix this
|
||||
// This test sometimes randomly fails because
|
||||
// for some reason the starting block number is not updated from
|
||||
// its original value (5197514) to the block number (6194632)
|
||||
// of the earliest header (mocks.MockHeader1) in the repository
|
||||
// It is not clear how this happens without one of the above insertErrs
|
||||
// having been thrown and without any errors thrown during the Init() call
|
||||
Expect(c.StartingBlock).To(Equal(int64(6194632)))
|
||||
Expect(c.Abi).To(Equal(constants.TusdAbiString))
|
||||
Expect(c.Name).To(Equal("TrueUSD"))
|
||||
Expect(c.Address).To(Equal(tusdAddr))
|
||||
})
|
||||
|
||||
It("Fails to initialize if first and block cannot be fetched from vDB headers table", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("Does nothing if nothing if no addresses are configured", func() {
|
||||
_, insertErr := headerRepository.CreateOrUpdateHeader(mocks.MockHeader1)
|
||||
Expect(insertErr).NotTo(HaveOccurred())
|
||||
_, insertErrTwo := headerRepository.CreateOrUpdateHeader(mocks.MockHeader3)
|
||||
Expect(insertErrTwo).NotTo(HaveOccurred())
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Addresses = nil
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
_, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Execute- against TrueUSD contract", func() {
|
||||
BeforeEach(func() {
|
||||
header1, err := blockChain.GetHeaderByNumber(6791668)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header2, err := blockChain.GetHeaderByNumber(6791669)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header3, err := blockChain.GetHeaderByNumber(6791670)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerRepository.CreateOrUpdateHeader(header1)
|
||||
headerID, err = headerRepository.CreateOrUpdateHeader(header2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerRepository.CreateOrUpdateHeader(header3)
|
||||
})
|
||||
|
||||
It("Transforms watched contract data into custom repositories", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
log := test_helpers.HeaderSyncTransferLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event", tusdAddr)).StructScan(&log)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(log.HeaderID).To(Equal(headerID))
|
||||
Expect(log.From).To(Equal("0x1062a747393198f70F71ec65A582423Dba7E5Ab3"))
|
||||
Expect(log.To).To(Equal("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0"))
|
||||
Expect(log.Value).To(Equal("9998940000000000000000"))
|
||||
})
|
||||
|
||||
It("Keeps track of contract-related addresses while transforming event data if they need to be used for later method polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
c, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(c.EmittedAddrs)).To(Equal(4))
|
||||
Expect(len(c.EmittedHashes)).To(Equal(0))
|
||||
|
||||
b, ok := c.EmittedAddrs[common.HexToAddress("0x1062a747393198f70F71ec65A582423Dba7E5Ab3")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedAddrs[common.HexToAddress("0x2930096dB16b4A44Ecd4084EA4bd26F7EeF1AEf0")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedAddrs[common.HexToAddress("0x571A326f5B15E16917dC17761c340c1ec5d06f6d")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedAddrs[common.HexToAddress("0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x09BbBBE21a5975cAc061D82f7b843b1234567890")]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x")]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[""]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x09THISE21a5IS5cFAKE1D82fAND43bCE06MADEUP")]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
|
||||
It("Polls given methods using generated token holder address", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.TusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.BalanceOf{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x1062a747393198f70F71ec65A582423Dba7E5Ab3' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Balance).To(Equal("55849938025000000000000"))
|
||||
Expect(res.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6791669'", tusdAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("Fails if initialization has not been done", func() {
|
||||
t := transformer.NewTransformer(test_helpers.TusdConfig, blockChain, db)
|
||||
err = t.Execute()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("transformer has no initialized contracts"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Execute- against ENS registry contract", func() {
|
||||
BeforeEach(func() {
|
||||
header1, err := blockChain.GetHeaderByNumber(6885695)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header2, err := blockChain.GetHeaderByNumber(6885696)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
header3, err := blockChain.GetHeaderByNumber(6885697)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerRepository.CreateOrUpdateHeader(header1)
|
||||
headerID, err = headerRepository.CreateOrUpdateHeader(header2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerRepository.CreateOrUpdateHeader(header3)
|
||||
})
|
||||
|
||||
It("Transforms watched contract data into custom repositories", func() {
|
||||
t := transformer.NewTransformer(test_helpers.ENSConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(t.Start).To(Equal(int64(6885698)))
|
||||
|
||||
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(log.HeaderID).To(Equal(headerID))
|
||||
Expect(log.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
|
||||
Expect(log.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
|
||||
Expect(log.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
})
|
||||
|
||||
It("Keeps track of contract-related hashes while transforming event data if they need to be used for later method polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
c, ok := t.Contracts[ensAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(c.EmittedHashes)).To(Equal(2))
|
||||
Expect(len(c.EmittedAddrs)).To(Equal(0))
|
||||
|
||||
b, ok := c.EmittedHashes[common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = c.EmittedHashes[common.HexToHash("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
// Doesn't keep track of address since it wouldn't be used in calling the 'owner' method
|
||||
_, ok = c.EmittedAddrs[common.HexToAddress("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef")]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
|
||||
It("Polls given method using list of collected hashes", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.Owner{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x9THIS110dcc444fIS242510c09bbAbe21aFAKEcacNODE82f7b843HASH61ba391' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
|
||||
It("It does not persist events if they do not pass the emitted arg filter", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.EventArgs = map[string][]string{
|
||||
ensAddr: {"fake_filter_value"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
log := test_helpers.HeaderSyncNewOwnerLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&log)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("does not exist"))
|
||||
})
|
||||
|
||||
It("If a method arg filter is applied, only those arguments are used in polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSConfig
|
||||
testConf.MethodArgs = map[string][]string{
|
||||
ensAddr: {"0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"},
|
||||
}
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := test_helpers.Owner{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
Expect(res.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&res)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Execute- against both ENS and TrueUSD", func() {
|
||||
BeforeEach(func() {
|
||||
for i := 6885692; i <= 6885701; i++ {
|
||||
header, err := blockChain.GetHeaderByNumber(int64(i))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = headerRepository.CreateOrUpdateHeader(header)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
It("Transforms watched contract data into custom repositories", func() {
|
||||
t := transformer.NewTransformer(test_helpers.ENSandTusdConfig, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(t.Start).To(Equal(int64(6885702)))
|
||||
|
||||
newOwnerLog := test_helpers.HeaderSyncNewOwnerLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.newowner_event", ensAddr)).StructScan(&newOwnerLog)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(newOwnerLog.Node).To(Equal("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"))
|
||||
Expect(newOwnerLog.Label).To(Equal("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047"))
|
||||
Expect(newOwnerLog.Owner).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
|
||||
transferLog := test_helpers.HeaderSyncTransferLog{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.transfer_event", tusdAddr)).StructScan(&transferLog)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// We don't know vulcID, so compare individual fields instead of complete structures
|
||||
Expect(transferLog.From).To(Equal("0x8cA465764873E71CEa525F5EB6AE973d650c22C2"))
|
||||
Expect(transferLog.To).To(Equal("0xc338482360651E5D30BEd77b7c85358cbBFB2E0e"))
|
||||
Expect(transferLog.Value).To(Equal("2800000000000000000000"))
|
||||
})
|
||||
|
||||
It("Keeps track of contract-related hashes and addresses while transforming event data if they need to be used for later method polling", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSandTusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ens, ok := t.Contracts[ensAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
tusd, ok := t.Contracts[tusdAddr]
|
||||
Expect(ok).To(Equal(true))
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(ens.EmittedHashes)).To(Equal(2))
|
||||
Expect(len(ens.EmittedAddrs)).To(Equal(0))
|
||||
Expect(len(tusd.EmittedAddrs)).To(Equal(2))
|
||||
Expect(len(tusd.EmittedHashes)).To(Equal(0))
|
||||
|
||||
b, ok := ens.EmittedHashes[common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = ens.EmittedHashes[common.HexToHash("0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = tusd.EmittedAddrs[common.HexToAddress("0x8cA465764873E71CEa525F5EB6AE973d650c22C2")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
b, ok = tusd.EmittedAddrs[common.HexToAddress("0xc338482360651E5D30BEd77b7c85358cbBFB2E0e")]
|
||||
Expect(ok).To(Equal(true))
|
||||
Expect(b).To(Equal(true))
|
||||
|
||||
_, ok = tusd.EmittedAddrs[common.HexToAddress("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef")]
|
||||
Expect(ok).To(Equal(false))
|
||||
})
|
||||
|
||||
It("Polls given methods for each contract, using list of collected values", func() {
|
||||
var testConf config.ContractConfig
|
||||
testConf = test_helpers.ENSandTusdConfig
|
||||
testConf.Methods = map[string][]string{
|
||||
ensAddr: {"owner"},
|
||||
tusdAddr: {"balanceOf"},
|
||||
}
|
||||
t := transformer.NewTransformer(testConf, blockChain, db)
|
||||
err = t.Init()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = t.Execute()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
owner := test_helpers.Owner{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(owner.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
Expect(owner.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ce695797aaf402b1c186bad9eca28842625b5047' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(owner.Address).To(Equal("0x0000000000000000000000000000000000000000"))
|
||||
Expect(owner.TokenName).To(Equal(""))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x95832c7a47ff8a7840e28b78ceMADEUPaaf4HASHc186badTHItransformers.8IS625bFAKE' AND block = '6885696'", ensAddr)).StructScan(&owner)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
|
||||
bal := test_helpers.BalanceOf{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x8cA465764873E71CEa525F5EB6AE973d650c22C2' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal.Balance).To(Equal("1954436000000000000000"))
|
||||
Expect(bal.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x09BbBBE21a5975cAc061D82f7b843b1234567890' AND block = '6885701'", tusdAddr)).StructScan(&bal)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("no rows in result set"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,137 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/client"
|
||||
rpc2 "github.com/vulcanize/vulcanizedb/pkg/geth/converters/rpc"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/geth/node"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/history"
|
||||
"github.com/vulcanize/vulcanizedb/test_config"
|
||||
)
|
||||
|
||||
var _ = Describe("Reading from the Geth blockchain", func() {
|
||||
var blockChain *geth.BlockChain
|
||||
|
||||
BeforeEach(func() {
|
||||
rawRpcClient, err := rpc.Dial(test_config.InfuraClient.IPCPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rpcClient := client.NewRpcClient(rawRpcClient, test_config.InfuraClient.IPCPath)
|
||||
ethClient := ethclient.NewClient(rawRpcClient)
|
||||
blockChainClient := client.NewEthClient(ethClient)
|
||||
node := node.MakeNode(rpcClient)
|
||||
transactionConverter := rpc2.NewRpcTransactionConverter(ethClient)
|
||||
blockChain = geth.NewBlockChain(blockChainClient, rpcClient, node, transactionConverter)
|
||||
})
|
||||
|
||||
It("reads two blocks", func(done Done) {
|
||||
blocks := fakes.NewMockBlockRepository()
|
||||
lastBlock, err := blockChain.LastBlock()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
queriedBlocks := []int64{lastBlock.Int64() - 5, lastBlock.Int64() - 6}
|
||||
_, err = history.RetrieveAndUpdateBlocks(blockChain, blocks, queriedBlocks)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
blocks.AssertCreateOrUpdateBlocksCallCountAndBlockNumbersEquals(2, []int64{lastBlock.Int64() - 5, lastBlock.Int64() - 6})
|
||||
close(done)
|
||||
}, 30)
|
||||
|
||||
It("retrieves the genesis block and first block", func(done Done) {
|
||||
genesisBlock, err := blockChain.GetBlockByNumber(int64(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
firstBlock, err := blockChain.GetBlockByNumber(int64(1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
lastBlockNumber, err := blockChain.LastBlock()
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(genesisBlock.Number).To(Equal(int64(0)))
|
||||
Expect(firstBlock.Number).To(Equal(int64(1)))
|
||||
Expect(lastBlockNumber.Int64()).To(BeNumerically(">", 0))
|
||||
close(done)
|
||||
}, 15)
|
||||
|
||||
It("retrieves the node info", func(done Done) {
|
||||
node := blockChain.Node()
|
||||
mainnetID := float64(1)
|
||||
|
||||
Expect(node.GenesisBlock).ToNot(BeNil())
|
||||
Expect(node.NetworkID).To(Equal(mainnetID))
|
||||
Expect(len(node.ID)).ToNot(BeZero())
|
||||
Expect(node.ClientName).ToNot(BeZero())
|
||||
|
||||
close(done)
|
||||
}, 15)
|
||||
|
||||
It("retrieves transaction", func() {
|
||||
// actual transaction: https://etherscan.io/tx/0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255
|
||||
txHash := common.HexToHash("0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255")
|
||||
transactions, err := blockChain.GetTransactions([]common.Hash{txHash})
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(transactions)).To(Equal(1))
|
||||
expectedData := []byte{149, 227, 197, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 160, 85, 105, 13, 157, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,
|
||||
241, 202, 218, 90, 30, 178, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 92, 155, 193, 43}
|
||||
expectedRaw := []byte{248, 201, 9, 132, 59, 154, 202, 0, 131, 1, 102, 93, 148, 44, 75, 208, 100, 185, 152, 131,
|
||||
128, 118, 250, 52, 26, 131, 208, 7, 252, 47, 165, 9, 87, 128, 184, 100, 149, 227, 197, 11, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 160, 85, 105, 13, 157, 184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 241, 202, 218, 90, 30, 178, 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 155, 193, 43, 37, 160, 237, 184, 236, 248, 23, 152,
|
||||
53, 238, 44, 215, 181, 234, 229, 157, 246, 212, 178, 88, 25, 116, 134, 163, 124, 64, 2, 66, 25, 118, 1, 253, 27,
|
||||
101, 160, 36, 226, 116, 43, 147, 236, 124, 76, 227, 250, 228, 168, 22, 19, 248, 155, 248, 151, 219, 14, 1, 186,
|
||||
159, 35, 154, 22, 222, 123, 254, 147, 63, 221}
|
||||
expectedModel := core.TransactionModel{
|
||||
Data: expectedData,
|
||||
From: "0x3b08b99441086edd66f36f9f9aee733280698378",
|
||||
GasLimit: 91741,
|
||||
GasPrice: 1000000000,
|
||||
Hash: "0x44d462f2a19ad267e276b234a62c542fc91c974d2e4754a325ca405f95440255",
|
||||
Nonce: 9,
|
||||
Raw: expectedRaw,
|
||||
Receipt: core.Receipt{},
|
||||
To: "0x2c4bd064b998838076fa341a83d007fc2fa50957",
|
||||
TxIndex: 30,
|
||||
Value: "0",
|
||||
}
|
||||
Expect(transactions[0]).To(Equal(expectedModel))
|
||||
})
|
||||
|
||||
//Benchmarking test: remove skip to test performance of block retrieval
|
||||
XMeasure("retrieving n blocks", func(b Benchmarker) {
|
||||
b.Time("runtime", func() {
|
||||
var blocks []core.Block
|
||||
n := 10
|
||||
for i := 5327459; i > 5327459-n; i-- {
|
||||
block, err := blockChain.GetBlockByNumber(int64(i))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
blocks = append(blocks, block)
|
||||
}
|
||||
Expect(len(blocks)).To(Equal(n))
|
||||
})
|
||||
}, 10)
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestIntegrationTest(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "IntegrationTest Suite")
|
||||
}
|
||||
@@ -1,252 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/contract"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/helpers/test_helpers"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/poller"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/contract_watcher/shared/types"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
)
|
||||
|
||||
var _ = Describe("Poller", func() {
|
||||
|
||||
var contractPoller poller.Poller
|
||||
var con *contract.Contract
|
||||
var db *postgres.DB
|
||||
var bc core.BlockChain
|
||||
|
||||
AfterEach(func() {
|
||||
test_helpers.TearDown(db)
|
||||
})
|
||||
|
||||
Describe("Full sync mode", func() {
|
||||
BeforeEach(func() {
|
||||
db, bc = test_helpers.SetupDBandBC()
|
||||
contractPoller = poller.NewPoller(bc, db, types.FullSync)
|
||||
})
|
||||
|
||||
Describe("PollContract", func() {
|
||||
It("Polls specified contract methods using contract's argument list", func() {
|
||||
con = test_helpers.SetupTusdContract(nil, []string{"balanceOf"})
|
||||
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||
con.StartingBlock = 6707322
|
||||
con.AddEmittedAddr(common.HexToAddress("0xfE9e8709d3215310075d67E3ed32A380CCf451C8"), common.HexToAddress("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"))
|
||||
|
||||
err := contractPoller.PollContract(*con, 6707323)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.BalanceOf{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
})
|
||||
|
||||
It("Polls specified contract methods using contract's hash list", func() {
|
||||
con = test_helpers.SetupENSContract(nil, []string{"owner"})
|
||||
Expect(con.Abi).To(Equal(constants.ENSAbiString))
|
||||
Expect(len(con.Methods)).To(Equal(1))
|
||||
con.AddEmittedHash(common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"), common.HexToHash("0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86"))
|
||||
|
||||
err := contractPoller.PollContractAt(*con, 6885877)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.Owner{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x546aA2EaE2514494EeaDb7bbb35243348983C59d"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
})
|
||||
|
||||
It("Does not poll and persist any methods if none are specified", func() {
|
||||
con = test_helpers.SetupTusdContract(nil, nil)
|
||||
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||
con.StartingBlock = 6707322
|
||||
con.AddEmittedAddr(common.HexToAddress("0xfE9e8709d3215310075d67E3ed32A380CCf451C8"), common.HexToAddress("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"))
|
||||
|
||||
err := contractPoller.PollContract(*con, 6707323)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.BalanceOf{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM full_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("FetchContractData", func() {
|
||||
It("Calls a single contract method", func() {
|
||||
var name = new(string)
|
||||
err := contractPoller.FetchContractData(constants.TusdAbiString, constants.TusdContractAddress, "name", nil, &name, 6197514)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*name).To(Equal("TrueUSD"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Header sync mode", func() {
|
||||
BeforeEach(func() {
|
||||
db, bc = test_helpers.SetupDBandBC()
|
||||
contractPoller = poller.NewPoller(bc, db, types.HeaderSync)
|
||||
})
|
||||
|
||||
Describe("PollContract", func() {
|
||||
It("Polls specified contract methods using contract's token holder address list", func() {
|
||||
con = test_helpers.SetupTusdContract(nil, []string{"balanceOf"})
|
||||
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||
con.StartingBlock = 6707322
|
||||
con.AddEmittedAddr(common.HexToAddress("0xfE9e8709d3215310075d67E3ed32A380CCf451C8"), common.HexToAddress("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"))
|
||||
|
||||
err := contractPoller.PollContract(*con, 6707323)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.BalanceOf{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("66386309548896882859581786"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE' AND block = '6707323'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Balance).To(Equal("17982350181394112023885864"))
|
||||
Expect(scanStruct.TokenName).To(Equal("TrueUSD"))
|
||||
})
|
||||
|
||||
It("Polls specified contract methods using contract's hash list", func() {
|
||||
con = test_helpers.SetupENSContract(nil, []string{"owner"})
|
||||
Expect(con.Abi).To(Equal(constants.ENSAbiString))
|
||||
Expect(len(con.Methods)).To(Equal(1))
|
||||
con.AddEmittedHash(common.HexToHash("0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae"), common.HexToHash("0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86"))
|
||||
|
||||
err := contractPoller.PollContractAt(*con, 6885877)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.Owner{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x7e74a86b6e146964fb965db04dc2590516da77f720bb6759337bf5632415fd86' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x546aA2EaE2514494EeaDb7bbb35243348983C59d"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.owner_method WHERE node_ = '0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae' AND block = '6885877'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
})
|
||||
|
||||
It("Does not poll and persist any methods if none are specified", func() {
|
||||
con = test_helpers.SetupTusdContract(nil, nil)
|
||||
Expect(con.Abi).To(Equal(constants.TusdAbiString))
|
||||
con.StartingBlock = 6707322
|
||||
con.AddEmittedAddr(common.HexToAddress("0xfE9e8709d3215310075d67E3ed32A380CCf451C8"), common.HexToAddress("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE"))
|
||||
|
||||
err := contractPoller.PollContract(*con, 6707323)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.BalanceOf{}
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.balanceof_method WHERE who_ = '0xfE9e8709d3215310075d67E3ed32A380CCf451C8' AND block = '6707322'", constants.TusdContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("Caches returned values of the appropriate types for downstream method polling if method piping is turned on", func() {
|
||||
con = test_helpers.SetupENSContract(nil, []string{"resolver"})
|
||||
Expect(con.Abi).To(Equal(constants.ENSAbiString))
|
||||
con.StartingBlock = 6921967
|
||||
con.EmittedAddrs = map[interface{}]bool{}
|
||||
con.Piping = false
|
||||
con.AddEmittedHash(common.HexToHash("0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8"))
|
||||
err := contractPoller.PollContract(*con, 6921968)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
scanStruct := test_helpers.Resolver{}
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
Expect(len(con.EmittedAddrs)).To(Equal(0)) // With piping off the address is not saved
|
||||
|
||||
test_helpers.TearDown(db)
|
||||
db, bc = test_helpers.SetupDBandBC()
|
||||
contractPoller = poller.NewPoller(bc, db, types.HeaderSync)
|
||||
|
||||
con.Piping = true
|
||||
err = contractPoller.PollContract(*con, 6921968)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = db.QueryRowx(fmt.Sprintf("SELECT * FROM header_%s.resolver_method WHERE node_ = '0x495b6e6efdedb750aa519919b5cf282bdaa86067b82a2293a3ff5723527141e8' AND block = '6921967'", constants.EnsContractAddress)).StructScan(&scanStruct)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(scanStruct.Address).To(Equal("0x5FfC014343cd971B7eb70732021E26C35B744cc4"))
|
||||
Expect(scanStruct.TokenName).To(Equal("ENS-Registry"))
|
||||
Expect(len(con.EmittedAddrs)).To(Equal(1)) // With piping on it is saved
|
||||
Expect(con.EmittedAddrs[common.HexToAddress("0x5FfC014343cd971B7eb70732021E26C35B744cc4")]).To(Equal(true))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("FetchContractData", func() {
|
||||
It("Calls a single contract method", func() {
|
||||
var name = new(string)
|
||||
err := contractPoller.FetchContractData(constants.TusdAbiString, constants.TusdContractAddress, "name", nil, &name, 6197514)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(*name).To(Equal("TrueUSD"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,8 +0,0 @@
|
||||
# Shared Tools
|
||||
|
||||
## Description
|
||||
Code that is useful for or used by plugins written on top of VulcanizeDB.
|
||||
|
||||
## Note
|
||||
Much code in this directory may not be used outside of the tests, but don't delete it - it could be used by a plugin.
|
||||
Renaming and/or deleting functions in this namespace requires a version bump to avoid breaking plugins.
|
||||
@@ -1,35 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package chunker_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func TestFactories(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Shared Chunker Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
})
|
||||
@@ -1,72 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package chunker
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
shared_t "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
)
|
||||
|
||||
type Chunker interface {
|
||||
AddConfigs(transformerConfigs []shared_t.EventTransformerConfig)
|
||||
ChunkLogs(logs []types.Log) map[string][]types.Log
|
||||
}
|
||||
|
||||
type LogChunker struct {
|
||||
AddressToNames map[string][]string
|
||||
NameToTopic0 map[string]common.Hash
|
||||
}
|
||||
|
||||
// Returns a new log chunker with initialised maps.
|
||||
// Needs to have configs added with `AddConfigs` to consider logs for the respective transformer.
|
||||
func NewLogChunker() *LogChunker {
|
||||
return &LogChunker{
|
||||
AddressToNames: map[string][]string{},
|
||||
NameToTopic0: map[string]common.Hash{},
|
||||
}
|
||||
}
|
||||
|
||||
// Configures the chunker by adding more addreses and topics to consider.
|
||||
func (chunker *LogChunker) AddConfigs(transformerConfigs []shared_t.EventTransformerConfig) {
|
||||
for _, config := range transformerConfigs {
|
||||
for _, address := range config.ContractAddresses {
|
||||
var lowerCaseAddress = strings.ToLower(address)
|
||||
chunker.AddressToNames[lowerCaseAddress] = append(chunker.AddressToNames[lowerCaseAddress], config.TransformerName)
|
||||
chunker.NameToTopic0[config.TransformerName] = common.HexToHash(config.Topic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Goes through an array of logs, associating relevant logs (matching addresses and topic) with transformers
|
||||
func (chunker *LogChunker) ChunkLogs(logs []types.Log) map[string][]types.Log {
|
||||
chunks := map[string][]types.Log{}
|
||||
for _, log := range logs {
|
||||
// Topic0 is not unique to each transformer, also need to consider the contract address
|
||||
relevantTransformers := chunker.AddressToNames[strings.ToLower(log.Address.String())]
|
||||
|
||||
for _, transformer := range relevantTransformers {
|
||||
if chunker.NameToTopic0[transformer] == log.Topics[0] {
|
||||
chunks[transformer] = append(chunks[transformer], log)
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package chunker_test
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
chunk "github.com/vulcanize/vulcanizedb/libraries/shared/chunker"
|
||||
shared_t "github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
)
|
||||
|
||||
var _ = Describe("Log chunker", func() {
|
||||
var (
|
||||
configs []shared_t.EventTransformerConfig
|
||||
chunker *chunk.LogChunker
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
configA := shared_t.EventTransformerConfig{
|
||||
TransformerName: "TransformerA",
|
||||
ContractAddresses: []string{"0x00000000000000000000000000000000000000A1", "0x00000000000000000000000000000000000000A2"},
|
||||
Topic: "0xA",
|
||||
}
|
||||
configB := shared_t.EventTransformerConfig{
|
||||
TransformerName: "TransformerB",
|
||||
ContractAddresses: []string{"0x00000000000000000000000000000000000000B1"},
|
||||
Topic: "0xB",
|
||||
}
|
||||
|
||||
configC := shared_t.EventTransformerConfig{
|
||||
TransformerName: "TransformerC",
|
||||
ContractAddresses: []string{"0x00000000000000000000000000000000000000A2"},
|
||||
Topic: "0xC",
|
||||
}
|
||||
|
||||
configs = []shared_t.EventTransformerConfig{configA, configB, configC}
|
||||
chunker = chunk.NewLogChunker()
|
||||
chunker.AddConfigs(configs)
|
||||
})
|
||||
|
||||
Describe("initialisation", func() {
|
||||
It("creates lookup maps correctly", func() {
|
||||
Expect(chunker.AddressToNames).To(Equal(map[string][]string{
|
||||
"0x00000000000000000000000000000000000000a1": {"TransformerA"},
|
||||
"0x00000000000000000000000000000000000000a2": {"TransformerA", "TransformerC"},
|
||||
"0x00000000000000000000000000000000000000b1": {"TransformerB"},
|
||||
}))
|
||||
|
||||
Expect(chunker.NameToTopic0).To(Equal(map[string]common.Hash{
|
||||
"TransformerA": common.HexToHash("0xA"),
|
||||
"TransformerB": common.HexToHash("0xB"),
|
||||
"TransformerC": common.HexToHash("0xC"),
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("AddConfigs", func() {
|
||||
It("can add more configs later", func() {
|
||||
configD := shared_t.EventTransformerConfig{
|
||||
TransformerName: "TransformerD",
|
||||
ContractAddresses: []string{"0x000000000000000000000000000000000000000D"},
|
||||
Topic: "0xD",
|
||||
}
|
||||
chunker.AddConfigs([]shared_t.EventTransformerConfig{configD})
|
||||
|
||||
Expect(chunker.AddressToNames).To(ContainElement([]string{"TransformerD"}))
|
||||
Expect(chunker.NameToTopic0).To(ContainElement(common.HexToHash("0xD")))
|
||||
})
|
||||
|
||||
It("lower cases address", func() {
|
||||
configD := shared_t.EventTransformerConfig{
|
||||
TransformerName: "TransformerD",
|
||||
ContractAddresses: []string{"0x000000000000000000000000000000000000000D"},
|
||||
Topic: "0xD",
|
||||
}
|
||||
chunker.AddConfigs([]shared_t.EventTransformerConfig{configD})
|
||||
|
||||
Expect(chunker.AddressToNames["0x000000000000000000000000000000000000000d"]).To(Equal([]string{"TransformerD"}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("ChunkLogs", func() {
|
||||
It("only associates logs with relevant topic0 and address to transformers", func() {
|
||||
logs := []types.Log{log1, log2, log3, log4, log5}
|
||||
chunks := chunker.ChunkLogs(logs)
|
||||
|
||||
Expect(chunks["TransformerA"]).To(And(ContainElement(log1), ContainElement(log4)))
|
||||
Expect(chunks["TransformerB"]).To(BeEmpty())
|
||||
Expect(chunks["TransformerC"]).To(ContainElement(log5))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var (
|
||||
// Match TransformerA
|
||||
log1 = types.Log{
|
||||
Address: common.HexToAddress("0xA1"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xA"),
|
||||
common.HexToHash("0xLogTopic1"),
|
||||
},
|
||||
}
|
||||
// Match TransformerA address, but not topic0
|
||||
log2 = types.Log{
|
||||
Address: common.HexToAddress("0xA1"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xB"),
|
||||
common.HexToHash("0xLogTopic2"),
|
||||
},
|
||||
}
|
||||
// Match TransformerA topic, but TransformerB address
|
||||
log3 = types.Log{
|
||||
Address: common.HexToAddress("0xB1"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xA"),
|
||||
common.HexToHash("0xLogTopic3"),
|
||||
},
|
||||
}
|
||||
// Match TransformerA, with the other address
|
||||
log4 = types.Log{
|
||||
Address: common.HexToAddress("0xA2"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xA"),
|
||||
common.HexToHash("0xLogTopic4"),
|
||||
},
|
||||
}
|
||||
// Match TransformerC, which shares address with TransformerA
|
||||
log5 = types.Log{
|
||||
Address: common.HexToAddress("0xA2"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xC"),
|
||||
common.HexToHash("0xLogTopic5"),
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package constants
|
||||
|
||||
type TransformerExecution bool
|
||||
|
||||
const (
|
||||
HeaderRecheck TransformerExecution = true
|
||||
HeaderMissing TransformerExecution = false
|
||||
RecheckHeaderCap = "4"
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package constants
|
||||
|
||||
var DataItemLength = 32
|
||||
@@ -1,388 +0,0 @@
|
||||
# Watching Contract Events
|
||||
|
||||
One approach VulcanizeDB takes to caching and indexing smart contracts is to watch contract events emitted in receipt logs.
|
||||
|
||||
With a header synced vDB we can watch events by iterating over headers retrieved from the synced `headers` table and using these headers to
|
||||
fetch and verify relevant event logs from a full Ethereum node, keeping track of which headers we have checked for which events
|
||||
with our `checked_headers` table.
|
||||
|
||||
## Assumptions
|
||||
|
||||
This approach assumes you are running a vDB header sync which is run against a light Ethereum node;
|
||||
this approach also assumes there is a full node available.
|
||||
|
||||
Looking forward, we will be building fetchers that enable sourcing data from IPFS instead of an ETH node.
|
||||
|
||||
## Shared Code
|
||||
|
||||
VulcanizeDB has shared code built out for building and plugging in event transformers
|
||||
|
||||
### [Event Watcher (header sync)](../staging/libraries/shared/watcher/event_watcher.go)
|
||||
|
||||
The event watcher is responsible for continuously fetching and delegating chunks of logs and their associated header to the appropriate transformers.
|
||||
|
||||
Using the `compose` or `composeAndExecute` command, event watchers can be loaded with plugin event transformers and execute over them.
|
||||
|
||||
### [Event Transformer](../staging/libraries/shared/transformer/event_transformer.go)
|
||||
|
||||
The event transformer is responsible for converting event logs into more useful data objects and storing them in Postgres.
|
||||
The event transformer is composed of converter and repository interfaces and a config struct:
|
||||
```go
|
||||
type EventTransformer struct {
|
||||
Config transformer.EventTransformerConfig
|
||||
Converter Converter
|
||||
Repository Repository
|
||||
}
|
||||
```
|
||||
|
||||
The event transformer executes over provided event logs at a given header.
|
||||
|
||||
In this process, the converter unpacks these logs into entities and then converts these entities
|
||||
to their final db models. These models are then written to the Postgres db by the repository.
|
||||
|
||||
```go
|
||||
func (transformer Transformer) Execute(logs []types.Log, header core.Header, recheckHeaders constants.TransformerExecution) error {
|
||||
transformerName := transformer.Config.TransformerName
|
||||
config := transformer.Config
|
||||
|
||||
if len(logs) < 1 {
|
||||
err := transformer.Repository.MarkHeaderChecked(header.Id)
|
||||
if err != nil {
|
||||
log.Printf("Error marking header as checked in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
entities, err := transformer.Converter.ToEntities(config.ContractAbi, logs)
|
||||
if err != nil {
|
||||
log.Printf("Error converting logs to entities in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
models, err := transformer.Converter.ToModels(entities)
|
||||
if err != nil {
|
||||
log.Printf("Error converting entities to models in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = transformer.Repository.Create(header.Id, models)
|
||||
if err != nil {
|
||||
log.Printf("Error persisting %v record: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Custom Code
|
||||
|
||||
In order to watch events at a smart contract, for those events the developer must create:
|
||||
|
||||
1. Config - struct to hold configuration information (contract address, starting block, event name and signature).
|
||||
1. Entity - struct to unpack the event log into.
|
||||
1. Model - struct representing the final data model we want to write to Postgres.
|
||||
1. Converter - an interface which can unpack event logs into our entities and convert those entities to our models.
|
||||
1. Repository - an interface to write our models to Postgres.
|
||||
1. EventTransformerInitializer - a public variable which exports our configured transformer to be loaded as part of a plugin.
|
||||
1. DB migrations - migrations to generate the Postgres schema, tables, views, function, etc that are needed to store and interface with the transformed data models.
|
||||
|
||||
The example event we will use looks like:
|
||||
```
|
||||
event ExampleEvent(bytes32 indexed arg1, address indexed arg2, bytes32 arg3, uint256 arg4, uint256 arg5);
|
||||
```
|
||||
|
||||
### Config
|
||||
|
||||
The config holds configuration variables for the event transformer, including a name for the transformer, the contract address
|
||||
it is working at, the contract's ABI, the topic (e.g. event signature; topic0) that it is filtering for, and starting
|
||||
and ending block numbers.
|
||||
|
||||
```go
|
||||
type EventTransformerConfig struct {
|
||||
TransformerName string
|
||||
ContractAddresses []string
|
||||
ContractAbi string
|
||||
Topic string
|
||||
StartingBlockNumber int64
|
||||
EndingBlockNumber int64 // Set -1 for indefinite transformer
|
||||
}
|
||||
```
|
||||
|
||||
### Entity
|
||||
|
||||
Entity field names for event arguments need to be exported and match the argument's name and type. LogIndex,
|
||||
TransactionIndex, and the Raw log are retained in order to link the data to it's source for downstream validation.
|
||||
|
||||
```go
|
||||
type ExampleEntity struct {
|
||||
Arg1 common.Hash
|
||||
Arg2 common.Address
|
||||
Arg3 common.Hash
|
||||
Arg4 *big.Int
|
||||
Arg5 *big.Int
|
||||
LogIndex uint
|
||||
TransactionIndex uint
|
||||
Raw types.Log
|
||||
}
|
||||
```
|
||||
|
||||
### Model
|
||||
|
||||
Model fields are not constrained by the event log structure.
|
||||
This allows us to rename our fields, decode or convert our log values into more useful types, and perform operations
|
||||
with or on the values before persisting the data to Postgres.
|
||||
|
||||
```go
|
||||
type ExampleModel struct {
|
||||
EventHash string
|
||||
UserAddress string
|
||||
FractionSkimmed string
|
||||
Surplus string
|
||||
Deficit string
|
||||
FinalPosition string
|
||||
LogIndex uint
|
||||
TransactionIndex uint
|
||||
Raw types.Log
|
||||
}
|
||||
```
|
||||
|
||||
### Converter
|
||||
|
||||
The converter needs to satisfy the interface. One for unpacking logs into the custom defined entities, and
|
||||
another for converting those entities to their final db models.
|
||||
|
||||
```go
|
||||
type Converter interface {
|
||||
ToEntities(contractAbi string, ethLog []types.Log) ([]interface{}, error)
|
||||
ToModels([]interface{}) ([]interface{}, error)
|
||||
}
|
||||
```
|
||||
|
||||
For the example event, this might look like:
|
||||
```go
|
||||
type ExampleConverter struct{}
|
||||
|
||||
func (ExampleConverter) ToEntities(contractAbi string, ethLogs []types.Log) ([]interface{}, error) {
|
||||
var entities []interface{}
|
||||
for _, ethLog := range ethLogs {
|
||||
entity := &ExampleEntity{}
|
||||
address := ethLog.Address
|
||||
abi, err := geth.ParseAbi(contractAbi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contract := bind.NewBoundContract(address, abi, nil, nil, nil)
|
||||
|
||||
err = contract.UnpackLog(entity, "ExampleEvent", ethLog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entity.Raw = ethLog
|
||||
entity.LogIndex = ethLog.Index
|
||||
entity.TransactionIndex = ethLog.TxIndex
|
||||
|
||||
entities = append(entities, *entity)
|
||||
}
|
||||
|
||||
return entities, nil
|
||||
}
|
||||
|
||||
func (converter ExampleConverter) ToModels(entities []interface{}) ([]interface{}, error) {
|
||||
var models []interface{}
|
||||
for _, entity := range entities {
|
||||
entity, ok := entity.(ExampleModel)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entity of type %T, not %T", entity, ExampleModel{})
|
||||
}
|
||||
|
||||
fractionSkimmed, err := hexutil.DecodeBig(entity.Arg3.Hex())
|
||||
if err != nil {
|
||||
reuturn nil, err
|
||||
}
|
||||
position := new(big.Int)
|
||||
position.Sub(entity.Arg4, entity.Arg5)
|
||||
finalPosition := new(big.Int)
|
||||
if preTaxPosition.Sign() < 0 {
|
||||
finalPosition = position
|
||||
} else {
|
||||
skim := new(big.Int)
|
||||
skim.Div(position, fractionSkimmed)
|
||||
finalPosition = position.Sub(position, skim)
|
||||
}
|
||||
|
||||
rawLog, err := json.Marshal(entity.Raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
model := ExampleModel{
|
||||
EventHash: entity.Arg1.Hex(),
|
||||
UserAddress: entity.Arg2.Hex(),
|
||||
FractionSkimmed: fractionSkimmed.String(),
|
||||
Surplus: entity.Arg4.String(),
|
||||
Deficit: entity.Arg5.String(),
|
||||
FinalPosition: finalPosition,
|
||||
LogIndex: entity.LogIndex,
|
||||
TransactionIndex: entity.TransactionIndex,
|
||||
Raw: rawLog,
|
||||
}
|
||||
models = append(models, model)
|
||||
}
|
||||
return models, nil
|
||||
}
|
||||
```
|
||||
Notice that in this example we have a bytes32 argument in the event that needs to be decoded to an integer before it can be worked with
|
||||
to produce our hypothetical `FinalPosition` field. This is to highlight the fact that contracts can and sometimes do encode the
|
||||
data types we want to work with into raw bytes. Writing custom transformers with these converters allows us to account for this.
|
||||
|
||||
### Repository
|
||||
|
||||
The repository needs to satisfy the interface and use the `Create` method to write the model to Postgres.
|
||||
```go
|
||||
type Repository interface {
|
||||
Create(headerID int64, models []interface{}) error
|
||||
MarkHeaderChecked(headerID int64) error
|
||||
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
|
||||
RecheckHeaders(startingBlockNumber, endingBlockNUmber int64) ([]core.Header, error)
|
||||
SetDB(db *postgres.DB)
|
||||
}
|
||||
```
|
||||
|
||||
For the example event, this might look like:
|
||||
```go
|
||||
type ExampleRepository struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
func (repository *ExampleRepository) SetDB(db *postgres.DB) {
|
||||
repository.db = db
|
||||
}
|
||||
|
||||
func (repository ExampleRepository) Create(headerID int64, models []interface{}) error {
|
||||
tx, dBaseErr := repository.db.Beginx()
|
||||
if dBaseErr != nil {
|
||||
return dBaseErr
|
||||
}
|
||||
for _, model := range models {
|
||||
model, ok := model.(ExampleModel)
|
||||
if !ok {
|
||||
rollbackErr := tx.Rollback()
|
||||
if rollbackErr != nil {
|
||||
log.Error("failed to rollback ", rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("model of type %T, not %T", model, ExampleModel{})
|
||||
}
|
||||
|
||||
_, execErr := tx.Exec(
|
||||
`INSERT into example_schema.example_event (header_id, event_hash, user_address, fraction_skimmed, surplus, deficit, final_position, log_idx, tx_idx, raw_log)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
ON CONFLICT (header_id, tx_idx, log_idx) DO UPDATE SET event_hash = $2, user_address = $3, fraction_skimmed = $4, surplus = $5, deficit = $6, final_position = $7, raw_log = $10;`,
|
||||
headerID, model.EventHash, model.UserAddress, model.FractonSkimmed, model.Surplus, model.Deficit, model.FinalPosition, model.LogIndex, model.TransactionIndex, model.Raw,
|
||||
)
|
||||
if execErr != nil {
|
||||
rollbackErr := tx.Rollback()
|
||||
if rollbackErr != nil {
|
||||
log.Error("failed to rollback ", rollbackErr)
|
||||
}
|
||||
return execErr
|
||||
}
|
||||
}
|
||||
|
||||
checkHeaderErr := repo.MarkHeaderCheckedInTransaction(headerID, tx, "example_event_checked")
|
||||
if checkHeaderErr != nil {
|
||||
rollbackErr := tx.Rollback()
|
||||
if rollbackErr != nil {
|
||||
log.Error("failed to rollback ", rollbackErr)
|
||||
}
|
||||
return checkHeaderErr
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (repository ExampleRepository) MarkHeaderChecked(headerID int64) error {
|
||||
return repo.MarkHeaderChecked(headerID, repository.db, "example_event_checked")
|
||||
}
|
||||
|
||||
func (repository ExampleRepository) MissingHeaders(startingBlockNumber int64, endingBlockNumber int64) ([]core.Header, error) {
|
||||
return repo.MissingHeaders(startingBlockNumber, endingBlockNumber, repository.db,"example_event_checked")
|
||||
}
|
||||
|
||||
func (repository ExampleRepository) RecheckHeaders(startingBlockNumber int64, endingBlockNumber int64) ([]core.Header, error) {
|
||||
return repo.RecheckHeaders(startingBlockNumber, endingBlockNumber, repository.db, "example_event_checked")
|
||||
}
|
||||
```
|
||||
|
||||
### EventTransformerInitializer
|
||||
|
||||
A transformer initializer variable needs to be exported from somewhere within the transformer repository so that the transformer can be
|
||||
loaded as part of a plugin in the `compose` or `composeAndExecute` commands. It is important that this variable is named `EventTransformerInitializer` and
|
||||
it must be of `type EventTransformerInitializer func(db *postgres.DB) EventTransformer`.
|
||||
|
||||
```go
|
||||
var EventTransformerInitializer transformer.EventTransformerInitializer = factories.Transformer{
|
||||
Config: exampleEventConfig,
|
||||
Converter: ExampleConverter{},
|
||||
Repository: &ExampleRepository{},
|
||||
}.NewTransformer
|
||||
```
|
||||
|
||||
### DB migrations
|
||||
|
||||
We use `goose` as our migration management tool. Any Go data model that needs to be written to Postgres by the
|
||||
repository needs a db migration for the corresponding Postgres data model.
|
||||
|
||||
Each contract or set of transformers being watched should define its own namespace with a db schema:
|
||||
```postgresql
|
||||
-- +goose Up
|
||||
CREATE SCHEMA example_schema;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP SCHEMA example_schema;
|
||||
|
||||
```
|
||||
|
||||
For the example event and its resulting model, the table we write to would look like:
|
||||
```postgresql
|
||||
-- +goose Up
|
||||
CREATE TABLE example_schema.example_event (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES headers (id) ON DELETE CASCADE,
|
||||
event_hash CHARACTER VARYING(66) NOT NULL,
|
||||
user_address CHARACTER VARYING(66) NOT NULL,
|
||||
fraction_skimmed NUMERIC NOT NULL,
|
||||
surplus NUMERIC NOT NULL,
|
||||
deficit NUMERIC NOT NULL,
|
||||
final_position NUMERIC NOT NULL,
|
||||
tx_idx INTEGER NOT NUll,
|
||||
log_idx INTEGER NOT NUll,
|
||||
raw_log JSONB,
|
||||
UNIQUE (header_id, tx_idx, log_idx)
|
||||
);
|
||||
|
||||
ALTER TABLE public.checked_headers
|
||||
ADD COLUMN example_event_checked INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE example_schema.example_event;
|
||||
|
||||
ALTER TABLE public.checked_headers
|
||||
DROP COLUMN example_event_checked;
|
||||
```
|
||||
|
||||
Notice that we have also added a column to the `checked_headers` table for this event so that we can keep track
|
||||
of which headers we have already filtered through for this event.
|
||||
|
||||
## Summary
|
||||
|
||||
To create a transformer for a contract event we need to create entities for unpacking the raw log, models to represent
|
||||
the final data structure, a converter to mediate this unpacking and conversion between entities to models, a repository to write
|
||||
these models to Postgres, db migrations to accommodate these models in Postgres, and a EventTransformerInitializer to export the
|
||||
configured transformer and load it as a plugin to the `compose` or `composeAndExecute` commands as described in the main readme.
|
||||
@@ -1,24 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package event
|
||||
|
||||
import "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
type Converter interface {
|
||||
ToEntities(contractAbi string, ethLog []types.Log) ([]interface{}, error)
|
||||
ToModels([]interface{}) ([]interface{}, error)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package event_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func TestFactories(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Factories Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Create(headerID int64, models []interface{}) error
|
||||
MarkHeaderChecked(headerID int64) error
|
||||
MissingHeaders(startingBlockNumber, endingBlockNumber int64) ([]core.Header, error)
|
||||
RecheckHeaders(startingBlockNumber, endingBlockNUmber int64) ([]core.Header, error)
|
||||
SetDB(db *postgres.DB)
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package event
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/datastore/postgres"
|
||||
)
|
||||
|
||||
type Transformer struct {
|
||||
Config transformer.EventTransformerConfig
|
||||
Converter Converter
|
||||
Repository Repository
|
||||
}
|
||||
|
||||
func (transformer Transformer) NewTransformer(db *postgres.DB) transformer.EventTransformer {
|
||||
transformer.Repository.SetDB(db)
|
||||
return transformer
|
||||
}
|
||||
|
||||
func (transformer Transformer) Execute(logs []types.Log, header core.Header, recheckHeaders constants.TransformerExecution) error {
|
||||
transformerName := transformer.Config.TransformerName
|
||||
config := transformer.Config
|
||||
|
||||
if len(logs) < 1 {
|
||||
err := transformer.Repository.MarkHeaderChecked(header.Id)
|
||||
if err != nil {
|
||||
log.Printf("Error marking header as checked in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
entities, err := transformer.Converter.ToEntities(config.ContractAbi, logs)
|
||||
if err != nil {
|
||||
log.Printf("Error converting logs to entities in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
models, err := transformer.Converter.ToModels(entities)
|
||||
if err != nil {
|
||||
log.Printf("Error converting entities to models in %v: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = transformer.Repository.Create(header.Id, models)
|
||||
if err != nil {
|
||||
log.Printf("Error persisting %v record: %v", transformerName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (transformer Transformer) GetName() string {
|
||||
return transformer.Config.TransformerName
|
||||
}
|
||||
|
||||
func (transformer Transformer) GetConfig() transformer.EventTransformerConfig {
|
||||
return transformer.Config
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 Vulcanize
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package event_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/constants"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/factories/event"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/mocks"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/test_data"
|
||||
"github.com/vulcanize/vulcanizedb/libraries/shared/transformer"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/core"
|
||||
"github.com/vulcanize/vulcanizedb/pkg/fakes"
|
||||
)
|
||||
|
||||
var _ = Describe("Transformer", func() {
|
||||
var (
|
||||
repository mocks.MockRepository
|
||||
converter mocks.MockConverter
|
||||
t transformer.EventTransformer
|
||||
headerOne core.Header
|
||||
config = test_data.GenericTestConfig
|
||||
logs = test_data.GenericTestLogs
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
repository = mocks.MockRepository{}
|
||||
converter = mocks.MockConverter{}
|
||||
|
||||
t = event.Transformer{
|
||||
Repository: &repository,
|
||||
Converter: &converter,
|
||||
Config: config,
|
||||
}.NewTransformer(nil)
|
||||
|
||||
headerOne = core.Header{Id: rand.Int63(), BlockNumber: rand.Int63()}
|
||||
})
|
||||
|
||||
It("sets the db", func() {
|
||||
Expect(repository.SetDbCalled).To(BeTrue())
|
||||
})
|
||||
|
||||
It("marks header checked if no logs returned", func() {
|
||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
repository.AssertMarkHeaderCheckedCalledWith(headerOne.Id)
|
||||
})
|
||||
|
||||
It("doesn't attempt to convert or persist an empty collection when there are no logs", func() {
|
||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(converter.ToEntitiesCalledCounter).To(Equal(0))
|
||||
Expect(converter.ToModelsCalledCounter).To(Equal(0))
|
||||
Expect(repository.CreateCalledCounter).To(Equal(0))
|
||||
})
|
||||
|
||||
It("does not call repository.MarkCheckedHeader when there are logs", func() {
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
repository.AssertMarkHeaderCheckedNotCalled()
|
||||
})
|
||||
|
||||
It("returns error if marking header checked returns err", func() {
|
||||
repository.SetMarkHeaderCheckedError(fakes.FakeError)
|
||||
|
||||
err := t.Execute([]types.Log{}, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(fakes.FakeError))
|
||||
})
|
||||
|
||||
It("converts an eth log to an entity", func() {
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(converter.ContractAbi).To(Equal(config.ContractAbi))
|
||||
Expect(converter.LogsToConvert).To(Equal(logs))
|
||||
})
|
||||
|
||||
It("returns an error if converter fails", func() {
|
||||
converter.ToEntitiesError = fakes.FakeError
|
||||
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(fakes.FakeError))
|
||||
})
|
||||
|
||||
It("converts an entity to a model", func() {
|
||||
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
||||
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(converter.EntitiesToConvert[0]).To(Equal(test_data.GenericEntity{}))
|
||||
})
|
||||
|
||||
It("returns an error if converting to models fails", func() {
|
||||
converter.EntitiesToReturn = []interface{}{test_data.GenericEntity{}}
|
||||
converter.ToModelsError = fakes.FakeError
|
||||
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(fakes.FakeError))
|
||||
})
|
||||
|
||||
It("persists the record", func() {
|
||||
converter.ModelsToReturn = []interface{}{test_data.GenericModel{}}
|
||||
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(repository.PassedHeaderID).To(Equal(headerOne.Id))
|
||||
Expect(repository.PassedModels[0]).To(Equal(test_data.GenericModel{}))
|
||||
})
|
||||
|
||||
It("returns error if persisting the record fails", func() {
|
||||
repository.SetCreateError(fakes.FakeError)
|
||||
err := t.Execute(logs, headerOne, constants.HeaderMissing)
|
||||
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(fakes.FakeError))
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user