Compare commits

...

284 Commits

Author SHA1 Message Date
nolash
e52db0de35 Do not callback when none in token proofs check 2021-10-14 15:33:54 +02:00
nolash
cc58735ff1 Do not link null callbacks for token proof check 2021-10-14 15:31:58 +02:00
f42f223ce9 Merge branch 'lash/api-choose-token' into 'master'
feat: Add token metadata API

Closes #118

See merge request grassrootseconomics/cic-internal-integration!289
2021-10-14 13:24:51 +00:00
Louis Holbrook
045a279ac2 feat: Add token metadata API 2021-10-14 13:24:51 +00:00
nolash
4b8eae41f7 Allow empty proofs in token info task 2021-10-14 12:22:41 +02:00
nolash
8d571b51c5 More deps upgrades 2021-10-14 05:28:17 +02:00
nolash
13970ec332 bump version cic-eth 2021-10-13 15:14:55 +02:00
nolash
7e6cc5714d Allow empty proof spec for tokens api 2021-10-13 15:13:43 +02:00
nolash
0f5bceb95b Bump version cic-eth 2021-10-13 14:24:29 +02:00
nolash
159fd07d63 Revert "Another attempt at config files, dockerfile change"
This reverts commit 9d3f8bf1e3.
2021-10-09 17:22:23 +02:00
nolash
9d3f8bf1e3 Another attempt at config files, dockerfile change 2021-10-09 17:12:29 +02:00
nolash
2229154c2b Add documentation 2021-10-09 16:55:05 +02:00
nolash
9637cb61f8 Add magic arg parsing for token lookup api 2021-10-09 16:21:03 +02:00
nolash
66f94ae694 Correct test default token task in unit test 2021-10-09 15:06:21 +02:00
nolash
1e65ea8511 reinstate celery seesion worker in token metadata test 2021-10-09 14:38:16 +02:00
nolash
21972e9df5 Complete token lookups with proofs 2021-10-09 14:18:10 +02:00
nolash
77fe41da4b Revert to multi-token task queue entry point for token api call 2021-10-09 13:39:33 +02:00
nolash
6098374a4e POC single token task chain for token proof lookups 2021-10-09 13:22:14 +02:00
nolash
f648d3ee68 WIP token info proof verify 2021-10-09 00:48:20 +02:00
nolash
7e2857d1e7 Add separate generic task for proof verification 2021-10-08 20:19:15 +02:00
nolash
df21db958b Move token tasks to cic_eth.eth.erc20 2021-10-08 19:21:47 +02:00
nolash
6ccffb15b6 Add trust check to token fetch in cic-eth task 2021-10-08 18:39:22 +02:00
nolash
53e9c63698 Add token api metadata getter (first without okota) 2021-10-08 16:04:13 +02:00
Louis Holbrook
6b59c87f68 Merge branch 'lash/okota' into 'master'
feat: Integrate okota

See merge request grassrootseconomics/cic-internal-integration!286
2021-10-07 21:05:58 +00:00
Louis Holbrook
9ee42b908d feat: Integrate okota 2021-10-07 21:05:58 +00:00
3343c4163e Merge branch 'lash/more-case-case' into 'master'
bug: cic-eth transaction listings

See merge request grassrootseconomics/cic-internal-integration!285
2021-10-07 15:12:35 +00:00
Louis Holbrook
f5bfc8ace2 bug: cic-eth transaction listings 2021-10-07 15:12:35 +00:00
8a9d2ee0be Merge branch 'bvander/default-docker-compose' into 'master'
chore: gift defaults

See merge request grassrootseconomics/cic-internal-integration!282
2021-09-27 13:15:21 +00:00
3608fd1fc7 chore: gift defaults 2021-09-27 16:14:31 +03:00
0d275f358b Merge branch 'philip/removes-docker-compose-profiles' into 'master'
removes profile attrs from docker-compose file.

See merge request grassrootseconomics/cic-internal-integration!281
2021-09-27 12:33:19 +00:00
3aef2aa65f removes profile attrs from docker-compose file. 2021-09-27 15:26:22 +03:00
5644baefb2 Merge branch 'philip/bump-cic-ussd-version' into 'master'
Bumps cic-ussd version following token fix.

See merge request grassrootseconomics/cic-internal-integration!280
2021-09-27 11:37:42 +00:00
1a7c4deab6 Bumps cic-ussd version following token fix. 2021-09-27 14:27:10 +03:00
0389d8623d Merge branch 'philip/token-value-fix' into 'master'
Refactors to avoid conversion of zero values to wei.

See merge request grassrootseconomics/cic-internal-integration!279
2021-09-22 08:17:39 +00:00
cf64387d81 Refactors to avoid conversion of zero values to wei. 2021-09-22 11:10:33 +03:00
79bcc8a9f1 Merge branch 'dev-k8s-deploy-new-data-seeding' into 'master'
Dataseeding container

See merge request grassrootseconomics/cic-internal-integration!267
2021-09-21 06:04:44 +00:00
7b57f1b4c2 Dataseeding container 2021-09-21 06:04:44 +00:00
76b8519637 Merge branch 'philip/cache-migration' into 'master'
Fixes migrations in cic-cache (ideally)

See merge request grassrootseconomics/cic-internal-integration!276
2021-09-21 05:33:46 +00:00
e89aec76fa Fixes migrations in cic-cache (ideally) 2021-09-21 05:33:46 +00:00
a138a0ec75 Merge branch 'bvander/update-buildkit-cache' into 'master'
chore: clean old dockerfile and add docs for clearing cache

See merge request grassrootseconomics/cic-internal-integration!277
2021-09-20 07:01:53 +00:00
5128c7828c chore: clean old dockerfile and add docs for clearing cache 2021-09-20 09:59:12 +03:00
2f005195e5 Merge branch 'philip/ussd-cli-fix' into 'master'
Philip/ussd cli fix

See merge request grassrootseconomics/cic-internal-integration!275
2021-09-18 09:07:05 +00:00
fb8db3ffd2 Philip/ussd cli fix 2021-09-18 09:07:05 +00:00
b5f647c4aa Merge branch 'philip/ussd-demurrage' into 'master'
Philip/ussd demurrage

See merge request grassrootseconomics/cic-internal-integration!263
2021-09-17 11:15:43 +00:00
6019143ba1 Philip/ussd demurrage 2021-09-17 11:15:43 +00:00
610440b722 Merge branch 'lash/signer-missing-symbol' into 'master'
Update signer to fill in missing sign to wire symbol

See merge request grassrootseconomics/cic-internal-integration!273
2021-09-17 06:54:58 +00:00
Louis Holbrook
d65455fc29 Update signer to fill in missing sign to wire symbol 2021-09-17 06:54:58 +00:00
43f8d1c30c Merge branch 'bvander/docker-compose-restarts' into 'master'
chore: fix docker compose restarts

See merge request grassrootseconomics/cic-internal-integration!274
2021-09-16 11:17:42 +00:00
b855211eed chore: fix docker compose restarts 2021-09-16 13:06:53 +03:00
1e0c475f39 Merge branch 'lash/contract-migration-dump' into 'master'
fix(contract-migration): Replace missing environment

See merge request grassrootseconomics/cic-internal-integration!271
2021-09-15 13:36:10 +00:00
Louis Holbrook
3e6cf594e3 fix(contract-migration): Replace missing environment 2021-09-15 13:36:09 +00:00
b8f79a2dd1 Merge branch 'bvander/extend-timoeout-contract-migration' into 'master'
chore: extend timeout for contract migration

See merge request grassrootseconomics/cic-internal-integration!272
2021-09-15 11:08:14 +00:00
540c2fd950 chore: extend timeout for contract migration 2021-09-15 14:04:18 +03:00
b9b06eced8 Merge branch 'staging' into 'master'
feat: add multi-project build to trigger deploy-k8s-dev in the devops repo

See merge request grassrootseconomics/cic-internal-integration!268
2021-09-09 21:28:49 +00:00
949bb29379 feat: add multi-project build to trigger deploy-k8s-dev in the devops repo 2021-09-09 21:28:49 +00:00
Louis Holbrook
0468906601 Merge branch 'lash/contract-migration-dump' into 'master'
fix(contract-migration): Make http signer work with local deployment

See merge request grassrootseconomics/cic-internal-integration!270
2021-09-09 13:38:10 +00:00
nolash
471243488e Add latest crypto-dev-signer install explicitly in dockerfile for cic-eth 2021-09-09 14:27:47 +02:00
3c4acd82ff test eth and meta only mrs 2021-09-07 11:56:17 -07:00
e07f992c5a Add new file 2021-09-07 18:38:20 +00:00
17e95cb19c Merge branch 'bvander/signer-as-service' into 'master'
run signer as a service over http

See merge request grassrootseconomics/cic-internal-integration!265
2021-09-06 19:10:08 +00:00
3c3a97ce15 run signer as a service over http 2021-09-06 12:07:57 -07:00
a492be4927 Merge branch 'lash/contract-migration-config' into 'master'
Sanitize contract migration configs

See merge request grassrootseconomics/cic-internal-integration!262
2021-09-06 10:06:58 +00:00
Louis Holbrook
1f555748b0 Sanitize contract migration configs 2021-09-06 10:06:58 +00:00
8aa4d20eea Update .gitlab-ci.yml 2021-09-01 20:00:44 +00:00
Louis Holbrook
90cf24dcee Merge branch 'lash/lockfix' into 'master'
Rehabilitate imports

See merge request grassrootseconomics/cic-internal-integration!261
2021-09-01 16:54:10 +00:00
Louis Holbrook
75b711dbd5 Rehabilitate imports 2021-09-01 16:54:10 +00:00
c21c1eb2ef fix data seeding node installs 2021-08-31 11:43:01 -07:00
eb5e612105 minor update to import_ussd script 2021-08-30 11:09:47 -07:00
e017d11770 update readme 2021-08-30 10:14:22 -07:00
e327af68e1 Merge branch 'philip/refactor-import-scripts' into 'master'
Consolidated ussd dataseeding script

See merge request grassrootseconomics/cic-internal-integration!252
2021-08-29 09:55:47 +00:00
92cc6a3f27 Consolidated ussd dataseeding script 2021-08-29 09:55:47 +00:00
f42bf7754a Merge branch 'lash/lockfix' into 'master'
Normalize initial INIT lock address

See merge request grassrootseconomics/cic-internal-integration!260
2021-08-29 09:07:46 +00:00
nolash
7342927e91 Normalize initial INIT lock address 2021-08-29 10:43:12 +02:00
17333af88f Merge branch 'bvander/docker-vm-builds' into 'master'
docker vm builds

See merge request grassrootseconomics/cic-internal-integration!259
2021-08-28 16:26:16 +00:00
6a68d2ed32 docker vm builds 2021-08-28 16:26:16 +00:00
Louis Holbrook
ef77f4c99a Merge branch 'lash/normalize-backend-tx' into 'master'
Normalize tx data for backend

Closes cic-eth#133

See merge request grassrootseconomics/cic-internal-integration!258
2021-08-28 11:10:18 +00:00
Louis Holbrook
56dbe8a502 Normalize tx data for backend 2021-08-28 11:10:18 +00:00
Louis Holbrook
2dc8ac6a12 Merge branch 'lash/upgrade-outer-tools' into 'master'
Upgrade outer tools

See merge request grassrootseconomics/cic-internal-integration!257
2021-08-27 13:13:00 +00:00
Louis Holbrook
0ced68e224 Upgrade outer tools 2021-08-27 13:13:00 +00:00
2afb20e715 Merge branch 'philip/ussd-post-test-bug-fixes' into 'master'
USSD post-test bug fixes

See merge request grassrootseconomics/cic-internal-integration!244
2021-08-25 10:33:35 +00:00
3b0113d0e4 USSD post-test bug fixes 2021-08-25 10:33:35 +00:00
Louis Holbrook
ebf4743a84 Merge branch 'lash/traffic-script-rehab' into 'master'
Implement chainlib cli for traffic script

See merge request grassrootseconomics/cic-internal-integration!255
2021-08-25 09:33:23 +00:00
Louis Holbrook
3bf92e7a8a Implement chainlib cli for traffic script 2021-08-25 09:33:23 +00:00
f0b4c42c68 cleanup contract migration dockerfiles 2021-08-24 15:33:18 -07:00
Louis Holbrook
b62d00180c Merge branch 'lash/cic-eth-seeding-fix' into 'master'
cic-eth data seeding rehab

See merge request grassrootseconomics/cic-internal-integration!254
2021-08-24 21:07:36 +00:00
Louis Holbrook
a49978cc36 cic-eth data seeding rehab 2021-08-24 21:07:36 +00:00
1b0ee269d0 add pre tag to contract-migration 2021-08-24 11:43:39 -07:00
aa2f363b27 fix em 2021-08-24 10:42:30 -07:00
2a24ce6938 remove mount 2021-08-24 10:33:53 -07:00
938a10b5c3 contract-migration ci parity 2021-08-24 10:26:09 -07:00
Louis Holbrook
76e33e578b Merge branch 'lash/verify-details' into 'master'
Add target count to verify

Closes #103

See merge request grassrootseconomics/cic-internal-integration!246
2021-08-24 15:56:41 +00:00
Louis Holbrook
2ec4262734 Add target count to verify 2021-08-24 15:56:41 +00:00
Louis Holbrook
7684fe3883 Merge branch 'lash/cic-eth-upgrade-more' into 'master'
Upgrade cic-ussd deps

See merge request grassrootseconomics/cic-internal-integration!247
2021-08-24 15:25:11 +00:00
Louis Holbrook
995a148c6a Upgrade cic-ussd deps 2021-08-24 15:25:11 +00:00
Louis Holbrook
511e099689 Merge branch 'lash/signer-update' into 'master'
Update signer

See merge request grassrootseconomics/cic-internal-integration!253
2021-08-24 11:35:52 +00:00
Louis Holbrook
f877218c55 Update signer 2021-08-24 11:35:52 +00:00
8ac9a1e99a reverting to fffb2bc3f4 2021-08-21 13:23:43 -04:00
c4cb095a29 Merge branch 'bvander/fix-cic-staff-client-docker' into 'master'
fix issues with build cicada, now it should do auto reload

See merge request grassrootseconomics/cic-internal-integration!250
2021-08-19 20:15:22 +00:00
05b8bbbbca fix issues with build cicada, now it should do auto reload 2021-08-19 20:15:22 +00:00
1ce32fbbe0 Merge branch 'fix-meta-docker-compose' into 'master'
fix a bug in the meta tag

See merge request grassrootseconomics/cic-internal-integration!249
2021-08-19 16:48:43 +00:00
3fd5e77e2c fix a bug in the meta tag 2021-08-19 12:46:32 -04:00
e27a49ef33 add docker swarm deployment configs and remove dependency on kaniko for ci builds 2021-08-19 12:29:41 -04:00
Louis Holbrook
fffb2bc3f4 Merge branch 'lash/faucet-workaround' into 'master'
Empty config dir in faucet setup

See merge request grassrootseconomics/cic-internal-integration!240
2021-08-18 06:34:08 +00:00
Louis Holbrook
8910fb0759 Empty config dir in faucet setup 2021-08-18 06:34:07 +00:00
Louis Holbrook
c84239c820 Merge branch 'lash/fix-configs' into 'master'
Fix configs after cic-base remove merges

See merge request grassrootseconomics/cic-internal-integration!245
2021-08-17 16:52:17 +00:00
Louis Holbrook
452047b900 Fix configs after cic-base remove merges 2021-08-17 16:52:17 +00:00
Louis Holbrook
b8be457c41 Merge branch 'lash/advanced-cache-queries' into 'master'
Finish cic-cache integration for transaction listings

Closes cic-eth#134 and #95

See merge request grassrootseconomics/cic-internal-integration!235
2021-08-17 08:03:14 +00:00
Louis Holbrook
0ec9813e5f Finish cic-cache integration for transaction listings 2021-08-17 08:03:14 +00:00
Louis Holbrook
defa7797dc Merge branch 'lash/remove-cic-base' into 'master'
Replace cic-base with chainlib cli utils

Closes #94

See merge request grassrootseconomics/cic-internal-integration!233
2021-08-17 06:46:51 +00:00
Louis Holbrook
bb3d38a1f9 Replace cic-base with chainlib cli utils 2021-08-17 06:46:51 +00:00
3be1c1b33d Merge branch 'spencer/meta-404' into 'master'
Meta returns 404 if resource is not found.

Closes #72

See merge request grassrootseconomics/cic-internal-integration!232
2021-08-09 19:13:05 +00:00
Spencer Ofwiti
d6c763f2d7 Meta returns 404 if resource is not found. 2021-08-09 19:13:05 +00:00
b7942ddcfa Merge branch 'philip/add-docker-compose-default' into 'master'
Adds password pepper value defaults.

See merge request grassrootseconomics/cic-internal-integration!242
2021-08-06 16:58:37 +00:00
8de5dc1540 Adds password pepper value defaults. 2021-08-06 19:53:36 +03:00
fad0a4b580 Merge branch 'philip/the-great-bump' into 'master'
The great bump

See merge request grassrootseconomics/cic-internal-integration!239
2021-08-06 16:29:01 +00:00
0672a17d2e The great bump 2021-08-06 16:29:01 +00:00
f764b73f66 Testing a whitespace change for deployment 2021-07-30 17:05:25 +00:00
806b82504f Merge branch 'bvander/fix-notify-migrations' into 'master'
Bvander/fix notify migrations

See merge request grassrootseconomics/cic-internal-integration!238
2021-07-29 18:29:17 +00:00
ac76e14129 Bvander/fix notify migrations 2021-07-29 18:29:17 +00:00
1c78f4d6d6 Merge branch 'bvander/fix-notify-migrations' into 'master'
migrations fix

See merge request grassrootseconomics/cic-internal-integration!237
2021-07-28 23:33:34 +00:00
0d6e228f8a migrations fix 2021-07-28 23:33:34 +00:00
7a3cb7ab75 Merge branch 'philip/notify-fixes' into 'master'
Philip/notify fixes

See merge request grassrootseconomics/cic-internal-integration!234
2021-07-23 15:15:30 +00:00
992c7b4022 Philip/notify fixes 2021-07-23 15:15:30 +00:00
f19173001e Merge branch 'lash/chainlib-nextgen' into 'master'
Implement nextgen chainlib and chainqueue upgrade

See merge request grassrootseconomics/cic-internal-integration!231
2021-07-21 17:34:51 +00:00
Louis Holbrook
f82bb4515d Implement nextgen chainlib and chainqueue upgrade 2021-07-21 17:34:51 +00:00
24e6db7d87 Merge branch 'philip/sms-cluster-issues' into 'master'
Censors sensitive config values.

See merge request grassrootseconomics/cic-internal-integration!209
2021-07-20 16:18:27 +00:00
ecdfb9bc5a Censors sensitive config values. 2021-07-20 16:18:27 +00:00
30415ac997 Merge branch 'bvander/data-seeding-profiles' into 'master'
e2e ussd import user scripts

See merge request grassrootseconomics/cic-internal-integration!230
2021-07-19 21:30:05 +00:00
d5a8b77349 e2e ussd import user scripts 2021-07-19 21:30:04 +00:00
Louis Holbrook
ed2521b582 Merge branch 'lash/migration-fix' into 'master'
Apply config changes to contract migration

See merge request grassrootseconomics/cic-internal-integration!229
2021-07-16 13:21:08 +00:00
Louis Holbrook
395930106a Apply config changes to contract migration 2021-07-16 13:21:07 +00:00
Louis Holbrook
ee1452e530 Merge branch 'lash/fix-ussd-impotrs' into 'master'
Make verify pass in ussd imports

See merge request grassrootseconomics/cic-internal-integration!228
2021-07-16 09:30:23 +00:00
nolash
8cdaf9f28a Fix confini instantiation in ussd 2021-07-15 00:54:48 +02:00
Louis Holbrook
402b968b6d Merge branch 'lash/dispatcher-leak' into 'master'
Fix dispatcher memory leak

See merge request grassrootseconomics/cic-internal-integration!220
2021-07-14 22:02:59 +00:00
Louis Holbrook
aa13517534 Fix dispatcher memory leak 2021-07-14 22:02:59 +00:00
Louis Holbrook
884b18f2f1 Merge branch 'lash/expand-tokens-in-list' into 'master'
Expand token symbol and decimals in tx listings

See merge request grassrootseconomics/cic-internal-integration!226
2021-07-14 15:06:20 +00:00
Louis Holbrook
494a8f3e88 Expand token symbol and decimals in tx listings 2021-07-14 15:06:20 +00:00
Geoff Turk
1214f605a7 Merge branch 'geoffturk/fix-geo-range' into 'master'
Fix out of range error for geolocation in import users script

See merge request grassrootseconomics/cic-internal-integration!225
2021-07-14 14:52:40 +00:00
Louis Holbrook
0783a6001c Merge branch 'lash/eth-abi-exorcism' into 'master'
Remove eth-abi dep in data seeding

See merge request grassrootseconomics/cic-internal-integration!224
2021-07-14 14:43:33 +00:00
Louis Holbrook
f9594b766a Remove eth-abi dep in data seeding 2021-07-14 14:43:32 +00:00
geoffturk
561ae62d5e Fix out of range error for geolocation in import users script 2021-07-13 09:42:53 +00:00
Louis Holbrook
d6782abbcc Merge branch 'lash/default-token-context' into 'master'
Add contextual token symbol and name defaults

Closes #85

See merge request grassrootseconomics/cic-internal-integration!219
2021-07-12 19:50:48 +00:00
Louis Holbrook
8f173fa30b Add contextual token symbol and name defaults 2021-07-12 19:50:48 +00:00
Louis Holbrook
3741cb3283 Merge branch 'lash/account-registry-filter-match' into 'master'
Only match account registry on correct contract address

Closes cic-eth#127

See merge request grassrootseconomics/cic-internal-integration!218
2021-07-12 19:50:32 +00:00
Louis Holbrook
54dd5acb62 Only match account registry on correct contract address 2021-07-12 19:50:32 +00:00
c52885a016 update data-seeding dockerignore 2021-07-12 09:44:05 -07:00
f0dd257e05 Merge branch 'bvander/fix-onchange-test-triggers' into 'master'
Fix onchange test triggers

See merge request grassrootseconomics/cic-internal-integration!223
2021-07-12 16:22:17 +00:00
e8c870d230 Merge branch 'bvander/fix-cic-meta-builds' into 'master'
updated cic-meta with new context ect.

See merge request grassrootseconomics/cic-internal-integration!222
2021-07-12 16:21:59 +00:00
4bb36a448d updated cic-meta with new context ect. 2021-07-12 16:21:58 +00:00
231163e2fc Update apps/cic-ussd/.gitlab-ci.yml, apps/cic-notify/.gitlab-ci.yml, apps/cic-cache/.gitlab-ci.yml files 2021-07-12 16:14:33 +00:00
e599933ef8 fix requirements 2021-07-11 10:08:04 -07:00
266bc3362d just remove the cic-eth slim image for rn 2021-07-11 09:51:08 -07:00
8350381754 fix the scripts path in cic-eth 2021-07-11 09:43:05 -07:00
6ddeacf036 fix the scripts path in cic-eth 2021-07-11 09:32:00 -07:00
fe017d2b0f Merge branch 'bvander/cic-eth-base-image-and-testing' into 'master'
Faster local builds with base image and buildkit and run all unit tests

See merge request grassrootseconomics/cic-internal-integration!217
2021-07-10 15:46:15 +00:00
d7973436e6 Faster local builds with base image and buildkit and run all unit tests 2021-07-10 15:46:14 +00:00
5025c31af6 update the create eth flags with redis host 2021-07-09 10:29:34 -07:00
Louis Holbrook
021c736707 Merge branch 'lash/demurrage-task' into 'master'
Add demurrage token amount adjust task

See merge request grassrootseconomics/cic-internal-integration!214
2021-07-08 15:28:05 +00:00
Louis Holbrook
e3028a5060 Add demurrage token amount adjust task 2021-07-08 15:28:04 +00:00
Louis Holbrook
5f6c57647f Merge branch 'lash/explicit-token-type' into 'master'
Add explicit token type toggle in contract migration

See merge request grassrootseconomics/cic-internal-integration!216
2021-07-07 07:56:45 +00:00
Louis Holbrook
ed029a936c Add explicit token type toggle in contract migration 2021-07-07 07:56:45 +00:00
Louis Holbrook
c559bb2fee Merge branch 'lash/free-api' into 'master'
Minimal dependencies in cic-eth api setup

Closes #75

See merge request grassrootseconomics/cic-internal-integration!210
2021-07-07 07:45:21 +00:00
Louis Holbrook
9b79034ed3 Minimal dependencies in cic-eth api setup 2021-07-07 07:45:21 +00:00
b7d5c6799f Merge branch 'bvander/data-seeding-updates' into 'master'
data-seeding image updates and tweak meta import

See merge request grassrootseconomics/cic-internal-integration!212
2021-07-05 17:14:47 +00:00
eef8bb2cf7 data-seeding image updates and tweak meta import 2021-07-05 17:14:46 +00:00
cf96fee430 Merge branch 'bvander/contract-migration-caching' into 'master'
refactor contract-migration and add buildkit caching

See merge request grassrootseconomics/cic-internal-integration!211
2021-07-05 17:13:27 +00:00
9740963431 refactor contract-migration and add buildkit caching 2021-07-05 17:13:26 +00:00
Louis Holbrook
a3c4932488 Merge branch 'lash/new-sarafu-token' into 'master'
Add flavors of demurrage token to dev deployment

See merge request grassrootseconomics/cic-internal-integration!181
2021-07-05 10:58:08 +00:00
Louis Holbrook
aa667951be Add flavors of demurrage token to dev deployment 2021-07-05 10:58:08 +00:00
Louis Holbrook
c2459cfd65 Merge branch 'lash/traffic' into 'master'
data-seeding: Rehabilitate traffic scripts

See merge request grassrootseconomics/cic-internal-integration!213
2021-07-04 17:19:21 +00:00
Louis Holbrook
e7102ff02d data-seeding: Rehabilitate traffic scripts 2021-07-04 17:19:21 +00:00
a942c785f6 Merge branch 'philip/improving-import-scripts-logging' into 'master'
Improves import scripts logging

Closes #74

See merge request grassrootseconomics/cic-internal-integration!207
2021-07-03 16:55:51 +00:00
70704b09ec Improves import scripts logging 2021-07-03 16:55:51 +00:00
Louis Holbrook
a075c55957 Merge branch 'lash/allowance' into 'master'
Add allowance check and transferFrom task

See merge request grassrootseconomics/cic-internal-integration!203
2021-06-30 18:15:40 +00:00
Louis Holbrook
6464f651ec Add allowance check and transferFrom task 2021-06-30 18:15:40 +00:00
Louis Holbrook
5145282946 Merge branch 'lash/faucet-verif' into 'master'
Consider faucet amount in verify balance

See merge request grassrootseconomics/cic-internal-integration!206
2021-06-30 18:14:53 +00:00
nolash
1e87f2ed31 Consider faucet amount in verify balance 2021-06-30 17:22:26 +02:00
Louis Holbrook
c852f41d76 Merge branch 'lash/move-to-chainlib-eth' into 'master'
Move to chainlib-eth dependency

See merge request grassrootseconomics/cic-internal-integration!199
2021-06-30 14:44:18 +00:00
Louis Holbrook
f8e68cff96 Move to chainlib-eth dependency 2021-06-30 14:44:17 +00:00
7027d77836 Merge branch 'philip/refactor-integration-tests' into 'master'
Philip/refactor integration tests

See merge request grassrootseconomics/cic-internal-integration!182
2021-06-30 14:27:56 +00:00
d356f8167d Philip/refactor integration tests 2021-06-30 14:27:56 +00:00
753d21fe95 Merge branch 'bvander/data-seeding-build-improvements' into 'master'
uses the new base image

See merge request grassrootseconomics/cic-internal-integration!204
2021-06-29 17:17:18 +00:00
3b6e031746 Merge branch 'philip/db-session-management' into 'master'
Philip/db session management

Closes cic-ussd#53

See merge request grassrootseconomics/cic-internal-integration!193
2021-06-29 10:49:25 +00:00
b1d5d45eef Philip/db session management 2021-06-29 10:49:25 +00:00
53317cb912 Merge branch 'philip/notify-queue-setting' into 'master'
Enable api level setting of queue value.

See merge request grassrootseconomics/cic-internal-integration!184
2021-06-29 10:21:43 +00:00
18382a1f35 Merge branch 'bvander/integration-notes' into 'master'
integration updates: meta imports

See merge request grassrootseconomics/cic-internal-integration!201
2021-06-28 22:40:54 +00:00
29e91fafab integration updates: meta imports 2021-06-28 22:40:54 +00:00
5b20a9a24a Merge branch 'bvander/better-data-seeding-container' into 'master'
add key store and vars

See merge request grassrootseconomics/cic-internal-integration!202
2021-06-28 22:40:04 +00:00
a252195bdc uses the new base image 2021-06-28 10:56:15 -07:00
Spencer Ofwiti
f1be3b633c Merge branch 'lash/downgrade-chainsyncer' into 'master'
Downgrade chainsyncer

See merge request grassrootseconomics/cic-internal-integration!200
2021-06-28 15:36:56 +00:00
nolash
e59a71188c Downgrade chainsyncer 2021-06-28 17:18:52 +02:00
1d0eb06f2f Merge branch 'bvander/data-seeding-keystore' into 'master'
add keystore

See merge request grassrootseconomics/cic-internal-integration!197
2021-06-26 17:12:46 +00:00
57127132b5 add keystore 2021-06-26 09:50:58 -07:00
0bf2c35fcd add key store and vars 2021-06-26 09:50:03 -07:00
d046595764 Merge branch 'philip/remove-hardcoded-import-script-configs' into 'master'
Refactors hardcoded config vars.

See merge request grassrootseconomics/cic-internal-integration!196
2021-06-26 15:58:07 +00:00
9dd7ec88fd Refactors hardcoded config vars. 2021-06-26 18:51:25 +03:00
282fd2ff52 Merge branch 'bvander/better-data-seeding-container' into 'master'
fix paths for data seeding container

See merge request grassrootseconomics/cic-internal-integration!195
2021-06-25 23:06:25 +00:00
8f85598861 fix paths and stuff 2021-06-25 15:59:10 -07:00
8529c349ca Merge branch 'philip/fix-import-script-pointers' into 'master'
Philip/fix import script pointers

See merge request grassrootseconomics/cic-internal-integration!194
2021-06-25 15:36:06 +00:00
4368d2bf59 Philip/fix import script pointers 2021-06-25 15:36:05 +00:00
da3c812bf5 Merge branch 'philip/add-age-metadata' into 'master'
Philip/add age metadata

See merge request grassrootseconomics/cic-internal-integration!186
2021-06-23 13:25:09 +00:00
82b1e87462 Philip/add age metadata 2021-06-23 13:25:09 +00:00
e13c423daf Merge branch 'philip/custom-metadata' into 'master'
Philip/custom metadata

See merge request grassrootseconomics/cic-internal-integration!192
2021-06-23 08:54:34 +00:00
56b3bd751d Philip/custom metadata 2021-06-23 08:54:34 +00:00
4f41c5bacf Merge branch 'philip/notify-sender-id-resolution' into 'master'
Handle empty string defaults in kubernetes secrets.

See merge request grassrootseconomics/cic-internal-integration!183
2021-06-23 07:02:22 +00:00
07583f0c3b Handle empty string defaults in kubernetes secrets. 2021-06-23 07:02:22 +00:00
0ae912082c Merge branch 'philip/refactor-phone-number-input-handling' into 'master'
Refactors handling of phone number inputs during transactions

See merge request grassrootseconomics/cic-internal-integration!185
2021-06-23 06:44:01 +00:00
094f4d4298 Refactors handling of phone number inputs during transactions 2021-06-23 06:44:01 +00:00
Spencer Ofwiti
9471b1d8ab Merge branch 'spencer/fix-import-scripts-build' into 'master'
Fix build process for import scripts.

See merge request grassrootseconomics/cic-internal-integration!188
2021-06-23 05:49:06 +00:00
Spencer Ofwiti
57100366d8 Fix build process for import scripts. 2021-06-23 05:49:06 +00:00
71e0973020 Merge branch 'lash/check-import-ussd' into 'master'
Rehabilitate ussd import scripts

Closes #57

See merge request grassrootseconomics/cic-internal-integration!166
2021-06-23 04:29:39 +00:00
Louis Holbrook
12ab5c2f66 Rehabilitate ussd import scripts 2021-06-23 04:29:38 +00:00
a804552620 Merge branch 'bvander/add-venv-to-ignore' into 'master'
add .venv folder

See merge request grassrootseconomics/cic-internal-integration!191
2021-06-22 21:35:21 +00:00
0319fa6076 add .venv folder 2021-06-22 14:26:41 -07:00
91dfc51d54 Merge branch 'bvander/change-meta-key-dir' into 'master'
change pgp key dir

See merge request grassrootseconomics/cic-internal-integration!190
2021-06-22 21:25:58 +00:00
4fd861f080 change pgp key dir 2021-06-22 14:15:38 -07:00
Louis Holbrook
28de7a4eac Merge branch 'lash/loglines' into 'master'
Loglines and dep bump

See merge request grassrootseconomics/cic-internal-integration!187
2021-06-19 06:49:42 +00:00
Louis Holbrook
a31e79b0f7 Loglines and dep bump 2021-06-19 06:49:42 +00:00
eb2f71aee0 Enable api level setting of queue value. 2021-06-14 10:38:23 +03:00
Spencer Ofwiti
e5b1352970 Merge branch 'spencer/meta-cicd' into 'master'
Refactor meta ci-cd pipeline.

See merge request grassrootseconomics/cic-internal-integration!171
2021-06-07 16:11:03 +00:00
Spencer Ofwiti
89b90da5d2 Refactor meta ci-cd pipeline. 2021-06-07 16:11:03 +00:00
9607994c31 Merge branch 'philip/add-support-phone-number' into 'master'
Philip/add support phone number

See merge request grassrootseconomics/cic-internal-integration!175
2021-06-07 08:02:03 +00:00
0da617d29e Philip/add support phone number 2021-06-07 08:02:03 +00:00
56bcad16a5 Merge branch 'philip/refactor-metadata-entry' into 'master'
Philip/refactor signup steps.

See merge request grassrootseconomics/cic-internal-integration!173
2021-06-07 07:47:03 +00:00
77d9936e39 Philip/refactor signup steps. 2021-06-07 07:47:02 +00:00
Louis Holbrook
72aeefc78b Merge branch 'lash/simple-compose' into 'master'
Simplify docker compose setup

See merge request grassrootseconomics/cic-internal-integration!180
2021-06-04 20:10:16 +00:00
nolash
fab9b0c520 Add long timeout to first account create in contract migration part 2 2021-06-04 22:00:06 +02:00
9566f8c8e2 Merge branch 'philip/qfix-vbumps' into 'master'
Bumps cic-eth and cic-base versions.

See merge request grassrootseconomics/cic-internal-integration!179
2021-06-04 08:34:14 +00:00
007d7a5121 Bumps cic-eth and cic-base versions. 2021-06-04 11:12:04 +03:00
fc20849aff Merge branch 'bvander/contract-migration-requirements' into 'master'
pull the req out of the container and bump em

See merge request grassrootseconomics/cic-internal-integration!178
2021-06-03 18:54:19 +00:00
1605e53216 pull the req out of the container and bump em 2021-06-03 11:43:13 -07:00
200fdf0e3c Merge branch 'no-host-mount-startup' into 'master'
no local file mounts for config files

See merge request grassrootseconomics/cic-internal-integration!177
2021-06-03 17:22:49 +00:00
022db04198 no local file mounts for config files 2021-06-03 17:22:47 +00:00
1c17048981 Update README.md 2021-06-03 17:19:55 +00:00
Louis Holbrook
04c0963f33 Merge branch 'lash/tmp-check-chaintool' into 'master'
Update queue/syncer module structure

See merge request grassrootseconomics/cic-internal-integration!176
2021-06-03 13:51:55 +00:00
Louis Holbrook
096ed9bc27 Update queue/syncer module structure 2021-06-03 13:51:55 +00:00
1a931eced4 Merge branch 'philip/management-integration-tests' into 'master'
Philip/management integration tests

See merge request grassrootseconomics/cic-internal-integration!159
2021-06-03 13:40:51 +00:00
ed9e032890 Philip/management integration tests 2021-06-03 13:40:51 +00:00
Louis Holbrook
69ae9b7c07 Merge branch 'lash/update-imports-readme' into 'master'
Bring import readme up-to-date

See merge request grassrootseconomics/cic-internal-integration!167
2021-06-02 17:55:46 +00:00
Louis Holbrook
634d3fb401 Bring import readme up-to-date 2021-06-02 17:55:46 +00:00
Louis Holbrook
65f722b291 Merge branch 'lash/stale-import-deps' into 'master'
Upgrade cic-eth package for imports

See merge request grassrootseconomics/cic-internal-integration!169
2021-06-02 16:09:40 +00:00
Louis Holbrook
0ad0f9981c Upgrade cic-eth package for imports 2021-06-02 16:09:39 +00:00
Louis Holbrook
5fb0f4a2e9 Merge branch 'lash/horse-cart' into 'master'
Fix false offset limit error

Closes #60

See merge request grassrootseconomics/cic-internal-integration!174
2021-06-02 15:27:30 +00:00
nolash
41a96b5584 Int comparisons on block numbers in cic cache lookup 2021-06-02 17:11:15 +02:00
Geoff Turk
d0f2bc0120 Merge branch 'geoffturk/mock-data' into 'master'
Add better mocked data

See merge request grassrootseconomics/cic-internal-integration!172
2021-06-02 14:59:00 +00:00
Geoff Turk
e2946052e0 Add more better mocked data 2021-06-02 14:53:53 +02:00
Louis Holbrook
546d69f1e9 Merge branch 'lash/coveralls-that-coverall' into 'master'
cic-eth: Reach 90% test coverage

Closes cic-eth#125

See merge request grassrootseconomics/cic-internal-integration!168
2021-05-31 15:34:17 +00:00
Louis Holbrook
fbf7351238 cic-eth: Reach 90% test coverage 2021-05-31 15:34:16 +00:00
Geoff Turk
b886384fa8 Merge branch 'geoffturk/fix-traffic' into 'master'
Fix traffic script

See merge request grassrootseconomics/cic-internal-integration!170
2021-05-27 10:38:08 +00:00
Geoff Turk
277033f3b5 Fix traffic script 2021-05-27 10:38:07 +00:00
4ae094fd30 Merge branch 'cic-eth-unittest' into 'master'
Unit tests for cic-eth

See merge request grassrootseconomics/cic-internal-integration!164
2021-05-25 16:22:26 +00:00
cb239f112a Unit tests for cic-eth 2021-05-25 16:22:26 +00:00
Geoff Turk
d971a6eded Merge branch 'fix-meta-data-seeding' into 'master'
Fix path to PGP exports directory

See merge request grassrootseconomics/cic-internal-integration!165
2021-05-21 16:42:21 +00:00
Spencer Ofwiti
b0a6df0177 Merge branch 'spencer/metadata-identifiers' into 'master'
Add meta update cli tool.

See merge request grassrootseconomics/cic-internal-integration!138
2021-05-21 09:42:08 +00:00
Spencer Ofwiti
92c9df4e19 Add meta update cli tool. 2021-05-21 09:42:08 +00:00
Geoff Turk
9c49d568e0 Fix path to PGP exports directory 2021-05-21 10:51:32 +02:00
Louis Holbrook
d7113f3923 Merge branch 'lash/rehabilitate-traffic-2' into 'master'
Rehabilitate traffic generator script

See merge request grassrootseconomics/cic-internal-integration!145
2021-05-20 21:25:15 +00:00
Louis Holbrook
c569fe4b17 Rehabilitate traffic generator script 2021-05-20 21:25:14 +00:00
1c650df27d Merge branch 'bvander/move-scripts-to-e2e-folder' into 'master'
move files out of scripts folder to their own dir

See merge request grassrootseconomics/cic-internal-integration!137
2021-05-20 14:31:08 +00:00
a31b7bc9cd move files out of scripts folder to their own dir 2021-05-20 14:31:08 +00:00
Geoff Turk
78ff58c1a2 Merge branch 'geoff/data-success' into 'master'
Add success field to transactions_all_data

See merge request grassrootseconomics/cic-internal-integration!163
2021-05-20 10:28:07 +00:00
1676addbeb Merge branch 'philip/meta-cluster-bug' into 'master'
Refactors to handle error in metadata handling

Closes cic-ussd#45

See merge request grassrootseconomics/cic-internal-integration!160
2021-05-19 16:25:10 +00:00
1efc25ac15 Refactors to handle error in metadata handling 2021-05-19 16:25:10 +00:00
Louis Holbrook
db2ec0dcfa Merge branch 'philip/notify-errors' into 'master'
Philip/notify errors

Closes cic-notify#4

See merge request grassrootseconomics/cic-internal-integration!161
2021-05-19 16:13:06 +00:00
5148e6428b Philip/notify errors 2021-05-19 16:13:06 +00:00
Louis Holbrook
0c186ed968 Merge branch 'lash/rehabilitate-tests-eth' into 'master'
Fix outdated module names in cic-eth tests

See merge request grassrootseconomics/cic-internal-integration!162
2021-05-19 15:11:08 +00:00
Louis Holbrook
c44439bd90 Fix outdated module names in cic-eth tests 2021-05-19 15:11:08 +00:00
Geoff Turk
0411603078 Add success field to transactions_all_data 2021-05-19 16:47:22 +02:00
eee895ea71 Merge branch 'willruddick-master-patch-91858' into 'master'
small updates. note other gender

See merge request grassrootseconomics/cic-internal-integration!154
2021-05-19 10:26:52 +00:00
Louis Holbrook
a5ca898532 Merge branch 'lash/update-contracts-in-migration-2' into 'master'
imports: Fix stale dep in sovereign import users script

See merge request grassrootseconomics/cic-internal-integration!149
2021-05-19 09:57:03 +00:00
Louis Holbrook
6d8508aebf imports: Fix stale dep in sovereign import users script 2021-05-19 09:57:02 +00:00
Louis Holbrook
f8f66984d2 Merge branch 'lash/no-ussd-contamination' into 'master'
Isolate ussd-related data files in imports

See merge request grassrootseconomics/cic-internal-integration!150
2021-05-19 09:55:24 +00:00
Louis Holbrook
0f02dd1b7c Isolate ussd-related data files in imports 2021-05-19 09:55:24 +00:00
63a4a82ab0 Merge branch 'philip/replicate-wills-changes' into 'master'
Replicates changes in broken MR by will.

See merge request grassrootseconomics/cic-internal-integration!158
2021-05-19 08:25:19 +00:00
949c1070a9 Replicates changes in broken MR by will. 2021-05-19 11:19:29 +03:00
5d9fbe9b64 Merge branch 'willruddick-master-patch-28332' into 'master'
shortened and changed service code

See merge request grassrootseconomics/cic-internal-integration!152
2021-05-19 07:47:09 +00:00
873a3f082a shortened and changed service code 2021-05-19 07:47:09 +00:00
7b408cf564 Merge branch 'willruddick-master-patch-28450' into 'master'
small changes, note the 'other' gender

See merge request grassrootseconomics/cic-internal-integration!151
2021-05-19 07:33:37 +00:00
Louis Holbrook
9dfbd7034c Merge branch 'lash/decimals-in-api' into 'master'
cic-eth-tasker: Add decimals and token name to default token api call return struct

Closes cic-eth#123

See merge request grassrootseconomics/cic-internal-integration!148
2021-05-19 06:59:43 +00:00
Louis Holbrook
235f5cede8 cic-eth-tasker: Add decimals and token name to default token api call return struct 2021-05-19 06:59:42 +00:00
Geoff Turk
0a59539f9a Merge branch 'lash/cache-data-api' into 'master'
cic-cache: Add data API

Closes cic-cache#11

See merge request grassrootseconomics/cic-internal-integration!157
2021-05-18 17:13:57 +00:00
Louis Holbrook
60b36945df cic-cache: Add data API 2021-05-18 17:13:57 +00:00
e8512ebbae small updates. note other gender 2021-05-17 11:27:45 +00:00
f2c955c60b small changes, note the 'other' gender 2021-05-17 11:06:10 +00:00
613 changed files with 21236 additions and 9779 deletions

7
.gitignore vendored
View File

@@ -8,3 +8,10 @@ gmon.out
*.egg-info *.egg-info
dist/ dist/
build/ build/
**/*sqlite
**/.nyc_output
**/coverage
**/.venv
.idea
**/.vim
**/*secret.yaml

View File

@@ -1,13 +1,43 @@
include: include:
- local: 'ci_templates/.cic-template.yml' #- local: 'ci_templates/.cic-template.yml' #kaniko build templates
- local: 'apps/contract-migration/.gitlab-ci.yml' # these includes are app specific unit tests
- local: 'apps/cic-eth/.gitlab-ci.yml' - local: 'apps/cic-eth/.gitlab-ci.yml'
- local: 'apps/cic-ussd/.gitlab-ci.yml' - local: 'apps/cic-ussd/.gitlab-ci.yml'
- local: 'apps/cic-notify/.gitlab-ci.yml' - local: 'apps/cic-notify/.gitlab-ci.yml'
- local: 'apps/cic-meta/.gitlab-ci.yml' - local: 'apps/cic-meta/.gitlab-ci.yml'
- local: 'apps/cic-cache/.gitlab-ci.yml' - local: 'apps/cic-cache/.gitlab-ci.yml'
#- local: 'apps/contract-migration/.gitlab-ci.yml'
#- local: 'apps/data-seeding/.gitlab-ci.yml'
stages: stages:
- build - build
- test - test
- release - deploy
image: registry.gitlab.com/grassrootseconomics/cic-internal-integration/docker-with-compose:latest
variables:
DOCKER_BUILDKIT: "1"
COMPOSE_DOCKER_CLI_BUILD: "1"
CI_DEBUG_TRACE: "true"
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
# runs on protected branches and pushes to repo
build-push:
stage: build
tags:
- integration
#script:
# - TAG=$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA sh ./scripts/build-push.sh
script:
- TAG=latest sh ./scripts/build-push.sh
rules:
- if: $CI_COMMIT_REF_PROTECTED == "true"
when: always
deploy-dev:
stage: deploy
trigger: grassrootseconomics/devops
when: manual

View File

@@ -2,25 +2,21 @@
## Getting started ## Getting started
## Make some keys This repo uses docker-compose and docker buildkit. Set the following environment variables to get started:
``` ```
docker build -t bloxie . && docker run -v "$(pwd)/keys:/root/keys" --rm -it -t bloxie account new --chain /root/bloxberg.json --keys-path /root/keys export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1
``` ```
start services, database, redis and local ethereum node
### Prepare the repo
This is stuff we need to put in makefile but for now...
File mounts and permisssions need to be set
``` ```
chmod -R 755 scripts/initdb apps/cic-meta/scripts/initdb docker-compose up -d
````
start cluster
``` ```
docker-compose up
Run app/contract-migration to deploy contracts
```
RUN_MASK=3 docker-compose up contract-migration
``` ```
stop cluster stop cluster
@@ -28,9 +24,9 @@ stop cluster
docker-compose down docker-compose down
``` ```
delete data stop cluster and delete data
``` ```
docker-compose down -v docker-compose down -v --remove-orphans
``` ```
rebuild an images rebuild an images
@@ -38,5 +34,7 @@ rebuild an images
docker-compose up --build <service_name> docker-compose up --build <service_name>
``` ```
Deployment variables are writtend to service-configs/.env after everthing is up. to delete the buildkit cache
```
docker builder prune --filter type=exec.cachemount
```

View File

@@ -1,34 +0,0 @@
# The solc image messes up the alpine environment, so we have to go all over again
FROM python:3.8.6-slim-buster
LABEL authors="Louis Holbrook <dev@holbrook.no> 0826EDA1702D1E87C6E2875121D2E7BB88C2A746"
LABEL spdx-license-identifier="GPL-3.0-or-later"
LABEL description="Base layer for buiding development images for the cic component suite"
RUN apt-get update && \
apt-get install -y git gcc g++ libpq-dev && \
apt-get install -y vim gawk jq telnet openssl iputils-ping curl wget gnupg socat bash procps make python2 postgresql-client
RUN echo installing nodejs tooling
COPY ./dev/nvm.sh /root/
# Install nvm with node and npm
# https://stackoverflow.com/questions/25899912/how-to-install-nvm-in-docker
ENV NVM_DIR /root/.nvm
ENV NODE_VERSION 15.3.0
ENV BANCOR_NODE_VERSION 10.16.0
RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash \
&& . $NVM_DIR/nvm.sh \
&& nvm install $NODE_VERSION \
&& nvm alias default $NODE_VERSION \
&& nvm use $NODE_VERSION \
# So many ridiculously stupid issues with node in docker that take oceans of absolutely wasted time to resolve
# owner of these files is "1001" by default - wtf
&& chown -R root:root "$NVM_DIR/versions/node/v$NODE_VERSION"
ENV NODE_PATH $NVM_DIR/versions/node//v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node//v$NODE_VERSION/bin:$PATH

View File

@@ -1 +0,0 @@
## this is an example base image if we wanted one for all the other apps. Its just OS level things

View File

@@ -2,4 +2,6 @@
omit = omit =
.venv/* .venv/*
scripts/* scripts/*
cic_cache/db/postgres/* cic_cache/db/migrations/*
cic_cache/version.py
cic_cache/cli

View File

@@ -0,0 +1,4 @@
.git
.cache
.dot
**/doc

View File

@@ -1,22 +1,17 @@
.cic_cache_variables: build-test-cic-cache:
variables: stage: test
APP_NAME: cic-cache tags:
DOCKERFILE_PATH: $APP_NAME/docker/Dockerfile - integration
variables:
.cic_cache_changes_target: APP_NAME: cic-cache
rules: MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA
- changes: script:
- $CONTEXT/$APP_NAME/* - cd apps/cic-cache
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile .
build-mr-cic-cache: - docker run $MR_IMAGE_TAG sh docker/run_tests.sh
extends: allow_failure: true
- .cic_cache_changes_target rules:
- .py_build_merge_request - if: $CI_PIPELINE_SOURCE == "merge_request_event"
- .cic_cache_variables changes:
- apps/$APP_NAME/**/*
build-push-cic-cache: when: always
extends:
- .py_build_push
- .cic_cache_variables

View File

@@ -0,0 +1 @@
include *requirements.txt cic_cache/data/config/*

View File

@@ -0,0 +1 @@
# CIC-CACHE

View File

@@ -55,15 +55,37 @@ class Api:
queue=callback_queue, queue=callback_queue,
) )
def list(self, offset, limit, address=None): def list(self, offset=0, limit=100, address=None, oldest=False):
s = celery.signature( s = celery.signature(
'cic_cache.tasks.tx.tx_filter', 'cic_cache.tasks.tx.tx_filter',
[ [
0, offset,
100, limit,
address, address,
oldest,
], ],
queue=None queue=self.queue,
)
if self.callback_param != None:
s.link(self.callback_success).on_error(self.callback_error)
t = s.apply_async()
return t
def list_content(self, offset=0, limit=100, address=None, block_offset=None, block_limit=None, oldest=False):
s = celery.signature(
'cic_cache.tasks.tx.tx_filter_content',
[
offset,
limit,
address,
block_offset,
block_limit,
oldest,
],
queue=self.queue,
) )
if self.callback_param != None: if self.callback_param != None:
s.link(self.callback_success).on_error(self.callback_error) s.link(self.callback_success).on_error(self.callback_error)

View File

@@ -1,30 +1,42 @@
# standard imports # standard imports
import logging import logging
import datetime
# third-party imports # external imports
import moolb import moolb
# local imports # local imports
from cic_cache.db import list_transactions_mined from cic_cache.db.list import (
from cic_cache.db import list_transactions_account_mined list_transactions_mined,
list_transactions_account_mined,
list_transactions_mined_with_data,
list_transactions_mined_with_data_index,
list_transactions_account_mined_with_data_index,
list_transactions_account_mined_with_data,
)
logg = logging.getLogger() logg = logging.getLogger()
class BloomCache: DEFAULT_FILTER_SIZE = 8192 * 8
DEFAULT_LIMIT = 100
class Cache:
def __init__(self, session): def __init__(self, session):
self.session = session self.session = session
class BloomCache(Cache):
@staticmethod @staticmethod
def __get_filter_size(n): def __get_filter_size(n):
n = 8192 * 8 n = DEFAULT_FILTER_SIZE
logg.warning('filter size hardcoded to {}'.format(n)) logg.warning('filter size hardcoded to {}'.format(n))
return n return n
def load_transactions(self, offset, limit): def load_transactions(self, offset, limit, block_offset=None, block_limit=None, oldest=False):
"""Retrieves a list of transactions from cache and creates a bloom filter pointing to blocks and transactions. """Retrieves a list of transactions from cache and creates a bloom filter pointing to blocks and transactions.
Block and transaction numbers are serialized as 32-bit big-endian numbers. The input to the second bloom filter is the concatenation of the serialized block number and transaction index. Block and transaction numbers are serialized as 32-bit big-endian numbers. The input to the second bloom filter is the concatenation of the serialized block number and transaction index.
@@ -41,7 +53,7 @@ class BloomCache:
:return: Lowest block, bloom filter for blocks, bloom filter for blocks|tx :return: Lowest block, bloom filter for blocks, bloom filter for blocks|tx
:rtype: tuple :rtype: tuple
""" """
rows = list_transactions_mined(self.session, offset, limit) rows = list_transactions_mined(self.session, offset, limit, block_offset=block_offset, block_limit=block_limit, oldest=oldest)
f_block = moolb.Bloom(BloomCache.__get_filter_size(limit), 3) f_block = moolb.Bloom(BloomCache.__get_filter_size(limit), 3)
f_blocktx = moolb.Bloom(BloomCache.__get_filter_size(limit), 3) f_blocktx = moolb.Bloom(BloomCache.__get_filter_size(limit), 3)
@@ -50,7 +62,12 @@ class BloomCache:
for r in rows: for r in rows:
if highest_block == -1: if highest_block == -1:
highest_block = r[0] highest_block = r[0]
lowest_block = r[0] lowest_block = r[0]
else:
if oldest:
highest_block = r[0]
else:
lowest_block = r[0]
block = r[0].to_bytes(4, byteorder='big') block = r[0].to_bytes(4, byteorder='big')
tx = r[1].to_bytes(4, byteorder='big') tx = r[1].to_bytes(4, byteorder='big')
f_block.add(block) f_block.add(block)
@@ -59,7 +76,7 @@ class BloomCache:
return (lowest_block, highest_block, f_block.to_bytes(), f_blocktx.to_bytes(),) return (lowest_block, highest_block, f_block.to_bytes(), f_blocktx.to_bytes(),)
def load_transactions_account(self, address, offset, limit): def load_transactions_account(self, address, offset, limit, block_offset=None, block_limit=None, oldest=False):
"""Same as load_transactions(...), but only retrieves transactions where the specified account address is sender or recipient. """Same as load_transactions(...), but only retrieves transactions where the specified account address is sender or recipient.
:param address: Address to retrieve transactions for. :param address: Address to retrieve transactions for.
@@ -71,7 +88,7 @@ class BloomCache:
:return: Lowest block, bloom filter for blocks, bloom filter for blocks|tx :return: Lowest block, bloom filter for blocks, bloom filter for blocks|tx
:rtype: tuple :rtype: tuple
""" """
rows = list_transactions_account_mined(self.session, address, offset, limit) rows = list_transactions_account_mined(self.session, address, offset, limit, block_offset=block_offset, block_limit=block_limit, oldest=oldest)
f_block = moolb.Bloom(BloomCache.__get_filter_size(limit), 3) f_block = moolb.Bloom(BloomCache.__get_filter_size(limit), 3)
f_blocktx = moolb.Bloom(BloomCache.__get_filter_size(limit), 3) f_blocktx = moolb.Bloom(BloomCache.__get_filter_size(limit), 3)
@@ -80,10 +97,74 @@ class BloomCache:
for r in rows: for r in rows:
if highest_block == -1: if highest_block == -1:
highest_block = r[0] highest_block = r[0]
lowest_block = r[0] lowest_block = r[0]
else:
if oldest:
highest_block = r[0]
else:
lowest_block = r[0]
block = r[0].to_bytes(4, byteorder='big') block = r[0].to_bytes(4, byteorder='big')
tx = r[1].to_bytes(4, byteorder='big') tx = r[1].to_bytes(4, byteorder='big')
f_block.add(block) f_block.add(block)
f_blocktx.add(block + tx) f_blocktx.add(block + tx)
logg.debug('added block {} tx {} lo {} hi {}'.format(r[0], r[1], lowest_block, highest_block)) logg.debug('added block {} tx {} lo {} hi {}'.format(r[0], r[1], lowest_block, highest_block))
return (lowest_block, highest_block, f_block.to_bytes(), f_blocktx.to_bytes(),) return (lowest_block, highest_block, f_block.to_bytes(), f_blocktx.to_bytes(),)
class DataCache(Cache):
def load_transactions_with_data(self, offset, limit, block_offset=None, block_limit=None, oldest=False):
if limit == 0:
limit = DEFAULT_LIMIT
rows = list_transactions_mined_with_data(self.session, offset, limit, block_offset, block_limit, oldest=oldest)
return self.__process_rows(rows, oldest)
def load_transactions_account_with_data(self, address, offset, limit, block_offset=None, block_limit=None, oldest=False):
if limit == 0:
limit = DEFAULT_LIMIT
rows = list_transactions_account_mined_with_data(self.session, address, offset, limit, block_offset, block_limit, oldest=oldest)
return self.__process_rows(rows, oldest)
def __process_rows(self, rows, oldest):
tx_cache = []
highest_block = -1;
lowest_block = -1;
date_is_str = None # stick this in startup
for r in rows:
if highest_block == -1:
highest_block = r['block_number']
lowest_block = r['block_number']
else:
if oldest:
highest_block = r['block_number']
else:
lowest_block = r['block_number']
tx_type = 'unknown'
if r['value'] != None:
tx_type = '{}.{}'.format(r['domain'], r['value'])
if date_is_str == None:
date_is_str = type(r['date_block']).__name__ == 'str'
o = {
'block_number': r['block_number'],
'tx_hash': r['tx_hash'],
'date_block': r['date_block'],
'sender': r['sender'],
'recipient': r['recipient'],
'from_value': int(r['from_value']),
'to_value': int(r['to_value']),
'source_token': r['source_token'],
'destination_token': r['destination_token'],
'success': r['success'],
'tx_type': tx_type,
}
if date_is_str:
o['date_block'] = datetime.datetime.fromisoformat(r['date_block'])
tx_cache.append(o)
return (lowest_block, highest_block, tx_cache)

View File

@@ -0,0 +1,15 @@
# local imports
from .base import *
from .chain import (
EthChainInterface,
chain_interface,
)
from .rpc import RPC
from .arg import ArgumentParser
from .config import Config
from .celery import CeleryApp
from .registry import (
connect_registry,
connect_token_registry,
connect_declarator,
)

View File

@@ -0,0 +1,20 @@
# external imports
from chainlib.eth.cli import ArgumentParser as BaseArgumentParser
# local imports
from .base import (
CICFlag,
Flag,
)
class ArgumentParser(BaseArgumentParser):
def process_local_flags(self, local_arg_flags):
if local_arg_flags & CICFlag.CELERY:
self.add_argument('-q', '--celery-queue', dest='celery_queue', type=str, default='cic-cache', help='Task queue')
if local_arg_flags & CICFlag.SYNCER:
self.add_argument('--offset', type=int, default=0, help='Start block height for initial history sync')
self.add_argument('--no-history', action='store_true', dest='no_history', help='Skip initial history sync')
if local_arg_flags & CICFlag.CHAIN:
self.add_argument('-r', '--registry-address', type=str, dest='registry_address', help='CIC registry contract address')

View File

@@ -0,0 +1,31 @@
# standard imports
import enum
# external imports
from chainlib.eth.cli import (
argflag_std_read,
argflag_std_write,
argflag_std_base,
Flag,
)
class CICFlag(enum.IntEnum):
# celery - nibble 1
CELERY = 1
# redis - nibble 2
# REDIS = 16
# REDIS_CALLBACK = 32
# chain - nibble 3
CHAIN = 256
# sync - nibble 4
SYNCER = 4096
argflag_local_task = CICFlag.CELERY
#argflag_local_taskcallback = argflag_local_task | CICFlag.REDIS | CICFlag.REDIS_CALLBACK
argflag_local_chain = CICFlag.CHAIN
argflag_local_sync = CICFlag.SYNCER | CICFlag.CHAIN

View File

@@ -0,0 +1,24 @@
# standard imports
import logging
# external imports
import celery
logg = logging.getLogger(__name__)
class CeleryApp:
@classmethod
def from_config(cls, config):
backend_url = config.get('CELERY_RESULT_URL')
broker_url = config.get('CELERY_BROKER_URL')
celery_app = None
if backend_url != None:
celery_app = celery.Celery(broker=broker_url, backend=backend_url)
logg.info('creating celery app on {} with backend on {}'.format(broker_url, backend_url))
else:
celery_app = celery.Celery(broker=broker_url)
logg.info('creating celery app without results backend on {}'.format(broker_url))
return celery_app

View File

@@ -0,0 +1,21 @@
# external imports
from chainlib.eth.block import (
block_by_number,
Block,
)
from chainlib.eth.tx import (
receipt,
Tx,
)
from chainlib.interface import ChainInterface
class EthChainInterface(ChainInterface):
def __init__(self):
self._tx_receipt = receipt
self._block_by_number = block_by_number
self._block_from_src = Block.from_src
self._src_normalize = Tx.src_normalize
chain_interface = EthChainInterface()

View File

@@ -0,0 +1,63 @@
# standard imports
import os
import logging
# external imports
from chainlib.eth.cli import (
Config as BaseConfig,
Flag,
)
# local imports
from .base import CICFlag
script_dir = os.path.dirname(os.path.realpath(__file__))
logg = logging.getLogger(__name__)
class Config(BaseConfig):
local_base_config_dir = os.path.join(script_dir, '..', 'data', 'config')
@classmethod
def from_args(cls, args, arg_flags, local_arg_flags, extra_args={}, default_config_dir=None, base_config_dir=None, default_fee_limit=None):
expanded_base_config_dir = [cls.local_base_config_dir]
if base_config_dir != None:
if isinstance(base_config_dir, str):
base_config_dir = [base_config_dir]
for d in base_config_dir:
expanded_base_config_dir.append(d)
config = BaseConfig.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=default_config_dir, base_config_dir=expanded_base_config_dir, load_callback=None)
local_args_override = {}
# if local_arg_flags & CICFlag.REDIS:
# local_args_override['REDIS_HOST'] = getattr(args, 'redis_host')
# local_args_override['REDIS_PORT'] = getattr(args, 'redis_port')
# local_args_override['REDIS_DB'] = getattr(args, 'redis_db')
# local_args_override['REDIS_TIMEOUT'] = getattr(args, 'redis_timeout')
if local_arg_flags & CICFlag.CHAIN:
local_args_override['CIC_REGISTRY_ADDRESS'] = getattr(args, 'registry_address')
if local_arg_flags & CICFlag.CELERY:
local_args_override['CELERY_QUEUE'] = getattr(args, 'celery_queue')
if local_arg_flags & CICFlag.SYNCER:
local_args_override['SYNCER_OFFSET'] = getattr(args, 'offset')
local_args_override['SYNCER_NO_HISTORY'] = getattr(args, 'no_history')
config.dict_override(local_args_override, 'local cli args')
# if local_arg_flags & CICFlag.REDIS_CALLBACK:
# config.add(getattr(args, 'redis_host_callback'), '_REDIS_HOST_CALLBACK')
# config.add(getattr(args, 'redis_port_callback'), '_REDIS_PORT_CALLBACK')
if local_arg_flags & CICFlag.CELERY:
config.add(config.true('CELERY_DEBUG'), 'CELERY_DEBUG', exists_ok=True)
logg.debug('config loaded:\n{}'.format(config))
return config

View File

@@ -0,0 +1,33 @@
# standard imports
import logging
# external imports
from cic_eth_registry import CICRegistry
from cic_eth_registry.lookup.declarator import AddressDeclaratorLookup
from cic_eth_registry.lookup.tokenindex import TokenIndexLookup
from chainlib.eth.constant import ZERO_ADDRESS
logg = logging.getLogger()
def connect_token_registry(self, conn, chain_spec, sender_address=ZERO_ADDRESS):
registry = CICRegistry(chain_spec, conn)
token_registry_address = registry.by_name('TokenRegistry', sender_address=sender_address)
logg.debug('using token registry address {}'.format(token_registry_address))
lookup = TokenIndexLookup(chain_spec, token_registry_address)
CICRegistry.add_lookup(lookup)
def connect_declarator(self, conn, chain_spec, trusted_addresses, sender_address=ZERO_ADDRESS):
registry = CICRegistry(chain_spec, conn)
declarator_address = registry.by_name('AddressDeclarator', sender_address=sender_address)
logg.debug('using declarator address {}'.format(declarator_address))
lookup = AddressDeclaratorLookup(chain_spec, declarator_address, trusted_addresses)
CICRegistry.add_lookup(lookup)
def connect_registry(conn, chain_spec, registry_address, sender_address=ZERO_ADDRESS):
CICRegistry.address = registry_address
registry = CICRegistry(chain_spec, conn)
registry_address = registry.by_name('ContractRegistry', sender_address=sender_address)
return registry

View File

@@ -0,0 +1,43 @@
# standard imports
import logging
# external imports
from chainlib.connection import (
RPCConnection,
ConnType,
)
from chainlib.eth.connection import EthUnixSignerConnection
from chainlib.chain import ChainSpec
logg = logging.getLogger(__name__)
class RPC:
def __init__(self, chain_spec, rpc_provider, signer_provider=None):
self.chain_spec = chain_spec
self.rpc_provider = rpc_provider
self.signer_provider = signer_provider
def get_default(self):
return RPCConnection.connect(self.chain_spec, 'default')
@staticmethod
def from_config(config):
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, 'default')
if config.get('SIGNER_PROVIDER'):
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, tag='signer')
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, 'signer')
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
logg.info('set up rpc: {}'.format(rpc))
return rpc
def __str__(self):
return 'RPC factory, chain {}, rpc {}, signer {}'.format(self.chain_spec, self.rpc_provider, self.signer_provider)

View File

@@ -0,0 +1,5 @@
[celery]
broker_url = redis://localhost:6379
result_url =
queue = cic-cache
debug = 0

View File

@@ -0,0 +1,4 @@
[cic]
registry_address =
trust_address =
health_modules = cic_eth.check.db,cic_eth.check.redis,cic_eth.check.signer,cic_eth.check.gas

View File

@@ -0,0 +1,10 @@
[database]
engine =
driver =
host =
port =
name = cic-cache
user =
password =
debug = 0
pool_size = 0

View File

@@ -0,0 +1,2 @@
[signer]
provider =

View File

@@ -0,0 +1,4 @@
[syncer]
loop_interval = 1
offset = 0
no_history = 0

View File

@@ -13,6 +13,9 @@ def list_transactions_mined(
session, session,
offset, offset,
limit, limit,
block_offset,
block_limit,
oldest=False,
): ):
"""Executes db query to return all confirmed transactions according to the specified offset and limit. """Executes db query to return all confirmed transactions according to the specified offset and limit.
@@ -23,7 +26,154 @@ def list_transactions_mined(
:result: Result set :result: Result set
:rtype: SQLAlchemy.ResultProxy :rtype: SQLAlchemy.ResultProxy
""" """
s = "SELECT block_number, tx_index FROM tx ORDER BY block_number DESC, tx_index DESC LIMIT {} OFFSET {}".format(limit, offset) order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT block_number, tx_index FROM tx WHERE block_number >= {} and block_number <= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, order_by, order_by, limit, offset)
else:
s = "SELECT block_number, tx_index FROM tx WHERE block_number >= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, order_by, order_by, limit, offset)
else:
s = "SELECT block_number, tx_index FROM tx ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(order_by, order_by, limit, offset)
r = session.execute(s)
return r
def list_transactions_mined_with_data(
session,
offset,
limit,
block_offset,
block_limit,
oldest=False,
):
"""Executes db query to return all confirmed transactions according to the specified offset and limit.
:param block_offset: First block to include in search
:type block_offset: int
:param block_limit: Last block to include in search
:type block_limit: int
:result: Result set
:rtype: SQLAlchemy.ResultProxy
"""
order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(order_by, order_by, limit, offset)
r = session.execute(s)
return r
def list_transactions_mined_with_data_index(
session,
offset,
end,
block_offset,
block_limit,
oldest=False,
):
"""Executes db query to return all confirmed transactions according to the specified offset and limit.
:param offset: Offset in data set to return transactions from
:type offset: int
:param limit: Max number of transactions to retrieve
:type limit: int
:result: Result set
:rtype: SQLAlchemy.ResultProxy
"""
order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} and block_number <= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, order_by, order_by, offset, end)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, order_by, order_by, offset, end)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(order_by, order_by, offset, end)
r = session.execute(s)
return r
def list_transactions_account_mined_with_data_index(
session,
address,
offset,
limit,
block_offset,
block_limit,
oldest=False,
):
"""Executes db query to return all confirmed transactions according to the specified offset and limit, filtered by address
:param offset: Offset in data set to return transactions from
:type offset: int
:param limit: Max number of transactions to retrieve
:type limit: int
:result: Result set
:rtype: SQLAlchemy.ResultProxy
"""
order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE sender = '{}' OR recipient = '{}' ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(address, address, order_by, order_by, limit, offset)
r = session.execute(s)
return r
def list_transactions_account_mined_with_data(
session,
address,
offset,
limit,
block_offset,
block_limit,
oldest=False,
):
"""Executes db query to return all confirmed transactions according to the specified offset and limit.
:param block_offset: First block to include in search
:type block_offset: int
:param block_limit: Last block to include in search
:type block_limit: int
:result: Result set
:rtype: SQLAlchemy.ResultProxy
"""
order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND block_number <= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE block_number >= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT tx_hash, block_number, date_block, sender, recipient, from_value, to_value, source_token, destination_token, success, domain, value FROM tx LEFT JOIN tag_tx_link ON tx.id = tag_tx_link.tx_id LEFT JOIN tag ON tag_tx_link.tag_id = tag.id WHERE sender = '{}' OR recipient = '{}' ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(address, address, order_by, order_by, limit, offset)
r = session.execute(s) r = session.execute(s)
return r return r
@@ -33,6 +183,9 @@ def list_transactions_account_mined(
address, address,
offset, offset,
limit, limit,
block_offset,
block_limit,
oldest=False,
): ):
"""Same as list_transactions_mined(...), but only retrieves transaction where the specified account address is sender or recipient. """Same as list_transactions_mined(...), but only retrieves transaction where the specified account address is sender or recipient.
@@ -45,7 +198,20 @@ def list_transactions_account_mined(
:result: Result set :result: Result set
:rtype: SQLAlchemy.ResultProxy :rtype: SQLAlchemy.ResultProxy
""" """
s = "SELECT block_number, tx_index FROM tx WHERE sender = '{}' OR recipient = '{}' ORDER BY block_number DESC, tx_index DESC LIMIT {} OFFSET {}".format(address, address, limit, offset)
order_by = 'DESC'
if oldest:
order_by = 'ASC'
if block_offset:
if block_limit:
s = "SELECT block_number, tx_index FROM tx WHERE block_number >= {} AND block_number <= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, block_limit, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT block_number, tx_index FROM tx WHERE block_number >= {} AND (sender = '{}' OR recipient = '{}') ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(block_offset, address, address, order_by, order_by, limit, offset)
else:
s = "SELECT block_number, tx_index FROM tx WHERE sender = '{}' OR recipient = '{}' ORDER BY block_number {}, tx_index {} LIMIT {} OFFSET {}".format(address, address, order_by, order_by, limit, offset)
r = session.execute(s) r = session.execute(s)
return r return r

View File

@@ -7,7 +7,7 @@ Create Date: 2021-04-01 08:10:29.156243
""" """
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from chainsyncer.db.migrations.sqlalchemy import ( from chainsyncer.db.migrations.default.export import (
chainsyncer_upgrade, chainsyncer_upgrade,
chainsyncer_downgrade, chainsyncer_downgrade,
) )

View File

@@ -100,3 +100,4 @@ class SessionBase(Model):
logg.debug('destroying session {}'.format(session_key)) logg.debug('destroying session {}'.format(session_key))
session.commit() session.commit()
session.close() session.close()
del SessionBase.localsessions[session_key]

View File

@@ -0,0 +1,115 @@
# standard imports
import logging
import json
import re
import base64
# external imports
from hexathon import add_0x
# local imports
from cic_cache.cache import (
BloomCache,
DataCache,
)
logg = logging.getLogger(__name__)
#logg = logging.getLogger()
re_transactions_all_bloom = r'/tx/(\d+)?/?(\d+)/?'
re_transactions_account_bloom = r'/tx/user/((0x)?[a-fA-F0-9]+)(/(\d+)(/(\d+))?)?/?'
re_transactions_all_data = r'/txa/(\d+)?/?(\d+)/?'
DEFAULT_LIMIT = 100
def process_transactions_account_bloom(session, env):
r = re.match(re_transactions_account_bloom, env.get('PATH_INFO'))
if not r:
return None
address = r[1]
if r[2] == None:
address = add_0x(address)
offset = 0
if r.lastindex > 2:
offset = r[4]
limit = DEFAULT_LIMIT
if r.lastindex > 4:
limit = r[6]
c = BloomCache(session)
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit)
o = {
'alg': 'sha256',
'low': lowest_block,
'high': highest_block,
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
'filter_rounds': 3,
}
j = json.dumps(o)
return ('application/json', j.encode('utf-8'),)
def process_transactions_all_bloom(session, env):
r = re.match(re_transactions_all_bloom, env.get('PATH_INFO'))
if not r:
return None
offset = DEFAULT_LIMIT
if r.lastindex > 0:
offset = r[1]
limit = 0
if r.lastindex > 1:
limit = r[2]
c = BloomCache(session)
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit)
o = {
'alg': 'sha256',
'low': lowest_block,
'high': highest_block,
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
'filter_rounds': 3,
}
j = json.dumps(o)
return ('application/json', j.encode('utf-8'),)
def process_transactions_all_data(session, env):
r = re.match(re_transactions_all_data, env.get('PATH_INFO'))
if not r:
return None
if env.get('HTTP_X_CIC_CACHE_MODE') != 'all':
return None
logg.debug('got data request {}'.format(env))
block_offset = r[1]
block_end = r[2]
if int(r[2]) < int(r[1]):
raise ValueError('cart before the horse, dude')
c = DataCache(session)
(lowest_block, highest_block, tx_cache) = c.load_transactions_with_data(0, 0, block_offset, block_end, oldest=True) # oldest needs to be settable
for r in tx_cache:
r['date_block'] = r['date_block'].timestamp()
o = {
'low': lowest_block,
'high': highest_block,
'data': tx_cache,
}
j = json.dumps(o)
return ('application/json', j.encode('utf-8'),)

View File

@@ -1,18 +1,20 @@
# standard imports # standard imports
import os import os
import re
import logging import logging
import argparse import argparse
import json
import base64 import base64
# third-party imports # external imports
import confini import confini
# local imports # local imports
from cic_cache import BloomCache
from cic_cache.db import dsn_from_config from cic_cache.db import dsn_from_config
from cic_cache.db.models.base import SessionBase from cic_cache.db.models.base import SessionBase
from cic_cache.runnable.daemons.query import (
process_transactions_account_bloom,
process_transactions_all_bloom,
process_transactions_all_data,
)
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger() logg = logging.getLogger()
@@ -44,72 +46,6 @@ logg.debug('config:\n{}'.format(config))
dsn = dsn_from_config(config) dsn = dsn_from_config(config)
SessionBase.connect(dsn, config.true('DATABASE_DEBUG')) SessionBase.connect(dsn, config.true('DATABASE_DEBUG'))
re_transactions_all_bloom = r'/tx/(\d+)?/?(\d+)/?'
re_transactions_account_bloom = r'/tx/user/((0x)?[a-fA-F0-9]+)/?(\d+)?/?(\d+)/?'
DEFAULT_LIMIT = 100
def process_transactions_account_bloom(session, env):
r = re.match(re_transactions_account_bloom, env.get('PATH_INFO'))
if not r:
return None
address = r[1]
if r[2] == None:
address = '0x' + address
offset = DEFAULT_LIMIT
if r.lastindex > 2:
offset = r[3]
limit = 0
if r.lastindex > 3:
limit = r[4]
c = BloomCache(session)
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit)
o = {
'alg': 'sha256',
'low': lowest_block,
'high': highest_block,
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
'filter_rounds': 3,
}
j = json.dumps(o)
return ('application/json', j.encode('utf-8'),)
def process_transactions_all_bloom(session, env):
r = re.match(re_transactions_all_bloom, env.get('PATH_INFO'))
if not r:
return None
offset = DEFAULT_LIMIT
if r.lastindex > 0:
offset = r[1]
limit = 0
if r.lastindex > 1:
limit = r[2]
c = BloomCache(session)
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit)
o = {
'alg': 'sha256',
'low': lowest_block,
'high': highest_block,
'block_filter': base64.b64encode(bloom_filter_block).decode('utf-8'),
'blocktx_filter': base64.b64encode(bloom_filter_tx).decode('utf-8'),
'filter_rounds': 3,
}
j = json.dumps(o)
return ('application/json', j.encode('utf-8'),)
# uwsgi application # uwsgi application
def application(env, start_response): def application(env, start_response):
@@ -119,10 +55,16 @@ def application(env, start_response):
session = SessionBase.create_session() session = SessionBase.create_session()
for handler in [ for handler in [
process_transactions_all_data,
process_transactions_all_bloom, process_transactions_all_bloom,
process_transactions_account_bloom, process_transactions_account_bloom,
]: ]:
r = handler(session, env) r = None
try:
r = handler(session, env)
except ValueError as e:
start_response('400 {}'.format(str(e)))
return []
if r != None: if r != None:
(mime_type, content) = r (mime_type, content) = r
break break

View File

@@ -8,14 +8,7 @@ import sys
import re import re
# external imports # external imports
import confini
import celery
import sqlalchemy import sqlalchemy
import rlp
import cic_base.config
import cic_base.log
import cic_base.argparse
import cic_base.rpc
from cic_eth_registry import CICRegistry from cic_eth_registry import CICRegistry
from cic_eth_registry.error import UnknownContractError from cic_eth_registry.error import UnknownContractError
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
@@ -28,13 +21,12 @@ from hexathon import (
strip_0x, strip_0x,
) )
from chainsyncer.backend.sql import SQLBackend from chainsyncer.backend.sql import SQLBackend
from chainsyncer.driver import ( from chainsyncer.driver.head import HeadSyncer
HeadSyncer, from chainsyncer.driver.history import HistorySyncer
HistorySyncer,
)
from chainsyncer.db.models.base import SessionBase from chainsyncer.db.models.base import SessionBase
# local imports # local imports
import cic_cache.cli
from cic_cache.db import ( from cic_cache.db import (
dsn_from_config, dsn_from_config,
add_tag, add_tag,
@@ -44,32 +36,36 @@ from cic_cache.runnable.daemons.filters import (
FaucetFilter, FaucetFilter,
) )
script_dir = os.path.realpath(os.path.dirname(__file__)) logging.basicConfig(level=logging.WARNING)
logg = logging.getLogger()
def add_block_args(argparser): # process args
argparser.add_argument('--history-start', type=int, default=0, dest='history_start', help='Start block height for initial history sync') arg_flags = cic_cache.cli.argflag_std_read
argparser.add_argument('--no-history', action='store_true', dest='no_history', help='Skip initial history sync') local_arg_flags = cic_cache.cli.argflag_local_sync
return argparser argparser = cic_cache.cli.ArgumentParser(arg_flags)
argparser.process_local_flags(local_arg_flags)
args = argparser.parse_args()
# process config
config = cic_cache.cli.Config.from_args(args, arg_flags, local_arg_flags)
logg = cic_base.log.create() # connect to database
argparser = cic_base.argparse.create(script_dir, cic_base.argparse.full_template)
argparser = cic_base.argparse.add(argparser, add_block_args, 'block')
args = cic_base.argparse.parse(argparser, logg)
config = cic_base.config.create(args.c, args, args.env_prefix)
config.add(args.history_start, 'SYNCER_HISTORY_START', True)
config.add(args.no_history, '_NO_HISTORY', True)
cic_base.config.log(config)
dsn = dsn_from_config(config) dsn = dsn_from_config(config)
SessionBase.connect(dsn, debug=config.true('DATABASE_DEBUG')) SessionBase.connect(dsn, debug=config.true('DATABASE_DEBUG'))
chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC')) # set up rpc
rpc = cic_cache.cli.RPC.from_config(config)
conn = rpc.get_default()
cic_base.rpc.setup(chain_spec, config.get('ETH_PROVIDER')) # set up chain provisions
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
registry = None
try:
registry = cic_cache.cli.connect_registry(conn, chain_spec, config.get('CIC_REGISTRY_ADDRESS'))
except UnknownContractError as e:
logg.exception('Registry contract connection failed for {}: {}'.format(config.get('CIC_REGISTRY_ADDRESS'), e))
sys.exit(1)
logg.info('connected contract registry {}'.format(config.get('CIC_REGISTRY_ADDRESS')))
def register_filter_tags(filters, session): def register_filter_tags(filters, session):
@@ -96,14 +92,12 @@ def main():
syncers = [] syncers = []
#if SQLBackend.first(chain_spec):
# backend = SQLBackend.initial(chain_spec, block_offset)
syncer_backends = SQLBackend.resume(chain_spec, block_offset) syncer_backends = SQLBackend.resume(chain_spec, block_offset)
if len(syncer_backends) == 0: if len(syncer_backends) == 0:
initial_block_start = config.get('SYNCER_HISTORY_START') initial_block_start = config.get('SYNCER_OFFSET')
initial_block_offset = block_offset initial_block_offset = block_offset
if config.get('_NO_HISTORY'): if config.get('SYNCER_NO_HISTORY'):
initial_block_start = block_offset initial_block_start = block_offset
initial_block_offset += 1 initial_block_offset += 1
syncer_backends.append(SQLBackend.initial(chain_spec, initial_block_offset, start_block_height=initial_block_start)) syncer_backends.append(SQLBackend.initial(chain_spec, initial_block_offset, start_block_height=initial_block_start))
@@ -113,10 +107,10 @@ def main():
logg.info('resuming sync session {}'.format(syncer_backend)) logg.info('resuming sync session {}'.format(syncer_backend))
for syncer_backend in syncer_backends: for syncer_backend in syncer_backends:
syncers.append(HistorySyncer(syncer_backend)) syncers.append(HistorySyncer(syncer_backend, cic_cache.cli.chain_interface))
syncer_backend = SQLBackend.live(chain_spec, block_offset+1) syncer_backend = SQLBackend.live(chain_spec, block_offset+1)
syncers.append(HeadSyncer(syncer_backend)) syncers.append(HeadSyncer(syncer_backend, cic_cache.cli.chain_interface))
trusted_addresses_src = config.get('CIC_TRUST_ADDRESS') trusted_addresses_src = config.get('CIC_TRUST_ADDRESS')
if trusted_addresses_src == None: if trusted_addresses_src == None:

View File

@@ -2,14 +2,17 @@
import celery import celery
# local imports # local imports
from cic_cache.cache import BloomCache from cic_cache.cache import (
BloomCache,
DataCache,
)
from cic_cache.db.models.base import SessionBase from cic_cache.db.models.base import SessionBase
celery_app = celery.current_app celery_app = celery.current_app
@celery_app.task(bind=True) @celery_app.task(bind=True)
def tx_filter(self, offset, limit, address=None, encoding='hex'): def tx_filter(self, offset, limit, address=None, oldest=False, encoding='hex'):
queue = self.request.delivery_info.get('routing_key') queue = self.request.delivery_info.get('routing_key')
session = SessionBase.create_session() session = SessionBase.create_session()
@@ -17,9 +20,9 @@ def tx_filter(self, offset, limit, address=None, encoding='hex'):
c = BloomCache(session) c = BloomCache(session)
b = None b = None
if address == None: if address == None:
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit) (lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions(offset, limit, oldest=oldest)
else: else:
(lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit) (lowest_block, highest_block, bloom_filter_block, bloom_filter_tx) = c.load_transactions_account(address, offset, limit, oldest=oldest)
session.close() session.close()
@@ -35,4 +38,17 @@ def tx_filter(self, offset, limit, address=None, encoding='hex'):
return o return o
@celery_app.task(bind=True)
def tx_filter_content(self, offset, limit, address=None, block_offset=None, block_limit=None, oldest=False, encoding='hex'):
session = SessionBase.create_session()
c = DataCache(session)
b = None
if address == None:
(lowest_block, highest_block, tx_cache) = c.load_transactions_with_data(offset, limit, block_offset=block_offset, block_limit=block_limit, oldest=oldest)
else:
(lowest_block, highest_block, tx_cache) = c.load_transactions_account_with_data_index(address, offset, limit, block_offset=block_offset, block_limit=block_limit)
session.close()
return (lowest_block, highest_block, tx_cache,)

View File

@@ -4,7 +4,7 @@ import semver
version = ( version = (
0, 0,
2, 2,
0, 1,
'alpha.2', 'alpha.2',
) )

View File

@@ -1,2 +0,0 @@
[bancor]
dir =

View File

@@ -1,4 +1,3 @@
[cic] [cic]
registry_address = registry_address =
chain_spec =
trust_address = trust_address =

View File

@@ -1,3 +0,0 @@
[bancor]
registry_address =
dir = /usr/local/share/bancor

View File

@@ -1,4 +1,3 @@
[cic] [cic]
chain_spec =
registry_address = registry_address =
trust_address = 0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C trust_address = 0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C

View File

@@ -1,3 +1,4 @@
[syncer] [syncer]
loop_interval = 1 loop_interval = 1
history_start = 0 offset = 0
no_history = 0

View File

@@ -1,2 +0,0 @@
[eth]
provider = ws://localhost:8545

View File

@@ -1,3 +0,0 @@
[syncer]
loop_interval = 5
history_start = 0

View File

@@ -1,2 +1,2 @@
[SYNCER] [syncer]
loop_interval = 1 loop_interval = 1

View File

@@ -1,52 +1,39 @@
FROM python:3.8.6-slim-buster # syntax = docker/dockerfile:1.2
FROM registry.gitlab.com/grassrootseconomics/cic-base-images:python-3.8.6-dev-55da5f4e as dev
# RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2b9
#COPY --from=0 /usr/local/share/cic/solidity/ /usr/local/share/cic/solidity/ COPY requirements.txt .
#RUN pip install $pip_extra_index_url_flag -r test_requirements.txt
#RUN pip install $pip_extra_index_url_flag .
#RUN pip install .[server]
WORKDIR /usr/src/cic-cache ARG EXTRA_INDEX_URL="https://pip.grassrootseconomics.net:8433"
ARG GITLAB_PYTHON_REGISTRY="https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple"
ARG EXTRA_PIP_ARGS=""
RUN --mount=type=cache,mode=0755,target=/root/.cache/pip \
pip install --index-url https://pypi.org/simple \
--extra-index-url $GITLAB_PYTHON_REGISTRY --extra-index-url $EXTRA_INDEX_URL $EXTRA_PIP_ARGS \
-r requirements.txt
ARG pip_extra_index_url_flag='--index https://pypi.org/simple --extra-index-url https://pip.grassrootseconomics.net:8433' COPY . .
ARG root_requirement_file='requirements.txt'
#RUN apk update && \ RUN python setup.py install
# apk add gcc musl-dev gnupg libpq
#RUN apk add postgresql-dev
#RUN apk add linux-headers
#RUN apk add libffi-dev
RUN apt-get update && \
apt install -y gcc gnupg libpq-dev wget make g++ gnupg bash procps git
# Copy shared requirements from top of mono-repo
RUN echo "copying root req file ${root_requirement_file}"
RUN pip install $pip_extra_index_url_flag cic-base[full_graph]==0.1.2b9
COPY cic-cache/requirements.txt ./
COPY cic-cache/setup.cfg \
cic-cache/setup.py \
./
COPY cic-cache/cic_cache/ ./cic_cache/
COPY cic-cache/scripts/ ./scripts/
COPY cic-cache/test_requirements.txt ./
RUN pip install $pip_extra_index_url_flag -r test_requirements.txt
RUN pip install $pip_extra_index_url_flag .
RUN pip install .[server]
COPY cic-cache/tests/ ./tests/
#COPY db/ cic-cache/db
#RUN apk add postgresql-client
# ini files in config directory defines the configurable parameters for the application # ini files in config directory defines the configurable parameters for the application
# they can all be overridden by environment variables # they can all be overridden by environment variables
# to generate a list of environment variables from configuration, use: confini-dump -z <dir> (executable provided by confini package) # to generate a list of environment variables from configuration, use: confini-dump -z <dir> (executable provided by confini package)
COPY cic-cache/config/ /usr/local/etc/cic-cache/ COPY config/ /usr/local/etc/cic-cache/
# for db migrations # for db migrations
RUN git clone https://github.com/vishnubob/wait-for-it.git /usr/local/bin/wait-for-it/ RUN git clone https://github.com/vishnubob/wait-for-it.git /usr/local/bin/wait-for-it/
COPY cic-cache/cic_cache/db/migrations/ /usr/local/share/cic-cache/alembic/ COPY cic_cache/db/migrations/ /usr/local/share/cic-cache/alembic/
COPY cic-cache/docker/start_tracker.sh ./start_tracker.sh COPY /docker/start_tracker.sh ./start_tracker.sh
COPY cic-cache/docker/db.sh ./db.sh COPY /docker/db.sh ./db.sh
RUN chmod 755 ./*.sh RUN chmod 755 ./*.sh
# Tracker # Tracker
# ENTRYPOINT ["/usr/local/bin/cic-cache-tracker", "-vv"] # ENTRYPOINT ["/usr/local/bin/cic-cache-tracker", "-vv"]
# Server # Server
# ENTRYPOINT [ "/usr/local/bin/uwsgi", "--wsgi-file", "/usr/local/lib/python3.8/site-packages/cic_cache/runnable/server.py", "--http", ":80", "--pyargv", "-vv" ] # ENTRYPOINT [ "/usr/local/bin/uwsgi", "--wsgi-file", "/usr/local/lib/python3.8/site-packages/cic_cache/runnable/server.py", "--http", ":80", "--pyargv", "-vv" ]
ENTRYPOINT []

View File

@@ -0,0 +1,10 @@
#! /bin/bash
set -e
pip install --extra-index-url https://pip.grassrootseconomics.net:8433 \
--extra-index-url https://gitlab.com/api/v4/projects/27624814/packages/pypi/simple \
-r test_requirements.txt
export PYTHONPATH=. && pytest -x --cov=cic_cache --cov-fail-under=90 --cov-report term-missing tests

View File

@@ -1,12 +1,14 @@
cic-base~=0.1.2b10
alembic==1.4.2 alembic==1.4.2
confini~=0.3.6rc3 confini>=0.3.6rc4,<0.5.0
uwsgi==2.0.19.1 uwsgi==2.0.19.1
moolb~=0.1.0 moolb~=0.1.1b2
cic-eth-registry~=0.5.5a4 cic-eth-registry~=0.6.1a1
SQLAlchemy==1.3.20 SQLAlchemy==1.3.20
semver==2.13.0 semver==2.13.0
psycopg2==2.8.6 psycopg2==2.8.6
celery==4.4.7 celery==4.4.7
redis==3.5.3 redis==3.5.3
chainsyncer[sql]~=0.0.2a4 chainsyncer[sql]>=0.0.6a3,<0.1.0
erc20-faucet>=0.3.2a2, <0.4.0
chainlib-eth>=0.0.9a14,<0.1.0
eth-address-index>=0.2.3a4,<0.3.0

View File

@@ -2,6 +2,7 @@
import os import os
import argparse import argparse
import logging import logging
import re
import alembic import alembic
from alembic.config import Config as AlembicConfig from alembic.config import Config as AlembicConfig
@@ -23,6 +24,8 @@ argparser = argparse.ArgumentParser()
argparser.add_argument('-c', type=str, default=config_dir, help='config file') argparser.add_argument('-c', type=str, default=config_dir, help='config file')
argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration') argparser.add_argument('--env-prefix', default=os.environ.get('CONFINI_ENV_PREFIX'), dest='env_prefix', type=str, help='environment prefix for variables to overwrite configuration')
argparser.add_argument('--migrations-dir', dest='migrations_dir', default=migrationsdir, type=str, help='path to alembic migrations directory') argparser.add_argument('--migrations-dir', dest='migrations_dir', default=migrationsdir, type=str, help='path to alembic migrations directory')
argparser.add_argument('--reset', action='store_true', help='downgrade before upgrading')
argparser.add_argument('-f', action='store_true', help='force action')
argparser.add_argument('-v', action='store_true', help='be verbose') argparser.add_argument('-v', action='store_true', help='be verbose')
argparser.add_argument('-vv', action='store_true', help='be more verbose') argparser.add_argument('-vv', action='store_true', help='be more verbose')
args = argparser.parse_args() args = argparser.parse_args()
@@ -53,4 +56,10 @@ ac = AlembicConfig(os.path.join(migrations_dir, 'alembic.ini'))
ac.set_main_option('sqlalchemy.url', dsn) ac.set_main_option('sqlalchemy.url', dsn)
ac.set_main_option('script_location', migrations_dir) ac.set_main_option('script_location', migrations_dir)
if args.reset:
if not args.f:
if not re.match(r'[yY][eE]?[sS]?', input('EEK! this will DELETE the existing db. are you sure??')):
logg.error('user chickened out on requested reset, bailing')
sys.exit(1)
alembic.command.downgrade(ac, 'base')
alembic.command.upgrade(ac, 'head') alembic.command.upgrade(ac, 'head')

View File

@@ -23,11 +23,13 @@ licence_files =
[options] [options]
python_requires = >= 3.6 python_requires = >= 3.6
include_package_data = True
packages = packages =
cic_cache cic_cache
cic_cache.tasks cic_cache.tasks
cic_cache.db cic_cache.db
cic_cache.db.models cic_cache.db.models
cic_cache.cli
cic_cache.runnable cic_cache.runnable
cic_cache.runnable.daemons cic_cache.runnable.daemons
cic_cache.runnable.daemons.filters cic_cache.runnable.daemons.filters
@@ -39,3 +41,4 @@ console_scripts =
cic-cache-trackerd = cic_cache.runnable.daemons.tracker:main cic-cache-trackerd = cic_cache.runnable.daemons.tracker:main
cic-cache-serverd = cic_cache.runnable.daemons.server:main cic-cache-serverd = cic_cache.runnable.daemons.server:main
cic-cache-taskerd = cic_cache.runnable.daemons.tasker:main cic-cache-taskerd = cic_cache.runnable.daemons.tasker:main
cic-cache-list = cic_cache.runable.list:main

View File

@@ -6,6 +6,5 @@ sqlparse==0.4.1
pytest-celery==0.0.0a1 pytest-celery==0.0.0a1
eth_tester==0.5.0b3 eth_tester==0.5.0b3
py-evm==0.3.0a20 py-evm==0.3.0a20
web3==5.12.2 sarafu-faucet~=0.0.7a1
cic-eth-registry~=0.5.5a3 erc20-transfer-authorization>=0.3.5a1,<0.4.0
cic-base[full]==0.1.2b8

View File

@@ -0,0 +1,40 @@
# standard imports
import os
# external imports
import chainlib.cli
# local imports
import cic_cache.cli
script_dir = os.path.dirname(os.path.realpath(__file__))
config_dir = os.path.join(script_dir, '..', 'testdata', 'config')
def test_argumentparserto_config():
argparser = cic_cache.cli.ArgumentParser()
local_flags = 0xffff
argparser.process_local_flags(local_flags)
argparser.add_argument('--foo', type=str)
args = argparser.parse_args([
'-q', 'baz',
'--offset', '13',
'--no-history',
'-r','0xdeadbeef',
'-vv',
'--foo', 'bar',
])
extra_args = {
'foo': '_BARBARBAR',
}
config = cic_cache.cli.Config.from_args(args, chainlib.cli.argflag_std_base, local_flags, extra_args=extra_args, base_config_dir=config_dir)
assert config.get('_BARBARBAR') == 'bar'
assert config.get('CELERY_QUEUE') == 'baz'
assert config.get('SYNCER_NO_HISTORY') == True
assert config.get('SYNCER_OFFSET') == 13
assert config.get('CIC_REGISTRY_ADDRESS') == '0xdeadbeef'

View File

@@ -0,0 +1,17 @@
# standard imports
import tempfile
# local imports
import cic_cache.cli
def test_cli_celery():
cf = tempfile.mkdtemp()
config = {
'CELERY_RESULT_URL': 'filesystem://' + cf,
}
cic_cache.cli.CeleryApp.from_config(config)
config['CELERY_BROKER_URL'] = 'filesystem://' + cf
cic_cache.cli.CeleryApp.from_config(config)

View File

@@ -0,0 +1,68 @@
# external imports
import pytest
from chainlib.eth.gas import (
Gas,
RPCGasOracle,
)
from chainlib.eth.nonce import RPCNonceOracle
from chainlib.eth.block import (
block_latest,
Block,
)
from chainlib.eth.pytest.fixtures_chain import default_chain_spec
from chainlib.eth.pytest.fixtures_ethtester import *
from cic_eth_registry.pytest.fixtures_contracts import *
from hexathon import add_0x
# local imports
import cic_cache.cli
@pytest.mark.xfail()
def test_cli_rpc(
eth_rpc,
eth_signer,
default_chain_spec,
):
config = {
'CHAIN_SPEC': str(default_chain_spec),
'RPC_HTTP_PROVIDER': 'http://localhost:8545',
}
rpc = cic_cache.cli.RPC.from_config(config, default_label='foo')
conn = rpc.get_by_label('foo')
#o = block_latest()
#conn.do(o)
def test_cli_chain(
default_chain_spec,
eth_rpc,
eth_signer,
contract_roles,
):
ifc = cic_cache.cli.EthChainInterface()
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], conn=eth_rpc)
gas_oracle = RPCGasOracle(conn=eth_rpc)
c = Gas(default_chain_spec, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle, signer=eth_signer)
recipient = add_0x(os.urandom(20).hex())
(tx_hash, o) = c.create(contract_roles['CONTRACT_DEPLOYER'], recipient, 1024)
r = eth_rpc.do(o)
o = ifc.tx_receipt(r)
r = eth_rpc.do(o)
assert r['status'] == 1
o = ifc.block_by_number(1)
block_src = eth_rpc.do(o)
block = ifc.block_from_src(block_src)
assert block.number == 1
with pytest.raises(KeyError):
assert block_src['gasUsed'] == 21000
assert block_src['gas_used'] == 21000
block_src = ifc.src_normalize(block_src)
assert block_src['gasUsed'] == 21000
assert block_src['gas_used'] == 21000

View File

@@ -5,9 +5,12 @@ import datetime
# external imports # external imports
import pytest import pytest
import moolb
# local imports # local imports
from cic_cache import db from cic_cache import db
from cic_cache import BloomCache
from cic_cache.cache import DEFAULT_FILTER_SIZE
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
root_dir = os.path.dirname(script_dir) root_dir = os.path.dirname(script_dir)
@@ -61,7 +64,6 @@ def txs(
dt.timestamp(), dt.timestamp(),
) )
tx_number = 42 tx_number = 42
tx_hash_second = '0x' + os.urandom(32).hex() tx_hash_second = '0x' + os.urandom(32).hex()
tx_signed_second = '0x' + os.urandom(128).hex() tx_signed_second = '0x' + os.urandom(128).hex()
@@ -88,3 +90,58 @@ def txs(
tx_hash_first, tx_hash_first,
tx_hash_second, tx_hash_second,
] ]
@pytest.fixture(scope='function')
def more_txs(
init_database,
list_defaults,
list_actors,
list_tokens,
txs,
):
session = init_database
tx_number = 666
tx_hash = '0x' + os.urandom(32).hex()
tx_signed = '0x' + os.urandom(128).hex()
nonce = 3
dt = datetime.datetime.utcnow()
dt += datetime.timedelta(hours=1)
db.add_transaction(
session,
tx_hash,
list_defaults['block']+2,
tx_number,
list_actors['alice'],
list_actors['diane'],
list_tokens['bar'],
list_tokens['bar'],
2048,
4096,
False,
dt.timestamp(),
)
session.commit()
return [tx_hash] + txs
@pytest.fixture(scope='function')
def tag_txs(
init_database,
txs,
):
db.add_tag(init_database, 'taag', domain='test')
init_database.commit()
db.tag_transaction(init_database, txs[1], 'taag', domain='test')
@pytest.fixture(scope='session')
def zero_filter():
return moolb.Bloom(DEFAULT_FILTER_SIZE, 3)

View File

@@ -10,6 +10,7 @@ from sqlalchemy import text
from chainlib.eth.tx import Tx from chainlib.eth.tx import Tx
from chainlib.eth.block import Block from chainlib.eth.block import Block
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.error import RequestMismatchException
from hexathon import ( from hexathon import (
strip_0x, strip_0x,
add_0x, add_0x,
@@ -18,10 +19,21 @@ from hexathon import (
# local imports # local imports
from cic_cache.db import add_tag from cic_cache.db import add_tag
from cic_cache.runnable.daemons.filters.erc20 import ERC20TransferFilter from cic_cache.runnable.daemons.filters.erc20 import ERC20TransferFilter
from cic_cache.runnable.daemons.filters.base import TagSyncFilter
logg = logging.getLogger() logg = logging.getLogger()
def test_base_filter_str(
init_database,
):
f = TagSyncFilter('foo')
assert 'foo' == str(f)
f = TagSyncFilter('foo', domain='bar')
assert 'bar.foo' == str(f)
def test_erc20_filter( def test_erc20_filter(
eth_rpc, eth_rpc,
foo_token, foo_token,
@@ -67,3 +79,95 @@ def test_erc20_filter(
s = text("SELECT x.tx_hash FROM tag a INNER JOIN tag_tx_link l ON l.tag_id = a.id INNER JOIN tx x ON x.id = l.tx_id WHERE a.domain = :a AND a.value = :b") s = text("SELECT x.tx_hash FROM tag a INNER JOIN tag_tx_link l ON l.tag_id = a.id INNER JOIN tx x ON x.id = l.tx_id WHERE a.domain = :a AND a.value = :b")
r = init_database.execute(s, {'a': fltr.tag_domain, 'b': fltr.tag_name}).fetchone() r = init_database.execute(s, {'a': fltr.tag_domain, 'b': fltr.tag_name}).fetchone()
assert r[0] == tx.hash assert r[0] == tx.hash
def test_erc20_filter_nocontract(
eth_rpc,
foo_token,
init_database,
list_defaults,
list_actors,
tags,
):
chain_spec = ChainSpec('foo', 'bar', 42, 'baz')
fltr = ERC20TransferFilter(chain_spec)
add_tag(init_database, fltr.tag_name, domain=fltr.tag_domain)
# incomplete args
data = 'a9059cbb'
data += strip_0x(list_actors['alice'])
data += '1000'.ljust(64, '0')
block = Block({
'hash': os.urandom(32).hex(),
'number': 42,
'timestamp': datetime.datetime.utcnow().timestamp(),
'transactions': [],
})
tx = Tx({
'to': os.urandom(20).hex(),
'from': list_actors['bob'],
'data': data,
'value': 0,
'hash': os.urandom(32).hex(),
'nonce': 13,
'gasPrice': 10000000,
'gas': 123456,
})
block.txs.append(tx)
tx.block = block
assert not fltr.filter(eth_rpc, block, tx, db_session=init_database)
@pytest.mark.parametrize(
'contract_method,contract_input,expected_exception',
[
('a9059cbb', os.urandom(32).hex(), ValueError), # not enough args
('a9059cbb', os.urandom(31).hex(), ValueError), # wrong arg boundary
('a9059cbc', os.urandom(64).hex(), RequestMismatchException), # wrong method
],
)
def test_erc20_filter_bogus(
eth_rpc,
foo_token,
init_database,
list_defaults,
list_actors,
tags,
contract_method,
contract_input,
expected_exception,
):
chain_spec = ChainSpec('foo', 'bar', 42, 'baz')
fltr = ERC20TransferFilter(chain_spec)
add_tag(init_database, fltr.tag_name, domain=fltr.tag_domain)
# incomplete args
data = contract_method
data += contract_input
block = Block({
'hash': os.urandom(32).hex(),
'number': 42,
'timestamp': datetime.datetime.utcnow().timestamp(),
'transactions': [],
})
tx = Tx({
'to': foo_token,
'from': list_actors['bob'],
'data': data,
'value': 0,
'hash': os.urandom(32).hex(),
'nonce': 13,
'gasPrice': 10000000,
'gas': 123456,
})
block.txs.append(tx)
tx.block = block
assert not fltr.filter(eth_rpc, block, tx, db_session=init_database)

View File

@@ -0,0 +1,31 @@
# standard imports
import json
# external imports
import pytest
# local imports
from cic_cache.runnable.daemons.query import process_transactions_all_data
def test_api_all_data(
init_database,
txs,
):
env = {
'PATH_INFO': '/txa/410000/420000',
'HTTP_X_CIC_CACHE_MODE': 'all',
}
j = process_transactions_all_data(init_database, env)
o = json.loads(j[1])
assert len(o['data']) == 2
env = {
'PATH_INFO': '/txa/420000/410000',
'HTTP_X_CIC_CACHE_MODE': 'all',
}
with pytest.raises(ValueError):
j = process_transactions_all_data(init_database, env)

View File

@@ -8,7 +8,9 @@ import json
import pytest import pytest
# local imports # local imports
from cic_cache import db
from cic_cache import BloomCache from cic_cache import BloomCache
from cic_cache.cache import DataCache
logg = logging.getLogger() logg = logging.getLogger()
@@ -17,7 +19,6 @@ def test_cache(
init_database, init_database,
list_defaults, list_defaults,
list_actors, list_actors,
list_tokens,
txs, txs,
): ):
@@ -33,3 +34,219 @@ def test_cache(
assert b[0] == list_defaults['block'] - 1 assert b[0] == list_defaults['block'] - 1
def test_cache_data(
init_database,
txs,
tag_txs,
):
session = init_database
c = DataCache(session)
b = c.load_transactions_with_data(0, 3) #410000, 420000) #, 100, block_offset=410000, block_limit=420000, oldest=True)
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == txs[0]
assert b[2][0]['tx_type'] == 'unknown'
assert b[2][1]['tx_type'] == 'test.taag'
def test_cache_ranges(
init_database,
list_defaults,
list_actors,
list_tokens,
more_txs,
):
session = init_database
oldest = list_defaults['block'] - 1
mid = list_defaults['block']
newest = list_defaults['block'] + 2
c = BloomCache(session)
b = c.load_transactions(0, 100)
assert b[0] == oldest
assert b[1] == newest
b = c.load_transactions(1, 2)
assert b[0] == oldest
assert b[1] == mid
b = c.load_transactions(0, 2)
assert b[0] == mid
assert b[1] == newest
b = c.load_transactions(0, 1)
assert b[0] == newest
assert b[1] == newest
b = c.load_transactions(0, 100, oldest=True)
assert b[0] == oldest
assert b[1] == newest
b = c.load_transactions(0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
b = c.load_transactions(0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == oldest
assert b[1] == mid
b = c.load_transactions(0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'], oldest=True)
assert b[0] == oldest
assert b[1] == mid
# now check when supplying account
b = c.load_transactions_account(list_actors['alice'], 0, 100)
assert b[0] == oldest
assert b[1] == newest
b = c.load_transactions_account(list_actors['bob'], 0, 100)
assert b[0] == mid
assert b[1] == mid
b = c.load_transactions_account(list_actors['diane'], 0, 100)
assert b[0] == oldest
assert b[1] == newest
# add block filter to the mix
b = c.load_transactions_account(list_actors['alice'], 0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
b = c.load_transactions_account(list_actors['alice'], 0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
b = c.load_transactions_account(list_actors['bob'], 0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == mid
assert b[1] == mid
b = c.load_transactions_account(list_actors['diane'], 0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == oldest
assert b[1] == oldest
def test_cache_ranges_data(
init_database,
list_defaults,
list_actors,
list_tokens,
more_txs,
):
session = init_database
oldest = list_defaults['block'] - 1
mid = list_defaults['block']
newest = list_defaults['block'] + 2
c = DataCache(session)
b = c.load_transactions_with_data(0, 100)
assert b[0] == oldest
assert b[1] == newest
assert len(b[2]) == 3
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][2]['tx_hash'] == more_txs[2]
b = c.load_transactions_with_data(1, 2)
assert b[0] == oldest
assert b[1] == mid
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[1]
assert b[2][1]['tx_hash'] == more_txs[2]
b = c.load_transactions_with_data(0, 2)
assert b[0] == mid
assert b[1] == newest
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[1]
b = c.load_transactions_with_data(0, 1)
assert b[0] == newest
assert b[1] == newest
assert len(b[2]) == 1
assert b[2][0]['tx_hash'] == more_txs[0]
b = c.load_transactions_with_data(0, 100, oldest=True)
assert b[0] == oldest
assert b[1] == newest
assert len(b[2]) == 3
assert b[2][0]['tx_hash'] == more_txs[2]
assert b[2][1]['tx_hash'] == more_txs[1]
assert b[2][2]['tx_hash'] == more_txs[0]
b = c.load_transactions_with_data(0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[1]
b = c.load_transactions_with_data(0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == oldest
assert b[1] == mid
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[1]
assert b[2][1]['tx_hash'] == more_txs[2]
b = c.load_transactions_with_data(0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'], oldest=True)
assert b[0] == oldest
assert b[1] == mid
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[2]
assert b[2][1]['tx_hash'] == more_txs[1]
# now check when supplying account
b = c.load_transactions_account_with_data(list_actors['alice'], 0, 100)
assert b[0] == oldest
assert b[1] == newest
assert len(b[2]) == 3
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[1]
assert b[2][2]['tx_hash'] == more_txs[2]
b = c.load_transactions_account_with_data(list_actors['bob'], 0, 100)
assert b[0] == mid
assert b[1] == mid
assert len(b[2]) == 1
assert b[2][0]['tx_hash'] == more_txs[1]
b = c.load_transactions_account_with_data(list_actors['diane'], 0, 100)
assert b[0] == oldest
assert b[1] == newest
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[2]
# add block filter to the mix
b = c.load_transactions_account_with_data(list_actors['alice'], 0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[1]
b = c.load_transactions_account_with_data(list_actors['alice'], 0, 100, block_offset=list_defaults['block'])
assert b[0] == mid
assert b[1] == newest
assert len(b[2]) == 2
assert b[2][0]['tx_hash'] == more_txs[0]
assert b[2][1]['tx_hash'] == more_txs[1]
b = c.load_transactions_account_with_data(list_actors['bob'], 0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == mid
assert b[1] == mid
assert len(b[2]) == 1
assert b[2][0]['tx_hash'] == more_txs[1]
b = c.load_transactions_account_with_data(list_actors['diane'], 0, 100, block_offset=list_defaults['block'] - 1, block_limit=list_defaults['block'])
assert b[0] == oldest
assert b[1] == oldest
assert len(b[2]) == 1
assert b[2][0]['tx_hash'] == more_txs[2]

View File

@@ -0,0 +1,230 @@
# standard imports
import logging
import json
import base64
import copy
import re
# external imports
import pytest
from hexathon import strip_0x
# local imports
from cic_cache.runnable.daemons.query import *
logg = logging.getLogger()
@pytest.mark.parametrize(
'query_path_prefix, query_role, query_address_index, query_offset, query_offset_index, query_limit, query_limit_index, match_re',
[
('/tx/user/', 'alice', 0, None, 3, None, 5, re_transactions_account_bloom),
('/tx/user/', 'alice', 0, 42, 3, None, 5, re_transactions_account_bloom),
('/tx/user/', 'alice', 0, 42, 3, 13, 5, re_transactions_account_bloom),
('/tx/', None, 0, None, 3, None, 5, re_transactions_all_bloom),
('/tx/', None, 0, 42, 3, None, 5, re_transactions_all_bloom),
('/tx/', None, 0, 42, 3, 13, 5, re_transactions_all_bloom),
('/txa/', None, 0, None, 3, None, 5, re_transactions_all_data),
('/txa/', None, 0, 42, 3, None, 5, re_transactions_all_data),
('/txa/', None, 0, 42, 3, 13, 5, re_transactions_all_data),
],
)
def test_query_regex(
list_actors,
query_path_prefix,
query_role,
query_address_index,
query_offset,
query_offset_index,
query_limit,
query_limit_index,
match_re,
):
paths = []
path = query_path_prefix
query_address = None
if query_role != None:
query_address = strip_0x(list_actors[query_role])
paths.append(path + '0x' + query_address)
paths.append(path + query_address)
if query_offset != None:
if query_limit != None:
for i in range(len(paths)-1):
paths[i] += '/{}/{}'.format(query_offset, query_limit)
else:
for i in range(len(paths)-1):
paths[i] += '/' + str(query_offset)
for i in range(len(paths)):
paths.append(paths[i] + '/')
for p in paths:
logg.debug('testing path {} against {}'.format(p, match_re))
m = re.match(match_re, p)
l = len(m.groups())
logg.debug('laast index match {} groups {}'.format(m.lastindex, l))
for i in range(l+1):
logg.debug('group {} {}'.format(i, m[i]))
if m.lastindex >= query_offset_index:
assert query_offset == int(m[query_offset_index + 1])
if m.lastindex >= query_limit_index:
assert query_limit == int(m[query_limit_index + 1])
if query_address_index != None:
match_address = strip_0x(m[query_address_index + 1])
assert query_address == match_address
@pytest.mark.parametrize(
'role_name, query_offset, query_limit, query_match',
[
('alice', None, None, [(420000, 13), (419999, 42)]),
('alice', None, 1, [(420000, 13)]),
('alice', 1, None, [(419999, 42)]), # 420000 == list_defaults['block']
('alice', 2, None, []), # 420000 == list_defaults['block']
],
)
def test_query_process_txs_account(
init_database,
list_defaults,
list_actors,
list_tokens,
txs,
zero_filter,
role_name,
query_offset,
query_limit,
query_match,
):
actor = None
try:
actor = list_actors[role_name]
except KeyError:
actor = os.urandom(20).hex()
path_info = '/tx/user/0x' + strip_0x(actor)
if query_offset != None:
path_info += '/' + str(query_offset)
if query_limit != None:
if query_offset == None:
path_info += '/0'
path_info += '/' + str(query_limit)
env = {
'PATH_INFO': path_info,
}
logg.debug('using path {}'.format(path_info))
r = process_transactions_account_bloom(init_database, env)
assert r != None
o = json.loads(r[1])
block_filter_data = base64.b64decode(o['block_filter'].encode('utf-8'))
zero_filter_data = zero_filter.to_bytes()
if len(query_match) == 0:
assert block_filter_data == zero_filter_data
return
assert block_filter_data != zero_filter_data
block_filter = copy.copy(zero_filter)
block_filter.merge(block_filter_data)
block_filter_data = block_filter.to_bytes()
assert block_filter_data != zero_filter_data
for (block, tx) in query_match:
block = block.to_bytes(4, byteorder='big')
assert block_filter.check(block)
@pytest.mark.parametrize(
'query_offset, query_limit, query_match',
[
(None, 2, [(420000, 13), (419999, 42)]),
(0, 1, [(420000, 13)]),
(1, 1, [(419999, 42)]),
(2, 0, []),
],
)
def test_query_process_txs_bloom(
init_database,
list_defaults,
list_actors,
list_tokens,
txs,
zero_filter,
query_offset,
query_limit,
query_match,
):
path_info = '/tx'
if query_offset != None:
path_info += '/' + str(query_offset)
if query_limit != None:
if query_offset == None:
path_info += '/0'
path_info += '/' + str(query_limit)
env = {
'PATH_INFO': path_info,
}
logg.debug('using path {}'.format(path_info))
r = process_transactions_all_bloom(init_database, env)
assert r != None
o = json.loads(r[1])
block_filter_data = base64.b64decode(o['block_filter'].encode('utf-8'))
zero_filter_data = zero_filter.to_bytes()
if len(query_match) == 0:
assert block_filter_data == zero_filter_data
return
assert block_filter_data != zero_filter_data
block_filter = copy.copy(zero_filter)
block_filter.merge(block_filter_data)
block_filter_data = block_filter.to_bytes()
assert block_filter_data != zero_filter_data
for (block, tx) in query_match:
block = block.to_bytes(4, byteorder='big')
assert block_filter.check(block)
@pytest.mark.parametrize(
'query_block_start, query_block_end, query_match_count',
[
(None, 42, 0),
(420000, 420001, 1),
(419999, 419999, 1), # matches are inclusive
(419999, 420000, 2),
(419999, 420001, 2),
],
)
def test_query_process_txs_data(
init_database,
list_defaults,
list_actors,
list_tokens,
txs,
zero_filter,
query_block_start,
query_block_end,
query_match_count,
):
path_info = '/txa'
if query_block_start != None:
path_info += '/' + str(query_block_start)
if query_block_end != None:
if query_block_start == None:
path_info += '/0'
path_info += '/' + str(query_block_end)
env = {
'PATH_INFO': path_info,
'HTTP_X_CIC_CACHE_MODE': 'all',
}
logg.debug('using path {}'.format(path_info))
r = process_transactions_all_data(init_database, env)
assert r != None
o = json.loads(r[1])
assert len(o['data']) == query_match_count

View File

@@ -0,0 +1,2 @@
[foo]
bar_baz = xyzzy

View File

@@ -0,0 +1 @@
include *requirements.txt

View File

@@ -0,0 +1,53 @@
# standard imports
import logging
# external imports
import celery
from erc20_demurrage_token.demurrage import DemurrageCalculator
from chainlib.connection import RPCConnection
from chainlib.chain import ChainSpec
from chainlib.eth.constant import ZERO_ADDRESS
from cic_eth_registry import CICRegistry
logg = logging.getLogger(__name__)
celery_app = celery.current_app
class NoopCalculator:
def amount_since(self, amount, timestamp):
logg.debug('noopcalculator amount {} timestamp {}'.format(amount, timestamp))
return amount
class DemurrageCalculationTask(celery.Task):
demurrage_token_calcs = {}
@classmethod
def register_token(cls, rpc, chain_spec, token_symbol, sender_address=ZERO_ADDRESS):
registry = CICRegistry(chain_spec, rpc)
token_address = registry.by_name(token_symbol, sender_address=sender_address)
try:
c = DemurrageCalculator.from_contract(rpc, chain_spec, token_address, sender_address=sender_address)
logg.info('found demurrage calculator for ERC20 {} @ {}'.format(token_symbol, token_address))
except:
logg.warning('Token {} at address {} does not appear to be a demurrage contract. Calls to balance adjust for this token will always return the same amount'.format(token_symbol, token_address))
c = NoopCalculator()
cls.demurrage_token_calcs[token_symbol] = c
@celery_app.task(bind=True, base=DemurrageCalculationTask)
def get_adjusted_balance(self, token_symbol, amount, timestamp):
c = self.demurrage_token_calcs[token_symbol]
return c.amount_since(amount, timestamp)
def aux_setup(rpc, config, sender_address=ZERO_ADDRESS):
chain_spec_str = config.get('CHAIN_SPEC')
chain_spec = ChainSpec.from_chain_str(chain_spec_str)
token_symbol = config.get('CIC_DEFAULT_TOKEN_SYMBOL')
DemurrageCalculationTask.register_token(rpc, chain_spec, token_symbol, sender_address=sender_address)

View File

@@ -0,0 +1,30 @@
# standard imports
import logging
# external imports
import celery
from cic_eth.api.base import ApiBase
app = celery.current_app
logg = logging.getLogger(__name__)
class Api(ApiBase):
def get_adjusted_balance(self, token_symbol, balance, timestamp):
s = celery.signature(
'cic_eth_aux.erc20_demurrage_token.get_adjusted_balance',
[
token_symbol,
balance,
timestamp,
],
queue=None,
)
if self.callback_param != None:
s.link(self.callback_success)
s.link.on_error(self.callback_error)
t = s.apply_async(queue=self.queue)
return t

View File

@@ -0,0 +1,4 @@
celery==4.4.7
erc20-demurrage-token~=0.0.3a1
cic-eth-registry>=0.6.1a2,<0.7.0
cic-eth[services]~=0.12.4a8

View File

@@ -0,0 +1,30 @@
[metadata]
name = cic-eth-aux-erc20-demurrage-token
version = 0.0.2a6
description = cic-eth tasks supporting erc20 demurrage token
author = Louis Holbrook
author_email = dev@holbrook.no
url = https://gitlab.com/ccicnet/erc20-demurrage-token
keywords =
ethereum
blockchain
cryptocurrency
erc20
classifiers =
Programming Language :: Python :: 3
Operating System :: OS Independent
Development Status :: 3 - Alpha
Environment :: No Input/Output (Daemon)
Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Topic :: Internet
#Topic :: Blockchain :: EVM
license = GPL3
licence_files =
LICENSE
[options]
include_package_data = True
python_requires = >= 3.6
packages =
cic_eth_aux.erc20_demurrage_token

View File

@@ -0,0 +1,25 @@
from setuptools import setup
requirements = []
f = open('requirements.txt', 'r')
while True:
l = f.readline()
if l == '':
break
requirements.append(l.rstrip())
f.close()
test_requirements = []
f = open('test_requirements.txt', 'r')
while True:
l = f.readline()
if l == '':
break
test_requirements.append(l.rstrip())
f.close()
setup(
install_requires=requirements,
tests_require=test_requirements,
)

View File

@@ -0,0 +1,11 @@
pytest==6.0.1
pytest-celery==0.0.0a1
pytest-mock==3.3.1
pytest-cov==2.10.1
eth-tester==0.5.0b3
py-evm==0.3.0a20
SQLAlchemy==1.3.20
liveness~=0.0.1a7
eth-accounts-index==0.1.1a1
eth-contract-registry==0.5.8a1
eth-address-index==0.2.1a1

View File

@@ -0,0 +1,88 @@
# external imports
import celery
from chainlib.eth.pytest.fixtures_chain import *
from chainlib.eth.pytest.fixtures_ethtester import *
from cic_eth_registry.pytest.fixtures_contracts import *
from cic_eth_registry.pytest.fixtures_tokens import *
from erc20_demurrage_token.unittest.base import TestTokenDeploy
from erc20_demurrage_token.token import DemurrageToken
from eth_token_index.index import TokenUniqueSymbolIndex
from eth_address_declarator.declarator import AddressDeclarator
# cic-eth imports
from cic_eth.pytest.fixtures_celery import *
from cic_eth.pytest.fixtures_token import *
from cic_eth.pytest.fixtures_config import *
@pytest.fixture(scope='function')
def demurrage_token(
default_chain_spec,
eth_rpc,
token_registry,
contract_roles,
eth_signer,
):
d = TestTokenDeploy(eth_rpc, token_symbol='BAR', token_name='Bar Token')
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], conn=eth_rpc)
c = DemurrageToken(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
token_address = d.deploy(eth_rpc, contract_roles['CONTRACT_DEPLOYER'], c, 'SingleNocap')
logg.debug('demurrage token contract "BAR" deployed to {}'.format(token_address))
return token_address
@pytest.fixture(scope='function')
def demurrage_token_symbol(
default_chain_spec,
eth_rpc,
demurrage_token,
contract_roles,
):
c = DemurrageToken(default_chain_spec)
o = c.symbol(demurrage_token, sender_address=contract_roles['CONTRACT_DEPLOYER'])
r = eth_rpc.do(o)
return c.parse_symbol(r)
@pytest.fixture(scope='function')
def demurrage_token_declaration(
foo_token_declaration,
):
return foo_token_declaration
@pytest.fixture(scope='function')
def register_demurrage_token(
default_chain_spec,
token_registry,
eth_rpc,
eth_signer,
register_lookups,
contract_roles,
demurrage_token_declaration,
demurrage_token,
address_declarator,
):
nonce_oracle = RPCNonceOracle(contract_roles['CONTRACT_DEPLOYER'], eth_rpc)
c = TokenUniqueSymbolIndex(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.register(token_registry, contract_roles['CONTRACT_DEPLOYER'], demurrage_token)
eth_rpc.do(o)
o = receipt(tx_hash_hex)
r = eth_rpc.do(o)
assert r['status'] == 1
nonce_oracle = RPCNonceOracle(contract_roles['TRUSTED_DECLARATOR'], eth_rpc)
c = AddressDeclarator(default_chain_spec, signer=eth_signer, nonce_oracle=nonce_oracle)
(tx_hash_hex, o) = c.add_declaration(address_declarator, contract_roles['TRUSTED_DECLARATOR'], demurrage_token, demurrage_token_declaration)
eth_rpc.do(o)
o = receipt(tx_hash_hex)
r = eth_rpc.do(o)
assert r['status'] == 1
return token_registry

View File

@@ -0,0 +1,69 @@
# standard imports
import logging
import copy
import datetime
# external imports
import celery
# cic-eth imports
from cic_eth_aux.erc20_demurrage_token import (
DemurrageCalculationTask,
aux_setup,
)
from cic_eth_aux.erc20_demurrage_token.api import Api as AuxApi
logg = logging.getLogger()
def test_demurrage_calulate_task(
default_chain_spec,
eth_rpc,
cic_registry,
celery_session_worker,
register_demurrage_token,
demurrage_token_symbol,
contract_roles,
load_config,
):
config = copy.copy(load_config)
config.add(str(default_chain_spec), 'CIC_CHAIN_SPEC', exists_ok=True)
config.add(demurrage_token_symbol, 'CIC_DEFAULT_TOKEN_SYMBOL', exists_ok=True)
aux_setup(eth_rpc, load_config, sender_address=contract_roles['CONTRACT_DEPLOYER'])
since = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
s = celery.signature(
'cic_eth_aux.erc20_demurrage_token.get_adjusted_balance',
[
demurrage_token_symbol,
1000,
since.timestamp(),
],
queue=None,
)
t = s.apply_async()
r = t.get_leaf()
assert t.successful()
assert r == 980
def test_demurrage_calculate_api(
default_chain_spec,
eth_rpc,
cic_registry,
celery_session_worker,
register_demurrage_token,
demurrage_token_symbol,
contract_roles,
load_config,
):
api = AuxApi(str(default_chain_spec), queue=None)
since = datetime.datetime.utcnow() - datetime.timedelta(minutes=1)
t = api.get_adjusted_balance(demurrage_token_symbol, 1000, since.timestamp())
r = t.get_leaf()
assert t.successful()
assert r == 980

View File

@@ -5,3 +5,6 @@ omit =
cic_eth/db/migrations/* cic_eth/db/migrations/*
cic_eth/sync/head.py cic_eth/sync/head.py
cic_eth/sync/mempool.py cic_eth/sync/mempool.py
cic_eth/queue/state.py
cic_eth/cli
*redis*.py

View File

@@ -0,0 +1,6 @@
.git
.cache
.dot
**/doc
**/.venv
**/venv

View File

@@ -1,22 +1,16 @@
.cic_eth_variables: build-test-cic-eth:
variables: stage: test
APP_NAME: cic-eth tags:
DOCKERFILE_PATH: $APP_NAME/docker/Dockerfile - integration
variables:
.cic_eth_changes_target: APP_NAME: cic-eth
rules: MR_IMAGE_TAG: mr-$APP_NAME-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA
- changes: script:
- $CONTEXT/$APP_NAME/* - cd apps/cic-eth
- docker build -t $MR_IMAGE_TAG -f docker/Dockerfile .
build-mr-cic-eth: - docker run $MR_IMAGE_TAG sh docker/run_tests.sh
extends: rules:
- .cic_eth_changes_target - if: $CI_PIPELINE_SOURCE == "merge_request_event"
- .py_build_merge_request changes:
- .cic_eth_variables - apps/$APP_NAME/**/*
when: always
build-push-cic-eth:
extends:
- .py_build_push
- .cic_eth_variables

2
apps/cic-eth/MANIFEST.in Normal file
View File

@@ -0,0 +1,2 @@
include *requirements.txt config/test/* cic_eth/data/config/*

View File

@@ -0,0 +1,5 @@
SQLAlchemy==1.3.20
cic-eth-registry>=0.6.1a5,<0.7.0
hexathon~=0.0.1a8
chainqueue>=0.0.4a6,<0.1.0
eth-erc20>=0.1.2a2,<0.2.0

View File

@@ -4,8 +4,12 @@ import logging
# external imports # external imports
import celery import celery
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from hexathon import (
add_0x,
strip_0x,
uniform as hex_uniform,
)
# local imports # local imports
from cic_eth.db.enum import LockEnum from cic_eth.db.enum import LockEnum
@@ -15,12 +19,17 @@ from cic_eth.task import (
CriticalSQLAlchemyTask, CriticalSQLAlchemyTask,
) )
from cic_eth.error import LockedError from cic_eth.error import LockedError
from cic_eth.encode import (
tx_normalize,
ZERO_ADDRESS_NORMAL,
)
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL, tx_hash=None): def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=LockEnum.ALL, tx_hash=None):
"""Task wrapper to set arbitrary locks """Task wrapper to set arbitrary locks
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -32,6 +41,7 @@ def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.AL
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
@@ -41,7 +51,7 @@ def lock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.AL
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.ALL): def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, flags=LockEnum.ALL):
"""Task wrapper to reset arbitrary locks """Task wrapper to reset arbitrary locks
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -53,6 +63,7 @@ def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
@@ -62,7 +73,7 @@ def unlock(chained_input, chain_spec_dict, address=ZERO_ADDRESS, flags=LockEnum.
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None): def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_hash=None):
"""Task wrapper to set send lock """Task wrapper to set send lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -72,6 +83,7 @@ def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.set(chain_str, LockEnum.SEND, address=address, tx_hash=tx_hash) r = Lock.set(chain_str, LockEnum.SEND, address=address, tx_hash=tx_hash)
logg.debug('Send locked for {}, flag now {}'.format(address, r)) logg.debug('Send locked for {}, flag now {}'.format(address, r))
@@ -79,7 +91,7 @@ def lock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS): def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
"""Task wrapper to reset send lock """Task wrapper to reset send lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -89,6 +101,7 @@ def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.reset(chain_str, LockEnum.SEND, address=address) r = Lock.reset(chain_str, LockEnum.SEND, address=address)
logg.debug('Send unlocked for {}, flag now {}'.format(address, r)) logg.debug('Send unlocked for {}, flag now {}'.format(address, r))
@@ -96,7 +109,7 @@ def unlock_send(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=None): def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL, tx_hash=None):
"""Task wrapper to set queue direct lock """Task wrapper to set queue direct lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -106,6 +119,7 @@ def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=Non
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.set(chain_str, LockEnum.QUEUE, address=address, tx_hash=tx_hash) r = Lock.set(chain_str, LockEnum.QUEUE, address=address, tx_hash=tx_hash)
logg.debug('Queue direct locked for {}, flag now {}'.format(address, r)) logg.debug('Queue direct locked for {}, flag now {}'.format(address, r))
@@ -113,7 +127,7 @@ def lock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS, tx_hash=Non
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS): def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS_NORMAL):
"""Task wrapper to reset queue direct lock """Task wrapper to reset queue direct lock
:param chain_str: Chain spec string representation :param chain_str: Chain spec string representation
@@ -123,6 +137,7 @@ def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
:returns: New lock state for address :returns: New lock state for address
:rtype: number :rtype: number
""" """
address = tx_normalize.wallet_address(address)
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
r = Lock.reset(chain_str, LockEnum.QUEUE, address=address) r = Lock.reset(chain_str, LockEnum.QUEUE, address=address)
logg.debug('Queue direct unlocked for {}, flag now {}'.format(address, r)) logg.debug('Queue direct unlocked for {}, flag now {}'.format(address, r))
@@ -131,11 +146,13 @@ def unlock_queue(chained_input, chain_spec_dict, address=ZERO_ADDRESS):
@celery_app.task(base=CriticalSQLAlchemyTask) @celery_app.task(base=CriticalSQLAlchemyTask)
def check_lock(chained_input, chain_spec_dict, lock_flags, address=None): def check_lock(chained_input, chain_spec_dict, lock_flags, address=None):
if address != None:
address = tx_normalize.wallet_address(address)
chain_str = '::' chain_str = '::'
if chain_spec_dict != None: if chain_spec_dict != None:
chain_str = str(ChainSpec.from_dict(chain_spec_dict)) chain_str = str(ChainSpec.from_dict(chain_spec_dict))
session = SessionBase.create_session() session = SessionBase.create_session()
r = Lock.check(chain_str, lock_flags, address=ZERO_ADDRESS, session=session) r = Lock.check(chain_str, lock_flags, address=ZERO_ADDRESS_NORMAL, session=session)
if address != None: if address != None:
r |= Lock.check(chain_str, lock_flags, address=address, session=session) r |= Lock.check(chain_str, lock_flags, address=address, session=session)
if r > 0: if r > 0:

View File

@@ -4,11 +4,22 @@ import logging
# external imports # external imports
import celery import celery
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from chainlib.eth.tx import unpack from chainlib.connection import RPCConnection
from chainqueue.query import get_tx from chainlib.eth.tx import (
from chainqueue.state import set_cancel unpack,
TxFactory,
)
from chainlib.eth.gas import OverrideGasOracle
from chainqueue.sql.query import get_tx
from chainqueue.sql.state import set_cancel
from chainqueue.db.models.otx import Otx from chainqueue.db.models.otx import Otx
from chainqueue.db.models.tx import TxCache from chainqueue.db.models.tx import TxCache
from hexathon import (
strip_0x,
add_0x,
uniform as hex_uniform,
)
from potaahto.symbols import snake_and_camel
# local imports # local imports
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
@@ -21,13 +32,15 @@ from cic_eth.admin.ctrl import (
) )
from cic_eth.queue.tx import queue_create from cic_eth.queue.tx import queue_create
from cic_eth.eth.gas import create_check_gas_task from cic_eth.eth.gas import create_check_gas_task
from cic_eth.task import BaseTask
from cic_eth.encode import tx_normalize
celery_app = celery.current_app celery_app = celery.current_app
logg = logging.getLogger() logg = logging.getLogger()
@celery_app.task(bind=True) @celery_app.task(bind=True, base=BaseTask)
def shift_nonce(self, chain_str, tx_hash_orig_hex, delta=1): def shift_nonce(self, chainspec_dict, tx_hash_orig_hex, delta=1):
"""Shift all transactions with nonces higher than the offset by the provided position delta. """Shift all transactions with nonces higher than the offset by the provided position delta.
Transactions who are replaced by transactions that move nonces will be marked as OVERRIDDEN. Transactions who are replaced by transactions that move nonces will be marked as OVERRIDDEN.
@@ -38,89 +51,109 @@ def shift_nonce(self, chain_str, tx_hash_orig_hex, delta=1):
:type tx_hash_orig_hex: str, 0x-hex :type tx_hash_orig_hex: str, 0x-hex
:param delta: Amount :param delta: Amount
""" """
chain_spec = ChainSpec.from_dict(chainspec_dict)
rpc = RPCConnection.connect(chain_spec, 'default')
rpc_signer = RPCConnection.connect(chain_spec, 'signer')
queue = None queue = None
try: try:
queue = self.request.delivery_info.get('routing_key') queue = self.request.delivery_info.get('routing_key')
except AttributeError: except AttributeError:
pass pass
chain_spec = ChainSpec.from_chain_str(chain_str) session = BaseTask.session_func()
tx_brief = get_tx(tx_hash_orig_hex) tx_brief = get_tx(chain_spec, tx_hash_orig_hex, session=session)
tx_raw = bytes.fromhex(strip_0x(tx_brief['signed_tx'][2:])) tx_raw = bytes.fromhex(strip_0x(tx_brief['signed_tx']))
tx = unpack(tx_raw, chain_spec) tx = unpack(tx_raw, chain_spec)
nonce = tx_brief['nonce'] nonce = tx_brief['nonce']
address = tx['from'] address = tx['from']
logg.debug('shifting nonce {} position(s) for address {}, offset {}'.format(delta, address, nonce)) logg.debug('shifting nonce {} position(s) for address {}, offset {}, hash {}'.format(delta, address, nonce, tx['hash']))
lock_queue(None, chain_str, address) lock_queue(None, chain_spec.asdict(), address=address)
lock_send(None, chain_str, address) lock_send(None, chain_spec.asdict(), address=address)
session = SessionBase.create_session() set_cancel(chain_spec, strip_0x(tx['hash']), manual=True, session=session)
query_address = tx_normalize.wallet_address(address)
q = session.query(Otx) q = session.query(Otx)
q = q.join(TxCache) q = q.join(TxCache)
q = q.filter(TxCache.sender==address) q = q.filter(TxCache.sender==query_address)
q = q.filter(Otx.nonce>=nonce+delta) q = q.filter(Otx.nonce>=nonce+delta)
q = q.order_by(Otx.nonce.asc()) q = q.order_by(Otx.nonce.asc())
otxs = q.all() otxs = q.all()
tx_hashes = [] tx_hashes = []
txs = [] txs = []
gas_total = 0
for otx in otxs: for otx in otxs:
tx_raw = bytes.fromhex(strip_0x(otx.signed_tx)) tx_raw = bytes.fromhex(strip_0x(otx.signed_tx))
tx_new = unpack(tx_raw, chain_spec) tx_new = unpack(tx_raw, chain_spec)
tx_new = snake_and_camel(tx_new)
tx_previous_hash_hex = tx_new['hash'] tx_previous_hash_hex = tx_new['hash']
tx_previous_nonce = tx_new['nonce'] tx_previous_nonce = tx_new['nonce']
tx_new['gas_price'] += 1
tx_new['gasPrice'] = tx_new['gas_price']
tx_new['nonce'] -= delta
gas_total += tx_new['gas_price'] * tx_new['gas']
logg.debug('tx_new {}'.format(tx_new))
logg.debug('gas running total {}'.format(gas_total))
del(tx_new['hash']) del(tx_new['hash'])
del(tx_new['hash_unsigned']) del(tx_new['hash_unsigned'])
tx_new['nonce'] -= delta del(tx_new['hashUnsigned'])
(tx_hash_hex, tx_signed_raw_hex) = sign_tx(tx_new, chain_str) gas_oracle = OverrideGasOracle(limit=tx_new['gas'], price=tx_new['gas_price'] + 1) # TODO: it should be possible to merely set this price here and if missing in the existing struct then fill it in (chainlib.eth.tx)
c = TxFactory(chain_spec, signer=rpc_signer, gas_oracle=gas_oracle)
(tx_hash_hex, tx_signed_raw_hex) = c.build_raw(tx_new)
logg.debug('tx {} -> {} nonce {} -> {}'.format(tx_previous_hash_hex, tx_hash_hex, tx_previous_nonce, tx_new['nonce'])) logg.debug('tx {} -> {} nonce {} -> {}'.format(tx_previous_hash_hex, tx_hash_hex, tx_previous_nonce, tx_new['nonce']))
otx = Otx( otx = Otx(
nonce=tx_new['nonce'], tx_new['nonce'],
address=tx_new['from'], tx_hash_hex,
tx_hash=tx_hash_hex, tx_signed_raw_hex,
signed_tx=tx_signed_raw_hex, )
)
session.add(otx) session.add(otx)
session.commit()
# TODO: cancel all first, then replace. Otherwise we risk two non-locked states for two different nonces. # TODO: cancel all first, then replace. Otherwise we risk two non-locked states for two different nonces.
set_cancel(tx_previous_hash_hex, True) set_cancel(chain_spec, strip_0x(tx_previous_hash_hex), manual=True, session=session)
TxCache.clone(tx_previous_hash_hex, tx_hash_hex) TxCache.clone(tx_previous_hash_hex, tx_hash_hex, session=session)
tx_hashes.append(tx_hash_hex) tx_hashes.append(tx_hash_hex)
txs.append(tx_signed_raw_hex) txs.append(tx_signed_raw_hex)
session.commit()
session.close() session.close()
s = create_check_gas_and_send_task( s = create_check_gas_task(
txs, txs,
chain_str, chain_spec,
tx_new['from'], #tx_new['from'],
tx_new['gas'], address,
tx_hashes, #gas=tx_new['gas'],
queue, gas=gas_total,
tx_hashes_hex=tx_hashes,
queue=queue,
) )
s_unlock_send = celery.signature( s_unlock_send = celery.signature(
'cic_eth.admin.ctrl.unlock_send', 'cic_eth.admin.ctrl.unlock_send',
[ [
chain_str, chain_spec.asdict(),
tx_new['from'], address,
#tx_new['from'],
], ],
queue=queue, queue=queue,
) )
s_unlock_direct = celery.signature( s_unlock_direct = celery.signature(
'cic_eth.admin.ctrl.unlock_queue', 'cic_eth.admin.ctrl.unlock_queue',
[ [
chain_str, chain_spec.asdict(),
tx_new['from'], address,
#tx_new['from'],
], ],
queue=queue, queue=queue,
) )

View File

@@ -1,19 +1,2 @@
# standard imports
import logging
# external imports
import celery
# local imports # local imports
from cic_eth.task import BaseTask from cic_eth.eth.erc20 import default_token
celery_app = celery.current_app
logg = logging.getLogger()
@celery_app.task(bind=True, base=BaseTask)
def default_token(self):
return {
'symbol': self.default_token_symbol,
'address': self.default_token_address,
}

View File

@@ -5,4 +5,3 @@
""" """
from .api_task import Api from .api_task import Api
from .api_admin import AdminApi

View File

@@ -8,6 +8,7 @@ from chainlib.eth.constant import (
ZERO_ADDRESS, ZERO_ADDRESS,
) )
from cic_eth_registry import CICRegistry from cic_eth_registry import CICRegistry
from cic_eth_registry.erc20 import ERC20Token
from cic_eth_registry.error import UnknownContractError from cic_eth_registry.error import UnknownContractError
from chainlib.eth.address import to_checksum_address from chainlib.eth.address import to_checksum_address
from chainlib.eth.contract import code from chainlib.eth.contract import code
@@ -20,6 +21,7 @@ from chainlib.hash import keccak256_hex_to_hex
from hexathon import ( from hexathon import (
strip_0x, strip_0x,
add_0x, add_0x,
uniform as hex_uniform,
) )
from chainlib.eth.gas import balance from chainlib.eth.gas import balance
from chainqueue.db.enum import ( from chainqueue.db.enum import (
@@ -30,13 +32,14 @@ from chainqueue.db.enum import (
status_str, status_str,
) )
from chainqueue.error import TxStateChangeError from chainqueue.error import TxStateChangeError
from eth_erc20 import ERC20
# local imports # local imports
from cic_eth.db.models.base import SessionBase from cic_eth.db.models.base import SessionBase
from cic_eth.db.models.role import AccountRole from cic_eth.db.models.role import AccountRole
from cic_eth.db.models.nonce import Nonce from cic_eth.db.models.nonce import Nonce
from cic_eth.error import InitializationError from cic_eth.error import InitializationError
from cic_eth.queue.query import get_tx from cic_eth.queue.query import get_tx_local
app = celery.current_app app = celery.current_app
@@ -188,6 +191,7 @@ class AdminApi:
s_manual = celery.signature( s_manual = celery.signature(
'cic_eth.queue.state.set_manual', 'cic_eth.queue.state.set_manual',
[ [
chain_spec.asdict(),
tx_hash_hex, tx_hash_hex,
], ],
queue=self.queue, queue=self.queue,
@@ -206,8 +210,9 @@ class AdminApi:
s.link(s_gas) s.link(s_gas)
return s_manual.apply_async() return s_manual.apply_async()
def check_nonce(self, address):
def check_nonce(self, chain_spec, address):
s = celery.signature( s = celery.signature(
'cic_eth.queue.query.get_account_tx', 'cic_eth.queue.query.get_account_tx',
[ [
@@ -228,13 +233,12 @@ class AdminApi:
s_get_tx = celery.signature( s_get_tx = celery.signature(
'cic_eth.queue.query.get_tx', 'cic_eth.queue.query.get_tx',
[ [
chain_spec.asdict(), chain_spec.asdict(),
k, k,
], ],
queue=self.queue, queue=self.queue,
) )
tx = s_get_tx.apply_async().get() tx = s_get_tx.apply_async().get()
#tx = get_tx(k)
logg.debug('checking nonce {} (previous {})'.format(tx['nonce'], last_nonce)) logg.debug('checking nonce {} (previous {})'.format(tx['nonce'], last_nonce))
nonce_otx = tx['nonce'] nonce_otx = tx['nonce']
if not is_alive(tx['status']) and tx['status'] & local_fail > 0: if not is_alive(tx['status']) and tx['status'] & local_fail > 0:
@@ -242,7 +246,9 @@ class AdminApi:
blocking_tx = k blocking_tx = k
blocking_nonce = nonce_otx blocking_nonce = nonce_otx
elif nonce_otx - last_nonce > 1: elif nonce_otx - last_nonce > 1:
logg.error('nonce gap; {} followed {} for account {}'.format(nonce_otx, last_nonce, tx['from'])) logg.debug('tx {}'.format(tx))
tx_obj = unpack(bytes.fromhex(strip_0x(tx['signed_tx'])), chain_spec)
logg.error('nonce gap; {} followed {} for account {}'.format(nonce_otx, last_nonce, tx_obj['from']))
blocking_tx = k blocking_tx = k
blocking_nonce = nonce_otx blocking_nonce = nonce_otx
break break
@@ -256,12 +262,13 @@ class AdminApi:
'blocking': blocking_nonce, 'blocking': blocking_nonce,
}, },
'tx': { 'tx': {
'blocking': blocking_tx, 'blocking': add_0x(blocking_tx),
}
} }
}
def fix_nonce(self, address, nonce, chain_spec): # TODO: is risky since it does not validate that there is actually a nonce problem?
def fix_nonce(self, chain_spec, address, nonce):
s = celery.signature( s = celery.signature(
'cic_eth.queue.query.get_account_tx', 'cic_eth.queue.query.get_account_tx',
[ [
@@ -275,15 +282,17 @@ class AdminApi:
txs = s.apply_async().get() txs = s.apply_async().get()
tx_hash_hex = None tx_hash_hex = None
session = SessionBase.create_session()
for k in txs.keys(): for k in txs.keys():
tx_dict = get_tx(k) tx_dict = get_tx_local(chain_spec, k, session=session)
if tx_dict['nonce'] == nonce: if tx_dict['nonce'] == nonce:
tx_hash_hex = k tx_hash_hex = k
session.close()
s_nonce = celery.signature( s_nonce = celery.signature(
'cic_eth.admin.nonce.shift_nonce', 'cic_eth.admin.nonce.shift_nonce',
[ [
self.rpc.chain_spec.asdict(), chain_spec.asdict(),
tx_hash_hex, tx_hash_hex,
], ],
queue=self.queue queue=self.queue
@@ -299,6 +308,8 @@ class AdminApi:
:param address: Ethereum address to return transactions for :param address: Ethereum address to return transactions for
:type address: str, 0x-hex :type address: str, 0x-hex
""" """
address = add_0x(hex_uniform(strip_0x(address)))
last_nonce = -1 last_nonce = -1
s = celery.signature( s = celery.signature(
'cic_eth.queue.query.get_account_tx', 'cic_eth.queue.query.get_account_tx',
@@ -388,12 +399,13 @@ class AdminApi:
t = s.apply_async() t = s.apply_async()
tx = t.get() tx = t.get()
source_token = None source_token = None
if tx['source_token'] != ZERO_ADDRESS: if tx['source_token'] != ZERO_ADDRESS:
source_token_declaration = None
if registry != None: if registry != None:
try: try:
source_token = registry.by_address(tx['source_token']) source_token_declaration = registry.by_address(tx['source_token'], sender_address=self.call_address)
except UnknownContractError: except UnknownContractError:
logg.warning('unknown source token contract {} (direct)'.format(tx['source_token'])) logg.warning('unknown source token contract {} (direct)'.format(tx['source_token']))
else: else:
@@ -406,16 +418,21 @@ class AdminApi:
queue=self.queue queue=self.queue
) )
t = s.apply_async() t = s.apply_async()
source_token = t.get() source_token_declaration = t.get()
if source_token == None:
logg.warning('unknown source token contract {} (task pool)'.format(tx['source_token'])) if source_token_declaration != None:
logg.warning('found declarator record for source token {} but not checking validity'.format(tx['source_token']))
source_token = ERC20Token(chain_spec, self.rpc, tx['source_token'])
logg.debug('source token set tup {}'.format(source_token))
destination_token = None destination_token = None
if tx['destination_token'] != ZERO_ADDRESS: if tx['destination_token'] != ZERO_ADDRESS:
destination_token_declaration = None
if registry != None: if registry != None:
try: try:
destination_token = registry.by_address(tx['destination_token']) destination_token_declaration = registry.by_address(tx['destination_token'], sender_address=self.call_address)
except UnknownContractError: except UnknownContractError:
logg.warning('unknown destination token contract {}'.format(tx['destination_token'])) logg.warning('unknown destination token contract {}'.format(tx['destination_token']))
else: else:
@@ -428,10 +445,10 @@ class AdminApi:
queue=self.queue queue=self.queue
) )
t = s.apply_async() t = s.apply_async()
destination_token = t.get() destination_token_declaration = t.get()
if destination_token == None: if destination_token_declaration != None:
logg.warning('unknown destination token contract {} (task pool)'.format(tx['destination_token'])) logg.warning('found declarator record for destination token {} but not checking validity'.format(tx['destination_token']))
destination_token = ERC20Token(chain_spec, self.rpc, tx['destination_token'])
tx['sender_description'] = 'Custodial account' tx['sender_description'] = 'Custodial account'
tx['recipient_description'] = 'Custodial account' tx['recipient_description'] = 'Custodial account'
@@ -543,13 +560,19 @@ class AdminApi:
if role != None: if role != None:
tx['recipient_description'] = role tx['recipient_description'] = role
erc20_c = ERC20(chain_spec)
if source_token != None: if source_token != None:
tx['source_token_symbol'] = source_token.symbol() tx['source_token_symbol'] = source_token.symbol
tx['sender_token_balance'] = source_token.function('balanceOf')(tx['sender']).call() o = erc20_c.balance_of(tx['source_token'], tx['sender'], sender_address=self.call_address)
r = self.rpc.do(o)
tx['sender_token_balance'] = erc20_c.parse_balance(r)
if destination_token != None: if destination_token != None:
tx['destination_token_symbol'] = destination_token.symbol() tx['destination_token_symbol'] = destination_token.symbol
tx['recipient_token_balance'] = source_token.function('balanceOf')(tx['recipient']).call() o = erc20_c.balance_of(tx['destination_token'], tx['recipient'], sender_address=self.call_address)
r = self.rpc.do(o)
tx['recipient_token_balance'] = erc20_c.parse_balance(r)
#tx['recipient_token_balance'] = destination_token.function('balanceOf')(tx['recipient']).call()
# TODO: this can mean either not subitted or culled, need to check other txs with same nonce to determine which # TODO: this can mean either not subitted or culled, need to check other txs with same nonce to determine which
tx['network_status'] = 'Not in node' tx['network_status'] = 'Not in node'

View File

@@ -8,63 +8,59 @@ import logging
# external imports # external imports
import celery import celery
from cic_eth_registry import CICRegistry
from chainlib.chain import ChainSpec from chainlib.chain import ChainSpec
from hexathon import strip_0x
# local imports # local imports
from cic_eth.db.enum import LockEnum from cic_eth.api.base import ApiBase
from cic_eth.enum import LockEnum
app = celery.current_app app = celery.current_app
logg = logging.getLogger(__name__) #logg = logging.getLogger(__name__)
logg = logging.getLogger()
class Api: class Api(ApiBase):
"""Creates task chains to perform well-known CIC operations.
Each method that sends tasks returns details about the root task. The root task uuid can be provided in the callback, to enable to caller to correlate the result with individual calls. It can also be used to independently poll the completion of a task chain. @staticmethod
def to_v_list(v, n):
"""Translate an arbitrary number of string and/or list arguments to a list of list of string arguments
:param callback_param: Static value to pass to callback :param v: Arguments
:type callback_param: str :type v: str or list
:param callback_task: Callback task that executes callback_param call. (Must be included by the celery worker) :param n: Number of elements to generate arguments for
:type callback_task: string :type n: int
:param queue: Name of worker queue to submit tasks to :rtype: list
:type queue: str :returns: list of assembled arguments
""" """
def __init__(self, chain_str, queue='cic-eth', callback_param=None, callback_task='cic_eth.callbacks.noop.noop', callback_queue=None): if isinstance(v, str):
self.chain_str = chain_str vv = v
self.chain_spec = ChainSpec.from_chain_str(chain_str) v = []
self.callback_param = callback_param for i in range(n):
self.callback_task = callback_task v.append([vv])
self.queue = queue elif not isinstance(v, list):
logg.debug('api using queue {}'.format(self.queue)) raise ValueError('argument must be single string, or list or strings or lists')
self.callback_success = None else:
self.callback_error = None if len(v) != n:
if callback_queue == None: raise ValueError('v argument count must match integer n')
callback_queue=self.queue for i in range(n):
if isinstance(v[i], str):
if callback_param != None: v[i] = [v[i]]
self.callback_success = celery.signature( elif not isinstance(v, list):
callback_task, raise ValueError('proof argument must be single string, or list or strings or lists')
[
callback_param,
0,
],
queue=callback_queue,
)
self.callback_error = celery.signature(
callback_task,
[
callback_param,
1,
],
queue=callback_queue,
)
return v
def default_token(self): def default_token(self):
"""Retrieves the default fallback token of the custodial network.
:returns: uuid of root task
:rtype: celery.Task
"""
s_token = celery.signature( s_token = celery.signature(
'cic_eth.admin.token.default_token', 'cic_eth.eth.erc20.default_token',
[], [],
queue=self.queue, queue=self.queue,
) )
@@ -74,29 +70,247 @@ class Api:
return s_token.apply_async() return s_token.apply_async()
def convert_transfer(self, from_address, to_address, target_return, minimum_return, from_token_symbol, to_token_symbol): def token(self, token_symbol, proof=None):
"""Executes a chain of celery tasks that performs conversion between two ERC20 tokens, and transfers to a specified receipient after convert has completed. """Single-token alias for tokens method.
See tokens method for details.
:param token_symbol: Token symbol to look up
:type token_symbol: str
:param proof: Proofs to add to signature verification for the token
:type proof: str or list
:returns: uuid of root task
:rtype: celery.Task
"""
if not isinstance(token_symbol, str):
raise ValueError('token symbol must be string')
return self.tokens([token_symbol], proof=proof)
def tokens(self, token_symbols, proof=None):
"""Perform a token data lookup from the token index. The token index will enforce unique associations between token symbol and contract address.
Token symbols are always strings, and should be specified using uppercase letters.
If the proof argument is included, the network will be queried for trusted signatures on the given proof(s). There must exist at least one trusted signature for every given proof for every token. Trusted signatures for the custodial system are provided at service startup.
The proof argument may be specified in a number of ways:
- as None, in which case proof checks are skipped (although there may still be builtin proof checks being performed)
- as a single string, where the same proof is used for each token lookup
- as an array of strings, where the respective proof is used for the respective token. number of proofs must match the number of tokens.
- as an array of lists, where the respective proofs in each list is used for the respective token. number of lists of proofs must match the number of tokens.
The success callback provided at the Api object instantiation will receive individual calls for each token that passes the proof checks. Each token that does not pass is passed to the Api error callback.
This method is not intended to be used synchronously. Do so at your peril.
:param token_symbols: Token symbol strings to look up
:type token_symbol: list
:param proof: Proof(s) to verify tokens against
:type proof: None, str or list
:returns: uuid of root task
:rtype: celery.Task
"""
if not isinstance(token_symbols, list):
raise ValueError('token symbols argument must be list')
if proof == None:
logg.debug('looking up tokens without external proof check: {}'.format(','.join(token_symbols)))
proof = ''
logg.debug('proof is {}'.format(proof))
l = len(token_symbols)
if len(proof) == 0:
l = 0
proof = Api.to_v_list(proof, l)
chain_spec_dict = self.chain_spec.asdict()
s_token_resolve = celery.signature(
'cic_eth.eth.erc20.resolve_tokens_by_symbol',
[
token_symbols,
chain_spec_dict,
],
queue=self.queue,
)
s_token_info = celery.signature(
'cic_eth.eth.erc20.token_info',
[
chain_spec_dict,
proof,
],
queue=self.queue,
)
s_token_verify = celery.signature(
'cic_eth.eth.erc20.verify_token_info',
[
chain_spec_dict,
self.callback_success,
self.callback_error,
],
queue=self.queue,
)
s_token_info.link(s_token_verify)
s_token_resolve.link(s_token_info)
return s_token_resolve.apply_async()
# def convert_transfer(self, from_address, to_address, target_return, minimum_return, from_token_symbol, to_token_symbol):
# """Executes a chain of celery tasks that performs conversion between two ERC20 tokens, and transfers to a specified receipient after convert has completed.
#
# :param from_address: Ethereum address of sender
# :type from_address: str, 0x-hex
# :param to_address: Ethereum address of receipient
# :type to_address: str, 0x-hex
# :param target_return: Estimated return from conversion
# :type target_return: int
# :param minimum_return: The least value of destination token return to allow
# :type minimum_return: int
# :param from_token_symbol: ERC20 token symbol of token being converted
# :type from_token_symbol: str
# :param to_token_symbol: ERC20 token symbol of token to receive
# :type to_token_symbol: str
# :returns: uuid of root task
# :rtype: celery.Task
# """
# raise NotImplementedError('out of service until new DEX migration is done')
# s_check = celery.signature(
# 'cic_eth.admin.ctrl.check_lock',
# [
# [from_token_symbol, to_token_symbol],
# self.chain_spec.asdict(),
# LockEnum.QUEUE,
# from_address,
# ],
# queue=self.queue,
# )
# s_nonce = celery.signature(
# 'cic_eth.eth.nonce.reserve_nonce',
# [
# self.chain_spec.asdict(),
# ],
# queue=self.queue,
# )
# s_tokens = celery.signature(
# 'cic_eth.eth.erc20.resolve_tokens_by_symbol',
# [
# self.chain_str,
# ],
# queue=self.queue,
# )
# s_convert = celery.signature(
# 'cic_eth.eth.bancor.convert_with_default_reserve',
# [
# from_address,
# target_return,
# minimum_return,
# to_address,
# self.chain_spec.asdict(),
# ],
# queue=self.queue,
# )
# s_nonce.link(s_tokens)
# s_check.link(s_nonce)
# if self.callback_param != None:
# s_convert.link(self.callback_success)
# s_tokens.link(s_convert).on_error(self.callback_error)
# else:
# s_tokens.link(s_convert)
#
# t = s_check.apply_async(queue=self.queue)
# return t
#
#
# def convert(self, from_address, target_return, minimum_return, from_token_symbol, to_token_symbol):
# """Executes a chain of celery tasks that performs conversion between two ERC20 tokens.
#
# :param from_address: Ethereum address of sender
# :type from_address: str, 0x-hex
# :param target_return: Estimated return from conversion
# :type target_return: int
# :param minimum_return: The least value of destination token return to allow
# :type minimum_return: int
# :param from_token_symbol: ERC20 token symbol of token being converted
# :type from_token_symbol: str
# :param to_token_symbol: ERC20 token symbol of token to receive
# :type to_token_symbol: str
# :returns: uuid of root task
# :rtype: celery.Task
# """
# raise NotImplementedError('out of service until new DEX migration is done')
# s_check = celery.signature(
# 'cic_eth.admin.ctrl.check_lock',
# [
# [from_token_symbol, to_token_symbol],
# self.chain_spec.asdict(),
# LockEnum.QUEUE,
# from_address,
# ],
# queue=self.queue,
# )
# s_nonce = celery.signature(
# 'cic_eth.eth.nonce.reserve_nonce',
# [
# self.chain_spec.asdict(),
# ],
# queue=self.queue,
# )
# s_tokens = celery.signature(
# 'cic_eth.eth.erc20.resolve_tokens_by_symbol',
# [
# self.chain_spec.asdict(),
# ],
# queue=self.queue,
# )
# s_convert = celery.signature(
# 'cic_eth.eth.bancor.convert_with_default_reserve',
# [
# from_address,
# target_return,
# minimum_return,
# from_address,
# self.chain_spec.asdict(),
# ],
# queue=self.queue,
# )
# s_nonce.link(s_tokens)
# s_check.link(s_nonce)
# if self.callback_param != None:
# s_convert.link(self.callback_success)
# s_tokens.link(s_convert).on_error(self.callback_error)
# else:
# s_tokens.link(s_convert)
#
# t = s_check.apply_async(queue=self.queue)
# return t
def transfer_from(self, from_address, to_address, value, token_symbol, spender_address):
"""Executes a chain of celery tasks that performs a transfer of ERC20 tokens by one address on behalf of another address to a third party.
:param from_address: Ethereum address of sender :param from_address: Ethereum address of sender
:type from_address: str, 0x-hex :type from_address: str, 0x-hex
:param to_address: Ethereum address of receipient :param to_address: Ethereum address of recipient
:type to_address: str, 0x-hex :type to_address: str, 0x-hex
:param target_return: Estimated return from conversion :param value: Estimated return from conversion
:type target_return: int :type value: int
:param minimum_return: The least value of destination token return to allow :param token_symbol: ERC20 token symbol of token to send
:type minimum_return: int :type token_symbol: str
:param from_token_symbol: ERC20 token symbol of token being converted :param spender_address: Ethereum address of recipient
:type from_token_symbol: str :type spender_address: str, 0x-hex
:param to_token_symbol: ERC20 token symbol of token to receive
:type to_token_symbol: str
:returns: uuid of root task :returns: uuid of root task
:rtype: celery.Task :rtype: celery.Task
""" """
raise NotImplementedError('out of service until new DEX migration is done')
s_check = celery.signature( s_check = celery.signature(
'cic_eth.admin.ctrl.check_lock', 'cic_eth.admin.ctrl.check_lock',
[ [
[from_token_symbol, to_token_symbol], [token_symbol],
self.chain_spec.asdict(), self.chain_spec.asdict(),
LockEnum.QUEUE, LockEnum.QUEUE,
from_address, from_address,
@@ -107,102 +321,51 @@ class Api:
'cic_eth.eth.nonce.reserve_nonce', 'cic_eth.eth.nonce.reserve_nonce',
[ [
self.chain_spec.asdict(), self.chain_spec.asdict(),
from_address,
], ],
queue=self.queue, queue=self.queue,
) )
s_tokens = celery.signature( s_tokens = celery.signature(
'cic_eth.eth.erc20.resolve_tokens_by_symbol', 'cic_eth.eth.erc20.resolve_tokens_by_symbol',
[ [
self.chain_str, self.chain_spec.asdict(),
], ],
queue=self.queue, queue=self.queue,
) )
s_convert = celery.signature( s_allow = celery.signature(
'cic_eth.eth.bancor.convert_with_default_reserve', 'cic_eth.eth.erc20.check_allowance',
[
from_address,
value,
self.chain_spec.asdict(),
spender_address,
],
queue=self.queue,
)
s_transfer = celery.signature(
'cic_eth.eth.erc20.transfer_from',
[ [
from_address, from_address,
target_return,
minimum_return,
to_address, to_address,
value,
self.chain_spec.asdict(), self.chain_spec.asdict(),
spender_address,
], ],
queue=self.queue, queue=self.queue,
) )
s_tokens.link(s_allow)
s_nonce.link(s_tokens) s_nonce.link(s_tokens)
s_check.link(s_nonce) s_check.link(s_nonce)
if self.callback_param != None: if self.callback_param != None:
s_convert.link(self.callback_success) s_transfer.link(self.callback_success)
s_tokens.link(s_convert).on_error(self.callback_error) s_allow.link(s_transfer).on_error(self.callback_error)
else: else:
s_tokens.link(s_convert) s_allow.link(s_transfer)
t = s_check.apply_async(queue=self.queue) t = s_check.apply_async(queue=self.queue)
return t return t
def convert(self, from_address, target_return, minimum_return, from_token_symbol, to_token_symbol):
"""Executes a chain of celery tasks that performs conversion between two ERC20 tokens.
:param from_address: Ethereum address of sender
:type from_address: str, 0x-hex
:param target_return: Estimated return from conversion
:type target_return: int
:param minimum_return: The least value of destination token return to allow
:type minimum_return: int
:param from_token_symbol: ERC20 token symbol of token being converted
:type from_token_symbol: str
:param to_token_symbol: ERC20 token symbol of token to receive
:type to_token_symbol: str
:returns: uuid of root task
:rtype: celery.Task
"""
raise NotImplementedError('out of service until new DEX migration is done')
s_check = celery.signature(
'cic_eth.admin.ctrl.check_lock',
[
[from_token_symbol, to_token_symbol],
self.chain_spec.asdict(),
LockEnum.QUEUE,
from_address,
],
queue=self.queue,
)
s_nonce = celery.signature(
'cic_eth.eth.nonce.reserve_nonce',
[
self.chain_spec.asdict(),
],
queue=self.queue,
)
s_tokens = celery.signature(
'cic_eth.eth.erc20.resolve_tokens_by_symbol',
[
self.chain_spec.asdict(),
],
queue=self.queue,
)
s_convert = celery.signature(
'cic_eth.eth.bancor.convert_with_default_reserve',
[
from_address,
target_return,
minimum_return,
from_address,
self.chain_spec.asdict(),
],
queue=self.queue,
)
s_nonce.link(s_tokens)
s_check.link(s_nonce)
if self.callback_param != None:
s_convert.link(self.callback_success)
s_tokens.link(s_convert).on_error(self.callback_error)
else:
s_tokens.link(s_convert)
t = s_check.apply_async(queue=self.queue)
return t
def transfer(self, from_address, to_address, value, token_symbol): def transfer(self, from_address, to_address, value, token_symbol):
"""Executes a chain of celery tasks that performs a transfer of ERC20 tokens from one address to another. """Executes a chain of celery tasks that performs a transfer of ERC20 tokens from one address to another.
@@ -218,6 +381,8 @@ class Api:
:returns: uuid of root task :returns: uuid of root task
:rtype: celery.Task :rtype: celery.Task
""" """
#from_address = strip_0x(from_address)
#to_address = strip_0x(to_address)
s_check = celery.signature( s_check = celery.signature(
'cic_eth.admin.ctrl.check_lock', 'cic_eth.admin.ctrl.check_lock',
[ [
@@ -484,9 +649,9 @@ class Api:
s_external_get = celery.signature( s_external_get = celery.signature(
external_task, external_task,
[ [
address,
offset, offset,
limit, limit,
address,
], ],
queue=external_queue, queue=external_queue,
) )

View File

@@ -0,0 +1,52 @@
# standard imports
import logging
# external imports
import celery
from chainlib.chain import ChainSpec
logg = logging.getLogger(__name__)
class ApiBase:
"""Creates task chains to perform well-known CIC operations.
Each method that sends tasks returns details about the root task. The root task uuid can be provided in the callback, to enable to caller to correlate the result with individual calls. It can also be used to independently poll the completion of a task chain.
:param callback_param: Static value to pass to callback
:type callback_param: str
:param callback_task: Callback task that executes callback_param call. (Must be included by the celery worker)
:type callback_task: string
:param queue: Name of worker queue to submit tasks to
:type queue: str
"""
def __init__(self, chain_str, queue='cic-eth', callback_param=None, callback_task='cic_eth.callbacks.noop.noop', callback_queue=None):
self.chain_str = chain_str
self.chain_spec = ChainSpec.from_chain_str(chain_str)
self.callback_param = callback_param
self.callback_task = callback_task
self.queue = queue
logg.debug('api using queue {}'.format(self.queue))
self.callback_success = None
self.callback_error = None
if callback_queue == None:
callback_queue=self.queue
if callback_param != None:
self.callback_success = celery.signature(
callback_task,
[
callback_param,
0,
],
queue=callback_queue,
)
self.callback_error = celery.signature(
callback_task,
[
callback_param,
1,
],
queue=callback_queue,
)

View File

@@ -1,7 +1,10 @@
import logging
import celery import celery
celery_app = celery.current_app celery_app = celery.current_app
logg = celery_app.log.get_default_logger() #logg = celery_app.log.get_default_logger()
logg = logging.getLogger()
@celery_app.task(bind=True) @celery_app.task(bind=True)

View File

@@ -21,7 +21,7 @@ def health(*args, **kwargs):
session = SessionBase.create_session() session = SessionBase.create_session()
config = kwargs['config'] config = kwargs['config']
chain_spec = ChainSpec.from_chain_str(config.get('CIC_CHAIN_SPEC')) chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
logg.debug('check gas balance of gas gifter for chain {}'.format(chain_spec)) logg.debug('check gas balance of gas gifter for chain {}'.format(chain_spec))
try: try:

View File

@@ -15,7 +15,7 @@ logg = logging.getLogger().getChild(__name__)
def health(*args, **kwargs): def health(*args, **kwargs):
blocked = True blocked = True
max_attempts = 5 max_attempts = 5
conn = RPCConnection.connect(kwargs['config'].get('CIC_CHAIN_SPEC'), tag='signer') conn = RPCConnection.connect(kwargs['config'].get('CHAIN_SPEC'), tag='signer')
for i in range(max_attempts): for i in range(max_attempts):
idx = i + 1 idx = i + 1
logg.debug('attempt signer connection check {}/{}'.format(idx, max_attempts)) logg.debug('attempt signer connection check {}/{}'.format(idx, max_attempts))

View File

@@ -0,0 +1,10 @@
# local imports
from .base import *
from .chain import (
EthChainInterface,
chain_interface,
)
from .rpc import RPC
from .arg import ArgumentParser
from .config import Config
from .celery import CeleryApp

View File

@@ -0,0 +1,31 @@
# external imports
from chainlib.eth.cli import ArgumentParser as BaseArgumentParser
# local imports
from .base import (
CICFlag,
Flag,
)
class ArgumentParser(BaseArgumentParser):
def process_local_flags(self, local_arg_flags):
if local_arg_flags & CICFlag.REDIS:
self.add_argument('--redis-host', dest='redis_host', type=str, help='redis host to use for task submission')
self.add_argument('--redis-port', dest='redis_port', type=int, help='redis host to use for task submission')
self.add_argument('--redis-db', dest='redis_db', type=int, help='redis db to use')
if local_arg_flags & CICFlag.REDIS_CALLBACK:
self.add_argument('--redis-host-callback', dest='redis_host_callback', default='localhost', type=str, help='redis host to use for callback')
self.add_argument('--redis-port-callback', dest='redis_port_callback', default=6379, type=int, help='redis port to use for callback')
self.add_argument('--redis-timeout', default=20.0, type=float, help='Redis callback timeout')
if local_arg_flags & CICFlag.CELERY:
self.add_argument('-q', '--celery-queue', dest='celery_queue', type=str, default='cic-eth', help='Task queue')
if local_arg_flags & CICFlag.SYNCER:
self.add_argument('--offset', type=int, default=0, help='Start block height for initial history sync')
self.add_argument('--no-history', action='store_true', dest='no_history', help='Skip initial history sync')
if local_arg_flags & CICFlag.CHAIN:
self.add_argument('-r', '--registry-address', type=str, dest='registry_address', help='CIC registry contract address')

View File

@@ -0,0 +1,31 @@
# standard imports
import enum
# external imports
from chainlib.eth.cli import (
argflag_std_read,
argflag_std_write,
argflag_std_base,
Flag,
)
class CICFlag(enum.IntEnum):
# celery - nibble 1
CELERY = 1
# redis - nibble 2
REDIS = 16
REDIS_CALLBACK = 32
# chain - nibble 3
CHAIN = 256
# sync - nibble 4
SYNCER = 4096
argflag_local_task = CICFlag.CELERY
argflag_local_taskcallback = argflag_local_task | CICFlag.REDIS | CICFlag.REDIS_CALLBACK
argflag_local_chain = CICFlag.CHAIN
argflag_local_sync = CICFlag.SYNCER | CICFlag.CHAIN

View File

@@ -0,0 +1,24 @@
# standard imports
import logging
# external imports
import celery
logg = logging.getLogger(__name__)
class CeleryApp:
@classmethod
def from_config(cls, config):
backend_url = config.get('CELERY_RESULT_URL')
broker_url = config.get('CELERY_BROKER_URL')
celery_app = None
if backend_url != None:
celery_app = celery.Celery(broker=broker_url, backend=backend_url)
logg.info('creating celery app on {} with backend on {}'.format(broker_url, backend_url))
else:
celery_app = celery.Celery(broker=broker_url)
logg.info('creating celery app without results backend on {}'.format(broker_url))
return celery_app

View File

@@ -0,0 +1,21 @@
# external imports
from chainlib.eth.block import (
block_by_number,
Block,
)
from chainlib.eth.tx import (
receipt,
Tx,
)
from chainlib.interface import ChainInterface
class EthChainInterface(ChainInterface):
def __init__(self):
self._tx_receipt = receipt
self._block_by_number = block_by_number
self._block_from_src = Block.from_src
self._src_normalize = Tx.src_normalize
chain_interface = EthChainInterface()

View File

@@ -0,0 +1,63 @@
# standard imports
import os
import logging
# external imports
from chainlib.eth.cli import (
Config as BaseConfig,
Flag,
)
# local imports
from .base import CICFlag
script_dir = os.path.dirname(os.path.realpath(__file__))
logg = logging.getLogger(__name__)
class Config(BaseConfig):
local_base_config_dir = os.path.join(script_dir, '..', 'data', 'config')
@classmethod
def from_args(cls, args, arg_flags, local_arg_flags, extra_args={}, default_config_dir=None, base_config_dir=None, default_fee_limit=None):
expanded_base_config_dir = [cls.local_base_config_dir]
if base_config_dir != None:
if isinstance(base_config_dir, str):
base_config_dir = [base_config_dir]
for d in base_config_dir:
expanded_base_config_dir.append(d)
config = BaseConfig.from_args(args, arg_flags, extra_args=extra_args, default_config_dir=default_config_dir, base_config_dir=expanded_base_config_dir, load_callback=None)
local_args_override = {}
if local_arg_flags & CICFlag.REDIS:
local_args_override['REDIS_HOST'] = getattr(args, 'redis_host')
local_args_override['REDIS_PORT'] = getattr(args, 'redis_port')
local_args_override['REDIS_DB'] = getattr(args, 'redis_db')
local_args_override['REDIS_TIMEOUT'] = getattr(args, 'redis_timeout')
if local_arg_flags & CICFlag.CHAIN:
local_args_override['CIC_REGISTRY_ADDRESS'] = getattr(args, 'registry_address')
if local_arg_flags & CICFlag.CELERY:
local_args_override['CELERY_QUEUE'] = getattr(args, 'celery_queue')
if local_arg_flags & CICFlag.SYNCER:
local_args_override['SYNCER_OFFSET'] = getattr(args, 'offset')
local_args_override['SYNCER_NO_HISTORY'] = getattr(args, 'no_history')
config.dict_override(local_args_override, 'local cli args')
if local_arg_flags & CICFlag.REDIS_CALLBACK:
config.add(getattr(args, 'redis_host_callback'), '_REDIS_HOST_CALLBACK')
config.add(getattr(args, 'redis_port_callback'), '_REDIS_PORT_CALLBACK')
if local_arg_flags & CICFlag.CELERY:
config.add(config.true('CELERY_DEBUG'), 'CELERY_DEBUG', exists_ok=True)
logg.debug('config loaded:\n{}'.format(config))
return config

View File

@@ -0,0 +1,90 @@
# standard imports
import logging
# external imports
from chainlib.connection import (
RPCConnection,
ConnType,
)
from chainlib.eth.connection import (
EthUnixSignerConnection,
EthHTTPSignerConnection,
)
from chainlib.chain import ChainSpec
logg = logging.getLogger(__name__)
class RPC:
def __init__(self, chain_spec, rpc_provider, signer_provider=None):
self.chain_spec = chain_spec
self.rpc_provider = rpc_provider
self.signer_provider = signer_provider
def get_default(self):
return self.get_by_label('default')
def get_by_label(self, label):
return RPCConnection.connect(self.chain_spec, label)
@staticmethod
def from_config(config, use_signer=False, default_label='default', signer_label='signer'):
chain_spec = ChainSpec.from_chain_str(config.get('CHAIN_SPEC'))
RPCConnection.register_location(config.get('RPC_PROVIDER'), chain_spec, default_label)
if use_signer:
RPCConnection.register_constructor(ConnType.UNIX, EthUnixSignerConnection, signer_label)
RPCConnection.register_constructor(ConnType.HTTP, EthHTTPSignerConnection, signer_label)
RPCConnection.register_constructor(ConnType.HTTP_SSL, EthHTTPSignerConnection, signer_label)
RPCConnection.register_location(config.get('SIGNER_PROVIDER'), chain_spec, signer_label)
rpc = RPC(chain_spec, config.get('RPC_PROVIDER'), signer_provider=config.get('SIGNER_PROVIDER'))
logg.info('set up rpc: {}'.format(rpc))
return rpc
def __str__(self):
return 'RPC factory, chain {}, rpc {}, signer {}'.format(self.chain_spec, self.rpc_provider, self.signer_provider)
# TOOD: re-implement file backend option from omittec code:
#broker = config.get('CELERY_BROKER_URL')
#if broker[:4] == 'file':
# bq = tempfile.mkdtemp()
# bp = tempfile.mkdtemp()
# conf_update = {
# 'broker_url': broker,
# 'broker_transport_options': {
# 'data_folder_in': bq,
# 'data_folder_out': bq,
# 'data_folder_processed': bp,
# },
# }
# if config.true('CELERY_DEBUG'):
# conf_update['result_extended'] = True
# current_app.conf.update(conf_update)
# logg.warning('celery broker dirs queue i/o {} processed {}, will NOT be deleted on shutdown'.format(bq, bp))
#else:
# conf_update = {
# 'broker_url': broker,
# }
# if config.true('CELERY_DEBUG'):
# conf_update['result_extended'] = True
# current_app.conf.update(conf_update)
#
#result = config.get('CELERY_RESULT_URL')
#if result[:4] == 'file':
# rq = tempfile.mkdtemp()
# current_app.conf.update({
# 'result_backend': 'file://{}'.format(rq),
# })
# logg.warning('celery backend store dir {} created, will NOT be deleted on shutdown'.format(rq))
#else:
# current_app.conf.update({
# 'result_backend': result,
# })
#

View File

@@ -0,0 +1,5 @@
[celery]
broker_url = redis://localhost:6379
result_url =
queue = cic-eth
debug = 0

View File

@@ -1,8 +1,6 @@
[cic] [cic]
registry_address = registry_address =
chain_spec = evm:bloxberg:8996
tx_retry_delay =
trust_address = trust_address =
default_token_symbol = GFT default_token_symbol =
health_modules = cic_eth.check.db,cic_eth.check.redis,cic_eth.check.signer,cic_eth.check.gas health_modules = cic_eth.check.db,cic_eth.check.redis,cic_eth.check.signer,cic_eth.check.gas
run_dir = /run run_dir = /run

View File

@@ -0,0 +1,10 @@
[database]
engine =
driver =
host =
port =
name =
user =
password =
debug = 0
pool_size = 0

View File

@@ -0,0 +1,2 @@
[dispatcher]
loop_interval = 1

View File

@@ -1,3 +1,2 @@
[eth] [eth]
provider = http://localhost:8545
gas_gifter_minimum_balance = 10000000000000000000000 gas_gifter_minimum_balance = 10000000000000000000000

View File

@@ -0,0 +1,5 @@
[redis]
host = localhost
port = 6379
db = 0
timeout = 20.0

View File

@@ -0,0 +1,3 @@
[retry]
delay =
batch_size =

View File

@@ -0,0 +1,2 @@
[signer]
provider =

View File

@@ -0,0 +1,4 @@
[syncer]
loop_interval = 1
offset = 0
no_history = 0

View File

@@ -1,158 +1 @@
# standard imports from cic_eth.enum import *
import enum
@enum.unique
class StatusBits(enum.IntEnum):
"""Individual bit flags that are combined to define the state and legacy of a queued transaction
"""
QUEUED = 0x01 # transaction should be sent to network
IN_NETWORK = 0x08 # transaction is in network
DEFERRED = 0x10 # an attempt to send the transaction to network has failed
GAS_ISSUES = 0x20 # transaction is pending sender account gas funding
LOCAL_ERROR = 0x100 # errors that originate internally from the component
NODE_ERROR = 0x200 # errors originating in the node (invalid RLP input...)
NETWORK_ERROR = 0x400 # errors that originate from the network (REVERT)
UNKNOWN_ERROR = 0x800 # unclassified errors (the should not occur)
FINAL = 0x1000 # transaction processing has completed
OBSOLETE = 0x2000 # transaction has been replaced by a different transaction with higher fee
MANUAL = 0x8000 # transaction processing has been manually overridden
@enum.unique
class StatusEnum(enum.IntEnum):
"""
- Inactive, not finalized. (<0)
* PENDING: The initial state of a newly added transaction record. No action has been performed on this transaction yet.
* SENDFAIL: The transaction was not received by the node.
* RETRY: The transaction is queued for a new send attempt after previously failing.
* READYSEND: The transaction is queued for its first send attempt
* OBSOLETED: A new transaction with the same nonce and higher gas has been sent to network.
* WAITFORGAS: The transaction is on hold pending gas funding.
- Active state: (==0)
* SENT: The transaction has been sent to the mempool.
- Inactive, finalized. (>0)
* FUBAR: Unknown error occurred and transaction is abandoned. Manual intervention needed.
* CANCELLED: The transaction was sent, but was not mined and has disappered from the mempool. This usually follows a transaction being obsoleted.
* OVERRIDDEN: Transaction has been manually overriden.
* REJECTED: The transaction was rejected by the node.
* REVERTED: The transaction was mined, but exception occurred during EVM execution. (Block number will be set)
* SUCCESS: THe transaction was successfully mined. (Block number will be set)
"""
PENDING = 0
SENDFAIL = StatusBits.DEFERRED | StatusBits.LOCAL_ERROR
RETRY = StatusBits.QUEUED | StatusBits.DEFERRED
READYSEND = StatusBits.QUEUED
OBSOLETED = StatusBits.OBSOLETE | StatusBits.IN_NETWORK
WAITFORGAS = StatusBits.GAS_ISSUES
SENT = StatusBits.IN_NETWORK
FUBAR = StatusBits.FINAL | StatusBits.UNKNOWN_ERROR
CANCELLED = StatusBits.IN_NETWORK | StatusBits.FINAL | StatusBits.OBSOLETE
OVERRIDDEN = StatusBits.FINAL | StatusBits.OBSOLETE | StatusBits.MANUAL
REJECTED = StatusBits.NODE_ERROR | StatusBits.FINAL
REVERTED = StatusBits.IN_NETWORK | StatusBits.FINAL | StatusBits.NETWORK_ERROR
SUCCESS = StatusBits.IN_NETWORK | StatusBits.FINAL
@enum.unique
class LockEnum(enum.IntEnum):
"""
STICKY: When set, reset is not possible
CREATE: Disable creation of accounts
SEND: Disable sending to network
QUEUE: Disable queueing new or modified transactions
"""
STICKY=1
INIT=2
CREATE=4
SEND=8
QUEUE=16
QUERY=32
ALL=int(0xfffffffffffffffe)
def status_str(v, bits_only=False):
"""Render a human-readable string describing the status
If the bit field exactly matches a StatusEnum value, the StatusEnum label will be returned.
If a StatusEnum cannot be matched, the string will be postfixed with "*", unless explicitly instructed to return bit field labels only.
:param v: Status bit field
:type v: number
:param bits_only: Only render individual bit labels.
:type bits_only: bool
:returns: Status string
:rtype: str
"""
s = ''
if not bits_only:
try:
s = StatusEnum(v).name
return s
except ValueError:
pass
if v == 0:
return 'NONE'
for i in range(16):
b = (1 << i)
if (b & 0xffff) & v:
n = StatusBits(b).name
if len(s) > 0:
s += ','
s += n
if not bits_only:
s += '*'
return s
def all_errors():
"""Bit mask of all error states
:returns: Error flags
:rtype: number
"""
return StatusBits.LOCAL_ERROR | StatusBits.NODE_ERROR | StatusBits.NETWORK_ERROR | StatusBits.UNKNOWN_ERROR
def is_error_status(v):
"""Check if value is an error state
:param v: Status bit field
:type v: number
:returns: True if error
:rtype: bool
"""
return bool(v & all_errors())
def dead():
"""Bit mask defining whether a transaction is still likely to be processed on the network.
:returns: Bit mask
:rtype: number
"""
return StatusBits.FINAL | StatusBits.OBSOLETE
def is_alive(v):
"""Check if transaction is still likely to be processed on the network.
The contingency of "likely" refers to the case a transaction has been obsoleted after sent to the network, but the network still confirms the obsoleted transaction. The return value of this method will not change as a result of this, BUT the state itself will (as the FINAL bit will be set).
:returns:
"""
return bool(v & dead() == 0)

View File

@@ -8,7 +8,8 @@ Create Date: 2021-04-02 18:30:55.398388
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from chainqueue.db.migrations.sqlalchemy import ( #from chainqueue.db.migrations.sqlalchemy import (
from chainqueue.db.migrations.default.export import (
chainqueue_upgrade, chainqueue_upgrade,
chainqueue_downgrade, chainqueue_downgrade,
) )

View File

@@ -8,8 +8,8 @@ Create Date: 2021-04-02 18:41:20.864265
import datetime import datetime
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from chainlib.eth.constant import ZERO_ADDRESS
from cic_eth.db.enum import LockEnum from cic_eth.db.enum import LockEnum
from cic_eth.encode import ZERO_ADDRESS_NORMAL
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
@@ -30,7 +30,7 @@ def upgrade():
sa.Column("otx_id", sa.Integer, sa.ForeignKey('otx.id'), nullable=True), sa.Column("otx_id", sa.Integer, sa.ForeignKey('otx.id'), nullable=True),
) )
op.create_index('idx_chain_address', 'lock', ['blockchain', 'address'], unique=True) op.create_index('idx_chain_address', 'lock', ['blockchain', 'address'], unique=True)
op.execute("INSERT INTO lock (address, date_created, blockchain, flags) VALUES('{}', '{}', '::', {})".format(ZERO_ADDRESS, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE)) op.execute("INSERT INTO lock (address, date_created, blockchain, flags) VALUES('{}', '{}', '::', {})".format(ZERO_ADDRESS_NORMAL, datetime.datetime.utcnow(), LockEnum.INIT | LockEnum.SEND | LockEnum.QUEUE))
def downgrade(): def downgrade():

Some files were not shown because too many files have changed in this diff Show More