github.com/ConsenSys/Quorum@v20.10.0+incompatible/NOTES.md (about)

     1  # Hacking on Quorum / various notes
     2  
     3  ## How does private state work?
     4  
     5  Original commit from Jeff explains the dual public and private state with INITIAL restrictions:
     6  ```
     7  commit 763f939f4725daa136161868d3b01fa7a84eb71e
     8  Author: Jeffrey Wilcke <jeffrey@ethereum.org>
     9  Date:   Mon Oct 31 12:46:40 2016 +0100
    10  
    11     core, core/vm: dual state & read only EVM
    12  
    13     This commit implements a dual state approach. The dual state approach
    14     separates public and private state by making the core vm environment
    15     context aware.
    16  
    17     Although not currently implemented it will need to prohibit value
    18     transfers and it must initialise all transactions from accounts on the
    19     public state. This means that sending transactions increments the
    20     account nonce on the public state and contract addresses are derived
    21     from the public state when initialised by a transaction. For obvious
    22     reasons, contract created by private contracts are still derived from
    23     public state.
    24  
    25     This is required in order to have consensus over the public state at all
    26     times as non-private participants would still process the transaction on
    27     the public state even though private payload can not be decrypted. This
    28     means that participants of a private group must do the same in order to
    29     have public consensus. However the creation of the contract and
    30     interaction still occurs on the private state.
    31  
    32     It implements support for the following calling model:
    33  
    34     S: sender, (X): private, X: public, ->: direction, [ ]: read only mode
    35  
    36     1. S -> A -> B
    37     2. S -> (A) -> (B)
    38     3. S -> (A) -> [ B -> C ]
    39  
    40     It does not support
    41  
    42     1. (S) -> A
    43     2. (S) -> (A)
    44     3. S -> (A) -> B
    45  
    46     Implemented "read only" mode for the EVM. Read only mode is checked
    47     during any opcode that could potentially modify the state. If such an
    48     opcode is encountered during "read only", it throws an exception.
    49  
    50     The EVM is flagged "read only" when a private contract calls in to
    51     public state.
    52  ```
    53  
    54  
    55  Some things have changed since, let's look at the EVM structure in some more detail:
    56  
    57  ```go
    58  type EVM struct {
    59  	...
    60  	// StateDB gives access to the underlying state
    61  	StateDB StateDB
    62  	// Depth is the current call stack
    63  	depth int
    64  	...
    65  
    66  	publicState       PublicState
    67  	privateState      PrivateState
    68  	states            [1027]*state.StateDB
    69  	currentStateDepth uint
    70  	readOnly          bool
    71  	readOnlyDepth     uint
    72  }
    73  ```
    74  
    75  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 }.
    76  
    77  The interface for calling is this `Push` / `Pop` sequence:
    78  
    79  ```go
    80  evm.Push(getDualState(evm, addr))
    81  defer func() { evm.Pop() }()
    82  // ... do work in the pushed state
    83  ```
    84  
    85  The definitions of `Push` and `Pop` are simple and important enough to duplicate here:
    86  
    87  ```go
    88  func (env *EVM) Push(statedb StateDB) {
    89  	if env.privateState != statedb {
    90  		env.readOnly = true
    91  		env.readOnlyDepth = env.currentStateDepth
    92  	}
    93  
    94  	if castedStateDb, ok := statedb.(*state.StateDB); ok {
    95  		env.states[env.currentStateDepth] = castedStateDb
    96  		env.currentStateDepth++
    97  	}
    98  
    99  	env.StateDB = statedb
   100  }
   101  func (env *EVM) Pop() {
   102  	env.currentStateDepth--
   103  	if env.readOnly && env.currentStateDepth == env.readOnlyDepth {
   104  		env.readOnly = false
   105  	}
   106  	env.StateDB = env.states[env.currentStateDepth-1]
   107  }
   108  ```
   109  
   110  Note the invariant that `StateDB` always points to the current state db.
   111  
   112  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?