From cbd9e4cd4cabb10b80f549311d2cd3c3af63a557 Mon Sep 17 00:00:00 2001
From: Wei-Ning Huang <w@cobinhood.com>
Date: Wed, 3 Oct 2018 23:18:55 +0800
Subject: core: vm: implement governance contract methods

---
 core/vm/governance.go | 269 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 218 insertions(+), 51 deletions(-)

(limited to 'core/vm')

diff --git a/core/vm/governance.go b/core/vm/governance.go
index c3b316a64..bf3e33c39 100644
--- a/core/vm/governance.go
+++ b/core/vm/governance.go
@@ -6,6 +6,13 @@ import (
 
 	"github.com/dexon-foundation/dexon/accounts/abi"
 	"github.com/dexon-foundation/dexon/common"
+	"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"
+	"github.com/dexon-foundation/dexon-consensus-core/core/types"
 )
 
 var GovernanceContractAddress = common.BytesToAddress([]byte{0XED}) // Reverse of DEX0
@@ -347,11 +354,32 @@ func RunGovernanceContract(evm *EVM, input []byte, contract *Contract) (
 	case "unstake":
 		return g.unstake()
 	case "proposeCRS":
-		return g.proposeCRS()
+		args := struct {
+			Round     *big.Int
+			SignedCRS []byte
+		}{}
+		if err := method.Inputs.Unpack(&args, argument); err != nil {
+			return nil, errExecutionReverted
+		}
+		return g.proposeCRS(args.Round, args.SignedCRS)
 	case "addDKGMasterPublicKey":
-		return g.addDKGMasterPublicKey()
+		args := struct {
+			Round              *big.Int
+			DKGMasterPublicKey []byte
+		}{}
+		if err := method.Inputs.Unpack(&args, argument); err != nil {
+			return nil, errExecutionReverted
+		}
+		return g.addDKGMasterPublicKey(args.Round, args.DKGMasterPublicKey)
 	case "addDKGComplaint":
-		return g.addDKGComplaint()
+		args := struct {
+			Round        *big.Int
+			DKGComplaint []byte
+		}{}
+		if err := method.Inputs.Unpack(&args, argument); err != nil {
+			return nil, errExecutionReverted
+		}
+		return g.addDKGComplaint(args.Round, args.DKGComplaint)
 	}
 	return nil, nil
 }
@@ -361,43 +389,13 @@ type StateHelper struct {
 	StateDB StateDB
 }
 
-// 0: address public governanceMultisig;
-func (s *StateHelper) governanceMultisig() common.Address {
-	return common.Address{}
-}
-
-// 1: int256 public numChains;
-func (s *StateHelper) numChains() *big.Int {
-	return nil
-}
-
-// 2: int256 public lambdaBA;
-func (s *StateHelper) lambdaBA() *big.Int {
-	return nil
-}
-
-// 3: int256 public lambdaDKG;
-func (s *StateHelper) lambdaDKG() *big.Int {
-	return nil
-}
-
-// 4: int256 public k;
-func (s *StateHelper) k() *big.Int {
-	return nil
-}
-
-// 5: int256 public phiRatio;  // stored as PhiRatio * 10^6
-func (s *StateHelper) phiRatio() *big.Int {
-	return nil
-}
-
 // struct Node {
 //     address owner;
 //     bytes publicKey;
 //     uint256 staked;
 // }
 //
-// 6: Node[] nodes;
+// 0: Node[] nodes;
 
 type nodeInfo struct {
 	owner     common.Address
@@ -408,13 +406,15 @@ type nodeInfo struct {
 func (s *StateHelper) nodesLength() *big.Int {
 	return nil
 }
-func (s *StateHelper) node(offset *big.Int) *nodeInfo {
+func (s *StateHelper) node(index *big.Int) *nodeInfo {
 	return nil
 }
 func (s *StateHelper) pushNode(n *nodeInfo) {
 }
+func (s *StateHelper) updateNode(index *big.Int, n *nodeInfo) {
+}
 
-// 7: mapping(address => uint256) public offset;
+// 1: mapping(address => uint256) public offset;
 func (s *StateHelper) offset(addr common.Address) *big.Int {
 	return nil
 }
@@ -423,7 +423,29 @@ func (s *StateHelper) putOffset(addr common.Address, offset *big.Int) {
 func (s *StateHelper) deleteOffset(addr common.Address) {
 }
 
-// 8: uint256 public round;
+// 2: mapping(uint256 => bytes32) public crs;
+func (s *StateHelper) crs(round *big.Int) common.Hash {
+	return common.Hash{}
+}
+func (s *StateHelper) putCRS(round *big.Int, crs []byte) common.Hash {
+	return common.Hash{}
+}
+
+// 3: mapping(uint256 => bytes[]) public DKGMasterPublicKeys;
+func (s *StateHelper) dkgMasterPublicKeys(round *big.Int) [][]byte {
+	return nil
+}
+func (s *StateHelper) pushDKGMasterPublicKey(round *big.Int, pk []byte) {
+}
+
+// 4: mapping(uint256 => bytes[]) public DKGComplaints;
+func (s *StateHelper) dkgComplaints(round *big.Int) [][]byte {
+	return nil
+}
+func (s *StateHelper) addDKGComplaint(round *big.Int, complaint []byte) {
+}
+
+// 5: uint256 public round;
 func (s *StateHelper) round() *big.Int {
 	return nil
 }
@@ -431,26 +453,59 @@ func (s *StateHelper) incRound() *big.Int {
 	return nil
 }
 
-// 9: mapping(uint256 => bytes32) public crs;
-func (s *StateHelper) crs(round *big.Int) common.Hash {
-	return common.Hash{}
+// 6: address public governanceMultisig;
+func (s *StateHelper) governanceMultisig() common.Address {
+	return common.Address{}
 }
-func (s *StateHelper) pushCRS(round *big.Int, crs []byte) common.Hash {
-	return common.Hash{}
+
+// 7: uint256 public numChains;
+func (s *StateHelper) numChains() *big.Int {
+	return nil
 }
 
-// 10: mapping(uint256 => bytes[]) public DKGMasterPublicKeys;
-func (s *StateHelper) dkgMasterPublicKey(round *big.Int) [][]byte {
+// 8: uint256 public lambdaBA;
+func (s *StateHelper) lambdaBA() *big.Int {
 	return nil
 }
-func (s *StateHelper) addDKGMasterPublicKey(round *big.Int, pk []byte) {
+
+// 9: uint256 public lambdaDKG;
+func (s *StateHelper) lambdaDKG() *big.Int {
+	return nil
 }
 
-// 11: mapping(uint256 => bytes[]) public DKGComplaints;
-func (s *StateHelper) dkgComplaint(round *big.Int) [][]byte {
+// 10: uint256 public k;
+func (s *StateHelper) k() *big.Int {
 	return nil
 }
-func (s *StateHelper) addDKGComplaint(round *big.Int, complaint []byte) {
+
+// 11: uint256 public phiRatio;  // stored as PhiRatio * 10^6
+func (s *StateHelper) phiRatio() *big.Int {
+	return nil
+}
+
+// 12: uint256 public numNotarySet;
+func (s *StateHelper) numNotarySet() *big.Int {
+	return nil
+}
+
+// 13: uint256 public numDKGSet;
+func (s *StateHelper) numDKGSet() *big.Int {
+	return nil
+}
+
+// 14: uint256 public roundInterval
+func (s *StateHelper) roundInterval() *big.Int {
+	return nil
+}
+
+// 15: uint256 public minBlockInterval
+func (s *StateHelper) minBlockInterval() *big.Int {
+	return nil
+}
+
+// 16: uint256 public maxBlockInterval
+func (s *StateHelper) maxBlockInterval() *big.Int {
+	return nil
 }
 
 type GovernanceContract struct {
@@ -472,21 +527,133 @@ func (G *GovernanceContract) updateConfiguration() ([]byte, error) {
 }
 
 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 != nil {
+		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)
 	return nil, nil
 }
 
 func (g *GovernanceContract) unstake() ([]byte, error) {
+	caller := g.contract.Caller()
+	offset := g.state.offset(caller)
+	if offset == nil {
+		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.
+	g.evm.Transfer(g.evm.StateDB, GovernanceContractAddress, caller, node.staked)
 	return nil, nil
 }
 
-func (g *GovernanceContract) proposeCRS() ([]byte, error) {
+func (g *GovernanceContract) proposeCRS(round *big.Int, signedCRS []byte) ([]byte, error) {
+	crs := g.state.crs(round)
+
+	// Revert if CRS for that round already exists.
+	if crs != (common.Hash{}) {
+		return nil, errExecutionReverted
+	}
+
+	prevRound := g.state.round()
+	prevCRS := g.state.crs(prevRound)
+
+	// round should be the next round number, abort otherwise.
+	if new(big.Int).Add(prevRound, big.NewInt(1)).Cmp(round) != 0 {
+		return nil, errExecutionReverted
+	}
+
+	// Prepare DKGMasterPublicKeys.
+	// TODO(w): make sure DKGMasterPKs are unique.
+	var dkgMasterPKs []*types.DKGMasterPublicKey
+	for _, pk := range g.state.dkgMasterPublicKeys(round) {
+		x := new(types.DKGMasterPublicKey)
+		if err := rlp.DecodeBytes(pk, x); err != nil {
+			panic(err)
+		}
+		dkgMasterPKs = append(dkgMasterPKs, x)
+	}
+
+	// Prepare DKGComplaints.
+	var dkgComplaints []*types.DKGComplaint
+	for _, comp := range g.state.dkgComplaints(round) {
+		x := new(types.DKGComplaint)
+		if err := rlp.DecodeBytes(comp, x); err != nil {
+			panic(err)
+		}
+		dkgComplaints = append(dkgComplaints, x)
+	}
+
+	threshold := int(g.state.numDKGSet().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) {
+		return nil, errExecutionReverted
+	}
+
+	// Save new CRS into state and increase round.
+	newCRS := crypto.Keccak256(signedCRS)
+	g.state.incRound()
+	g.state.putCRS(round, newCRS)
+
 	return nil, nil
 }
 
-func (g *GovernanceContract) addDKGMasterPublicKey() ([]byte, error) {
+func (g *GovernanceContract) addDKGMasterPublicKey(round *big.Int, pk []byte) ([]byte, error) {
+	var dkgMasterPK types.DKGMasterPublicKey
+	if err := rlp.DecodeBytes(pk, &dkgMasterPK); err != nil {
+		return nil, errExecutionReverted
+	}
+	verified, _ := core.VerifyDKGMasterPublicKeySignature(&dkgMasterPK)
+	if !verified {
+		return nil, errExecutionReverted
+	}
+
+	g.state.pushDKGMasterPublicKey(round, pk)
 	return nil, nil
 }
 
-func (g *GovernanceContract) addDKGComplaint() ([]byte, error) {
+func (g *GovernanceContract) addDKGComplaint(round *big.Int, comp []byte) ([]byte, error) {
+	var dkgComplaint types.DKGComplaint
+	if err := rlp.DecodeBytes(comp, &dkgComplaint); err != nil {
+		return nil, errExecutionReverted
+	}
+	verified, _ := core.VerifyDKGComplaintSignature(&dkgComplaint)
+	if !verified {
+		return nil, errExecutionReverted
+	}
+
+	g.state.addDKGComplaint(round, comp)
 	return nil, nil
 }
-- 
cgit v1.2.3