forked from cerc-io/ipld-eth-server
Compare commits
412
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d24be29ef0 | ||
|
|
0e91e42128 | ||
|
|
4e9f4bbad6 | ||
|
|
e40e8a7335 | ||
|
|
ddc7485c08 | ||
|
|
cc35b06ddf | ||
|
|
1a24075da7 | ||
|
|
c9e6055cfd | ||
|
|
9eb7b22f47 | ||
|
|
4cf523b626 | ||
|
|
451d71a528 | ||
|
|
ec21e95182 | ||
|
|
0d1da46d6d | ||
|
|
45502e7117 | ||
|
|
74a7748483 | ||
|
|
415e426946 | ||
|
|
ba01123f54 | ||
|
|
b90c28906a | ||
|
|
6cb54ca790 | ||
|
|
bcd00c6e2b | ||
|
|
923a8c3241 | ||
|
|
07a991d3e4 | ||
|
|
a63640933e | ||
|
|
a5ac6c19cf | ||
|
|
45c5a25d54 | ||
|
|
72e903cf68 | ||
|
|
b7b4631231 | ||
|
|
f8de0defad | ||
|
|
aaf571b04c | ||
|
|
4121863a4c | ||
|
|
fa4428b167 | ||
|
|
221114cf56 | ||
|
|
8e652a634f | ||
|
|
1fc53ccab1 | ||
|
|
31e9a7dc5e | ||
|
|
c19cc5c44d | ||
|
|
6bd563e3d5 | ||
|
|
b3d9e01d67 | ||
|
|
0c44882cb2 | ||
|
|
80413211c2 | ||
|
|
7f15befdee | ||
|
|
217cfc63ec | ||
|
|
9550d60467 | ||
|
|
da1c8b2332 | ||
|
|
fe7329c284 | ||
|
|
f144d932cd | ||
|
|
491939b35f | ||
|
|
248d9eb7a2 | ||
|
|
a3d2e71ca6 | ||
|
|
be1d4281e8 | ||
|
|
e2b83a5737 | ||
|
|
613dd6acf4 | ||
|
|
8966d1b6c2 | ||
|
|
c556845d1a | ||
|
|
cf8c08f01e | ||
|
|
5c6fb45d36 | ||
|
|
222fa6d6ed | ||
|
|
6c82eafef1 | ||
|
|
89b9a05234 | ||
|
|
27d9413544 | ||
|
|
4817674c5c | ||
|
|
8556332ff8 | ||
|
|
81884d4826 | ||
|
|
389ba2e360 | ||
|
|
c25d220f71 | ||
|
|
8ea4d1e40d | ||
|
|
ace12fbc35 | ||
|
|
8b8f644b2a | ||
|
|
8bfcce95ca | ||
|
|
0b12c3c329 | ||
|
|
037131528c | ||
|
|
be34f0e8fd | ||
|
|
04096fab0c | ||
|
|
dc14656bbc | ||
|
|
9676690a2d | ||
|
|
025b1c7c9e | ||
|
|
36e590c092 | ||
|
|
384d939dc2 | ||
|
|
24a45a3f00 | ||
|
|
867323a1ab | ||
|
|
da30a957a7 | ||
|
|
705835512e | ||
|
|
2a0e9f8dfd | ||
|
|
c8bdaefe97 | ||
|
|
a141d154b7 | ||
|
|
76986d9497 | ||
|
|
233fa29740 | ||
|
|
4883590d85 | ||
|
|
181e3745f1 | ||
|
|
4b697b2a98 | ||
|
|
77812f2673 | ||
|
|
849b17f4bd | ||
|
|
56c85709c1 | ||
|
|
2be2a06575 | ||
|
|
39fb9207b2 | ||
|
|
3914889d53 | ||
|
|
1a6ff273ba | ||
|
|
f57b816530 | ||
|
|
76aaa93c50 | ||
|
|
778bf82dfd | ||
|
|
e8c1db69b4 | ||
|
|
86eee94f9b | ||
|
|
d785fda414 | ||
|
|
41e04a1c30 | ||
|
|
3aa5cb36ef | ||
|
|
43ddbc7eea | ||
|
|
8df8b50cb1 | ||
|
|
072ba1edcc | ||
|
|
f61691a26e | ||
|
|
6ab34eb878 | ||
|
|
3e211d5978 | ||
|
|
f600ea46bc | ||
|
|
6fa38fd198 | ||
|
|
2fa941f084 | ||
|
|
14332c2cd9 | ||
|
|
a780782bb6 | ||
|
|
627f2c7f81 | ||
|
|
925b22869a | ||
|
|
e6fb859967 | ||
|
|
850b305bf6 | ||
|
|
2f6f939982 | ||
|
|
409521416b | ||
|
|
72d3174f63 | ||
|
|
e288a2933d | ||
|
|
1020ec18a4 | ||
|
|
3118bf4964 | ||
|
|
61e6585f1c | ||
|
|
e924974ece | ||
|
|
20b75ff18f | ||
|
|
ef65993412 | ||
|
|
520be6bc86 | ||
|
|
01074ab55c | ||
|
|
88e20dfc6e | ||
|
|
4446219c36 | ||
|
|
88a89c0cc2 | ||
|
|
63e77c9bc0 | ||
|
|
19570f733f | ||
|
|
3058dc48a8 | ||
|
|
5d86b6e029 | ||
|
|
3b863dc76b | ||
|
|
e92b66638e | ||
|
|
cb39a94792 | ||
|
|
ef4d1f958d | ||
|
|
de12fab935 | ||
|
|
34a6d3af4b | ||
|
|
c28ebcc4f2 | ||
|
|
93a995627c | ||
|
|
316bf0990a | ||
|
|
143d79fdfc | ||
|
|
c07ee1d78a | ||
|
|
c3bcb8138b | ||
|
|
f733250afe | ||
|
|
e6869f4236 | ||
|
|
f3c247b54b | ||
|
|
ace7eaad7d | ||
|
|
45de492b4c | ||
|
|
b56215db3f | ||
|
|
f9a31ed862 | ||
|
|
84e22e3b9c | ||
|
|
907c7132f4 | ||
|
|
373d24e26b | ||
|
|
34a2e2369c | ||
|
|
1b513a3c02 | ||
|
|
bd093e0ea6 | ||
|
|
19e3f04f29 | ||
|
|
140989cbf7 | ||
|
|
68152b0d77 | ||
|
|
6d676a342d | ||
|
|
b912b7e75a | ||
|
|
9347affb77 | ||
|
|
61e04e1d9a | ||
|
|
c3d267a6d4 | ||
|
|
fcd2b963b0 | ||
|
|
20c04a98bc | ||
|
|
25b49f784b | ||
|
|
2a1ec043b8 | ||
|
|
e9b5e6b995 | ||
|
|
d53bfae243 | ||
|
|
2fc57c2f31 | ||
|
|
f184b9fd49 | ||
|
|
bc986f7aad | ||
|
|
1973e8032d | ||
|
|
58cb6c252d | ||
|
|
9fef3687a0 | ||
|
|
88ced60707 | ||
|
|
cc6822f7a6 | ||
|
|
0f2b6fd843 | ||
|
|
13e0a5cd19 | ||
|
|
b0674df3ce | ||
|
|
e81da697bc | ||
|
|
7eb7849b5e | ||
|
|
500bba43b4 | ||
|
|
f09f665b11 | ||
|
|
a1781b1eeb | ||
|
|
f6a6a294bb | ||
|
|
81546618d3 | ||
|
|
54db8f23e0 | ||
|
|
1057b001f1 | ||
|
|
3a216b2ca3 | ||
|
|
bca33381dd | ||
|
|
70c539c8d7 | ||
|
|
ec3165f62c | ||
|
|
c3eab5d58e | ||
|
|
6d6f97169b | ||
|
|
77d28264f7 | ||
|
|
885d934c90 | ||
|
|
5acecec955 | ||
|
|
5772d52eb1 | ||
|
|
2de9c5bd48 | ||
|
|
838ed033f8 | ||
|
|
cf4543961c | ||
|
|
f9abcfd33c | ||
|
|
32b4f56557 | ||
|
|
ed4171a1ab | ||
|
|
7f0c8fb2a1 | ||
|
|
04a0f9a751 | ||
|
|
1467ea3924 | ||
|
|
458aae1c1e | ||
|
|
4d9edd2008 | ||
|
|
121c75cc1d | ||
|
|
967c148eff | ||
|
|
71837c4b24 | ||
|
|
d0c3241730 | ||
|
|
a28892f1d3 | ||
|
|
3d1b308326 | ||
|
|
c0a4600ce7 | ||
|
|
b3e4fbfa39 | ||
|
|
000c0ef066 | ||
|
|
d8a5358a70 | ||
|
|
e00e602098 | ||
|
|
148addb24d | ||
|
|
2630e2d8dc | ||
|
|
924d0be0b9 | ||
|
|
70f7face75 | ||
|
|
afc63ac960 | ||
|
|
f1a61d0991 | ||
|
|
9a5581b543 | ||
|
|
1d18d1fed8 | ||
|
|
0a14bd9f0f | ||
|
|
a3ca08b653 | ||
|
|
b2828a814f | ||
|
|
a284a566d5 | ||
|
|
b90fcb53e6 | ||
|
|
18266c4f9d | ||
|
|
42f066772b | ||
|
|
9aaea6e016 | ||
|
|
969da82f6e | ||
|
|
f7fe3c2fc1 | ||
|
|
c4f7fa2821 | ||
|
|
b1a6fb0514 | ||
|
|
e7744cce4b | ||
|
|
491d779d58 | ||
|
|
2c1bc557e5 | ||
|
|
9cf75ca66c | ||
|
|
3b3716499f | ||
|
|
b3ef2934b7 | ||
|
|
8c16dc2b35 | ||
|
|
7babaf6c05 | ||
|
|
954e028306 | ||
|
|
6844c60f7b | ||
|
|
97d8c4dc86 | ||
|
|
1141f3909c | ||
|
|
c62799fdd6 | ||
|
|
cef4b1ddc6 | ||
|
|
39141bd30d | ||
|
|
0aa0f38c93 | ||
|
|
8ac6d48772 | ||
|
|
bcbd2de5f3 | ||
|
|
d09b756768 | ||
|
|
36b62f9b72 | ||
|
|
9b960a105f | ||
|
|
2e5286baef | ||
|
|
f8b9d9475e | ||
|
|
9d590e15bc | ||
|
|
d5cf74e202 | ||
|
|
7d88e78fa8 | ||
|
|
9d6791706d | ||
|
|
50d53535bb | ||
|
|
c813ff00f4 | ||
|
|
53f06a66f5 | ||
|
|
3c57fa1064 | ||
|
|
141b3eaffe | ||
|
|
31cbaec567 | ||
|
|
75debec01a | ||
|
|
07519468e9 | ||
|
|
524ab42674 | ||
|
|
325516da70 | ||
|
|
08df7beca3 | ||
|
|
f4ff261b21 | ||
|
|
aff66d950b | ||
|
|
677cdf0a72 | ||
|
|
72e830e164 | ||
|
|
6b919a8734 | ||
|
|
86aa1c16e6 | ||
|
|
4ea61b08ca | ||
|
|
88e499e5d3 | ||
|
|
df454c414e | ||
|
|
5200fd71dc | ||
|
|
86f3f44cac | ||
|
|
f6780ddd95 | ||
|
|
8d10dc98ee | ||
|
|
d3f30b621b | ||
|
|
33bb152c04 | ||
|
|
bc4d277012 | ||
|
|
69438761c3 | ||
|
|
30658799dd | ||
|
|
86e9edd3d1 | ||
|
|
a181a5c25b | ||
|
|
9f81ffa8e0 | ||
|
|
36fe35123f | ||
|
|
211ec12009 | ||
|
|
7df5bbc99a | ||
|
|
e92d35b084 | ||
|
|
013946fd73 | ||
|
|
b11fb949f5 | ||
|
|
c0a91b9d9f | ||
|
|
ca07107cec | ||
|
|
b128f894c4 | ||
|
|
7c06d4b3a1 | ||
|
|
b208281ad6 | ||
|
|
b664aee621 | ||
|
|
cffceb53db | ||
|
|
dc25ea7f87 | ||
|
|
e1026d5261 | ||
|
|
a480c28a67 | ||
|
|
1d4abcb69b | ||
|
|
b5d57b6afc | ||
|
|
3af06ada1d | ||
|
|
7a2ccaa8a7 | ||
|
|
16aa9652a5 | ||
|
|
a8dd77294a | ||
|
|
4f4ab1dd4f | ||
|
|
ef2d8f789d | ||
|
|
e561fd3178 | ||
|
|
128e30b3a8 | ||
|
|
2d0367fe6c | ||
|
|
e1cab4fadc | ||
|
|
0a8b54d366 | ||
|
|
beea9d503d | ||
|
|
df1a233028 | ||
|
|
909c85b547 | ||
|
|
d9b05233b0 | ||
|
|
c5eae5fb75 | ||
|
|
ce706d0b07 | ||
|
|
5cad354eac | ||
|
|
60bfc1c045 | ||
|
|
20af343efb | ||
|
|
6369835757 | ||
|
|
5b177e716c | ||
|
|
33a0c8e0e7 | ||
|
|
71bc3f8e7b | ||
|
|
4a1a18610c | ||
|
|
98f5a16771 | ||
|
|
bf46200c07 | ||
|
|
797450dd2a | ||
|
|
770d27e2c8 | ||
|
|
6e127acbf3 | ||
|
|
4c18554fbe | ||
|
|
f627c2edfa | ||
|
|
1043df9156 | ||
|
|
b1f00c5776 | ||
|
|
2d15e8c2f1 | ||
|
|
a0d70b544b | ||
|
|
efebea7a18 | ||
|
|
4427bebe0a | ||
|
|
4e063171a9 | ||
|
|
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 |
@@ -0,0 +1,79 @@
|
||||
name: Test, Build, and/or Publish
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
# continue-on-error: true # Uncomment once integration is finished
|
||||
runs-on: ubuntu-latest
|
||||
# Map a step output to a job output
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v4
|
||||
with:
|
||||
# All of these options are optional, so you can remove them if you are happy with the defaults
|
||||
concurrent_skipping: "never"
|
||||
skip_after_successful_duplicate: "true"
|
||||
do_not_skip: '["workflow_dispatch", "schedule"]'
|
||||
run-tests:
|
||||
uses: ./.github/workflows/tests.yaml
|
||||
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
|
||||
needs: pre_job
|
||||
secrets:
|
||||
BUILD_HOSTNAME: ${{ secrets.BUILD_HOSTNAME }}
|
||||
BUILD_USERNAME: ${{ secrets.BUILD_USERNAME }}
|
||||
BUILD_KEY: ${{ secrets.BUILD_KEY }}
|
||||
with:
|
||||
STACK_ORCHESTRATOR_REF: "f2fd766f5400fcb9eb47b50675d2e3b1f2753702"
|
||||
GO_ETHEREUM_REF: "be1757b9fd884cb20c8d7faac8fa81fc49bb7216"
|
||||
IPLD_ETH_DB_REF: "be345e0733d2c025e4082c5154e441317ae94cf7"
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
needs: run-tests
|
||||
if: |
|
||||
always() &&
|
||||
(needs.run-tests.result == 'success' || needs.run-tests.result == 'skipped') &&
|
||||
github.event_name == 'release'
|
||||
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}}
|
||||
|
||||
push_to_registries:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
always() &&
|
||||
(needs.build.result == 'success') &&
|
||||
github.event_name == 'release'
|
||||
needs: build
|
||||
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}}
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Set up repo
|
||||
start_dir=$(pwd)
|
||||
temp_dir=$(mktemp -d)
|
||||
cd $temp_dir
|
||||
git clone -b $(cat /tmp/git_head_ref) "https://github.com/$(cat /tmp/git_repository).git"
|
||||
cd ipld-eth-server
|
||||
|
||||
## Remove the branch and github related info. This way future runs wont be confused.
|
||||
rm -f /tmp/git_head_ref /tmp/git_repository
|
||||
|
||||
# Spin up DB and run migrations
|
||||
docker-compose up -d migrations ipld-eth-db
|
||||
trap "docker-compose down -v --remove-orphans; cd $start_dir ; rm -r $temp_dir" SIGINT SIGTERM ERR
|
||||
sleep 30
|
||||
|
||||
# Remove old logs so there's no confusion, then run test
|
||||
rm -f /tmp/test.log /tmp/return_test.txt
|
||||
PGPASSWORD=password DATABASE_USER=vdbm DATABASE_PORT=8077 DATABASE_PASSWORD=password DATABASE_HOSTNAME=localhost DATABASE_NAME=vulcanize_testing make test > /tmp/test.log
|
||||
echo $? > /tmp/return_test.txt
|
||||
|
||||
# Clean up
|
||||
docker-compose down -v --remove-orphans
|
||||
cd $start_dir
|
||||
rm -fr $temp_dir
|
||||
@@ -0,0 +1,212 @@
|
||||
name: Test the stack.
|
||||
on:
|
||||
workflow_call:
|
||||
secrets:
|
||||
BUILD_HOSTNAME:
|
||||
required: true
|
||||
BUILD_USERNAME:
|
||||
required: true
|
||||
BUILD_KEY:
|
||||
required: true
|
||||
inputs:
|
||||
STACK_ORCHESTRATOR_REF:
|
||||
required: true
|
||||
type: string
|
||||
GO_ETHEREUM_REF:
|
||||
required: true
|
||||
type: string
|
||||
IPLD_ETH_DB_REF:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run docker build
|
||||
run: make docker-build
|
||||
test:
|
||||
name: Run unit tests
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
# To run the unit tests you need to add secrets to your repository.
|
||||
BUILD_HOSTNAME: ${{ secrets.BUILD_HOSTNAME }}
|
||||
BUILD_USERNAME: ${{ secrets.BUILD_USERNAME }}
|
||||
BUILD_KEY: ${{ secrets.BUILD_KEY }}
|
||||
#strategy:
|
||||
# matrix:
|
||||
# go-version: [1.16.x, 1.17.x, 1.18.x]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# Passed experience with GHA has taught me to store variables in files instead of passing them as variables.
|
||||
- name: Output variables to files
|
||||
run: |
|
||||
echo $GITHUB_REPOSITORY > /tmp/git_repository
|
||||
[ -z "$GITHUB_HEAD_REF" ] && echo $GITHUB_REF_NAME > /tmp/git_head_ref || echo $GITHUB_HEAD_REF > /tmp/git_head_ref
|
||||
echo "-----BEGIN OPENSSH PRIVATE KEY-----" >> /tmp/key
|
||||
echo ${{ env.BUILD_KEY }} >> /tmp/key
|
||||
echo "-----END OPENSSH PRIVATE KEY-----" >> /tmp/key
|
||||
chmod 400 /tmp/key
|
||||
cat /tmp/git_repository
|
||||
cat /tmp/git_head_ref
|
||||
|
||||
- name: Raw SCP
|
||||
run: |
|
||||
scp -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key /tmp/git_repository ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }}:/tmp/git_repository
|
||||
scp -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key /tmp/git_head_ref ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }}:/tmp/git_head_ref
|
||||
scp -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key .github/workflows/run_unit_test.sh ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }}:/tmp/run_unit_test.sh
|
||||
|
||||
- name: Trigger Unit Test
|
||||
run: |
|
||||
ssh -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }} go install github.com/onsi/ginkgo/ginkgo@latest
|
||||
ssh -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }} /tmp/run_unit_test.sh
|
||||
|
||||
- name: Get the logs and cat them
|
||||
run: |
|
||||
scp -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }}:/tmp/test.log .
|
||||
cat ./test.log
|
||||
|
||||
- name: Check Error Code
|
||||
run: |
|
||||
scp -o 'StrictHostKeyChecking no' -o UserKnownHostsFile=/dev/null -q -i /tmp/key ${{ env.BUILD_USERNAME }}@${{ env.BUILD_HOSTNAME }}:/tmp/return_test.txt .
|
||||
[ $(cat ./return_test.txt) -eq 0 ]
|
||||
|
||||
integrationtest:
|
||||
name: Run integration tests
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
DB_WRITE: true
|
||||
ETH_FORWARD_ETH_CALLS: false
|
||||
ETH_PROXY_ON_ERROR: false
|
||||
ETH_HTTP_PATH: "go-ethereum:8545"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create GOPATH
|
||||
run: mkdir -p /tmp/go
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18.x"
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: "./ipld-eth-server"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.STACK_ORCHESTRATOR_REF }}
|
||||
path: "./stack-orchestrator/"
|
||||
repository: vulcanize/stack-orchestrator
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.GO_ETHEREUM_REF }}
|
||||
repository: vulcanize/go-ethereum
|
||||
path: "./go-ethereum/"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.IPLD_ETH_DB_REF }}
|
||||
repository: vulcanize/ipld-eth-db
|
||||
path: "./ipld-eth-db/"
|
||||
- name: Create config file
|
||||
run: |
|
||||
echo vulcanize_go_ethereum=$GITHUB_WORKSPACE/go-ethereum/ > ./config.sh
|
||||
echo vulcanize_ipld_eth_db=$GITHUB_WORKSPACE/ipld-eth-db/ >> ./config.sh
|
||||
echo vulcanize_ipld_eth_server=$GITHUB_WORKSPACE/ipld-eth-server/ >> ./config.sh
|
||||
echo vulcanize_test_contract=$GITHUB_WORKSPACE/ipld-eth-server/test/contract >> ./config.sh
|
||||
echo genesis_file_path=start-up-files/go-ethereum/genesis.json >> ./config.sh
|
||||
echo db_write=$DB_WRITE >> ./config.sh
|
||||
echo eth_forward_eth_calls=$ETH_FORWARD_ETH_CALLS >> ./config.sh
|
||||
echo eth_proxy_on_error=$ETH_PROXY_ON_ERROR >> ./config.sh
|
||||
echo eth_http_path=$ETH_HTTP_PATH >> ./config.sh
|
||||
cat ./config.sh
|
||||
- name: Build geth
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
|
||||
./compile-geth.sh \
|
||||
-p "$GITHUB_WORKSPACE/config.sh" \
|
||||
-e docker
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
docker-compose \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-ipld-eth-server.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-contract.yml" \
|
||||
--env-file "$GITHUB_WORKSPACE/config.sh" \
|
||||
up -d --build
|
||||
- name: Test
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/ipld-eth-server
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8081)" != "200" ]; do echo "waiting for ipld-eth-server..." && sleep 5; done && \
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
|
||||
make integrationtest
|
||||
|
||||
integrationtest_forwardethcalls:
|
||||
name: Run integration tests for direct proxy fall-through of eth_calls
|
||||
env:
|
||||
GOPATH: /tmp/go
|
||||
DB_WRITE: false
|
||||
ETH_FORWARD_ETH_CALLS: true
|
||||
ETH_PROXY_ON_ERROR: false
|
||||
ETH_HTTP_PATH: "go-ethereum:8545"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create GOPATH
|
||||
run: mkdir -p /tmp/go
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.18.x"
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: "./ipld-eth-server"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.STACK_ORCHESTRATOR_REF }}
|
||||
path: "./stack-orchestrator/"
|
||||
repository: vulcanize/stack-orchestrator
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.GO_ETHEREUM_REF }}
|
||||
repository: vulcanize/go-ethereum
|
||||
path: "./go-ethereum/"
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ inputs.IPLD_ETH_DB_REF }}
|
||||
repository: vulcanize/ipld-eth-db
|
||||
path: "./ipld-eth-db/"
|
||||
- name: Create config file
|
||||
run: |
|
||||
echo vulcanize_go_ethereum=$GITHUB_WORKSPACE/go-ethereum/ > ./config.sh
|
||||
echo vulcanize_ipld_eth_db=$GITHUB_WORKSPACE/ipld-eth-db/ >> ./config.sh
|
||||
echo vulcanize_ipld_eth_server=$GITHUB_WORKSPACE/ipld-eth-server/ >> ./config.sh
|
||||
echo vulcanize_test_contract=$GITHUB_WORKSPACE/ipld-eth-server/test/contract >>./config.sh
|
||||
echo genesis_file_path=start-up-files/go-ethereum/genesis.json >> ./config.sh
|
||||
echo db_write=$DB_WRITE >> ./config.sh
|
||||
echo eth_forward_eth_calls=$ETH_FORWARD_ETH_CALLS >> ./config.sh
|
||||
echo eth_proxy_on_error=$ETH_PROXY_ON_ERROR >> ./config.sh
|
||||
echo eth_http_path=$ETH_HTTP_PATH >> ./config.sh
|
||||
cat ./config.sh
|
||||
- name: Build geth
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/stack-orchestrator/helper-scripts
|
||||
./compile-geth.sh \
|
||||
-p "$GITHUB_WORKSPACE/config.sh" \
|
||||
-e docker
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
docker-compose \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-db-sharding.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-go-ethereum.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-ipld-eth-server.yml" \
|
||||
-f "$GITHUB_WORKSPACE/stack-orchestrator/docker/local/docker-compose-contract.yml" \
|
||||
--env-file "$GITHUB_WORKSPACE/config.sh" \
|
||||
up -d --build
|
||||
- name: Test
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/ipld-eth-server
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8081)" != "200" ]; do echo "waiting for ipld-eth-server..." && sleep 5; done && \
|
||||
while [ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8545)" != "200" ]; do echo "waiting for geth-statediff..." && sleep 5; done && \
|
||||
make integrationtest
|
||||
+1
-1
@@ -7,7 +7,7 @@ Vagrantfile
|
||||
vagrant*.sh
|
||||
.vagrant
|
||||
test_scripts/
|
||||
ipfs-blockchain-watcher
|
||||
ipld-eth-server
|
||||
postgraphile/build/
|
||||
postgraphile/node_modules/
|
||||
postgraphile/package-lock.json
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
dist: trusty
|
||||
language: go
|
||||
go:
|
||||
- 1.12
|
||||
services:
|
||||
- postgresql
|
||||
addons:
|
||||
ssh_known_hosts: arch1.vdb.to
|
||||
postgresql: '11.2'
|
||||
go_import_path: github.com/vulcanize/ipfs-blockchain-watcher
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_e1db309e8776_key -iv $encrypted_e1db309e8776_iv
|
||||
-in temp_rsa.enc -out temp_rsa -d
|
||||
- eval "$(ssh-agent -s)"
|
||||
- chmod 600 temp_rsa
|
||||
- ssh-add temp_rsa
|
||||
- ssh -4 -fNL 8545:localhost:8545 geth@arch1.vdb.to
|
||||
- make installtools
|
||||
- bash ./scripts/install-postgres-11.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
|
||||
script:
|
||||
- env GO111MODULE=on make test
|
||||
- env GO111MODULE=on make integrationtest
|
||||
notifications:
|
||||
email: false
|
||||
+43
-18
@@ -1,24 +1,49 @@
|
||||
FROM golang:alpine as builder
|
||||
RUN apk --update --no-cache add make git g++
|
||||
FROM golang:1.18-alpine as builder
|
||||
|
||||
# Build statically linked vDB binary (wonky path because of Dep)
|
||||
RUN mkdir -p /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
ADD . /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
WORKDIR /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
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
|
||||
|
||||
# Second stage
|
||||
# Cache the modules
|
||||
ENV GO111MODULE=on
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build the binary
|
||||
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o ipld-eth-server .
|
||||
|
||||
# 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/ipfs-blockchain-watcher/vulcanizedb /app/vulcanizedb
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/environments/staging.toml /app/environments/
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/dockerfiles/startup_script.sh /app/
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/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/environments environments
|
||||
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
|
||||
@@ -3,16 +3,11 @@ BASE = $(GOPATH)/src/$(PACKAGE)
|
||||
PKGS = go list ./... | grep -v "^vendor/"
|
||||
|
||||
# Tools
|
||||
## Testing library
|
||||
GINKGO = $(BIN)/ginkgo
|
||||
$(BIN)/ginkgo:
|
||||
go get -u github.com/onsi/ginkgo/ginkgo
|
||||
|
||||
## Migration tool
|
||||
GOOSE = $(BIN)/goose
|
||||
$(BIN)/goose:
|
||||
go get -u -d github.com/pressly/goose/cmd/goose
|
||||
go build -tags='no_mysql no_sqlite' -o $(BIN)/goose github.com/pressly/goose/cmd/goose
|
||||
go get -u github.com/pressly/goose/cmd/goose
|
||||
|
||||
## Source linter
|
||||
LINT = $(BIN)/golint
|
||||
@@ -27,8 +22,9 @@ $(BIN)/gometalinter.v2:
|
||||
|
||||
|
||||
.PHONY: installtools
|
||||
installtools: | $(LINT) $(GOOSE) $(GINKGO)
|
||||
installtools: | $(LINT) $(GOOSE)
|
||||
echo "Installing tools"
|
||||
go mod download
|
||||
|
||||
.PHONY: metalint
|
||||
metalint: | $(METALINT)
|
||||
@@ -46,37 +42,35 @@ HOST_NAME = localhost
|
||||
PORT = 5432
|
||||
NAME =
|
||||
USER = postgres
|
||||
CONNECT_STRING=postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(NAME)?sslmode=disable
|
||||
PASSWORD = password
|
||||
CONNECT_STRING=postgresql://$(USER):$(PASSWORD)@$(HOST_NAME):$(PORT)/$(NAME)?sslmode=disable
|
||||
|
||||
#Test
|
||||
TEST_DB = vulcanize_testing
|
||||
TEST_CONNECT_STRING = postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(TEST_DB)?sslmode=disable
|
||||
TEST_CONNECT_STRING = postgresql://$(DATABASE_USER):$(DATABASE_PASSWORD)@$(DATABASE_HOSTNAME):$(DATABASE_PORT)/$(TEST_DB)?sslmode=disable
|
||||
TEST_CONNECT_STRING_LOCAL = postgresql://$(USER)@$(HOST_NAME):$(PORT)/$(TEST_DB)?sslmode=disable
|
||||
|
||||
.PHONY: test
|
||||
test: | $(GINKGO) $(LINT)
|
||||
test: | $(GOOSE)
|
||||
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
|
||||
go run github.com/onsi/ginkgo/ginkgo -r --skipPackage=test
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest: | $(GINKGO) $(LINT)
|
||||
integrationtest: | $(GOOSE)
|
||||
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/
|
||||
go run github.com/onsi/ginkgo/ginkgo -r test/ -v
|
||||
|
||||
.PHONY: test_local
|
||||
test_local: | $(GOOSE)
|
||||
go vet ./...
|
||||
go fmt ./...
|
||||
./scripts/run_unit_test.sh
|
||||
|
||||
build:
|
||||
go fmt ./...
|
||||
go build
|
||||
GO111MODULE=on go build
|
||||
|
||||
# Parameter checks
|
||||
## Check that DB variables are provided
|
||||
@@ -105,7 +99,7 @@ rollback: $(GOOSE) checkdbvars
|
||||
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
|
||||
$(GOOSE) -dir db/migrations postgres "$(CONNECT_STRING)" down-to "$(MIGRATION)"
|
||||
@@ -138,25 +132,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,170 +1,41 @@
|
||||
# ipfs-blockchain-watcher
|
||||
# ipld-eth-server
|
||||
|
||||
[](https://goreportcard.com/report/github.com/vulcanize/ipfs-blockchain-watcher)
|
||||
[](https://goreportcard.com/report/github.com/vulcanize/ipld-eth-server)
|
||||
|
||||
> Tool for extracting and indexing blockchain data on PG-IPFS
|
||||
> ipld-eth-server is the server backend for indexed ETH IPLD objects
|
||||
|
||||
## Table of Contents
|
||||
1. [Background](#background)
|
||||
1. [Architecture](#architecture)
|
||||
1. [Install](#install)
|
||||
1. [Usage](#usage)
|
||||
1. [Contributing](#contributing)
|
||||
1. [License](#license)
|
||||
|
||||
## Background
|
||||
ipfs-blockchain-watcher is a collection of interfaces that are used to extract, process, and store in Postgres-IPFS
|
||||
all chain data. The raw data indexed by ipfs-blockchain-watcher serves as the basis for more specific watchers and applications.
|
||||
NOTE: WIP
|
||||
|
||||
Currently the service supports complete processing of all Bitcoin and Ethereum data.
|
||||
ipld-eth-server is used to service queries against the Ethereum IPLD objects indexed by [ipld-eth-indexer](https://github.com/vulcanize/ipld-eth-indexer).
|
||||
|
||||
## Architecture
|
||||
More details on the design of ipfs-blockchain-watcher can be found in [here](./documentation/architecture.md)
|
||||
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
|
||||
Minimal build dependencies
|
||||
* Go (1.18)
|
||||
* Git
|
||||
* GCC compiler
|
||||
* This repository
|
||||
|
||||
External dependency
|
||||
* Postgres database populated by [ipld-eth-db](https://github.com/vulcanize/ipld-eth-db)
|
||||
|
||||
## Install
|
||||
1. [Postgres](#postgres)
|
||||
1. [Goose](#goose)
|
||||
1. [IPFS](#ipfs)
|
||||
1. [Blockchain](#blockchain)
|
||||
1. [Watcher](#watcher)
|
||||
Start by downloading ipld-eth-server and moving into the repo:
|
||||
|
||||
### Postgres
|
||||
1. [Install Postgres](https://wiki.postgresql.org/wiki/Detailed_installation_guides)
|
||||
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/ipfs-blockchain-watcher`
|
||||
1. Run the migrations: `make migrate HOST_NAME=localhost NAME=vulcanize_public PORT=5432`
|
||||
- There are optional vars `USER=username` and `PASS=password` if the database user is not the default user `postgres` and/or a password is present
|
||||
- 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`
|
||||
`GO111MODULE=off go get -d github.com/vulcanize/ipld-eth-server/v4`
|
||||
|
||||
* 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)
|
||||
|
||||
### Goose
|
||||
We use [goose](https://github.com/pressly/goose) as our migration management tool. While it is not necessary to use `goose` for manual setup, it
|
||||
is required for running the automated tests.
|
||||
|
||||
### IPFS
|
||||
We use IPFS to store IPLD objects for each type of data we extract from on chain.
|
||||
|
||||
To start, download and install [IPFS](https://github.com/vulcanize/go-ipfs):
|
||||
|
||||
`go get github.com/ipfs/go-ipfs`
|
||||
|
||||
`cd $GOPATH/src/github.com/ipfs/go-ipfs`
|
||||
|
||||
`make install`
|
||||
|
||||
If we want to use Postgres as our backing datastore, we need to use the vulcanize fork of go-ipfs.
|
||||
|
||||
Start by adding the fork and switching over to it:
|
||||
|
||||
`git remote add vulcanize https://github.com/vulcanize/go-ipfs.git`
|
||||
|
||||
`git fetch vulcanize`
|
||||
|
||||
`git checkout -b postgres_update vulcanize/postgres_update`
|
||||
|
||||
Now install this fork of ipfs, first be sure to remove any previous installation:
|
||||
|
||||
`make install`
|
||||
|
||||
Check that is installed properly by running:
|
||||
|
||||
`ipfs`
|
||||
|
||||
You should see the CLI info/help output.
|
||||
|
||||
And now we initialize with the `postgresds` profile.
|
||||
If ipfs was previously initialized we will need to remove the old profile first.
|
||||
We also need to provide env variables for the postgres connection:
|
||||
|
||||
We can either set these manually, e.g.
|
||||
```bash
|
||||
export IPFS_PGHOST=
|
||||
export IPFS_PGUSER=
|
||||
export IPFS_PGDATABASE=
|
||||
export IPFS_PGPORT=
|
||||
export IPFS_PGPASSWORD=
|
||||
```
|
||||
|
||||
And then run the ipfs command:
|
||||
|
||||
`ipfs init --profile=postgresds`
|
||||
|
||||
Or we can use the pre-made script at `GOPATH/src/github.com/ipfs/go-ipfs/misc/utility/ipfs_postgres.sh`
|
||||
which has usage:
|
||||
|
||||
`./ipfs_postgres.sh <IPFS_PGHOST> <IPFS_PGPORT> <IPFS_PGUSER> <IPFS_PGDATABASE>"`
|
||||
|
||||
and will ask us to enter the password, avoiding storing it to an ENV variable.
|
||||
|
||||
Once we have initialized ipfs, that is all we need to do with it- we do not need to run a daemon during the subsequent processes.
|
||||
|
||||
### Blockchain
|
||||
This section describes how to setup an Ethereum or Bitcoin node to serve as a data source for ipfs-blockchain-watcher
|
||||
|
||||
#### Ethereum
|
||||
For Ethereum, we currently *require* [a special fork of go-ethereum](https://github.com/vulcanize/go-ethereum/tree/statediff_at_anyblock-1.9.11). This can be setup as follows.
|
||||
Skip this steps if you already have access to a node that displays the statediffing endpoints.
|
||||
|
||||
Begin by downloading geth and switching to the vulcanize/rpc_statediffing branch:
|
||||
|
||||
`go get github.com/ethereum/go-ethereum`
|
||||
|
||||
`cd $GOPATH/src/github.com/ethereum/go-ethereum`
|
||||
|
||||
`git remote add vulcanize https://github.com/vulcanize/go-ethereum.git`
|
||||
|
||||
`git fetch vulcanize`
|
||||
|
||||
`git checkout -b statediffing vulcanize/statediff_at_anyblock-1.9.11`
|
||||
|
||||
Now, install this fork of geth (make sure any old versions have been uninstalled/binaries removed first):
|
||||
|
||||
`make geth`
|
||||
|
||||
And run the output binary with statediffing turned on:
|
||||
|
||||
`cd $GOPATH/src/github.com/ethereum/go-ethereum/build/bin`
|
||||
|
||||
`./geth --statediff --statediff.streamblock --ws --syncmode=full`
|
||||
|
||||
Note: if you wish to access historical data (perform `backFill`) then the node will need to operate as an archival node (`--gcmode=archive`)
|
||||
|
||||
Note: other CLI options- statediff specific ones included- can be explored with `./geth help`
|
||||
|
||||
The output from geth should mention that it is `Starting statediff service` and block synchronization should begin shortly thereafter.
|
||||
Note that until it receives a subscriber, the statediffing process does nothing but wait for one. Once a subscription is received, this
|
||||
will be indicated in the output and node will begin processing and sending statediffs.
|
||||
|
||||
Also in the output will be the endpoints that we will use to interface with the node.
|
||||
The default ws url is "127.0.0.1:8546" and the default http url is "127.0.0.1:8545".
|
||||
These values will be used as the `ethereum.wsPath` and `ethereum.httpPath` in the config, respectively.
|
||||
|
||||
#### Bitcoin
|
||||
For Bitcoin, ipfs-blockchain-watcher is able to operate entirely through the universally exposed JSON-RPC interfaces.
|
||||
This means we can use any of the standard full nodes (e.g. bitcoind, btcd) as our data source.
|
||||
|
||||
Point at a remote node or set one up locally using the instructions for [bitcoind](https://github.com/bitcoin/bitcoin) and [btcd](https://github.com/btcsuite/btcd).
|
||||
|
||||
The default http url is "127.0.0.1:8332". We will use the http endpoint as both the `bitcoin.wsPath` and `bitcoin.httpPath`
|
||||
(bitcoind does not support websocket endpoints, we are currently using a "subscription" wrapper around the http endpoints)
|
||||
|
||||
### Watcher
|
||||
Finally, we can setup the watcher process itself.
|
||||
|
||||
Start by downloading vulcanizedb and moving into the repo:
|
||||
|
||||
`go get github.com/vulcanize/ipfs-blockchain-watcher`
|
||||
|
||||
`cd $GOPATH/src/github.com/vulcanize/ipfs-blockchain-watcher`
|
||||
`cd $GOPATH/src/github.com/vulcanize/ipld-eth-server/v4@v4.x.x`
|
||||
|
||||
Then, build the binary:
|
||||
|
||||
@@ -173,62 +44,35 @@ Then, build the binary:
|
||||
## Usage
|
||||
After building the binary, run as
|
||||
|
||||
`./ipfs-blockchain-watcher watch --config=<config_file.toml`
|
||||
`./ipld-eth-server serve --config=<the name of your config file.toml>`
|
||||
|
||||
### Configuration
|
||||
|
||||
Below is the set of universal config parameters for the ipfs-blockchain-watcher command, in .toml form, with the respective environmental variables commented to the side.
|
||||
This set of parameters needs to be set no matter the chain type.
|
||||
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.
|
||||
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "vdbm" # $DATABASE_USER
|
||||
user = "postgres" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[ipfs]
|
||||
path = "~/.ipfs" # $IPFS_PATH
|
||||
mode = "postgres" # $IPFS_MODE
|
||||
[log]
|
||||
level = "info" # $LOGRUS_LEVEL
|
||||
|
||||
[superNode]
|
||||
chain = "bitcoin" # $SUPERNODE_CHAIN
|
||||
server = true # $SUPERNODE_SERVER
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SUPERNODE_IPC_PATH
|
||||
wsPath = "127.0.0.1:8082" # $SUPERNODE_WS_PATH
|
||||
httpPath = "127.0.0.1:8083" # $SUPERNODE_HTTP_PATH
|
||||
sync = true # $SUPERNODE_SYNC
|
||||
workers = 1 # $SUPERNODE_WORKERS
|
||||
backFill = true # $SUPERNODE_BACKFILL
|
||||
frequency = 45 # $SUPERNODE_FREQUENCY
|
||||
batchSize = 1 # $SUPERNODE_BATCH_SIZE
|
||||
batchNumber = 50 # $SUPERNODE_BATCH_NUMBER
|
||||
timeout = 300 # $HTTP_TIMEOUT
|
||||
validationLevel = 1 # $SUPERNODE_VALIDATION_LEVEL
|
||||
```
|
||||
[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
|
||||
graphql = true # $SERVER_GRAPHQL
|
||||
graphqlEndpoint = "" # $SERVER_GRAPHQL_ENDPOINT
|
||||
|
||||
Additional parameters need to be set depending on the specific chain.
|
||||
|
||||
For Bitcoin:
|
||||
|
||||
```toml
|
||||
[bitcoin]
|
||||
wsPath = "127.0.0.1:8332" # $BTC_WS_PATH
|
||||
httpPath = "127.0.0.1:8332" # $BTC_HTTP_PATH
|
||||
pass = "password" # $BTC_NODE_PASSWORD
|
||||
user = "username" # $BTC_NODE_USER
|
||||
nodeID = "ocd0" # $BTC_NODE_ID
|
||||
clientName = "Omnicore" # $BTC_CLIENT_NAME
|
||||
genesisBlock = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" # $BTC_GENESIS_BLOCK
|
||||
networkID = "0xD9B4BEF9" # $BTC_NETWORK_ID
|
||||
```
|
||||
|
||||
For Ethereum:
|
||||
|
||||
```toml
|
||||
[ethereum]
|
||||
wsPath = "127.0.0.1:8546" # $ETH_WS_PATH
|
||||
chainID = "1" # $ETH_CHAIN_ID
|
||||
defaultSender = "" # $ETH_DEFAULT_SENDER_ADDR
|
||||
rpcGasCap = "1000000000000" # $ETH_RPC_GAS_CAP
|
||||
httpPath = "127.0.0.1:8545" # $ETH_HTTP_PATH
|
||||
nodeID = "arch1" # $ETH_NODE_ID
|
||||
clientName = "Geth" # $ETH_CLIENT_NAME
|
||||
@@ -236,12 +80,86 @@ For Ethereum:
|
||||
networkID = "1" # $ETH_NETWORK_ID
|
||||
```
|
||||
|
||||
### Exposing the data
|
||||
We can expose a number of different APIs for remote access to ipfs-blockchain-watcher data, these are dicussed in more detail [here](./documentation/apis.md)
|
||||
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
|
||||
The `ethereum` fields set the chainID and default sender address to use for EVM simulation, and can optionally be used to configure a remote eth node to forward cache misses to
|
||||
|
||||
|
||||
### 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_call`
|
||||
`eth_getBalance`
|
||||
`eth_getStorageAt`
|
||||
`eth_getCode`
|
||||
`eth_getProof`
|
||||
`eth_blockNumber`
|
||||
`eth_getHeaderByNumber`
|
||||
`eth_getHeaderByHash`
|
||||
`eth_getBlockByNumber`
|
||||
`eth_getBlockByHash`
|
||||
`eth_getTransactionCount`
|
||||
`eth_getBlockTransactionCountByHash`
|
||||
`eth_getBlockTransactionCountByNumber`
|
||||
`eth_getTransactionByHash`
|
||||
`eth_getRawTransactionByHash`
|
||||
`eth_getTransactionByBlockHashAndIndex`
|
||||
`eth_getTransactionByBlockNumberAndIndex`
|
||||
`eth_getRawTransactionByBlockHashAndIndex`
|
||||
`eth_getRawTransactionByBlockNumberAndIndex`
|
||||
`eth_getTransactionReceipt`
|
||||
`eth_getLogs`
|
||||
`eth_getUncleCountByBlockHash`
|
||||
`eth_getUncleCountByBlockNumber`
|
||||
`eth_getUncleByBlockHashAndIndex`
|
||||
`eth_getUncleByBlockNumberAndIndex`
|
||||
|
||||
TODO: Add the rest of the standard endpoints and unique endpoints (e.g. getSlice)
|
||||
|
||||
|
||||
### CLI Options and Environment variables
|
||||
|
||||
|
||||
| CLI Option | Environment Variable | Default Value | Comment |
|
||||
| ----------------------------- | ----------------------------- | ---------------- | ----------------------------------- |
|
||||
| `database-hostname` | `DATABASE_HOSTNAME` | localhost | IPLD database host |
|
||||
| `database-port` | `DATABASE_PORT` | 5432 | IPLD database port |
|
||||
| `database-name` | `DATABASE_NAME` | vulcanize_public | IPLD database name |
|
||||
| `database-user` | `DATABASE_USER` | | IPLD database user |
|
||||
| `database-password` | `DATABASE_PASSWORD` | | IPLD database password |
|
||||
| `eth-server-graphql` | `ETH_SERVER_GRAPHQL` | false | If `true` enable Eth GraphQL Server |
|
||||
| `eth-server-graphql-path` | `ETH_SERVER_GRAPHQLPATH` | | If `eth-server-graphql` set to true, endpoint url for graphql server (host:port) |
|
||||
| `eth-server-http` | `ETH_SERVER_HTTP` | true | If `true` enable Eth HTTP JSON-RPC Server |
|
||||
| `eth-server-http-path` | `ETH_SERVER_HTTPPATH` | | If `eth-server-http` set to `true`, endpoint url for Eth HTTP JSON-RPC server (host:port) |
|
||||
| `eth-server-ws` | `ETH_SERVER_WS` | false | If `true` enable Eth WS JSON-RPC Server |
|
||||
| `eth-server-ws-path` | `ETH_SERVER_WSPATH` | | If `eth-server-ws` set to `true`, endpoint url for Eth WS JSON-RPC server (host:port) |
|
||||
| `eth-server-ipc` | `ETH_SERVER_IPC` | false | If `true` enable Eth IPC JSON-RPC Server |
|
||||
| `eth-server-ipc-path` | `ETH_SERVER_IPC_PATH` | | If `eth-server-ws` set to `true`, path for Eth IPC JSON-RPC server |
|
||||
| `ipld-server-graphql` | `IPLD_SERVER_GRAPHQL` | false | If `true` enable IPLD GraphQL Server |
|
||||
| `ipld-server-graphql-path` | `IPLD_SERVER_GRAPHQLPATH` | | If `ipld-server-graphql` set to true, endpoint url for graphql server (host:port) |
|
||||
| `ipld-postgraphile-path` | `IPLD_POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of IPLD db |
|
||||
| `tracing-http-path` | `TRACING_HTTPPATH` | | If `ipld-server-graphql` set to true, http url for tracing server |
|
||||
| `tracing-postgraphile-path` | `TRACING.POSTGRAPHILEPATH` | | If `ipld-server-graphql` set to true, http url for postgraphile server on top of tracing db |
|
||||
|
||||
|
||||
### Testing
|
||||
`make test` will run the unit tests
|
||||
`make test` setups a clean `vulcanize_testing` db
|
||||
|
||||
Follow steps in [test/README.md](./test/README.md)
|
||||
|
||||
## Monitoring
|
||||
|
||||
* Enable http server and metrics using parameters `--http --metrics`
|
||||
* ipld-eth-server exposes prometheus metrics at `/metric` endpoint
|
||||
* start prometheus using `monitoring/prometheus.yml` config (`prometheus --config.file=monitoring/prometheus.yml`)
|
||||
* start grafana, connect to prometheus datasource and import dashboard from `monitoring/grafana/dashboard_main.json`
|
||||
|
||||

|
||||
|
||||
|
||||
## Contributing
|
||||
Contributions are welcome!
|
||||
@@ -249,4 +167,4 @@ 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](LICENSE) © Vulcanize Inc
|
||||
[AGPL-3.0](LICENSE) © Vulcanize Inc
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"chainId": 4,
|
||||
"homesteadBlock": 1,
|
||||
"eip150Block": 2,
|
||||
"eip150Hash": "0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9",
|
||||
"eip155Block": 3,
|
||||
"eip158Block": 3,
|
||||
"byzantiumBlock": 3,
|
||||
"constantinopleBlock": 3,
|
||||
"petersburgBlock": 3,
|
||||
"istanbulBlock": 3,
|
||||
"clique": {
|
||||
"period": 15,
|
||||
"epoch": 30000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright © 2021 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 (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func addDatabaseFlags(command *cobra.Command) {
|
||||
// database flags
|
||||
command.PersistentFlags().String("database-name", "vulcanize_public", "database name")
|
||||
command.PersistentFlags().Int("database-port", 5432, "database port")
|
||||
command.PersistentFlags().String("database-hostname", "localhost", "database hostname")
|
||||
command.PersistentFlags().String("database-user", "", "database user")
|
||||
command.PersistentFlags().String("database-password", "", "database password")
|
||||
|
||||
// database flag bindings
|
||||
viper.BindPFlag("database.name", command.PersistentFlags().Lookup("database-name"))
|
||||
viper.BindPFlag("database.port", command.PersistentFlags().Lookup("database-port"))
|
||||
viper.BindPFlag("database.hostname", command.PersistentFlags().Lookup("database-hostname"))
|
||||
viper.BindPFlag("database.user", command.PersistentFlags().Lookup("database-user"))
|
||||
viper.BindPFlag("database.password", command.PersistentFlags().Lookup("database-password"))
|
||||
}
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
// 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"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/resync"
|
||||
v "github.com/vulcanize/ipfs-blockchain-watcher/version"
|
||||
)
|
||||
|
||||
// resyncCmd represents the resync command
|
||||
var resyncCmd = &cobra.Command{
|
||||
Use: "resync",
|
||||
Short: "Resync historical data",
|
||||
Long: `Use this command to fill in sections of missing data in the super node`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
rsyncCmdCommand()
|
||||
},
|
||||
}
|
||||
|
||||
func rsyncCmdCommand() {
|
||||
logWithCommand.Infof("running vdb version: %s", v.VersionWithMeta)
|
||||
logWithCommand.Debug("loading super node configuration variables")
|
||||
rConfig, err := resync.NewReSyncConfig()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Infof("resync config: %+v", rConfig)
|
||||
if rConfig.IPFSMode == shared.LocalInterface {
|
||||
if err := ipfs.InitIPFSPlugins(); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
logWithCommand.Debug("initializing new resync service")
|
||||
rService, err := resync.NewResyncService(rConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("starting up resync process")
|
||||
if err := rService.Resync(); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Infof("%s %s resync finished", rConfig.Chain.String(), rConfig.ResyncType.String())
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(resyncCmd)
|
||||
|
||||
// flags
|
||||
resyncCmd.PersistentFlags().String("ipfs-path", "", "ipfs repository path")
|
||||
|
||||
resyncCmd.PersistentFlags().String("resync-chain", "", "which chain to support, options are currently Ethereum or Bitcoin.")
|
||||
resyncCmd.PersistentFlags().String("resync-type", "", "which type of data to resync")
|
||||
resyncCmd.PersistentFlags().Int("resync-start", 0, "block height to start resync")
|
||||
resyncCmd.PersistentFlags().Int("resync-stop", 0, "block height to stop resync")
|
||||
resyncCmd.PersistentFlags().Int("resync-batch-size", 0, "data fetching batch size")
|
||||
resyncCmd.PersistentFlags().Int("resync-batch-number", 0, "how many goroutines to fetch data concurrently")
|
||||
resyncCmd.PersistentFlags().Bool("resync-clear-old-cache", false, "if true, clear out old data of the provided type within the resync range before resyncing")
|
||||
resyncCmd.PersistentFlags().Bool("resync-reset-validation", false, "if true, reset times_validated to 0")
|
||||
resyncCmd.PersistentFlags().Int("resync-timeout", 15, "timeout used for resync http requests")
|
||||
|
||||
resyncCmd.PersistentFlags().String("btc-http-path", "", "http url for bitcoin node")
|
||||
resyncCmd.PersistentFlags().String("btc-password", "", "password for btc node")
|
||||
resyncCmd.PersistentFlags().String("btc-username", "", "username for btc node")
|
||||
resyncCmd.PersistentFlags().String("btc-node-id", "", "btc node id")
|
||||
resyncCmd.PersistentFlags().String("btc-client-name", "", "btc client name")
|
||||
resyncCmd.PersistentFlags().String("btc-genesis-block", "", "btc genesis block hash")
|
||||
resyncCmd.PersistentFlags().String("btc-network-id", "", "btc network id")
|
||||
|
||||
resyncCmd.PersistentFlags().String("eth-http-path", "", "http url for ethereum node")
|
||||
resyncCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
resyncCmd.PersistentFlags().String("eth-client-name", "", "eth client name")
|
||||
resyncCmd.PersistentFlags().String("eth-genesis-block", "", "eth genesis block hash")
|
||||
resyncCmd.PersistentFlags().String("eth-network-id", "", "eth network id")
|
||||
|
||||
// and their bindings
|
||||
viper.BindPFlag("ipfs.path", resyncCmd.PersistentFlags().Lookup("ipfs-path"))
|
||||
|
||||
viper.BindPFlag("resync.chain", resyncCmd.PersistentFlags().Lookup("resync-chain"))
|
||||
viper.BindPFlag("resync.type", resyncCmd.PersistentFlags().Lookup("resync-type"))
|
||||
viper.BindPFlag("resync.start", resyncCmd.PersistentFlags().Lookup("resync-start"))
|
||||
viper.BindPFlag("resync.stop", resyncCmd.PersistentFlags().Lookup("resync-stop"))
|
||||
viper.BindPFlag("resync.batchSize", resyncCmd.PersistentFlags().Lookup("resync-batch-size"))
|
||||
viper.BindPFlag("resync.batchNumber", resyncCmd.PersistentFlags().Lookup("resync-batch-number"))
|
||||
viper.BindPFlag("resync.clearOldCache", resyncCmd.PersistentFlags().Lookup("resync-clear-old-cache"))
|
||||
viper.BindPFlag("resync.resetValidation", resyncCmd.PersistentFlags().Lookup("resync-reset-validation"))
|
||||
viper.BindPFlag("resync.timeout", resyncCmd.PersistentFlags().Lookup("resync-timeout"))
|
||||
|
||||
viper.BindPFlag("bitcoin.httpPath", resyncCmd.PersistentFlags().Lookup("btc-http-path"))
|
||||
viper.BindPFlag("bitcoin.pass", resyncCmd.PersistentFlags().Lookup("btc-password"))
|
||||
viper.BindPFlag("bitcoin.user", resyncCmd.PersistentFlags().Lookup("btc-username"))
|
||||
viper.BindPFlag("bitcoin.nodeID", resyncCmd.PersistentFlags().Lookup("btc-node-id"))
|
||||
viper.BindPFlag("bitcoin.clientName", resyncCmd.PersistentFlags().Lookup("btc-client-name"))
|
||||
viper.BindPFlag("bitcoin.genesisBlock", resyncCmd.PersistentFlags().Lookup("btc-genesis-block"))
|
||||
viper.BindPFlag("bitcoin.networkID", resyncCmd.PersistentFlags().Lookup("btc-network-id"))
|
||||
|
||||
viper.BindPFlag("ethereum.httpPath", resyncCmd.PersistentFlags().Lookup("eth-http-path"))
|
||||
viper.BindPFlag("ethereum.nodeID", resyncCmd.PersistentFlags().Lookup("eth-node-id"))
|
||||
viper.BindPFlag("ethereum.clientName", resyncCmd.PersistentFlags().Lookup("eth-client-name"))
|
||||
viper.BindPFlag("ethereum.genesisBlock", resyncCmd.PersistentFlags().Lookup("eth-genesis-block"))
|
||||
viper.BindPFlag("ethereum.networkID", resyncCmd.PersistentFlags().Lookup("eth-network-id"))
|
||||
}
|
||||
+62
-45
@@ -19,44 +19,39 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/config"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/prom"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
databaseConfig config.Database
|
||||
ipc string
|
||||
envFile string
|
||||
subCommand string
|
||||
logWithCommand log.Entry
|
||||
)
|
||||
|
||||
const (
|
||||
pollingInterval = 7 * time.Second
|
||||
validationWindow = 15
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "vulcanizedb",
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func initFuncs(cmd *cobra.Command, args []string) {
|
||||
setViperConfigs()
|
||||
logfile := viper.GetString("logfile")
|
||||
viper.BindEnv("log.file", "LOGRUS_FILE")
|
||||
logfile := viper.GetString("log.file")
|
||||
if logfile != "" {
|
||||
file, err := os.OpenFile(logfile,
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
@@ -73,21 +68,23 @@ func initFuncs(cmd *cobra.Command, args []string) {
|
||||
if err := logLevel(); err != nil {
|
||||
log.Fatal("Could not set log level: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func setViperConfigs() {
|
||||
ipc = viper.GetString("client.ipcpath")
|
||||
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"),
|
||||
if viper.GetBool("metrics") {
|
||||
prom.Init()
|
||||
}
|
||||
|
||||
if viper.GetBool("prom.http") {
|
||||
addr := fmt.Sprintf(
|
||||
"%s:%s",
|
||||
viper.GetString("prom.http.addr"),
|
||||
viper.GetString("prom.http.port"),
|
||||
)
|
||||
prom.Serve(addr)
|
||||
}
|
||||
viper.Set("database.config", databaseConfig)
|
||||
}
|
||||
|
||||
func logLevel() error {
|
||||
viper.BindEnv("log.level", "LOGRUS_LEVEL")
|
||||
lvl, err := log.ParseLevel(viper.GetString("log.level"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -102,39 +99,59 @@ func logLevel() error {
|
||||
|
||||
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().StringVar(&envFile, "env", "", "environment file location")
|
||||
|
||||
rootCmd.PersistentFlags().String("client-ipcPath", "", "location of geth.ipc file")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
|
||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "log level (trace, debug, info, warn, error, fatal, panic)")
|
||||
rootCmd.PersistentFlags().String("log-file", "", "file path for logging")
|
||||
|
||||
rootCmd.PersistentFlags().Bool("metrics", false, "enable metrics")
|
||||
|
||||
rootCmd.PersistentFlags().Bool("prom-http", false, "enable http service for prometheus")
|
||||
rootCmd.PersistentFlags().String("prom-http-addr", "127.0.0.1", "http host for prometheus")
|
||||
rootCmd.PersistentFlags().String("prom-http-port", "8090", "http port for prometheus")
|
||||
|
||||
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("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
|
||||
viper.BindPFlag("log.file", rootCmd.PersistentFlags().Lookup("log-file"))
|
||||
|
||||
viper.BindPFlag("metrics", rootCmd.PersistentFlags().Lookup("metrics"))
|
||||
|
||||
viper.BindPFlag("prom.http", rootCmd.PersistentFlags().Lookup("prom-http"))
|
||||
viper.BindPFlag("prom.http.addr", rootCmd.PersistentFlags().Lookup("prom-http-addr"))
|
||||
viper.BindPFlag("prom.http.port", rootCmd.PersistentFlags().Lookup("prom-http-port"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if cfgFile == "" && envFile == "" {
|
||||
log.Fatal("No configuration file specified, use --config , --env flag to provide configuration")
|
||||
}
|
||||
|
||||
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()))
|
||||
if filepath.Ext(cfgFile) != ".toml" {
|
||||
log.Fatal("Provide .toml file for --config flag")
|
||||
}
|
||||
} else {
|
||||
log.Warn("No config file passed with --config flag")
|
||||
|
||||
viper.SetConfigFile(cfgFile)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
log.Fatalf("Couldn't read config file: %s", err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Using config file: %s", viper.ConfigFileUsed())
|
||||
}
|
||||
|
||||
if envFile != "" {
|
||||
if filepath.Ext(envFile) != ".env" {
|
||||
log.Fatal("Provide .env file for --env flag")
|
||||
}
|
||||
|
||||
if err := godotenv.Load(envFile); err != nil {
|
||||
log.Fatalf("Failed to set environment variable from env file: %s", err.Error())
|
||||
}
|
||||
|
||||
log.Infof("Using env file: %s", envFile)
|
||||
}
|
||||
}
|
||||
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/mailgun/groupcache/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/vulcanize/gap-filler/pkg/mux"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/graphql"
|
||||
srpc "github.com/vulcanize/ipld-eth-server/v4/pkg/rpc"
|
||||
s "github.com/vulcanize/ipld-eth-server/v4/pkg/serve"
|
||||
v "github.com/vulcanize/ipld-eth-server/v4/version"
|
||||
)
|
||||
|
||||
var ErrNoRpcEndpoints = errors.New("no rpc endpoints is available")
|
||||
|
||||
// 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)
|
||||
}
|
||||
graphQL, err := startEthGraphQL(server, serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
err = startIpldGraphQL(serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
err = startGroupCacheService(serverConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
if serverConfig.StateValidationEnabled {
|
||||
go startStateTrieValidator(serverConfig, server)
|
||||
logWithCommand.Info("state validator enabled")
|
||||
} else {
|
||||
logWithCommand.Info("state validator disabled")
|
||||
}
|
||||
|
||||
shutdown := make(chan os.Signal, 1)
|
||||
signal.Notify(shutdown, os.Interrupt)
|
||||
<-shutdown
|
||||
if graphQL != nil {
|
||||
graphQL.Stop()
|
||||
}
|
||||
server.Stop()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func startServers(server s.Server, settings *s.Config) error {
|
||||
if settings.IPCEnabled {
|
||||
logWithCommand.Info("starting up IPC server")
|
||||
_, _, err := srpc.StartIPCEndpoint(settings.IPCEndpoint, server.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("IPC server is disabled")
|
||||
}
|
||||
|
||||
if settings.WSEnabled {
|
||||
logWithCommand.Info("starting up WS server")
|
||||
_, _, err := srpc.StartWSEndpoint(settings.WSEndpoint, server.APIs(), []string{"vdb", "net"}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("WS server is disabled")
|
||||
}
|
||||
|
||||
if settings.HTTPEnabled {
|
||||
logWithCommand.Info("starting up HTTP server")
|
||||
_, err := srpc.StartHTTPEndpoint(settings.HTTPEndpoint, server.APIs(), []string{"vdb", "eth", "net"}, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("HTTP server is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startEthGraphQL(server s.Server, settings *s.Config) (graphQLServer *graphql.Service, err error) {
|
||||
if settings.EthGraphqlEnabled {
|
||||
logWithCommand.Info("starting up ETH GraphQL server")
|
||||
endPoint := settings.EthGraphqlEndpoint
|
||||
if endPoint != "" {
|
||||
graphQLServer, err = graphql.New(server.Backend(), endPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = graphQLServer.Start(nil)
|
||||
}
|
||||
} else {
|
||||
logWithCommand.Info("ETH GraphQL server is disabled")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func startIpldGraphQL(settings *s.Config) error {
|
||||
if settings.IpldGraphqlEnabled {
|
||||
logWithCommand.Info("starting up IPLD GraphQL server")
|
||||
|
||||
gqlIpldAddr, err := url.Parse(settings.IpldPostgraphileEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gqlTracingAPIAddr, err := url.Parse(settings.TracingPostgraphileEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ethClients, err := parseRpcAddresses(settings.EthHttpEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tracingClients []*rpc.Client
|
||||
tracingEndpoint := viper.GetString("tracing.httpPath")
|
||||
if tracingEndpoint != "" {
|
||||
tracingClients, err = parseRpcAddresses(tracingEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
router, err := mux.NewServeMux(&mux.Options{
|
||||
BasePath: "/",
|
||||
EnableGraphiQL: true,
|
||||
Postgraphile: mux.PostgraphileOptions{
|
||||
Default: gqlIpldAddr,
|
||||
TracingAPI: gqlTracingAPIAddr,
|
||||
},
|
||||
RPC: mux.RPCOptions{
|
||||
DefaultClients: ethClients,
|
||||
TracingClients: tracingClients,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go http.ListenAndServe(settings.IpldGraphqlEndpoint, router)
|
||||
} else {
|
||||
logWithCommand.Info("IPLD GraphQL server is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startGroupCacheService(settings *s.Config) error {
|
||||
gcc := settings.GroupCache
|
||||
|
||||
if gcc.Pool.Enabled {
|
||||
logWithCommand.Info("starting up groupcache pool HTTTP server")
|
||||
|
||||
pool := groupcache.NewHTTPPoolOpts(gcc.Pool.HttpEndpoint, &groupcache.HTTPPoolOptions{})
|
||||
pool.Set(gcc.Pool.PeerHttpEndpoints...)
|
||||
|
||||
httpURL, err := url.Parse(gcc.Pool.HttpEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
server := http.Server{
|
||||
Addr: httpURL.Host,
|
||||
Handler: pool,
|
||||
}
|
||||
|
||||
// Start a HTTP server to listen for peer requests from the groupcache
|
||||
go server.ListenAndServe()
|
||||
|
||||
logWithCommand.Infof("groupcache pool endpoint opened for url %s", httpURL)
|
||||
} else {
|
||||
logWithCommand.Info("Groupcache pool is disabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func startStateTrieValidator(config *s.Config, server s.Server) {
|
||||
validateEveryNthBlock := config.StateValidationEveryNthBlock
|
||||
|
||||
var lastBlockNumber uint64
|
||||
backend := server.Backend()
|
||||
|
||||
for {
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
block, err := backend.CurrentBlock()
|
||||
if err != nil {
|
||||
log.Errorln("Error fetching current block for state trie validator")
|
||||
continue
|
||||
}
|
||||
|
||||
stateRoot := block.Root()
|
||||
blockNumber := block.NumberU64()
|
||||
blockHash := block.Hash()
|
||||
|
||||
if validateEveryNthBlock <= 0 || // Used for static replicas where block number doesn't progress.
|
||||
(blockNumber > lastBlockNumber) && (blockNumber%validateEveryNthBlock == 0) {
|
||||
|
||||
// The validate trie call will take a long time on mainnet, e.g. a few hours.
|
||||
if err = backend.ValidateTrie(stateRoot); err != nil {
|
||||
log.Fatalf("Error validating trie for block number %d hash %s state root %s",
|
||||
blockNumber,
|
||||
blockHash,
|
||||
stateRoot,
|
||||
)
|
||||
}
|
||||
|
||||
log.Infof("Successfully validated trie for block number %d hash %s state root %s",
|
||||
blockNumber,
|
||||
blockHash,
|
||||
stateRoot,
|
||||
)
|
||||
|
||||
if validateEveryNthBlock <= 0 {
|
||||
// Static replica, sleep a long-ish time (1/2 of cache expiry time) since we only need to keep the cache warm.
|
||||
time.Sleep((time.Minute * time.Duration(config.GroupCache.StateDB.CacheExpiryInMins)) / 2)
|
||||
}
|
||||
|
||||
lastBlockNumber = blockNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseRpcAddresses(value string) ([]*rpc.Client, error) {
|
||||
rpcAddresses := strings.Split(value, ",")
|
||||
rpcClients := make([]*rpc.Client, 0, len(rpcAddresses))
|
||||
for _, address := range rpcAddresses {
|
||||
rpcClient, err := rpc.Dial(address)
|
||||
if err != nil {
|
||||
logWithCommand.Errorf("couldn't connect to %s. Error: %s", address, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rpcClients = append(rpcClients, rpcClient)
|
||||
}
|
||||
|
||||
if len(rpcClients) == 0 {
|
||||
logWithCommand.Error(ErrNoRpcEndpoints)
|
||||
return nil, ErrNoRpcEndpoints
|
||||
}
|
||||
|
||||
return rpcClients, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serveCmd)
|
||||
|
||||
addDatabaseFlags(serveCmd)
|
||||
|
||||
// flags for all config variables
|
||||
// eth graphql and json-rpc parameters
|
||||
serveCmd.PersistentFlags().Bool("eth-server-graphql", false, "turn on the eth graphql server")
|
||||
serveCmd.PersistentFlags().String("eth-server-graphql-path", "", "endpoint url for eth graphql server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-http", true, "turn on the eth http json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-http-path", "", "endpoint url for eth http json-rpc server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-ws", false, "turn on the eth websocket json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-ws-path", "", "endpoint url for eth websocket json-rpc server (host:port)")
|
||||
serveCmd.PersistentFlags().Bool("eth-server-ipc", false, "turn on the eth ipc json-rpc server")
|
||||
serveCmd.PersistentFlags().String("eth-server-ipc-path", "", "path for eth ipc json-rpc server")
|
||||
|
||||
// ipld and tracing graphql parameters
|
||||
serveCmd.PersistentFlags().Bool("ipld-server-graphql", false, "turn on the ipld graphql server")
|
||||
serveCmd.PersistentFlags().String("ipld-server-graphql-path", "", "endpoint url for ipld graphql server (host:port)")
|
||||
serveCmd.PersistentFlags().String("ipld-postgraphile-path", "", "http url to postgraphile on top of ipld database")
|
||||
serveCmd.PersistentFlags().String("tracing-http-path", "", "http url to tracing service")
|
||||
serveCmd.PersistentFlags().String("tracing-postgraphile-path", "", "http url to postgraphile on top of tracing db")
|
||||
|
||||
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", "Geth", "eth client name")
|
||||
serveCmd.PersistentFlags().String("eth-genesis-block", "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "eth genesis block hash")
|
||||
serveCmd.PersistentFlags().String("eth-network-id", "1", "eth network id")
|
||||
serveCmd.PersistentFlags().String("eth-chain-id", "1", "eth chain id")
|
||||
serveCmd.PersistentFlags().String("eth-default-sender", "", "default sender address")
|
||||
serveCmd.PersistentFlags().String("eth-rpc-gas-cap", "", "rpc gas cap (for eth_Call execution)")
|
||||
serveCmd.PersistentFlags().String("eth-chain-config", "", "json chain config file location")
|
||||
serveCmd.PersistentFlags().Bool("eth-supports-state-diff", false, "whether the proxy ethereum client supports statediffing endpoints")
|
||||
serveCmd.PersistentFlags().Bool("eth-forward-eth-calls", false, "whether to immediately forward eth_calls to proxy client")
|
||||
serveCmd.PersistentFlags().Bool("eth-proxy-on-error", true, "whether to forward all failed calls to proxy client")
|
||||
|
||||
// groupcache flags
|
||||
serveCmd.PersistentFlags().Bool("gcache-pool-enabled", false, "turn on the groupcache pool")
|
||||
serveCmd.PersistentFlags().String("gcache-pool-http-path", "", "http url for groupcache node")
|
||||
serveCmd.PersistentFlags().StringArray("gcache-pool-http-peers", []string{}, "http urls for groupcache peers")
|
||||
serveCmd.PersistentFlags().Int("gcache-statedb-cache-size", 16, "state DB cache size in MB")
|
||||
serveCmd.PersistentFlags().Int("gcache-statedb-cache-expiry", 60, "state DB cache expiry time in mins")
|
||||
serveCmd.PersistentFlags().Int("gcache-statedb-log-stats-interval", 60, "state DB cache stats log interval in secs")
|
||||
|
||||
// state validator flags
|
||||
serveCmd.PersistentFlags().Bool("validator-enabled", false, "turn on the state validator")
|
||||
serveCmd.PersistentFlags().Uint("validator-every-nth-block", 1500, "only validate every Nth block")
|
||||
|
||||
// and their bindings
|
||||
// eth graphql server
|
||||
viper.BindPFlag("eth.server.graphql", serveCmd.PersistentFlags().Lookup("eth-server-graphql"))
|
||||
viper.BindPFlag("eth.server.graphqlPath", serveCmd.PersistentFlags().Lookup("eth-server-graphql-path"))
|
||||
|
||||
// eth http json-rpc server
|
||||
viper.BindPFlag("eth.server.http", serveCmd.PersistentFlags().Lookup("eth-server-http"))
|
||||
viper.BindPFlag("eth.server.httpPath", serveCmd.PersistentFlags().Lookup("eth-server-http-path"))
|
||||
|
||||
// eth websocket json-rpc server
|
||||
viper.BindPFlag("eth.server.ws", serveCmd.PersistentFlags().Lookup("eth-server-ws"))
|
||||
viper.BindPFlag("eth.server.wsPath", serveCmd.PersistentFlags().Lookup("eth-server-ws-path"))
|
||||
|
||||
// eth ipc json-rpc server
|
||||
viper.BindPFlag("eth.server.ipc", serveCmd.PersistentFlags().Lookup("eth-server-ipc"))
|
||||
viper.BindPFlag("eth.server.ipcPath", serveCmd.PersistentFlags().Lookup("eth-server-ipc-path"))
|
||||
|
||||
// ipld and tracing graphql parameters
|
||||
viper.BindPFlag("ipld.server.graphql", serveCmd.PersistentFlags().Lookup("ipld-server-graphql"))
|
||||
viper.BindPFlag("ipld.server.graphqlPath", serveCmd.PersistentFlags().Lookup("ipld-server-graphql-path"))
|
||||
viper.BindPFlag("ipld.postgraphilePath", serveCmd.PersistentFlags().Lookup("ipld-postgraphile-path"))
|
||||
viper.BindPFlag("tracing.httpPath", serveCmd.PersistentFlags().Lookup("tracing-http-path"))
|
||||
viper.BindPFlag("tracing.postgraphilePath", serveCmd.PersistentFlags().Lookup("tracing-postgraphile-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"))
|
||||
viper.BindPFlag("ethereum.chainID", serveCmd.PersistentFlags().Lookup("eth-chain-id"))
|
||||
viper.BindPFlag("ethereum.defaultSender", serveCmd.PersistentFlags().Lookup("eth-default-sender"))
|
||||
viper.BindPFlag("ethereum.rpcGasCap", serveCmd.PersistentFlags().Lookup("eth-rpc-gas-cap"))
|
||||
viper.BindPFlag("ethereum.chainConfig", serveCmd.PersistentFlags().Lookup("eth-chain-config"))
|
||||
viper.BindPFlag("ethereum.supportsStateDiff", serveCmd.PersistentFlags().Lookup("eth-supports-state-diff"))
|
||||
viper.BindPFlag("ethereum.forwardEthCalls", serveCmd.PersistentFlags().Lookup("eth-forward-eth-calls"))
|
||||
viper.BindPFlag("ethereum.proxyOnError", serveCmd.PersistentFlags().Lookup("eth-proxy-on-error"))
|
||||
|
||||
// groupcache flags
|
||||
viper.BindPFlag("groupcache.pool.enabled", serveCmd.PersistentFlags().Lookup("gcache-pool-enabled"))
|
||||
viper.BindPFlag("groupcache.pool.httpEndpoint", serveCmd.PersistentFlags().Lookup("gcache-pool-http-path"))
|
||||
viper.BindPFlag("groupcache.pool.peerHttpEndpoints", serveCmd.PersistentFlags().Lookup("gcache-pool-http-peers"))
|
||||
viper.BindPFlag("groupcache.statedb.cacheSizeInMB", serveCmd.PersistentFlags().Lookup("gcache-statedb-cache-size"))
|
||||
viper.BindPFlag("groupcache.statedb.cacheExpiryInMins", serveCmd.PersistentFlags().Lookup("gcache-statedb-cache-expiry"))
|
||||
viper.BindPFlag("groupcache.statedb.logStatsIntervalInSecs", serveCmd.PersistentFlags().Lookup("gcache-statedb-log-stats-interval"))
|
||||
|
||||
// state validator flags
|
||||
viper.BindPFlag("validator.enabled", serveCmd.PersistentFlags().Lookup("validator-enabled"))
|
||||
viper.BindPFlag("validator.everyNthBlock", serveCmd.PersistentFlags().Lookup("validator-every-nth-block"))
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"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"
|
||||
@@ -28,31 +27,29 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/client"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/core"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/eth"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/streamer"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/watcher"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||
w "github.com/vulcanize/ipld-eth-server/v4/pkg/serve"
|
||||
)
|
||||
|
||||
// streamEthSubscriptionCmd represents the streamEthSubscription command
|
||||
var streamEthSubscriptionCmd = &cobra.Command{
|
||||
Use: "streamEthSubscription",
|
||||
Short: "This command is used to subscribe to the super node eth stream with the provided filters",
|
||||
Long: `This command is for demo and testing purposes and is used to subscribe to the super node with the provided subscription configuration parameters.
|
||||
It does not do anything with the data streamed from the super node other than unpack it and print it out for demonstration purposes.`,
|
||||
// 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)
|
||||
streamEthSubscription()
|
||||
subscribe()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(streamEthSubscriptionCmd)
|
||||
rootCmd.AddCommand(subscribeCmd)
|
||||
}
|
||||
|
||||
func streamEthSubscription() {
|
||||
func subscribe() {
|
||||
// Prep the subscription config/filters to be sent to the server
|
||||
ethSubConfig, err := eth.NewEthSubscriptionConfig()
|
||||
if err != nil {
|
||||
@@ -60,18 +57,17 @@ func streamEthSubscription() {
|
||||
}
|
||||
|
||||
// Create a new rpc client and a subscription streamer with that client
|
||||
rpcClient := getRPCClient()
|
||||
str := streamer.NewSuperNodeStreamer(rpcClient)
|
||||
|
||||
// Buffered channel for reading subscription payloads
|
||||
payloadChan := make(chan watcher.SubscriptionPayload, 20000)
|
||||
|
||||
// Subscribe to the super node service with the given config/filter parameters
|
||||
rlpParams, err := rlp.EncodeToBytes(ethSubConfig)
|
||||
rpcClient, err := getRPCClient()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
sub, err := str.Stream(payloadChan, rlpParams)
|
||||
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)
|
||||
}
|
||||
@@ -132,7 +128,7 @@ func streamEthSubscription() {
|
||||
}
|
||||
// This assumes leafs only
|
||||
for _, stateNode := range ethData.StateNodes {
|
||||
var acct state.Account
|
||||
var acct types.StateAccount
|
||||
err = rlp.DecodeBytes(stateNode.IPLD.Data, &acct)
|
||||
if err != nil {
|
||||
logWithCommand.Error(err)
|
||||
@@ -167,14 +163,10 @@ func streamEthSubscription() {
|
||||
}
|
||||
}
|
||||
|
||||
func getRPCClient() core.RPCClient {
|
||||
vulcPath := viper.GetString("superNode.ethSubscription.wsPath")
|
||||
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
|
||||
}
|
||||
rawRPCClient, err := rpc.Dial(vulcPath)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
return client.NewRPCClient(rawRPCClient, vulcPath)
|
||||
return rpc.Dial(vulcPath)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright © 2021 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 (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
validator "github.com/vulcanize/eth-ipfs-state-validator/v4/pkg"
|
||||
ipfsethdb "github.com/vulcanize/ipfs-ethdb/v4/postgres"
|
||||
|
||||
s "github.com/vulcanize/ipld-eth-server/v4/pkg/serve"
|
||||
)
|
||||
|
||||
const GroupName = "statedb-validate"
|
||||
const CacheExpiryInMins = 8 * 60 // 8 hours
|
||||
const CacheSizeInMB = 16 // 16 MB
|
||||
|
||||
var validateCmd = &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "valdiate state",
|
||||
Long: `This command validates the trie for the given state root`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
validate()
|
||||
},
|
||||
}
|
||||
|
||||
func validate() {
|
||||
config, err := s.NewConfig()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
|
||||
stateRootStr := viper.GetString("stateRoot")
|
||||
if stateRootStr == "" {
|
||||
logWithCommand.Fatal("must provide a state root for state validation")
|
||||
}
|
||||
|
||||
stateRoot := common.HexToHash(stateRootStr)
|
||||
cacheSize := viper.GetInt("cacheSize")
|
||||
|
||||
ethDB := ipfsethdb.NewDatabase(config.DB, ipfsethdb.CacheConfig{
|
||||
Name: GroupName,
|
||||
Size: cacheSize * 1024 * 1024,
|
||||
ExpiryDuration: time.Minute * time.Duration(CacheExpiryInMins),
|
||||
})
|
||||
|
||||
val := validator.NewValidator(nil, ethDB)
|
||||
if err = val.ValidateTrie(stateRoot); err != nil {
|
||||
log.Fatalln("Error validating state root")
|
||||
}
|
||||
|
||||
stats := ethDB.(*ipfsethdb.Database).GetCacheStats()
|
||||
log.Debugf("groupcache stats %+v", stats)
|
||||
|
||||
log.Infoln("Successfully validated state root")
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(validateCmd)
|
||||
|
||||
addDatabaseFlags(validateCmd)
|
||||
|
||||
validateCmd.PersistentFlags().String("state-root", "", "root of the state trie we wish to validate")
|
||||
viper.BindPFlag("stateRoot", validateCmd.PersistentFlags().Lookup("state-root"))
|
||||
|
||||
validateCmd.PersistentFlags().Int("cache-size", CacheSizeInMB, "cache size in MB")
|
||||
viper.BindPFlag("cacheSize", validateCmd.PersistentFlags().Lookup("cache-size"))
|
||||
}
|
||||
+5
-5
@@ -19,20 +19,20 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
v "github.com/vulcanize/ipfs-blockchain-watcher/version"
|
||||
v "github.com/vulcanize/ipld-eth-server/v4/version"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints the version of ipfs-blockchain-watcher",
|
||||
Long: `Use this command to fetch the version of ipfs-blockchain-watcher
|
||||
Short: "Prints the version of ipld-eth-server",
|
||||
Long: `Use this command to fetch the version of ipld-eth-server
|
||||
|
||||
Usage: ./ipfs-blockchain-watcher version`,
|
||||
Usage: ./ipld-eth-server version`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
logWithCommand.Infof("ipfs-blockchain-watcher version: %s", v.VersionWithMeta)
|
||||
logWithCommand.Infof("ipld-eth-server version: %s", v.VersionWithMeta)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
// 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/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/watcher"
|
||||
v "github.com/vulcanize/ipfs-blockchain-watcher/version"
|
||||
)
|
||||
|
||||
// superNodeCmd represents the superNode command
|
||||
var superNodeCmd = &cobra.Command{
|
||||
Use: "superNode",
|
||||
Short: "sync chain data into PG-IPFS",
|
||||
Long: `This command configures a VulcanizeDB ipfs-blockchain-watcher.
|
||||
|
||||
The Sync process streams all chain data from the appropriate chain, processes this data into IPLD objects
|
||||
and publishes them to IPFS. It then indexes the CIDs against useful data fields/metadata in Postgres.
|
||||
|
||||
The Serve process creates and exposes a rpc subscription server over ws and ipc. Transformers can subscribe to
|
||||
these endpoints to stream
|
||||
|
||||
The BackFill process spins up a background process which periodically probes the Postgres database to identify
|
||||
and fill in gaps in the data
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
subCommand = cmd.CalledAs()
|
||||
logWithCommand = *log.WithField("SubCommand", subCommand)
|
||||
superNode()
|
||||
},
|
||||
}
|
||||
|
||||
func superNode() {
|
||||
logWithCommand.Infof("running vdb version: %s", v.VersionWithMeta)
|
||||
logWithCommand.Debug("loading super node configuration variables")
|
||||
superNodeConfig, err := watcher.NewSuperNodeConfig()
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Infof("super node config: %+v", superNodeConfig)
|
||||
if superNodeConfig.IPFSMode == shared.LocalInterface {
|
||||
if err := ipfs.InitIPFSPlugins(); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
wg := &sync.WaitGroup{}
|
||||
logWithCommand.Debug("initializing new super node service")
|
||||
superNode, err := watcher.NewSuperNode(superNodeConfig)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
var forwardPayloadChan chan shared.ConvertedData
|
||||
if superNodeConfig.Serve {
|
||||
logWithCommand.Info("starting up super node servers")
|
||||
forwardPayloadChan = make(chan shared.ConvertedData, watcher.PayloadChanBufferSize)
|
||||
superNode.Serve(wg, forwardPayloadChan)
|
||||
if err := startServers(superNode, superNodeConfig); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
if superNodeConfig.Sync {
|
||||
logWithCommand.Info("starting up super node sync process")
|
||||
if err := superNode.Sync(wg, forwardPayloadChan); err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
}
|
||||
var backFiller watcher.BackFillInterface
|
||||
if superNodeConfig.BackFill {
|
||||
logWithCommand.Debug("initializing new super node backfill service")
|
||||
backFiller, err = watcher.NewBackFillService(superNodeConfig, forwardPayloadChan)
|
||||
if err != nil {
|
||||
logWithCommand.Fatal(err)
|
||||
}
|
||||
logWithCommand.Info("starting up super node backfill process")
|
||||
backFiller.BackFill(wg)
|
||||
}
|
||||
shutdown := make(chan os.Signal)
|
||||
signal.Notify(shutdown, os.Interrupt)
|
||||
<-shutdown
|
||||
if superNodeConfig.BackFill {
|
||||
backFiller.Stop()
|
||||
}
|
||||
superNode.Stop()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func startServers(superNode watcher.SuperNode, settings *watcher.Config) error {
|
||||
logWithCommand.Debug("starting up IPC server")
|
||||
_, _, err := rpc.StartIPCEndpoint(settings.IPCEndpoint, superNode.APIs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logWithCommand.Debug("starting up WS server")
|
||||
_, _, err = rpc.StartWSEndpoint(settings.WSEndpoint, superNode.APIs(), []string{"vdb"}, nil, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logWithCommand.Debug("starting up HTTP server")
|
||||
_, _, err = rpc.StartHTTPEndpoint(settings.HTTPEndpoint, superNode.APIs(), []string{settings.Chain.API()}, nil, nil, rpc.HTTPTimeouts{})
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(superNodeCmd)
|
||||
|
||||
// flags for all config variables
|
||||
superNodeCmd.PersistentFlags().String("ipfs-path", "", "ipfs repository path")
|
||||
|
||||
superNodeCmd.PersistentFlags().String("supernode-chain", "", "which chain to support, options are currently Ethereum or Bitcoin.")
|
||||
superNodeCmd.PersistentFlags().Bool("supernode-server", false, "turn vdb server on or off")
|
||||
superNodeCmd.PersistentFlags().String("supernode-ws-path", "", "vdb server ws path")
|
||||
superNodeCmd.PersistentFlags().String("supernode-http-path", "", "vdb server http path")
|
||||
superNodeCmd.PersistentFlags().String("supernode-ipc-path", "", "vdb server ipc path")
|
||||
superNodeCmd.PersistentFlags().Bool("supernode-sync", false, "turn vdb sync on or off")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-workers", 0, "how many worker goroutines to publish and index data")
|
||||
superNodeCmd.PersistentFlags().Bool("supernode-back-fill", false, "turn vdb backfill on or off")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-frequency", 0, "how often (in seconds) the backfill process checks for gaps")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-batch-size", 0, "data fetching batch size")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-batch-number", 0, "how many goroutines to fetch data concurrently")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-validation-level", 0, "backfill will resync any data below this level")
|
||||
superNodeCmd.PersistentFlags().Int("supernode-timeout", 0, "timeout used for backfill http requests")
|
||||
|
||||
superNodeCmd.PersistentFlags().String("btc-ws-path", "", "ws url for bitcoin node")
|
||||
superNodeCmd.PersistentFlags().String("btc-http-path", "", "http url for bitcoin node")
|
||||
superNodeCmd.PersistentFlags().String("btc-password", "", "password for btc node")
|
||||
superNodeCmd.PersistentFlags().String("btc-username", "", "username for btc node")
|
||||
superNodeCmd.PersistentFlags().String("btc-node-id", "", "btc node id")
|
||||
superNodeCmd.PersistentFlags().String("btc-client-name", "", "btc client name")
|
||||
superNodeCmd.PersistentFlags().String("btc-genesis-block", "", "btc genesis block hash")
|
||||
superNodeCmd.PersistentFlags().String("btc-network-id", "", "btc network id")
|
||||
|
||||
superNodeCmd.PersistentFlags().String("eth-ws-path", "", "ws url for ethereum node")
|
||||
superNodeCmd.PersistentFlags().String("eth-http-path", "", "http url for ethereum node")
|
||||
superNodeCmd.PersistentFlags().String("eth-node-id", "", "eth node id")
|
||||
superNodeCmd.PersistentFlags().String("eth-client-name", "", "eth client name")
|
||||
superNodeCmd.PersistentFlags().String("eth-genesis-block", "", "eth genesis block hash")
|
||||
superNodeCmd.PersistentFlags().String("eth-network-id", "", "eth network id")
|
||||
|
||||
// and their bindings
|
||||
viper.BindPFlag("ipfs.path", superNodeCmd.PersistentFlags().Lookup("ipfs-path"))
|
||||
|
||||
viper.BindPFlag("superNode.chain", superNodeCmd.PersistentFlags().Lookup("supernode-chain"))
|
||||
viper.BindPFlag("superNode.server", superNodeCmd.PersistentFlags().Lookup("supernode-server"))
|
||||
viper.BindPFlag("superNode.wsPath", superNodeCmd.PersistentFlags().Lookup("supernode-ws-path"))
|
||||
viper.BindPFlag("superNode.httpPath", superNodeCmd.PersistentFlags().Lookup("supernode-http-path"))
|
||||
viper.BindPFlag("superNode.ipcPath", superNodeCmd.PersistentFlags().Lookup("supernode-ipc-path"))
|
||||
viper.BindPFlag("superNode.sync", superNodeCmd.PersistentFlags().Lookup("supernode-sync"))
|
||||
viper.BindPFlag("superNode.workers", superNodeCmd.PersistentFlags().Lookup("supernode-workers"))
|
||||
viper.BindPFlag("superNode.backFill", superNodeCmd.PersistentFlags().Lookup("supernode-back-fill"))
|
||||
viper.BindPFlag("superNode.frequency", superNodeCmd.PersistentFlags().Lookup("supernode-frequency"))
|
||||
viper.BindPFlag("superNode.batchSize", superNodeCmd.PersistentFlags().Lookup("supernode-batch-size"))
|
||||
viper.BindPFlag("superNode.batchNumber", superNodeCmd.PersistentFlags().Lookup("supernode-batch-number"))
|
||||
viper.BindPFlag("superNode.validationLevel", superNodeCmd.PersistentFlags().Lookup("supernode-validation-level"))
|
||||
viper.BindPFlag("superNode.timeout", superNodeCmd.PersistentFlags().Lookup("supernode-timeout"))
|
||||
|
||||
viper.BindPFlag("bitcoin.wsPath", superNodeCmd.PersistentFlags().Lookup("btc-ws-path"))
|
||||
viper.BindPFlag("bitcoin.httpPath", superNodeCmd.PersistentFlags().Lookup("btc-http-path"))
|
||||
viper.BindPFlag("bitcoin.pass", superNodeCmd.PersistentFlags().Lookup("btc-password"))
|
||||
viper.BindPFlag("bitcoin.user", superNodeCmd.PersistentFlags().Lookup("btc-username"))
|
||||
viper.BindPFlag("bitcoin.nodeID", superNodeCmd.PersistentFlags().Lookup("btc-node-id"))
|
||||
viper.BindPFlag("bitcoin.clientName", superNodeCmd.PersistentFlags().Lookup("btc-client-name"))
|
||||
viper.BindPFlag("bitcoin.genesisBlock", superNodeCmd.PersistentFlags().Lookup("btc-genesis-block"))
|
||||
viper.BindPFlag("bitcoin.networkID", superNodeCmd.PersistentFlags().Lookup("btc-network-id"))
|
||||
|
||||
viper.BindPFlag("ethereum.wsPath", superNodeCmd.PersistentFlags().Lookup("eth-ws-path"))
|
||||
viper.BindPFlag("ethereum.httpPath", superNodeCmd.PersistentFlags().Lookup("eth-http-path"))
|
||||
viper.BindPFlag("ethereum.nodeID", superNodeCmd.PersistentFlags().Lookup("eth-node-id"))
|
||||
viper.BindPFlag("ethereum.clientName", superNodeCmd.PersistentFlags().Lookup("eth-client-name"))
|
||||
viper.BindPFlag("ethereum.genesisBlock", superNodeCmd.PersistentFlags().Lookup("eth-genesis-block"))
|
||||
viper.BindPFlag("ethereum.networkID", superNodeCmd.PersistentFlags().Lookup("eth-network-id"))
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE nodes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_name VARCHAR,
|
||||
genesis_block VARCHAR(66),
|
||||
network_id VARCHAR,
|
||||
node_id VARCHAR(128),
|
||||
CONSTRAINT node_uc UNIQUE (genesis_block, network_id, node_id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE nodes;
|
||||
@@ -1,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE SCHEMA eth;
|
||||
|
||||
-- +goose Down
|
||||
DROP SCHEMA eth;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- +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,
|
||||
td NUMERIC NOT NULL,
|
||||
node_id INTEGER NOT NULL REFERENCES nodes (id) ON DELETE CASCADE,
|
||||
reward NUMERIC NOT NULL,
|
||||
UNIQUE (block_number, block_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.header_cids;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- +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,
|
||||
reward NUMERIC NOT NULL,
|
||||
UNIQUE (header_id, block_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.uncle_cids;
|
||||
@@ -1,14 +0,0 @@
|
||||
-- +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,
|
||||
dst VARCHAR(66) NOT NULL,
|
||||
src VARCHAR(66) NOT NULL,
|
||||
UNIQUE (header_id, tx_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.transaction_cids;
|
||||
@@ -1,14 +0,0 @@
|
||||
-- +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,
|
||||
contract VARCHAR(66),
|
||||
topic0s VARCHAR(66)[],
|
||||
topic1s VARCHAR(66)[],
|
||||
topic2s VARCHAR(66)[],
|
||||
topic3s VARCHAR(66)[]
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.receipt_cids;
|
||||
@@ -1,12 +0,0 @@
|
||||
-- +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_key VARCHAR(66) NOT NULL,
|
||||
leaf BOOLEAN NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
UNIQUE (header_id, state_key)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.state_cids;
|
||||
@@ -1,12 +0,0 @@
|
||||
-- +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_key VARCHAR(66) NOT NULL,
|
||||
leaf BOOLEAN NOT NULL,
|
||||
cid TEXT NOT NULL,
|
||||
UNIQUE (state_id, storage_key)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.storage_cids;
|
||||
@@ -1,8 +0,0 @@
|
||||
-- +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,5 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE SCHEMA btc;
|
||||
|
||||
-- +goose Down
|
||||
DROP SCHEMA btc;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE btc.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,
|
||||
timestamp NUMERIC NOT NULL,
|
||||
bits BIGINT NOT NULL,
|
||||
node_id INTEGER NOT NULL REFERENCES nodes (id) ON DELETE CASCADE,
|
||||
UNIQUE (block_number, block_hash)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE btc.header_cids;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE btc.transaction_cids (
|
||||
id SERIAL PRIMARY KEY,
|
||||
header_id INTEGER NOT NULL REFERENCES btc.header_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
index INTEGER NOT NULL,
|
||||
tx_hash VARCHAR(66) NOT NULL UNIQUE,
|
||||
cid TEXT NOT NULL,
|
||||
segwit BOOL NOT NULL,
|
||||
witness_hash VARCHAR(66)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE btc.transaction_cids;
|
||||
@@ -1,15 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE btc.tx_outputs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tx_id INTEGER NOT NULL REFERENCES btc.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
index INTEGER NOT NULL,
|
||||
value BIGINT NOT NULL,
|
||||
pk_script BYTEA NOT NULL,
|
||||
script_class INTEGER NOT NULL,
|
||||
addresses VARCHAR(66)[],
|
||||
required_sigs INTEGER NOT NULL,
|
||||
UNIQUE (tx_id, index)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE btc.tx_outputs;
|
||||
@@ -1,14 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE btc.tx_inputs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tx_id INTEGER NOT NULL REFERENCES btc.transaction_cids (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
|
||||
index INTEGER NOT NULL,
|
||||
witness VARCHAR[],
|
||||
sig_script BYTEA NOT NULL,
|
||||
outpoint_tx_hash VARCHAR(66) NOT NULL,
|
||||
outpoint_index NUMERIC NOT NULL,
|
||||
UNIQUE (tx_id, index)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE btc.tx_inputs;
|
||||
@@ -1,9 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.queue_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
data BYTEA NOT NULL,
|
||||
height BIGINT UNIQUE NOT NULL
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE eth.queue_data;
|
||||
@@ -1,9 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE btc.queue_data (
|
||||
id SERIAL PRIMARY KEY,
|
||||
data BYTEA NOT NULL,
|
||||
height BIGINT UNIQUE NOT NULL
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE btc.queue_data;
|
||||
@@ -1,11 +0,0 @@
|
||||
-- +goose Up
|
||||
COMMENT ON TABLE public.nodes IS E'@name NodeInfo';
|
||||
COMMENT ON TABLE btc.header_cids IS E'@name BtcHeaderCids';
|
||||
COMMENT ON TABLE btc.transaction_cids IS E'@name BtcTransactionCids';
|
||||
COMMENT ON TABLE btc.queue_data IS E'@name BtcQueueData';
|
||||
COMMENT ON TABLE eth.transaction_cids IS E'@name EthTransactionCids';
|
||||
COMMENT ON TABLE eth.header_cids IS E'@name EthHeaderCids';
|
||||
COMMENT ON TABLE eth.queue_data IS E'@name EthQueueData';
|
||||
COMMENT ON COLUMN public.nodes.node_id IS E'@name ChainNodeID';
|
||||
COMMENT ON COLUMN eth.header_cids.node_id IS E'@name EthNodeID';
|
||||
COMMENT ON COLUMN btc.header_cids.node_id IS E'@name BtcNodeID';
|
||||
@@ -1,37 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.state_cids
|
||||
ADD COLUMN state_path BYTEA;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
DROP COLUMN leaf;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
ADD COLUMN node_type INTEGER;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
ALTER COLUMN state_key DROP NOT NULL;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
DROP CONSTRAINT state_cids_header_id_state_key_key;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
ADD CONSTRAINT state_cids_header_id_state_path_key UNIQUE (header_id, state_path);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE eth.state_cids
|
||||
ADD CONSTRAINT state_cids_header_id_state_key_key UNIQUE (header_id, state_key);
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
DROP CONSTRAINT state_cids_header_id_state_path_key;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
ALTER COLUMN state_key SET NOT NULL;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
DROP COLUMN node_type;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
ADD COLUMN leaf BOOLEAN NOT NULL;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
DROP COLUMN state_path;
|
||||
@@ -1,37 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.storage_cids
|
||||
ADD COLUMN storage_path BYTEA;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
DROP COLUMN leaf;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ADD COLUMN node_type INTEGER;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ALTER COLUMN storage_key DROP NOT NULL;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
DROP CONSTRAINT storage_cids_state_id_storage_key_key;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ADD CONSTRAINT storage_cids_state_id_storage_path_key UNIQUE (state_id, storage_path);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE eth.storage_cids
|
||||
DROP CONSTRAINT storage_cids_state_id_storage_path_key;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ADD CONSTRAINT storage_cids_state_id_storage_key_key UNIQUE (state_id, storage_key);
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ALTER COLUMN storage_key SET NOT NULL;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
DROP COLUMN node_type;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
ADD COLUMN leaf BOOLEAN NOT NULL;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
DROP COLUMN storage_path;
|
||||
@@ -1,37 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN state_root VARCHAR(66);
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN tx_root VARCHAR(66);
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN receipt_root VARCHAR(66);
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN uncle_root VARCHAR(66);
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN bloom BYTEA;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN timestamp NUMERIC;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN timestamp;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN bloom;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN uncle_root;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN receipt_root;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN tx_root;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN state_root;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE eth.state_accounts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
state_id INTEGER NOT NULL REFERENCES eth.state_cids (id) ON DELETE CASCADE,
|
||||
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,13 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.state_cids
|
||||
RENAME COLUMN state_key TO state_leaf_key;
|
||||
|
||||
ALTER TABLE eth.storage_cids
|
||||
RENAME COLUMN storage_key TO storage_leaf_key;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE eth.storage_cids
|
||||
RENAME COLUMN storage_leaf_key TO storage_key;
|
||||
|
||||
ALTER TABLE eth.state_cids
|
||||
RENAME COLUMN state_leaf_key TO state_key;
|
||||
@@ -1,22 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.receipt_cids
|
||||
ADD COLUMN log_contracts VARCHAR(66)[];
|
||||
|
||||
ALTER TABLE eth.receipt_cids
|
||||
ADD COLUMN contract_hash VARCHAR(66);
|
||||
|
||||
WITH uniques AS (SELECT DISTINCT ON (tx_id) * FROM eth.receipt_cids)
|
||||
DELETE FROM eth.receipt_cids WHERE receipt_cids.id NOT IN (SELECT id FROM uniques);
|
||||
|
||||
ALTER TABLE eth.receipt_cids
|
||||
ADD CONSTRAINT receipt_cids_tx_id_key UNIQUE (tx_id);
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE eth.receipt_cids
|
||||
DROP CONSTRAINT receipt_cids_tx_id_key;
|
||||
|
||||
ALTER TABLE eth.receipt_cids
|
||||
DROP COLUMN contract_hash;
|
||||
|
||||
ALTER TABLE eth.receipt_cids
|
||||
DROP COLUMN log_contracts;
|
||||
@@ -1,13 +0,0 @@
|
||||
-- +goose Up
|
||||
ALTER TABLE eth.header_cids
|
||||
ADD COLUMN times_validated INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
ALTER TABLE btc.header_cids
|
||||
ADD COLUMN times_validated INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE btc.header_cids
|
||||
DROP COLUMN times_validated;
|
||||
|
||||
ALTER TABLE eth.header_cids
|
||||
DROP COLUMN times_validated;
|
||||
-1075
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
contract:
|
||||
build:
|
||||
context: ./test/contract
|
||||
args:
|
||||
ETH_ADDR: "http://go-ethereum:8545"
|
||||
environment:
|
||||
ETH_ADDR: "http://go-ethereum:8545"
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
@@ -0,0 +1,59 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
migrations:
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- ipld-eth-db
|
||||
image: vulcanize/ipld-eth-db:v4.2.1-alpha
|
||||
environment:
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_NAME: "vulcanize_testing"
|
||||
DATABASE_PASSWORD: "password"
|
||||
DATABASE_HOSTNAME: "ipld-eth-db"
|
||||
DATABASE_PORT: 5432
|
||||
|
||||
ipld-eth-db:
|
||||
image: timescale/timescaledb:latest-pg14
|
||||
restart: always
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
environment:
|
||||
POSTGRES_USER: "vdbm"
|
||||
POSTGRES_DB: "vulcanize_testing"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
ports:
|
||||
- "127.0.0.1:8077:5432"
|
||||
|
||||
eth-server:
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- ipld-eth-db
|
||||
build:
|
||||
context: ./
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
- golang:1.13-alpine
|
||||
environment:
|
||||
IPLD_SERVER_GRAPHQL: "true"
|
||||
IPLD_POSTGRAPHILEPATH: http://graphql:5000
|
||||
ETH_SERVER_HTTPPATH: 0.0.0.0:8081
|
||||
VDB_COMMAND: "serve"
|
||||
ETH_CHAIN_CONFIG: "/tmp/chain.json"
|
||||
DATABASE_NAME: "vulcanize_testing"
|
||||
DATABASE_HOSTNAME: "ipld-eth-db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
ETH_CHAIN_ID: 4
|
||||
ETH_FORWARD_ETH_CALLS: $ETH_FORWARD_ETH_CALLS
|
||||
ETH_PROXY_ON_ERROR: $ETH_PROXY_ON_ERROR
|
||||
ETH_HTTP_PATH: $ETH_HTTP_PATH
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./chain.json
|
||||
target: /tmp/chain.json
|
||||
ports:
|
||||
- "127.0.0.1:8081:8081"
|
||||
|
||||
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,40 +0,0 @@
|
||||
FROM golang:alpine
|
||||
|
||||
RUN apk --update --no-cache add make git g++ linux-headers
|
||||
# DEBUG
|
||||
RUN apk add busybox-extras
|
||||
|
||||
# this is probably a noob move, but I want apk from alpine for the above but need to avoid Go 1.13 below as this error still occurs https://github.com/ipfs/go-ipfs/issues/6603
|
||||
FROM golang:1.12.4 as builder
|
||||
|
||||
# Get and build vulcanizedb
|
||||
ADD . /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
|
||||
# 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 .
|
||||
|
||||
WORKDIR /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
|
||||
# app container
|
||||
FROM alpine
|
||||
|
||||
ARG USER
|
||||
|
||||
RUN adduser -Du 5000 $USER
|
||||
WORKDIR /app
|
||||
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/ipfs-blockchain-watcher/dockerfiles/migrations/startup_script.sh .
|
||||
|
||||
|
||||
# keep binaries immutable
|
||||
COPY --from=builder /go/src/github.com/pressly/goose/cmd/goose/goose goose
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/db/migrations migrations/vulcanizedb
|
||||
# XXX dir is already writeable RUN touch vulcanizedb.log
|
||||
|
||||
CMD ["./startup_script.sh"]
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Runs the db migrations and starts the super node services
|
||||
|
||||
# Exit if the variable tests fail
|
||||
set -e
|
||||
set +x
|
||||
|
||||
# Check the database variables are set
|
||||
test $DATABASE_HOSTNAME
|
||||
test $DATABASE_NAME
|
||||
test $DATABASE_PORT
|
||||
test $DATABASE_USER
|
||||
test $DATABASE_PASSWORD
|
||||
set +e
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# If the db migrations ran without err
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "Migrations ran successfully"
|
||||
exit 0
|
||||
else
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,8 +0,0 @@
|
||||
FROM node:alpine
|
||||
|
||||
RUN npm install -g postgraphile
|
||||
RUN npm install -g postgraphile-plugin-connection-filter
|
||||
RUN npm install -g @graphile/pg-pubsub
|
||||
|
||||
EXPOSE 5000
|
||||
ENTRYPOINT ["postgraphile"]
|
||||
@@ -1,60 +0,0 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:10.12-alpine
|
||||
environment:
|
||||
POSTGRES_USER: "vdbm"
|
||||
POSTGRES_DB: "vulcanize_public"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
volumes:
|
||||
- vulcanizedb_db_data:/var/lib/postgresql/data
|
||||
expose:
|
||||
- "5432"
|
||||
ports:
|
||||
- "127.0.0.1:8079:5432"
|
||||
|
||||
migrations:
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./../../
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
dockerfile: ./dockerfiles/migrations/Dockerfile
|
||||
args:
|
||||
USER: "vdbm"
|
||||
environment:
|
||||
DATABASE_NAME: "vulcanize_public"
|
||||
DATABASE_HOSTNAME: "db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
|
||||
graphql:
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
- migrations
|
||||
build:
|
||||
context: ./../../
|
||||
cache_from:
|
||||
- node:alpine
|
||||
dockerfile: ./dockerfiles/postgraphile/Dockerfile
|
||||
expose:
|
||||
- "5000"
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000"
|
||||
command: ["--plugins", "@graphile/pg-pubsub",
|
||||
"--subscriptions",
|
||||
"--simple-subscriptions",
|
||||
"--connection", "postgres://vdbm:password@db:5432/vulcanize_public",
|
||||
"--port", "5000",
|
||||
"-n", "0.0.0.0",
|
||||
"--schema", "public,btc,eth",
|
||||
"--append-plugins", "postgraphile-plugin-connection-filter"]
|
||||
|
||||
volumes:
|
||||
vulcanizedb_db_data:
|
||||
@@ -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
|
||||
@@ -1,48 +0,0 @@
|
||||
FROM golang:1.12-alpine as builder
|
||||
|
||||
RUN apk --update --no-cache add make git g++ linux-headers
|
||||
# DEBUG
|
||||
RUN apk add busybox-extras
|
||||
|
||||
# Get and build vulcanizedb
|
||||
ADD . /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
WORKDIR /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
RUN GO111MODULE=on GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o vulcanizedb .
|
||||
|
||||
# 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 .
|
||||
|
||||
WORKDIR /go/src/github.com/vulcanize/ipfs-blockchain-watcher
|
||||
|
||||
# app container
|
||||
FROM alpine
|
||||
|
||||
ARG USER
|
||||
ARG CONFIG_FILE
|
||||
ARG EXPOSE_PORT_1
|
||||
ARG EXPOSE_PORT_2
|
||||
|
||||
RUN adduser -Du 5000 $USER
|
||||
WORKDIR /app
|
||||
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/ipfs-blockchain-watcher/$CONFIG_FILE config.toml
|
||||
COPY --chown=5000:5000 --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/dockerfiles/super_node/startup_script.sh .
|
||||
COPY --chown=5000:5000 --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/dockerfiles/super_node/entrypoint.sh .
|
||||
|
||||
|
||||
# keep binaries immutable
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/vulcanizedb vulcanizedb
|
||||
COPY --from=builder /go/src/github.com/pressly/goose/cmd/goose/goose goose
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/db/migrations migrations/vulcanizedb
|
||||
COPY --from=builder /go/src/github.com/vulcanize/ipfs-blockchain-watcher/environments environments
|
||||
|
||||
EXPOSE $EXPOSE_PORT_1
|
||||
EXPOSE $EXPOSE_PORT_2
|
||||
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
@@ -1,87 +0,0 @@
|
||||
version: '3.2'
|
||||
|
||||
services:
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:10.12-alpine
|
||||
environment:
|
||||
POSTGRES_USER: "vdbm"
|
||||
POSTGRES_DB: "vulcanize_public"
|
||||
POSTGRES_PASSWORD: "password"
|
||||
volumes:
|
||||
- vulcanizedb_db_data:/var/lib/postgresql/data
|
||||
expose:
|
||||
- "5432"
|
||||
ports:
|
||||
- "127.0.0.1:8079:5432"
|
||||
|
||||
btc:
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./../../
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
- golang:1.12.4
|
||||
dockerfile: ./dockerfiles/super_node/Dockerfile
|
||||
args:
|
||||
USER: "vdbm"
|
||||
CONFIG_FILE: ./environments/superNodeBTC.toml
|
||||
environment:
|
||||
VDB_COMMAND: "superNode"
|
||||
DATABASE_NAME: "vulcanize_public"
|
||||
DATABASE_HOSTNAME: "db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
ports:
|
||||
- "127.0.0.1:8082:8082"
|
||||
- "127.0.0.1:8083:8083"
|
||||
|
||||
eth:
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./../../
|
||||
cache_from:
|
||||
- alpine:latest
|
||||
- golang:1.12.4
|
||||
dockerfile: ./dockerfiles/super_node/Dockerfile
|
||||
args:
|
||||
USER: "vdbm"
|
||||
CONFIG_FILE: ./environments/superNodeETH.toml
|
||||
environment:
|
||||
VDB_COMMAND: "superNode"
|
||||
DATABASE_NAME: "vulcanize_public"
|
||||
DATABASE_HOSTNAME: "db"
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
- "127.0.0.1:8081:8081"
|
||||
|
||||
graphql:
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
build:
|
||||
context: ./../../
|
||||
cache_from:
|
||||
- node:alpine
|
||||
dockerfile: ./dockerfiles/postgraphile/Dockerfile
|
||||
expose:
|
||||
- "5000"
|
||||
ports:
|
||||
- "127.0.0.1:5000:5000"
|
||||
command: ["--plugins", "@graphile/pg-pubsub",
|
||||
"--subscriptions",
|
||||
"--simple-subscriptions",
|
||||
"--connection", "postgres://vdbm:password@db:5432/vulcanize_public",
|
||||
"--port", "5000",
|
||||
"-n", "0.0.0.0",
|
||||
"--schema", "public,btc,eth",
|
||||
"--append-plugins", "postgraphile-plugin-connection-filter"]
|
||||
|
||||
volumes:
|
||||
vulcanizedb_db_data:
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Runs the db migrations and starts the super node services
|
||||
|
||||
# Exit if the variable tests fail
|
||||
set -e
|
||||
set +x
|
||||
|
||||
# Check the database variables are set
|
||||
# XXX set defaults, don't silently fail
|
||||
#test $DATABASE_HOSTNAME
|
||||
#test $DATABASE_NAME
|
||||
#test $DATABASE_PORT
|
||||
#test $DATABASE_USER
|
||||
#test $DATABASE_PASSWORD
|
||||
#test $IPFS_INIT
|
||||
#test $IPFS_PATH
|
||||
VDB_COMMAND=${VDB_COMMAND:-superNode}
|
||||
set +e
|
||||
|
||||
# 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 vulcanizedb process"
|
||||
VDB_CONFIG_FILE=${VDB_CONFIG_FILE:-config.toml}
|
||||
DEFAULT_OPTIONS="--config=$VDB_CONFIG_FILE"
|
||||
VDB_FULL_CL=${VDB_FULL_CL:-$VDB_COMMAND $DEFAULT_OPTIONS}
|
||||
echo running: ./vulcanizedb $VDB_FULL_CL $@
|
||||
|
||||
case "$1" in
|
||||
"/bin/sh" )
|
||||
echo dropping to shell
|
||||
exec /bin/sh
|
||||
esac
|
||||
|
||||
vdb_args="$@"
|
||||
# default is to use the config passed by the build arg
|
||||
if [[ -z "$vdb_args" ]]; then
|
||||
vdb_args="--config=config.toml"
|
||||
fi
|
||||
|
||||
echo running: ./vulcanizedb $vdb_args
|
||||
./vulcanizedb $vdb_args
|
||||
rv=$?
|
||||
|
||||
if [ $rv != 0 ]; then
|
||||
echo "VulcanizeDB startup failed"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Runs the db migrations and starts the super node services
|
||||
|
||||
# Exit if the variable tests fail
|
||||
set -e
|
||||
set +x
|
||||
|
||||
# Check the database variables are set
|
||||
test $DATABASE_HOSTNAME
|
||||
test $DATABASE_NAME
|
||||
test $DATABASE_PORT
|
||||
test $DATABASE_USER
|
||||
test $DATABASE_PASSWORD
|
||||
test $IPFS_INIT
|
||||
test $IPFS_PATH
|
||||
test $VDB_COMMAND
|
||||
set +e
|
||||
|
||||
# Export our database variables so that the IPFS Postgres plugin can use them
|
||||
export IPFS_PGHOST=$DATABASE_HOSTNAME
|
||||
export IPFS_PGUSER=$DATABASE_USER
|
||||
export IPFS_PGDATABASE=$DATABASE_NAME
|
||||
export IPFS_PGPORT=$DATABASE_PORT
|
||||
export IPFS_PGPASSWORD=$DATABASE_PASSWORD
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# If the db migrations ran without err
|
||||
if [[ $? -eq 0 ]]; then
|
||||
# and IPFS_INIT is true
|
||||
if [[ "$IPFS_INIT" = true ]] ; then
|
||||
# initialize PG-IPFS
|
||||
echo "Initializing Postgres-IPFS profile"
|
||||
./ipfs init --profile=postgresds
|
||||
else
|
||||
echo "IPFS profile already initialized, skipping initialization"
|
||||
fi
|
||||
else
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If IPFS initialization was successful
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "Running the VulcanizeDB process"
|
||||
./vulcanizedb ${VDB_COMMAND} --config=config.toml
|
||||
else
|
||||
echo "Could not initialize IPFS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If VulcanizeDB process was successful
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "VulcanizeDB process ran successfully"
|
||||
else
|
||||
echo "Could not start VulcanizeDB process. Is the config file correct?"
|
||||
exit 1
|
||||
fi
|
||||
+82
-86
@@ -1,5 +1,5 @@
|
||||
## ipfs-blockchain-watcher APIs
|
||||
We can expose a number of different APIs for remote access to ipfs-blockchain-watcher data
|
||||
## ipld-eth-server APIs
|
||||
We can expose a number of different APIs for remote access to ipld-eth-server data
|
||||
|
||||
|
||||
### Table of Contents
|
||||
@@ -9,10 +9,10 @@ We can expose a number of different APIs for remote access to ipfs-blockchain-wa
|
||||
|
||||
|
||||
### Postgraphile
|
||||
ipfs-blockchain-watcher stores all processed data in Postgres using PG-IPFS, this includes all of the IPLD objects.
|
||||
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.
|
||||
e.g.
|
||||
|
||||
`postgraphile --plugins @graphile/pg-pubsub --subscriptions --simple-subscriptions -c postgres://localhost:5432/vulcanize_public?sslmode=disable -s public,btc,eth -a -j`
|
||||
|
||||
@@ -22,38 +22,36 @@ All of their data can then be queried with standard [GraphQL](https://graphql.or
|
||||
|
||||
|
||||
### RPC Subscription Interface
|
||||
A direct, real-time subscription to the data being processed by ipfs-blockchain-watcher can be established over WS or IPC through the [Stream](../pkg/watcher/api.go#L53) RPC method.
|
||||
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/streamer/super_node_streamer.go).
|
||||
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 ipfs-blockchain-watcher 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)).
|
||||
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 ipfs-blockchain-watcher using the `Stream` RPC method is provided below
|
||||
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
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/client"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/eth"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/streamer"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/watcher"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/watch"
|
||||
)
|
||||
|
||||
config, _ := eth.NewEthSubscriptionConfig()
|
||||
rlpConfig, _ := rlp.EncodeToBytes(config)
|
||||
vulcPath := viper.GetString("superNode.ethSubscription.path")
|
||||
rawRPCClient, _ := rpc.Dial(vulcPath)
|
||||
rpcClient := client.NewRPCClient(rawRPCClient, vulcPath)
|
||||
stream := streamer.NewSuperNodeStreamer(rpcClient)
|
||||
payloadChan := make(chan watcher.SubscriptionPayload, 20000)
|
||||
subscription, _ := stream.Stream(payloadChan, rlpConfig)
|
||||
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:
|
||||
@@ -67,32 +65,32 @@ An example of how to subscribe to a real-time Ethereum data feed from ipfs-block
|
||||
The .toml file being used to fill the Ethereum subscription config would look something like this:
|
||||
|
||||
```toml
|
||||
[superNode]
|
||||
[superNode.ethSubscription]
|
||||
[watcher]
|
||||
[watcher.ethSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[superNode.ethSubscription.headerFilter]
|
||||
[watcher.ethSubscription.headerFilter]
|
||||
off = false
|
||||
uncles = false
|
||||
[superNode.ethSubscription.txFilter]
|
||||
[watcher.ethSubscription.txFilter]
|
||||
off = false
|
||||
src = []
|
||||
dst = []
|
||||
[superNode.ethSubscription.receiptFilter]
|
||||
[watcher.ethSubscription.receiptFilter]
|
||||
off = false
|
||||
contracts = []
|
||||
topic0s = []
|
||||
topic1s = []
|
||||
topic2s = []
|
||||
topic3s = []
|
||||
[superNode.ethSubscription.stateFilter]
|
||||
[watcher.ethSubscription.stateFilter]
|
||||
off = false
|
||||
addresses = []
|
||||
intermediateNodes = false
|
||||
[superNode.ethSubscription.storageFilter]
|
||||
[watcher.ethSubscription.storageFilter]
|
||||
off = true
|
||||
addresses = []
|
||||
storageKeys = []
|
||||
@@ -101,81 +99,79 @@ The .toml file being used to fill the Ethereum subscription config would look so
|
||||
|
||||
These configuration parameters are broken down as follows:
|
||||
|
||||
`ethSubscription.wsPath` is used to define the SuperNode ws url OR ipc endpoint we subscribe to
|
||||
`ethSubscription.wsPath` is used to define the watcher ws url OR ipc endpoint to subscribe to
|
||||
|
||||
`ethSubscription.historicalData` specifies whether or not ipfs-blockchain-watcher should look up historical data in its cache and
|
||||
send that to the subscriber, if this is set to `false` then we only streams newly synced/incoming data
|
||||
`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 ipfs-blockchain-watcher to only send historical data with the specified range and
|
||||
`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 we want to receive data in
|
||||
`ethSubscription.startingBlock` is the starting block number for the range to receive data in
|
||||
|
||||
`ethSubscription.endingBlock` is the ending block number for the range we want to receive data in;
|
||||
setting to 0 means there is no end/we will continue streaming indefinitely.
|
||||
`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 ipfs-blockchain-watcher to not send any headers to the subscriber
|
||||
- setting `uncles` to true tells ipfs-blockchain-watcher to send uncles in addition to normal headers.
|
||||
- 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 ipfs-blockchain-watcher to not send any transactions to the subscriber
|
||||
- `src` and `dst` are string arrays which can be filled with ETH addresses we want to filter transactions for,
|
||||
if they have any addresses then ipfs-blockchain-watcher will only send transactions that were sent or received by the addresses contained
|
||||
- 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 ipfs-blockchain-watcher to not send any receipts to the subscriber
|
||||
- `topic0s` is a string array which can be filled with event topics we want to filter for,
|
||||
if it has any topics then ipfs-blockchain-watcher will only send receipts that contain logs which have that topic0.
|
||||
- `contracts` is a string array which can be filled with contract addresses we want to filter for, if it contains any contract addresses the super node will
|
||||
- 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 super node, regardless of whether or not the receipt satisfies the `topics` or `contracts` filters.
|
||||
- `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 ipfs-blockchain-watcher to not send any state data to the subscriber
|
||||
- `addresses` is a string array which can be filled with ETH addresses we want to filter state for,
|
||||
if it has any addresses then ipfs-blockchain-watcher will only send state leafs (accounts) corresponding to those account addresses.
|
||||
- By default ipfs-blockchain-watcher only sends along state leafs, if we want to receive branch and extension nodes as well `intermediateNodes` can be set to `true`.
|
||||
- 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 ipfs-blockchain-watcher to not send any storage data to the subscriber
|
||||
- `addresses` is a string array which can be filled with ETH addresses we want to filter storage for,
|
||||
if it has any addresses then ipfs-blockchain-watcher 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 we want to filter storage data for. It is important to note that the storage keys need to be the actual keccak256 hashes, whereas
|
||||
- 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 ipfs-blockchain-watcher only sends along storage leafs, if we want to receive branch and extension nodes as well `intermediateNodes` can be set to `true`.
|
||||
- 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 ipfs-blockchain-watcher using the `Stream` RPC method is provided below
|
||||
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
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/libraries/shared/streamer"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/eth/client"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/super_node"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/super_node/btc"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/btc"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/client"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/watch"
|
||||
)
|
||||
|
||||
config, _ := btc.NewBtcSubscriptionConfig()
|
||||
rlpConfig, _ := rlp.EncodeToBytes(config)
|
||||
vulcPath := viper.GetString("superNode.btcSubscription.path")
|
||||
rawRPCClient, _ := rpc.Dial(vulcPath)
|
||||
rpcClient := client.NewRPCClient(rawRPCClient, vulcPath)
|
||||
stream := streamer.NewSuperNodeStreamer(rpcClient)
|
||||
payloadChan := make(chan super_node.SubscriptionPayload, 20000)
|
||||
subscription, _ := stream.Stream(payloadChan, rlpConfig)
|
||||
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:
|
||||
@@ -189,16 +185,16 @@ An example of how to subscribe to a real-time Bitcoin data feed from ipfs-blockc
|
||||
The .toml file being used to fill the Bitcoin subscription config would look something like this:
|
||||
|
||||
```toml
|
||||
[superNode]
|
||||
[superNode.btcSubscription]
|
||||
[watcher]
|
||||
[watcher.btcSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[superNode.btcSubscription.headerFilter]
|
||||
[watcher.btcSubscription.headerFilter]
|
||||
off = false
|
||||
[superNode.btcSubscription.txFilter]
|
||||
[watcher.btcSubscription.txFilter]
|
||||
off = false
|
||||
segwit = false
|
||||
witnessHashes = []
|
||||
@@ -210,35 +206,35 @@ The .toml file being used to fill the Bitcoin subscription config would look som
|
||||
|
||||
These configuration parameters are broken down as follows:
|
||||
|
||||
`btcSubscription.wsPath` is used to define the SuperNode ws url OR ipc endpoint we subscribe to
|
||||
`btcSubscription.wsPath` is used to define the ipld-eth-server ws url OR ipc endpoint to subscribe to
|
||||
|
||||
`btcSubscription.historicalData` specifies whether or not ipfs-blockchain-watcher should look up historical data in its cache and
|
||||
send that to the subscriber, if this is set to `false` then ipfs-blockchain-watcher only streams newly synced/incoming data
|
||||
`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 ipfs-blockchain-watcher to only send historical data with the specified range and
|
||||
`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 we want to receive data in
|
||||
`btcSubscription.startingBlock` is the starting block number for the range to receive data in
|
||||
|
||||
`btcSubscription.endingBlock` is the ending block number for the range we want to receive data in;
|
||||
setting to 0 means there is no end/we will continue streaming indefinitely.
|
||||
`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 ipfs-blockchain-watcher to
|
||||
- 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 ipfs-blockchain-watcher to not send any transactions to the subscriber.
|
||||
- Setting `segwit` to true tells ipfs-blockchain-watcher to only send segwit transactions.
|
||||
- `witnessHashes` is a string array that can be filled with witness hash string; if it contains any hashes ipfs-blockchain-watcher 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 ipfs-blockchain-watcher 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 ipfs-blockchain-watcher will only send transactions that have at least one tx output with one of the specified pkscript classes;
|
||||
- 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 ipfs-blockchain-watcher 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 ipfs-blockchain-watcher will only send transactions that have at least one tx output with at least one of the provided addresses.
|
||||
- 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:
|
||||
@@ -246,7 +242,7 @@ In addition to providing novel Postgraphile and RPC-Subscription endpoints, we a
|
||||
standard chain APIs. This will allow direct compatibility with software that already makes use of the standard interfaces.
|
||||
|
||||
#### Ethereum JSON-RPC API
|
||||
ipfs-blockchain-watcher currently faithfully recapitulates portions of the Ethereum JSON-RPC api standard.
|
||||
ipld-eth-server currently faithfully recapitulates portions of the Ethereum JSON-RPC api standard.
|
||||
|
||||
The currently supported endpoints include:
|
||||
`eth_blockNumber`
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
# ipfs-blockchain-watcher architecture
|
||||
1. [Processes](#processes)
|
||||
1. [Command](#command)
|
||||
1. [Configuration](#config)
|
||||
1. [Database](#database)
|
||||
1. [APIs](#apis)
|
||||
1. [Resync](#resync)
|
||||
1. [IPFS Considerations](#ipfs-considerations)
|
||||
|
||||
## Processes
|
||||
ipfs-blockchain-watcher is a [service](../pkg/super_node/service.go#L61) comprised of the following interfaces:
|
||||
|
||||
* [Payload Fetcher](../pkg/super_node/shared/interfaces.go#L29): Fetches raw chain data from a half-duplex endpoint (HTTP/IPC), used for historical data fetching. ([BTC](../../pkg/super_node/btc/payload_fetcher.go), [ETH](../../pkg/super_node/eth/payload_fetcher.go)).
|
||||
* [Payload Streamer](../pkg/super_node/shared/interfaces.go#L24): Streams raw chain data from a full-duplex endpoint (WebSocket/IPC), used for syncing data at the head of the chain in real-time. ([BTC](../../pkg/super_node/btc/http_streamer.go), [ETH](../../pkg/super_node/eth/streamer.go)).
|
||||
* [Payload Converter](../pkg/super_node/shared/interfaces.go#L34): Converters raw chain data to an intermediary form prepared for IPFS publishing. ([BTC](../../pkg/super_node/btc/converter.go), [ETH](../../pkg/super_node/eth/converter.go)).
|
||||
* [IPLD Publisher](../pkg/super_node/shared/interfaces.go#L39): Publishes the converted data to IPFS, returning their CIDs and associated metadata for indexing. ([BTC](../../pkg/super_node/btc/publisher.go), [ETH](../../pkg/super_node/eth/publisher.go)).
|
||||
* [CID Indexer](../pkg/super_node/shared/interfaces.go#L44): Indexes CIDs in Postgres with their associated metadata. This metadata is chain specific and selected based on utility. ([BTC](../../pkg/super_node/btc/indexer.go), [ETH](../../pkg/super_node/eth/indexer.go)).
|
||||
* [CID Retriever](../pkg/super_node/shared/interfaces.go#L54): Retrieves CIDs from Postgres by searching against their associated metadata, is used to lookup data to serve API requests/subscriptions. ([BTC](../../pkg/super_node/btc/retriever.go), [ETH](../../pkg/super_node/eth/retriever.go)).
|
||||
* [IPLD Fetcher](../pkg/super_node/shared/interfaces.go#L62): Fetches the IPLDs needed to service API requests/subscriptions from IPFS using retrieved CIDS; can route through a IPFS block-exchange to search for objects that are not directly available. ([BTC](../../pkg/super_node/btc/ipld_fetcher.go), [ETH](../../pkg/super_node/eth/ipld_fetcher.go))
|
||||
* [Response Filterer](../pkg/super_node/shared/interfaces.go#L49): Filters converted data payloads served to API subscriptions; filters according to the subscriber provided parameters. ([BTC](../../pkg/super_node/btc/filterer.go), [ETH](../../pkg/super_node/eth/filterer.go)).
|
||||
* [API](https://github.com/ethereum/go-ethereum/blob/master/rpc/types.go#L31): Expose RPC methods for clients to interface with the data. Chain-specific APIs should aim to recapitulate as much of the native API as possible. ([VDB](../../pkg/super_node/api.go), [ETH](../../pkg/super_node/eth/api.go)).
|
||||
|
||||
|
||||
Appropriating the service for a new chain is done by creating underlying types to satisfy these interfaces for
|
||||
the specifics of that chain.
|
||||
|
||||
The service uses these interfaces to operate in any combination of three modes: `sync`, `serve`, and `backfill`.
|
||||
* Sync: Streams raw chain data at the head, converts and publishes it to IPFS, and indexes the resulting set of CIDs in Postgres with useful metadata.
|
||||
* BackFill: Automatically searches for and detects gaps in the DB; fetches, converts, publishes, and indexes the data to fill these gaps.
|
||||
* Serve: Opens up IPC, HTTP, and WebSocket servers on top of the ipfs-blockchain-watcher DB and any concurrent sync and/or backfill processes.
|
||||
|
||||
|
||||
These three modes are all operated through a single vulcanizeDB command: `watch`
|
||||
|
||||
## Command
|
||||
|
||||
Usage: `./ipfs-blockchain-watcher watch --config={config.toml}`
|
||||
|
||||
Configuration can also be done through CLI options and/or environmental variables.
|
||||
CLI options can be found using `./ipfs-blockchain-watcher watch --help`.
|
||||
|
||||
## Config
|
||||
|
||||
Below is the set of universal config parameters for the ipfs-blockchain-watcher command, in .toml form, with the respective environmental variables commented to the side.
|
||||
This set of parameters needs to be set no matter the chain type.
|
||||
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "vdbm" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[ipfs]
|
||||
path = "~/.ipfs" # $IPFS_PATH
|
||||
mode = "direct" # $IPFS_MODE
|
||||
|
||||
[superNode]
|
||||
chain = "bitcoin" # $SUPERNODE_CHAIN
|
||||
server = true # $SUPERNODE_SERVER
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SUPERNODE_IPC_PATH
|
||||
wsPath = "127.0.0.1:8082" # $SUPERNODE_WS_PATH
|
||||
httpPath = "127.0.0.1:8083" # $SUPERNODE_HTTP_PATH
|
||||
sync = true # $SUPERNODE_SYNC
|
||||
workers = 1 # $SUPERNODE_WORKERS
|
||||
backFill = true # $SUPERNODE_BACKFILL
|
||||
frequency = 45 # $SUPERNODE_FREQUENCY
|
||||
batchSize = 1 # $SUPERNODE_BATCH_SIZE
|
||||
batchNumber = 50 # $SUPERNODE_BATCH_NUMBER
|
||||
timeout = 300 # $HTTP_TIMEOUT
|
||||
validationLevel = 1 # $SUPERNODE_VALIDATION_LEVEL
|
||||
```
|
||||
|
||||
Additional parameters need to be set depending on the specific chain.
|
||||
|
||||
For Bitcoin:
|
||||
|
||||
```toml
|
||||
[bitcoin]
|
||||
wsPath = "127.0.0.1:8332" # $BTC_WS_PATH
|
||||
httpPath = "127.0.0.1:8332" # $BTC_HTTP_PATH
|
||||
pass = "password" # $BTC_NODE_PASSWORD
|
||||
user = "username" # $BTC_NODE_USER
|
||||
nodeID = "ocd0" # $BTC_NODE_ID
|
||||
clientName = "Omnicore" # $BTC_CLIENT_NAME
|
||||
genesisBlock = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" # $BTC_GENESIS_BLOCK
|
||||
networkID = "0xD9B4BEF9" # $BTC_NETWORK_ID
|
||||
```
|
||||
|
||||
For Ethereum:
|
||||
|
||||
```toml
|
||||
[ethereum]
|
||||
wsPath = "127.0.0.1:8546" # $ETH_WS_PATH
|
||||
httpPath = "127.0.0.1:8545" # $ETH_HTTP_PATH
|
||||
nodeID = "arch1" # $ETH_NODE_ID
|
||||
clientName = "Geth" # $ETH_CLIENT_NAME
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # $ETH_GENESIS_BLOCK
|
||||
networkID = "1" # $ETH_NETWORK_ID
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
Currently, ipfs-blockchain-watcher persists all data to a single Postgres database. The migrations for this DB can be found [here](../../db/migrations).
|
||||
Chain-specific data is populated under a chain-specific schema (e.g. `eth` and `btc`) while shared data- such as the IPFS blocks table- is populated under the `public` schema.
|
||||
Subsequent watchers which act on the raw chain data should build and populate their own schemas or separate databases entirely.
|
||||
|
||||
In the future, we will be moving to a foreign table based architecture wherein a single db is used for shared data while each watcher uses
|
||||
its own database and accesses and acts on the shared data through foreign tables. Isolating watchers to their own databases will prevent complications and
|
||||
conflicts between watcher db migrations.
|
||||
|
||||
|
||||
## APIs
|
||||
|
||||
ipfs-blockchain-watcher provides mutliple types of APIs by which to interface with its data.
|
||||
More detailed information on the APIs can be found [here](apis.md).
|
||||
|
||||
## Resync
|
||||
|
||||
A separate command `resync` is available for directing the resyncing of data within specified ranges.
|
||||
This is useful if we want to re-validate a range of data using a new source or clean out bad/deprecated data.
|
||||
More detailed information on this command can be found [here](resync.md).
|
||||
|
||||
## IPFS Considerations
|
||||
|
||||
Currently the IPLD Publisher and Fetcher can either use internalized IPFS processes which interface with a local IPFS repository, or can interface
|
||||
directly with the backing Postgres database.
|
||||
Both these options circumvent the need to run a full IPFS daemon with a [go-ipld-eth](https://github.com/ipfs/go-ipld-eth) or [go-ipld-btc](https://github.com/ipld/go-ipld-btc) plugin.
|
||||
The former approach can lead to issues with lock-contention on the IPFS repo if another IPFS process is configured and running at the same $IPFS_PATH, it also necessitates the need for
|
||||
a locally configured IPFS repository. The later bypasses the need for a configured IPFS repository/$IPFS_PATH and allows all Postgres write operations at a given block height
|
||||
to occur in a single transaction, the only disadvantage is that by avoiding moving through an IPFS node intermediary we lose the direct ability to reach out to the block
|
||||
exchange for data we do not have locally.
|
||||
|
||||
Once go-ipld-eth and go-ipld-btc have been updated to work with a modern version of PG-IPFS, an additional option will be provided to direct
|
||||
all publishing and fetching of IPLD objects through a remote IPFS daemon.
|
||||
@@ -1,73 +0,0 @@
|
||||
## ipfs-blockchain-watcher resync
|
||||
The `resync` command is made available for directing the resyncing of ipfs-blockchain-watcherdata within specified ranges.
|
||||
It also contains a utility for cleaning out old data, and resetting the validation level of data.
|
||||
|
||||
### Rational
|
||||
|
||||
Manual resyncing of data is useful when we want to re-validate data within specific ranges using a new source.
|
||||
|
||||
Cleaning out data is useful when we need to remove bad/deprecated data or prepare for breaking changes to the db schemas.
|
||||
|
||||
Resetting the validation level of data is useful for designating ranges of data for resyncing by an ongoing ipfs-blockchain-watcher
|
||||
backfill process.
|
||||
|
||||
### Command
|
||||
|
||||
Usage: `./ipfs-blockchain-watcher resync --config={config.toml}`
|
||||
|
||||
Configuration can also be done through CLI options and/or environmental variables.
|
||||
CLI options can be found using `./ipfs-blockchain-watcher resync --help`.
|
||||
|
||||
### Config
|
||||
|
||||
Below is the set of universal config parameters for the resync command, in .toml form, with the respective environmental variables commented to the side.
|
||||
This set of parameters needs to be set no matter the chain type.
|
||||
|
||||
```toml
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "vdbm" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[ipfs]
|
||||
path = "~/.ipfs" # $IPFS_PATH
|
||||
|
||||
[resync]
|
||||
chain = "ethereum" # $RESYNC_CHAIN
|
||||
type = "state" # $RESYNC_TYPE
|
||||
start = 0 # $RESYNC_START
|
||||
stop = 1000 # $RESYNC_STOP
|
||||
batchSize = 10 # $RESYNC_BATCH_SIZE
|
||||
batchNumber = 100 # $RESYNC_BATCH_NUMBER
|
||||
timeout = 300 # $HTTP_TIMEOUT
|
||||
clearOldCache = true # $RESYNC_CLEAR_OLD_CACHE
|
||||
resetValidation = true # $RESYNC_RESET_VALIDATION
|
||||
```
|
||||
|
||||
Additional parameters need to be set depending on the specific chain.
|
||||
|
||||
For Bitcoin:
|
||||
|
||||
```toml
|
||||
[bitcoin]
|
||||
httpPath = "127.0.0.1:8332" # $BTC_HTTP_PATH
|
||||
pass = "password" # $BTC_NODE_PASSWORD
|
||||
user = "username" # $BTC_NODE_USER
|
||||
nodeID = "ocd0" # $BTC_NODE_ID
|
||||
clientName = "Omnicore" # $BTC_CLIENT_NAME
|
||||
genesisBlock = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" # $BTC_GENESIS_BLOCK
|
||||
networkID = "0xD9B4BEF9" # $BTC_NETWORK_ID
|
||||
```
|
||||
|
||||
For Ethereum:
|
||||
|
||||
```toml
|
||||
[ethereum]
|
||||
httpPath = "127.0.0.1:8545" # $ETH_HTTP_PATH
|
||||
nodeID = "arch1" # $ETH_NODE_ID
|
||||
clientName = "Geth" # $ETH_CLIENT_NAME
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # $ETH_GENESIS_BLOCK
|
||||
networkID = "1" # $ETH_NETWORK_ID
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
These are the components of a VulcanizeDB Watcher:
|
||||
* Data Fetcher/Streamer sources:
|
||||
* go-ethereum
|
||||
* bitcoind
|
||||
* btcd
|
||||
* IPFS
|
||||
* Transformers contain:
|
||||
* converter
|
||||
* publisher
|
||||
* indexer
|
||||
* Endpoints contain:
|
||||
* api
|
||||
* backend
|
||||
* filterer
|
||||
* retriever
|
||||
* ipld_server
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
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
|
||||
@@ -0,0 +1,30 @@
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "postgres" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[log]
|
||||
level = "info" # $LOGRUS_LEVEL
|
||||
|
||||
[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
|
||||
graphql = true # $SERVER_GRAPHQL
|
||||
graphqlEndpoint = "127.0.0.1:8083" # $SERVER_GRAPHQL_ENDPOINT
|
||||
|
||||
[ethereum]
|
||||
chainConfig = "./chain.json" # ETH_CHAIN_CONFIG
|
||||
chainID = "1" # $ETH_CHAIN_ID
|
||||
defaultSender = "" # $ETH_DEFAULT_SENDER_ADDR
|
||||
rpcGasCap = "1000000000000" # $ETH_RPC_GAS_CAP
|
||||
httpPath = "127.0.0.1:8545" # $ETH_HTTP_PATH
|
||||
supportsStateDiff = true # $ETH_SUPPORTS_STATEDIFF
|
||||
forwardEthCalls = false # $ETH_FORWARD_ETH_CALLS
|
||||
proxyOnError = true # $ETH_PROXY_ON_ERROR
|
||||
nodeID = "arch1" # $ETH_NODE_ID
|
||||
clientName = "Geth" # $ETH_CLIENT_NAME
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # $ETH_GENESIS_BLOCK
|
||||
networkID = "1" # $ETH_NETWORK_ID
|
||||
@@ -1,29 +1,29 @@
|
||||
[superNode]
|
||||
[superNode.ethSubscription]
|
||||
[watcher]
|
||||
[watcher.ethSubscription]
|
||||
historicalData = false
|
||||
historicalDataOnly = false
|
||||
startingBlock = 0
|
||||
endingBlock = 0
|
||||
wsPath = "ws://127.0.0.1:8080"
|
||||
[superNode.ethSubscription.headerFilter]
|
||||
[watcher.ethSubscription.headerFilter]
|
||||
off = false
|
||||
uncles = false
|
||||
[superNode.ethSubscription.txFilter]
|
||||
[watcher.ethSubscription.txFilter]
|
||||
off = false
|
||||
src = []
|
||||
dst = []
|
||||
[superNode.ethSubscription.receiptFilter]
|
||||
[watcher.ethSubscription.receiptFilter]
|
||||
off = false
|
||||
contracts = []
|
||||
topic0s = []
|
||||
topic1s = []
|
||||
topic2s = []
|
||||
topic3s = []
|
||||
[superNode.ethSubscription.stateFilter]
|
||||
[watcher.ethSubscription.stateFilter]
|
||||
off = false
|
||||
addresses = []
|
||||
intermediateNodes = false
|
||||
[superNode.ethSubscription.storageFilter]
|
||||
[watcher.ethSubscription.storageFilter]
|
||||
off = true
|
||||
addresses = []
|
||||
storageKeys = []
|
||||
@@ -1,45 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "vdbm" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[database.sync]
|
||||
maxIdle = 1
|
||||
[database.backFill]
|
||||
maxIdle = 5
|
||||
|
||||
[resync]
|
||||
chain = "bitcoin" # $RESYNC_CHAIN
|
||||
type = "full" # $RESYNC_TYPE
|
||||
start = 0 # $RESYNC_START
|
||||
stop = 0 # $RESYNC_STOP
|
||||
batchSize = 5 # $RESYNC_BATCH_SIZE
|
||||
batchNumber = 5 # $RESYNC_BATCH_NUMBER
|
||||
clearOldCache = false # $RESYNC_CLEAR_OLD_CACHE
|
||||
resetValidation = true # $RESYNC_RESET_VALIDATION
|
||||
|
||||
[superNode]
|
||||
chain = "bitcoin" # $SUPERNODE_CHAIN
|
||||
server = true # $SUPERNODE_SERVER
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SUPERNODE_IPC_PATH
|
||||
wsPath = "127.0.0.1:8082" # $SUPERNODE_WS_PATH
|
||||
httpPath = "127.0.0.1:8083" # $SUPERNODE_HTTP_PATH
|
||||
sync = true # $SUPERNODE_SYNC
|
||||
workers = 1 # $SUPERNODE_WORKERS
|
||||
backFill = true # $SUPERNODE_BACKFILL
|
||||
frequency = 45 # $SUPERNODE_FREQUENCY
|
||||
batchSize = 5 # $SUPERNODE_BATCH_SIZE
|
||||
batchNumber = 5 # $SUPERNODE_BATCH_NUMBER
|
||||
validationLevel = 1 # $SUPERNODE_VALIDATION_LEVEL
|
||||
|
||||
[bitcoin]
|
||||
wsPath = "127.0.0.1:8332" # $BTC_WS_PATH
|
||||
httpPath = "127.0.0.1:8332" # $BTC_HTTP_PATH
|
||||
pass = "password" # $BTC_NODE_PASSWORD
|
||||
user = "username" # $BTC_NODE_USER
|
||||
nodeID = "ocd0" # $BTC_NODE_ID
|
||||
clientName = "Omnicore" # $BTC_CLIENT_NAME
|
||||
genesisBlock = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" # $BTC_GENESIS_BLOCK
|
||||
networkID = "0xD9B4BEF9" # $BTC_NETWORK_ID
|
||||
@@ -1,45 +0,0 @@
|
||||
[database]
|
||||
name = "vulcanize_public" # $DATABASE_NAME
|
||||
hostname = "localhost" # $DATABASE_HOSTNAME
|
||||
port = 5432 # $DATABASE_PORT
|
||||
user = "vdbm" # $DATABASE_USER
|
||||
password = "" # $DATABASE_PASSWORD
|
||||
|
||||
[database.sync]
|
||||
maxIdle = 1
|
||||
[database.backFill]
|
||||
maxIdle = 5
|
||||
|
||||
[resync]
|
||||
chain = "ethereum" # $RESYNC_CHAIN
|
||||
type = "state" # $RESYNC_TYPE
|
||||
start = 0 # $RESYNC_START
|
||||
stop = 0 # $RESYNC_STOP
|
||||
batchSize = 5 # $RESYNC_BATCH_SIZE
|
||||
batchNumber = 5 # $RESYNC_BATCH_NUMBER
|
||||
timeout = 300 # $HTTP_TIMEOUT
|
||||
clearOldCache = true # $RESYNC_CLEAR_OLD_CACHE
|
||||
resetValidation = true # $RESYNC_RESET_VALIDATION
|
||||
|
||||
[superNode]
|
||||
chain = "ethereum" # $SUPERNODE_CHAIN
|
||||
server = true # $SUPERNODE_SERVER
|
||||
ipcPath = "~/.vulcanize/vulcanize.ipc" # $SUPERNODE_IPC_PATH
|
||||
wsPath = "127.0.0.1:8081" # $SUPERNODE_WS_PATH
|
||||
httpPath = "127.0.0.1:8082" # $SUPERNODE_HTTP_PATH
|
||||
sync = true # $SUPERNODE_SYNC
|
||||
workers = 1 # $SUPERNODE_WORKERS
|
||||
backFill = true # $SUPERNODE_BACKFILL
|
||||
frequency = 15 # $SUPERNODE_FREQUENCY
|
||||
batchSize = 5 # $SUPERNODE_BATCH_SIZE
|
||||
batchNumber = 5 # $SUPERNODE_BATCH_NUMBER
|
||||
timeout = 300 # $HTTP_TIMEOUT
|
||||
validationLevel = 1 # $SUPERNODE_VALIDATION_LEVEL
|
||||
|
||||
[ethereum]
|
||||
wsPath = "127.0.0.1:8546" # $ETH_WS_PATH
|
||||
httpPath = "127.0.0.1:8545" # $ETH_HTTP_PATH
|
||||
nodeID = "arch1" # $ETH_NODE_ID
|
||||
clientName = "Geth" # $ETH_CLIENT_NAME
|
||||
genesisBlock = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" # $ETH_GENESIS_BLOCK
|
||||
networkID = "1" # $ETH_NETWORK_ID
|
||||
@@ -1,30 +1,296 @@
|
||||
module github.com/vulcanize/ipfs-blockchain-watcher
|
||||
module github.com/vulcanize/ipld-eth-server/v4
|
||||
|
||||
go 1.12
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.20.1-beta
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/ethereum/go-ethereum v1.9.11
|
||||
github.com/ipfs/go-block-format v0.0.2
|
||||
github.com/ipfs/go-blockservice v0.1.3
|
||||
github.com/ipfs/go-cid v0.0.5
|
||||
github.com/ipfs/go-filestore v1.0.0 // indirect
|
||||
github.com/ipfs/go-ipfs v0.5.1
|
||||
github.com/ipfs/go-ipfs-blockstore v1.0.0
|
||||
github.com/ipfs/go-ipfs-ds-help v1.0.0
|
||||
github.com/ipfs/go-ipfs-exchange-interface v0.0.1
|
||||
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
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 // indirect
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect
|
||||
github.com/ethereum/go-ethereum v1.10.23
|
||||
github.com/graph-gophers/graphql-go v1.3.0
|
||||
github.com/ipfs/go-block-format v0.0.3
|
||||
github.com/ipfs/go-cid v0.2.0
|
||||
github.com/ipfs/go-ipfs-blockstore v1.2.0
|
||||
github.com/ipfs/go-ipfs-ds-help v1.1.0
|
||||
github.com/jmoiron/sqlx v1.3.5
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/machinebox/graphql v0.2.2
|
||||
github.com/mailgun/groupcache/v2 v2.3.0
|
||||
github.com/multiformats/go-multihash v0.2.0
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.19.0
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/viper v1.11.0
|
||||
github.com/vulcanize/eth-ipfs-state-validator/v4 v4.0.7-alpha
|
||||
github.com/vulcanize/gap-filler v0.4.0
|
||||
github.com/vulcanize/ipfs-ethdb/v4 v4.0.7-alpha
|
||||
gorm.io/driver/postgres v1.3.7
|
||||
gorm.io/gorm v1.23.5
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.9.11 => github.com/vulcanize/go-ethereum v1.9.11-statediff-0.0.2
|
||||
require (
|
||||
bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect
|
||||
github.com/Stebalien/go-bitfield v0.0.1 // indirect
|
||||
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect
|
||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/btcsuite/btcd v0.22.1 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/containerd/cgroups v1.0.3 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
|
||||
github.com/cskr/pubsub v1.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/elastic/gosigar v0.14.2 // indirect
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
|
||||
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect
|
||||
github.com/flynn/noise v1.0.0 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/friendsofgo/graphiql v0.2.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect
|
||||
github.com/georgysavva/scany v0.2.9 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/gopacket v1.1.19 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/graphql-go/graphql v0.7.9 // indirect
|
||||
github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-bexpr v0.1.10 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
|
||||
github.com/holiman/uint256 v1.2.0 // indirect
|
||||
github.com/huin/goupnp v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/influxdata/influxdb v1.8.3 // indirect
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/go-bitfield v1.0.0 // indirect
|
||||
github.com/ipfs/go-bitswap v0.8.0 // indirect
|
||||
github.com/ipfs/go-blockservice v0.4.0 // indirect
|
||||
github.com/ipfs/go-cidutil v0.1.0 // indirect
|
||||
github.com/ipfs/go-datastore v0.5.1 // indirect
|
||||
github.com/ipfs/go-delegated-routing v0.3.0 // indirect
|
||||
github.com/ipfs/go-ds-measure v0.2.0 // indirect
|
||||
github.com/ipfs/go-fetcher v1.6.1 // indirect
|
||||
github.com/ipfs/go-filestore v1.2.0 // indirect
|
||||
github.com/ipfs/go-fs-lock v0.0.7 // indirect
|
||||
github.com/ipfs/go-graphsync v0.13.1 // indirect
|
||||
github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect
|
||||
github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.3.0 // indirect
|
||||
github.com/ipfs/go-ipfs-files v0.1.1 // indirect
|
||||
github.com/ipfs/go-ipfs-keystore v0.0.2 // indirect
|
||||
github.com/ipfs/go-ipfs-pinner v0.2.1 // indirect
|
||||
github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect
|
||||
github.com/ipfs/go-ipfs-pq v0.0.2 // indirect
|
||||
github.com/ipfs/go-ipfs-provider v0.7.1 // indirect
|
||||
github.com/ipfs/go-ipfs-routing v0.2.1 // indirect
|
||||
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
|
||||
github.com/ipfs/go-ipld-cbor v0.0.5 // indirect
|
||||
github.com/ipfs/go-ipld-format v0.4.0 // indirect
|
||||
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
|
||||
github.com/ipfs/go-ipns v0.1.2 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
||||
github.com/ipfs/go-merkledag v0.6.0 // indirect
|
||||
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
|
||||
github.com/ipfs/go-mfs v0.2.1 // indirect
|
||||
github.com/ipfs/go-namesys v0.5.0 // indirect
|
||||
github.com/ipfs/go-path v0.3.0 // indirect
|
||||
github.com/ipfs/go-peertaskqueue v0.7.1 // indirect
|
||||
github.com/ipfs/go-unixfs v0.4.0 // indirect
|
||||
github.com/ipfs/go-unixfsnode v1.4.0 // indirect
|
||||
github.com/ipfs/go-verifcid v0.0.1 // indirect
|
||||
github.com/ipfs/interface-go-ipfs-core v0.7.0 // indirect
|
||||
github.com/ipfs/kubo v0.14.0 // indirect
|
||||
github.com/ipld/edelweiss v0.1.4 // indirect
|
||||
github.com/ipld/go-codec-dagpb v1.4.0 // indirect
|
||||
github.com/ipld/go-ipld-prime v0.17.0 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.12.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.11.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.16.1 // indirect
|
||||
github.com/jackc/puddle v1.2.1 // indirect
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||
github.com/jbenet/goprocess v0.1.4 // indirect
|
||||
github.com/jinzhu/copier v0.2.4 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
github.com/klauspost/compress v1.15.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/koron/go-ssdp v0.0.2 // indirect
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 // indirect
|
||||
github.com/libp2p/go-cidranger v1.1.0 // indirect
|
||||
github.com/libp2p/go-doh-resolver v0.4.0 // indirect
|
||||
github.com/libp2p/go-eventbus v0.2.1 // indirect
|
||||
github.com/libp2p/go-flow-metrics v0.0.3 // indirect
|
||||
github.com/libp2p/go-libp2p v0.20.3 // indirect
|
||||
github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
|
||||
github.com/libp2p/go-libp2p-core v0.16.1 // indirect
|
||||
github.com/libp2p/go-libp2p-discovery v0.7.0 // indirect
|
||||
github.com/libp2p/go-libp2p-kad-dht v0.16.0 // indirect
|
||||
github.com/libp2p/go-libp2p-kbucket v0.4.7 // indirect
|
||||
github.com/libp2p/go-libp2p-loggables v0.1.0 // indirect
|
||||
github.com/libp2p/go-libp2p-peerstore v0.6.0 // indirect
|
||||
github.com/libp2p/go-libp2p-pubsub v0.6.1 // indirect
|
||||
github.com/libp2p/go-libp2p-pubsub-router v0.5.0 // indirect
|
||||
github.com/libp2p/go-libp2p-record v0.1.3 // indirect
|
||||
github.com/libp2p/go-libp2p-resource-manager v0.3.0 // indirect
|
||||
github.com/libp2p/go-libp2p-routing-helpers v0.2.3 // indirect
|
||||
github.com/libp2p/go-libp2p-swarm v0.11.0 // indirect
|
||||
github.com/libp2p/go-libp2p-xor v0.1.0 // indirect
|
||||
github.com/libp2p/go-mplex v0.7.0 // indirect
|
||||
github.com/libp2p/go-msgio v0.2.0 // indirect
|
||||
github.com/libp2p/go-nat v0.1.0 // indirect
|
||||
github.com/libp2p/go-netroute v0.2.0 // indirect
|
||||
github.com/libp2p/go-openssl v0.0.7 // indirect
|
||||
github.com/libp2p/go-reuseport v0.2.0 // indirect
|
||||
github.com/libp2p/go-yamux/v3 v3.1.2 // indirect
|
||||
github.com/libp2p/zeroconf/v2 v2.1.1 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.27.1 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
|
||||
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||
github.com/matryer/is v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.48 // indirect
|
||||
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mitchellh/pointerstructure v1.2.0 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/multiformats/go-base32 v0.0.4 // indirect
|
||||
github.com/multiformats/go-base36 v0.1.0 // indirect
|
||||
github.com/multiformats/go-multiaddr v0.5.0 // indirect
|
||||
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
|
||||
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||
github.com/multiformats/go-multibase v0.1.0 // indirect
|
||||
github.com/multiformats/go-multicodec v0.5.0 // indirect
|
||||
github.com/multiformats/go-multistream v0.3.3 // indirect
|
||||
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect
|
||||
github.com/pganalyze/pg_query_go/v2 v2.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.33.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/prometheus/tsdb v0.10.0 // indirect
|
||||
github.com/raulk/clock v1.1.0 // indirect
|
||||
github.com/raulk/go-watchdog v1.2.0 // indirect
|
||||
github.com/rjeczalik/notify v0.9.1 // indirect
|
||||
github.com/rs/cors v1.7.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/smartystreets/assertions v1.0.1 // indirect
|
||||
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.7.2 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
|
||||
github.com/thoas/go-funk v0.9.2 // indirect
|
||||
github.com/tidwall/gjson v1.14.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
|
||||
github.com/urfave/cli/v2 v2.10.2 // indirect
|
||||
github.com/valyala/fastjson v1.6.3 // indirect
|
||||
github.com/vulcanize/go-eth-state-node-iterator v1.1.4 // indirect
|
||||
github.com/wI2L/jsondiff v0.2.0 // indirect
|
||||
github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
|
||||
github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect
|
||||
github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect
|
||||
github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/otel v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.7.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/dig v1.14.0 // indirect
|
||||
go.uber.org/fx v1.16.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/blake3 v1.1.7 // indirect
|
||||
)
|
||||
|
||||
replace github.com/ethereum/go-ethereum v1.10.23 => github.com/vulcanize/go-ethereum v1.10.23-statediff-4.2.0-alpha
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/cmd"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
@@ -0,0 +1,353 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": 4,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": null,
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"mean"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"pluginVersion": "7.2.1",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "ipld_eth_server_ws_count",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Websocket Connection Count",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"datasource": null,
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"reduceOptions": {
|
||||
"calcs": [
|
||||
"mean"
|
||||
],
|
||||
"fields": "",
|
||||
"values": false
|
||||
},
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"pluginVersion": "7.2.1",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "ipld_eth_server_ipc_count",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "IPC Connection Count",
|
||||
"type": "gauge"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 2,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.2.1",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(ipld_eth_server_http_count[1m])",
|
||||
"interval": "",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "HTTP requests per second",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
"unit": "s"
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 8
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 7,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.2.1",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.9, rate(ipld_eth_server_http_duration_bucket[1m]))",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
"legendFormat": "0.9",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.75, rate(ipld_eth_server_http_duration_bucket[1m]))",
|
||||
"interval": "",
|
||||
"legendFormat": "0.75",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.5, rate(ipld_eth_server_http_duration_bucket[1m]))",
|
||||
"interval": "",
|
||||
"legendFormat": "0.5",
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "HTTP Requests duration (percentile)",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "s",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"refresh": "10s",
|
||||
"schemaVersion": 26,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "ipld-eth-server",
|
||||
"uid": "lFVEvNtGk",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
global:
|
||||
scrape_interval: 10s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'ipld-eth-server'
|
||||
static_configs:
|
||||
- targets: ['localhost:8090']
|
||||
Vendored
BIN
Binary file not shown.
@@ -1,302 +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 btc
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/utils"
|
||||
)
|
||||
|
||||
// CIDRetriever satisfies the CIDRetriever interface for bitcoin
|
||||
type CIDRetriever struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
// NewCIDRetriever returns a pointer to a new CIDRetriever which supports the CIDRetriever interface
|
||||
func NewCIDRetriever(db *postgres.DB) *CIDRetriever {
|
||||
return &CIDRetriever{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// RetrieveFirstBlockNumber is used to retrieve the first block number in the db
|
||||
func (bcr *CIDRetriever) RetrieveFirstBlockNumber() (int64, error) {
|
||||
var blockNumber int64
|
||||
err := bcr.db.Get(&blockNumber, "SELECT block_number FROM btc.header_cids ORDER BY block_number ASC LIMIT 1")
|
||||
return blockNumber, err
|
||||
}
|
||||
|
||||
// RetrieveLastBlockNumber is used to retrieve the latest block number in the db
|
||||
func (bcr *CIDRetriever) RetrieveLastBlockNumber() (int64, error) {
|
||||
var blockNumber int64
|
||||
err := bcr.db.Get(&blockNumber, "SELECT block_number FROM btc.header_cids ORDER BY block_number DESC LIMIT 1 ")
|
||||
return blockNumber, err
|
||||
}
|
||||
|
||||
// Retrieve is used to retrieve all of the CIDs which conform to the passed StreamFilters
|
||||
func (bcr *CIDRetriever) Retrieve(filter shared.SubscriptionSettings, blockNumber int64) ([]shared.CIDsForFetching, bool, error) {
|
||||
streamFilter, ok := filter.(*SubscriptionSettings)
|
||||
if !ok {
|
||||
return nil, true, fmt.Errorf("btc retriever expected filter type %T got %T", &SubscriptionSettings{}, filter)
|
||||
}
|
||||
log.Debug("retrieving cids")
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := bcr.db.Beginx()
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
// Retrieve cached header CIDs
|
||||
headers, err := bcr.RetrieveHeaderCIDs(tx, blockNumber)
|
||||
if err != nil {
|
||||
log.Error("header cid retrieval error")
|
||||
return nil, true, err
|
||||
}
|
||||
cws := make([]shared.CIDsForFetching, len(headers))
|
||||
empty := true
|
||||
for i, header := range headers {
|
||||
cw := new(CIDWrapper)
|
||||
cw.BlockNumber = big.NewInt(blockNumber)
|
||||
if !streamFilter.HeaderFilter.Off {
|
||||
cw.Header = header
|
||||
empty = false
|
||||
}
|
||||
// Retrieve cached trx CIDs
|
||||
if !streamFilter.TxFilter.Off {
|
||||
cw.Transactions, err = bcr.RetrieveTxCIDs(tx, streamFilter.TxFilter, header.ID)
|
||||
if err != nil {
|
||||
log.Error("transaction cid retrieval error")
|
||||
return nil, true, err
|
||||
}
|
||||
if len(cw.Transactions) > 0 {
|
||||
empty = false
|
||||
}
|
||||
}
|
||||
cws[i] = cw
|
||||
}
|
||||
|
||||
return cws, empty, err
|
||||
}
|
||||
|
||||
// RetrieveHeaderCIDs retrieves and returns all of the header cids at the provided blockheight
|
||||
func (bcr *CIDRetriever) RetrieveHeaderCIDs(tx *sqlx.Tx, blockNumber int64) ([]HeaderModel, error) {
|
||||
log.Debug("retrieving header cids for block ", blockNumber)
|
||||
headers := make([]HeaderModel, 0)
|
||||
pgStr := `SELECT * FROM btc.header_cids
|
||||
WHERE block_number = $1`
|
||||
return headers, tx.Select(&headers, pgStr, blockNumber)
|
||||
}
|
||||
|
||||
// RetrieveTxCIDs retrieves and returns all of the trx cids at the provided blockheight that conform to the provided filter parameters
|
||||
// also returns the ids for the returned transaction cids
|
||||
func (bcr *CIDRetriever) RetrieveTxCIDs(tx *sqlx.Tx, txFilter TxFilter, headerID int64) ([]TxModel, error) {
|
||||
log.Debug("retrieving transaction cids for header id ", headerID)
|
||||
args := make([]interface{}, 0, 3)
|
||||
results := make([]TxModel, 0)
|
||||
id := 1
|
||||
pgStr := fmt.Sprintf(`SELECT transaction_cids.id, transaction_cids.header_id,
|
||||
transaction_cids.tx_hash, transaction_cids.cid,
|
||||
transaction_cids.segwit, transaction_cids.witness_hash, transaction_cids.index
|
||||
FROM btc.transaction_cids, btc.header_cids, btc.tx_inputs, btc.tx_outputs
|
||||
WHERE transaction_cids.header_id = header_cids.id
|
||||
AND tx_inputs.tx_id = transaction_cids.id
|
||||
AND tx_outputs.tx_id = transaction_cids.id
|
||||
AND header_cids.id = $%d`, id)
|
||||
args = append(args, headerID)
|
||||
id++
|
||||
if txFilter.Segwit {
|
||||
pgStr += ` AND transaction_cids.segwit = true`
|
||||
}
|
||||
if txFilter.MultiSig {
|
||||
pgStr += ` AND tx_outputs.required_sigs > 1`
|
||||
}
|
||||
if len(txFilter.WitnessHashes) > 0 {
|
||||
pgStr += fmt.Sprintf(` AND transaction_cids.witness_hash = ANY($%d::VARCHAR(66)[])`, id)
|
||||
args = append(args, pq.Array(txFilter.WitnessHashes))
|
||||
id++
|
||||
}
|
||||
if len(txFilter.Addresses) > 0 {
|
||||
pgStr += fmt.Sprintf(` AND tx_outputs.addresses && $%d::VARCHAR(66)[]`, id)
|
||||
args = append(args, pq.Array(txFilter.Addresses))
|
||||
id++
|
||||
}
|
||||
if len(txFilter.Indexes) > 0 {
|
||||
pgStr += fmt.Sprintf(` AND transaction_cids.index = ANY($%d::INTEGER[])`, id)
|
||||
args = append(args, pq.Array(txFilter.Indexes))
|
||||
id++
|
||||
}
|
||||
if len(txFilter.PkScriptClasses) > 0 {
|
||||
pgStr += fmt.Sprintf(` AND tx_outputs.script_class = ANY($%d::INTEGER[])`, id)
|
||||
args = append(args, pq.Array(txFilter.PkScriptClasses))
|
||||
}
|
||||
return results, tx.Select(&results, pgStr, args...)
|
||||
}
|
||||
|
||||
// RetrieveGapsInData is used to find the the block numbers at which we are missing data in the db
|
||||
func (bcr *CIDRetriever) RetrieveGapsInData(validationLevel int) ([]shared.Gap, error) {
|
||||
log.Info("searching for gaps in the btc super node database")
|
||||
startingBlock, err := bcr.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("btc CIDRetriever RetrieveFirstBlockNumber error: %v", err)
|
||||
}
|
||||
var initialGap []shared.Gap
|
||||
if startingBlock != 0 {
|
||||
stop := uint64(startingBlock - 1)
|
||||
log.Infof("found gap at the beginning of the btc sync from 0 to %d", stop)
|
||||
initialGap = []shared.Gap{{
|
||||
Start: 0,
|
||||
Stop: stop,
|
||||
}}
|
||||
}
|
||||
|
||||
pgStr := `SELECT header_cids.block_number + 1 AS start, min(fr.block_number) - 1 AS stop FROM btc.header_cids
|
||||
LEFT JOIN btc.header_cids r on btc.header_cids.block_number = r.block_number - 1
|
||||
LEFT JOIN btc.header_cids fr on btc.header_cids.block_number < fr.block_number
|
||||
WHERE r.block_number is NULL and fr.block_number IS NOT NULL
|
||||
GROUP BY header_cids.block_number, r.block_number`
|
||||
results := make([]struct {
|
||||
Start uint64 `db:"start"`
|
||||
Stop uint64 `db:"stop"`
|
||||
}, 0)
|
||||
if err := bcr.db.Select(&results, pgStr); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
emptyGaps := make([]shared.Gap, len(results))
|
||||
for i, res := range results {
|
||||
emptyGaps[i] = shared.Gap{
|
||||
Start: res.Start,
|
||||
Stop: res.Stop,
|
||||
}
|
||||
}
|
||||
|
||||
// Find sections of blocks where we are below the validation level
|
||||
// There will be no overlap between these "gaps" and the ones above
|
||||
pgStr = `SELECT block_number FROM btc.header_cids
|
||||
WHERE times_validated < $1
|
||||
ORDER BY block_number`
|
||||
var heights []uint64
|
||||
if err := bcr.db.Select(&heights, pgStr, validationLevel); err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
return append(append(initialGap, emptyGaps...), utils.MissingHeightsToGaps(heights)...), nil
|
||||
}
|
||||
|
||||
// RetrieveBlockByHash returns all of the CIDs needed to compose an entire block, for a given block hash
|
||||
func (bcr *CIDRetriever) RetrieveBlockByHash(blockHash common.Hash) (HeaderModel, []TxModel, error) {
|
||||
log.Debug("retrieving block cids for block hash ", blockHash.String())
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := bcr.db.Beginx()
|
||||
if err != nil {
|
||||
return HeaderModel{}, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
headerCID, err := bcr.RetrieveHeaderCIDByHash(tx, blockHash)
|
||||
if err != nil {
|
||||
log.Error("header cid retrieval error")
|
||||
return HeaderModel{}, nil, err
|
||||
}
|
||||
txCIDs, err := bcr.RetrieveTxCIDsByHeaderID(tx, headerCID.ID)
|
||||
if err != nil {
|
||||
log.Error("tx cid retrieval error")
|
||||
}
|
||||
return headerCID, txCIDs, err
|
||||
}
|
||||
|
||||
// RetrieveBlockByNumber returns all of the CIDs needed to compose an entire block, for a given block number
|
||||
func (bcr *CIDRetriever) RetrieveBlockByNumber(blockNumber int64) (HeaderModel, []TxModel, error) {
|
||||
log.Debug("retrieving block cids for block number ", blockNumber)
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := bcr.db.Beginx()
|
||||
if err != nil {
|
||||
return HeaderModel{}, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
headerCID, err := bcr.RetrieveHeaderCIDs(tx, blockNumber)
|
||||
if err != nil {
|
||||
log.Error("header cid retrieval error")
|
||||
return HeaderModel{}, nil, err
|
||||
}
|
||||
if len(headerCID) < 1 {
|
||||
return HeaderModel{}, nil, fmt.Errorf("header cid retrieval error, no header CIDs found at block %d", blockNumber)
|
||||
}
|
||||
txCIDs, err := bcr.RetrieveTxCIDsByHeaderID(tx, headerCID[0].ID)
|
||||
if err != nil {
|
||||
log.Error("tx cid retrieval error")
|
||||
}
|
||||
return headerCID[0], txCIDs, err
|
||||
}
|
||||
|
||||
// RetrieveHeaderCIDByHash returns the header for the given block hash
|
||||
func (bcr *CIDRetriever) RetrieveHeaderCIDByHash(tx *sqlx.Tx, blockHash common.Hash) (HeaderModel, error) {
|
||||
log.Debug("retrieving header cids for block hash ", blockHash.String())
|
||||
pgStr := `SELECT * FROM btc.header_cids
|
||||
WHERE block_hash = $1`
|
||||
var headerCID HeaderModel
|
||||
return headerCID, tx.Get(&headerCID, pgStr, blockHash.String())
|
||||
}
|
||||
|
||||
// RetrieveTxCIDsByHeaderID retrieves all tx CIDs for the given header id
|
||||
func (bcr *CIDRetriever) RetrieveTxCIDsByHeaderID(tx *sqlx.Tx, headerID int64) ([]TxModel, error) {
|
||||
log.Debug("retrieving tx cids for block id ", headerID)
|
||||
pgStr := `SELECT * FROM btc.transaction_cids
|
||||
WHERE header_id = $1`
|
||||
var txCIDs []TxModel
|
||||
return txCIDs, tx.Select(&txCIDs, pgStr, headerID)
|
||||
}
|
||||
@@ -1,193 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// Cleaner satisfies the shared.Cleaner interface fo bitcoin
|
||||
type Cleaner struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
// NewCleaner returns a new Cleaner struct that satisfies the shared.Cleaner interface
|
||||
func NewCleaner(db *postgres.DB) *Cleaner {
|
||||
return &Cleaner{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// ResetValidation resets the validation level to 0 to enable revalidation
|
||||
func (c *Cleaner) ResetValidation(rngs [][2]uint64) error {
|
||||
tx, err := c.db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rng := range rngs {
|
||||
logrus.Infof("btc db cleaner resetting validation level to 0 for block range %d to %d", rng[0], rng[1])
|
||||
pgStr := `UPDATE btc.header_cids
|
||||
SET times_validated = 0
|
||||
WHERE block_number BETWEEN $1 AND $2`
|
||||
if _, err := tx.Exec(pgStr, rng[0], rng[1]); err != nil {
|
||||
shared.Rollback(tx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// Clean removes the specified data from the db within the provided block range
|
||||
func (c *Cleaner) Clean(rngs [][2]uint64, t shared.DataType) error {
|
||||
tx, err := c.db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, rng := range rngs {
|
||||
logrus.Infof("btc db cleaner cleaning up block range %d to %d", rng[0], rng[1])
|
||||
if err := c.clean(tx, rng, t); err != nil {
|
||||
shared.Rollback(tx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Infof("btc db cleaner vacuum analyzing cleaned tables to free up space from deleted rows")
|
||||
return c.vacuumAnalyze(t)
|
||||
}
|
||||
|
||||
func (c *Cleaner) clean(tx *sqlx.Tx, rng [2]uint64, t shared.DataType) error {
|
||||
switch t {
|
||||
case shared.Full, shared.Headers:
|
||||
return c.cleanFull(tx, rng)
|
||||
case shared.Transactions:
|
||||
if err := c.cleanTransactionIPLDs(tx, rng); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.cleanTransactionMetaData(tx, rng)
|
||||
default:
|
||||
return fmt.Errorf("btc cleaner unrecognized type: %s", t.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumAnalyze(t shared.DataType) error {
|
||||
switch t {
|
||||
case shared.Full, shared.Headers:
|
||||
if err := c.vacuumHeaders(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.vacuumTxs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.vacuumTxInputs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.vacuumTxOutputs(); err != nil {
|
||||
return err
|
||||
}
|
||||
case shared.Transactions:
|
||||
if err := c.vacuumTxs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.vacuumTxInputs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.vacuumTxOutputs(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("btc cleaner unrecognized type: %s", t.String())
|
||||
}
|
||||
return c.vacuumIPLDs()
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumHeaders() error {
|
||||
_, err := c.db.Exec(`VACUUM ANALYZE btc.header_cids`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumTxs() error {
|
||||
_, err := c.db.Exec(`VACUUM ANALYZE btc.transaction_cids`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumTxInputs() error {
|
||||
_, err := c.db.Exec(`VACUUM ANALYZE btc.tx_inputs`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumTxOutputs() error {
|
||||
_, err := c.db.Exec(`VACUUM ANALYZE btc.tx_outputs`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) vacuumIPLDs() error {
|
||||
_, err := c.db.Exec(`VACUUM ANALYZE public.blocks`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) cleanFull(tx *sqlx.Tx, rng [2]uint64) error {
|
||||
if err := c.cleanTransactionIPLDs(tx, rng); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.cleanHeaderIPLDs(tx, rng); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.cleanHeaderMetaData(tx, rng)
|
||||
}
|
||||
|
||||
func (c *Cleaner) cleanTransactionIPLDs(tx *sqlx.Tx, rng [2]uint64) error {
|
||||
pgStr := `DELETE FROM public.blocks A
|
||||
USING btc.transaction_cids B, btc.header_cids C
|
||||
WHERE A.key = B.cid
|
||||
AND B.header_id = C.id
|
||||
AND C.block_number BETWEEN $1 AND $2`
|
||||
_, err := tx.Exec(pgStr, rng[0], rng[1])
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) cleanTransactionMetaData(tx *sqlx.Tx, rng [2]uint64) error {
|
||||
pgStr := `DELETE FROM btc.transaction_cids A
|
||||
USING btc.header_cids B
|
||||
WHERE A.header_id = B.id
|
||||
AND B.block_number BETWEEN $1 AND $2`
|
||||
_, err := tx.Exec(pgStr, rng[0], rng[1])
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) cleanHeaderIPLDs(tx *sqlx.Tx, rng [2]uint64) error {
|
||||
pgStr := `DELETE FROM public.blocks A
|
||||
USING btc.header_cids B
|
||||
WHERE A.key = B.cid
|
||||
AND B.block_number BETWEEN $1 AND $2`
|
||||
_, err := tx.Exec(pgStr, rng[0], rng[1])
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Cleaner) cleanHeaderMetaData(tx *sqlx.Tx, rng [2]uint64) error {
|
||||
pgStr := `DELETE FROM btc.header_cids
|
||||
WHERE block_number BETWEEN $1 AND $2`
|
||||
_, err := tx.Exec(pgStr, rng[0], rng[1])
|
||||
return err
|
||||
}
|
||||
@@ -1,340 +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 btc_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
// Block 0
|
||||
// header variables
|
||||
blockHash1 = crypto.Keccak256Hash([]byte{00, 02})
|
||||
blocKNumber1 = big.NewInt(0)
|
||||
headerCid1 = "mockHeader1CID"
|
||||
parentHash = crypto.Keccak256Hash([]byte{00, 01})
|
||||
headerModel1 = btc.HeaderModel{
|
||||
BlockHash: blockHash1.String(),
|
||||
BlockNumber: blocKNumber1.String(),
|
||||
ParentHash: parentHash.String(),
|
||||
CID: headerCid1,
|
||||
}
|
||||
|
||||
// tx variables
|
||||
tx1CID = "mockTx1CID"
|
||||
tx2CID = "mockTx2CID"
|
||||
tx1Hash = crypto.Keccak256Hash([]byte{01, 01})
|
||||
tx2Hash = crypto.Keccak256Hash([]byte{01, 02})
|
||||
opHash = crypto.Keccak256Hash([]byte{02, 01})
|
||||
txModels1 = []btc.TxModelWithInsAndOuts{
|
||||
{
|
||||
Index: 0,
|
||||
CID: tx1CID,
|
||||
TxHash: tx1Hash.String(),
|
||||
SegWit: true,
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
TxWitness: []string{"mockWitness"},
|
||||
SignatureScript: []byte{01},
|
||||
PreviousOutPointIndex: 0,
|
||||
PreviousOutPointHash: opHash.String(),
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 50000000,
|
||||
PkScript: []byte{02},
|
||||
ScriptClass: 0,
|
||||
RequiredSigs: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
CID: tx2CID,
|
||||
TxHash: tx2Hash.String(),
|
||||
SegWit: true,
|
||||
},
|
||||
}
|
||||
mockCIDPayload1 = &btc.CIDPayload{
|
||||
HeaderCID: headerModel1,
|
||||
TransactionCIDs: txModels1,
|
||||
}
|
||||
|
||||
// Block 1
|
||||
// header variables
|
||||
blockHash2 = crypto.Keccak256Hash([]byte{00, 03})
|
||||
blocKNumber2 = big.NewInt(1)
|
||||
headerCid2 = "mockHeaderCID2"
|
||||
headerModel2 = btc.HeaderModel{
|
||||
BlockNumber: blocKNumber2.String(),
|
||||
BlockHash: blockHash2.String(),
|
||||
ParentHash: blockHash1.String(),
|
||||
CID: headerCid2,
|
||||
}
|
||||
|
||||
// tx variables
|
||||
tx3CID = "mockTx3CID"
|
||||
tx3Hash = crypto.Keccak256Hash([]byte{01, 03})
|
||||
txModels2 = []btc.TxModelWithInsAndOuts{
|
||||
{
|
||||
Index: 0,
|
||||
CID: tx3CID,
|
||||
TxHash: tx3Hash.String(),
|
||||
SegWit: true,
|
||||
},
|
||||
}
|
||||
mockCIDPayload2 = &btc.CIDPayload{
|
||||
HeaderCID: headerModel2,
|
||||
TransactionCIDs: txModels2,
|
||||
}
|
||||
rngs = [][2]uint64{{0, 1}}
|
||||
cids = []string{
|
||||
headerCid1,
|
||||
headerCid2,
|
||||
tx1CID,
|
||||
tx2CID,
|
||||
tx3CID,
|
||||
}
|
||||
mockData = []byte{'\x01'}
|
||||
)
|
||||
|
||||
var _ = Describe("Cleaner", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
repo *btc.CIDIndexer
|
||||
cleaner *btc.Cleaner
|
||||
)
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
repo = btc.NewCIDIndexer(db)
|
||||
cleaner = btc.NewCleaner(db)
|
||||
})
|
||||
|
||||
Describe("Clean", func() {
|
||||
BeforeEach(func() {
|
||||
err := repo.Index(mockCIDPayload1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = repo.Index(mockCIDPayload2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, cid := range cids {
|
||||
_, err = db.Exec(`INSERT INTO public.blocks (key, data) VALUES ($1, $2)`, cid, mockData)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
tx, err := db.Beginx()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var startingIPFSBlocksCount int
|
||||
pgStr := `SELECT COUNT(*) FROM public.blocks`
|
||||
err = tx.Get(&startingIPFSBlocksCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var startingTxCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.transaction_cids`
|
||||
err = tx.Get(&startingTxCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var startingHeaderCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.header_cids`
|
||||
err = tx.Get(&startingHeaderCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Commit()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(startingIPFSBlocksCount).To(Equal(5))
|
||||
Expect(startingTxCount).To(Equal(3))
|
||||
Expect(startingHeaderCount).To(Equal(2))
|
||||
})
|
||||
AfterEach(func() {
|
||||
btc.TearDownDB(db)
|
||||
})
|
||||
It("Cleans everything", func() {
|
||||
err := cleaner.Clean(rngs, shared.Full)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tx, err := db.Beginx()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txCount int
|
||||
pgStr := `SELECT COUNT(*) FROM btc.transaction_cids`
|
||||
err = tx.Get(&txCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txInCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_inputs`
|
||||
err = tx.Get(&txInCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txOutCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_outputs`
|
||||
err = tx.Get(&txOutCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var headerCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.header_cids`
|
||||
err = tx.Get(&headerCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var blocksCount int
|
||||
pgStr = `SELECT COUNT(*) FROM public.blocks`
|
||||
err = tx.Get(&blocksCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Commit()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(blocksCount).To(Equal(0))
|
||||
Expect(txCount).To(Equal(0))
|
||||
Expect(txInCount).To(Equal(0))
|
||||
Expect(txOutCount).To(Equal(0))
|
||||
Expect(headerCount).To(Equal(0))
|
||||
})
|
||||
It("Cleans headers and all linked data", func() {
|
||||
err := cleaner.Clean(rngs, shared.Headers)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tx, err := db.Beginx()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txCount int
|
||||
pgStr := `SELECT COUNT(*) FROM btc.transaction_cids`
|
||||
err = tx.Get(&txCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txInCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_inputs`
|
||||
err = tx.Get(&txInCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txOutCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_outputs`
|
||||
err = tx.Get(&txOutCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var headerCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.header_cids`
|
||||
err = tx.Get(&headerCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var blocksCount int
|
||||
pgStr = `SELECT COUNT(*) FROM public.blocks`
|
||||
err = tx.Get(&blocksCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Commit()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(blocksCount).To(Equal(0))
|
||||
Expect(txCount).To(Equal(0))
|
||||
Expect(txInCount).To(Equal(0))
|
||||
Expect(txOutCount).To(Equal(0))
|
||||
Expect(headerCount).To(Equal(0))
|
||||
})
|
||||
It("Cleans transactions", func() {
|
||||
err := cleaner.Clean(rngs, shared.Transactions)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tx, err := db.Beginx()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txCount int
|
||||
pgStr := `SELECT COUNT(*) FROM btc.transaction_cids`
|
||||
err = tx.Get(&txCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txInCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_inputs`
|
||||
err = tx.Get(&txInCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var txOutCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.tx_outputs`
|
||||
err = tx.Get(&txOutCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var headerCount int
|
||||
pgStr = `SELECT COUNT(*) FROM btc.header_cids`
|
||||
err = tx.Get(&headerCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var blocksCount int
|
||||
pgStr = `SELECT COUNT(*) FROM public.blocks`
|
||||
err = tx.Get(&blocksCount, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Commit()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(blocksCount).To(Equal(2))
|
||||
Expect(txCount).To(Equal(0))
|
||||
Expect(txInCount).To(Equal(0))
|
||||
Expect(txOutCount).To(Equal(0))
|
||||
Expect(headerCount).To(Equal(2))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("ResetValidation", func() {
|
||||
BeforeEach(func() {
|
||||
err := repo.Index(mockCIDPayload1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = repo.Index(mockCIDPayload2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var validationTimes []int
|
||||
pgStr := `SELECT times_validated FROM btc.header_cids`
|
||||
err = db.Select(&validationTimes, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(validationTimes)).To(Equal(2))
|
||||
Expect(validationTimes[0]).To(Equal(1))
|
||||
Expect(validationTimes[1]).To(Equal(1))
|
||||
|
||||
err = repo.Index(mockCIDPayload1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
validationTimes = []int{}
|
||||
pgStr = `SELECT times_validated FROM btc.header_cids ORDER BY block_number`
|
||||
err = db.Select(&validationTimes, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(validationTimes)).To(Equal(2))
|
||||
Expect(validationTimes[0]).To(Equal(2))
|
||||
Expect(validationTimes[1]).To(Equal(1))
|
||||
})
|
||||
AfterEach(func() {
|
||||
btc.TearDownDB(db)
|
||||
})
|
||||
It("Resets the validation level", func() {
|
||||
err := cleaner.ResetValidation(rngs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var validationTimes []int
|
||||
pgStr := `SELECT times_validated FROM btc.header_cids`
|
||||
err = db.Select(&validationTimes, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(validationTimes)).To(Equal(2))
|
||||
Expect(validationTimes[0]).To(Equal(0))
|
||||
Expect(validationTimes[1]).To(Equal(0))
|
||||
|
||||
err = repo.Index(mockCIDPayload2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
validationTimes = []int{}
|
||||
pgStr = `SELECT times_validated FROM btc.header_cids ORDER BY block_number`
|
||||
err = db.Select(&validationTimes, pgStr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(validationTimes)).To(Equal(2))
|
||||
Expect(validationTimes[0]).To(Equal(0))
|
||||
Expect(validationTimes[1]).To(Equal(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,102 +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 btc
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// PayloadConverter satisfies the PayloadConverter interface for bitcoin
|
||||
type PayloadConverter struct {
|
||||
chainConfig *chaincfg.Params
|
||||
}
|
||||
|
||||
// NewPayloadConverter creates a pointer to a new PayloadConverter which satisfies the PayloadConverter interface
|
||||
func NewPayloadConverter(chainConfig *chaincfg.Params) *PayloadConverter {
|
||||
return &PayloadConverter{
|
||||
chainConfig: chainConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// Convert method is used to convert a bitcoin BlockPayload to an IPLDPayload
|
||||
// Satisfies the shared.PayloadConverter interface
|
||||
func (pc *PayloadConverter) Convert(payload shared.RawChainData) (shared.ConvertedData, error) {
|
||||
btcBlockPayload, ok := payload.(BlockPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("btc converter: expected payload type %T got %T", BlockPayload{}, payload)
|
||||
}
|
||||
txMeta := make([]TxModelWithInsAndOuts, len(btcBlockPayload.Txs))
|
||||
for i, tx := range btcBlockPayload.Txs {
|
||||
txModel := TxModelWithInsAndOuts{
|
||||
TxHash: tx.Hash().String(),
|
||||
Index: int64(i),
|
||||
SegWit: tx.HasWitness(),
|
||||
TxOutputs: make([]TxOutput, len(tx.MsgTx().TxOut)),
|
||||
TxInputs: make([]TxInput, len(tx.MsgTx().TxIn)),
|
||||
}
|
||||
if tx.HasWitness() {
|
||||
txModel.WitnessHash = tx.WitnessHash().String()
|
||||
}
|
||||
for i, in := range tx.MsgTx().TxIn {
|
||||
txModel.TxInputs[i] = TxInput{
|
||||
Index: int64(i),
|
||||
SignatureScript: in.SignatureScript,
|
||||
PreviousOutPointHash: in.PreviousOutPoint.Hash.String(),
|
||||
PreviousOutPointIndex: in.PreviousOutPoint.Index,
|
||||
TxWitness: convertBytesToHexArray(in.Witness),
|
||||
}
|
||||
}
|
||||
for i, out := range tx.MsgTx().TxOut {
|
||||
scriptClass, addresses, numberOfSigs, err := txscript.ExtractPkScriptAddrs(out.PkScript, pc.chainConfig)
|
||||
// if we receive an error but the txscript type isn't NonStandardTy then something went wrong
|
||||
if err != nil && scriptClass != txscript.NonStandardTy {
|
||||
return nil, err
|
||||
}
|
||||
stringAddrs := make([]string, len(addresses))
|
||||
for i, addr := range addresses {
|
||||
stringAddrs[i] = addr.EncodeAddress()
|
||||
}
|
||||
txModel.TxOutputs[i] = TxOutput{
|
||||
Index: int64(i),
|
||||
Value: out.Value,
|
||||
PkScript: out.PkScript,
|
||||
RequiredSigs: int64(numberOfSigs),
|
||||
ScriptClass: uint8(scriptClass),
|
||||
Addresses: stringAddrs,
|
||||
}
|
||||
}
|
||||
txMeta[i] = txModel
|
||||
}
|
||||
return ConvertedPayload{
|
||||
BlockPayload: btcBlockPayload,
|
||||
TxMetaData: txMeta,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertBytesToHexArray(bytea [][]byte) []string {
|
||||
var strs []string
|
||||
for _, b := range bytea {
|
||||
strs = append(strs, hex.EncodeToString(b))
|
||||
}
|
||||
return strs
|
||||
}
|
||||
@@ -1,43 +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 btc_test
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc/mocks"
|
||||
)
|
||||
|
||||
var _ = Describe("Converter", func() {
|
||||
Describe("Convert", func() {
|
||||
It("Converts mock BlockPayloads into the expected IPLDPayloads", func() {
|
||||
converter := btc.NewPayloadConverter(&chaincfg.MainNetParams)
|
||||
payload, err := converter.Convert(mocks.MockBlockPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
convertedPayload, ok := payload.(btc.ConvertedPayload)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(convertedPayload).To(Equal(mocks.MockConvertedPayload))
|
||||
Expect(convertedPayload.BlockHeight).To(Equal(mocks.MockBlockHeight))
|
||||
Expect(convertedPayload.Header).To(Equal(&mocks.MockBlock.Header))
|
||||
Expect(convertedPayload.Txs).To(Equal(mocks.MockTransactions))
|
||||
Expect(convertedPayload.TxMetaData).To(Equal(mocks.MockTxsMetaData))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,159 +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 btc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/multiformats/go-multihash"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/ipld"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// ResponseFilterer satisfies the ResponseFilterer interface for bitcoin
|
||||
type ResponseFilterer struct{}
|
||||
|
||||
// NewResponseFilterer creates a new Filterer satisfying the ResponseFilterer interface
|
||||
func NewResponseFilterer() *ResponseFilterer {
|
||||
return &ResponseFilterer{}
|
||||
}
|
||||
|
||||
// Filter is used to filter through btc data to extract and package requested data into a Payload
|
||||
func (s *ResponseFilterer) Filter(filter shared.SubscriptionSettings, payload shared.ConvertedData) (shared.IPLDs, error) {
|
||||
btcFilters, ok := filter.(*SubscriptionSettings)
|
||||
if !ok {
|
||||
return IPLDs{}, fmt.Errorf("btc filterer expected filter type %T got %T", &SubscriptionSettings{}, filter)
|
||||
}
|
||||
btcPayload, ok := payload.(ConvertedPayload)
|
||||
if !ok {
|
||||
return IPLDs{}, fmt.Errorf("btc filterer expected payload type %T got %T", ConvertedPayload{}, payload)
|
||||
}
|
||||
height := int64(btcPayload.BlockPayload.BlockHeight)
|
||||
if checkRange(btcFilters.Start.Int64(), btcFilters.End.Int64(), height) {
|
||||
response := new(IPLDs)
|
||||
if err := s.filterHeaders(btcFilters.HeaderFilter, response, btcPayload); err != nil {
|
||||
return IPLDs{}, err
|
||||
}
|
||||
if err := s.filterTransactions(btcFilters.TxFilter, response, btcPayload); err != nil {
|
||||
return IPLDs{}, err
|
||||
}
|
||||
response.BlockNumber = big.NewInt(height)
|
||||
return *response, nil
|
||||
}
|
||||
return IPLDs{}, nil
|
||||
}
|
||||
|
||||
func (s *ResponseFilterer) filterHeaders(headerFilter HeaderFilter, response *IPLDs, payload ConvertedPayload) error {
|
||||
if !headerFilter.Off {
|
||||
headerBuffer := new(bytes.Buffer)
|
||||
if err := payload.Header.Serialize(headerBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
data := headerBuffer.Bytes()
|
||||
cid, err := ipld.RawdataToCid(ipld.MBitcoinHeader, data, multihash.DBL_SHA2_256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response.Header = ipfs.BlockModel{
|
||||
Data: data,
|
||||
CID: cid.String(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRange(start, end, actual int64) bool {
|
||||
if (end <= 0 || end >= actual) && start <= actual {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *ResponseFilterer) filterTransactions(trxFilter TxFilter, response *IPLDs, payload ConvertedPayload) error {
|
||||
if !trxFilter.Off {
|
||||
response.Transactions = make([]ipfs.BlockModel, 0, len(payload.TxMetaData))
|
||||
for i, txMeta := range payload.TxMetaData {
|
||||
if checkTransaction(txMeta, trxFilter) {
|
||||
trxBuffer := new(bytes.Buffer)
|
||||
if err := payload.Txs[i].MsgTx().Serialize(trxBuffer); err != nil {
|
||||
return err
|
||||
}
|
||||
data := trxBuffer.Bytes()
|
||||
cid, err := ipld.RawdataToCid(ipld.MBitcoinTx, data, multihash.DBL_SHA2_256)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
response.Transactions = append(response.Transactions, ipfs.BlockModel{
|
||||
Data: data,
|
||||
CID: cid.String(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkTransaction returns true if the provided transaction has a hit on the filter
|
||||
func checkTransaction(txMeta TxModelWithInsAndOuts, txFilter TxFilter) bool {
|
||||
passesSegwitFilter := false
|
||||
if !txFilter.Segwit || (txFilter.Segwit && txMeta.SegWit) {
|
||||
passesSegwitFilter = true
|
||||
}
|
||||
passesMultiSigFilter := !txFilter.MultiSig
|
||||
if txFilter.MultiSig {
|
||||
for _, out := range txMeta.TxOutputs {
|
||||
if out.RequiredSigs > 1 {
|
||||
passesMultiSigFilter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
passesWitnessFilter := len(txFilter.WitnessHashes) == 0
|
||||
for _, wantedWitnessHash := range txFilter.WitnessHashes {
|
||||
if wantedWitnessHash == txMeta.WitnessHash {
|
||||
passesWitnessFilter = true
|
||||
}
|
||||
}
|
||||
passesAddressFilter := len(txFilter.Addresses) == 0
|
||||
for _, wantedAddress := range txFilter.Addresses {
|
||||
for _, out := range txMeta.TxOutputs {
|
||||
for _, actualAddress := range out.Addresses {
|
||||
if wantedAddress == actualAddress {
|
||||
passesAddressFilter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
passesIndexFilter := len(txFilter.Indexes) == 0
|
||||
for _, wantedIndex := range txFilter.Indexes {
|
||||
if wantedIndex == txMeta.Index {
|
||||
passesIndexFilter = true
|
||||
}
|
||||
}
|
||||
passesPkScriptClassFilter := len(txFilter.PkScriptClasses) == 0
|
||||
for _, wantedPkScriptClass := range txFilter.PkScriptClasses {
|
||||
for _, out := range txMeta.TxOutputs {
|
||||
if out.ScriptClass == wantedPkScriptClass {
|
||||
passesPkScriptClassFilter = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return passesSegwitFilter && passesMultiSigFilter && passesWitnessFilter && passesAddressFilter && passesIndexFilter && passesPkScriptClassFilter
|
||||
}
|
||||
@@ -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 btc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// HTTPPayloadStreamer satisfies the PayloadStreamer interface for bitcoin over http endpoints (since bitcoin core doesn't support websockets)
|
||||
type HTTPPayloadStreamer struct {
|
||||
Config *rpcclient.ConnConfig
|
||||
lastHash []byte
|
||||
}
|
||||
|
||||
// NewHTTPPayloadStreamer creates a pointer to a new PayloadStreamer which satisfies the PayloadStreamer interface for bitcoin
|
||||
func NewHTTPPayloadStreamer(clientConfig *rpcclient.ConnConfig) *HTTPPayloadStreamer {
|
||||
return &HTTPPayloadStreamer{
|
||||
Config: clientConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// Stream is the main loop for subscribing to data from the btc block notifications
|
||||
// Satisfies the shared.PayloadStreamer interface
|
||||
func (ps *HTTPPayloadStreamer) Stream(payloadChan chan shared.RawChainData) (shared.ClientSubscription, error) {
|
||||
logrus.Debug("streaming block payloads from btc")
|
||||
client, err := rpcclient.New(ps.Config, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
for {
|
||||
// start at
|
||||
select {
|
||||
case <-ticker.C:
|
||||
height, err := client.GetBlockCount()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
blockHash, err := client.GetBlockHash(height)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
blockHashBytes := blockHash.CloneBytes()
|
||||
if bytes.Equal(blockHashBytes, ps.lastHash) {
|
||||
continue
|
||||
}
|
||||
block, err := client.GetBlock(blockHash)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
ps.lastHash = blockHashBytes
|
||||
payloadChan <- BlockPayload{
|
||||
Header: &block.Header,
|
||||
BlockHeight: height,
|
||||
Txs: msgTxsToUtilTxs(block.Transactions),
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}()
|
||||
return &HTTPClientSubscription{client: client, errChan: errChan}, nil
|
||||
}
|
||||
|
||||
// HTTPClientSubscription is a wrapper around the underlying bitcoind rpc client
|
||||
// to fit the shared.ClientSubscription interface
|
||||
type HTTPClientSubscription struct {
|
||||
client *rpcclient.Client
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// Unsubscribe satisfies the rpc.Subscription interface
|
||||
func (bcs *HTTPClientSubscription) Unsubscribe() {
|
||||
bcs.client.Shutdown()
|
||||
}
|
||||
|
||||
// Err() satisfies the rpc.Subscription interface
|
||||
func (bcs *HTTPClientSubscription) Err() <-chan error {
|
||||
return bcs.errChan
|
||||
}
|
||||
@@ -1,132 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
type CIDIndexer struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
func NewCIDIndexer(db *postgres.DB) *CIDIndexer {
|
||||
return &CIDIndexer{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) Index(cids shared.CIDsForIndexing) error {
|
||||
cidWrapper, ok := cids.(*CIDPayload)
|
||||
if !ok {
|
||||
return fmt.Errorf("btc indexer expected cids type %T got %T", &CIDPayload{}, cids)
|
||||
}
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := in.db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
headerID, err := in.indexHeaderCID(tx, cidWrapper.HeaderCID)
|
||||
if err != nil {
|
||||
logrus.Error("btc indexer error when indexing header")
|
||||
return err
|
||||
}
|
||||
err = in.indexTransactionCIDs(tx, cidWrapper.TransactionCIDs, headerID)
|
||||
if err != nil {
|
||||
logrus.Error("btc indexer error when indexing transactions")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) indexHeaderCID(tx *sqlx.Tx, header HeaderModel) (int64, error) {
|
||||
var headerID int64
|
||||
err := tx.QueryRowx(`INSERT INTO btc.header_cids (block_number, block_hash, parent_hash, cid, timestamp, bits, node_id, times_validated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (block_number, block_hash) DO UPDATE SET (parent_hash, cid, timestamp, bits, node_id, times_validated) = ($3, $4, $5, $6, $7, btc.header_cids.times_validated + 1)
|
||||
RETURNING id`,
|
||||
header.BlockNumber, header.BlockHash, header.ParentHash, header.CID, header.Timestamp, header.Bits, in.db.NodeID, 1).Scan(&headerID)
|
||||
return headerID, err
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) indexTransactionCIDs(tx *sqlx.Tx, transactions []TxModelWithInsAndOuts, headerID int64) error {
|
||||
for _, transaction := range transactions {
|
||||
txID, err := in.indexTransactionCID(tx, transaction, headerID)
|
||||
if err != nil {
|
||||
logrus.Error("btc indexer error when indexing header")
|
||||
return err
|
||||
}
|
||||
for _, input := range transaction.TxInputs {
|
||||
if err := in.indexTxInput(tx, input, txID); err != nil {
|
||||
logrus.Error("btc indexer error when indexing tx inputs")
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, output := range transaction.TxOutputs {
|
||||
if err := in.indexTxOutput(tx, output, txID); err != nil {
|
||||
logrus.Error("btc indexer error when indexing tx outputs")
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) indexTransactionCID(tx *sqlx.Tx, transaction TxModelWithInsAndOuts, headerID int64) (int64, error) {
|
||||
var txID int64
|
||||
err := tx.QueryRowx(`INSERT INTO btc.transaction_cids (header_id, tx_hash, index, cid, segwit, witness_hash)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (tx_hash) DO UPDATE SET (header_id, index, cid, segwit, witness_hash) = ($1, $3, $4, $5, $6)
|
||||
RETURNING id`,
|
||||
headerID, transaction.TxHash, transaction.Index, transaction.CID, transaction.SegWit, transaction.WitnessHash).Scan(&txID)
|
||||
return txID, err
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) indexTxInput(tx *sqlx.Tx, txInput TxInput, txID int64) error {
|
||||
_, err := tx.Exec(`INSERT INTO btc.tx_inputs (tx_id, index, witness, sig_script, outpoint_tx_hash, outpoint_index)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
ON CONFLICT (tx_id, index) DO UPDATE SET (witness, sig_script, outpoint_tx_hash, outpoint_index) = ($3, $4, $5, $6)`,
|
||||
txID, txInput.Index, pq.Array(txInput.TxWitness), txInput.SignatureScript, txInput.PreviousOutPointHash, txInput.PreviousOutPointIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
func (in *CIDIndexer) indexTxOutput(tx *sqlx.Tx, txOuput TxOutput, txID int64) error {
|
||||
_, err := tx.Exec(`INSERT INTO btc.tx_outputs (tx_id, index, value, pk_script, script_class, addresses, required_sigs)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
ON CONFLICT (tx_id, index) DO UPDATE SET (value, pk_script, script_class, addresses, required_sigs) = ($3, $4, $5, $6, $7)`,
|
||||
txID, txOuput.Index, txOuput.Value, txOuput.PkScript, txOuput.ScriptClass, txOuput.Addresses, txOuput.RequiredSigs)
|
||||
return err
|
||||
}
|
||||
@@ -1,87 +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 btc_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc/mocks"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
var _ = Describe("Indexer", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
err error
|
||||
repo *btc.CIDIndexer
|
||||
)
|
||||
BeforeEach(func() {
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
repo = btc.NewCIDIndexer(db)
|
||||
})
|
||||
AfterEach(func() {
|
||||
btc.TearDownDB(db)
|
||||
})
|
||||
|
||||
Describe("Index", func() {
|
||||
It("Indexes CIDs and related metadata into vulcanizedb", func() {
|
||||
err = repo.Index(&mocks.MockCIDPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
pgStr := `SELECT * FROM btc.header_cids
|
||||
WHERE block_number = $1`
|
||||
// check header was properly indexed
|
||||
header := new(btc.HeaderModel)
|
||||
err = db.Get(header, pgStr, mocks.MockHeaderMetaData.BlockNumber)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(header.CID).To(Equal(mocks.MockHeaderMetaData.CID))
|
||||
Expect(header.BlockNumber).To(Equal(mocks.MockHeaderMetaData.BlockNumber))
|
||||
Expect(header.Bits).To(Equal(mocks.MockHeaderMetaData.Bits))
|
||||
Expect(header.Timestamp).To(Equal(mocks.MockHeaderMetaData.Timestamp))
|
||||
Expect(header.BlockHash).To(Equal(mocks.MockHeaderMetaData.BlockHash))
|
||||
Expect(header.ParentHash).To(Equal(mocks.MockHeaderMetaData.ParentHash))
|
||||
// check trxs were properly indexed
|
||||
trxs := make([]btc.TxModel, 0)
|
||||
pgStr = `SELECT transaction_cids.id, transaction_cids.header_id, transaction_cids.index,
|
||||
transaction_cids.tx_hash, transaction_cids.cid, transaction_cids.segwit, transaction_cids.witness_hash
|
||||
FROM btc.transaction_cids INNER JOIN btc.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
WHERE header_cids.block_number = $1`
|
||||
err = db.Select(&trxs, pgStr, mocks.MockHeaderMetaData.BlockNumber)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(trxs)).To(Equal(3))
|
||||
for _, tx := range trxs {
|
||||
Expect(tx.SegWit).To(Equal(false))
|
||||
Expect(tx.HeaderID).To(Equal(header.ID))
|
||||
Expect(tx.WitnessHash).To(Equal(""))
|
||||
switch tx.Index {
|
||||
case 0:
|
||||
Expect(tx.CID).To(Equal("mockTrxCID1"))
|
||||
Expect(tx.TxHash).To(Equal(mocks.MockBlock.Transactions[0].TxHash().String()))
|
||||
case 1:
|
||||
Expect(tx.CID).To(Equal("mockTrxCID2"))
|
||||
Expect(tx.TxHash).To(Equal(mocks.MockBlock.Transactions[1].TxHash().String()))
|
||||
case 2:
|
||||
Expect(tx.CID).To(Equal("mockTrxCID3"))
|
||||
Expect(tx.TxHash).To(Equal(mocks.MockBlock.Transactions[2].TxHash().String()))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,135 +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 btc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ipfs/go-block-format"
|
||||
"github.com/ipfs/go-blockservice"
|
||||
"github.com/ipfs/go-cid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
errUnexpectedNumberOfIPLDs = errors.New("ipfs batch fetch returned unexpected number of IPLDs")
|
||||
)
|
||||
|
||||
// IPLDFetcher satisfies the IPLDFetcher interface for ethereum
|
||||
type IPLDFetcher struct {
|
||||
BlockService blockservice.BlockService
|
||||
}
|
||||
|
||||
// NewIPLDFetcher creates a pointer to a new IPLDFetcher
|
||||
// It interfaces with PG-IPFS through an internalized IPFS node interface
|
||||
func NewIPLDFetcher(ipfsPath string) (*IPLDFetcher, error) {
|
||||
blockService, err := ipfs.InitIPFSBlockService(ipfsPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IPLDFetcher{
|
||||
BlockService: blockService,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fetch is the exported method for fetching and returning all the IPLDS specified in the CIDWrapper
|
||||
func (f *IPLDFetcher) Fetch(cids shared.CIDsForFetching) (shared.IPLDs, error) {
|
||||
cidWrapper, ok := cids.(*CIDWrapper)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("btc fetcher: expected cids type %T got %T", &CIDWrapper{}, cids)
|
||||
}
|
||||
log.Debug("fetching iplds")
|
||||
iplds := IPLDs{}
|
||||
iplds.BlockNumber = cidWrapper.BlockNumber
|
||||
var err error
|
||||
iplds.Header, err = f.FetchHeader(cidWrapper.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iplds.Transactions, err = f.FetchTrxs(cidWrapper.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iplds, nil
|
||||
}
|
||||
|
||||
// FetchHeaders fetches headers
|
||||
// It uses the f.fetch method
|
||||
func (f *IPLDFetcher) FetchHeader(c HeaderModel) (ipfs.BlockModel, error) {
|
||||
log.Debug("fetching header ipld")
|
||||
dc, err := cid.Decode(c.CID)
|
||||
if err != nil {
|
||||
return ipfs.BlockModel{}, err
|
||||
}
|
||||
header, err := f.fetch(dc)
|
||||
if err != nil {
|
||||
return ipfs.BlockModel{}, err
|
||||
}
|
||||
return ipfs.BlockModel{
|
||||
Data: header.RawData(),
|
||||
CID: header.Cid().String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FetchTrxs fetches transactions
|
||||
// It uses the f.fetchBatch method
|
||||
func (f *IPLDFetcher) FetchTrxs(cids []TxModel) ([]ipfs.BlockModel, error) {
|
||||
log.Debug("fetching transaction iplds")
|
||||
trxCids := make([]cid.Cid, len(cids))
|
||||
for i, c := range cids {
|
||||
dc, err := cid.Decode(c.CID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trxCids[i] = dc
|
||||
}
|
||||
trxs := f.fetchBatch(trxCids)
|
||||
trxIPLDs := make([]ipfs.BlockModel, len(trxs))
|
||||
for i, trx := range trxs {
|
||||
trxIPLDs[i] = ipfs.BlockModel{
|
||||
Data: trx.RawData(),
|
||||
CID: trx.Cid().String(),
|
||||
}
|
||||
}
|
||||
if len(trxIPLDs) != len(trxCids) {
|
||||
log.Errorf("ipfs fetcher: number of transaction blocks returned (%d) does not match number expected (%d)", len(trxs), len(trxCids))
|
||||
return trxIPLDs, errUnexpectedNumberOfIPLDs
|
||||
}
|
||||
return trxIPLDs, nil
|
||||
}
|
||||
|
||||
// fetch is used to fetch a single cid
|
||||
func (f *IPLDFetcher) fetch(cid cid.Cid) (blocks.Block, error) {
|
||||
return f.BlockService.GetBlock(context.Background(), cid)
|
||||
}
|
||||
|
||||
// fetchBatch is used to fetch a batch of IPFS data blocks by cid
|
||||
// There is no guarantee all are fetched, and no error in such a case, so
|
||||
// downstream we will need to confirm which CIDs were fetched in the result set
|
||||
func (f *IPLDFetcher) fetchBatch(cids []cid.Cid) []blocks.Block {
|
||||
fetchedBlocks := make([]blocks.Block, 0, len(cids))
|
||||
blockChan := f.BlockService.GetBlocks(context.Background(), cids)
|
||||
for block := range blockChan {
|
||||
fetchedBlocks = append(fetchedBlocks, block)
|
||||
}
|
||||
return fetchedBlocks
|
||||
}
|
||||
@@ -1,107 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// IPLDPGFetcher satisfies the IPLDFetcher interface for ethereum
|
||||
// it interfaces directly with PG-IPFS instead of going through a node-interface or remote node
|
||||
type IPLDPGFetcher struct {
|
||||
db *postgres.DB
|
||||
}
|
||||
|
||||
// NewIPLDPGFetcher creates a pointer to a new IPLDPGFetcher
|
||||
func NewIPLDPGFetcher(db *postgres.DB) *IPLDPGFetcher {
|
||||
return &IPLDPGFetcher{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch is the exported method for fetching and returning all the IPLDS specified in the CIDWrapper
|
||||
func (f *IPLDPGFetcher) Fetch(cids shared.CIDsForFetching) (shared.IPLDs, error) {
|
||||
cidWrapper, ok := cids.(*CIDWrapper)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("btc fetcher: expected cids type %T got %T", &CIDWrapper{}, cids)
|
||||
}
|
||||
log.Debug("fetching iplds")
|
||||
iplds := IPLDs{}
|
||||
iplds.BlockNumber = cidWrapper.BlockNumber
|
||||
|
||||
tx, err := f.db.Beginx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
iplds.Header, err = f.FetchHeader(tx, cidWrapper.Header)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("btc pg fetcher: header fetching error: %s", err.Error())
|
||||
}
|
||||
iplds.Transactions, err = f.FetchTrxs(tx, cidWrapper.Transactions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("btc pg fetcher: transaction fetching error: %s", err.Error())
|
||||
}
|
||||
return iplds, err
|
||||
}
|
||||
|
||||
// FetchHeaders fetches headers
|
||||
func (f *IPLDPGFetcher) FetchHeader(tx *sqlx.Tx, c HeaderModel) (ipfs.BlockModel, error) {
|
||||
log.Debug("fetching header ipld")
|
||||
headerBytes, err := shared.FetchIPLD(tx, c.CID)
|
||||
if err != nil {
|
||||
return ipfs.BlockModel{}, err
|
||||
}
|
||||
return ipfs.BlockModel{
|
||||
Data: headerBytes,
|
||||
CID: c.CID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FetchTrxs fetches transactions
|
||||
func (f *IPLDPGFetcher) FetchTrxs(tx *sqlx.Tx, cids []TxModel) ([]ipfs.BlockModel, error) {
|
||||
log.Debug("fetching transaction iplds")
|
||||
trxIPLDs := make([]ipfs.BlockModel, len(cids))
|
||||
for i, c := range cids {
|
||||
trxBytes, err := shared.FetchIPLD(tx, c.CID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trxIPLDs[i] = ipfs.BlockModel{
|
||||
Data: trxBytes,
|
||||
CID: c.CID,
|
||||
}
|
||||
}
|
||||
return trxIPLDs, nil
|
||||
}
|
||||
@@ -1,64 +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 mocks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// PayloadConverter is the underlying struct for the Converter interface
|
||||
type PayloadConverter struct {
|
||||
PassedStatediffPayload btc.BlockPayload
|
||||
ReturnIPLDPayload btc.ConvertedPayload
|
||||
ReturnErr error
|
||||
}
|
||||
|
||||
// Convert method is used to convert a geth statediff.Payload to a IPLDPayload
|
||||
func (pc *PayloadConverter) Convert(payload shared.RawChainData) (shared.ConvertedData, error) {
|
||||
stateDiffPayload, ok := payload.(btc.BlockPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("convert expected payload type %T got %T", btc.BlockPayload{}, payload)
|
||||
}
|
||||
pc.PassedStatediffPayload = stateDiffPayload
|
||||
return pc.ReturnIPLDPayload, pc.ReturnErr
|
||||
}
|
||||
|
||||
// IterativePayloadConverter is the underlying struct for the Converter interface
|
||||
type IterativePayloadConverter struct {
|
||||
PassedStatediffPayload []btc.BlockPayload
|
||||
ReturnIPLDPayload []btc.ConvertedPayload
|
||||
ReturnErr error
|
||||
iteration int
|
||||
}
|
||||
|
||||
// Convert method is used to convert a geth statediff.Payload to a IPLDPayload
|
||||
func (pc *IterativePayloadConverter) Convert(payload shared.RawChainData) (shared.ConvertedData, error) {
|
||||
stateDiffPayload, ok := payload.(btc.BlockPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("convert expected payload type %T got %T", btc.BlockPayload{}, payload)
|
||||
}
|
||||
pc.PassedStatediffPayload = append(pc.PassedStatediffPayload, stateDiffPayload)
|
||||
if len(pc.PassedStatediffPayload) < pc.iteration+1 {
|
||||
return nil, fmt.Errorf("IterativePayloadConverter does not have a payload to return at iteration %d", pc.iteration)
|
||||
}
|
||||
returnPayload := pc.ReturnIPLDPayload[pc.iteration]
|
||||
pc.iteration++
|
||||
return returnPayload, pc.ReturnErr
|
||||
}
|
||||
@@ -1,65 +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 mocks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// IPLDPublisher is the underlying struct for the Publisher interface
|
||||
type IPLDPublisher struct {
|
||||
PassedIPLDPayload btc.ConvertedPayload
|
||||
ReturnCIDPayload *btc.CIDPayload
|
||||
ReturnErr error
|
||||
}
|
||||
|
||||
// Publish publishes an IPLDPayload to IPFS and returns the corresponding CIDPayload
|
||||
func (pub *IPLDPublisher) Publish(payload shared.ConvertedData) (shared.CIDsForIndexing, error) {
|
||||
ipldPayload, ok := payload.(btc.ConvertedPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("publish expected payload type %T got %T", &btc.ConvertedPayload{}, payload)
|
||||
}
|
||||
pub.PassedIPLDPayload = ipldPayload
|
||||
return pub.ReturnCIDPayload, pub.ReturnErr
|
||||
}
|
||||
|
||||
// IterativeIPLDPublisher is the underlying struct for the Publisher interface; used in testing
|
||||
type IterativeIPLDPublisher struct {
|
||||
PassedIPLDPayload []btc.ConvertedPayload
|
||||
ReturnCIDPayload []*btc.CIDPayload
|
||||
ReturnErr error
|
||||
iteration int
|
||||
}
|
||||
|
||||
// Publish publishes an IPLDPayload to IPFS and returns the corresponding CIDPayload
|
||||
func (pub *IterativeIPLDPublisher) Publish(payload shared.ConvertedData) (shared.CIDsForIndexing, error) {
|
||||
ipldPayload, ok := payload.(btc.ConvertedPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("publish expected payload type %T got %T", &btc.ConvertedPayload{}, payload)
|
||||
}
|
||||
pub.PassedIPLDPayload = append(pub.PassedIPLDPayload, ipldPayload)
|
||||
if len(pub.ReturnCIDPayload) < pub.iteration+1 {
|
||||
return nil, fmt.Errorf("IterativeIPLDPublisher does not have a payload to return at iteration %d", pub.iteration)
|
||||
}
|
||||
returnPayload := pub.ReturnCIDPayload[pub.iteration]
|
||||
pub.iteration++
|
||||
return returnPayload, pub.ReturnErr
|
||||
}
|
||||
@@ -1,744 +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 mocks
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
)
|
||||
|
||||
var (
|
||||
MockBlockHeight int64 = 1337
|
||||
MockBlock = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04,
|
||||
0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9,
|
||||
0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f,
|
||||
0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
}), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250
|
||||
MerkleRoot: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0,
|
||||
0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22,
|
||||
0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85,
|
||||
0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3,
|
||||
}), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766
|
||||
Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC
|
||||
Bits: 0x1b04864c, // 453281356
|
||||
Nonce: 0x10572b0f, // 274148111
|
||||
},
|
||||
Transactions: []*wire.MsgTx{
|
||||
{
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{},
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
|
||||
},
|
||||
Sequence: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOut: []*wire.TxOut{
|
||||
{
|
||||
Value: 0x12a05f200, // 5000000000
|
||||
PkScript: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
|
||||
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
|
||||
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
|
||||
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
|
||||
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
|
||||
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
|
||||
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
|
||||
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
|
||||
0x84, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
},
|
||||
},
|
||||
LockTime: 0,
|
||||
},
|
||||
{
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
|
||||
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
|
||||
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
|
||||
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
|
||||
}), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03
|
||||
Index: 0,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x49, // OP_DATA_73
|
||||
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
|
||||
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
|
||||
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
|
||||
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
|
||||
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
|
||||
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
|
||||
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
|
||||
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
|
||||
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
|
||||
0x01, // 73-byte signature
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
|
||||
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
|
||||
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
|
||||
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
|
||||
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
|
||||
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
|
||||
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
|
||||
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
|
||||
0xd3, // 65-byte pubkey
|
||||
},
|
||||
Sequence: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOut: []*wire.TxOut{
|
||||
{
|
||||
Value: 0x2123e300, // 556000000
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
|
||||
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
|
||||
0xf7, 0xf5, 0x8b, 0x32,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
},
|
||||
{
|
||||
Value: 0x108e20f00, // 4444000000
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
|
||||
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
|
||||
0x52, 0xde, 0x3d, 0x7c,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
},
|
||||
},
|
||||
LockTime: 0,
|
||||
},
|
||||
{
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
|
||||
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
|
||||
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
|
||||
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
|
||||
}), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3
|
||||
Index: 1,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x47, // OP_DATA_71
|
||||
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
|
||||
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
|
||||
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
|
||||
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
|
||||
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
|
||||
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
|
||||
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
|
||||
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
|
||||
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
|
||||
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
|
||||
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
|
||||
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
|
||||
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
|
||||
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
|
||||
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
|
||||
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
|
||||
0x0f, // 65-byte pubkey
|
||||
},
|
||||
Sequence: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOut: []*wire.TxOut{
|
||||
{
|
||||
Value: 0xf4240, // 1000000
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
|
||||
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
|
||||
0xad, 0xbe, 0x7e, 0x10,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
},
|
||||
{
|
||||
Value: 0x11d260c0, // 299000000
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
|
||||
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
|
||||
0xb3, 0x40, 0x9c, 0xd9,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
},
|
||||
},
|
||||
LockTime: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
MockTransactions = []*btcutil.Tx{
|
||||
btcutil.NewTx(MockBlock.Transactions[0]),
|
||||
btcutil.NewTx(MockBlock.Transactions[1]),
|
||||
btcutil.NewTx(MockBlock.Transactions[2]),
|
||||
}
|
||||
MockBlockPayload = btc.BlockPayload{
|
||||
Header: &MockBlock.Header,
|
||||
Txs: MockTransactions,
|
||||
BlockHeight: MockBlockHeight,
|
||||
}
|
||||
sClass1, addresses1, numOfSigs1, _ = txscript.ExtractPkScriptAddrs([]byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
|
||||
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
|
||||
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
|
||||
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
|
||||
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
|
||||
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
|
||||
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
|
||||
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
|
||||
0x84, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
}, &chaincfg.MainNetParams)
|
||||
sClass2a, addresses2a, numOfSigs2a, _ = txscript.ExtractPkScriptAddrs([]byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
|
||||
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
|
||||
0xf7, 0xf5, 0x8b, 0x32,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
}, &chaincfg.MainNetParams)
|
||||
sClass2b, addresses2b, numOfSigs2b, _ = txscript.ExtractPkScriptAddrs([]byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
|
||||
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
|
||||
0x52, 0xde, 0x3d, 0x7c,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
}, &chaincfg.MainNetParams)
|
||||
sClass3a, addresses3a, numOfSigs3a, _ = txscript.ExtractPkScriptAddrs([]byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
|
||||
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
|
||||
0xad, 0xbe, 0x7e, 0x10,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
}, &chaincfg.MainNetParams)
|
||||
sClass3b, addresses3b, numOfSigs3b, _ = txscript.ExtractPkScriptAddrs([]byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
|
||||
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
|
||||
0xb3, 0x40, 0x9c, 0xd9,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
}, &chaincfg.MainNetParams)
|
||||
MockTxsMetaData = []btc.TxModelWithInsAndOuts{
|
||||
{
|
||||
TxHash: MockBlock.Transactions[0].TxHash().String(),
|
||||
Index: 0,
|
||||
SegWit: MockBlock.Transactions[0].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
SignatureScript: []byte{
|
||||
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
|
||||
},
|
||||
PreviousOutPointHash: chainhash.Hash{}.String(),
|
||||
PreviousOutPointIndex: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Value: 5000000000,
|
||||
Index: 0,
|
||||
PkScript: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
|
||||
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
|
||||
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
|
||||
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
|
||||
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
|
||||
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
|
||||
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
|
||||
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
|
||||
0x84, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass1),
|
||||
RequiredSigs: int64(numOfSigs1),
|
||||
Addresses: stringSliceFromAddresses(addresses1),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TxHash: MockBlock.Transactions[1].TxHash().String(),
|
||||
Index: 1,
|
||||
SegWit: MockBlock.Transactions[1].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
PreviousOutPointHash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
|
||||
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
|
||||
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
|
||||
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
|
||||
}).String(),
|
||||
PreviousOutPointIndex: 0,
|
||||
SignatureScript: []byte{
|
||||
0x49, // OP_DATA_73
|
||||
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
|
||||
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
|
||||
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
|
||||
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
|
||||
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
|
||||
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
|
||||
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
|
||||
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
|
||||
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
|
||||
0x01, // 73-byte signature
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
|
||||
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
|
||||
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
|
||||
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
|
||||
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
|
||||
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
|
||||
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
|
||||
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
|
||||
0xd3, // 65-byte pubkey
|
||||
},
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 556000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
|
||||
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
|
||||
0xf7, 0xf5, 0x8b, 0x32,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass2a),
|
||||
RequiredSigs: int64(numOfSigs2a),
|
||||
Addresses: stringSliceFromAddresses(addresses2a),
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Value: 4444000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
|
||||
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
|
||||
0x52, 0xde, 0x3d, 0x7c,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass2b),
|
||||
RequiredSigs: int64(numOfSigs2b),
|
||||
Addresses: stringSliceFromAddresses(addresses2b),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TxHash: MockBlock.Transactions[2].TxHash().String(),
|
||||
Index: 2,
|
||||
SegWit: MockBlock.Transactions[2].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
PreviousOutPointHash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
|
||||
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
|
||||
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
|
||||
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
|
||||
}).String(),
|
||||
PreviousOutPointIndex: 1,
|
||||
SignatureScript: []byte{
|
||||
0x47, // OP_DATA_71
|
||||
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
|
||||
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
|
||||
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
|
||||
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
|
||||
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
|
||||
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
|
||||
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
|
||||
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
|
||||
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
|
||||
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
|
||||
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
|
||||
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
|
||||
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
|
||||
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
|
||||
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
|
||||
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
|
||||
0x0f, // 65-byte pubkey
|
||||
},
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
|
||||
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
|
||||
0xad, 0xbe, 0x7e, 0x10,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass3a),
|
||||
RequiredSigs: int64(numOfSigs3a),
|
||||
Addresses: stringSliceFromAddresses(addresses3a),
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Value: 299000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
|
||||
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
|
||||
0xb3, 0x40, 0x9c, 0xd9,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass3b),
|
||||
RequiredSigs: int64(numOfSigs3b),
|
||||
Addresses: stringSliceFromAddresses(addresses3b),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
MockTxsMetaDataPostPublish = []btc.TxModelWithInsAndOuts{
|
||||
{
|
||||
CID: "mockTrxCID1",
|
||||
TxHash: MockBlock.Transactions[0].TxHash().String(),
|
||||
Index: 0,
|
||||
SegWit: MockBlock.Transactions[0].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
SignatureScript: []byte{
|
||||
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
|
||||
},
|
||||
PreviousOutPointHash: chainhash.Hash{}.String(),
|
||||
PreviousOutPointIndex: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Value: 5000000000,
|
||||
Index: 0,
|
||||
PkScript: []byte{
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25,
|
||||
0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73,
|
||||
0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7,
|
||||
0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16,
|
||||
0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24,
|
||||
0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed,
|
||||
0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28,
|
||||
0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf,
|
||||
0x84, // 65-byte signature
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass1),
|
||||
RequiredSigs: int64(numOfSigs1),
|
||||
Addresses: stringSliceFromAddresses(addresses1),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CID: "mockTrxCID2",
|
||||
TxHash: MockBlock.Transactions[1].TxHash().String(),
|
||||
Index: 1,
|
||||
SegWit: MockBlock.Transactions[1].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
PreviousOutPointHash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60,
|
||||
0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac,
|
||||
0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07,
|
||||
0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87,
|
||||
}).String(),
|
||||
PreviousOutPointIndex: 0,
|
||||
SignatureScript: []byte{
|
||||
0x49, // OP_DATA_73
|
||||
0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3,
|
||||
0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6,
|
||||
0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94,
|
||||
0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58,
|
||||
0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00,
|
||||
0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62,
|
||||
0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c,
|
||||
0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60,
|
||||
0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48,
|
||||
0x01, // 73-byte signature
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d,
|
||||
0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38,
|
||||
0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25,
|
||||
0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e,
|
||||
0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8,
|
||||
0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd,
|
||||
0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b,
|
||||
0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3,
|
||||
0xd3, // 65-byte pubkey
|
||||
},
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 556000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60,
|
||||
0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e,
|
||||
0xf7, 0xf5, 0x8b, 0x32,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass2a),
|
||||
RequiredSigs: int64(numOfSigs2a),
|
||||
Addresses: stringSliceFromAddresses(addresses2a),
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Value: 4444000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f,
|
||||
0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b,
|
||||
0x52, 0xde, 0x3d, 0x7c,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass2b),
|
||||
RequiredSigs: int64(numOfSigs2b),
|
||||
Addresses: stringSliceFromAddresses(addresses2b),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CID: "mockTrxCID3",
|
||||
TxHash: MockBlock.Transactions[2].TxHash().String(),
|
||||
Index: 2,
|
||||
SegWit: MockBlock.Transactions[2].HasWitness(),
|
||||
TxInputs: []btc.TxInput{
|
||||
{
|
||||
Index: 0,
|
||||
PreviousOutPointHash: chainhash.Hash([32]byte{ // Make go vet happy.
|
||||
0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d,
|
||||
0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27,
|
||||
0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65,
|
||||
0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf,
|
||||
}).String(),
|
||||
PreviousOutPointIndex: 1,
|
||||
SignatureScript: []byte{
|
||||
0x47, // OP_DATA_71
|
||||
0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf,
|
||||
0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5,
|
||||
0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34,
|
||||
0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31,
|
||||
0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee,
|
||||
0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f,
|
||||
0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c,
|
||||
0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e,
|
||||
0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01,
|
||||
0x41, // OP_DATA_65
|
||||
0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78,
|
||||
0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5,
|
||||
0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39,
|
||||
0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21,
|
||||
0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee,
|
||||
0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3,
|
||||
0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95,
|
||||
0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85,
|
||||
0x0f, // 65-byte pubkey
|
||||
},
|
||||
},
|
||||
},
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
Value: 1000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04,
|
||||
0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d,
|
||||
0xad, 0xbe, 0x7e, 0x10,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass3a),
|
||||
RequiredSigs: int64(numOfSigs3a),
|
||||
Addresses: stringSliceFromAddresses(addresses3a),
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
Value: 299000000,
|
||||
PkScript: []byte{
|
||||
0x76, // OP_DUP
|
||||
0xa9, // OP_HASH160
|
||||
0x14, // OP_DATA_20
|
||||
0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1,
|
||||
0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab,
|
||||
0xb3, 0x40, 0x9c, 0xd9,
|
||||
0x88, // OP_EQUALVERIFY
|
||||
0xac, // OP_CHECKSIG
|
||||
},
|
||||
ScriptClass: uint8(sClass3b),
|
||||
RequiredSigs: int64(numOfSigs3b),
|
||||
Addresses: stringSliceFromAddresses(addresses3b),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
MockHeaderMetaData = btc.HeaderModel{
|
||||
CID: "mockHeaderCID",
|
||||
ParentHash: MockBlock.Header.PrevBlock.String(),
|
||||
BlockNumber: strconv.Itoa(int(MockBlockHeight)),
|
||||
BlockHash: MockBlock.Header.BlockHash().String(),
|
||||
Timestamp: MockBlock.Header.Timestamp.UnixNano(),
|
||||
Bits: MockBlock.Header.Bits,
|
||||
}
|
||||
MockConvertedPayload = btc.ConvertedPayload{
|
||||
BlockPayload: MockBlockPayload,
|
||||
TxMetaData: MockTxsMetaData,
|
||||
}
|
||||
MockCIDPayload = btc.CIDPayload{
|
||||
HeaderCID: MockHeaderMetaData,
|
||||
TransactionCIDs: MockTxsMetaDataPostPublish,
|
||||
}
|
||||
DummyCIDPayloadForFKReference = btc.CIDPayload{
|
||||
HeaderCID: btc.HeaderModel{
|
||||
CID: "dummyHeader",
|
||||
ParentHash: "",
|
||||
BlockHash: "",
|
||||
BlockNumber: "1336",
|
||||
Bits: 1,
|
||||
Timestamp: 1000000000,
|
||||
},
|
||||
TransactionCIDs: []btc.TxModelWithInsAndOuts{
|
||||
{
|
||||
TxHash: "87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03",
|
||||
CID: "dummyTx1",
|
||||
Index: 0,
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
RequiredSigs: 0,
|
||||
Value: 0,
|
||||
PkScript: []byte{},
|
||||
ScriptClass: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TxHash: "cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3",
|
||||
CID: "dummyTx2",
|
||||
Index: 1,
|
||||
TxOutputs: []btc.TxOutput{
|
||||
{
|
||||
Index: 0,
|
||||
RequiredSigs: 0,
|
||||
Value: 0,
|
||||
PkScript: []byte{},
|
||||
ScriptClass: 0,
|
||||
},
|
||||
{
|
||||
Index: 1,
|
||||
RequiredSigs: 0,
|
||||
Value: 0,
|
||||
PkScript: []byte{},
|
||||
ScriptClass: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func stringSliceFromAddresses(addrs []btcutil.Address) []string {
|
||||
strs := make([]string, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
strs[i] = addr.EncodeAddress()
|
||||
}
|
||||
return strs
|
||||
}
|
||||
@@ -1,79 +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 btc
|
||||
|
||||
import "github.com/lib/pq"
|
||||
|
||||
// HeaderModel is the db model for btc.header_cids table
|
||||
type HeaderModel struct {
|
||||
ID int64 `db:"id"`
|
||||
BlockNumber string `db:"block_number"`
|
||||
BlockHash string `db:"block_hash"`
|
||||
ParentHash string `db:"parent_hash"`
|
||||
CID string `db:"cid"`
|
||||
Timestamp int64 `db:"timestamp"`
|
||||
Bits uint32 `db:"bits"`
|
||||
NodeID int64 `db:"node_id"`
|
||||
TimesValidated int64 `db:"times_validated"`
|
||||
}
|
||||
|
||||
// TxModel is the db model for btc.transaction_cids table
|
||||
type TxModel struct {
|
||||
ID int64 `db:"id"`
|
||||
HeaderID int64 `db:"header_id"`
|
||||
Index int64 `db:"index"`
|
||||
TxHash string `db:"tx_hash"`
|
||||
CID string `db:"cid"`
|
||||
SegWit bool `db:"segwit"`
|
||||
WitnessHash string `db:"witness_hash"`
|
||||
}
|
||||
|
||||
// TxModelWithInsAndOuts is the db model for btc.transaction_cids table that includes the children tx_input and tx_output tables
|
||||
type TxModelWithInsAndOuts struct {
|
||||
ID int64 `db:"id"`
|
||||
HeaderID int64 `db:"header_id"`
|
||||
Index int64 `db:"index"`
|
||||
TxHash string `db:"tx_hash"`
|
||||
CID string `db:"cid"`
|
||||
SegWit bool `db:"segwit"`
|
||||
WitnessHash string `db:"witness_hash"`
|
||||
TxInputs []TxInput
|
||||
TxOutputs []TxOutput
|
||||
}
|
||||
|
||||
// TxInput is the db model for btc.tx_inputs table
|
||||
type TxInput struct {
|
||||
ID int64 `db:"id"`
|
||||
TxID int64 `db:"tx_id"`
|
||||
Index int64 `db:"index"`
|
||||
TxWitness []string `db:"witness"`
|
||||
SignatureScript []byte `db:"sig_script"`
|
||||
PreviousOutPointIndex uint32 `db:"outpoint_tx_hash"`
|
||||
PreviousOutPointHash string `db:"outpoint_index"`
|
||||
}
|
||||
|
||||
// TxOutput is the db model for btc.tx_outputs table
|
||||
type TxOutput struct {
|
||||
ID int64 `db:"id"`
|
||||
TxID int64 `db:"tx_id"`
|
||||
Index int64 `db:"index"`
|
||||
Value int64 `db:"value"`
|
||||
PkScript []byte `db:"pk_script"`
|
||||
ScriptClass uint8 `db:"script_class"`
|
||||
RequiredSigs int64 `db:"required_sigs"`
|
||||
Addresses pq.StringArray `db:"addresses"`
|
||||
}
|
||||
@@ -1,76 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// PayloadFetcher satisfies the PayloadFetcher interface for bitcoin
|
||||
type PayloadFetcher struct {
|
||||
// PayloadFetcher is thread-safe as long as the underlying client is thread-safe, since it has/modifies no other state
|
||||
// http.Client is thread-safe
|
||||
client *rpcclient.Client
|
||||
}
|
||||
|
||||
// NewStateDiffFetcher returns a PayloadFetcher
|
||||
func NewPayloadFetcher(c *rpcclient.ConnConfig) (*PayloadFetcher, error) {
|
||||
client, err := rpcclient.New(c, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PayloadFetcher{
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FetchAt fetches the block payloads at the given block heights
|
||||
func (fetcher *PayloadFetcher) FetchAt(blockHeights []uint64) ([]shared.RawChainData, error) {
|
||||
blockPayloads := make([]shared.RawChainData, len(blockHeights))
|
||||
for i, height := range blockHeights {
|
||||
hash, err := fetcher.client.GetBlockHash(int64(height))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bitcoin PayloadFetcher GetBlockHash err at blockheight %d: %s", height, err.Error())
|
||||
}
|
||||
block, err := fetcher.client.GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bitcoin PayloadFetcher GetBlock err at blockheight %d: %s", height, err.Error())
|
||||
}
|
||||
blockPayloads[i] = BlockPayload{
|
||||
BlockHeight: int64(height),
|
||||
Header: &block.Header,
|
||||
Txs: msgTxsToUtilTxs(block.Transactions),
|
||||
}
|
||||
}
|
||||
return blockPayloads, nil
|
||||
}
|
||||
|
||||
func msgTxsToUtilTxs(msgs []*wire.MsgTx) []*btcutil.Tx {
|
||||
txs := make([]*btcutil.Tx, len(msgs))
|
||||
for i, msg := range msgs {
|
||||
tx := btcutil.NewTx(msg)
|
||||
tx.SetIndex(i)
|
||||
txs[i] = tx
|
||||
}
|
||||
return txs
|
||||
}
|
||||
@@ -1,124 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/ipld"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// IPLDPublisherAndIndexer satisfies the IPLDPublisher interface for bitcoin
|
||||
// It interfaces directly with the public.blocks table of PG-IPFS rather than going through an ipfs intermediary
|
||||
// It publishes and indexes IPLDs together in a single sqlx.Tx
|
||||
type IPLDPublisherAndIndexer struct {
|
||||
indexer *CIDIndexer
|
||||
}
|
||||
|
||||
// NewIPLDPublisherAndIndexer creates a pointer to a new IPLDPublisherAndIndexer which satisfies the IPLDPublisher interface
|
||||
func NewIPLDPublisherAndIndexer(db *postgres.DB) *IPLDPublisherAndIndexer {
|
||||
return &IPLDPublisherAndIndexer{
|
||||
indexer: NewCIDIndexer(db),
|
||||
}
|
||||
}
|
||||
|
||||
// Publish publishes an IPLDPayload to IPFS and returns the corresponding CIDPayload
|
||||
func (pub *IPLDPublisherAndIndexer) Publish(payload shared.ConvertedData) (shared.CIDsForIndexing, error) {
|
||||
ipldPayload, ok := payload.(ConvertedPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("btc publisher expected payload type %T got %T", ConvertedPayload{}, payload)
|
||||
}
|
||||
// Generate the iplds
|
||||
headerNode, txNodes, txTrieNodes, err := ipld.FromHeaderAndTxs(ipldPayload.Header, ipldPayload.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Begin new db tx
|
||||
tx, err := pub.indexer.db.Beginx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
// Publish trie nodes
|
||||
for _, node := range txTrieNodes {
|
||||
if err := shared.PublishIPLD(tx, node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Publish and index header
|
||||
if err := shared.PublishIPLD(tx, headerNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := HeaderModel{
|
||||
CID: headerNode.Cid().String(),
|
||||
ParentHash: ipldPayload.Header.PrevBlock.String(),
|
||||
BlockNumber: strconv.Itoa(int(ipldPayload.BlockPayload.BlockHeight)),
|
||||
BlockHash: ipldPayload.Header.BlockHash().String(),
|
||||
Timestamp: ipldPayload.Header.Timestamp.UnixNano(),
|
||||
Bits: ipldPayload.Header.Bits,
|
||||
}
|
||||
headerID, err := pub.indexer.indexHeaderCID(tx, header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Publish and index txs
|
||||
for i, txNode := range txNodes {
|
||||
if err := shared.PublishIPLD(tx, txNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txModel := ipldPayload.TxMetaData[i]
|
||||
txModel.CID = txNode.Cid().String()
|
||||
txID, err := pub.indexer.indexTransactionCID(tx, txModel, headerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, input := range txModel.TxInputs {
|
||||
if err := pub.indexer.indexTxInput(tx, input, txID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, output := range txModel.TxOutputs {
|
||||
if err := pub.indexer.indexTxOutput(tx, output, txID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This IPLDPublisher does both publishing and indexing, we do not need to pass anything forward to the indexer
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Index satisfies the shared.CIDIndexer interface
|
||||
func (pub *IPLDPublisherAndIndexer) Index(cids shared.CIDsForIndexing) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,121 +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 btc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipfs/go-ipfs-blockstore"
|
||||
"github.com/ipfs/go-ipfs-ds-help"
|
||||
"github.com/multiformats/go-multihash"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/ipld"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc/mocks"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
var _ = Describe("PublishAndIndexer", func() {
|
||||
var (
|
||||
db *postgres.DB
|
||||
err error
|
||||
repo *btc.IPLDPublisherAndIndexer
|
||||
ipfsPgGet = `SELECT data FROM public.blocks
|
||||
WHERE key = $1`
|
||||
)
|
||||
BeforeEach(func() {
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
repo = btc.NewIPLDPublisherAndIndexer(db)
|
||||
})
|
||||
AfterEach(func() {
|
||||
btc.TearDownDB(db)
|
||||
})
|
||||
|
||||
Describe("Publish", func() {
|
||||
It("Published and indexes header and transaction IPLDs in a single tx", func() {
|
||||
emptyReturn, err := repo.Publish(mocks.MockConvertedPayload)
|
||||
Expect(emptyReturn).To(BeNil())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
pgStr := `SELECT * FROM btc.header_cids
|
||||
WHERE block_number = $1`
|
||||
// check header was properly indexed
|
||||
buf := bytes.NewBuffer(make([]byte, 0, 80))
|
||||
err = mocks.MockBlock.Header.Serialize(buf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerBytes := buf.Bytes()
|
||||
c, _ := ipld.RawdataToCid(ipld.MBitcoinHeader, headerBytes, multihash.DBL_SHA2_256)
|
||||
header := new(btc.HeaderModel)
|
||||
err = db.Get(header, pgStr, mocks.MockHeaderMetaData.BlockNumber)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(header.CID).To(Equal(c.String()))
|
||||
Expect(header.BlockNumber).To(Equal(mocks.MockHeaderMetaData.BlockNumber))
|
||||
Expect(header.Bits).To(Equal(mocks.MockHeaderMetaData.Bits))
|
||||
Expect(header.Timestamp).To(Equal(mocks.MockHeaderMetaData.Timestamp))
|
||||
Expect(header.BlockHash).To(Equal(mocks.MockHeaderMetaData.BlockHash))
|
||||
Expect(header.ParentHash).To(Equal(mocks.MockHeaderMetaData.ParentHash))
|
||||
dc, err := cid.Decode(header.CID)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
mhKey := dshelp.MultihashToDsKey(dc.Hash())
|
||||
prefixedKey := blockstore.BlockPrefix.String() + mhKey.String()
|
||||
var data []byte
|
||||
err = db.Get(&data, ipfsPgGet, prefixedKey)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(data).To(Equal(headerBytes))
|
||||
|
||||
// check that txs were properly indexed
|
||||
trxs := make([]btc.TxModel, 0)
|
||||
pgStr = `SELECT transaction_cids.id, transaction_cids.header_id, transaction_cids.index,
|
||||
transaction_cids.tx_hash, transaction_cids.cid, transaction_cids.segwit, transaction_cids.witness_hash
|
||||
FROM btc.transaction_cids INNER JOIN btc.header_cids ON (transaction_cids.header_id = header_cids.id)
|
||||
WHERE header_cids.block_number = $1`
|
||||
err = db.Select(&trxs, pgStr, mocks.MockHeaderMetaData.BlockNumber)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(trxs)).To(Equal(3))
|
||||
txData := make([][]byte, len(mocks.MockTransactions))
|
||||
txCIDs := make([]string, len(mocks.MockTransactions))
|
||||
for i, m := range mocks.MockTransactions {
|
||||
buf := bytes.NewBuffer(make([]byte, 0))
|
||||
err = m.MsgTx().Serialize(buf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx := buf.Bytes()
|
||||
txData[i] = tx
|
||||
c, _ := ipld.RawdataToCid(ipld.MBitcoinTx, tx, multihash.DBL_SHA2_256)
|
||||
txCIDs[i] = c.String()
|
||||
}
|
||||
for _, tx := range trxs {
|
||||
Expect(tx.SegWit).To(Equal(false))
|
||||
Expect(tx.HeaderID).To(Equal(header.ID))
|
||||
Expect(tx.WitnessHash).To(Equal(""))
|
||||
Expect(tx.CID).To(Equal(txCIDs[tx.Index]))
|
||||
Expect(tx.TxHash).To(Equal(mocks.MockBlock.Transactions[tx.Index].TxHash().String()))
|
||||
dc, err := cid.Decode(tx.CID)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
mhKey := dshelp.MultihashToDsKey(dc.Hash())
|
||||
prefixedKey := blockstore.BlockPrefix.String() + mhKey.String()
|
||||
var data []byte
|
||||
err = db.Get(&data, ipfsPgGet, prefixedKey)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(data).To(Equal(txData[tx.Index]))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,117 +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 btc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/dag_putters"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/ipld"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// IPLDPublisher satisfies the IPLDPublisher for ethereum
|
||||
type IPLDPublisher struct {
|
||||
HeaderPutter ipfs.DagPutter
|
||||
TransactionPutter ipfs.DagPutter
|
||||
TransactionTriePutter ipfs.DagPutter
|
||||
}
|
||||
|
||||
// NewIPLDPublisher creates a pointer to a new Publisher which satisfies the IPLDPublisher interface
|
||||
func NewIPLDPublisher(ipfsPath string) (*IPLDPublisher, error) {
|
||||
node, err := ipfs.InitIPFSNode(ipfsPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IPLDPublisher{
|
||||
HeaderPutter: dag_putters.NewBtcHeaderDagPutter(node),
|
||||
TransactionPutter: dag_putters.NewBtcTxDagPutter(node),
|
||||
TransactionTriePutter: dag_putters.NewBtcTxTrieDagPutter(node),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Publish publishes an IPLDPayload to IPFS and returns the corresponding CIDPayload
|
||||
func (pub *IPLDPublisher) Publish(payload shared.ConvertedData) (shared.CIDsForIndexing, error) {
|
||||
ipldPayload, ok := payload.(ConvertedPayload)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("eth publisher expected payload type %T got %T", &ConvertedPayload{}, payload)
|
||||
}
|
||||
// Generate nodes
|
||||
headerNode, txNodes, txTrieNodes, err := ipld.FromHeaderAndTxs(ipldPayload.Header, ipldPayload.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Process and publish headers
|
||||
headerCid, err := pub.publishHeader(headerNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := HeaderModel{
|
||||
CID: headerCid,
|
||||
ParentHash: ipldPayload.Header.PrevBlock.String(),
|
||||
BlockNumber: strconv.Itoa(int(ipldPayload.BlockPayload.BlockHeight)),
|
||||
BlockHash: ipldPayload.Header.BlockHash().String(),
|
||||
Timestamp: ipldPayload.Header.Timestamp.UnixNano(),
|
||||
Bits: ipldPayload.Header.Bits,
|
||||
}
|
||||
// Process and publish transactions
|
||||
transactionCids, err := pub.publishTransactions(txNodes, txTrieNodes, ipldPayload.TxMetaData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Package CIDs and their metadata into a single struct
|
||||
return &CIDPayload{
|
||||
HeaderCID: header,
|
||||
TransactionCIDs: transactionCids,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pub *IPLDPublisher) publishHeader(header *ipld.BtcHeader) (string, error) {
|
||||
cid, err := pub.HeaderPutter.DagPut(header)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cid, nil
|
||||
}
|
||||
|
||||
func (pub *IPLDPublisher) publishTransactions(transactions []*ipld.BtcTx, txTrie []*ipld.BtcTxTrie, trxMeta []TxModelWithInsAndOuts) ([]TxModelWithInsAndOuts, error) {
|
||||
txCids := make([]TxModelWithInsAndOuts, len(transactions))
|
||||
for i, tx := range transactions {
|
||||
cid, err := pub.TransactionPutter.DagPut(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txCids[i] = TxModelWithInsAndOuts{
|
||||
CID: cid,
|
||||
Index: trxMeta[i].Index,
|
||||
TxHash: trxMeta[i].TxHash,
|
||||
SegWit: trxMeta[i].SegWit,
|
||||
WitnessHash: trxMeta[i].WitnessHash,
|
||||
TxInputs: trxMeta[i].TxInputs,
|
||||
TxOutputs: trxMeta[i].TxOutputs,
|
||||
}
|
||||
}
|
||||
for _, txNode := range txTrie {
|
||||
// We don't do anything with the tx trie cids atm
|
||||
if _, err := pub.TransactionTriePutter.DagPut(txNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return txCids, nil
|
||||
}
|
||||
@@ -1,81 +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 btc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc"
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/btc/mocks"
|
||||
mocks2 "github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs/mocks"
|
||||
)
|
||||
|
||||
var (
|
||||
mockHeaderDagPutter *mocks2.MappedDagPutter
|
||||
mockTrxDagPutter *mocks2.MappedDagPutter
|
||||
mockTrxTrieDagPutter *mocks2.DagPutter
|
||||
)
|
||||
|
||||
var _ = Describe("Publisher", func() {
|
||||
BeforeEach(func() {
|
||||
mockHeaderDagPutter = new(mocks2.MappedDagPutter)
|
||||
mockTrxDagPutter = new(mocks2.MappedDagPutter)
|
||||
mockTrxTrieDagPutter = new(mocks2.DagPutter)
|
||||
})
|
||||
|
||||
Describe("Publish", func() {
|
||||
It("Publishes the passed IPLDPayload objects to IPFS and returns a CIDPayload for indexing", func() {
|
||||
by := new(bytes.Buffer)
|
||||
err := mocks.MockConvertedPayload.BlockPayload.Header.Serialize(by)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
headerBytes := by.Bytes()
|
||||
err = mocks.MockTransactions[0].MsgTx().Serialize(by)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx1Bytes := by.Bytes()
|
||||
err = mocks.MockTransactions[1].MsgTx().Serialize(by)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx2Bytes := by.Bytes()
|
||||
err = mocks.MockTransactions[2].MsgTx().Serialize(by)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx3Bytes := by.Bytes()
|
||||
mockHeaderDagPutter.CIDsToReturn = map[common.Hash]string{
|
||||
common.BytesToHash(headerBytes): "mockHeaderCID",
|
||||
}
|
||||
mockTrxDagPutter.CIDsToReturn = map[common.Hash]string{
|
||||
common.BytesToHash(tx1Bytes): "mockTrxCID1",
|
||||
common.BytesToHash(tx2Bytes): "mockTrxCID2",
|
||||
common.BytesToHash(tx3Bytes): "mockTrxCID3",
|
||||
}
|
||||
publisher := btc.IPLDPublisher{
|
||||
HeaderPutter: mockHeaderDagPutter,
|
||||
TransactionPutter: mockTrxDagPutter,
|
||||
TransactionTriePutter: mockTrxTrieDagPutter,
|
||||
}
|
||||
payload, err := publisher.Publish(mocks.MockConvertedPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cidPayload, ok := payload.(*btc.CIDPayload)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(cidPayload).To(Equal(&mocks.MockCIDPayload))
|
||||
Expect(cidPayload.HeaderCID).To(Equal(mocks.MockHeaderMetaData))
|
||||
Expect(cidPayload.TransactionCIDs).To(Equal(mocks.MockTxsMetaDataPostPublish))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,86 +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 btc
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
const (
|
||||
PayloadChanBufferSize = 20000 // the max eth sub buffer size
|
||||
)
|
||||
|
||||
// PayloadStreamer satisfies the PayloadStreamer interface for bitcoin
|
||||
type PayloadStreamer struct {
|
||||
Config *rpcclient.ConnConfig
|
||||
}
|
||||
|
||||
// NewPayloadStreamer creates a pointer to a new PayloadStreamer which satisfies the PayloadStreamer interface for bitcoin
|
||||
func NewPayloadStreamer(clientConfig *rpcclient.ConnConfig) *PayloadStreamer {
|
||||
return &PayloadStreamer{
|
||||
Config: clientConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// Stream is the main loop for subscribing to data from the btc block notifications
|
||||
// Satisfies the shared.PayloadStreamer interface
|
||||
func (ps *PayloadStreamer) Stream(payloadChan chan shared.RawChainData) (shared.ClientSubscription, error) {
|
||||
logrus.Info("streaming block payloads from btc")
|
||||
blockNotificationHandler := rpcclient.NotificationHandlers{
|
||||
// Notification handler for block connections, forwards new block data to the payloadChan
|
||||
OnFilteredBlockConnected: func(height int32, header *wire.BlockHeader, txs []*btcutil.Tx) {
|
||||
payloadChan <- BlockPayload{
|
||||
BlockHeight: int64(height),
|
||||
Header: header,
|
||||
Txs: txs,
|
||||
}
|
||||
},
|
||||
}
|
||||
// Create a new client, and connect to btc ws server
|
||||
client, err := rpcclient.New(ps.Config, &blockNotificationHandler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Register for block connect notifications.
|
||||
if err := client.NotifyBlocks(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.WaitForShutdown()
|
||||
return &ClientSubscription{client: client}, nil
|
||||
}
|
||||
|
||||
// ClientSubscription is a wrapper around the underlying btcd rpc client
|
||||
// to fit the shared.ClientSubscription interface
|
||||
type ClientSubscription struct {
|
||||
client *rpcclient.Client
|
||||
}
|
||||
|
||||
// Unsubscribe satisfies the rpc.Subscription interface
|
||||
func (bcs *ClientSubscription) Unsubscribe() {
|
||||
bcs.client.Shutdown()
|
||||
}
|
||||
|
||||
// Err() satisfies the rpc.Subscription interface with a dummy err channel
|
||||
func (bcs *ClientSubscription) Err() <-chan error {
|
||||
errChan := make(chan error)
|
||||
return errChan
|
||||
}
|
||||
@@ -1,115 +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 btc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/shared"
|
||||
)
|
||||
|
||||
// SubscriptionSettings config is used by a subscriber to specify what bitcoin data to stream from the super node
|
||||
type SubscriptionSettings struct {
|
||||
BackFill bool
|
||||
BackFillOnly bool
|
||||
Start *big.Int
|
||||
End *big.Int // set to 0 or a negative value to have no ending block
|
||||
HeaderFilter HeaderFilter
|
||||
TxFilter TxFilter
|
||||
}
|
||||
|
||||
// HeaderFilter contains filter settings for headers
|
||||
type HeaderFilter struct {
|
||||
Off bool
|
||||
}
|
||||
|
||||
// TxFilter contains filter settings for txs
|
||||
type TxFilter struct {
|
||||
Off bool
|
||||
Segwit bool // allow filtering for segwit trxs
|
||||
WitnessHashes []string // allow filtering for specific witness hashes
|
||||
Indexes []int64 // allow filtering for specific transaction indexes (e.g. 0 for coinbase transactions)
|
||||
PkScriptClasses []uint8 // allow filtering for txs that have at least one tx output with the specified pkscript class
|
||||
MultiSig bool // allow filtering for txs that have at least one tx output that requires more than one signature
|
||||
Addresses []string // allow filtering for txs that have at least one tx output with at least one of the provided addresses
|
||||
}
|
||||
|
||||
// Init is used to initialize a EthSubscription struct with env variables
|
||||
func NewBtcSubscriptionConfig() (*SubscriptionSettings, error) {
|
||||
sc := new(SubscriptionSettings)
|
||||
// Below default to false, which means we do not backfill by default
|
||||
sc.BackFill = viper.GetBool("superNode.btcSubscription.historicalData")
|
||||
sc.BackFillOnly = viper.GetBool("superNode.btcSubscription.historicalDataOnly")
|
||||
// Below default to 0
|
||||
// 0 start means we start at the beginning and 0 end means we continue indefinitely
|
||||
sc.Start = big.NewInt(viper.GetInt64("superNode.btcSubscription.startingBlock"))
|
||||
sc.End = big.NewInt(viper.GetInt64("superNode.btcSubscription.endingBlock"))
|
||||
// Below default to false, which means we get all headers by default
|
||||
sc.HeaderFilter = HeaderFilter{
|
||||
Off: viper.GetBool("superNode.btcSubscription.headerFilter.off"),
|
||||
}
|
||||
// Below defaults to false and two slices of length 0
|
||||
// Which means we get all transactions by default
|
||||
pksc := viper.Get("superNode.btcSubscription.txFilter.pkScriptClass")
|
||||
pkScriptClasses, ok := pksc.([]uint8)
|
||||
if !ok {
|
||||
return nil, errors.New("superNode.btcSubscription.txFilter.pkScriptClass needs to be an array of uint8s")
|
||||
}
|
||||
is := viper.Get("superNode.btcSubscription.txFilter.indexes")
|
||||
indexes, ok := is.([]int64)
|
||||
if !ok {
|
||||
return nil, errors.New("superNode.btcSubscription.txFilter.indexes needs to be an array of int64s")
|
||||
}
|
||||
sc.TxFilter = TxFilter{
|
||||
Off: viper.GetBool("superNode.btcSubscription.txFilter.off"),
|
||||
Segwit: viper.GetBool("superNode.btcSubscription.txFilter.segwit"),
|
||||
WitnessHashes: viper.GetStringSlice("superNode.btcSubscription.txFilter.witnessHashes"),
|
||||
PkScriptClasses: pkScriptClasses,
|
||||
Indexes: indexes,
|
||||
MultiSig: viper.GetBool("superNode.btcSubscription.txFilter.multiSig"),
|
||||
Addresses: viper.GetStringSlice("superNode.btcSubscription.txFilter.addresses"),
|
||||
}
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
// StartingBlock satisfies the SubscriptionSettings() interface
|
||||
func (sc *SubscriptionSettings) StartingBlock() *big.Int {
|
||||
return sc.Start
|
||||
}
|
||||
|
||||
// EndingBlock satisfies the SubscriptionSettings() interface
|
||||
func (sc *SubscriptionSettings) EndingBlock() *big.Int {
|
||||
return sc.End
|
||||
}
|
||||
|
||||
// HistoricalData satisfies the SubscriptionSettings() interface
|
||||
func (sc *SubscriptionSettings) HistoricalData() bool {
|
||||
return sc.BackFill
|
||||
}
|
||||
|
||||
// HistoricalDataOnly satisfies the SubscriptionSettings() interface
|
||||
func (sc *SubscriptionSettings) HistoricalDataOnly() bool {
|
||||
return sc.BackFillOnly
|
||||
}
|
||||
|
||||
// ChainType satisfies the SubscriptionSettings() interface
|
||||
func (sc *SubscriptionSettings) ChainType() shared.ChainType {
|
||||
return shared.Bitcoin
|
||||
}
|
||||
@@ -1,43 +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 btc
|
||||
|
||||
import (
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/postgres"
|
||||
)
|
||||
|
||||
// TearDownDB is used to tear down the super node dbs after tests
|
||||
func TearDownDB(db *postgres.DB) {
|
||||
tx, err := db.Beginx()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = tx.Exec(`DELETE FROM btc.header_cids`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM btc.transaction_cids`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM btc.tx_inputs`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM btc.tx_outputs`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM blocks`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = tx.Commit()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
@@ -1,76 +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 btc
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/vulcanize/ipfs-blockchain-watcher/pkg/ipfs"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// BlockPayload packages the block and tx data received from block connection notifications
|
||||
type BlockPayload struct {
|
||||
BlockHeight int64
|
||||
Header *wire.BlockHeader
|
||||
Txs []*btcutil.Tx
|
||||
}
|
||||
|
||||
// ConvertedPayload is a custom type which packages raw BTC data for publishing to IPFS and filtering to subscribers
|
||||
// Returned by PayloadConverter
|
||||
// Passed to IPLDPublisher and ResponseFilterer
|
||||
type ConvertedPayload struct {
|
||||
BlockPayload
|
||||
TxMetaData []TxModelWithInsAndOuts
|
||||
}
|
||||
|
||||
// Height satisfies the StreamedIPLDs interface
|
||||
func (cp ConvertedPayload) Height() int64 {
|
||||
return cp.BlockPayload.BlockHeight
|
||||
}
|
||||
|
||||
// CIDPayload is a struct to hold all the CIDs and their associated meta data for indexing in Postgres
|
||||
// Returned by IPLDPublisher
|
||||
// Passed to CIDIndexer
|
||||
type CIDPayload struct {
|
||||
HeaderCID HeaderModel
|
||||
TransactionCIDs []TxModelWithInsAndOuts
|
||||
}
|
||||
|
||||
// CIDWrapper is used to direct fetching of IPLDs from IPFS
|
||||
// Returned by CIDRetriever
|
||||
// Passed to IPLDFetcher
|
||||
type CIDWrapper struct {
|
||||
BlockNumber *big.Int
|
||||
Header HeaderModel
|
||||
Transactions []TxModel
|
||||
}
|
||||
|
||||
// IPLDs is used to package raw IPLD block data fetched from IPFS and returned by the server
|
||||
// Returned by IPLDFetcher and ResponseFilterer
|
||||
type IPLDs struct {
|
||||
BlockNumber *big.Int
|
||||
Header ipfs.BlockModel
|
||||
Transactions []ipfs.BlockModel
|
||||
}
|
||||
|
||||
// Height satisfies the StreamedIPLDs interface
|
||||
func (i IPLDs) Height() int64 {
|
||||
return i.BlockNumber.Int64()
|
||||
}
|
||||
@@ -14,31 +14,31 @@
|
||||
// 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 mocks
|
||||
// Client is used by watchers to stream chain IPLD data from a vulcanizedb ipld-eth-server
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/v4/pkg/serve"
|
||||
)
|
||||
|
||||
type StreamClient struct {
|
||||
passedContext context.Context
|
||||
passedResult interface{}
|
||||
passedNamespace string
|
||||
passedPayloadChan interface{}
|
||||
passedSubscribeArgs []interface{}
|
||||
// Client is used to subscribe to the ipld-eth-server ipld data stream
|
||||
type Client struct {
|
||||
c *rpc.Client
|
||||
}
|
||||
|
||||
func (client *StreamClient) Subscribe(ctx context.Context, namespace string, payloadChan interface{}, args ...interface{}) (*rpc.ClientSubscription, error) {
|
||||
client.passedNamespace = namespace
|
||||
client.passedPayloadChan = payloadChan
|
||||
client.passedContext = ctx
|
||||
|
||||
for _, arg := range args {
|
||||
client.passedSubscribeArgs = append(client.passedSubscribeArgs, arg)
|
||||
// NewClient creates a new Client
|
||||
func NewClient(c *rpc.Client) *Client {
|
||||
return &Client{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
subscription := rpc.ClientSubscription{}
|
||||
return &subscription, nil
|
||||
// Stream is the main loop for subscribing to iplds from an ipld-eth-server server
|
||||
func (c *Client) Stream(payloadChan chan serve.SubscriptionPayload, params eth.SubscriptionSettings) (*rpc.ClientSubscription, error) {
|
||||
return c.c.Subscribe(context.Background(), "vdb", payloadChan, "stream", params)
|
||||
}
|
||||
@@ -1,93 +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 client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// RPCClient is a wrapper around the geth RPC client
|
||||
type RPCClient struct {
|
||||
client *rpc.Client
|
||||
ipcPath string
|
||||
}
|
||||
|
||||
// BatchElem is a struct to hold the elements of a BatchCall
|
||||
type BatchElem struct {
|
||||
Method string
|
||||
Args []interface{}
|
||||
Result interface{}
|
||||
Error error
|
||||
}
|
||||
|
||||
// NewRPCClient creates a new RpcClient
|
||||
func NewRPCClient(client *rpc.Client, ipcPath string) RPCClient {
|
||||
return RPCClient{
|
||||
client: client,
|
||||
ipcPath: ipcPath,
|
||||
}
|
||||
}
|
||||
|
||||
// CallContext makes an rpc method call with the provided context and arguments
|
||||
func (client RPCClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
|
||||
//If an empty interface (or other nil object) is passed to CallContext, when the JSONRPC message is created the params will
|
||||
//be interpreted as [null]. This seems to work fine for most of the ethereum clients (which presumably ignore a null parameter.
|
||||
//Ganache however does not ignore it, and throws an 'Incorrect number of arguments' error.
|
||||
if args == nil {
|
||||
return client.client.CallContext(ctx, result, method)
|
||||
}
|
||||
return client.client.CallContext(ctx, result, method, args...)
|
||||
}
|
||||
|
||||
func (client RPCClient) IpcPath() string {
|
||||
return client.ipcPath
|
||||
}
|
||||
|
||||
func (client RPCClient) SupportedModules() (map[string]string, error) {
|
||||
return client.client.SupportedModules()
|
||||
}
|
||||
|
||||
func (client RPCClient) BatchCall(batch []BatchElem) error {
|
||||
var rpcBatch []rpc.BatchElem
|
||||
for _, batchElem := range batch {
|
||||
var newBatchElem = rpc.BatchElem{
|
||||
Result: batchElem.Result,
|
||||
Method: batchElem.Method,
|
||||
Args: batchElem.Args,
|
||||
Error: batchElem.Error,
|
||||
}
|
||||
rpcBatch = append(rpcBatch, newBatchElem)
|
||||
}
|
||||
return client.client.BatchCall(rpcBatch)
|
||||
}
|
||||
|
||||
// Subscribe subscribes to an rpc "namespace_subscribe" subscription with the given channel
|
||||
// The first argument needs to be the method we wish to invoke
|
||||
func (client RPCClient) Subscribe(namespace string, payloadChan interface{}, args ...interface{}) (*rpc.ClientSubscription, error) {
|
||||
chanVal := reflect.ValueOf(payloadChan)
|
||||
if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 {
|
||||
return nil, errors.New("second argument to Subscribe must be a writable channel")
|
||||
}
|
||||
if chanVal.IsNil() {
|
||||
return nil, errors.New("channel given to Subscribe must not be nil")
|
||||
}
|
||||
return client.client.Subscribe(context.Background(), namespace, payloadChan, args...)
|
||||
}
|
||||
@@ -1,21 +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 config
|
||||
|
||||
type Client struct {
|
||||
IPCPath string
|
||||
}
|
||||
@@ -1,54 +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 config_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var vulcanizeConfig = []byte(`
|
||||
[database]
|
||||
name = "dbname"
|
||||
hostname = "localhost"
|
||||
port = 5432
|
||||
|
||||
[client]
|
||||
ipcPath = "IPCPATH/geth.ipc"
|
||||
`)
|
||||
|
||||
var _ = Describe("Loading the config", func() {
|
||||
|
||||
It("reads the private config using the environment", func() {
|
||||
viper.SetConfigName("config")
|
||||
viper.AddConfigPath("$GOPATH/src/github.com/vulcanize/ipfs-blockchain-watcher/environments/")
|
||||
Expect(viper.Get("client.ipcpath")).To(BeNil())
|
||||
|
||||
testConfig := viper.New()
|
||||
testConfig.SetConfigType("toml")
|
||||
err := testConfig.ReadConfig(bytes.NewBuffer(vulcanizeConfig))
|
||||
Expect(err).To(BeNil())
|
||||
Expect(testConfig.Get("database.hostname")).To(Equal("localhost"))
|
||||
Expect(testConfig.Get("database.name")).To(Equal("dbname"))
|
||||
Expect(testConfig.Get("database.port")).To(Equal(int64(5432)))
|
||||
Expect(testConfig.Get("client.ipcpath")).To(Equal("IPCPATH/geth.ipc"))
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,78 +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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Env variables
|
||||
const (
|
||||
DATABASE_NAME = "DATABASE_NAME"
|
||||
DATABASE_HOSTNAME = "DATABASE_HOSTNAME"
|
||||
DATABASE_PORT = "DATABASE_PORT"
|
||||
DATABASE_USER = "DATABASE_USER"
|
||||
DATABASE_PASSWORD = "DATABASE_PASSWORD"
|
||||
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS"
|
||||
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS"
|
||||
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
Hostname string
|
||||
Name string
|
||||
User string
|
||||
Password string
|
||||
Port int
|
||||
MaxIdle int
|
||||
MaxOpen int
|
||||
MaxLifetime int
|
||||
}
|
||||
|
||||
func DbConnectionString(dbConfig Database) string {
|
||||
if len(dbConfig.User) > 0 && len(dbConfig.Password) > 0 {
|
||||
return fmt.Sprintf("postgresql://%s:%s@%s:%d/%s?sslmode=disable",
|
||||
dbConfig.User, dbConfig.Password, dbConfig.Hostname, dbConfig.Port, dbConfig.Name)
|
||||
}
|
||||
if len(dbConfig.User) > 0 && len(dbConfig.Password) == 0 {
|
||||
return fmt.Sprintf("postgresql://%s@%s:%d/%s?sslmode=disable",
|
||||
dbConfig.User, dbConfig.Hostname, dbConfig.Port, dbConfig.Name)
|
||||
}
|
||||
return fmt.Sprintf("postgresql://%s:%d/%s?sslmode=disable", dbConfig.Hostname, dbConfig.Port, dbConfig.Name)
|
||||
}
|
||||
|
||||
func (d *Database) Init() {
|
||||
viper.BindEnv("database.name", DATABASE_NAME)
|
||||
viper.BindEnv("database.hostname", DATABASE_HOSTNAME)
|
||||
viper.BindEnv("database.port", DATABASE_PORT)
|
||||
viper.BindEnv("database.user", DATABASE_USER)
|
||||
viper.BindEnv("database.password", DATABASE_PASSWORD)
|
||||
viper.BindEnv("database.maxIdle", DATABASE_MAX_IDLE_CONNECTIONS)
|
||||
viper.BindEnv("database.maxOpen", DATABASE_MAX_OPEN_CONNECTIONS)
|
||||
viper.BindEnv("database.maxLifetime", DATABASE_MAX_CONN_LIFETIME)
|
||||
|
||||
d.Name = viper.GetString("database.name")
|
||||
d.Hostname = viper.GetString("database.hostname")
|
||||
d.Port = viper.GetInt("database.port")
|
||||
d.User = viper.GetString("database.user")
|
||||
d.Password = viper.GetString("database.password")
|
||||
d.MaxIdle = viper.GetInt("database.maxIdle")
|
||||
d.MaxOpen = viper.GetInt("database.maxOpen")
|
||||
d.MaxLifetime = viper.GetInt("database.maxLifetime")
|
||||
}
|
||||
@@ -1,36 +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 core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
type EthClient interface {
|
||||
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
|
||||
CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||
FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error)
|
||||
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||
TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error)
|
||||
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
|
||||
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user