aboutsummaryrefslogblamecommitdiffstats
path: root/core/vm/governance.go
blob: a60e99b95f17df9de73831c3fdbffdf2df0f932a (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                        
                                                      





                                                                                 
                                                                               


                                                                                      





                                         




                         




                         






                            






                              
                            


                   
                         







                              


                         




                         







                              


                   
                         







                              
               




                         






                         






                              



                       
                         



























                              













                              













                              



                        














                               












                              
                         







                              









                         
                                  











                              






















                              
                 
                        











                              
                 











                               


















                                 






















                                   


                      

                            

        

                           

        

                            

        
                    
                         

        
                           




















                                   











                                    
                            












                                    
                        


                         

















                                    
                            












                                    
                        


                         
                           


                       
                             








                                    
                            






















                                    
                               











                                                              
                                                        
         






                                                

 
                                                              


                                                         












                                


                                                                        



                               






                                                       
                                                 
                              

                            





                                                                                

                                    
                                                                                   





                                                        

                                                                                   

                                                        
                                              








                                                                              
                                     
                                

                                          
                   
                                                                              

                                                        
                                                                          
                              
                                

                                         
                   
                                                                              

                                                        




                                                                  
















                                                                              






















                                                                               

























                                                                               

                                                                









































































                                                                              



                       








                              
                











                           

                                                         
                              


                       
































































                                                                                                      



                                                      






























                                                                            





























                                                                             





                       
                







                                              
                                                     
 
                                                      

                             
                                                          













                                                                                                


                                             

                                      
                                                                                            

                                    
 
                                                               
                                                          












                                                                                                
 
 
                                             
                                                            
                                                               
                                                                     

                                                                       
                                                               
                                                                      

                                                         
                                                               
                                            

 
                        
                                         
                                                   

                                                       

                                                   
                                                
 

                                                

                                                                                     
 

                                                   
 
                                              

 
                                        
                                                                    
                                                                           

                                                                         
                                                                            

 
                                  
                                                              
                                                                     
 
                                                                          

                                                                             
 














                                                                                            
 








                                                                                       

 


                                                                 
                                                 
 
 
                            
                                            
                                                         

 
                           
                                           
                                                        
 
 
                            
                                            
                                                         

 
                    
                                    
                                                 
 
 
                                                         
                                           
                                                        

 
                                
                                                
                                                             

 
                             
                                             
                                                          

 
                               
                                                
                                                             

 
                                  
                                                   
                                                                

 
                                  
                                                   
                                                                

 















                                                                                            
                                                                   





                                
                                                                              

                                   
                                                                       



                                   



                                         
                                                                                        
















                                                                                                           



                                                                      



                                           

                                           










                                                       

                                



                                                        

                                        
                                          















                                                            
                                                                        
                                                                                     

                                


                       


                                                                           

                                                                  
                                                                   
                            




                                                      
                                                        
                                                               
                                                      






                                                              
                                                   
                                                           
                                                





                                                                
                                                           










                                                                         
                            




                                                      



                                                      
 


                                                                                  


                       


















                                                                                           
                                               























                                                                      
                                                                                               

                                        
 




                                                
                                                    
                                                                 
                            



                                                                           
                            


                                                






                                                           
                                                 


                                      


                       


                                                                                              
                                             

                                                                       

                                                
                                                                    
                      
                            


                                                





                                                           
 






                                                            

                       
package vm

import (
    "math/big"
    "strings"

    "github.com/dexon-foundation/dexon/accounts/abi"
    "github.com/dexon-foundation/dexon/common"
    "github.com/dexon-foundation/dexon/core/types"
    "github.com/dexon-foundation/dexon/crypto"
    "github.com/dexon-foundation/dexon/rlp"

    coreCommon "github.com/dexon-foundation/dexon-consensus-core/common"
    "github.com/dexon-foundation/dexon-consensus-core/core"
    coreCrypto "github.com/dexon-foundation/dexon-consensus-core/core/crypto"
    coreTypes "github.com/dexon-foundation/dexon-consensus-core/core/types"
)

var GovernanceContractAddress = common.BytesToAddress([]byte{0XED}) // Reverse of DEX0
var minStake = big.NewInt(10000000000000)

const abiJSON = `
[
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      },
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "dkgComplaints",
    "outputs": [
      {
        "name": "",
        "type": "bytes"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "notarySetSize",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "dkgSetSize",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "lambdaBA",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "crs",
    "outputs": [
      {
        "name": "",
        "type": "bytes32"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "phiRatio",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "address"
      }
    ],
    "name": "offset",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "roundInterval",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "name": "",
        "type": "address"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "lambdaDKG",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "minBlockInterval",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "k",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      },
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "dkgMasterPublicKeys",
    "outputs": [
      {
        "name": "",
        "type": "bytes"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      },
      {
        "name": "",
        "type": "address"
      }
    ],
    "name": "dkgFinalizeds",
    "outputs": [
      {
        "name": "",
        "type": "bool"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "numChains",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [],
    "name": "maxBlockInterval",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "dkgFinalizedsCount",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "ConfigurationChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "name": "round",
        "type": "uint256"
      },
      {
        "indexed": false,
        "name": "crs",
        "type": "bytes32"
      }
    ],
    "name": "CRSProposed",
    "type": "event"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "NumChains",
        "type": "uint256"
      },
      {
        "name": "LambdaBA",
        "type": "uint256"
      },
      {
        "name": "LambdaDKG",
        "type": "uint256"
      },
      {
        "name": "K",
        "type": "uint256"
      },
      {
        "name": "PhiRatio",
        "type": "uint256"
      },
      {
        "name": "NotarySetSize",
        "type": "uint256"
      },
      {
        "name": "DKGSetSize",
        "type": "uint256"
      },
      {
        "name": "RoundInterval",
        "type": "uint256"
      },
      {
        "name": "MinBlockInterval",
        "type": "uint256"
      },
      {
        "name": "MaxBlockInterval",
        "type": "uint256"
      }
    ],
    "name": "updateConfiguration",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "SignedCRS",
        "type": "bytes"
      }
    ],
    "name": "proposeCRS",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "Round",
        "type": "uint256"
      },
      {
        "name": "Complaint",
        "type": "bytes"
      }
    ],
    "name": "addDKGComplaint",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "Round",
        "type": "uint256"
      },
      {
        "name": "PublicKey",
        "type": "bytes"
      }
    ],
    "name": "addDKGMasterPublicKey",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "Round",
        "type": "uint256"
      },
      {
        "name": "Finalize",
        "type": "bytes"
      }
    ],
    "name": "addDKGFinalize",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "PublicKey",
        "type": "bytes"
      }
    ],
    "name": "stake",
    "outputs": [],
    "payable": true,
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [],
    "name": "unstake",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  }
]
`

var abiObject abi.ABI
var sig2Method map[string]abi.Method
var events map[string]abi.Event

func init() {
    // Parse governance contract ABI.
    abiObject, err := abi.JSON(strings.NewReader(abiJSON))
    if err != nil {
        panic(err)
    }

    sig2Method = make(map[string]abi.Method)

    // Construct dispatch table.
    for _, method := range abiObject.Methods {
        sig2Method[string(method.Id())] = method
    }

    events = make(map[string]abi.Event)

    // Event cache.
    for _, event := range abiObject.Events {
        events[event.Name] = event
    }
}

func nodeIDToAddress(nodeID coreTypes.NodeID) common.Address {
    return common.BytesToAddress(nodeID.Bytes()[12:])
}

type configParams struct {
    NumChains        uint32
    LambdaBA         uint64
    LambdaDKG        uint64
    K                int
    PhiRatio         float32
    NotarySetSize    uint32
    DKGSetSize       uint32
    RoundInterval    uint64
    MinBlockInterval uint64
    MaxBlockInterval uint64
}

// RunGovernanceContract executes governance contract.
func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (
    ret []byte, err error) {
    if len(input) < 4 {
        return nil, nil
    }

    // Parse input.
    method, exists := sig2Method[string(input[:4])]
    if !exists {
        return nil, errExecutionReverted
    }

    // Dispatch method call.
    g := newGovernanceContract(evm, contract)
    arguments := input[4:]

    switch method.Name {
    case "updateConfiguration":
        var config configParams
        if err := method.Inputs.Unpack(&config, arguments); err != nil {
            return nil, errExecutionReverted
        }
        g.updateConfiguration(&config)
    case "stake":
        var publicKey []byte
        if err := method.Inputs.Unpack(&publicKey, arguments); err != nil {
            return nil, errExecutionReverted
        }
        return g.stake(publicKey)
    case "unstake":
        return g.unstake()
    case "proposeCRS":
        var signedCRS []byte
        if err := method.Inputs.Unpack(&signedCRS, arguments); err != nil {
            return nil, errExecutionReverted
        }
        return g.proposeCRS(signedCRS)
    case "addDKGComplaint":
        args := struct {
            Round     *big.Int
            Complaint []byte
        }{}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        return g.addDKGComplaint(args.Round, args.Complaint)
    case "addDKGMasterPublicKey":
        args := struct {
            Round     *big.Int
            PublicKey []byte
        }{}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        return g.addDKGMasterPublicKey(args.Round, args.PublicKey)
    case "addDKGFinalize":
        args := struct {
            Round    *big.Int
            Finalize []byte
        }{}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        return g.addDKGFinalize(args.Round, args.Finalize)

    // --------------------------------
    // Solidity auto generated methods.
    // --------------------------------

    case "dkgComplaints":
        round, index := new(big.Int), new(big.Int)
        args := []interface{}{&round, &index}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        complaints := g.state.dkgComplaints(round)
        if int(index.Uint64()) >= len(complaints) {
            return nil, errExecutionReverted
        }
        complaint := complaints[index.Uint64()]
        res, err := method.Outputs.Pack(complaint)
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "dkgFinalizeds":
        round, addr := new(big.Int), common.Address{}
        args := []interface{}{&round, &addr}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        finalized := g.state.dkgFinalized(round, addr)
        res, err := method.Outputs.Pack(finalized)
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "dkgFinalizedsCount":
        round := new(big.Int)
        if err := method.Inputs.Unpack(&round, arguments); err != nil {
            return nil, errExecutionReverted
        }
        count := g.state.dkgFinalizedsCount(round)
        res, err := method.Outputs.Pack(count)
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "dkgMasterPublicKeys":
        round, index := new(big.Int), new(big.Int)
        args := []interface{}{&round, &index}
        if err := method.Inputs.Unpack(&args, arguments); err != nil {
            return nil, errExecutionReverted
        }
        pks := g.state.dkgMasterPublicKeys(round)
        if int(index.Uint64()) >= len(pks) {
            return nil, errExecutionReverted
        }
        pk := pks[index.Uint64()]
        res, err := method.Outputs.Pack(pk)
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "crs":
        round := new(big.Int)
        if err := method.Inputs.Unpack(&round, arguments); err != nil {
            return nil, errExecutionReverted
        }
        res, err := method.Outputs.Pack(g.state.crs(round))
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "owner":
        res, err := method.Outputs.Pack(g.state.owner())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "k":
        res, err := method.Outputs.Pack(g.state.k())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "lambdaBA":
        res, err := method.Outputs.Pack(g.state.lambdaBA())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "lambdaDKG":
        res, err := method.Outputs.Pack(g.state.lambdaDKG())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "maxBlockInterval":
        res, err := method.Outputs.Pack(g.state.maxBlockInterval())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "minBlockInterval":
        res, err := method.Outputs.Pack(g.state.minBlockInterval())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "numChains":
        res, err := method.Outputs.Pack(g.state.numChains())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "dkgSetSize":
        res, err := method.Outputs.Pack(g.state.dkgSetSize())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "notarySetSize":
        res, err := method.Outputs.Pack(g.state.notarySetSize())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "offset":
        addr := common.Address{}
        if err := method.Inputs.Unpack(&addr, arguments); err != nil {
            return nil, errExecutionReverted
        }
        res, err := method.Outputs.Pack(g.state.offset(addr))
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "phiRatio":
        res, err := method.Outputs.Pack(g.state.phiRatio())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    case "roundInterval":
        res, err := method.Outputs.Pack(g.state.roundInterval())
        if err != nil {
            return nil, errExecutionReverted
        }
        return res, nil
    }
    return nil, nil
}

// Storage position enums.
const (
    nodesLoc = iota
    offsetLoc
    crsLoc
    dkgMasterPublicKeysLoc
    dkgComplaintsLoc
    dkgFinailizedLoc
    dkgFinalizedsCountLoc
    ownerLoc
    numChainsLoc
    lambdaBALoc
    lambdaDKGLoc
    kLoc
    phiRatioLoc
    notarySetSizeLoc
    dkgSetSizeLoc
    roundIntervalLoc
    minBlockIntervalLoc
    maxBlockIntervalLoc
)

// State manipulation helper fro the governance contract.
type StateHelper struct {
    Address common.Address
    StateDB StateDB
}

func (s *StateHelper) getState(loc common.Hash) common.Hash {
    return s.StateDB.GetState(s.Address, loc)
}

func (s *StateHelper) setState(loc common.Hash, val common.Hash) {
    s.StateDB.SetState(s.Address, loc, val)
}

func (s *StateHelper) getStateBigInt(loc *big.Int) *big.Int {
    res := s.StateDB.GetState(s.Address, common.BigToHash(loc))
    return new(big.Int).SetBytes(res.Bytes())
}

func (s *StateHelper) setStateBigInt(loc *big.Int, val *big.Int) {
    s.setState(common.BigToHash(loc), common.BigToHash(val))
}

func (s *StateHelper) getSlotLoc(loc *big.Int) *big.Int {
    return new(big.Int).SetBytes(crypto.Keccak256(common.BigToHash(loc).Bytes()))
}

func (s *StateHelper) getMapLoc(pos *big.Int, key []byte) *big.Int {
    return new(big.Int).SetBytes(crypto.Keccak256(
        key, common.BigToHash(pos).Bytes()))
}

func (s *StateHelper) readBytes(loc *big.Int) []byte {
    // length of the dynamic array (bytes).
    rawLength := s.getStateBigInt(loc)
    lengthByte := new(big.Int).Mod(rawLength, big.NewInt(256))

    // bytes length <= 31, lengthByte % 2 == 0
    // return the high 31 bytes.
    if new(big.Int).Mod(lengthByte, big.NewInt(2)).Cmp(big.NewInt(0)) == 0 {
        length := new(big.Int).Div(lengthByte, big.NewInt(2)).Uint64()
        return rawLength.Bytes()[:length]
    }

    // actual length = (rawLength - 1) / 2
    length := new(big.Int).Div(new(big.Int).Sub(rawLength, big.NewInt(1)), big.NewInt(2)).Uint64()

    // data address.
    dataLoc := s.getSlotLoc(loc)

    // read continiously for length bytes.
    carry := int64(0)
    if length%32 > 0 {
        carry = 1
    }
    chunks := int64(length/32) + carry
    var data []byte
    for i := int64(0); i < chunks; i++ {
        loc = new(big.Int).Add(dataLoc, big.NewInt(i))
        data = append(data, s.getState(common.BigToHash(loc)).Bytes()...)
    }
    data = data[:length]
    return data
}

func (s *StateHelper) writeBytes(loc *big.Int, data []byte) {
    length := int64(len(data))

    // short bytes (length <= 31)
    if length < 32 {
        data2 := append([]byte(nil), data...)
        // Right pad with zeros
        for len(data2) < 31 {
            data2 = append(data2, byte(0))
        }
        data2 = append(data2, byte(length*2))
        s.setState(common.BigToHash(loc), common.BytesToHash(data2))
        return
    }

    // write 2 * length + 1
    storedLength := new(big.Int).Add(new(big.Int).Mul(
        big.NewInt(length), big.NewInt(2)), big.NewInt(1))
    s.setStateBigInt(loc, storedLength)
    // write data chunck.
    dataLoc := s.getSlotLoc(loc)
    carry := int64(0)
    if length%32 > 0 {
        carry = 1
    }
    chunks := length/32 + carry
    for i := int64(0); i < chunks; i++ {
        loc = new(big.Int).Add(dataLoc, big.NewInt(i))
        maxLoc := (i + 1) * 32
        if maxLoc > length {
            maxLoc = length
        }
        data2 := data[i*32 : maxLoc]
        // Right pad with zeros
        for len(data2) < 32 {
            data2 = append(data2, byte(0))
        }
        s.setState(common.BigToHash(loc), common.BytesToHash(data2))
    }
}

func (s *StateHelper) read2DByteArray(pos, index *big.Int) [][]byte {
    baseLoc := s.getSlotLoc(pos)
    loc := new(big.Int).Add(baseLoc, index)

    arrayLength := s.getStateBigInt(loc)
    dataLoc := s.getSlotLoc(loc)

    data := [][]byte{}
    for i := int64(0); i < int64(arrayLength.Uint64()); i++ {
        elementLoc := new(big.Int).Add(dataLoc, big.NewInt(i))
        data = append(data, s.readBytes(elementLoc))
    }

    return data
}
func (s *StateHelper) appendTo2DByteArray(pos, index *big.Int, data []byte) {
    // Find the loc of the last element.
    baseLoc := s.getSlotLoc(pos)
    loc := new(big.Int).Add(baseLoc, index)

    // increase length by 1.
    arrayLength := s.getStateBigInt(loc)
    s.setStateBigInt(loc, new(big.Int).Add(arrayLength, big.NewInt(1)))

    // write element.
    dataLoc := s.getSlotLoc(loc)
    elementLoc := new(big.Int).Add(dataLoc, arrayLength)
    s.writeBytes(elementLoc, data)
}

// struct Node {
//     address owner;
//     bytes publicKey;
//     uint256 staked;
// }
//
// Node[] nodes;

type nodeInfo struct {
    owner     common.Address
    publicKey []byte
    staked    *big.Int
}

func (s *StateHelper) nodesLength() *big.Int {
    return s.getStateBigInt(big.NewInt(nodesLoc))
}
func (s *StateHelper) node(index *big.Int) *nodeInfo {
    node := new(nodeInfo)

    arrayBaseLoc := s.getSlotLoc(big.NewInt(nodesLoc))
    elementBaseLoc := new(big.Int).Add(arrayBaseLoc, new(big.Int).Mul(index, big.NewInt(3)))

    // owner.
    loc := elementBaseLoc
    node.owner = common.BytesToAddress(s.getState(common.BigToHash(elementBaseLoc)).Bytes())

    // publicKey.
    loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1))
    node.publicKey = s.readBytes(loc)

    // staked.
    loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2))
    node.staked = s.getStateBigInt(loc)

    return nil
}
func (s *StateHelper) pushNode(n *nodeInfo) {
    // increase length by 1
    arrayLength := s.nodesLength()
    s.setStateBigInt(big.NewInt(nodesLoc), new(big.Int).Add(arrayLength, big.NewInt(1)))

    s.updateNode(arrayLength, n)
}
func (s *StateHelper) updateNode(index *big.Int, n *nodeInfo) {
    arrayBaseLoc := s.getSlotLoc(big.NewInt(nodesLoc))
    elementBaseLoc := new(big.Int).Add(arrayBaseLoc, new(big.Int).Mul(index, big.NewInt(3)))

    // owner.
    loc := elementBaseLoc
    s.setState(common.BigToHash(loc), n.owner.Hash())

    // publicKey.
    loc = new(big.Int).Add(elementBaseLoc, big.NewInt(1))
    s.writeBytes(loc, n.publicKey)

    // staked.
    loc = new(big.Int).Add(elementBaseLoc, big.NewInt(2))
    s.setStateBigInt(loc, n.staked)
}

// mapping(address => uint256) public offset;
func (s *StateHelper) offset(addr common.Address) *big.Int {
    loc := s.getMapLoc(big.NewInt(offsetLoc), addr.Bytes())
    return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1))
}
func (s *StateHelper) putOffset(addr common.Address, offset *big.Int) {
    loc := s.getMapLoc(big.NewInt(offsetLoc), addr.Bytes())
    s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1)))
}
func (s *StateHelper) deleteOffset(addr common.Address) {
    loc := s.getMapLoc(big.NewInt(offsetLoc), addr.Bytes())
    s.setStateBigInt(loc, big.NewInt(0))
}

// bytes32[] public crs;
func (s *StateHelper) lenCRS() *big.Int {
    return s.getStateBigInt(big.NewInt(crsLoc))
}
func (s *StateHelper) crs(index *big.Int) common.Hash {
    baseLoc := s.getSlotLoc(big.NewInt(crsLoc))
    loc := new(big.Int).Add(baseLoc, index)
    return s.getState(common.BigToHash(loc))
}
func (s *StateHelper) pushCRS(crs common.Hash) {
    // increase length by 1.
    length := s.getStateBigInt(big.NewInt(crsLoc))
    s.setStateBigInt(big.NewInt(crsLoc), new(big.Int).Add(length, big.NewInt(1)))

    baseLoc := s.getSlotLoc(big.NewInt(crsLoc))
    loc := new(big.Int).Add(baseLoc, length)

    s.setState(common.BigToHash(loc), crs)
}

// bytes[][] public dkgMasterPublicKeys;
func (s *StateHelper) dkgMasterPublicKeys(round *big.Int) [][]byte {
    return s.read2DByteArray(big.NewInt(dkgMasterPublicKeysLoc), round)
}
func (s *StateHelper) pushDKGMasterPublicKey(round *big.Int, pk []byte) {
    s.appendTo2DByteArray(big.NewInt(dkgMasterPublicKeysLoc), round, pk)
}

// bytes[][] public dkgComplaints;
func (s *StateHelper) dkgComplaints(round *big.Int) [][]byte {
    return s.read2DByteArray(big.NewInt(dkgComplaintsLoc), round)
}
func (s *StateHelper) pushDKGComplaint(round *big.Int, complaint []byte) {
    s.appendTo2DByteArray(big.NewInt(dkgComplaintsLoc), round, complaint)
}

// mapping(address => bool)[] public dkgFinalized;
func (s *StateHelper) dkgFinalized(round *big.Int, addr common.Address) bool {
    baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinailizedLoc)), round)
    mapLoc := s.getMapLoc(baseLoc, addr.Bytes())
    return s.getStateBigInt(mapLoc).Cmp(big.NewInt(0)) != 0
}
func (s *StateHelper) putDKGFinalized(round *big.Int, addr common.Address, finalized bool) {
    baseLoc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinailizedLoc)), round)
    mapLoc := s.getMapLoc(baseLoc, addr.Bytes())
    res := big.NewInt(0)
    if finalized {
        res = big.NewInt(1)
    }
    s.setStateBigInt(mapLoc, res)
}

// uint256[] public dkgFinalizedsCount;
func (s *StateHelper) dkgFinalizedsCount(round *big.Int) *big.Int {
    loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedsCountLoc)), round)
    return s.getStateBigInt(loc)
}
func (s *StateHelper) incDKGFinalizedsCount(round *big.Int) {
    loc := new(big.Int).Add(s.getSlotLoc(big.NewInt(dkgFinalizedsCountLoc)), round)
    count := s.getStateBigInt(loc)
    s.setStateBigInt(loc, new(big.Int).Add(count, big.NewInt(1)))
}

// address public owner;
func (s *StateHelper) owner() common.Address {
    val := s.getState(common.BigToHash(big.NewInt(ownerLoc)))
    return common.BytesToAddress(val.Bytes())
}

// uint256 public numChains;
func (s *StateHelper) numChains() *big.Int {
    return s.getStateBigInt(big.NewInt(numChainsLoc))
}

// uint256 public lambdaBA;
func (s *StateHelper) lambdaBA() *big.Int {
    return s.getStateBigInt(big.NewInt(lambdaBALoc))
}

// uint256 public lambdaDKG;
func (s *StateHelper) lambdaDKG() *big.Int {
    return s.getStateBigInt(big.NewInt(lambdaDKGLoc))
}

// uint256 public k;
func (s *StateHelper) k() *big.Int {
    return s.getStateBigInt(big.NewInt(kLoc))
}

// uint256 public phiRatio;  // stored as PhiRatio * 10^6
func (s *StateHelper) phiRatio() *big.Int {
    return s.getStateBigInt(big.NewInt(phiRatioLoc))
}

// uint256 public notarySetSize;
func (s *StateHelper) notarySetSize() *big.Int {
    return s.getStateBigInt(big.NewInt(notarySetSizeLoc))
}

// uint256 public dkgSetSize;
func (s *StateHelper) dkgSetSize() *big.Int {
    return s.getStateBigInt(big.NewInt(dkgSetSizeLoc))
}

// uint256 public roundInterval
func (s *StateHelper) roundInterval() *big.Int {
    return s.getStateBigInt(big.NewInt(roundIntervalLoc))
}

// uint256 public minBlockInterval
func (s *StateHelper) minBlockInterval() *big.Int {
    return s.getStateBigInt(big.NewInt(minBlockIntervalLoc))
}

// uint256 public maxBlockInterval
func (s *StateHelper) maxBlockInterval() *big.Int {
    return s.getStateBigInt(big.NewInt(maxBlockIntervalLoc))
}

func (s *StateHelper) emitConfigurationChangedEvent() {
    s.StateDB.AddLog(&types.Log{
        Address: s.Address,
        Topics:  []common.Hash{events["ConfigurationChanged"].Id()},
        Data:    []byte{},
    })
}

func (s *StateHelper) emitCRSProposed(round *big.Int, crs common.Hash) {
    s.StateDB.AddLog(&types.Log{
        Address: s.Address,
        Topics:  []common.Hash{events["CRSProposed"].Id(), common.BigToHash(round)},
        Data:    crs.Bytes(),
    })
}

// GovernanceContract represents the governance contract of DEXCON.
type GovernanceContract struct {
    evm      *EVM
    state    StateHelper
    contract *Contract
}

func newGovernanceContract(evm *EVM, contract *Contract) *GovernanceContract {
    return &GovernanceContract{
        evm:      evm,
        state:    StateHelper{contract.Address(), evm.StateDB},
        contract: contract,
    }
}

func (g *GovernanceContract) penalize() {
    g.contract.UseGas(g.contract.Gas)
}

func (g *GovernanceContract) updateConfiguration(config *configParams) ([]byte, error) {
    // Only owner can update configuration.
    if g.contract.Caller() != g.state.owner() {
        return nil, errExecutionReverted
    }

    g.state.setStateBigInt(big.NewInt(numChainsLoc), big.NewInt(int64(config.NumChains)))
    g.state.setStateBigInt(big.NewInt(lambdaBALoc), big.NewInt(int64(config.LambdaBA)))
    g.state.setStateBigInt(big.NewInt(lambdaDKGLoc), big.NewInt(int64(config.LambdaDKG)))
    g.state.setStateBigInt(big.NewInt(kLoc), big.NewInt(int64(config.K)))
    g.state.setStateBigInt(big.NewInt(phiRatioLoc), big.NewInt(int64(config.PhiRatio)))
    g.state.setStateBigInt(big.NewInt(notarySetSizeLoc), big.NewInt(int64(config.NotarySetSize)))
    g.state.setStateBigInt(big.NewInt(dkgSetSizeLoc), big.NewInt(int64(config.DKGSetSize)))
    g.state.setStateBigInt(big.NewInt(roundIntervalLoc), big.NewInt(int64(config.RoundInterval)))
    g.state.setStateBigInt(big.NewInt(minBlockIntervalLoc), big.NewInt(int64(config.MinBlockInterval)))
    g.state.setStateBigInt(big.NewInt(maxBlockIntervalLoc), big.NewInt(int64(config.MaxBlockInterval)))

    g.state.emitConfigurationChangedEvent()
    return nil, nil
}

func (g *GovernanceContract) stake(publicKey []byte) ([]byte, error) {
    caller := g.contract.Caller()
    offset := g.state.offset(caller)

    // Can not stake if already staked.
    if offset.Cmp(big.NewInt(0)) >= 0 {
        g.penalize()
        return nil, errExecutionReverted
    }

    // TODO(w): check of pk belongs to the address.
    offset = g.state.nodesLength()
    g.state.pushNode(&nodeInfo{
        owner:     caller,
        publicKey: publicKey,
        staked:    g.contract.Value(),
    })
    g.state.putOffset(caller, offset)

    g.contract.UseGas(21000)
    return nil, nil
}

func (g *GovernanceContract) unstake() ([]byte, error) {
    caller := g.contract.Caller()
    offset := g.state.offset(caller)
    if offset.Cmp(big.NewInt(0)) < 0 {
        return nil, errExecutionReverted
    }

    node := g.state.node(offset)
    length := g.state.nodesLength()
    lastIndex := new(big.Int).Sub(length, big.NewInt(1))

    // Delete the node.
    if offset != lastIndex {
        lastNode := g.state.node(lastIndex)
        g.state.updateNode(offset, lastNode)
        g.state.putOffset(lastNode.owner, offset)
        g.state.deleteOffset(caller)
    }

    // Return the staked fund.
    // TODO(w): use OP_CALL so this show up is internal transaction.
    g.evm.Transfer(g.evm.StateDB, GovernanceContractAddress, caller, node.staked)

    g.contract.UseGas(21000)
    return nil, nil
}

func (g *GovernanceContract) proposeCRS(signedCRS []byte) ([]byte, error) {
    round := g.state.lenCRS()
    prevCRS := g.state.crs(round)

    // round should be the next round number, abort otherwise.
    if new(big.Int).Add(round, big.NewInt(1)).Cmp(round) != 0 {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Prepare DKGMasterPublicKeys.
    // TODO(w): make sure DKGMasterPKs are unique.
    var dkgMasterPKs []*coreTypes.DKGMasterPublicKey
    for _, pk := range g.state.dkgMasterPublicKeys(round) {
        x := new(coreTypes.DKGMasterPublicKey)
        if err := rlp.DecodeBytes(pk, x); err != nil {
            panic(err)
        }
        dkgMasterPKs = append(dkgMasterPKs, x)
    }

    // Prepare DKGComplaints.
    var dkgComplaints []*coreTypes.DKGComplaint
    for _, comp := range g.state.dkgComplaints(round) {
        x := new(coreTypes.DKGComplaint)
        if err := rlp.DecodeBytes(comp, x); err != nil {
            panic(err)
        }
        dkgComplaints = append(dkgComplaints, x)
    }

    threshold := int(g.state.dkgSetSize().Uint64() / 3)

    dkgGPK, err := core.NewDKGGroupPublicKey(
        round.Uint64(), dkgMasterPKs, dkgComplaints, threshold)
    if err != nil {
        return nil, errExecutionReverted
    }
    signature := coreCrypto.Signature{
        Type:      "bls",
        Signature: signedCRS,
    }
    if !dkgGPK.VerifySignature(coreCommon.Hash(prevCRS), signature) {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Save new CRS into state and increase round.
    newCRS := crypto.Keccak256(signedCRS)
    crs := common.BytesToHash(newCRS)

    g.state.pushCRS(crs)
    g.state.emitCRSProposed(g.state.lenCRS(), crs)

    // To encourage DKG set to propose the correct value, correctly submitting
    // this should cause nothing.
    g.contract.UseGas(0)
    return nil, nil
}

func (g *GovernanceContract) addDKGComplaint(round *big.Int, comp []byte) ([]byte, error) {
    caller := g.contract.Caller()

    // Finalized caller is not allowed to propose complaint.
    if g.state.dkgFinalized(round, caller) {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Calculate 2f
    threshold := new(big.Int).Mul(
        big.NewInt(2),
        new(big.Int).Div(g.state.dkgSetSize(), big.NewInt(3)))

    // If 2f + 1 of DKG set is finalized, one can not propose complaint anymore.
    if g.state.dkgFinalizedsCount(round).Cmp(threshold) > 0 {
        return nil, errExecutionReverted
    }

    var dkgComplaint coreTypes.DKGComplaint
    if err := rlp.DecodeBytes(comp, &dkgComplaint); err != nil {
        g.penalize()
        return nil, errExecutionReverted
    }
    verified, _ := core.VerifyDKGComplaintSignature(&dkgComplaint)
    if !verified {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Verify that the message is sent from the caller.
    signer := nodeIDToAddress(dkgComplaint.ProposerID)
    if signer != caller {
        g.penalize()
        return nil, errExecutionReverted
    }

    g.state.pushDKGComplaint(round, comp)

    // Set this to relatively high to prevent spamming
    g.contract.UseGas(10000000)
    return nil, nil
}

func (g *GovernanceContract) addDKGMasterPublicKey(round *big.Int, pk []byte) ([]byte, error) {
    caller := g.contract.Caller()
    offset := g.state.offset(caller)

    // Can not add dkg mpk if not staked.
    if offset.Cmp(big.NewInt(0)) < 0 {
        return nil, errExecutionReverted
    }

    var dkgMasterPK coreTypes.DKGMasterPublicKey
    if err := rlp.DecodeBytes(pk, &dkgMasterPK); err != nil {
        g.penalize()
        return nil, errExecutionReverted
    }
    verified, _ := core.VerifyDKGMasterPublicKeySignature(&dkgMasterPK)
    if !verified {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Verify that the message is sent from the caller.
    signer := nodeIDToAddress(dkgMasterPK.ProposerID)
    if signer != caller {
        g.penalize()
        return nil, errExecutionReverted
    }

    g.state.pushDKGMasterPublicKey(round, pk)

    // DKG operation is expensive.
    g.contract.UseGas(100000)
    return nil, nil
}

func (g *GovernanceContract) addDKGFinalize(round *big.Int, finalize []byte) ([]byte, error) {
    caller := g.contract.Caller()

    var dkgFinalize coreTypes.DKGFinalize
    if err := rlp.DecodeBytes(finalize, &dkgFinalize); err != nil {
        g.penalize()
        return nil, errExecutionReverted
    }
    verified, _ := core.VerifyDKGFinalizeSignature(&dkgFinalize)
    if !verified {
        g.penalize()
        return nil, errExecutionReverted
    }

    // Verify that the message is sent from the caller.
    signer := nodeIDToAddress(dkgFinalize.ProposerID)
    if signer != caller {
        g.penalize()
        return nil, errExecutionReverted
    }

    if !g.state.dkgFinalized(round, caller) {
        g.state.putDKGFinalized(round, caller, true)
        g.state.incDKGFinalizedsCount(round)
    }

    // DKG operation is expensive.
    g.contract.UseGas(100000)
    return nil, nil
}