From e40e331f87c20cf6e28e52b34fdaeda4c43d13e5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 14 Jul 2017 19:06:38 +0200 Subject: [PATCH] Flesh out quark glossary --- docs/quark/glossary.md | 164 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/docs/quark/glossary.md b/docs/quark/glossary.md index 91162b403..5a905d03b 100644 --- a/docs/quark/glossary.md +++ b/docs/quark/glossary.md @@ -1,7 +1,10 @@ # Glossary This defines many of the terms that are used in the other documents. If there -is every a concept that seems unclear, check here. +is every a concept that seems unclear, check here. This is mainly to provide +a background and general understanding of the different words and concepts +that are used. Other documents will explain in more detail how to combine +these concepts to build a particular application. ## Transaction @@ -19,6 +22,17 @@ repos), to easily embed an arbitrary Tx inside another without specifying the specific type, and provide an automatic json representation to provide to users (or apps) to inspect the chain. +Note how we can wrap any other transaction, add a fee level, and not worry +about the encoding in our module any more? + +```Go +type Fee struct { + Fee coin.Coin `json:"fee"` + Payer basecoin.Actor `json:"payer"` // the address who pays the fee + Tx basecoin.Tx `json:"tx"` +} +``` + ## Context As the request passes through the system, it can pick up information, that must @@ -70,14 +84,160 @@ get worried....) ## Handler +The ABCI interface is handled by `app`, which translates these data structures +into an internal format that is more convenient, but unable to travel over the +wire. The basic interface for any code that modifies state is the `Handler` +interface, which provides four methods: + +```Go + Name() string + CheckTx(ctx Context, store state.KVStore, tx Tx) (Result, error) + DeliverTx(ctx Context, store state.KVStore, tx Tx) (Result, error) + SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error) +``` + +Note the `Context`, `Store`, and `Tx` as principal carriers of information. And +that Result is always success, and we have a second error return for errors +(which is much more standard go that `res.IsErr()`) + +The `Handler` interface is designed to be the basis for all modules that +execute transaction, and this can provide a large degree of code +interoperability, much like `http.Handler` does in golang web development. + ## Middleware +Middleware is a series of processing steps that any request must travel through +before (and after) executing the registered `Handler`. Some examples are a +logger (that records the time before executing the tx, then outputs info - +including duration - after the execution), of a signature checker (which +unwraps the tx by one layer, verifies signatutes, and adds the permissions to +the Context before passing the request along). + +In keeping with the standardazation of `http.Handler` and inspired by the +super minimal [negroni](https://github.com/urfave/negroni/blob/master/README.md) +package, we just provide one more `Middleware` interface, which has an extra +`next` parameter, and a `Stack` that can wire all the levels together (which +also gives us a place to perform isolation of each step). + +```Go + Name() string + CheckTx(ctx Context, store state.KVStore, tx Tx, next Checker) (Result, error) + DeliverTx(ctx Context, store state.KVStore, tx Tx, next Deliver) (Result, error) + SetOption(l log.Logger, store state.KVStore, module, key, value string, next Optioner) (string, error) +``` + ## Modules -## Dispathcer +A module is a set of functionality that is more or less self-sufficient. It +usually contains the following pieces: + +* transaction types (either end transactions, or transaction wrappers) +* custom error codes +* data models (to persist in the kv store) +* handler (to handle any end transactions) +* middleware (to handler any wrapper transactions) + +To enable a module, you must add the appropriate middleware (if any) to the +stack in main.go, as well as adding the handler (if any) to the dispatcher. +One the stack is compiled into a `Handler`, then all tx are handled by the +proper module. + +## Dispatcher + +We usually will want to have multiple modules working together, and need to +make sure the correct transactions get to the correct module. So we have have +`coin` sending money, `roles` creating multi-sig accounts, and `ibc` following +other chains all working together without interference. + +After the chain of middleware, we can register a `Dispatcher`, which also +implements the `Handler` interface. We then register a list of modules with +the dispatcher. Every module has a unique `Name()`, which is used for +isolating its state space. We use this same name for routing tx. Each tx +implementation must be registed with go-wire via `TxMapper`, so we just look +at the registered name of this tx, which should be of the form +`/xxx`. The dispatcher grabs the appropriate module name from + the tx name and routes it if the module is present. + +This all seems a bit of magic, but really just making use of the other magic +(go-wire) that we are already using, rather than add another layer. The only +thing you need to remember is to use the following pattern, then all the tx +will be properly routed: + +```Go +const ( + NameCoin = "coin" + TypeSend = NameCoin + "/send" +) +``` ## IPC (Inter-Plugin Communication) +But wait, there's more... since we have isolated all the modules from each +other, we need to allow some way for them to interact in a controlled fashion. +Some examples are the `fee` middleware, which wants to deduct coins from +the calling account (in the `coin` module), or a vote that requires a payment. + +If we want to make a call from the middleware, this is relatively simple. +The middleware already has a handle to the `next` Handler, which will +execute the rest of the stack. It can simple create a new SendTx and pass +it down the stack. If it returns success, then do the rest of the processing +(and send the original tx down the stack), otherwise abort. + +However, if one `Handler` inside the `Dispatcher` wants to do this, it +becomes more complex. The solution is that the `Dispatcher` accepts not +a `Handler`, but a `Dispatchable`, which looks like a middleware, except +that the `next` argument is a callback to the dispatcher to execute a +sub-transaction. If a module doesn't want to use this functionality, +it can just implement `Handler` and call `stack.WrapHandler(h)` to convert +it to a `Dispatchable` that never uses the callback. + +One example of this is the counter app, which can optionally accept a payment. +If the tx contains a payment, it must create a SendTx and pass this to the +dispatcher to deduct the amount from the proper account. Take a look at +[counter plugin](https://github.com/tendermint/basecoin/blob/unstable/docs/guide/counter/plugins/counter/counter.go) for a better idea. + +## Permissions + +This system requires a more complex permissioning system to allow the modules +to have limited access to each other. Also to allow more types of permissions +than simple public key signatures. So, rather than just use an address to +identify who is performing an action, we can use a more complex structure: + +```Go +type Actor struct { + ChainID string `json:"chain"` // this is empty unless it comes from a different chain + App string `json:"app"` // the app that the actor belongs to + Address data.Bytes `json:"addr"` // arbitrary app-specific unique id +} +``` + +`ChainID` is to be used for IBC, which is discussed below, but right now focus +on `App` and `Address`. For a signature, the App is `auth`, and any modules can +check to see if a specific public key address signed like this +`ctx.HasPermission(auth.SigPerm(addr))`. However, we can also authorize a +tx with `roles`, which handles multi-sig accounts, it checks if there were +enough signatures by checking as above, then it can add the role permission like +`ctx = ctx.WithPermissions(NewPerm(assume.Role))` + +In addition to permissioning, the Actors are addresses just like public key +addresses. So one can create a mulit-sig role, then send coin there, which +can only be moved upon meeting the authorization requirements from that module. +`coin` doesn't even know the existence of `roles` and one could build any +other sort of module to provide permissions (like bind the outcome of an +election to move coins or to modify the accounts on a role). + +One idea (not implemented) is to provide scopes on the permissions. Right now, +if I sign a tx to one module, it can pass it on to any other module over IPC +with the same permissions. It could move coins, vote in an election, or +anything else. Ideally, when signing, one could also specify the scope(s) that +this signature authorizes. The [oauth protocol](https://api.slack.com/docs/oauth-scopes) +also has to deal with a similar problem, and maybe could provide some inspiration. + +## Replay Protection + +Is implemented as middleware. Rigel can add more info here. Or look +at [the github issue](https://github.com/tendermint/basecoin/issues/160) + ## IBC (Inter-Blockchain Communication) Wow, this is a big topic. Also a WIP. Add more here...