Skip to content

Commit

Permalink
Add private state.
Browse files Browse the repository at this point in the history
  • Loading branch information
joelburget committed Aug 24, 2017
1 parent 7e94f76 commit 3f18431
Show file tree
Hide file tree
Showing 79 changed files with 2,713 additions and 808 deletions.
18 changes: 18 additions & 0 deletions BUILDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

# Building Quorum

Clone the repository and build the source:

```
git clone https://github.com/jpmorganchase/quorum.git
cd quorum
make all
make test
```

Binaries are placed within `./build/bin`, most notably `geth` and `bootnode`. Either add this directory to your `$PATH` or copy those two bins into your PATH:

```sh
# assumes that /usr/local/bin is in your PATH
cp ./build/bin/geth ./build/bin/bootnode /usr/local/bin/
```
89 changes: 89 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Hacking on Quorum / various notes

## Testing with Constellation

### `tm.conf`

Replace with appropriate absolute paths:

TODO(joel): figure out how to use relative paths

```
url = "http://127.0.0.1:9000/"
port = 9000
socket = "/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/tm.ipc"
othernodes = []
storage = "/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/constellation"
publickeys = ["/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/test.pub"]
privatekeys = ["/Users/joel/go/src/github.com/ethereum/go-ethereum/qdata/test.key"]
```

Run constellation:

```
> mkdir qdata
> constellation-node tm.conf
```

Now you should be able to run the private state tests as well: `env PRIVATE_CONFIG=(pwd)/tm.conf go test ./...`.

## How does private state work?

Let's look at the EVM structure:

```go
type EVM struct {
...
// StateDB gives access to the underlying state
StateDB StateDB
// Depth is the current call stack
depth int
...

publicState PublicState
privateState PrivateState
states [1027]*state.StateDB
currentStateDepth uint
readOnly bool
readOnlyDepth uint
}
```

The vanilla EVM has a call depth limit of 1024. Our `states` parallel the EVM call stack, recording as contracts in the public and private state call back and forth to each other. Note it doesn't have to be a "public -> private -> public -> private" back-and-forth chain. It can be any sequence of { public, private }.

The interface for calling is this `Push` / `Pop` sequence:

```go
evm.Push(getDualState(evm, addr))
defer func() { evm.Pop() }()
// ... do work in the pushed state
```

The definitions of `Push` and `Pop` are simple and important enough to duplicate here:

```go
func (env *EVM) Push(statedb StateDB) {
if env.privateState != statedb {
env.readOnly = true
env.readOnlyDepth = env.currentStateDepth
}

if castedStateDb, ok := statedb.(*state.StateDB); ok {
env.states[env.currentStateDepth] = castedStateDb
env.currentStateDepth++
}

env.StateDB = statedb
}
func (env *EVM) Pop() {
env.currentStateDepth--
if env.readOnly && env.currentStateDepth == env.readOnlyDepth {
env.readOnly = false
}
env.StateDB = env.states[env.currentStateDepth-1]
}
```

Note the invariant that `StateDB` always points to the current state db.

The other interesting note is read only mode. Any time we call from the private state into the public state (`env.privateState != statedb`), we require anything deeper to be *read only*. Private state transactions can't affect public state, so we throw an EVM exception on any mutating operation (`SELFDESTRUCT, CREATE, SSTORE, LOG0, LOG1, LOG2, LOG3, LOG4`). Question: have any more mutating operations been added? Question: could we not mutate deeper private state?
360 changes: 108 additions & 252 deletions README.md

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
statedb, _ := b.blockchain.State()
statedb, _, _ := b.blockchain.State()
return statedb.GetCode(contract), nil
}

Expand All @@ -112,7 +112,7 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
statedb, _ := b.blockchain.State()
statedb, _, _ := b.blockchain.State()
return statedb.GetBalance(contract), nil
}

Expand All @@ -124,7 +124,7 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return 0, errBlockNumberUnsupported
}
statedb, _ := b.blockchain.State()
statedb, _, _ := b.blockchain.State()
return statedb.GetNonce(contract), nil
}

Expand All @@ -136,7 +136,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
statedb, _ := b.blockchain.State()
statedb, _, _ := b.blockchain.State()
val := statedb.GetState(contract, key)
return val[:], nil
}
Expand All @@ -163,11 +163,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
state, err := b.blockchain.State()
state, _, err := b.blockchain.State()
if err != nil {
return nil, err
}
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state, state)
return rval, err
}

Expand All @@ -177,7 +177,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())

rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState, b.pendingState)
return rval, err
}

Expand Down Expand Up @@ -215,7 +215,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
call.Gas = new(big.Int).SetUint64(mid)

snapshot := b.pendingState.Snapshot()
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState, b.pendingState)
b.pendingState.RevertToSnapshot(snapshot)

// If the transaction became invalid or used all the gas (failed), raise the gas limit
Expand All @@ -231,7 +231,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs

// callContract implemens common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) {
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb, privateState *state.StateDB) ([]byte, *big.Int, error) {
// Ensure message is initialized properly.
if call.GasPrice == nil {
call.GasPrice = big.NewInt(1)
Expand All @@ -251,7 +251,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
vmenv := vm.NewEVM(evmContext, statedb, privateState, b.config, vm.Config{})
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
// TODO utilize returned failed flag to help gas estimation.
ret, gasUsed, _, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
Expand Down
2 changes: 1 addition & 1 deletion accounts/keystore/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
return nil, ErrLocked
}
// Depending on the presence of the chain ID, sign with EIP155 or homestead
if chainID != nil {
if chainID != nil { // && !params.IsQuorum {
return types.SignTx(tx, types.NewEIP155Signer(chainID), unlockedKey.PrivateKey)
}
return types.SignTx(tx, types.HomesteadSigner{}, unlockedKey.PrivateKey)
Expand Down
82 changes: 44 additions & 38 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"github.com/ethereum/go-ethereum/raft"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"github.com/naoina/toml"
"time"
)
Expand Down Expand Up @@ -137,6 +137,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
}

utils.SetShhConfig(ctx, stack, &cfg.Shh)
cfg.Eth.RaftMode = ctx.GlobalBool(utils.RaftModeFlag.Name)

return stack, cfg
}
Expand All @@ -157,43 +158,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
ethChan := utils.RegisterEthService(stack, &cfg.Eth)

if ctx.GlobalBool(utils.RaftModeFlag.Name) {
blockTimeMillis := ctx.GlobalInt(utils.RaftBlockTimeFlag.Name)
datadir := ctx.GlobalString(utils.DataDirFlag.Name)
joinExistingId := ctx.GlobalInt(utils.RaftJoinExistingFlag.Name)

if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
privkey := cfg.Node.NodeKey()
strId := discover.PubkeyID(&privkey.PublicKey).String()
blockTimeNanos := time.Duration(blockTimeMillis) * time.Millisecond
peers := cfg.Node.StaticNodes()

var myId uint16
var joinExisting bool

if joinExistingId > 0 {
myId = uint16(joinExistingId)
joinExisting = true
} else {
peerIds := make([]string, len(peers))
for peerIdx, peer := range peers {
peerId := peer.ID.String()
peerIds[peerIdx] = peerId
if peerId == strId {
myId = uint16(peerIdx) + 1
}
}

if myId == 0 {
utils.Fatalf("failed to find local enode ID (%v) amongst peer IDs: %v", strId, peerIds)
}
}

ethereum := <-ethChan

return raft.New(ctx, params.TestChainConfig, myId, joinExisting, blockTimeNanos, ethereum, peers, datadir)
}); err != nil {
utils.Fatalf("Failed to register the Raft service: %v", err)
}
RegisterRaftService(stack, ctx, cfg, ethChan)
}

// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
Expand Down Expand Up @@ -249,3 +214,44 @@ func dumpConfig(ctx *cli.Context) error {
os.Stdout.Write(out)
return nil
}

func RegisterRaftService(stack *node.Node, ctx *cli.Context, cfg gethConfig, ethChan <-chan *eth.Ethereum) {
blockTimeMillis := ctx.GlobalInt(utils.RaftBlockTimeFlag.Name)
datadir := ctx.GlobalString(utils.DataDirFlag.Name)
joinExistingId := ctx.GlobalInt(utils.RaftJoinExistingFlag.Name)

if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
privkey := cfg.Node.NodeKey()
strId := discover.PubkeyID(&privkey.PublicKey).String()
blockTimeNanos := time.Duration(blockTimeMillis) * time.Millisecond
peers := cfg.Node.StaticNodes()

var myId uint16
var joinExisting bool

if joinExistingId > 0 {
myId = uint16(joinExistingId)
joinExisting = true
} else {
peerIds := make([]string, len(peers))
for peerIdx, peer := range peers {
peerId := peer.ID.String()
peerIds[peerIdx] = peerId
if peerId == strId {
myId = uint16(peerIdx) + 1
}
}

if myId == 0 {
utils.Fatalf("failed to find local enode ID (%v) amongst peer IDs: %v", strId, peerIds)
}
}

ethereum := <-ethChan

return raft.New(ctx, ethereum.ChainConfig(), myId, joinExisting, blockTimeNanos, ethereum, peers, datadir)
}); err != nil {
utils.Fatalf("Failed to register the Raft service: %v", err)
}

}
10 changes: 9 additions & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ var (
Name: "shh.pow",
Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW,
}
}

// Raft flags
RaftModeFlag = cli.BoolFlag{
Expand All @@ -505,6 +505,12 @@ var (
Name: "emitcheckpoints",
Usage: "If enabled, emit specially formatted logging checkpoints",
}

// Quorum
EnableNodePermissionFlag = cli.BoolFlag{
Name: "permissioned",
Usage: "If enabled, the node will allow only a defined list of nodes to connect",
}
)

// MakeDataDir retrieves the currently requested data directory, terminating
Expand Down Expand Up @@ -832,6 +838,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
setWS(ctx, cfg)
setNodeUserIdent(ctx, cfg)

cfg.EnableNodePermission = ctx.GlobalBool(EnableNodePermissionFlag.Name)

switch {
case ctx.GlobalIsSet(DataDirFlag.Name):
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
Expand Down
Loading

0 comments on commit 3f18431

Please sign in to comment.