Giter VIP home page Giter VIP logo

trin's Introduction

Trin

Trin is a Rust implementation of a Portal Network client.

The Portal Network is still in the research phase, and this client is experimental.

Do not rely on Trin in a production setting.

NOTE: Unix-only

Trin currently only runs on Unix-based platforms (Linux, macOS). We plan to eventually implement support for Windows, but until then do not expect any support for issues on Windows.

How to use Trin

Check out the Trin book to quickly get up and running with Trin.

NOTE: This project uses Git Submodules. If you just cloned the project, be sure to run: git submodule update --init. See our dedicated page for more info.

Experimental Status

Trin is a prototype Portal Network client. This implementation and the Portal Network specifications will continue to co-evolve.

In this stage of development, Trin lacks comprehensive data validation.

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for contributing in the Trin book, then check out issues that are labeled Good First Issue.

Join our Discord to participate in the conversation!

trin's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trin's Issues

Configure network IP address to listen on

Not sure how "production ready" we're targeting with this, but if we're getting the listen port from the config, it would also make sense to get the bind address, no?

Originally posted by @SamWilsn in https://github.com/carver/trin/pull/26#discussion_r641847423

Sure, I mean there are a great deal of things between here and an alpha. I'd prefer to put this off until >=beta, since I'm not clear when it's important for someone to limit which address is being listened on. Configuring a port is immediately useful, to run two at the same time.

But presumably someone will want to do it someday, so we'll add a tracking issue.

Build issue with discv5 dependency

$ cargo build --release
    Updating git repository `https://github.com/quilt/discv5.git`
error: failed to resolve patches for `https://github.com/rust-lang/crates.io-index`

Caused by:
  failed to load source for dependency `discv5`

Caused by:
  Unable to update https://github.com/quilt/discv5.git?branch=trin-quilt#13c661ec

Caused by:
  object not found - no match for id (13c661eca2d26ea0d7c68eb63f2cc36eab51c1f4); class=Odb (9); code=NotFound (-3)

It seems the referred commit hash is not current anymore, since the trin-quilt branch was rebased. The Cargo.lock still refers to the old one.

My quick attempt was removing the Cargo.lock and/or running cargo update, but that will use a commit which is lacking TalkRequest and TalkReqHandler, and compilation fails.

Handle an inbound Portal WHOAREYOU

Current state

We try to ping a portal node now, but when we get a whoareyou, we... maybe try to send the same message back?

impl TalkReqHandler for ProtocolHandler {
    fn talkreq_response(&self, request: TalkRequest) {
        // Send the same request back to the sender!?
        if self.protocol_sender.send(request).is_err() {
            warn!("sending on disconnected channel");
        }
    }
}

Desired state

Send back a handshake packet: https://github.com/ethereum/devp2p/blob/6eddaf50298d551a83bcc242e7ce7024c6cc8590/discv5/discv5-wire.md#handshake-message-packet-flag--2

Save the session to a simple local list of sessions, for later decryption of messages.

Next up will be to resend the original message, encrypted with the new handshake parameters.

We can skip all the kbucket tracking for now, just a simple list of active sessions is sufficient for this issue.

devp2p should have plenty of reference methods for how to implement this, since it's essentially the same as the base-layer protocol. Maybe it's possible to even use some of the library methods directly?

Use STUN server to speed up the external socket address discovery

Add a client like stunclient to trin, and run rustun server with something like:

cargo run --release --example binding_srv

On launch, we can check our external address to create an ideal ENR much faster than waiting for a bunch of independent PONGs to come in. (It also simplifies bootstrapping, where we launch new peers and connect them to each other)

Switch STUN client to async

Follow up to #23

This might mean forking stunclient? There is an async method, but it looks old and may not be compatible with new-style futures. I'm not familiar enough with the history of async on rust to know without investigating.

JSON-RPC endpoints

Currently, on discv5 layer, we are supporting only discv5_NodeInfo and discv5_RoutingTableInfo json-rpc endpoints. Nick mentioned this API example in ddht.
Do we want to support most of the endpoints there if not all of them?

What about the dummy node in the testing framework, is there any json-rpc endpoint that we don't want to expose in the release candidate version, but we want to use it for dev work and testing purposes only?

Some random ideas

Hi,

I just came across this repo and I'm super excited about this it. It's what I was looking for for a while. I explored similar ideas in the past [1, 2].

I was wondering: Are there specific plans for the structure of the p2p network already. Since most of my problems with building scalable (web) dapps resulted in using centralized infrastructure for querying and caching Ethereum data, I'd find it quite useful to span the network across the web too. To serve my dapp today, I'm having to rent a 110 EUR Eth full node. I do so in the cloud :(

Hence, in most of my prior work, I looked mostly into two domains:

  1. What type of data requests can be cached by trusting the full node? I attempted to build a browser-based service worker cache and identified some RPC methods that return immutable data [2]. I'm now realizing that technically most eth_calls could also be cached, assuming the latest tag is always replaced with the block number.
  2. How could the number of requests to an Ethereum full node be reduced on the overall network? Yes (1) caching is one solution. But then also I thought that syndicating state data via webtorrent would be interesting.

I haven't done much research on (2), but I'd imagine that if enough nodes ran a p2p webtorrent network that is compatible both for system-level applications and browsers, much of the traffic that's currently directed to e.g. Infura could simply be served through torrent. In [1] I did a tiny tec spike to see if RPC requests could be treated as single webtorrent links - and concluded it was possible.

I'd find it quite exciting if web browsers became part of the Ethereum network. Suddenly, not only deliberate network participants would help to host the network: but all users too. Surely, browsers wouldn't raise to the service quality of dedicated servers. However, I imagine still lots of load could just be distributed.

Anyways, these are just some ideas that I have had in the past and that I wanted to contribute to this repository. I'm happy to iterate on them further if there's interest. Also: since this issue isn't really actionable, feel free to close it.

Best,
Tim

References

Improve quality of `FoundContent` enrs included in response

In discovery.rs, discovery.find_nodes_close_to_content should be updated to return the 32 nodes that are absolutely closest to the target content_key from its routing table. Currently, it returns the first 32 nodes it iterates over that are closer to the key than itself. Which means that the response could omit nodes that are available in the routing table and closer to the target than the nodes included in the response.

Whether to use joint or isolated binaries, to orchestrate multiple networks

The "portal network" is effectively a collection of networks, each with different protocols (for example: state, chain history, and proposed transactions...). The json-rpc endpoint needs to be a single service that a web3 client can communicate with. That endpoint also needs to synthesize results from each protocol. So, how do we architect this choice.

A couple of obvious options are:

  1. A joint binary, probably built with isolated crates, that can serve the json-rpc endpoint and make direct API calls for each protocol
  2. A constellations of independent binaries, perhaps communicating over IPC or TCP ports, where a single orchestrating binary launches the json-rpc server, as well as spawning a binary for each protocol

Testing framework for p2p networking tests

The peer to peer interactive tests are pretty minimal right now, because of the difficulty of orchestrating them. It would be a huge win to have a testing framework that made it easy to test peer-to-peer network calls.

There is a lot of flexibility here on how to do it. Some possibilities to consider:

  • make a transparent proxy that records the traffic -- probably not: everything is encrypted, so verifying the traffic is tough
  • run two nodes as a black box and grep the logs for expected values
  • run two nodes as a black box and query the database for its new state
  • run a dummy peer that is responsible for talking to the target client, and provoking expected results
  • internal to trin, mock a communications channel to test the expected back and forth -- probably not: to specific to trin, and skips testing a lot of important networking code

It would be really cool if whatever solution we come up with would work for cross-client testing.


The very first step is to run this test manually. That probably leads us down the path of grepping the logs for the test framework, since looking through the logs is a natural way to run a manual test. Be sure to review https://docs.rs/env_logger/0.9.0/env_logger/#enabling-logging to see how to turn on debug logs for networking specifics, but not flood the screen with all the debug logs. You might need to add your own code to emit logs relevant to the networking.

Trin db strategy

This is just an issue to track an ongoing design decision regarding trin and its db strategy.
tl;dr: Is there one central db that subnets share, or is each subnet responsible for its own db.

Some thoughts so far...
@jacobkaufmann

As far as the database stuff goes, my assumption is that we would require multiple database instances because we would need to manage the storage capacity for each subnetwork individually. For example, a user may wish to allocate X space to state and Y space to history. I'm not sure this is possible with a single database instance, but I haven't looked into the RocksDB crate enough to be absolutely sure.

@mrferris

On the topic of storage, my thoughts are that we have separate database instances per network, each abstracted by a separate PortalStorage instance (#69) per network.
My main reasoning is simplicity. If we have every network's data combined in one DB instance, we'd need to store metadata on each key to denote which network it belongs to. The PortalStorage struct would need to get more complex in order to keep track of everything for every network at the same time....or we could have multiple PortalStorage instances interacting with a single database instance, in which case we need to manage locking etc...debugging storage functionality also gets more complex with everything in one RocksDB.
Is there a benefit to combining the databases that I might be missing? I'm only seeing the extra complexity. That said, I have yet to look into the effects of multiple DBs vs 1 DB on disk i/o, so that might be what I'm missing.

@carver

I think most users won't want to manage the storage space of each subnetwork individually (they probably won't even know the internal detail that there are multiple subnetworks). Think of a typical bittorrent user, and how little they probably know about the details of the protocol.
Even if we decide to implement this per-network limit, I'm sure we could find other ways than running multiple DBs. So I think we should at least remain open to the idea of piping everything into a single DB (which I expect to have performance benefits due to I/O serialization).
Yup, that's exactly the issue. Some client (Erigon?) had to do a big rewrite to a single DB in order to avoid causing big I/O delays by trying to hammer the disk a bunch in parallel from different threads.
FWIW, I think we're going to have to deal with locking logic anyway, even within a single subnet.

Separate crate for handling common code

Because I’m starting to work on implementing CLI parsing support for both networks, I’m thinking of what is the best way to structure the common code.

As we are moving forward to workspace architecture and because circular dependencies in packages are not possible (we can’t import root module in the current network packages, because the root depends on them), If we want more than one package to use the same module, it seems natural to just have a third common package, and both state and history to depend on it.

Let me know if this makes sense and we can add the third package to https://github.com/carver/trin/pull/57

Initial Integration tests for HTTP / IPC servers

From https://github.com/carver/trin/pull/2#issuecomment-791053427

A natural follow-up will be to figure out a way to do integration tests on the http and ipc servers. Maybe we can hit only the clientVersion endpoint for now, though we'll obviously want to test Infura endpoints eventually. Also, we'll probably want arbitrary backing URLs. That's important soon, so we don't hit Infura for real during tests, and later so we can support any type of endpoint (like alchemyapi.io).

Strategy for handling CLI arguments for selecting subnetworks.

This issue is to track the discussion about the ideal way to structure cli arguments. Basically....
cargo run --networks history,gossip or cargo run --history --gossip

Some thoughts to consider...

  • Different subnets require different arguments.
    • This is just an example, but it's probably safe to assume there might end up being a fair amount of cli options for trin and its subnets..
      • cargo run --networks history,gossip --history-udp 8086 --gossip-udp 8087 ...
      • cargo run --history --history-udp 8086 --gossip --gossip-udp 8087 --max-gossip-db-size 1gb ...
    • This raises the question that at some point, we'll probably want to support (and encourage) the use of a config file to set the different params to avoid cli arg congestion
  • What are our expectations for the average end-users understanding of trin's internals? How much custom configuration are they expected to want control over. What are the realistic use cases where an end user would only want to run a single (or subset of) subnet(s)?

Set up domain for STUN server

Follow up to #23

Set up the STUN server to run on a domain that can be configured to round-robin to multiple servers, or at least switched if we find that the current one is down.

Note that the current setup is on a floating IP so we should be able to reassign to a new server if we need to spin one up, in a pinch Hm, nope: using a floating IP borks the STUN request with:

[ERROR] Other (cause; assertion failed: `left == right`; assertion failed: `(left == right)` (left: `1581330`, right: `554869826`): Unexpected MAGIC_COOKIE: actual=0x00182112, expected=0x2112a442)
HISTORY:
  [0] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/stun_codec-0.1.13/src/message.rs:266
  [1] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/stun_codec-0.1.13/src/message.rs:295 -- message_type=Type { class: Request, method: Method(650) }, message_len=1, transaction_id=TransactionId(0xA442F3376B9F6D7FC245542D)
  [2] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecodec-0.4.14/src/combinator.rs:1368
  [3] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/stun_codec-0.1.13/src/message.rs:430
  [4] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/bytecodec-0.4.14/src/decode.rs:448
  [5] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/fibers_transport-0.1.3/src/udp.rs:199 -- peer=50.0.92.2:9000
  [6] at /root/.cargo/registry/src/github.com-1ecc6299db9ec823/fibers_transport-0.1.3/src/error.rs:19 -- original_error_kind=InvalidInput
  [7] at src/transport/udp.rs:358
  [8] at src/transport/udp.rs:193
  [9] at src/channel.rs:176
  [10] at src/error.rs:61 -- original_error_kind=CodecError
  [11] at src/server.rs:340

JSON-RPC handler does not support the Overlay protocol

Currently, the main JSON-RPC handler is initialized with discovery protocol, before the initialization of the overlay networks.

The problem of this implementation is that the handler has access only to the underlying discv5 protocol which prevents us from serving network specific endpoints. Examples of such endpoints could be requesting the overlay data radius value or modifying records in the overlay routing table.

One possible way to refactor this is to first initialize the overlay networks and then pass them to the network specific json-rpc handlers.

Content-id derivation function

So in order to get the content-id from the content-key, there is some function that maps the key to the id. On the history network, this is just simply taking the sha256 hash of the entire key. On the state network, it is more complicated since depending on the type of content, we may use different hash functions, which may hash different portions of the content-key.

So I am thinking this method will be defined in utils.rs and it will take in a key as parameter, and return the id after performing appropriate computation. To do the right computation, it needs to determine 1) if this key is for content from history or state and 2) if from state, what type of content exactly is it (account trie node, contract trie node).

For 2), the function can just extract the first byte from the key, since that is the content_type and be done with it there. For 1, I am not so sure how to do this. If the only parameter is the key, how can we differentiate between the networks?

Cannot terminate process

When I run cargo run -p trin it launches both state and history networks, but there is no way to cancel it. If you then try to run the state or history networks individually, like cargo run -p trin-state, it leads to a (non-terminating) error of code 98, "Address already in use". This is even after I already killed the previous process.

Track State Network peers independently from discv5 base layer

As I imagine it, that means: replicating (reusing?) the Kademlia bucket code used in the discv5 crate, but with state-net clients only. Presumably, we also want to do the ping/pong to ensure liveness, and the network crawl to try to fill the buckets.

So that would break down into:

  • A new set of Kademlia buckets for tracking
  • Use ping to ensure peer liveness
  • Respond to the overlay "find nodes" using peers from the new buckets
  • Crawl the peers in the overlay network to fill buckets
  • When "found content" has an empty payload, use the new buckets to fill it (ie~ in find_nodes_close_to_content())

This is a fairly large issue, bigger than one PR presumably. So if whoever works on this wants to open separate issues for each task I'd 👍🏻 . Otherwise, I'm happy to split this into subtasks after someone submits the first PR (maybe just the first bullet above?). I think that splitting, and identifying next tasks, will be easier once there's a bit more structure.

Verify Advertise messages against state root block headers

Peers should verify the proposed state received in an Advertise. One important part of the verification is that the advertised state has the same state root as defined by a header.

A few unresolved items come up:

  • Advertise sends a list of hashes, but doesn't explicitly claim a state root. That leaves the receiving peer to guess and check all the hashes against the header state roots, I suppose
  • Ultimately we want to have access to the headers using a gossip network adjacent to the state network. For now, we might just poll Infura for updates to the header chain.

Maybe we should consider modifying the Advertise message. Some options that come to mind:

  • to include a new entry with the new state root (which ought to be a duplicate with one of the hashes, or maybe we exclude it from the hash list
  • make it required that the first hash sent with Advertise is the new state root
  • include the header hash that the new state is generated in

Also, how frequently can we poll for the latest header, using a free Infura (or Alchemy) account? (If it's continuous all month? If it's only running for ~3 hours a day?)

Subnetwork overlay protocols implementation

  • How are the subnetwork overlay discv5 protocols instantiated?
  • What responsibilities does the orchestrator have, and what is needed in each subnetwork initialization to kick off their overlay networks?
  • If a user runs a subnetwork as a solo binary, will it still work?

Implement --ephemeral flag

When testing internal p2p network calls, we would like to be able to run multiple node instances simultaneously.
To avoid db folder collusion, we want to set unique db folder name for every node id.

As a default db folder, current implementation is using last 8 characters of base64 encoded Enr appended to the application name:
https://github.com/carver/trin/blob/d99232fd43c8dd0ec055150f382546cbba747bef/trin-core/src/utils.rs#L58

It will be better, if we change this to append the first few characters (instead of last 8) of the as-displayed node-id to the application name.

Explore why `Run Clippy` job in CI is taking so long

Right now the Run Clippy job on my current ci build took 8 1/2 minutes. Seems way too long, it would be nice if somebody could explore our options for cutting this down. It seems as if clippy is checking every single dependency, which imo is completely unnecessary, and a waste of a time sitting around waiting for the ci check to complete.

Testnet Details

This issue is to track a discussion around the initial details for the inaugural trin testnet. Once the open questions are settled, this will be moved to a milestone with accompanying issues to track progress. The lists below are just a starting point - feel free to make any suggestions to add/remove/edit and I'll update the list accordingly.

Goal

Establishing a test network that is purely the overlay network (ping /ping/find/found) with nodes able to build and maintain their routing tables. That lets us start deploying longer lived nodes and to iterate in a production-like environment.

Open Questions

  • How many nodes do we want in the testnet?
    • 32 for now - and capacity to expand to 256 once we start serving real content
  • What's the best way to monitor testnet? Would a dashboard of some kind be useful?
  • What kind of cloud resources are required? How much storage should each node provide?
  • Deployment scripts. Fab? K8s?
  • Updating testnet nodes - Do we want to establish a trin release cycle with some regular cadence (2 / 3 / 4 weeks)?

Todos

  • Request cloud resources from the EF
  • Complete v0.1 features & cut release
  • Deploy testnet
  • Monitor

v0.1 features

Use same private keys across runs of trin

Eventually, we'd want the ability to use known private keys rather than generating new ones every time, right? aka. something like the --private-key flag in the python implementation.

Originally posted by @njgheorghita in https://github.com/carver/trin/pull/14#discussion_r630849366

Right, and even if you don't supply keys, maybe we don't want to regenerate them every time, because that would probably cause a lot of churn in which state we store (assuming it's related to our node id). So probably we'd want to save/load from a file, if it's not supplied in a cmd line flag.

Keep current JSONRPC implementation or use library

@ogenev

My concern about the current json-rpc implementation, is that we may need to drop it soon 😄. I'm currently playing with paritytech's json-rpc library and I'm going to implement it in the testing framework branch. I guess next week we will have more clarity if we want to switch to this library or not. (looks promising so far, better error handling, logging and of course less boilerplate code).

Message subscription and dispatch

My model of rust's execution model is minimal at best. Any help in mapping this into rust conventions is appreciated.

In the current code we spawn a background process process_discv5_requests that handles incoming requests:

tokio::spawn(events.process_discv5_requests());

The process_discv5_requests enters an infinite loop, picking up one inbound packet, checking that it is a TalkRequest and then handling it in process_one_request which returns a response message which is then dispatched back to the original sender.

The handling process is implemented with a match against each of the different message types, constructing the response packet and returning it.

This architecture is unlikely to facilitate a clean and rubust client for multiple reasons.

Doing work after handling a request

We need the ability to do work after dispatching the response packet. An example:

The OFFER/ACCEPT message pair are used to initiate a uTP stream over which the actual data will be transferred. For this to work, the node receiving the OFFER message will send back an ACCEPT message with a connection-id for the uTP stream. After sending the ACCEPT back the receiving node will need to listen for an incoming uTP stream with that connection-id. There is likely additional validation needed after receiving the whole payload. Timeouts will also be a thing during this sequence of messages.

If the handling of the message happens within the main message handling loop, then we will end up blocking the processing of messages until this sequence has completed.

Doing maintenance work.

Every PONG message containes an ENR sequence number. Every time we receive a pong, we should check whether the sequence number is newer than our local record. In the event of a higher sequence number we need to request an updated ENR record from the node.

PING messages are going to be used in a number of places. One specific use is validating liviliness which happens in a few different contexts. We do not want to copy/paste this sequence number checking code across these places. We also do not want to have to remember to trigger this process each time we use the PING message.

The only place where PONG messages can currently be handled in the current architecture is at the call site where the ping was sent, which is very limiting.

The solution to this is to implement an API that allows subscriptions to specific message types. This allows us to spawn a background process which subscribes and is given a chance to process every inbound PONG message.

This same subscription API can also easily be used to implement standard handling of request messages. Here is the handling of inbound PING requests in the python implementation of this:

https://github.com/ethereum/ddht/blob/142911d134ff839f3f79ff8fe9e45d3fe5a58cd0/ddht/v5_1/alexandria/network.py#L290-L308

The suggested architecture

Starting with the main stream of TALKREQ and TALKRESP messages.

def process_requests(...):
    for message in stream_of_messages:
        subscription_manager.handle_message(message)

The subscription_manager in this case is doing something like this:

class SubscriptionManager:
    def handle_message(message):
        subscriptions = self.get_subscriptions_for_message_type(type(message))
        for subscription in subscriptions:
            # the message just gets put in a queue to be consumed elsewhere
            subscription.append(message)

What this facilitates is the ability to implement message handling as long running background processes, instead of doing it in the main handling loop. This ensures that the main handling loop isn't blocked by messages that take a long time to process.

Set-up CI

  • linting #11
    • rustfmt
    • clippy
    • anything else?
  • automated test run
  • anything else?
  • steal the setup from fe?

Troubleshooting trin startup issue after discv5 version bump

I'm having trouble getting trin up and consistently running after rebasing my various branches to the latest commit. It seems to be the port to the new discv5 0.1.0-beta.5 version. But, essentially my trin client isn't starting. Never experienced this kind of issue with trin before. I've rolled back to the pre discv5 version bump, and things appear to be working correctly. Is this an issue anybody else is experiencing? @SamWilsn @carver ? Maybe there's some new configuration required that I'm missing? My best, but most likely inadequate, debugging attempts haven't revealed much insight into what's going wrong, but it seems like the cause might be somewhere within the discv5 library.

Jul 01 16:35:07.940  INFO trin: About to spawn portal p2p with boot nodes: []
Jul 01 16:35:17.943 DEBUG trin::portalnet::socket: STUN claims that public network endpoint is: Err("Timed out waiting for STUN server reply")
Jul 01 16:35:17.951  INFO trin::portalnet::discovery: Starting discv5 with local enr encoded=enr:-IS4QKXQytRMuNJcJiING0ORNuGWhBgtS0Y2qSeydxUIqI88PCQ_k6txUKsPHKcTGEPI7Sqas37q9z-nEsM76-2CNoYBgmlkgnY0gmlwhMCoAWSJc2VjcDI1NmsxoQKMMoaHJ0mz-Jv1uCJpRHV5rFNoAxwtAwVMTJJ_EsLrcoN1ZHCCIyg decoded=ENR: NodeId: 0xf3d0..abcf, Socket: Some(192.168.1.100:9000)
Jul 01 16:35:17.954 DEBUG discv5::discv5: Service is already shutdown
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: "ServiceNotStarted"', src/main.rs:53:80
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Handling subnetwork bootnodes

We have bootnodes for the base portal discovery protocol, and for the various subnetwork routing tables.
What's the best way to handle these bootnodes?

A bootnode for the history subnet, might not be available on the state subnet.

  • Users can submit bootnodes via the cli arg --bootnodes. We could expand this to accept different bootnodes for different subnetworks. But, my instinct is that maybe that's a bit excessive for the user to concern themselves with. And this should be generally understood as a bootnode for any network, 'portal', 'portal:state', 'portal:history', etc...
  • Subnets should be spawned only after a bootnode for a particular subnet has been located. It seems to me like we need to implement some logic, before spawning a subnet, to ping all available bootnodes / peers until we find one (or multiple) that responds for that subprotocol, and then use that as the bootnode(s) for spawning the subnet.

Support JSON-RPC params member

Current json-rpc implementation does not support passing parameter values to the request object.
To provide an adequate json-rpc API, we want to support passing parameters which will used during the invocation of the methods.

Full JSON-RPC 2.0 specification is provided here.

Design an API for decoding and handling responses

Currently, although we have the code to send various protocol messages and also decode and handle these requests and send responses, we do not have an implementation of decoding and handling these responses. Right now, all we have is Discv5 returning the talkrequest response as a slice of bytes. We need an API to decode these responses and then handle them, such as storing the content for a FoundContent response, for example. A couple ideas:

  1. Within the process_one_request() method, we could have add lines to match Response messages as well, and then have the appropriate handlers in the rest of the code. For example, in order to handle responses, we could have:
let request = match Message::from_bytes(talk_request.body()) {
            // Ok(Message::Response(r)) => r
            Ok(Message::Request(r)) => r,
  1. Within the impl PortalnetEvents block, we could just have another function process_one_response() which is analogous to the process_one_request() method but has handlers to response messages, which may call other functions that perform some action.

The first method probably doesn't make much sense, since it would clutter up a buncha code. The second seemed to me like the obvious way to do things, but there are probably other ways as well.

Fix win-build circleci job

In circle-ci runs, the win-build job doesn't run. Seems like we either need to add credits to carver's account or migrate this repo over to the ethereum org. @carver any thoughts?

Screen Shot 2021-08-13 at 1 23 55 PM

Refactor duplicated logic in http/ipc servers.

https://github.com/carver/trin/pull/2/files#r590773236

Oof, there's a lot of duplicated logic in here between the two servers. Not sure the best way to DRY off-hand. Maybe make a function that takes in the json iterator and returns an iterator over the result values.

Also improve input data validation

I know the error handling was already this way in the IPC code, but it's bad news. Sending bad input data to trin should reply with an error, instead of crashing trin.

https://github.com/carver/trin/pull/2/files#diff-b2812f19576dd53d0c35b107a322f58a00fce6977f4b5976e1961853982af3ccR199-R203

Adding implementation to block accumulator with the ability to reorg [WIP]

Currently I have created a separate repo that has a basic accumulator implementation.

https://github.com/chee-chyuan/accumulator_v3

The repo aims to generate test vectors for the master and epoch accumulator.

The following Ssz sedes were used:
master_sede = List[epoch_hash:bytes32, 16777216]
epoch_sede = List[Container[block_hash:bytes32, total_difficulty:uint256],2048]

Randomly selected epoch hashes:
Epoch 1 - 0x73819a452bf470f23c49a9a57386f516292c0f8ec5aca9b14a3ff4ff290ba238.
Epoch 100 - 0xe3195cd7fb347734751e7e5960977473e6f5731c85289a678c9e93b3ebb98d2c
Epoch 200 - 0xfd3998a95f49ef3060557e4ac4492c8e26a784b37ffdaef006e3f97cc51342a5
Epoch 300 - 0x377d0f7e6b416670fb9bf8119e95c8b8ed6fb52a17d27023fafced53f0256035
Epoch 400 - 0xe7a537f748049b61898e062224db09fd59d98fea7b1084f1f2fd4f69ea337772

Block header validity function

Implement the block header validity function defined in section 4.3.4 of the Ethereum Yellow Paper.

A node should perform basic block header validation to ensure that it only stores and transmits valid Ethereum block headers.

Without the accumulator, we cannot validate that a block is part of the canonical chain. Temporarily, we could make a request to Infura for a given block hash.

In memory storage cache for POKE

tracking this concept first mentioned in #69

In our on-site we discussed briefly how we may handle hot spots. Data such as the trie node for the Header.state_root are going to be accessed disproportionately more than other data. We want a mechanism that counteracts this.

Using Ephemeral LRU storage

The proposed solution is to use the POKE mechanism to spread the data out further in the network. The goal is that when a specific piece of data is in high demand, it will quickly spread across the network, making it available from a broader set of nodes. The problem that arises is that the radius mechanism will prevent the data from spreading beyond a certain point. Specifically, the data would stop spreading it hit the point where it would beyond the average radius from it's storage location.

We should probably not implement any of this until we see that it is actually a problem in the live network....

The solution to this would be to have a mechanism through which nodes could ephemerally store data that is outside of its radius but also in high demand. This data store could be managed using an LRU eviction mechanism, and accept any valid data that is being actively requested. To achieve this, a node would need to keep track of what content keys have been requested recently, and accept pokes for data in the recently requested data set. This solution isn't fully thought out since there's no real way to differentiate between actual high demand and artifical high demand executed as a sort of DOS eviction attack.

Alternatively, the cache could be managed in a slightly more complex way, only accepting data that is outside of the nodes radius, but with some sort of probability function for eviction which prefers the data which is closer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.