mirror of
git://holbrook.no/eth-monitor.git
synced 2025-04-08 21:01:02 +02:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ca80c0d75f | ||
|
d7ee238bff | ||
|
2a54256821 | ||
|
a29ae35597 | ||
|
c99259b2ed | ||
|
fa694c957b | ||
|
e0b6e2c14b | ||
|
e176874c30 | ||
|
10e16dcb00 | ||
|
d36b3ed673 | ||
|
3f5d24a6c1 | ||
|
3dee984eb9 | ||
|
39ee38b0dc | ||
|
270a35bada | ||
|
a66bb4e12d | ||
|
f72b1740d6 | ||
|
705bdc9471 | ||
|
f2733b50f9 | ||
|
5598740173 | ||
|
c6b3ef6707 | ||
|
b65d63daa1 | ||
|
b8fe525cbe | ||
|
f59fd52d43 | ||
|
a175ea79ee | ||
|
243bc86b73 | ||
|
fe9b657b36 | ||
|
2f3e652ca3 | ||
|
eedc886179 | ||
|
a7814de98d | ||
|
28584aec35 | ||
|
02d27ef167 | ||
|
d5b4a8d362 | ||
|
177aa95abe | ||
|
39dc90fe9e | ||
|
80eee2b779 | ||
|
239e10ba5a | ||
|
bc3f4ff8fb | ||
|
b87aa875f0 | ||
|
6d23bd04d2 | ||
|
89d9121baa | ||
|
7d5f2d9b4e | ||
|
c2d8a03483 |
59
CHANGELOG
59
CHANGELOG
@ -1,3 +1,62 @@
|
||||
- 0.8.8
|
||||
* Skip rules filter processing for cache when deactivated
|
||||
* Add match-all flag to rule processing
|
||||
* Add match-all flag to CLI to toggle setting match_all flag to rule processing for include criteria
|
||||
* Implement state dir (rundir), and last synced block output
|
||||
- 0.8.7
|
||||
* Upgrade chainsyncer (and shep) to avoid state deletion on partial filter list interrupts
|
||||
- 0.8.6
|
||||
* Handle crash on conrtact creation when recipient filter is active
|
||||
- 0.8.5
|
||||
* Instantiate constructor for chain interface superclass
|
||||
* Remove unused settings transform method for sync interface
|
||||
- 0.8.4
|
||||
* Update man pages with rpc batch limit setting
|
||||
- 0.8.3
|
||||
* Update man pages
|
||||
- 0.8.2
|
||||
* Handle undefined content-key argument
|
||||
- 0.8.1
|
||||
* Implement syncer context
|
||||
* Add key-value parameter for cli, environment, config to pass to syncer context
|
||||
- 0.8.0
|
||||
* Implement RPC batch limits for syncer
|
||||
- 0.7.6
|
||||
* Make rpc dialect work with chain interface
|
||||
- 0.7.5
|
||||
* Add readme (from man page)
|
||||
* Add package descriptino (from man page)
|
||||
- 0.7.4
|
||||
* Add man pages to package
|
||||
- 0.7.3
|
||||
* Upgrade deps
|
||||
- 0.7.2
|
||||
* Fix tx index in default output for sync command
|
||||
- 0.7.1
|
||||
* Change license to AGPL3 and copyright waived to public domain
|
||||
* Add missing log module in cli lib
|
||||
- 0.7.0
|
||||
* Reactivate cache rpc
|
||||
* Upgrade dependencies
|
||||
- 0.6.0
|
||||
* Allow missing filters in arugments
|
||||
- 0.5.1
|
||||
* Remove useless need to add session dir for mem backend
|
||||
* Reenable renderers and filters specified through args in new settings setup
|
||||
- 0.5.0
|
||||
* Implement on chainlib 0.3.0
|
||||
- 0.4.8
|
||||
* Upgrade chainlib to 0.2.0
|
||||
- 0.4.5
|
||||
* Upgrade chainsyncer
|
||||
- 0.4.4
|
||||
* Upgrade deps
|
||||
- 0.4.3
|
||||
* Upgrade deps
|
||||
- 0.4.2
|
||||
* Implement non-persistent (in-memory) sync state
|
||||
- 0.4.1
|
||||
* Correct too restrictive python version constraint
|
||||
- 0.4.0
|
||||
* Add rocksdb backend
|
||||
- 0.3.3
|
||||
|
141
LICENSE
141
LICENSE
@ -1,5 +1,5 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
@ -7,17 +7,15 @@
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
@ -72,7 +60,7 @@ modification follow.
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
@ -1 +1 @@
|
||||
include *requirements.txt eth_monitor/data/config/*
|
||||
include *requirements.txt LICENSE CHANGELOG WAIVER WAIVER.asc eth_monitor/data/config/* man/build/*.1 README*
|
||||
|
12
Makefile
12
Makefile
@ -1,10 +1,12 @@
|
||||
PREFIX ?= /usr/local
|
||||
BUILD_DIR = build/$(PREFIX)/share/man
|
||||
#BUILD_DIR = build/$(PREFIX)/share/man
|
||||
MAN_DIR = man
|
||||
|
||||
man:
|
||||
mkdir -vp $(BUILD_DIR)
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor -d $(BUILD_DIR)/ man
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor-list -d $(BUILD_DIR)/ man
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor-import -d $(BUILD_DIR)/ man
|
||||
mkdir -vp $(MAN_DIR)/build
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor -d $(MAN_DIR)/build $(MAN_DIR)
|
||||
cp -v $(MAN_DIR)/build/eth-monitor.1 $(MAN_DIR)/build/eth-monitor-sync.1
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor-list -d $(MAN_DIR)/build $(MAN_DIR)
|
||||
chainlib-man.py -b 0xbf -v -n eth-monitor-import -d $(MAN_DIR)/build $(MAN_DIR)
|
||||
|
||||
.PHONY: man
|
||||
|
284
README.md
Normal file
284
README.md
Normal file
@ -0,0 +1,284 @@
|
||||
# NAME
|
||||
|
||||
eth-monitor - Cache, index and monitor transactions with an EVM node rpc
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**eth-monitor** \[ --skip-history \] \[ --single \] \[ p *eth_provider*
|
||||
\] \[ --includes-file *file* \] \[ -i chain_spec \] **eth-monitor** \[
|
||||
--skip-history \] \[ --single \] \[ p *eth_provider* \] \[
|
||||
--excludes-file *file* \] \[ --include-default \] \[ -i chain_spec \]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The **eth-monitor** has fulfills three distinct but related functions:
|
||||
|
||||
> 1\. A customizable view of on transactions of interest.
|
||||
>
|
||||
> 2\. A block and transaction cache.
|
||||
>
|
||||
> 3\. Arbitrary code executions using a transaction (and its block) as
|
||||
> input.
|
||||
|
||||
Using an EVM RPC endpoint, the **eth-monitor** tool will retrieve blocks
|
||||
within a given range and provides arbitrary processing of each
|
||||
transaction.
|
||||
|
||||
A collection of options is provided to control the behavior of which
|
||||
block ranges to sync, which criteria to use for display and cache, and
|
||||
what code to execute for matching transactions. Details on each topic
|
||||
can be found in the **SYNCING**, **MATCHING ADDRESSES** and **DEFINING
|
||||
FILTERS** sections below, respectively.
|
||||
|
||||
Example executions of the tool can be found in the **EXAMPLES** section.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
**-0**
|
||||
Omit newline to output
|
||||
|
||||
**--address ***address*
|
||||
Add an address of interest to match any role. Complements
|
||||
**--address-file***.*
|
||||
|
||||
**-c ***config_dir, ***--config ***config_dir*
|
||||
Load configuration files from given directory. All files with an .ini
|
||||
extension will be loaded, of which all must contain valid ini file data.
|
||||
|
||||
**--dumpconfig ***format*
|
||||
Output configuration settings rendered from environment and inputs.
|
||||
Valid arguments are *ini for ini file output, and env for environment
|
||||
variable output. See ***CONFIGURATION***.*
|
||||
|
||||
**--env-prefix**
|
||||
Environment prefix for variables to overwrite configuration. Example: If
|
||||
**--env-prefix*** is set to ***FOO*** then configuration variable
|
||||
***BAR_BAZ*** would be set by environment variable ***FOO_BAZ_BAR***.
|
||||
Also see ***ENVIRONMENT***.*
|
||||
|
||||
**--excludes-file ***file*
|
||||
Load address exclude matching rules from file. See **MATCHING
|
||||
ADDRESSES***.*
|
||||
|
||||
**--exec ***address*
|
||||
Add an address of interest to executable address array. Complements
|
||||
**--address-file***.*
|
||||
|
||||
**--filter ***module*
|
||||
Add code execution filter to all matched transactions. The argument must
|
||||
be a python module path. Several filters may be added by supplying the
|
||||
option multiple times. Filters will be executed in the order the options
|
||||
are given. See **DEFINING FILTERS*** section of ***eth-monitor (1)***
|
||||
for more details.*
|
||||
|
||||
**--height**
|
||||
Block height at which to query state for. Does not apply to
|
||||
transactions.
|
||||
|
||||
**-i ***chain_spec, ***--chain-spec ***chain_spec*
|
||||
Chain specification string, in the format
|
||||
\<engine\>:\<fork\>:\<chain_id\>:\<common_name\>. Example:
|
||||
"evm:london:1:ethereum". Overrides the *RPC_CREDENTIALS configuration
|
||||
setting.*
|
||||
|
||||
**--include-default **
|
||||
Match all addresses by default. Addresses may be excluded using
|
||||
--excludes-file. If this is set, --input, --output, --exec and
|
||||
--includes-file will have no effect.
|
||||
|
||||
**--includes-file ***file*
|
||||
Load address include matching rules from file. See **MATCHING
|
||||
ADDRESSES***.*
|
||||
|
||||
**--input ***address*
|
||||
Add an address of interest to inputs (recipients) array. Complements
|
||||
**--address-file***.*
|
||||
|
||||
**-n ***namespace, ***--namespace ***namespace*
|
||||
Load given configuration namespace. Configuration will be loaded from
|
||||
the immediate configuration subdirectory with the same name.
|
||||
|
||||
**--no-logs**
|
||||
Turn of logging completely. Negates **-v*** and ***-vv**
|
||||
|
||||
**--output ***address*
|
||||
Add an address of interest to outputs (sender) array. Complements
|
||||
**--address-file***.*
|
||||
|
||||
**-p***, ***--rpc-provider**
|
||||
Fully-qualified URL of RPC provider. Overrides the *RPC_PROVIDER
|
||||
configuration setting.*
|
||||
|
||||
**--raw**
|
||||
Produce output most optimized for machines.
|
||||
|
||||
**--renderer ***module*
|
||||
Add output renderer filter to all matched transactions. The argument
|
||||
must be a python module path. Several renderers may be added by
|
||||
supplying the option multiple times. See **RENDERERS*** section of
|
||||
***eth-monitor (1)*** for more details.*
|
||||
|
||||
**--rpc-dialect**
|
||||
RPC backend dialect. If specified it *may help with encoding and
|
||||
decoding issues. Overrides the RPC_DIALECT configuration setting.*
|
||||
|
||||
**--store-block-data **
|
||||
Store block data in cache for matching transactions. Requires
|
||||
**--cache-dir***.*
|
||||
|
||||
**--store-tx-data **
|
||||
Store transaction data in cache for matching transactions. Requires
|
||||
**--cache-dir***.*
|
||||
|
||||
**-u***, ***--unsafe**
|
||||
Allow addresses that do not pass checksum.
|
||||
|
||||
**-v**
|
||||
Verbose. Show logs for important state changes.
|
||||
|
||||
**-vv**
|
||||
Very verbose. Show logs with debugging information.
|
||||
|
||||
**--x-address ***address*
|
||||
Add an address of interest to match any role.
|
||||
|
||||
**--x-exec ***address*
|
||||
Add an address of disinterest to executable address array.
|
||||
|
||||
**--x-input ***address*
|
||||
Add an address of disinterest to inputs (recipients) array.
|
||||
|
||||
**--x-output ***address*
|
||||
Add an address of disinterest to outputs (sender) array.
|
||||
|
||||
# CONFIGURATION
|
||||
|
||||
All configuration settings may be overriden both by environment
|
||||
variables, or by overriding settings with the contents of ini-files in
|
||||
the directory defined by the **-c*** option.*
|
||||
|
||||
The active configuration, with values assigned from environment and
|
||||
arguments, can be output using the **--dumpconfig*** format option. Note
|
||||
that entries having keys prefixed with underscore (e.g. \_SEQ) are not
|
||||
actual configuration settings, and thus cannot be overridden with
|
||||
environment variables.*
|
||||
|
||||
To refer to a configuration setting by environment variables, the
|
||||
*section and key are concatenated together with an underscore, and
|
||||
transformed to upper-case. For example, the configuration variable
|
||||
FOO_BAZ_BAR refers to an ini-file entry as follows:*
|
||||
|
||||
[foo]
|
||||
bar_baz = xyzzy
|
||||
|
||||
In the **ENVIRONMENT*** section below, the relevant configuration
|
||||
settings for this tool is listed along with a short description of its
|
||||
meaning.*
|
||||
|
||||
Some configuration settings may also be overriden by command line
|
||||
options. Also note that the use of the **-n*** and ***--env-prefix***
|
||||
options affect how environment and configuration is read. The effects of
|
||||
options on how configuration settings are affective is described in the
|
||||
respective ***OPTIONS*** section.*
|
||||
|
||||
# MATCHING ADDRESSES
|
||||
|
||||
By default, addresses to match against transactions need to be
|
||||
explicitly specified. This behavior can be reversed with the
|
||||
**--include-default*** option. Addresses to match are defined using the
|
||||
***--input***, ***--output*** and ***--exec*** options. Addresses
|
||||
specified multiple times will be deduplicated.*
|
||||
|
||||
Inclusion rules may also be loaded from file by specifying the
|
||||
**--includes-file*** and ***--excludes-file*** options. Each file must
|
||||
specify the outputs, inputs and exec addresses as comma separated lists
|
||||
respectively, separated by tabs.*
|
||||
|
||||
In the current state of this tool, address matching will affect all
|
||||
parts of the processing; cache, code execution and rendering.
|
||||
|
||||
# SYNCING
|
||||
|
||||
When a sync is initiated, the state of this sync is persisted. This way,
|
||||
previous syncs that did not complete for some reason will be resumed
|
||||
where they left off.
|
||||
|
||||
A special sync type **--head*** starts syncing at the current head of
|
||||
the chain, and continue to sync until interrupted. When resuming sync, a
|
||||
new sync range between the current block head and the block height at
|
||||
which the previous ***--head*** sync left off will automatically be
|
||||
created.*
|
||||
|
||||
Syncs can be forced to (re)run for ranges regardless of previous state
|
||||
by using the **--single*** option. However, there is no protection in
|
||||
place from preventing code filters from being executed again on the same
|
||||
transaction when this is done. See ***DEFINING FILTERS*** below.*
|
||||
|
||||
# CACHE
|
||||
|
||||
When syncing, the hash of a block and transaction matching the address
|
||||
criteria will be stored in the cache. The hashes can be used for future
|
||||
data lookups.
|
||||
|
||||
If **--store-block-data*** and/or ***--store-tx-data*** is set, a copy
|
||||
of the block and/or transaction data will also be stored, respectively.*
|
||||
|
||||
# RENDERING
|
||||
|
||||
Rendering in the context of **eth-monitor*** refers to a formatted
|
||||
output stream that occurs independently of caching and code execution.*
|
||||
|
||||
Filters for rendering may be specified by specifying python modules to
|
||||
the **--renderer*** option. This option may be specified multiple
|
||||
times.*
|
||||
|
||||
Rendering filters will be executed in order, and the first filter to
|
||||
return *False*
|
||||
|
||||
# DEFINING FILTERS
|
||||
|
||||
A python module used for filter must fulfill two conditions:
|
||||
|
||||
> 1\. It must provide a class named *Filter in the package base
|
||||
> namespace.*
|
||||
>
|
||||
> 2\. The *Filter class must include a method named filter with the
|
||||
> signature def filter(self, conn, block, tx, db_session=None). *
|
||||
|
||||
Filters will strictly be executed in the order which they are defined on
|
||||
the command line.
|
||||
|
||||
# FURTHER READING
|
||||
|
||||
Refer to the **chainsyncer*** chapter n info chaintool for in-depth
|
||||
information on the subjects of syncing and filtering.*
|
||||
|
||||
# ENVIRONMENT
|
||||
|
||||
*CHAIN_SPEC*
|
||||
String specifying the type of chain connected to, in the format
|
||||
*\<engine\>:\<fork\>:\<network_id\>:\<common_name\>. For EVM nodes the
|
||||
engine value will always be evm.*
|
||||
|
||||
*RPC_DIALECT*
|
||||
Enables translations of EVM node specific formatting and response codes.
|
||||
|
||||
*RPC_PROVIDER*
|
||||
Fully-qualified URL to the RPC endpoint of the blockchain node.
|
||||
|
||||
# LICENSE
|
||||
|
||||
This documentation and its source is licensed under the Creative Commons
|
||||
Attribution-Sharealike 4.0 International license.
|
||||
|
||||
The source code of the tool this documentation describes is licensed
|
||||
under the GNU General Public License 3.0.
|
||||
|
||||
# COPYRIGHT
|
||||
|
||||
Louis Holbrook \<dev@holbrook.no\> (https://holbrook.no) PGP:
|
||||
59A844A484AC11253D3A3E9DCDCBD24DD1D0E001
|
||||
|
||||
# SOURCE CODE
|
||||
|
||||
https://git.defalsify.org
|
17
WAIVER
Normal file
17
WAIVER
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright waiver for the python package "eth-monitor"
|
||||
|
||||
I dedicate any and all copyright interest in this software to the
|
||||
public domain. I make this dedication for the benefit of the public at
|
||||
large and to the detriment of my heirs and successors. I intend this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all
|
||||
present and future rights to this software under copyright law.
|
||||
|
||||
To the best of my knowledge and belief, my contributions are either
|
||||
originally authored by me or are derived from prior works which I have
|
||||
verified are also in the public domain and are not subject to claims
|
||||
of copyright by other parties.
|
||||
|
||||
To the best of my knowledge and belief, no individual, business,
|
||||
organization, government, or other entity has any copyright interest
|
||||
in my contributions, and I affirm that I will not make contributions
|
||||
that are otherwise encumbered.
|
29
WAIVER.asc
Normal file
29
WAIVER.asc
Normal file
@ -0,0 +1,29 @@
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
owGVU39QFFUc58fFwHYYYvwq0cfPoO6umsoEJgMJ4wQVGn7IiMje3ru7B7e7d/vj
|
||||
rmMwS7iwMPImp8LUIGdATWgsHamjyYCQPGZkYEIkQ7QcEobRVIYGTXu7B2r1V3/c
|
||||
zO2+7/fz8+2uYH+fQN+YwQn3jq7WNb5t/rwuoDhDW5T1GmW96YwDmazFwSGjSQB2
|
||||
EtkgBwwsBwQTBBaHYGIZYCGpStIIQSwUTGqaZZDAcrEEoQV6qEcUKUBAMg780wPS
|
||||
bAbUPTTECJCDvPQHwyEe8KxBsJMcBAIr4RMWUWdGFNCzNIkYDdACmqyE3tF5aITp
|
||||
F9ToIAMNSACswSvOu0wKhJnkjFDm9+LiZYFDNGTkWdoBTBBxvDzAixQFeZ7leIlO
|
||||
EihtYULiAUKMopPwAIvDEABJyTgcNCPGKiLeJCNjTxbIWaAgIsEhnWPvhAXblQ4l
|
||||
KoMoiNiqHAXvVfZgBCKjx0nfD8tM2jUEUcDOW+UXtFcyrN0M9fMGdVgENKikA4pl
|
||||
sEudKEnG5jAkRHiXI1gMiBgsB3ci4gI5iPccgIYABynNYWJcM1bIsTSwcAi/trNc
|
||||
JQ/sJkSZcC4m0gYJ7B0ZEB6TVkgzz3prvBe8tzVv7XiCYQWcrq4C4rSwWcpMIpon
|
||||
sIf7FrEIVlKILxQnIMj/D7+MxK5HNqQXSbMK6EQeMbhHFTZrJBlUJRenAkapMUbq
|
||||
RyWZ9bLhJ6kiE8nL9/S/95PANv4dqEqm1wLSYEAcjUWSAn6yI3zBJafyRf3HAiGP
|
||||
SEnIrHbE40IYSqR1mEOvId7xe07h4xvoEx0eowhMnG62qJfV9V6qy1/4LB/ykz5G
|
||||
HyIoZOFNd90jtxQHErIvnUw+crf7TkR7+uXvTo3EF1TtnJoq72mEuYl96unSuZCI
|
||||
8zDj9Ppjm3unPFlP3bg9Xp7KbOskM5OuG/6ccl17V/vsiLt4Q8qXnujZjceO1Mwe
|
||||
3fHm1pOhqc6WU+uvDpS8Yu+mF/kPLEkqVuZuGdwY3DFdv51uSPg0Mj8kq93xbQfV
|
||||
8Jerv+tVpXbvocLG1LSKQ+1Lgt8W7dd+y94VUNZ6Tlml73um+pfhbfqBmR/O1nKj
|
||||
YQH7xjTllr7C1V2LfdTDdfFXnwbWE4teyEmfW5wUalwXPe23O/r7nJXvryrpWD4G
|
||||
n3j8p6HkweaalIuPOXO+2fTh5pSgioo97tKZccVeNTFX+YHxRubnEVHK30d2Hld0
|
||||
Dm1tWXewv35Prtv3eJNr7mNdUUZV650y99qm/ZdbHK9PehJXwpnJ0WXh8KP86pzc
|
||||
ULfTj7bF6iOjFNn7mtq+GA8vS9mdN7G6wDX86IraTav+CANKD3K+cdD/4X5Tz+TR
|
||||
puaCHyeH2p8nn7yotqTF5sc3xrV+0tOpGdgf1G1962bRr4eXj4vp9FiYx2rz+FWt
|
||||
Ma14ObL5q3Mp57dUO/NqPjszd/tKEGcLnTibfCu9ovGMUFLa67owU852LVXWD96d
|
||||
bLii2B7cdjhG8+KGn8vFAy+9N+tKczgzHa64wrxGd28XuzQ1avpEWu7Xa1NrL4Se
|
||||
TrDqRq//DQ==
|
||||
=yHWM
|
||||
-----END PGP MESSAGE-----
|
39
eth_monitor/callback.py
Normal file
39
eth_monitor/callback.py
Normal file
@ -0,0 +1,39 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BlockCallbackFilter:
|
||||
|
||||
def __init__(self):
|
||||
self.filters = []
|
||||
|
||||
|
||||
def register(self, fltr):
|
||||
self.filters.append(fltr)
|
||||
|
||||
|
||||
def filter(self, conn, block):
|
||||
for fltr in self.filters:
|
||||
fltr.filter(conn, block)
|
||||
|
||||
|
||||
def state_change_callback(k, old_state, new_state):
|
||||
logg.log(logging.STATETRACE, 'state change: {} {} -> {}'.format(k, old_state, new_state))
|
||||
|
||||
|
||||
def filter_change_callback(k, old_state, new_state):
|
||||
logg.log(logging.STATETRACE, 'filter change: {} {} -> {}'.format(k, old_state, new_state))
|
||||
|
||||
|
||||
def pre_callback(conn):
|
||||
logg.debug('starting sync loop iteration')
|
||||
|
||||
|
||||
def post_callback(conn):
|
||||
logg.debug('ending sync loop iteration')
|
||||
|
||||
|
||||
def block_callback(conn, block):
|
||||
logg.info('processing {} {}'.format(block, datetime.datetime.fromtimestamp(block.timestamp)))
|
@ -1,18 +1,26 @@
|
||||
# external imports
|
||||
from chainlib.interface import ChainInterface
|
||||
from chainlib.eth.block import (
|
||||
block_latest,
|
||||
block_by_number,
|
||||
Block,
|
||||
)
|
||||
from chainlib.eth.tx import (
|
||||
receipt,
|
||||
Tx,
|
||||
transaction,
|
||||
)
|
||||
|
||||
class EthChainInterface(ChainInterface):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, dialect_filter=None, batch_limit=1):
|
||||
super(EthChainInterface, self).__init__(dialect_filter=dialect_filter, batch_limit=batch_limit)
|
||||
self.batch_limit = batch_limit
|
||||
self._block_latest = block_latest
|
||||
self._block_by_number = block_by_number
|
||||
self._block_from_src = Block.from_src
|
||||
self._tx_from_src = Tx.from_src
|
||||
self._tx_receipt = receipt
|
||||
self._src_normalize = Tx.src_normalize
|
||||
self._dialect_filter = dialect_filter
|
||||
self._tx_by_hash = transaction
|
||||
|
2
eth_monitor/cli/__init__.py
Normal file
2
eth_monitor/cli/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .arg import process_args
|
||||
from .config import process_config
|
37
eth_monitor/cli/arg.py
Normal file
37
eth_monitor/cli/arg.py
Normal file
@ -0,0 +1,37 @@
|
||||
def process_args(argparser, args, flags):
|
||||
# session flags
|
||||
argparser.add_argument('--state-dir', dest='state_dir', type=str, help='Directory to store sync state')
|
||||
argparser.add_argument('--session-id', dest='session_id', type=str, help='Use state from specified session id')
|
||||
argparser.add_argument('--cache-dir', dest='cache_dir', type=str, help='Directory to store tx data')
|
||||
|
||||
# address rules flags
|
||||
argparser.add_argument('--input', action='append', type=str, help='Add input (recipient) addresses to includes list')
|
||||
argparser.add_argument('--output', action='append', type=str, help='Add output (sender) addresses to includes list')
|
||||
argparser.add_argument('--exec', action='append', type=str, help='Add exec (contract) addresses to includes list')
|
||||
argparser.add_argument('--data', action='append', type=str, help='Add data prefix strings to include list')
|
||||
argparser.add_argument('--data-in', action='append', dest='data_in', type=str, help='Add data contain strings to include list')
|
||||
argparser.add_argument('--x-data', action='append', dest='x_data', type=str, help='Add data prefix string to exclude list')
|
||||
argparser.add_argument('--x-data-in', action='append', dest='x_data_in', type=str, help='Add data contain string to exclude list')
|
||||
argparser.add_argument('--address', action='append', type=str, help='Add addresses as input, output and exec to includes list')
|
||||
argparser.add_argument('--x-input', action='append', type=str, dest='x_input', help='Add input (recipient) addresses to excludes list')
|
||||
argparser.add_argument('--x-output', action='append', type=str, dest='x_output', help='Add output (sender) addresses to excludes list')
|
||||
argparser.add_argument('--x-exec', action='append', type=str, dest='x_exec', help='Add exec (contract) addresses to excludes list')
|
||||
argparser.add_argument('--x-address', action='append', type=str, dest='x_address', help='Add addresses as input, output and exec to excludes list')
|
||||
argparser.add_argument('--includes-file', type=str, dest='includes_file', help='Load include rules from file')
|
||||
argparser.add_argument('--excludes-file', type=str, dest='excludes_file', help='Load exclude rules from file')
|
||||
argparser.add_argument('--include-default', dest='include_default', action='store_true', help='Include all transactions by default')
|
||||
|
||||
# filter flags
|
||||
argparser.add_argument('--renderer', type=str, action='append', default=[], help='Python modules to dynamically load for rendering of transaction output')
|
||||
argparser.add_argument('--filter', type=str, action='append', help='Add python module to tx filter path')
|
||||
argparser.add_argument('--block-filter', type=str, dest='block_filter', action='append', help='Add python module to block filter path')
|
||||
|
||||
# cache flags
|
||||
argparser.add_argument('--store-tx-data', action='store_true', dest='store_tx_data', help='Store tx data in cache store')
|
||||
argparser.add_argument('--store-block-data', action='store_true', dest='store_block_data', help='Store block data in cache store')
|
||||
argparser.add_argument('--fresh', action='store_true', help='Do not read block and tx data from cache, even if available')
|
||||
argparser.add_argument('--match-all', action='store_true', dest='match_all', help='Match all include filter criteria')
|
||||
|
||||
# misc flags
|
||||
argparser.add_argument('-k', '--context-key', dest='context_key', action='append', type=str, help='Add a key-value pair to be added to the context')
|
||||
argparser.add_argument('--run-dir', type=str, dest='run_dir', help='Output key sync and processing state properties to given diretory')
|
62
eth_monitor/cli/config.py
Normal file
62
eth_monitor/cli/config.py
Normal file
@ -0,0 +1,62 @@
|
||||
# local imports
|
||||
from .rules import (
|
||||
rules_address_args,
|
||||
rules_data_args,
|
||||
to_config_names,
|
||||
)
|
||||
|
||||
def process_config(config, arg, args, flags):
|
||||
arg_override = {}
|
||||
|
||||
rules_args = rules_address_args + rules_data_args
|
||||
|
||||
for rules_arg in rules_args:
|
||||
(vy, vn) = to_config_names(rules_arg)
|
||||
arg = getattr(args, rules_arg)
|
||||
if arg == None:
|
||||
v = config.get(vy)
|
||||
if bool(v):
|
||||
arg_override[vy] = v.split(',')
|
||||
else:
|
||||
arg_override[vy] = arg
|
||||
|
||||
arg = getattr(args, 'x_' + rules_arg)
|
||||
if arg == None:
|
||||
v = config.get(vn)
|
||||
if bool(v):
|
||||
arg_override[vn] = v.split(',')
|
||||
else:
|
||||
arg_override[vn] = arg
|
||||
|
||||
arg_override['ETHMONITOR_INCLUDES_FILE'] = getattr(args, 'includes_file')
|
||||
arg_override['ETHMONITOR_EXCLUDES_FILE'] = getattr(args, 'excludes_file')
|
||||
arg_override['ETHMONITOR_INCLUDE_DEFAULT'] = getattr(args, 'include_default')
|
||||
|
||||
arg_override['ETHMONITOR_RENDERER'] = getattr(args, 'renderer')
|
||||
arg_override['ETHMONITOR_FILTER'] = getattr(args, 'filter')
|
||||
arg_override['ETHMONITOR_BLOCK_FILTER'] = getattr(args, 'block_filter')
|
||||
|
||||
arg_override['ETHMONITOR_STATE_DIR'] = getattr(args, 'state_dir')
|
||||
|
||||
arg_override['ETHMONITOR_CONTEXT_KEY'] = getattr(args, 'context_key')
|
||||
|
||||
arg_override['ETHMONITOR_MATCH_ALL'] = getattr(args, 'match_all')
|
||||
|
||||
arg_override['ETHCACHE_STORE_BLOCK'] = getattr(args, 'store_block_data')
|
||||
arg_override['ETHCACHE_STORE_TX'] = getattr(args, 'store_tx_data')
|
||||
|
||||
config.dict_override(arg_override, 'local cli args')
|
||||
|
||||
for rules_arg in rules_args:
|
||||
(vy, vn) = to_config_names(rules_arg)
|
||||
if config.get(vy) == None:
|
||||
config.add([], vy, True)
|
||||
if config.get(vn) == None:
|
||||
config.add([], vn, True)
|
||||
|
||||
config.add(getattr(args, 'session_id'), '_SESSION_ID', False)
|
||||
config.add(getattr(args, 'cache_dir'), '_CACHE_DIR', False)
|
||||
config.add(getattr(args, 'run_dir'), '_RUN_DIR', False)
|
||||
config.add(getattr(args, 'fresh'), '_FRESH', False)
|
||||
|
||||
return config
|
21
eth_monitor/cli/log.py
Normal file
21
eth_monitor/cli/log.py
Normal file
@ -0,0 +1,21 @@
|
||||
# standard imports
|
||||
import logging
|
||||
|
||||
# external imports
|
||||
from chainlib.cli.log import process_log as base_process_log
|
||||
|
||||
logging.STATETRACE = 5
|
||||
|
||||
|
||||
def process_log(args, logger):
|
||||
if args.vvv:
|
||||
logger.setLevel(logging.STATETRACE)
|
||||
else:
|
||||
logger = base_process_log(args, logger)
|
||||
|
||||
logging.getLogger('chainlib.connection').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainlib.eth.tx').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainlib.eth.src').setLevel(logging.WARNING)
|
||||
|
||||
return logger
|
||||
|
16
eth_monitor/cli/rules.py
Normal file
16
eth_monitor/cli/rules.py
Normal file
@ -0,0 +1,16 @@
|
||||
rules_address_args = [
|
||||
'input',
|
||||
'output',
|
||||
'exec',
|
||||
'address',
|
||||
]
|
||||
|
||||
rules_data_args = [
|
||||
'data',
|
||||
'data_in',
|
||||
]
|
||||
|
||||
|
||||
def to_config_names(v):
|
||||
v = v.upper()
|
||||
return ('ETHMONITOR_' + v, 'ETHMONITOR_X_' + v)
|
3
eth_monitor/data/config/cache.ini
Normal file
3
eth_monitor/data/config/cache.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[ethcache]
|
||||
store_tx = 0
|
||||
store_block = 0
|
@ -1,2 +0,0 @@
|
||||
[chain]
|
||||
spec = evm:berlin:1:ethereum
|
22
eth_monitor/data/config/monitor.ini
Normal file
22
eth_monitor/data/config/monitor.ini
Normal file
@ -0,0 +1,22 @@
|
||||
[ethmonitor]
|
||||
input =
|
||||
output =
|
||||
exec =
|
||||
x_input =
|
||||
x_output =
|
||||
x_exec =
|
||||
address =
|
||||
x_address =
|
||||
data =
|
||||
x_data =
|
||||
data_in =
|
||||
x_data_in =
|
||||
includes_file =
|
||||
excludes_file =
|
||||
renderer =
|
||||
filter =
|
||||
block_filter =
|
||||
include_default = 0
|
||||
state_dir = ./.eth-monitor
|
||||
context_key =
|
||||
match_all = 0
|
@ -1,3 +0,0 @@
|
||||
[syncer]
|
||||
loop_interval = 5
|
||||
backend = fs
|
2
eth_monitor/error.py
Normal file
2
eth_monitor/error.py
Normal file
@ -0,0 +1,2 @@
|
||||
class RuleFail(Exception):
|
||||
pass
|
@ -12,26 +12,11 @@ logg = logging.getLogger(__name__)
|
||||
|
||||
class RuledFilter(SyncFilter):
|
||||
|
||||
def __init__(self, rules_filter=None):
|
||||
if self.store.chain_dir == None:
|
||||
raise RuntimeError('store must be initialized. call RuledFilter.init() first')
|
||||
def __init__(self, rules_filter=None, store=None):
|
||||
self.rules_filter = rules_filter
|
||||
|
||||
|
||||
@staticmethod
|
||||
def init(store, include_block_data=False, include_tx_data=False):
|
||||
RuledFilter.store = store
|
||||
RuledFilter.include_block_data = include_block_data
|
||||
RuledFilter.include_tx_data = include_tx_data
|
||||
|
||||
|
||||
@classmethod
|
||||
def block_callback(cls, block, extra=None):
|
||||
logg.info('processing {}'.format(block))
|
||||
cls.store.put_block(block, include_data=cls.include_block_data)
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
def filter(self, conn, block, tx, **kwargs):
|
||||
if self.rules_filter != None:
|
||||
if not self.rules_filter.apply_rules(tx):
|
||||
logg.debug('rule match failed for tx {}'.format(tx.hash))
|
||||
|
13
eth_monitor/filters/block.py
Normal file
13
eth_monitor/filters/block.py
Normal file
@ -0,0 +1,13 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
|
||||
class Filter:
|
||||
|
||||
def __init__(self, store, include_block_data=False):
|
||||
self.store = store
|
||||
self.include_block_data = include_block_data
|
||||
|
||||
|
||||
def filter(self, conn, block, **kwargs):
|
||||
self.store.put_block(block, include_data=self.include_block_data)
|
@ -9,14 +9,20 @@ logg = logging.getLogger(__name__)
|
||||
|
||||
class Filter(RuledFilter):
|
||||
|
||||
def ruled_filter(self, conn, block, tx, db_session=None):
|
||||
def __init__(self, store, rules_filter=None, include_tx_data=False):
|
||||
super(Filter, self).__init__(rules_filter=rules_filter)
|
||||
self.store = store
|
||||
self.include_tx_data = include_tx_data
|
||||
|
||||
|
||||
def ruled_filter(self, conn, block, tx, **kwargs):
|
||||
self.store.put_tx(tx, include_data=self.include_tx_data)
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
r = super(Filter, self).filter(conn, block, tx, db_session=db_session)
|
||||
def filter(self, conn, block, tx, **kwargs):
|
||||
r = super(Filter, self).filter(conn, block, tx, **kwargs)
|
||||
if r == True:
|
||||
return True
|
||||
|
||||
self.ruled_filter(conn, block, tx, db_session=db_session)
|
||||
self.ruled_filter(conn, block, tx, **kwargs)
|
||||
return False
|
||||
|
@ -16,7 +16,7 @@ logg = logging.getLogger(__name__)
|
||||
|
||||
# Interface defining the signature for renderer in OutFilter
|
||||
# return string after local transformation
|
||||
def apply_interface(c, s, chain_str, conn, block, tx, db_session=None):
|
||||
def apply_interface(c, s, chain_str, conn, block, tx, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
@ -49,8 +49,8 @@ class OutFilter(RuledFilter):
|
||||
self.result = OutResult()
|
||||
|
||||
|
||||
def filter(self, conn, block, tx, db_session=None):
|
||||
r = super(OutFilter, self).filter(conn, block, tx, db_session=db_session)
|
||||
def filter(self, conn, block, tx, **kwargs):
|
||||
r = super(OutFilter, self).filter(conn, block, tx, **kwargs)
|
||||
if r == True:
|
||||
return True
|
||||
|
||||
@ -74,7 +74,7 @@ class OutFilter(RuledFilter):
|
||||
datetime.datetime.fromtimestamp(block.timestamp),
|
||||
block.number,
|
||||
strip_0x(block.hash),
|
||||
tx.index,
|
||||
tx.index + 1,
|
||||
tx_count,
|
||||
strip_0x(tx.hash),
|
||||
tx.status.name,
|
||||
|
16
eth_monitor/filters/run.py
Normal file
16
eth_monitor/filters/run.py
Normal file
@ -0,0 +1,16 @@
|
||||
# standard imports
|
||||
import os
|
||||
|
||||
|
||||
class Filter:
|
||||
|
||||
def __init__(self, run_dir):
|
||||
self.run_dir = run_dir
|
||||
self.fp = os.path.join(run_dir, 'block')
|
||||
|
||||
|
||||
def filter(self, conn, block):
|
||||
f = open(self.fp, 'w')
|
||||
f.write(str(block.number))
|
||||
f.close()
|
||||
return False
|
@ -35,7 +35,7 @@ class Importer:
|
||||
block = Block(r)
|
||||
|
||||
if self.block_callback != None:
|
||||
self.block_callback(block)
|
||||
self.block_callback(None, block)
|
||||
|
||||
tx_src = block.txs[int(v['transactionIndex'])]
|
||||
|
||||
|
@ -4,6 +4,7 @@ import uuid
|
||||
|
||||
# external imports
|
||||
from chainlib.eth.address import is_same_address
|
||||
from .error import RuleFail
|
||||
|
||||
logg = logging.getLogger()
|
||||
|
||||
@ -11,14 +12,17 @@ logg = logging.getLogger()
|
||||
|
||||
class RuleData:
|
||||
|
||||
def __init__(self, fragments, description=None):
|
||||
def __init__(self, fragments, description=None, match_all=False):
|
||||
self.fragments = fragments
|
||||
self.description = description
|
||||
if self.description == None:
|
||||
self.description = str(uuid.uuid4())
|
||||
self.match_all = match_all
|
||||
|
||||
|
||||
def check(self, sender, recipient, data, tx_hash):
|
||||
have_fail = False
|
||||
have_match = False
|
||||
if len(self.fragments) == 0:
|
||||
return False
|
||||
|
||||
@ -28,9 +32,16 @@ class RuleData:
|
||||
continue
|
||||
if fragment in data:
|
||||
logg.debug('tx {} rule {} match in DATA FRAGMENT {}'.format(tx_hash, self.description, fragment))
|
||||
return True
|
||||
if not self.match_all:
|
||||
return True
|
||||
have_match = True
|
||||
else:
|
||||
logg.debug('data match all {}'.format(self.match_all))
|
||||
if self.match_all:
|
||||
return False
|
||||
have_fail = True
|
||||
|
||||
return False
|
||||
return have_match
|
||||
|
||||
|
||||
def __str__(self):
|
||||
@ -41,11 +52,13 @@ class RuleData:
|
||||
|
||||
class RuleMethod:
|
||||
|
||||
def __init__(self, methods, description=None):
|
||||
def __init__(self, methods, description=None, match_all=False):
|
||||
self.methods = methods
|
||||
self.description = description
|
||||
if self.description == None:
|
||||
self.description = str(uuid.uuid4())
|
||||
if match_all:
|
||||
logg.warning('match_all ignord for RuleMethod rule')
|
||||
|
||||
|
||||
def check(self, sender, recipient, data, tx_hash):
|
||||
@ -71,28 +84,62 @@ class RuleMethod:
|
||||
|
||||
class RuleSimple:
|
||||
|
||||
def __init__(self, outputs, inputs, executables, description=None):
|
||||
def __init__(self, outputs, inputs, executables, description=None, match_all=False):
|
||||
self.description = description
|
||||
if self.description == None:
|
||||
self.description = str(uuid.uuid4())
|
||||
self.outputs = outputs
|
||||
self.inputs = inputs
|
||||
self.executables = executables
|
||||
self.match_all = match_all
|
||||
|
||||
|
||||
|
||||
def check(self, sender, recipient, data, tx_hash):
|
||||
r = None
|
||||
try:
|
||||
r = self.__check(sender, recipient, data, tx_hash)
|
||||
except RuleFail:
|
||||
return False
|
||||
return r
|
||||
|
||||
|
||||
def __check(self, sender, recipient, data, tx_hash):
|
||||
have_fail = False
|
||||
have_match = False
|
||||
for rule in self.outputs:
|
||||
if rule != None and is_same_address(sender, rule):
|
||||
logg.debug('tx {} rule {} match in SENDER {}'.format(tx_hash, self.description, sender))
|
||||
return True
|
||||
if not self.match_all:
|
||||
return True
|
||||
have_match = True
|
||||
else:
|
||||
if self.match_all:
|
||||
raise RuleFail(rule)
|
||||
have_fail = True
|
||||
if recipient == None:
|
||||
return False
|
||||
for rule in self.inputs:
|
||||
if rule != None and is_same_address(recipient, rule):
|
||||
logg.debug('tx {} rule {} match in RECIPIENT {}'.format(tx_hash, self.description, recipient))
|
||||
return True
|
||||
if not self.match_all:
|
||||
return True
|
||||
have_match = True
|
||||
else:
|
||||
if self.match_all:
|
||||
raise RuleFail(rule)
|
||||
have_fail = True
|
||||
for rule in self.executables:
|
||||
if rule != None and is_same_address(recipient, rule):
|
||||
logg.debug('tx {} rule {} match in EXECUTABLE {}'.format(tx_hash, self.description, recipient))
|
||||
return True
|
||||
if not self.match_all:
|
||||
return True
|
||||
have_match = True
|
||||
else:
|
||||
if self.match_all:
|
||||
raise RuleFail(rule)
|
||||
have_fail = True
|
||||
|
||||
return have_match
|
||||
|
||||
|
||||
def __str__(self):
|
||||
@ -105,10 +152,11 @@ class RuleSimple:
|
||||
|
||||
class AddressRules:
|
||||
|
||||
def __init__(self, include_by_default=False):
|
||||
def __init__(self, include_by_default=False, match_all=False):
|
||||
self.excludes = []
|
||||
self.includes = []
|
||||
self.include_by_default = include_by_default
|
||||
self.match_all = match_all
|
||||
|
||||
|
||||
def exclude(self, rule):
|
||||
@ -125,20 +173,29 @@ class AddressRules:
|
||||
return self.apply_rules_addresses(tx.outputs[0], tx.inputs[0], tx.payload, tx.hash)
|
||||
|
||||
|
||||
# TODO: rename
|
||||
def apply_rules_addresses(self, sender, recipient, data, tx_hash):
|
||||
v = self.include_by_default
|
||||
have_fail = False
|
||||
have_match = False
|
||||
|
||||
for rule in self.includes:
|
||||
if rule.check(sender, recipient, data, tx_hash):
|
||||
v = True
|
||||
logg.info('match in includes rule: {}'.format(rule))
|
||||
if not self.match_all:
|
||||
break
|
||||
elif self.match_all:
|
||||
v = False
|
||||
break
|
||||
|
||||
if not v:
|
||||
return v
|
||||
|
||||
for rule in self.excludes:
|
||||
if rule.check(sender, recipient, data, tx_hash):
|
||||
v = False
|
||||
logg.info('match in excludes rule: {}'.format(rule))
|
||||
break
|
||||
|
||||
if not self.match_all:
|
||||
break
|
||||
|
||||
return v
|
||||
|
17
eth_monitor/run.py
Normal file
17
eth_monitor/run.py
Normal file
@ -0,0 +1,17 @@
|
||||
# standard imports
|
||||
import os
|
||||
import logging
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def cleanup_run(settings):
|
||||
if not settings.get('RUN_OUT'):
|
||||
return
|
||||
lockfile = os.path.join(settings.get('RUN_DIR'), '.lock')
|
||||
os.unlink(lockfile)
|
||||
logg.debug('freed rundir {}'.format(settings.get('RUN_DIR')))
|
||||
|
||||
|
||||
def cleanup(settings):
|
||||
cleanup_run(settings)
|
@ -10,6 +10,8 @@ import uuid
|
||||
import datetime
|
||||
|
||||
# external imports
|
||||
import chainlib.cli
|
||||
import chainsyncer.cli
|
||||
from chainlib.chain import ChainSpec
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainlib.eth.block import block_latest
|
||||
@ -17,416 +19,100 @@ from hexathon import (
|
||||
strip_0x,
|
||||
add_0x,
|
||||
)
|
||||
#from chainsyncer.store.fs import SyncFsStore
|
||||
from chainsyncer.cli.arg import (
|
||||
apply_arg as apply_arg_sync,
|
||||
apply_flag as apply_flag_sync,
|
||||
)
|
||||
from chainsyncer.cli.config import process_config as process_config_sync
|
||||
from chainsyncer.driver.chain_interface import ChainInterfaceDriver
|
||||
from chainsyncer.error import SyncDone
|
||||
|
||||
from eth_cache.rpc import CacheRPC
|
||||
from eth_cache.store.file import FileStore
|
||||
from chainsyncer.data import config_dir as chainsyncer_config_dir
|
||||
from chainlib.settings import ChainSettings
|
||||
from chainlib.eth.settings import process_settings
|
||||
from chainlib.eth.cli.arg import (
|
||||
Arg,
|
||||
ArgFlag,
|
||||
process_args,
|
||||
)
|
||||
from chainlib.eth.cli.config import (
|
||||
Config,
|
||||
process_config,
|
||||
)
|
||||
|
||||
# local imports
|
||||
from eth_monitor.chain import EthChainInterface
|
||||
from eth_monitor.filters.cache import Filter as CacheFilter
|
||||
from eth_monitor.rules import (
|
||||
AddressRules,
|
||||
RuleSimple,
|
||||
RuleMethod,
|
||||
RuleData,
|
||||
from eth_monitor.callback import (
|
||||
pre_callback,
|
||||
post_callback,
|
||||
)
|
||||
from eth_monitor.filters import RuledFilter
|
||||
from eth_monitor.filters.out import OutFilter
|
||||
from eth_monitor.config import override, list_from_prefix
|
||||
import eth_monitor.cli
|
||||
from eth_monitor.cli.log import process_log
|
||||
from eth_monitor.settings import process_settings as process_settings_local
|
||||
from eth_monitor.run import cleanup
|
||||
|
||||
logging.STATETRACE = 5
|
||||
logging.basicConfig(level=logging.WARNING)
|
||||
logg = logging.getLogger()
|
||||
|
||||
default_eth_provider = os.environ.get('RPC_PROVIDER')
|
||||
if default_eth_provider == None:
|
||||
default_eth_provider = os.environ.get('ETH_PROVIDER', 'http://localhost:8545')
|
||||
|
||||
script_dir = os.path.realpath(os.path.dirname(__file__))
|
||||
exec_dir = os.path.realpath(os.getcwd())
|
||||
#default_config_dir = os.environ.get('CONFINI_DIR', os.path.join(exec_dir, 'config'))
|
||||
base_config_dir = os.path.join(script_dir, '..', 'data', 'config')
|
||||
config_dir = os.path.join(script_dir, '..', 'data', 'config')
|
||||
|
||||
argparser = argparse.ArgumentParser('master eth events monitor')
|
||||
argparser.add_argument('-p', '--provider', dest='p', default=default_eth_provider, type=str, help='Web3 provider url (http only)')
|
||||
argparser.add_argument('-c', type=str, help='config file')
|
||||
argparser.add_argument('-i', '--chain-spec', dest='i', type=str, help='Chain specification string')
|
||||
argparser.add_argument('--offset', type=int, default=0, help='Start sync on this block')
|
||||
argparser.add_argument('--until', type=int, default=0, help='Terminate sync on this block')
|
||||
argparser.add_argument('--head', action='store_true', help='Start at current block height (overrides --offset, assumes --keep-alive)')
|
||||
argparser.add_argument('--seq', action='store_true', help='Use sequential rpc ids')
|
||||
argparser.add_argument('--skip-history', action='store_true', dest='skip_history', help='Skip history sync')
|
||||
argparser.add_argument('--keep-alive', action='store_true', dest='keep_alive', help='Continue to sync head after history sync complete')
|
||||
argparser.add_argument('--input', default=[], action='append', type=str, help='Add input (recipient) addresses to includes list')
|
||||
argparser.add_argument('--output', default=[], action='append', type=str, help='Add output (sender) addresses to includes list')
|
||||
argparser.add_argument('--exec', default=[], action='append', type=str, help='Add exec (contract) addresses to includes list')
|
||||
argparser.add_argument('--data', default=[], action='append', type=str, help='Add data prefix strings to include list')
|
||||
argparser.add_argument('--data-in', default=[], action='append', dest='data_in', type=str, help='Add data contain strings to include list')
|
||||
argparser.add_argument('--x-data', default=[], action='append', dest='xdata', type=str, help='Add data prefix string to exclude list')
|
||||
argparser.add_argument('--x-data-in', default=[], action='append', dest='xdata_in', type=str, help='Add data contain string to exclude list')
|
||||
argparser.add_argument('--address', default=[], action='append', type=str, help='Add addresses as input, output and exec to includes list')
|
||||
argparser.add_argument('--x-input', default=[], action='append', type=str, dest='xinput', help='Add input (recipient) addresses to excludes list')
|
||||
argparser.add_argument('--x-output', default=[], action='append', type=str, dest='xoutput', help='Add output (sender) addresses to excludes list')
|
||||
argparser.add_argument('--x-exec', default=[], action='append', type=str, dest='xexec', help='Add exec (contract) addresses to excludes list')
|
||||
argparser.add_argument('--x-address', default=[], action='append', type=str, dest='xaddress', help='Add addresses as input, output and exec to excludes list')
|
||||
argparser.add_argument('--includes-file', type=str, dest='includes_file', help='Load include rules from file')
|
||||
argparser.add_argument('--include-default', dest='include_default', action='store_true', help='Include all transactions by default')
|
||||
argparser.add_argument('--store-tx-data', dest='store_tx_data', action='store_true', help='Include all transaction data objects by default')
|
||||
argparser.add_argument('--store-block-data', dest='store_block_data', action='store_true', help='Include all block data objects by default')
|
||||
argparser.add_argument('--address-file', type=str, dest='excludes_file', help='Load exclude rules from file')
|
||||
argparser.add_argument('--renderer', type=str, action='append', default=[], help='Python modules to dynamically load for rendering of transaction output')
|
||||
argparser.add_argument('--filter', type=str, action='append', help='Add python module filter path')
|
||||
argparser.add_argument('--cache-dir', dest='cache_dir', type=str, help='Directory to store tx data')
|
||||
argparser.add_argument('--state-dir', dest='state_dir', default=exec_dir, type=str, help='Directory to store sync state')
|
||||
argparser.add_argument('--fresh', action='store_true', help='Do not read block and tx data from cache, even if available')
|
||||
argparser.add_argument('--single', action='store_true', help='Execute a single sync, regardless of previous states')
|
||||
argparser.add_argument('--session-id', dest='session_id', type=str, help='Use state from specified session id')
|
||||
argparser.add_argument('--backend', type=str, help='State store backend')
|
||||
arg_flags = ArgFlag()
|
||||
arg_flags = apply_flag_sync(arg_flags)
|
||||
|
||||
arg = Arg(arg_flags)
|
||||
arg = apply_arg_sync(arg)
|
||||
|
||||
|
||||
flags = arg_flags.STD_BASE_READ | arg_flags.PROVIDER | arg_flags.CHAIN_SPEC | arg_flags.VERYVERBOSE | arg_flags.SYNC_RANGE_EXT | arg_flags.STATE
|
||||
argparser = chainlib.eth.cli.ArgumentParser()
|
||||
argparser = process_args(argparser, arg, flags)
|
||||
argparser.add_argument('--list-backends', dest='list_backends', action='store_true', help='List built-in store backends')
|
||||
argparser.add_argument('-v', action='store_true', help='Be verbose')
|
||||
argparser.add_argument('-vv', action='store_true', help='Be more verbose')
|
||||
argparser.add_argument('-vvv', action='store_true', help='Be incredibly verbose')
|
||||
eth_monitor.cli.process_args(argparser, arg, flags)
|
||||
|
||||
args = argparser.parse_args(sys.argv[1:])
|
||||
|
||||
if args.list_backends:
|
||||
for v in [
|
||||
'fs',
|
||||
'rocksdb',
|
||||
'mem',
|
||||
]:
|
||||
print(v)
|
||||
sys.exit(0)
|
||||
|
||||
if args.vvv:
|
||||
logg.setLevel(logging.STATETRACE)
|
||||
else:
|
||||
logging.getLogger('chainlib.connection').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainlib.eth.tx').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainsyncer.driver.history').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainsyncer.driver.head').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainsyncer.backend.file').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainsyncer.backend.sql').setLevel(logging.WARNING)
|
||||
logging.getLogger('chainsyncer.filter').setLevel(logging.WARNING)
|
||||
logg = process_log(args, logg)
|
||||
|
||||
if args.vv:
|
||||
logg.setLevel(logging.DEBUG)
|
||||
elif args.v:
|
||||
logg.setLevel(logging.INFO)
|
||||
|
||||
config_dir = args.c
|
||||
config = confini.Config(base_config_dir, os.environ.get('CONFINI_ENV_PREFIX'), override_dirs=args.c)
|
||||
config.process()
|
||||
args_override = {
|
||||
'CHAIN_SPEC': getattr(args, 'i'),
|
||||
'SYNCER_BACKEND': getattr(args, 'backend'),
|
||||
}
|
||||
config.dict_override(args_override, 'cli')
|
||||
config.add(args.offset, '_SYNC_OFFSET', True)
|
||||
config.add(args.skip_history, '_NO_HISTORY', True)
|
||||
config.add(args.single, '_SINGLE', True)
|
||||
config.add(args.head, '_HEAD', True)
|
||||
config.add(args.keep_alive, '_KEEP_ALIVE', True)
|
||||
config.add(os.path.realpath(args.state_dir), '_STATE_DIR', True)
|
||||
config.add(args.cache_dir, '_CACHE_DIR', True)
|
||||
config.add(args.session_id, '_SESSION_ID', True)
|
||||
override(config, 'renderer', env=os.environ, args=args)
|
||||
override(config, 'filter', env=os.environ, args=args)
|
||||
|
||||
if config.get('_SESSION_ID') == None:
|
||||
if config.get('_SINGLE'):
|
||||
config.add(str(uuid.uuid4()), '_SESSION_ID', True)
|
||||
else:
|
||||
config.add('default', '_SESSION_ID', True)
|
||||
config = Config()
|
||||
config.add_schema_dir(config_dir)
|
||||
config.add_schema_dir(chainsyncer_config_dir)
|
||||
config = process_config(config, arg, args, flags)
|
||||
config = process_config_sync(config, arg, args, flags)
|
||||
config = eth_monitor.cli.process_config(config, arg, args, flags)
|
||||
logg.debug('loaded config:\n{}'.format(config))
|
||||
|
||||
chain_spec = ChainSpec.from_chain_str(args.i)
|
||||
|
||||
rpc_id_generator = None
|
||||
if args.seq:
|
||||
rpc_id_generator = IntSequenceGenerator()
|
||||
|
||||
auth = None
|
||||
if os.environ.get('RPC_AUTHENTICATION') == 'basic':
|
||||
from chainlib.auth import BasicAuth
|
||||
auth = BasicAuth(os.environ['RPC_USERNAME'], os.environ['RPC_PASSWORD'])
|
||||
rpc = EthHTTPConnection(args.p)
|
||||
|
||||
|
||||
def setup_address_arg_rules(rules, args):
|
||||
include_inputs = args.input
|
||||
include_outputs = args.output
|
||||
include_exec = args.exec
|
||||
exclude_inputs = args.xinput
|
||||
exclude_outputs = args.xoutput
|
||||
exclude_exec = args.xexec
|
||||
|
||||
for address in args.address:
|
||||
include_inputs.append(address)
|
||||
include_outputs.append(address)
|
||||
include_exec.append(address)
|
||||
|
||||
for address in args.xaddress:
|
||||
exclude_inputs.append(address)
|
||||
exclude_outputs.append(address)
|
||||
exclude_exec.append(address)
|
||||
|
||||
includes = RuleSimple(include_outputs, include_inputs, include_exec, description='INCLUDE')
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleSimple(exclude_outputs, exclude_inputs, exclude_exec, description='EXCLUDE')
|
||||
rules.exclude(excludes)
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
def setup_data_arg_rules(rules, args):
|
||||
include_data = []
|
||||
for v in args.data:
|
||||
include_data.append(v.lower())
|
||||
exclude_data = []
|
||||
for v in args.xdata:
|
||||
exclude_data.append(v.lower())
|
||||
|
||||
includes = RuleMethod(include_data, description='INCLUDE')
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleMethod(exclude_data, description='EXCLUDE')
|
||||
rules.exclude(excludes)
|
||||
|
||||
include_data = []
|
||||
for v in args.data_in:
|
||||
include_data.append(v.lower())
|
||||
exclude_data = []
|
||||
for v in args.xdata_in:
|
||||
exclude_data.append(v.lower())
|
||||
|
||||
includes = RuleData(include_data, description='INCLUDE')
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleData(exclude_data, description='EXCLUDE')
|
||||
rules.exclude(excludes)
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
def setup_address_file_rules(rules, includes_file=None, excludes_file=None, include_default=False, include_block_default=False):
|
||||
|
||||
if includes_file != None:
|
||||
f = open(includes_file, 'r')
|
||||
logg.debug('reading includes rules from {}'.format(os.path.realpath(includes_file)))
|
||||
while True:
|
||||
r = f.readline()
|
||||
if r == '':
|
||||
break
|
||||
r = r.rstrip()
|
||||
v = r.split("\t")
|
||||
|
||||
sender = []
|
||||
recipient = []
|
||||
executable = []
|
||||
|
||||
try:
|
||||
if v[0] != '':
|
||||
sender = v[0].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if v[1] != '':
|
||||
recipient = v[1].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if v[2] != '':
|
||||
executable = v[2].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
rule = RuleSimple(sender, recipient, executable)
|
||||
rules.include(rule)
|
||||
|
||||
if excludes_file != None:
|
||||
f = open(includes_file, 'r')
|
||||
logg.debug('reading excludes rules from {}'.format(os.path.realpath(excludes_file)))
|
||||
while True:
|
||||
r = f.readline()
|
||||
if r == '':
|
||||
break
|
||||
r = r.rstrip()
|
||||
v = r.split("\t")
|
||||
|
||||
sender = None
|
||||
recipient = None
|
||||
executable = None
|
||||
|
||||
if v[0] != '':
|
||||
sender = v[0].strip(',')
|
||||
if v[1] != '':
|
||||
recipient = v[1].strip(',')
|
||||
if v[2] != '':
|
||||
executable = v[2].strip(',')
|
||||
|
||||
rule = RuleSimple(sender, recipient, executable)
|
||||
rules.exclude(rule)
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
def setup_filter(chain_spec, cache_dir, include_tx_data, include_block_data):
|
||||
store = None
|
||||
if cache_dir == None:
|
||||
logg.warning('no cache dir specified, will discard everything!!')
|
||||
from eth_cache.store.null import NullStore
|
||||
store = NullStore()
|
||||
else:
|
||||
store = FileStore(chain_spec, cache_dir)
|
||||
cache_dir = os.path.realpath(cache_dir)
|
||||
if cache_dir == None:
|
||||
import tempfile
|
||||
cache_dir = tempfile.mkdtemp()
|
||||
logg.info('using chain spec {} and store {}'.format(chain_spec, store))
|
||||
RuledFilter.init(store, include_tx_data=include_tx_data, include_block_data=include_block_data)
|
||||
|
||||
return store
|
||||
|
||||
|
||||
def setup_cache_filter(rules_filter=None):
|
||||
return CacheFilter(rules_filter=rules_filter)
|
||||
|
||||
|
||||
def pre_callback():
|
||||
logg.debug('starting sync loop iteration')
|
||||
|
||||
|
||||
def post_callback():
|
||||
logg.debug('ending sync loop iteration')
|
||||
|
||||
|
||||
def block_callback(block, tx):
|
||||
logg.info('processing {} {}'.format(block, datetime.datetime.fromtimestamp(block.timestamp)))
|
||||
|
||||
|
||||
def state_change_callback(k, old_state, new_state):
|
||||
logg.log(logging.STATETRACE, 'state change: {} {} -> {}'.format(k, old_state, new_state))
|
||||
|
||||
|
||||
def filter_change_callback(k, old_state, new_state):
|
||||
logg.log(logging.STATETRACE, 'filter change: {} {} -> {}'.format(k, old_state, new_state))
|
||||
settings = ChainSettings()
|
||||
settings = process_settings(settings, config)
|
||||
settings = process_settings_local(settings, config)
|
||||
logg.debug('loaded settings:\n{}'.format(settings))
|
||||
|
||||
|
||||
def main():
|
||||
o = block_latest()
|
||||
r = rpc.do(o)
|
||||
block_offset = int(strip_0x(r), 16) + 1
|
||||
logg.info('network block height is {}'.format(block_offset))
|
||||
logg.info('session is {}'.format(settings.get('SESSION_ID')))
|
||||
|
||||
keep_alive = False
|
||||
session_block_offset = 0
|
||||
block_limit = 0
|
||||
if args.head:
|
||||
session_block_offset = block_offset
|
||||
block_limit = -1
|
||||
keep_alive = True
|
||||
else:
|
||||
session_block_offset = args.offset
|
||||
|
||||
if args.until > 0:
|
||||
if not args.head and args.until <= session_block_offset:
|
||||
raise ValueError('sync termination block number must be later than offset ({} >= {})'.format(session_block_offset, args.until))
|
||||
block_limit = args.until
|
||||
elif config.true('_KEEP_ALIVE'):
|
||||
keep_alive=True
|
||||
block_limit = -1
|
||||
|
||||
if session_block_offset == -1:
|
||||
session_block_offset = block_offset
|
||||
elif not config.true('_KEEP_ALIVE'):
|
||||
if block_limit == 0:
|
||||
block_limit = block_offset
|
||||
|
||||
address_rules = AddressRules(include_by_default=args.include_default)
|
||||
address_rules = setup_data_arg_rules(
|
||||
address_rules,
|
||||
args,
|
||||
drv = ChainInterfaceDriver(
|
||||
settings.get('SYNC_STORE'),
|
||||
settings.get('SYNCER_INTERFACE'),
|
||||
offset=settings.get('SYNCER_OFFSET'),
|
||||
target=settings.get('SYNCER_LIMIT'),
|
||||
pre_callback=pre_callback,
|
||||
post_callback=post_callback,
|
||||
block_callback=settings.get('BLOCK_HANDLER').filter,
|
||||
)
|
||||
address_rules = setup_address_file_rules(
|
||||
address_rules,
|
||||
includes_file=args.includes_file,
|
||||
excludes_file=args.excludes_file,
|
||||
)
|
||||
address_rules = setup_address_arg_rules(
|
||||
address_rules,
|
||||
args,
|
||||
)
|
||||
|
||||
store = setup_filter(
|
||||
chain_spec,
|
||||
config.get('_CACHE_DIR'),
|
||||
bool(args.store_tx_data),
|
||||
bool(args.store_block_data),
|
||||
)
|
||||
|
||||
cache_filter = setup_cache_filter(
|
||||
rules_filter=address_rules,
|
||||
)
|
||||
|
||||
filters = [
|
||||
cache_filter,
|
||||
]
|
||||
|
||||
for fltr in list_from_prefix(config, 'filter'):
|
||||
m = importlib.import_module(fltr)
|
||||
fltr_object = m.Filter(rules_filter=address_rules)
|
||||
filters.append(fltr_object)
|
||||
logg.info('using filter module {}'.format(fltr))
|
||||
|
||||
renderers_mods = []
|
||||
for renderer in list_from_prefix(config, 'renderer'):
|
||||
m = importlib.import_module(renderer)
|
||||
renderers_mods.append(m)
|
||||
logg.info('using renderer module {}'.format(renderer))
|
||||
|
||||
chain_interface = EthChainInterface()
|
||||
|
||||
out_filter = OutFilter(chain_spec, rules_filter=address_rules, renderers=renderers_mods)
|
||||
filters.append(out_filter)
|
||||
|
||||
syncer_store_module = None
|
||||
syncer_store_class = None
|
||||
if config.get('SYNCER_BACKEND') == 'fs':
|
||||
syncer_store_module = importlib.import_module('chainsyncer.store.fs')
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncFsStore')
|
||||
elif config.get('SYNCER_BACKEND') == 'rocksdb':
|
||||
syncer_store_module = importlib.import_module('chainsyncer.store.rocksdb')
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncRocksDbStore')
|
||||
else:
|
||||
syncer_store_module = importlib.import_module(config.get('SYNCER_BACKEND'))
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncStore')
|
||||
|
||||
logg.info('using engine {} module {}.{}'.format(config.get('SYNCER_BACKEND'), syncer_store_module.__file__, syncer_store_class.__name__))
|
||||
|
||||
state_dir = os.path.join(config.get('_STATE_DIR'), config.get('SYNCER_BACKEND'))
|
||||
sync_store = syncer_store_class(state_dir, session_id=config.get('_SESSION_ID'), state_event_callback=state_change_callback, filter_state_event_callback=filter_change_callback)
|
||||
logg.info('session is {}'.format(sync_store.session_id))
|
||||
|
||||
for fltr in filters:
|
||||
sync_store.register(fltr)
|
||||
drv = ChainInterfaceDriver(sync_store, chain_interface, offset=session_block_offset, target=block_limit, pre_callback=pre_callback, post_callback=post_callback, block_callback=block_callback)
|
||||
|
||||
use_rpc = rpc
|
||||
if not args.fresh:
|
||||
use_rpc = CacheRPC(rpc, store)
|
||||
|
||||
i = 0
|
||||
try:
|
||||
r = drv.run(use_rpc)
|
||||
r = drv.run(settings.get('CONN'), ctx=settings.get('SYNCER_CONTEXT'))
|
||||
except SyncDone as e:
|
||||
sys.stderr.write("sync {} done at block {}\n".format(drv, e))
|
||||
|
||||
i += 1
|
||||
cleanup(settings)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
438
eth_monitor/settings.py
Normal file
438
eth_monitor/settings.py
Normal file
@ -0,0 +1,438 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
import importlib
|
||||
import tempfile
|
||||
|
||||
# external imports
|
||||
from chainlib.settings import ChainSettings
|
||||
from chainlib.eth.connection import EthHTTPConnection
|
||||
from chainsyncer.settings import *
|
||||
from eth_monitor.chain import EthChainInterface
|
||||
from chainlib.eth.address import is_address
|
||||
from eth_cache.rpc import CacheRPC
|
||||
from eth_cache.store.file import FileStore
|
||||
from chainsyncer.settings import process_sync_range
|
||||
|
||||
|
||||
# local imports
|
||||
from eth_monitor.rules import (
|
||||
AddressRules,
|
||||
RuleSimple,
|
||||
RuleMethod,
|
||||
RuleData,
|
||||
)
|
||||
from eth_monitor.cli.rules import to_config_names
|
||||
from eth_monitor.callback import (
|
||||
state_change_callback,
|
||||
filter_change_callback,
|
||||
BlockCallbackFilter,
|
||||
)
|
||||
from eth_monitor.filters import RuledFilter
|
||||
from eth_monitor.filters.cache import Filter as CacheFilter
|
||||
from eth_monitor.config import override, list_from_prefix
|
||||
from eth_monitor.filters.out import OutFilter
|
||||
from eth_monitor.filters.block import Filter as BlockFilter
|
||||
from eth_monitor.filters.run import Filter as RunFilter
|
||||
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def process_monitor_session(settings, config):
|
||||
session_id = config.get('_SESSION_ID')
|
||||
if session_id == None:
|
||||
if config.get('_SINGLE'):
|
||||
session_id = str(uuid.uuid4())
|
||||
else:
|
||||
session_id = 'default'
|
||||
|
||||
settings.set('SESSION_ID', session_id)
|
||||
settings.set('SESSION_OK', True)
|
||||
return settings
|
||||
|
||||
|
||||
def process_monitor_rundir(settings, config):
|
||||
settings.set('RUN_OUT', False)
|
||||
if config.get('_RUN_DIR') == None:
|
||||
return settings
|
||||
|
||||
run_dir = config.get('_RUN_DIR')
|
||||
try:
|
||||
os.makedirs(run_dir, exist_ok=True)
|
||||
except Exception as e:
|
||||
logg.error('could not create run dir, deactivating run output: ' + str(e))
|
||||
return settings
|
||||
|
||||
lockfile = os.path.join(run_dir, '.lock')
|
||||
try:
|
||||
f = open(lockfile, 'x')
|
||||
f.close()
|
||||
except FileExistsError:
|
||||
logg.error('run dir {} is already in use, deactivating run output'.format(run_dir))
|
||||
return settings
|
||||
|
||||
settings.set('RUN_OUT', True)
|
||||
settings.set('RUN_DIR', run_dir)
|
||||
return settings
|
||||
|
||||
|
||||
def process_monitor_session_dir(settings, config):
|
||||
syncer_store_module = None
|
||||
syncer_store_class = None
|
||||
sync_store = None
|
||||
session_id = settings.get('SESSION_ID')
|
||||
state_dir = None
|
||||
if config.get('SYNCER_BACKEND') == 'mem':
|
||||
syncer_store_module = importlib.import_module('chainsyncer.store.mem')
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncMemStore')
|
||||
sync_store = syncer_store_class(
|
||||
session_id=session_id,
|
||||
state_event_callback=state_change_callback,
|
||||
filter_state_event_callback=filter_change_callback,
|
||||
)
|
||||
|
||||
else:
|
||||
if config.get('SYNCER_BACKEND') == 'fs':
|
||||
syncer_store_module = importlib.import_module('chainsyncer.store.fs')
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncFsStore')
|
||||
elif config.get('SYNCER_BACKEND') == 'rocksdb':
|
||||
syncer_store_module = importlib.import_module('chainsyncer.store.rocksdb')
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncRocksDbStore')
|
||||
else:
|
||||
syncer_store_module = importlib.import_module(config.get('SYNCER_BACKEND'))
|
||||
syncer_store_class = getattr(syncer_store_module, 'SyncStore')
|
||||
state_dir = os.path.join(config.get('ETHMONITOR_STATE_DIR'), config.get('SYNCER_BACKEND'))
|
||||
os.makedirs(state_dir, exist_ok=True)
|
||||
session_dir = os.path.join(state_dir, session_id)
|
||||
sync_store = syncer_store_class(
|
||||
session_dir,
|
||||
session_id=session_id,
|
||||
state_event_callback=state_change_callback,
|
||||
filter_state_event_callback=filter_change_callback,
|
||||
)
|
||||
settings.set('SESSION_DIR', session_dir)
|
||||
|
||||
logg.info('using engine {} module {}.{}'.format(config.get('SYNCER_BACKEND'), syncer_store_module.__file__, syncer_store_class.__name__))
|
||||
|
||||
settings.set('STATE_DIR', state_dir)
|
||||
settings.set('SYNC_STORE', sync_store)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def process_address_arg_rules(settings, config):
|
||||
rules = settings.get('RULES')
|
||||
category = {
|
||||
'input': {
|
||||
'i': [],
|
||||
'x': [],
|
||||
},
|
||||
'output': {
|
||||
'i': [],
|
||||
'x': [],
|
||||
},
|
||||
'exec': {
|
||||
'i': [],
|
||||
'x': [],
|
||||
},
|
||||
}
|
||||
for rules_arg in [
|
||||
'input',
|
||||
'output',
|
||||
'exec',
|
||||
]:
|
||||
(vy, vn) = to_config_names(rules_arg)
|
||||
for address in config.get(vy):
|
||||
if not is_address(address):
|
||||
raise ValueError('invalid address in config {}: {}'.format(vy, address))
|
||||
category[rules_arg]['i'].append(address)
|
||||
for address in config.get(vn):
|
||||
if not is_address(address):
|
||||
raise ValueError('invalid address in config {}: {}'.format(vn, address))
|
||||
category[rules_arg]['x'].append(address)
|
||||
|
||||
includes = RuleSimple(
|
||||
category['output']['i'],
|
||||
category['input']['i'],
|
||||
category['exec']['i'],
|
||||
description='INCLUDE',
|
||||
match_all=settings.get('MATCH_ALL'),
|
||||
)
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleSimple(
|
||||
category['output']['x'],
|
||||
category['input']['x'],
|
||||
category['exec']['x'],
|
||||
description='EXCLUDE',
|
||||
)
|
||||
rules.exclude(excludes)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def process_data_arg_rules(settings, config):
|
||||
rules = settings.get('RULES')
|
||||
|
||||
include_data = []
|
||||
for v in config.get('ETHMONITOR_DATA'):
|
||||
include_data.append(v.lower())
|
||||
exclude_data = []
|
||||
for v in config.get('ETHMONITOR_X_DATA'):
|
||||
exclude_data.append(v.lower())
|
||||
|
||||
includes = RuleMethod(include_data, description='INCLUDE')
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleMethod(exclude_data, description='EXCLUDE')
|
||||
rules.exclude(excludes)
|
||||
|
||||
include_data = []
|
||||
for v in config.get('ETHMONITOR_DATA_IN'):
|
||||
include_data.append(v.lower())
|
||||
exclude_data = []
|
||||
for v in config.get('ETHMONITOR_X_DATA_IN'):
|
||||
exclude_data.append(v.lower())
|
||||
|
||||
includes = RuleData(include_data, description='INCLUDE', match_all=settings.get('MATCH_ALL'))
|
||||
rules.include(includes)
|
||||
|
||||
excludes = RuleData(exclude_data, description='EXCLUDE')
|
||||
rules.exclude(excludes)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def process_address_file_rules(settings, config): #rules, includes_file=None, excludes_file=None, include_default=False, include_block_default=False):
|
||||
rules = settings.get('RULES')
|
||||
includes_file = config.get('ETHMONITOR_INCLUDES_FILE')
|
||||
if includes_file != None:
|
||||
f = open(includes_file, 'r')
|
||||
logg.debug('reading includes rules from {}'.format(os.path.realpath(includes_file)))
|
||||
while True:
|
||||
r = f.readline()
|
||||
if r == '':
|
||||
break
|
||||
r = r.rstrip()
|
||||
v = r.split("\t")
|
||||
|
||||
sender = []
|
||||
recipient = []
|
||||
executable = []
|
||||
|
||||
try:
|
||||
if v[0] != '':
|
||||
sender = v[0].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if v[1] != '':
|
||||
recipient = v[1].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if v[2] != '':
|
||||
executable = v[2].split(',')
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
rule = RuleSimple(sender, recipient, executable, match_all=settings.get('MATCH_ALL'))
|
||||
rules.include(rule)
|
||||
|
||||
excludes_file = config.get('ETHMONITOR_EXCLUDES_FILE')
|
||||
if excludes_file != None:
|
||||
f = open(includes_file, 'r')
|
||||
logg.debug('reading excludes rules from {}'.format(os.path.realpath(excludes_file)))
|
||||
while True:
|
||||
r = f.readline()
|
||||
if r == '':
|
||||
break
|
||||
r = r.rstrip()
|
||||
v = r.split("\t")
|
||||
|
||||
sender = None
|
||||
recipient = None
|
||||
executable = None
|
||||
|
||||
if v[0] != '':
|
||||
sender = v[0].strip(',')
|
||||
if v[1] != '':
|
||||
recipient = v[1].strip(',')
|
||||
if v[2] != '':
|
||||
executable = v[2].strip(',')
|
||||
|
||||
rule = RuleSimple(sender, recipient, executable)
|
||||
rules.exclude(rule)
|
||||
return settings
|
||||
|
||||
|
||||
def process_arg_rules(settings, config):
|
||||
address_rules = AddressRules(include_by_default=config.get('ETHMONITOR_INCLUDE_DEFAULT'))
|
||||
settings.set('MATCH_ALL', config.true('ETHMONITOR_MATCH_ALL'))
|
||||
settings.set('RULES', address_rules)
|
||||
settings = process_address_arg_rules(settings, config)
|
||||
settings = process_data_arg_rules(settings, config)
|
||||
settings = process_address_file_rules(settings, config)
|
||||
return settings
|
||||
|
||||
|
||||
def process_cache_store(settings, config):
|
||||
cache_dir = config.get('_CACHE_DIR')
|
||||
store = None
|
||||
if cache_dir == None:
|
||||
logg.warning('no cache dir specified, will discard everything!!')
|
||||
from eth_cache.store.null import NullStore
|
||||
store = NullStore()
|
||||
else:
|
||||
store = FileStore(settings.get('CHAIN_SPEC'), cache_dir)
|
||||
cache_dir = os.path.realpath(cache_dir)
|
||||
if cache_dir == None:
|
||||
import tempfile
|
||||
cache_dir = tempfile.mkdtemp()
|
||||
logg.info('using cache store {}'.format(store))
|
||||
|
||||
settings.set('CACHE_STORE', store)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def process_cache_filter(settings, config):
|
||||
cache_store = settings.get('CACHE_STORE')
|
||||
cache_rules = AddressRules(include_by_default=True)
|
||||
if str(cache_store) != 'Nullstore':
|
||||
cache_rules = settings.o['RULES']
|
||||
fltr = CacheFilter(cache_store, rules_filter=cache_rules, include_tx_data=config.true('ETHCACHE_STORE_TX'))
|
||||
sync_store = settings.get('SYNC_STORE')
|
||||
sync_store.register(fltr)
|
||||
|
||||
fltr = BlockFilter(cache_store, include_block_data=config.true('ETHCACHE_STORE_BLOCK'))
|
||||
hndlr = settings.get('BLOCK_HANDLER')
|
||||
hndlr.register(fltr)
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
def process_run_filter(settings, config):
|
||||
if not settings.get('RUN_OUT'):
|
||||
return settings
|
||||
fltr = RunFilter(settings.get('RUN_DIR'))
|
||||
hndlr = settings.get('BLOCK_HANDLER')
|
||||
hndlr.register(fltr)
|
||||
return settings
|
||||
|
||||
def process_tx_filter(settings, config):
|
||||
for fltr in list_from_prefix(config, 'filter'):
|
||||
m = importlib.import_module(fltr)
|
||||
fltr_object = m.Filter(rules_filter=settings.get('RULES'))
|
||||
store = settings.get('SYNC_STORE')
|
||||
store.register(fltr_object)
|
||||
logg.info('using filter module {}'.format(fltr))
|
||||
return settings
|
||||
|
||||
|
||||
def process_block_filter(settings, config):
|
||||
block_filter_handler = BlockCallbackFilter()
|
||||
for block_filter in list_from_prefix(config, 'block_filter'):
|
||||
m = importlib.import_module(block_filter)
|
||||
block_filter_handler.register(m)
|
||||
logg.info('using block filter module {}'.format(block_filter))
|
||||
|
||||
settings.set('BLOCK_HANDLER', block_filter_handler)
|
||||
return settings
|
||||
|
||||
|
||||
def process_out_filter(settings, config):
|
||||
out_filter = OutFilter(
|
||||
settings.o['CHAIN_SPEC'],
|
||||
rules_filter=settings.o['RULES'],
|
||||
renderers=settings.o['RENDERER'],
|
||||
)
|
||||
store = settings.get('SYNC_STORE')
|
||||
store.register(out_filter)
|
||||
return settings
|
||||
|
||||
|
||||
def process_arg_filter(settings, config):
|
||||
store = settings.get('SYNC_STORE')
|
||||
if config.get('ETHMONITOR_FILTER') != None:
|
||||
for k in config.get('ETHMONITOR_FILTER'):
|
||||
m = importlib.import_module(k)
|
||||
fltr = m.Filter()
|
||||
store.register(fltr)
|
||||
return settings
|
||||
|
||||
|
||||
def process_filter(settings, config):
|
||||
settings.set('FILTER', [])
|
||||
settings = process_renderer(settings, config)
|
||||
settings = process_block_filter(settings, config)
|
||||
settings = process_cache_filter(settings, config)
|
||||
settings = process_run_filter(settings, config)
|
||||
settings = process_tx_filter(settings, config)
|
||||
settings = process_out_filter(settings, config)
|
||||
settings = process_arg_filter(settings, config)
|
||||
return settings
|
||||
|
||||
|
||||
def process_renderer(settings, config):
|
||||
renderers_mods = []
|
||||
for renderer in config.get('ETHMONITOR_RENDERER'):
|
||||
m = importlib.import_module(renderer)
|
||||
renderers_mods.append(m)
|
||||
logg.info('using renderer module {}'.format(renderer))
|
||||
settings.set('RENDERER', renderers_mods)
|
||||
return settings
|
||||
|
||||
|
||||
def process_cache_rpc(settings, config):
|
||||
if str(settings.get('CACHE_STORE')) == 'Nullstore':
|
||||
logg.debug('cache store is null, cache rpc proxy will be deactivated')
|
||||
return settings
|
||||
if not config.true('_FRESH'):
|
||||
rpc = CacheRPC(settings.get('CONN'), settings.get('CACHE_STORE'))
|
||||
settings.set('CONN', rpc)
|
||||
return settings
|
||||
|
||||
|
||||
def process_sync(settings, config):
|
||||
dialect_filter = settings.get('RPC_DIALECT_FILTER')
|
||||
settings.set('SYNCER_INTERFACE', EthChainInterface(dialect_filter=dialect_filter, batch_limit=settings.get('RPC_BATCH_LIMIT')))
|
||||
settings = process_sync_range(settings, config)
|
||||
return settings
|
||||
|
||||
|
||||
def process_cache(settings, config):
|
||||
settings = process_cache_store(settings, config)
|
||||
settings = process_cache_rpc(settings, config)
|
||||
return settings
|
||||
|
||||
|
||||
def process_user_context(settings, config):
|
||||
ctx_usr = {}
|
||||
ks = config.get('ETHMONITOR_CONTEXT_KEY')
|
||||
if ks != None:
|
||||
for kv in ks:
|
||||
(k, v) = kv.split('=', 1)
|
||||
ctx_usr[k] = v
|
||||
ctx = {
|
||||
'driver': 'eth-monitor',
|
||||
'rundir': settings.get('RUN_DIR'),
|
||||
'usr': ctx_usr,
|
||||
}
|
||||
settings.set('SYNCER_CONTEXT', ctx)
|
||||
return settings
|
||||
|
||||
|
||||
def process_settings(settings, config):
|
||||
settings = process_monitor_session(settings, config)
|
||||
settings = process_monitor_session_dir(settings, config)
|
||||
settings = process_monitor_rundir(settings, config)
|
||||
settings = process_arg_rules(settings, config)
|
||||
settings = process_sync(settings, config)
|
||||
settings = process_cache(settings, config)
|
||||
settings = process_user_context(settings, config)
|
||||
settings = process_filter(settings, config)
|
||||
return settings
|
170
man/build/eth-monitor-import.1
Normal file
170
man/build/eth-monitor-import.1
Normal file
@ -0,0 +1,170 @@
|
||||
.TH eth-monitor-import 1
|
||||
|
||||
|
||||
.SH NAME
|
||||
eth-monitor-import \- Import transaction data from an indexing service
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY eth-monitor-import
|
||||
[ -i \fIchain_spec\fP] [ --api-key-file \fIfile\fp ] [ --address-file \fIfile\fP ] [ -a \fIaddress\fP ... ] [ --cache-dir \fIdirectory\fP ] \fIservice\fP
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
Use an indexing service to retrieve transaction hashes for one or more addresses. Supported services may be listed using the \fB--list-services\fP option.
|
||||
.P
|
||||
Which addresses to retrieve data for may be defined by the \fB-a\fP \fIaddress\fP option. Alternatively, the \fB--address-file\fP \fIfile\fP option may be used, where addresses are supplied from the given file as a comma-separated list. The address matching mechanism used in transaction processing is the same as for \fBeth-monitor(1)\fP.
|
||||
.P
|
||||
Only block and transaction hashes are used from the indexing service. The RPC endpoint will be used to retrieve the block and transaction data.
|
||||
.P
|
||||
If \fB--cache-dir\fP \fIdirectory\fP is defined, data will be cached to the given path using the same caching filter as \fBeth-monitor(1)\fP. \fB--store-tx-data\fP and \fB--store-block-data-\fP define whether also transaction and block data is stored to cache, respectively.
|
||||
|
||||
|
||||
.SS OPTIONS
|
||||
|
||||
.TP
|
||||
\fB-0\fP
|
||||
Omit newline to output
|
||||
|
||||
.TP
|
||||
\fB--address-file \fI\fIfile
|
||||
\fP\fP
|
||||
Load address include matching rules from file. Addresses must be given as a comma-separated list.
|
||||
|
||||
.TP
|
||||
\fB-c \fI\fIconfig_dir\fP\fP, \fB--config \fI\fIconfig_dir\fP\fP
|
||||
Load configuration files from given directory. All files with an .ini extension will be loaded, of which all must contain valid ini file data.
|
||||
|
||||
.TP
|
||||
\fB--dumpconfig \fI\fIformat\fP\fP
|
||||
Output configuration settings rendered from environment and inputs. Valid arguments are \fIini\fP for ini file output, and \fIenv\fP for environment variable output. See \fBCONFIGURATION\fP.
|
||||
|
||||
.TP
|
||||
\fB--env-prefix\fP
|
||||
Environment prefix for variables to overwrite configuration. Example: If \fB--env-prefix\fP is set to \fBFOO\fP then configuration variable \fBBAR_BAZ\fP would be set by environment variable \fBFOO_BAZ_BAR\fP. Also see \fBENVIRONMENT\fP.
|
||||
|
||||
.TP
|
||||
\fB--height\fP
|
||||
Block height at which to query state for. Does not apply to transactions.
|
||||
|
||||
.TP
|
||||
\fB-i \fI\fIchain_spec\fP\fP, \fB--chain-spec \fI\fIchain_spec\fP\fP
|
||||
Chain specification string, in the format <engine>:<fork>:<chain_id>:<common_name>. Example: "evm:london:1:ethereum". Overrides the \fIRPC_CREDENTIALS\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--list-services \fI\fI
|
||||
\fP\fP
|
||||
List all supported services.
|
||||
|
||||
.TP
|
||||
\fB-n \fI\fInamespace\fP\fP, \fB--namespace \fI\fInamespace\fP\fP
|
||||
Load given configuration namespace. Configuration will be loaded from the immediate configuration subdirectory with the same name.
|
||||
|
||||
.TP
|
||||
\fB--no-logs\fP
|
||||
Turn of logging completely. Negates \fB-v\fP and \fB-vv\fP
|
||||
|
||||
.TP
|
||||
\fB-p\fP, \fB--rpc-provider\fP
|
||||
Fully-qualified URL of RPC provider. Overrides the \fIRPC_PROVIDER\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--raw\fP
|
||||
Produce output most optimized for machines.
|
||||
|
||||
.TP
|
||||
\fB--rpc-batch-limit\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together. Overrides the \fIRPC_BATCH_LIMIT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--rpc-dialect\fP
|
||||
RPC backend dialect. If specified it \fImay\fP help with encoding and decoding issues. Overrides the \fIRPC_DIALECT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--socks-host \fI\fIhost
|
||||
\fP\fP
|
||||
Connect through the specified socks4a host (e.g. tor)
|
||||
|
||||
.TP
|
||||
\fB--socks-port \fI\fIport
|
||||
\fP\fP
|
||||
Connect through the specified socks4a host port (e.g. tor)
|
||||
|
||||
.TP
|
||||
\fB--store-block-data \fI\fI
|
||||
\fP\fP
|
||||
Store block data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB--store-tx-data \fI\fI
|
||||
\fP\fP
|
||||
Store transaction data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB-u\fP, \fB--unsafe\fP
|
||||
Allow addresses that do not pass checksum.
|
||||
|
||||
.TP
|
||||
\fB-v\fP
|
||||
Verbose. Show logs for important state changes.
|
||||
|
||||
.TP
|
||||
\fB-vv\fP
|
||||
Very verbose. Show logs with debugging information.
|
||||
|
||||
.SH CONFIGURATION
|
||||
|
||||
All configuration settings may be overriden both by environment variables, or by overriding settings with the contents of ini-files in the directory defined by the \fB-c\fP option.
|
||||
|
||||
The active configuration, with values assigned from environment and arguments, can be output using the \fB--dumpconfig\fP \fIformat\fP option. Note that entries having keys prefixed with underscore (e.g. _SEQ) are not actual configuration settings, and thus cannot be overridden with environment variables.
|
||||
|
||||
To refer to a configuration setting by environment variables, the \fIsection\fP and \fIkey\fP are concatenated together with an underscore, and transformed to upper-case. For example, the configuration variable \fIFOO_BAZ_BAR\fP refers to an ini-file entry as follows:
|
||||
|
||||
.EX
|
||||
[foo]
|
||||
bar_baz = xyzzy
|
||||
.EE
|
||||
|
||||
In the \fBENVIRONMENT\fP section below, the relevant configuration settings for this tool is listed along with a short description of its meaning.
|
||||
|
||||
Some configuration settings may also be overriden by command line options. Also note that the use of the \fB-n\fP and \fB--env-prefix\fP options affect how environment and configuration is read. The effects of options on how configuration settings are affective is described in the respective \fBOPTIONS\fP section.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
|
||||
|
||||
.TP
|
||||
\fICHAIN_SPEC\fP
|
||||
String specifying the type of chain connected to, in the format \fI<engine>:<fork>:<network_id>:<common_name>\fP. For EVM nodes the \fIengine\fP value will always be \fIevm\fP.
|
||||
|
||||
.TP
|
||||
\fIRPC_BATCH_LIMIT\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together.
|
||||
|
||||
.TP
|
||||
\fIRPC_DIALECT\fP
|
||||
Enables translations of EVM node specific formatting and response codes.
|
||||
|
||||
.TP
|
||||
\fIRPC_PROVIDER\fP
|
||||
Fully-qualified URL to the RPC endpoint of the blockchain node.
|
||||
|
||||
.SH LICENSE
|
||||
|
||||
This documentation and its source is licensed under the Creative Commons Attribution-Sharealike 4.0 International license.
|
||||
|
||||
The source code of the tool this documentation describes is licensed under the GNU General Public License 3.0.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Louis Holbrook <dev@holbrook.no> (https://holbrook.no)
|
||||
PGP: 59A844A484AC11253D3A3E9DCDCBD24DD1D0E001
|
||||
|
||||
|
||||
|
||||
.SH SOURCE CODE
|
||||
|
||||
https://git.defalsify.org
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
eth-monitor (1)
|
159
man/build/eth-monitor-list.1
Normal file
159
man/build/eth-monitor-list.1
Normal file
@ -0,0 +1,159 @@
|
||||
.TH eth-monitor-list 1
|
||||
|
||||
|
||||
.SH NAME
|
||||
eth-monitor-list \- Query transactions cache
|
||||
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY eth-monitor-list
|
||||
[ -i \fIchain_spec\fP ] [ p \fIeth_provider\fP ] [ -a \fIaddress\fP ... ] \fIcache_dir\fP
|
||||
.YS
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
List transactions stored in cache matching the given address.
|
||||
.P
|
||||
Any block data and/or transaction data matchin the relevant hashes returned by the query will be used to create the output. The \fB--fresh\fP option may be defined to force all block and transaction data from the RPC provider endpoint instead.
|
||||
.P
|
||||
For details on rendering and filtering, please refer to to \fBeth-monitor (1)\fP man page.
|
||||
|
||||
|
||||
.SS OPTIONS
|
||||
|
||||
.TP
|
||||
\fB-0\fP
|
||||
Omit newline to output
|
||||
|
||||
.TP
|
||||
\fB--address \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to match any role. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-c \fI\fIconfig_dir\fP\fP, \fB--config \fI\fIconfig_dir\fP\fP
|
||||
Load configuration files from given directory. All files with an .ini extension will be loaded, of which all must contain valid ini file data.
|
||||
|
||||
.TP
|
||||
\fB--dumpconfig \fI\fIformat\fP\fP
|
||||
Output configuration settings rendered from environment and inputs. Valid arguments are \fIini\fP for ini file output, and \fIenv\fP for environment variable output. See \fBCONFIGURATION\fP.
|
||||
|
||||
.TP
|
||||
\fB--env-prefix\fP
|
||||
Environment prefix for variables to overwrite configuration. Example: If \fB--env-prefix\fP is set to \fBFOO\fP then configuration variable \fBBAR_BAZ\fP would be set by environment variable \fBFOO_BAZ_BAR\fP. Also see \fBENVIRONMENT\fP.
|
||||
|
||||
.TP
|
||||
\fB--filter \fI\fImodule
|
||||
\fP\fP
|
||||
Add code execution filter to all matching transactions. The argument must be a python module path. Several filters may be added by supplying the option multiple times. Filters will be executed in the order the options are given. See \fBDEFINING FILTERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--fresh \fI\fI
|
||||
\fP\fP
|
||||
Only use hashes from cache, and retrieve all block and transaction data from RPC endpoint.
|
||||
|
||||
.TP
|
||||
\fB--height\fP
|
||||
Block height at which to query state for. Does not apply to transactions.
|
||||
|
||||
.TP
|
||||
\fB-i \fI\fIchain_spec\fP\fP, \fB--chain-spec \fI\fIchain_spec\fP\fP
|
||||
Chain specification string, in the format <engine>:<fork>:<chain_id>:<common_name>. Example: "evm:london:1:ethereum". Overrides the \fIRPC_CREDENTIALS\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB-n \fI\fInamespace\fP\fP, \fB--namespace \fI\fInamespace\fP\fP
|
||||
Load given configuration namespace. Configuration will be loaded from the immediate configuration subdirectory with the same name.
|
||||
|
||||
.TP
|
||||
\fB--no-logs\fP
|
||||
Turn of logging completely. Negates \fB-v\fP and \fB-vv\fP
|
||||
|
||||
.TP
|
||||
\fB-p\fP, \fB--rpc-provider\fP
|
||||
Fully-qualified URL of RPC provider. Overrides the \fIRPC_PROVIDER\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--raw\fP
|
||||
Produce output most optimized for machines.
|
||||
|
||||
.TP
|
||||
\fB--renderer \fI\fImodule
|
||||
\fP\fP
|
||||
Add output renderer filter to all matching transactions. The argument must be a python module path. Several renderers may be added by supplying the option multiple times. See \fBRENDERERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--rpc-batch-limit\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together. Overrides the \fIRPC_BATCH_LIMIT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--rpc-dialect\fP
|
||||
RPC backend dialect. If specified it \fImay\fP help with encoding and decoding issues. Overrides the \fIRPC_DIALECT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB-u\fP, \fB--unsafe\fP
|
||||
Allow addresses that do not pass checksum.
|
||||
|
||||
.TP
|
||||
\fB-v\fP
|
||||
Verbose. Show logs for important state changes.
|
||||
|
||||
.TP
|
||||
\fB-vv\fP
|
||||
Very verbose. Show logs with debugging information.
|
||||
|
||||
.SH CONFIGURATION
|
||||
|
||||
All configuration settings may be overriden both by environment variables, or by overriding settings with the contents of ini-files in the directory defined by the \fB-c\fP option.
|
||||
|
||||
The active configuration, with values assigned from environment and arguments, can be output using the \fB--dumpconfig\fP \fIformat\fP option. Note that entries having keys prefixed with underscore (e.g. _SEQ) are not actual configuration settings, and thus cannot be overridden with environment variables.
|
||||
|
||||
To refer to a configuration setting by environment variables, the \fIsection\fP and \fIkey\fP are concatenated together with an underscore, and transformed to upper-case. For example, the configuration variable \fIFOO_BAZ_BAR\fP refers to an ini-file entry as follows:
|
||||
|
||||
.EX
|
||||
[foo]
|
||||
bar_baz = xyzzy
|
||||
.EE
|
||||
|
||||
In the \fBENVIRONMENT\fP section below, the relevant configuration settings for this tool is listed along with a short description of its meaning.
|
||||
|
||||
Some configuration settings may also be overriden by command line options. Also note that the use of the \fB-n\fP and \fB--env-prefix\fP options affect how environment and configuration is read. The effects of options on how configuration settings are affective is described in the respective \fBOPTIONS\fP section.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
|
||||
|
||||
.TP
|
||||
\fICHAIN_SPEC\fP
|
||||
String specifying the type of chain connected to, in the format \fI<engine>:<fork>:<network_id>:<common_name>\fP. For EVM nodes the \fIengine\fP value will always be \fIevm\fP.
|
||||
|
||||
.TP
|
||||
\fIRPC_BATCH_LIMIT\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together.
|
||||
|
||||
.TP
|
||||
\fIRPC_DIALECT\fP
|
||||
Enables translations of EVM node specific formatting and response codes.
|
||||
|
||||
.TP
|
||||
\fIRPC_PROVIDER\fP
|
||||
Fully-qualified URL to the RPC endpoint of the blockchain node.
|
||||
|
||||
.SH LICENSE
|
||||
|
||||
This documentation and its source is licensed under the Creative Commons Attribution-Sharealike 4.0 International license.
|
||||
|
||||
The source code of the tool this documentation describes is licensed under the GNU General Public License 3.0.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Louis Holbrook <dev@holbrook.no> (https://holbrook.no)
|
||||
PGP: 59A844A484AC11253D3A3E9DCDCBD24DD1D0E001
|
||||
|
||||
|
||||
|
||||
.SH SOURCE CODE
|
||||
|
||||
https://git.defalsify.org
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
eth-monitor (1)
|
276
man/build/eth-monitor-sync.1
Normal file
276
man/build/eth-monitor-sync.1
Normal file
@ -0,0 +1,276 @@
|
||||
.TH eth-monitor 1
|
||||
|
||||
.SH NAME
|
||||
eth-monitor \- Cache, index and monitor transactions with an EVM node rpc
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY eth-monitor
|
||||
[ --skip-history ] [ --single ] [ p \fIeth_provider\fP ] [ --includes-file \fIfile\fP ] [ -i chain_spec ]
|
||||
.YS
|
||||
.SY eth-monitor
|
||||
[ --skip-history ] [ --single ] [ p \fIeth_provider\fP ] [ --excludes-file \fIfile\fP ] [ --include-default ] [ -i chain_spec ]
|
||||
.YS
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
The \fBeth-monitor\fP has fulfills three distinct but related functions:
|
||||
.IP
|
||||
1. A customizable view of on transactions of interest.
|
||||
.IP
|
||||
2. A block and transaction cache.
|
||||
.IP
|
||||
3. Arbitrary code executions using a transaction (and its block) as input.
|
||||
|
||||
.P
|
||||
Using an EVM RPC endpoint, the \fBeth-monitor\fP tool will retrieve blocks within a given range and provides arbitrary processing of each transaction.
|
||||
.P
|
||||
A collection of options is provided to control the behavior of which block ranges to sync, which criteria to use for display and cache, and what code to execute for matching transactions. Details on each topic can be found in the \fBSYNCING\fP, \fBMATCHING ADDRESSES\fP and \fBDEFINING FILTERS\fP sections below, respectively.
|
||||
.P
|
||||
Example executions of the tool can be found in the \fBEXAMPLES\fP section.
|
||||
.P
|
||||
|
||||
|
||||
.SS OPTIONS
|
||||
|
||||
.TP
|
||||
\fB-0\fP
|
||||
Omit newline to output
|
||||
|
||||
.TP
|
||||
\fB--address \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to match any role. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-c \fI\fIconfig_dir\fP\fP, \fB--config \fI\fIconfig_dir\fP\fP
|
||||
Load configuration files from given directory. All files with an .ini extension will be loaded, of which all must contain valid ini file data.
|
||||
|
||||
.TP
|
||||
\fB--context-key \fI\fIkey=value
|
||||
\fP\fP
|
||||
Add a key-value pair that gets passed to the syncer context. May be specified several times.
|
||||
|
||||
.TP
|
||||
\fB--dumpconfig \fI\fIformat\fP\fP
|
||||
Output configuration settings rendered from environment and inputs. Valid arguments are \fIini\fP for ini file output, and \fIenv\fP for environment variable output. See \fBCONFIGURATION\fP.
|
||||
|
||||
.TP
|
||||
\fB--env-prefix\fP
|
||||
Environment prefix for variables to overwrite configuration. Example: If \fB--env-prefix\fP is set to \fBFOO\fP then configuration variable \fBBAR_BAZ\fP would be set by environment variable \fBFOO_BAZ_BAR\fP. Also see \fBENVIRONMENT\fP.
|
||||
|
||||
.TP
|
||||
\fB--excludes-file \fI\fIfile
|
||||
\fP\fP
|
||||
Load address exclude matching rules from file. See \fBMATCHING ADDRESSES\fP.
|
||||
|
||||
.TP
|
||||
\fB--exec \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to executable address array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB--filter \fI\fImodule
|
||||
\fP\fP
|
||||
Add code execution filter to all matched transactions. The argument must be a python module path. Several filters may be added by supplying the option multiple times. Filters will be executed in the order the options are given. See \fBDEFINING FILTERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--height\fP
|
||||
Block height at which to query state for. Does not apply to transactions.
|
||||
|
||||
.TP
|
||||
\fB-i \fI\fIchain_spec\fP\fP, \fB--chain-spec \fI\fIchain_spec\fP\fP
|
||||
Chain specification string, in the format <engine>:<fork>:<chain_id>:<common_name>. Example: "evm:london:1:ethereum". Overrides the \fIRPC_CREDENTIALS\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--include-default \fI\fI
|
||||
\fP\fP
|
||||
Match all addresses by default. Addresses may be excluded using --excludes-file. If this is set, --input, --output, --exec and --includes-file will have no effect.
|
||||
|
||||
.TP
|
||||
\fB--includes-file \fI\fIfile
|
||||
\fP\fP
|
||||
Load address include matching rules from file. See \fBMATCHING ADDRESSES\fP.
|
||||
|
||||
.TP
|
||||
\fB--input \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to inputs (recipients) array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-n \fI\fInamespace\fP\fP, \fB--namespace \fI\fInamespace\fP\fP
|
||||
Load given configuration namespace. Configuration will be loaded from the immediate configuration subdirectory with the same name.
|
||||
|
||||
.TP
|
||||
\fB--no-logs\fP
|
||||
Turn of logging completely. Negates \fB-v\fP and \fB-vv\fP
|
||||
|
||||
.TP
|
||||
\fB--output \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to outputs (sender) array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-p\fP, \fB--rpc-provider\fP
|
||||
Fully-qualified URL of RPC provider. Overrides the \fIRPC_PROVIDER\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--raw\fP
|
||||
Produce output most optimized for machines.
|
||||
|
||||
.TP
|
||||
\fB--renderer \fI\fImodule
|
||||
\fP\fP
|
||||
Add output renderer filter to all matched transactions. The argument must be a python module path. Several renderers may be added by supplying the option multiple times. See \fBRENDERERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--rpc-batch-limit\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together. Overrides the \fIRPC_BATCH_LIMIT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--rpc-dialect\fP
|
||||
RPC backend dialect. If specified it \fImay\fP help with encoding and decoding issues. Overrides the \fIRPC_DIALECT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--store-block-data \fI\fI
|
||||
\fP\fP
|
||||
Store block data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB--store-tx-data \fI\fI
|
||||
\fP\fP
|
||||
Store transaction data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB-u\fP, \fB--unsafe\fP
|
||||
Allow addresses that do not pass checksum.
|
||||
|
||||
.TP
|
||||
\fB-v\fP
|
||||
Verbose. Show logs for important state changes.
|
||||
|
||||
.TP
|
||||
\fB-vv\fP
|
||||
Very verbose. Show logs with debugging information.
|
||||
|
||||
.TP
|
||||
\fB--x-address \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to match any role.
|
||||
|
||||
.TP
|
||||
\fB--x-exec \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to executable address array.
|
||||
|
||||
.TP
|
||||
\fB--x-input \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to inputs (recipients) array.
|
||||
|
||||
.TP
|
||||
\fB--x-output \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to outputs (sender) array.
|
||||
|
||||
.SH CONFIGURATION
|
||||
|
||||
All configuration settings may be overriden both by environment variables, or by overriding settings with the contents of ini-files in the directory defined by the \fB-c\fP option.
|
||||
|
||||
The active configuration, with values assigned from environment and arguments, can be output using the \fB--dumpconfig\fP \fIformat\fP option. Note that entries having keys prefixed with underscore (e.g. _SEQ) are not actual configuration settings, and thus cannot be overridden with environment variables.
|
||||
|
||||
To refer to a configuration setting by environment variables, the \fIsection\fP and \fIkey\fP are concatenated together with an underscore, and transformed to upper-case. For example, the configuration variable \fIFOO_BAZ_BAR\fP refers to an ini-file entry as follows:
|
||||
|
||||
.EX
|
||||
[foo]
|
||||
bar_baz = xyzzy
|
||||
.EE
|
||||
|
||||
In the \fBENVIRONMENT\fP section below, the relevant configuration settings for this tool is listed along with a short description of its meaning.
|
||||
|
||||
Some configuration settings may also be overriden by command line options. Also note that the use of the \fB-n\fP and \fB--env-prefix\fP options affect how environment and configuration is read. The effects of options on how configuration settings are affective is described in the respective \fBOPTIONS\fP section.
|
||||
|
||||
.SH MATCHING ADDRESSES
|
||||
By default, addresses to match against transactions need to be explicitly specified. This behavior can be reversed with the \fB--include-default\fP option. Addresses to match are defined using the \fB--input\fP, \fB--output\fP and \fB--exec\fP options. Addresses specified multiple times will be deduplicated.
|
||||
.P
|
||||
Inclusion rules may also be loaded from file by specifying the \fB--includes-file\fP and \fB--excludes-file\fP options. Each file must specify the outputs, inputs and exec addresses as comma separated lists respectively, separated by tabs.
|
||||
.P
|
||||
In the current state of this tool, address matching will affect all parts of the processing; cache, code execution and rendering.
|
||||
|
||||
.SH SYNCING
|
||||
When a sync is initiated, the state of this sync is persisted. This way, previous syncs that did not complete for some reason will be resumed where they left off.
|
||||
.P
|
||||
A special sync type \fB--head\fP starts syncing at the current head of the chain, and continue to sync until interrupted. When resuming sync, a new sync range between the current block head and the block height at which the previous \fB--head\fP sync left off will automatically be created.
|
||||
.P
|
||||
Syncs can be forced to (re)run for ranges regardless of previous state by using the \fB--single\fP option. However, there is no protection in place from preventing code filters from being executed again on the same transaction when this is done. See \fBDEFINING FILTERS\fP below.
|
||||
|
||||
|
||||
.SH CACHE
|
||||
When syncing, the hash of a block and transaction matching the address criteria will be stored in the cache. The hashes can be used for future data lookups.
|
||||
.P
|
||||
If \fB--store-block-data\fP and/or \fB--store-tx-data\fP is set, a copy of the block and/or transaction data will also be stored, respectively.
|
||||
|
||||
|
||||
.SH RENDERING
|
||||
Rendering in the context of \fBeth-monitor\fP refers to a formatted output stream that occurs independently of caching and code execution.
|
||||
.P
|
||||
Filters for rendering may be specified by specifying python modules to the \fB--renderer\fP option. This option may be specified multiple times.
|
||||
.P
|
||||
Rendering filters will be executed in order, and the first filter to return \fIFalse\fP
|
||||
|
||||
|
||||
.SH DEFINING FILTERS
|
||||
|
||||
Filters will strictly be executed in the order which they are defined on the command line.
|
||||
|
||||
A python module used for filter must fulfill two conditions:
|
||||
|
||||
.IP
|
||||
1. It must provide a class named \fIFilter\fP in the package base namespace.
|
||||
.IP
|
||||
2. The \fIFilter\fP class must extend the \fIchainsyncer.filter.SyncFilter\fP interface, and at least override the \fIfilter\fP method.
|
||||
|
||||
|
||||
.SS SYNCER AND FILTER CONTEXT
|
||||
|
||||
Key-value pairs specified with `--context-key` will be passed to the filter's \fIprepare\fP method, aswell as the \fIctx\fP parameter of the \fIfilter\fP method.
|
||||
|
||||
|
||||
.SH FURTHER READING
|
||||
|
||||
Refer to the \fBchainsyncer\fP chapter n \fIinfo chaintool\fP for in-depth information on the subjects of syncing and filtering.
|
||||
.SH ENVIRONMENT
|
||||
|
||||
|
||||
.TP
|
||||
\fICHAIN_SPEC\fP
|
||||
String specifying the type of chain connected to, in the format \fI<engine>:<fork>:<network_id>:<common_name>\fP. For EVM nodes the \fIengine\fP value will always be \fIevm\fP.
|
||||
|
||||
.TP
|
||||
\fIRPC_BATCH_LIMIT\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together.
|
||||
|
||||
.TP
|
||||
\fIRPC_DIALECT\fP
|
||||
Enables translations of EVM node specific formatting and response codes.
|
||||
|
||||
.TP
|
||||
\fIRPC_PROVIDER\fP
|
||||
Fully-qualified URL to the RPC endpoint of the blockchain node.
|
||||
|
||||
.SH LICENSE
|
||||
|
||||
This documentation and its source is licensed under the Creative Commons Attribution-Sharealike 4.0 International license.
|
||||
|
||||
The source code of the tool this documentation describes is licensed under the GNU General Public License 3.0.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Louis Holbrook <dev@holbrook.no> (https://holbrook.no)
|
||||
PGP: 59A844A484AC11253D3A3E9DCDCBD24DD1D0E001
|
||||
|
||||
|
||||
|
||||
.SH SOURCE CODE
|
||||
|
||||
https://git.defalsify.org
|
||||
|
276
man/build/eth-monitor.1
Normal file
276
man/build/eth-monitor.1
Normal file
@ -0,0 +1,276 @@
|
||||
.TH eth-monitor 1
|
||||
|
||||
.SH NAME
|
||||
eth-monitor \- Cache, index and monitor transactions with an EVM node rpc
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY eth-monitor
|
||||
[ --skip-history ] [ --single ] [ p \fIeth_provider\fP ] [ --includes-file \fIfile\fP ] [ -i chain_spec ]
|
||||
.YS
|
||||
.SY eth-monitor
|
||||
[ --skip-history ] [ --single ] [ p \fIeth_provider\fP ] [ --excludes-file \fIfile\fP ] [ --include-default ] [ -i chain_spec ]
|
||||
.YS
|
||||
|
||||
|
||||
.SH DESCRIPTION
|
||||
The \fBeth-monitor\fP has fulfills three distinct but related functions:
|
||||
.IP
|
||||
1. A customizable view of on transactions of interest.
|
||||
.IP
|
||||
2. A block and transaction cache.
|
||||
.IP
|
||||
3. Arbitrary code executions using a transaction (and its block) as input.
|
||||
|
||||
.P
|
||||
Using an EVM RPC endpoint, the \fBeth-monitor\fP tool will retrieve blocks within a given range and provides arbitrary processing of each transaction.
|
||||
.P
|
||||
A collection of options is provided to control the behavior of which block ranges to sync, which criteria to use for display and cache, and what code to execute for matching transactions. Details on each topic can be found in the \fBSYNCING\fP, \fBMATCHING ADDRESSES\fP and \fBDEFINING FILTERS\fP sections below, respectively.
|
||||
.P
|
||||
Example executions of the tool can be found in the \fBEXAMPLES\fP section.
|
||||
.P
|
||||
|
||||
|
||||
.SS OPTIONS
|
||||
|
||||
.TP
|
||||
\fB-0\fP
|
||||
Omit newline to output
|
||||
|
||||
.TP
|
||||
\fB--address \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to match any role. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-c \fI\fIconfig_dir\fP\fP, \fB--config \fI\fIconfig_dir\fP\fP
|
||||
Load configuration files from given directory. All files with an .ini extension will be loaded, of which all must contain valid ini file data.
|
||||
|
||||
.TP
|
||||
\fB--context-key \fI\fIkey=value
|
||||
\fP\fP
|
||||
Add a key-value pair that gets passed to the syncer context. May be specified several times.
|
||||
|
||||
.TP
|
||||
\fB--dumpconfig \fI\fIformat\fP\fP
|
||||
Output configuration settings rendered from environment and inputs. Valid arguments are \fIini\fP for ini file output, and \fIenv\fP for environment variable output. See \fBCONFIGURATION\fP.
|
||||
|
||||
.TP
|
||||
\fB--env-prefix\fP
|
||||
Environment prefix for variables to overwrite configuration. Example: If \fB--env-prefix\fP is set to \fBFOO\fP then configuration variable \fBBAR_BAZ\fP would be set by environment variable \fBFOO_BAZ_BAR\fP. Also see \fBENVIRONMENT\fP.
|
||||
|
||||
.TP
|
||||
\fB--excludes-file \fI\fIfile
|
||||
\fP\fP
|
||||
Load address exclude matching rules from file. See \fBMATCHING ADDRESSES\fP.
|
||||
|
||||
.TP
|
||||
\fB--exec \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to executable address array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB--filter \fI\fImodule
|
||||
\fP\fP
|
||||
Add code execution filter to all matched transactions. The argument must be a python module path. Several filters may be added by supplying the option multiple times. Filters will be executed in the order the options are given. See \fBDEFINING FILTERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--height\fP
|
||||
Block height at which to query state for. Does not apply to transactions.
|
||||
|
||||
.TP
|
||||
\fB-i \fI\fIchain_spec\fP\fP, \fB--chain-spec \fI\fIchain_spec\fP\fP
|
||||
Chain specification string, in the format <engine>:<fork>:<chain_id>:<common_name>. Example: "evm:london:1:ethereum". Overrides the \fIRPC_CREDENTIALS\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--include-default \fI\fI
|
||||
\fP\fP
|
||||
Match all addresses by default. Addresses may be excluded using --excludes-file. If this is set, --input, --output, --exec and --includes-file will have no effect.
|
||||
|
||||
.TP
|
||||
\fB--includes-file \fI\fIfile
|
||||
\fP\fP
|
||||
Load address include matching rules from file. See \fBMATCHING ADDRESSES\fP.
|
||||
|
||||
.TP
|
||||
\fB--input \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to inputs (recipients) array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-n \fI\fInamespace\fP\fP, \fB--namespace \fI\fInamespace\fP\fP
|
||||
Load given configuration namespace. Configuration will be loaded from the immediate configuration subdirectory with the same name.
|
||||
|
||||
.TP
|
||||
\fB--no-logs\fP
|
||||
Turn of logging completely. Negates \fB-v\fP and \fB-vv\fP
|
||||
|
||||
.TP
|
||||
\fB--output \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to outputs (sender) array. Complements \fB--address-file\fP.
|
||||
|
||||
.TP
|
||||
\fB-p\fP, \fB--rpc-provider\fP
|
||||
Fully-qualified URL of RPC provider. Overrides the \fIRPC_PROVIDER\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--raw\fP
|
||||
Produce output most optimized for machines.
|
||||
|
||||
.TP
|
||||
\fB--renderer \fI\fImodule
|
||||
\fP\fP
|
||||
Add output renderer filter to all matched transactions. The argument must be a python module path. Several renderers may be added by supplying the option multiple times. See \fBRENDERERS\fP section of \fBeth-monitor (1)\fP for more details.
|
||||
|
||||
.TP
|
||||
\fB--rpc-batch-limit\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together. Overrides the \fIRPC_BATCH_LIMIT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--rpc-dialect\fP
|
||||
RPC backend dialect. If specified it \fImay\fP help with encoding and decoding issues. Overrides the \fIRPC_DIALECT\fP configuration setting.
|
||||
|
||||
.TP
|
||||
\fB--store-block-data \fI\fI
|
||||
\fP\fP
|
||||
Store block data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB--store-tx-data \fI\fI
|
||||
\fP\fP
|
||||
Store transaction data in cache for matching transactions. Requires \fB--cache-dir\fP.
|
||||
|
||||
.TP
|
||||
\fB-u\fP, \fB--unsafe\fP
|
||||
Allow addresses that do not pass checksum.
|
||||
|
||||
.TP
|
||||
\fB-v\fP
|
||||
Verbose. Show logs for important state changes.
|
||||
|
||||
.TP
|
||||
\fB-vv\fP
|
||||
Very verbose. Show logs with debugging information.
|
||||
|
||||
.TP
|
||||
\fB--x-address \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of interest to match any role.
|
||||
|
||||
.TP
|
||||
\fB--x-exec \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to executable address array.
|
||||
|
||||
.TP
|
||||
\fB--x-input \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to inputs (recipients) array.
|
||||
|
||||
.TP
|
||||
\fB--x-output \fI\fIaddress
|
||||
\fP\fP
|
||||
Add an address of disinterest to outputs (sender) array.
|
||||
|
||||
.SH CONFIGURATION
|
||||
|
||||
All configuration settings may be overriden both by environment variables, or by overriding settings with the contents of ini-files in the directory defined by the \fB-c\fP option.
|
||||
|
||||
The active configuration, with values assigned from environment and arguments, can be output using the \fB--dumpconfig\fP \fIformat\fP option. Note that entries having keys prefixed with underscore (e.g. _SEQ) are not actual configuration settings, and thus cannot be overridden with environment variables.
|
||||
|
||||
To refer to a configuration setting by environment variables, the \fIsection\fP and \fIkey\fP are concatenated together with an underscore, and transformed to upper-case. For example, the configuration variable \fIFOO_BAZ_BAR\fP refers to an ini-file entry as follows:
|
||||
|
||||
.EX
|
||||
[foo]
|
||||
bar_baz = xyzzy
|
||||
.EE
|
||||
|
||||
In the \fBENVIRONMENT\fP section below, the relevant configuration settings for this tool is listed along with a short description of its meaning.
|
||||
|
||||
Some configuration settings may also be overriden by command line options. Also note that the use of the \fB-n\fP and \fB--env-prefix\fP options affect how environment and configuration is read. The effects of options on how configuration settings are affective is described in the respective \fBOPTIONS\fP section.
|
||||
|
||||
.SH MATCHING ADDRESSES
|
||||
By default, addresses to match against transactions need to be explicitly specified. This behavior can be reversed with the \fB--include-default\fP option. Addresses to match are defined using the \fB--input\fP, \fB--output\fP and \fB--exec\fP options. Addresses specified multiple times will be deduplicated.
|
||||
.P
|
||||
Inclusion rules may also be loaded from file by specifying the \fB--includes-file\fP and \fB--excludes-file\fP options. Each file must specify the outputs, inputs and exec addresses as comma separated lists respectively, separated by tabs.
|
||||
.P
|
||||
In the current state of this tool, address matching will affect all parts of the processing; cache, code execution and rendering.
|
||||
|
||||
.SH SYNCING
|
||||
When a sync is initiated, the state of this sync is persisted. This way, previous syncs that did not complete for some reason will be resumed where they left off.
|
||||
.P
|
||||
A special sync type \fB--head\fP starts syncing at the current head of the chain, and continue to sync until interrupted. When resuming sync, a new sync range between the current block head and the block height at which the previous \fB--head\fP sync left off will automatically be created.
|
||||
.P
|
||||
Syncs can be forced to (re)run for ranges regardless of previous state by using the \fB--single\fP option. However, there is no protection in place from preventing code filters from being executed again on the same transaction when this is done. See \fBDEFINING FILTERS\fP below.
|
||||
|
||||
|
||||
.SH CACHE
|
||||
When syncing, the hash of a block and transaction matching the address criteria will be stored in the cache. The hashes can be used for future data lookups.
|
||||
.P
|
||||
If \fB--store-block-data\fP and/or \fB--store-tx-data\fP is set, a copy of the block and/or transaction data will also be stored, respectively.
|
||||
|
||||
|
||||
.SH RENDERING
|
||||
Rendering in the context of \fBeth-monitor\fP refers to a formatted output stream that occurs independently of caching and code execution.
|
||||
.P
|
||||
Filters for rendering may be specified by specifying python modules to the \fB--renderer\fP option. This option may be specified multiple times.
|
||||
.P
|
||||
Rendering filters will be executed in order, and the first filter to return \fIFalse\fP
|
||||
|
||||
|
||||
.SH DEFINING FILTERS
|
||||
|
||||
Filters will strictly be executed in the order which they are defined on the command line.
|
||||
|
||||
A python module used for filter must fulfill two conditions:
|
||||
|
||||
.IP
|
||||
1. It must provide a class named \fIFilter\fP in the package base namespace.
|
||||
.IP
|
||||
2. The \fIFilter\fP class must extend the \fIchainsyncer.filter.SyncFilter\fP interface, and at least override the \fIfilter\fP method.
|
||||
|
||||
|
||||
.SS SYNCER AND FILTER CONTEXT
|
||||
|
||||
Key-value pairs specified with `--context-key` will be passed to the filter's \fIprepare\fP method, aswell as the \fIctx\fP parameter of the \fIfilter\fP method.
|
||||
|
||||
|
||||
.SH FURTHER READING
|
||||
|
||||
Refer to the \fBchainsyncer\fP chapter n \fIinfo chaintool\fP for in-depth information on the subjects of syncing and filtering.
|
||||
.SH ENVIRONMENT
|
||||
|
||||
|
||||
.TP
|
||||
\fICHAIN_SPEC\fP
|
||||
String specifying the type of chain connected to, in the format \fI<engine>:<fork>:<network_id>:<common_name>\fP. For EVM nodes the \fIengine\fP value will always be \fIevm\fP.
|
||||
|
||||
.TP
|
||||
\fIRPC_BATCH_LIMIT\fP
|
||||
Set number of RPC requests that can be set to the RPC provider as a batch request. This is made available through settings to any request builder implementing batch requests. A value of 1 means no batch will be used. A value of 0 indicates that the limit is not relevant. Any other positive value signals the maximum number of requests to be batched together.
|
||||
|
||||
.TP
|
||||
\fIRPC_DIALECT\fP
|
||||
Enables translations of EVM node specific formatting and response codes.
|
||||
|
||||
.TP
|
||||
\fIRPC_PROVIDER\fP
|
||||
Fully-qualified URL to the RPC endpoint of the blockchain node.
|
||||
|
||||
.SH LICENSE
|
||||
|
||||
This documentation and its source is licensed under the Creative Commons Attribution-Sharealike 4.0 International license.
|
||||
|
||||
The source code of the tool this documentation describes is licensed under the GNU General Public License 3.0.
|
||||
|
||||
.SH COPYRIGHT
|
||||
|
||||
Louis Holbrook <dev@holbrook.no> (https://holbrook.no)
|
||||
PGP: 59A844A484AC11253D3A3E9DCDCBD24DD1D0E001
|
||||
|
||||
|
||||
|
||||
.SH SOURCE CODE
|
||||
|
||||
https://git.defalsify.org
|
||||
|
@ -29,14 +29,19 @@ Rendering filters will be executed in order, and the first filter to return \fIF
|
||||
|
||||
.SH DEFINING FILTERS
|
||||
|
||||
Filters will strictly be executed in the order which they are defined on the command line.
|
||||
|
||||
A python module used for filter must fulfill two conditions:
|
||||
|
||||
.IP
|
||||
1. It must provide a class named \fIFilter\fP in the package base namespace.
|
||||
.IP
|
||||
2. The \fIFilter\fP class must include a method named \fIfilter\fP with the signature \fIdef filter(self, conn, block, tx, db_session=None)\fP.
|
||||
2. The \fIFilter\fP class must extend the \fIchainsyncer.filter.SyncFilter\fP interface, and at least override the \fIfilter\fP method.
|
||||
|
||||
Filters will strictly be executed in the order which they are defined on the command line.
|
||||
|
||||
.SS SYNCER AND FILTER CONTEXT
|
||||
|
||||
Key-value pairs specified with `--context-key` will be passed to the filter's \fIprepare\fP method, aswell as the \fIctx\fP parameter of the \fIfilter\fP method.
|
||||
|
||||
|
||||
.SH FURTHER READING
|
||||
|
@ -13,3 +13,4 @@ storetx Store transaction data in cache for matching transactions. Requires \fB-
|
||||
storeblock Store block data in cache for matching transactions. Requires \fB--cache-dir\fP. --store-block-data
|
||||
renderer Add output renderer filter to all matched transactions. The argument must be a python module path. Several renderers may be added by supplying the option multiple times. See \fBRENDERERS\fP section of \fBeth-monitor (1)\fP for more details. --renderer module
|
||||
filter Add code execution filter to all matched transactions. The argument must be a python module path. Several filters may be added by supplying the option multiple times. Filters will be executed in the order the options are given. See \fBDEFINING FILTERS\fP section of \fBeth-monitor (1)\fP for more details. --filter module
|
||||
context_key Add a key-value pair that gets passed to the syncer context. May be specified several times. --context-key key=value
|
||||
|
@ -1,6 +1,6 @@
|
||||
chainlib-eth>=0.1.0b4,<0.2.0
|
||||
chainlib>=0.1.0b1,<0.2.0
|
||||
chainsyncer~=0.3.2
|
||||
eth-erc20~=0.2.0
|
||||
chainlib-eth~=0.6.0
|
||||
chainlib~=0.5.2
|
||||
chainsyncer~=0.8.5
|
||||
leveldir~=0.3.0
|
||||
eth-cache~=0.1.0
|
||||
eth-cache~=0.4.0
|
||||
confini~=0.6.3
|
||||
|
17
run_tests.sh
Normal file
17
run_tests.sh
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -a
|
||||
set -e
|
||||
set -x
|
||||
default_pythonpath=$PYTHONPATH:.
|
||||
export PYTHONPATH=${default_pythonpath:-.}
|
||||
>&2 echo using pythonpath $PYTHONPATH
|
||||
for f in `ls tests/*.py`; do
|
||||
python $f
|
||||
done
|
||||
for f in `ls tests/rules/*.py`; do
|
||||
python $f
|
||||
done
|
||||
set +x
|
||||
set +e
|
||||
set +a
|
10
setup.cfg
10
setup.cfg
@ -1,6 +1,6 @@
|
||||
[metadata]
|
||||
name = eth-monitor
|
||||
version = 0.4.0
|
||||
version = 0.8.9
|
||||
description = Monitor and cache transactions using match filters
|
||||
author = Louis Holbrook
|
||||
author_email = dev@holbrook.no
|
||||
@ -17,23 +17,25 @@ classifiers =
|
||||
Topic :: Software Development :: Libraries
|
||||
Environment :: Console
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
||||
Topic :: Internet
|
||||
# Topic :: Blockchain :: EVM
|
||||
license = GPL3
|
||||
license = AGPLv3+
|
||||
licence_files =
|
||||
LICENSE
|
||||
|
||||
[options]
|
||||
include_package_data = True
|
||||
python_requires = >=3.7,<3.10
|
||||
python_requires = >=3.8
|
||||
packages =
|
||||
eth_monitor
|
||||
eth_monitor.importers
|
||||
eth_monitor.filters
|
||||
eth_monitor.runnable
|
||||
eth_monitor.mock
|
||||
eth_monitor.cli
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
eth-monitor = eth_monitor.runnable.sync:main
|
||||
eth-monitor-sync = eth_monitor.runnable.sync:main
|
||||
|
11
setup.py
11
setup.py
@ -21,8 +21,19 @@ while True:
|
||||
test_requirements.append(l.rstrip())
|
||||
f.close()
|
||||
|
||||
f = open('README.md', 'r')
|
||||
description = f.read()
|
||||
f.close()
|
||||
|
||||
man_dir = 'man/build'
|
||||
setup(
|
||||
install_requires=requirements,
|
||||
tests_require=test_requirements,
|
||||
data_files=[("man/man1", [
|
||||
os.path.join(man_dir, 'eth-monitor.1'),
|
||||
os.path.join(man_dir, 'eth-monitor-sync.1'),
|
||||
]
|
||||
)],
|
||||
long_description=description,
|
||||
long_description_content_type='text/markdown',
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
eth_tester==0.5.0b3
|
||||
py-evm==0.3.0a20
|
||||
rlp==2.0.1
|
||||
eth_tester==0.10.0b4
|
||||
py-evm==0.10.0b4
|
||||
rlp==3.0.0
|
||||
pytest==6.0.1
|
||||
coverage==5.5
|
||||
|
160
tests/rules/test_base.py
Normal file
160
tests/rules/test_base.py
Normal file
@ -0,0 +1,160 @@
|
||||
# standard imports
|
||||
import logging
|
||||
import unittest
|
||||
import os
|
||||
|
||||
# local imports
|
||||
from eth_monitor.rules import *
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class TestRule(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.alice = os.urandom(20).hex()
|
||||
self.bob = os.urandom(20).hex()
|
||||
self.carol = os.urandom(20).hex()
|
||||
self.dave = os.urandom(20).hex()
|
||||
self.x = os.urandom(20).hex()
|
||||
self.y = os.urandom(20).hex()
|
||||
self.hsh = os.urandom(32).hex()
|
||||
|
||||
|
||||
def test_address_include(self):
|
||||
data = b''
|
||||
outs = [self.alice]
|
||||
ins = []
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
outs = []
|
||||
ins = [self.alice]
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
outs = []
|
||||
ins = []
|
||||
execs = [self.x]
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
data = b'deadbeef0123456789'
|
||||
data_match = [data[:8]]
|
||||
rule = RuleMethod(data_match)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, b'abcd' + data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
rule = RuleData(data_match)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, b'abcd' + data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
|
||||
def test_address_exclude(self):
|
||||
data = b''
|
||||
outs = [self.alice]
|
||||
ins = []
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
|
||||
c = AddressRules()
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
c = AddressRules(include_by_default=True)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
outs = []
|
||||
ins = [self.alice]
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules(include_by_default=True)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
outs = []
|
||||
ins = []
|
||||
execs = [self.x]
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules(include_by_default=True)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
data = b'deadbeef0123456789'
|
||||
data_match = [data[:8]]
|
||||
rule = RuleMethod(data_match)
|
||||
c = AddressRules(include_by_default=True)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, b'abcd' + data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
rule = RuleData(data_match)
|
||||
c = AddressRules(include_by_default=True)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.x, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, b'abcd' + data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, b'abcd', self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
|
||||
def test_address_include_exclude(self):
|
||||
data = b''
|
||||
outs = [self.alice]
|
||||
ins = []
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
c.exclude(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
90
tests/rules/test_greedy.py
Normal file
90
tests/rules/test_greedy.py
Normal file
@ -0,0 +1,90 @@
|
||||
import logging
|
||||
import unittest
|
||||
import os
|
||||
|
||||
# local imports
|
||||
from eth_monitor.rules import *
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
|
||||
|
||||
class TestRule(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.alice = os.urandom(20).hex()
|
||||
self.bob = os.urandom(20).hex()
|
||||
self.carol = os.urandom(20).hex()
|
||||
self.dave = os.urandom(20).hex()
|
||||
self.x = os.urandom(20).hex()
|
||||
self.y = os.urandom(20).hex()
|
||||
self.hsh = os.urandom(32).hex()
|
||||
|
||||
|
||||
def test_greedy_includes(self):
|
||||
data = b''
|
||||
outs = [self.alice]
|
||||
ins = [self.carol]
|
||||
execs = []
|
||||
rule = RuleSimple(outs, ins, execs, match_all=True)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.carol, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.alice, self.carol, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
rule = RuleSimple(outs, ins, execs)
|
||||
c = AddressRules(match_all=True)
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.alice, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
r = c.apply_rules_addresses(self.bob, self.carol, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
r = c.apply_rules_addresses(self.alice, self.carol, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
|
||||
def test_greedy_data(self):
|
||||
data = os.urandom(128).hex()
|
||||
data_match_one = data[4:8]
|
||||
data_match_two = data[32:42]
|
||||
data_match_fail = os.urandom(64).hex()
|
||||
data_match = [data_match_one]
|
||||
|
||||
rule = RuleData(data_match, match_all=True)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
data_match = [data_match_two]
|
||||
rule = RuleData(data_match, match_all=True)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
data_match = [data_match_two, data_match_one]
|
||||
rule = RuleData(data_match, match_all=True)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertTrue(r)
|
||||
|
||||
data_match = [data_match_two, data_match_fail, data_match_one]
|
||||
rule = RuleData(data_match, match_all=True)
|
||||
c = AddressRules()
|
||||
c.include(rule)
|
||||
r = c.apply_rules_addresses(self.alice, self.bob, data, self.hsh)
|
||||
self.assertFalse(r)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user