omgnetwork / plasma-mvp

OmiseGO's research implementation of Minimal Viable Plasma
MIT License
561 stars 158 forks source link

Add child chain validation to the client #32

Open DavidKnott opened 6 years ago

DavidKnott commented 6 years ago

The command line interface is able to hit the child chain server and receive blocks up until the most recent block, the next step is to add validation on the client side to make sure that each block being received from the child chain is valid (because they're not everyone should exit).

nvonpentz commented 6 years ago

Thanks for all your work on this. I have a few thoughts.

The client will do many of the same transaction validations that the child chain server does. Instead of duplicating the validation logic in the client, is it favorable to modularize the validation code so it can be used in both?

Related, it looks like the child chain server doesn't have a database, and is storing the blockchain in memory. Is this intended?

https://github.com/omisego/plasma-mvp/blob/569e880178d20e589b35161e305c7e698e48129b/plasma/child_chain/child_chain.py#L17

This is different from the client which stores blocks in a database.

https://github.com/omisego/plasma-mvp/blob/569e880178d20e589b35161e305c7e698e48129b/plasma/cli/client.py#L50

Keeping this consistent may make it easier to reuse the code, if that's the direction you want to go.

smartcontracts commented 6 years ago

@nvonpentz @DavidKnott I definitely agree that the server should have a database. Whether or not the CLI should store state is dependent on what we want the CLI to do. My opinion is that the CLI should also be stateless and simply make calls to the server via the client wrapper.

Some of the design decisions behind the first iterations of Plasma will definitely effect what the client looks like. If we assume that the operator is centralized (for now), then our architecture should look something like this:

This has the effect that validation only happens in the child_chain, meaning we don't have to duplicate any code. If a block received from the operator fails to validate or the operator doesn't send a block, then the daemon will also automatically submit an exit via the local child_chain.

State would also only have to be stored by child_chain.

nvonpentz commented 6 years ago

@kfichter Thanks for the quick response. I like your approach; having the users and the operator run different instances of the same child chain that each individually store state makes sense to me.

Do you want to have the client keep track of a local child chain in addition to the child chain service that interacts with the operator's child chain:

https://github.com/omisego/plasma-mvp/blob/569e880178d20e589b35161e305c7e698e48129b/plasma/client/client.py#L17

This local child chain would store state as you described and the CLI wouldn't need to store state as it does currently:

https://github.com/omisego/plasma-mvp/blob/569e880178d20e589b35161e305c7e698e48129b/plasma/cli/client.py#L50

When the operator submits a new block to the root chain, the user should get the corresponding block from the operator's plasma chain (do you want to add a SubmitBlock event?). The user then checks if each of the transactions are valid by cross referencing it's own local child chain, and making sure the block hash matches the block hash submitted to the root chain. If there is no problems, add the block to the user's local child chain. Otherwise, it automatically initiates an exit.

Is that roughly what you had in mind?

smartcontracts commented 6 years ago

@nvonpentz

The user then checks if each of the transactions are valid by cross referencing it's own local child chain, and making sure the block hash matches the block hash submitted to the root chain. If there is no problems, add the block to the user's local child chain. Otherwise, it automatically initiates an exit.

Exactly. In the long term, the idea should be that the network is completely p2p. Operators broadcast blocks whenever they're submitted to the root chain. Users receive or request those blocks and double check them against their own local chain. Users will exit if something goes wrong (users see a hash published to the root chain but don't receive the block within some amount of time, block is invalid, etc.).

In the short term, this probably won't be a p2p network but a direct connection to the operator. That definitely opens the operator up to some DoS attacks, but that's fine for now.

smartcontracts commented 6 years ago

Okay, here's the plan:

Although the initial implementation won't be a p2p network, it makes sense to "pretend" that it's a p2p network where every client has only one peer (the operator).

Full nodes will run ChildChain as a stateful client. They'll also run a daemon that'll read blocks from the operator, insert each block into the child chain, and exit if anything ever goes wrong.

This also means that we need to come up with a better mechanism than simply listening events for Deposits and Exits. I think it would make sense if deposits and exits are only included in the child chain if the event happened at least N (whatever we determine almost certain finality to be) blocks ago. The simplest way to accomplish this is to run a background loop that reads all Deposit/Exit events that occurred between the blocks current_block - N*2 and current_block - N, and processes any events not yet seen.