ef84972e7c
This PR mirrors https://github.com/testinprod-io/op-erigon/pull/54. Actual implementation for `admin_addPeer` method. RPC Spec: Refer to https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin. > The addPeer administrative method requests adding a new remote node to the list of tracked static nodes. The node will try to maintain connectivity to these nodes at all times, reconnecting every once in a while if the remote connection goes down. Requires https://github.com/ledgerwatch/erigon-lib/pull/1033/ After https://github.com/ledgerwatch/erigon-lib/pull/1033 is merged, will update erigon-lib version, removing replace at go.mod. Note that even if RPC response returns `true`, it does not guarantee that RLPx protocol is established between peers. It just adds node entrypoint to its static peer list, and periodically tries and tests connections. ## Testing This RPC needs integration testing, so I made some scenario. Use below command for testing: Spin up two dev nodes which p2p enabled: Start Node 1: RPC running at port 8545: ```sh ./build/bin/erigon --datadir=dev --chain=dev --port=30303 --http.port=8545 --authrpc.port=8551 --torrent.port=42069 --no-downloader --nodiscover --private.api.addr=127.0.0.1:9090 --http --ws --http.api=admin --p2p.allowed-ports=30306,30307,30308 --authrpc.jwtsecret=/tmp/jwt1 --p2p.protocol=67,68 --log.console.verbosity=5 ``` Start Node 2: RPC running at port 8546: ```sh ./build/bin/erigon --datadir=dev2 --chain=dev --port=30304 --http.port=8546 --authrpc.port=8552 --torrent.port=42068 --no-downloader --nodiscover --private.api.addr=127.0.0.1:9091 --http --ws --http.api=admin --p2p.allowed-ports=30309,30310,30311 --authrpc.jwtsecret=/tmp/jwt2 --p2p.protocol=67,68 --log.console.verbosity=5 ``` Get nodeInfo of node 1 using `admin_nodeInfo` RPC: ```sh curl --location 'localhost:8545/' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc":"2.0", "method":"admin_nodeInfo", "params":[], "id":1 }' ``` Example response: ``` { "jsonrpc": "2.0", "id": 1, "result": { "id": "b75e0c4d2113b6f144ea8fd356a8f90e612a2a5f48a13c78d7e0e176e5724eb2", "name": "erigon/v2.47.0-dev-5d86cdb5/darwin-arm64/go1.19.6", "enode": "enode://05ab575d947f2d73065ea0f795dc2d96ed0ad603f3e730ab90dc881122d552c9f59ffcb148fe50546bec8b319daeb3c22ec02e7d12a7c4f2ac4cd26456a04a7c@127.0.0.1:30303?discport=0", ... ``` Get nodeInfo of node 2 using `admin_nodeInfo` RPC: ```sh curl --location 'localhost:8546/' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc":"2.0", "method":"admin_nodeInfo", "params":[], "id":2 }' ``` Example response: ``` { "jsonrpc": "2.0", "id": 2, "result": { "id": "32d721e4d75219b021d7f83235f1f1eb8b705d6f85e634bccde564b8f7f94d78", "name": "erigon/v2.47.0-dev-5d86cdb5/darwin-arm64/go1.19.6", "enode": "enode://1abb8579647779e13b7f68d18f9c776cbd29281841c7f950e9cf9afa996e31120a6f481cea8e90e0f42a0eb1aa00aeafee81c4bae6c31aa16810b795c6d6e069@127.0.0.1:30304?discport=0", ... ``` Call `admin_addPeer` RPC to node 2: ```sh curl --location 'localhost:8546/' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc":"2.0", "method":"admin_addPeer", "params":["enode://05ab575d947f2d73065ea0f795dc2d96ed0ad603f3e730ab90dc881122d552c9f59ffcb148fe50546bec8b319daeb3c22ec02e7d12a7c4f2ac4cd26456a04a7c@127.0.0.1:30303"], "id":2 }' ``` Example response: ``` { "jsonrpc": "2.0", "id": 2, "result": true } ``` Check peer info of node 1 using `admin_peers` RPC: ```sh curl --location 'localhost:8545/' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc":"2.0", "method":"admin_peers", "params":[], "id":1 }' ``` Example response: ``` { "jsonrpc": "2.0", "id": 1, "result": [ { "enode": "enode://1abb8579647779e13b7f68d18f9c776cbd29281841c7f950e9cf9afa996e31120a6f481cea8e90e0f42a0eb1aa00aeafee81c4bae6c31aa16810b795c6d6e069@127.0.0.1:55426", "id": "32d721e4d75219b021d7f83235f1f1eb8b705d6f85e634bccde564b8f7f94d78", "name": "erigon/v2.47.0-dev-5d86cdb5/darwin-arm64/go1.19.6", "caps": [ "eth/66", "eth/67" ], "network": { "localAddress": "127.0.0.1:30303", "remoteAddress": "127.0.0.1:55426", "inbound": true, "trusted": false, "static": false }, "protocols": null } ] } ``` --------- Co-authored-by: alex.sharov <AskAlexSharov@gmail.com> |
||
---|---|---|
.. | ||
cbor | ||
olddb | ||
privateapi | ||
prune | ||
db_interface.go | ||
kv_util.go | ||
Readme.md | ||
walk.go |
Ethdb
package hold's bouquet of objects to access DB
Words "KV" and "DB" have special meaning here:
- KV - key-value-style API to access data: let developer manage transactions, stateful cursors.
- DB - object-oriented-style API to access data: Get/Put/Delete/WalkOverTable/MultiPut, managing transactions internally.
So, DB abstraction fits 95% times and leads to more maintainable code - because it looks stateless.
About "key-value-style": Modern key-value databases don't provide Get/Put/Delete methods, because it's very hard-drive-unfriendly - it pushes developers do random-disk-access which is order of magnitude slower than sequential read. To enforce sequential-reads - introduced stateful cursors/iterators - they intentionally look as file-api: open_cursor/seek/write_data_from_current_position/move_to_end/step_back/step_forward/delete_key_on_current_position/append.
Class diagram:
// This is not call graph, just show classes from low-level to high-level.
// And show which classes satisfy which interfaces.
+-----------------------------------+ +-----------------------------------+
| github.com/erigontech/mdbx-go | | google.golang.org/grpc.ClientConn |
| (app-agnostic MDBX go bindings) | | (app-agnostic RPC and streaming) |
+-----------------------------------+ +-----------------------------------+
| |
| |
v v
+-----------------------------------+ +-----------------------------------+
| ethdb/kv_mdbx.go | | ethdb/kv_remote.go |
| (tg-specific MDBX implementaion) | | (tg-specific remote DB access) |
+-----------------------------------+ +-----------------------------------+
| |
| |
v v
+----------------------------------------------------------------------------------------------+
| ethdb/kv_abstract.go |
| (Common KV interface. DB-friendly, disk-friendly, cpu-cache-friendly. |
| Same app code can work with local or remote database. |
| Allows experiment with another database implementations. |
| Supports context.Context for cancelation. Any operation can return error) |
+----------------------------------------------------------------------------------------------+
| | |
| | |
v v v
+-----------------------------------+ +-----------------------------------+ +-----------------------------------+
| ethdb/object_db.go | | ethdb/tx_db.go | | ethdb/remote/remotedbserver |
| (thread-safe, stateless, | | (non-thread-safe, more performant | | (grpc server, using kv_abstract, |
| opens/close short transactions | | than object_db, method Begin | | kv_remote call this server, 1 |
| internally when need) | | DOESN'T create new TxDb object) | | transaction maps on 1 grpc stream |
+-----------------------------------+ +-----------------------------------+ +-----------------------------------+
| |
| |
v v
+-----------------------------------------------------------------------------------------------+
| ethdb/interface.go |
| (Common DB interfaces. ethdb.Database and ethdb.DbWithPendingMutations are widely used) |
+-----------------------------------------------------------------------------------------------+
|
|
v
+--------------------------------------------------+
| ethdb/mutation.go |
| (also known as "batch", recording all writes and |
| them flush to DB in sorted way only when call |
| .Commit(), use it to avoid random-writes. |
| It use and satisfy ethdb.Database in same time |
+--------------------------------------------------+
ethdb.AbstractKV design:
-
InMemory, ReadOnly:
NewMDBX().Flags(mdbx.ReadOnly).InMem().Open()
-
MultipleDatabases, Customization:
NewMDBX().Path(path).WithBucketsConfig(config).Open()
-
1 Transaction object can be used only within 1 goroutine.
-
Only 1 write transaction can be active at a time (other will wait).
-
Unlimited read transactions can be active concurrently (not blocked by write transaction).
-
Methods db.Update, db.View - can be used to open and close short transaction.
-
Methods Begin/Commit/Rollback - for long transaction.
-
it's safe to call .Rollback() after .Commit(), multiple rollbacks are also safe. Common transaction pattern:
tx, err := db.Begin(true, ethdb.RW)
if err != nil {
return err
}
defer tx.Rollback() // important to avoid transactions leak at panic or early return
// ... code which uses database in transaction
err := tx.Commit()
if err != nil {
return err
}
-
No internal copies/allocations. It means: 1. app must copy keys/values before put to database. 2. Data after read from db - valid only during current transaction - copy it if plan use data after transaction Commit/Rollback.
-
Methods .Bucket() and .Cursor(), can’t return nil, can't return error.
-
Bucket and Cursor - are interfaces - means different classes can satisfy it: for example
MdbxCursor
andMdbxDupSortCursor
classes satisfy it. If your are not familiar with "DupSort" concept, please read dupsort.md first. -
If Cursor returns err!=nil then key SHOULD be != nil (can be []byte{} for example). Then traversal code look as:
for k, v, err := c.First(); k != nil; k, v, err = c.Next() {
if err != nil {
return err
}
// logic
}
- Move cursor:
cursor.Seek(key)
ethdb.Database design:
- Allows pass multiple implementations
- Allows traversal tables by
db.Walk
ethdb.TxDb design:
- holds inside 1 long-running transaction and 1 cursor per table
- method Begin DOESN'T create new TxDb object, it means this object can be passed into other objects by pointer, and high-level app code can start/commit transactions when it needs without re-creating all objects which holds TxDb pointer.
- This is the reason why txDb.CommitAndBegin() method works: inside it creating new transaction object, pinter to TxDb stays valid.
How to dump/load table
Install all database tools: make db-tools
./build/bin/mdbx_dump -a <datadir>/erigon/chaindata | lz4 > dump.lz4
lz4 -d < dump.lz4 | ./build/bin/mdbx_load -an <datadir>/erigon/chaindata
How to get table checksum
./build/bin/mdbx_dump -s table_name <datadir>/erigon/chaindata | tail -n +4 | sha256sum # tail here is for excluding header
Header example:
VERSION=3
geometry=l268435456,c268435456,u25769803776,s268435456,g268435456
mapsize=756375552
maxreaders=120
format=bytevalue
database=TBL0001
type=btree
db_pagesize=4096
duplicates=1
dupsort=1
HEADER=END