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 }