From 0594f51ee194bc975a75d293a789d98f47f3f4d9 Mon Sep 17 00:00:00 2001
From: Jhih-Ming Huang <jm@byzantine-lab.io>
Date: Fri, 16 Aug 2019 08:28:50 +0000
Subject: core: add p2p whitelist for consortium

---
 core/vm/oracle_contract_abi.go   | 946 ++++++++++++++++++++++-----------------
 core/vm/oracle_contracts.go      | 182 +++++++-
 core/vm/oracle_contracts_test.go | 191 ++++++++
 dex/handler.go                   |  33 ++
 dex/handler_test.go              |  77 +++-
 dex/helper_test.go               |  11 +-
 p2p/peer_error.go                |   1 +
 params/config.go                 |  70 ++-
 params/gen_dexcon_config.go      |  12 +
 9 files changed, 1078 insertions(+), 445 deletions(-)

diff --git a/core/vm/oracle_contract_abi.go b/core/vm/oracle_contract_abi.go
index 535161bd4..23ec7cd9d 100644
--- a/core/vm/oracle_contract_abi.go
+++ b/core/vm/oracle_contract_abi.go
@@ -55,6 +55,60 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "MinStake",
+        "type": "uint256"
+      },
+      {
+        "name": "LockupPeriod",
+        "type": "uint256"
+      },
+      {
+        "name": "MinGasPrice",
+        "type": "uint256"
+      },
+      {
+        "name": "BlockGasLimit",
+        "type": "uint256"
+      },
+      {
+        "name": "LambdaBA",
+        "type": "uint256"
+      },
+      {
+        "name": "LambdaDKG",
+        "type": "uint256"
+      },
+      {
+        "name": "NotaryParamAlpha",
+        "type": "uint256"
+      },
+      {
+        "name": "NotaryParamBeta",
+        "type": "uint256"
+      },
+      {
+        "name": "RoundLength",
+        "type": "uint256"
+      },
+      {
+        "name": "MinBlockInterval",
+        "type": "uint256"
+      },
+      {
+        "name": "FineValues",
+        "type": "uint256[]"
+      }
+    ],
+    "name": "updateConfiguration",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [],
@@ -69,6 +123,39 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "PublicKey",
+        "type": "bytes"
+      }
+    ],
+    "name": "addDKGMasterPublicKey",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "name": "addressWhitelist",
+    "outputs": [
+      {
+        "name": "",
+        "type": "address"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -138,6 +225,20 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "MPKReady",
+        "type": "bytes"
+      }
+    ],
+    "name": "addDKGMPKReady",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [],
@@ -152,6 +253,20 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "Finalize",
+        "type": "bytes"
+      }
+    ],
+    "name": "addDKGFinalize",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [],
@@ -167,37 +282,45 @@ const GovernanceABIJSON = `
     "type": "function"
   },
   {
-    "constant": true,
-    "inputs": [],
-    "name": "minStake",
-    "outputs": [
+    "constant": false,
+    "inputs": [
       {
-        "name": "",
+        "name": "Amount",
         "type": "uint256"
       }
     ],
+    "name": "unstake",
+    "outputs": [],
     "payable": false,
-    "stateMutability": "view",
+    "stateMutability": "nonpayable",
     "type": "function"
   },
   {
-    "constant": true,
-    "inputs": [],
-    "name": "crsRound",
-    "outputs": [
+    "constant": false,
+    "inputs": [
       {
-        "name": "",
+        "name": "Type",
         "type": "uint256"
+      },
+      {
+        "name": "Arg1",
+        "type": "bytes"
+      },
+      {
+        "name": "Arg2",
+        "type": "bytes"
       }
     ],
+    "name": "report",
+    "outputs": [],
     "payable": false,
-    "stateMutability": "view",
+    "stateMutability": "nonpayable",
     "type": "function"
   },
   {
     "constant": true,
     "inputs": [],
-    "name": "notaryParamAlpha",
+    "name": "minStake",
     "outputs": [
       {
         "name": "",
@@ -209,32 +332,73 @@ const GovernanceABIJSON = `
     "type": "function"
   },
   {
-    "constant": true,
+    "constant": false,
     "inputs": [],
-    "name": "dkgSuccessesCount",
-    "outputs": [
+    "name": "stake",
+    "outputs": [],
+    "payable": true,
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [],
+    "name": "withdraw",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
       {
-        "name": "",
-        "type": "uint256"
+        "name": "NodeAddress",
+        "type": "address"
+      }
+    ],
+    "name": "payFine",
+    "outputs": [],
+    "payable": true,
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "NewPublicKey",
+        "type": "bytes"
       }
     ],
+    "name": "replaceNodePublicKey",
+    "outputs": [],
     "payable": false,
-    "stateMutability": "view",
+    "stateMutability": "nonpayable",
     "type": "function"
   },
   {
-    "constant": true,
+    "constant": false,
     "inputs": [
       {
-        "name": "",
+        "name": "NewOwner",
         "type": "address"
       }
     ],
-    "name": "dkgFinalizeds",
+    "name": "transferNodeOwnership",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "crsRound",
     "outputs": [
       {
         "name": "",
-        "type": "bool"
+        "type": "uint256"
       }
     ],
     "payable": false,
@@ -244,7 +408,7 @@ const GovernanceABIJSON = `
   {
     "constant": true,
     "inputs": [],
-    "name": "blockGasLimit",
+    "name": "notaryParamAlpha",
     "outputs": [
       {
         "name": "",
@@ -258,7 +422,7 @@ const GovernanceABIJSON = `
   {
     "constant": true,
     "inputs": [],
-    "name": "dkgRound",
+    "name": "dkgSuccessesCount",
     "outputs": [
       {
         "name": "",
@@ -269,20 +433,48 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "Success",
+        "type": "bytes"
+      }
+    ],
+    "name": "addDKGSuccess",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [],
-    "name": "totalStaked",
+    "name": "withdrawable",
     "outputs": [
       {
         "name": "",
-        "type": "uint256"
+        "type": "bool"
       }
     ],
     "payable": false,
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "NewSignedCRS",
+        "type": "bytes"
+      }
+    ],
+    "name": "resetDKG",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -291,11 +483,11 @@ const GovernanceABIJSON = `
         "type": "address"
       }
     ],
-    "name": "nodesOffsetByAddress",
+    "name": "dkgFinalizeds",
     "outputs": [
       {
         "name": "",
-        "type": "int256"
+        "type": "bool"
       }
     ],
     "payable": false,
@@ -305,11 +497,11 @@ const GovernanceABIJSON = `
   {
     "constant": true,
     "inputs": [],
-    "name": "crs",
+    "name": "blockGasLimit",
     "outputs": [
       {
         "name": "",
-        "type": "bytes32"
+        "type": "uint256"
       }
     ],
     "payable": false,
@@ -319,7 +511,7 @@ const GovernanceABIJSON = `
   {
     "constant": true,
     "inputs": [],
-    "name": "roundLength",
+    "name": "whitelistLength",
     "outputs": [
       {
         "name": "",
@@ -333,7 +525,119 @@ const GovernanceABIJSON = `
   {
     "constant": true,
     "inputs": [],
-    "name": "nextHalvingSupply",
+    "name": "dkgRound",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "OldOwner",
+        "type": "address"
+      },
+      {
+        "name": "NewOwner",
+        "type": "address"
+      }
+    ],
+    "name": "transferNodeOwnershipByFoundation",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "totalStaked",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [
+      {
+        "name": "",
+        "type": "address"
+      }
+    ],
+    "name": "nodesOffsetByAddress",
+    "outputs": [
+      {
+        "name": "",
+        "type": "int256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "crs",
+    "outputs": [
+      {
+        "name": "",
+        "type": "bytes32"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "addr",
+        "type": "address"
+      }
+    ],
+    "name": "removeFromWhitelist",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "roundLength",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "nextHalvingSupply",
     "outputs": [
       {
         "name": "",
@@ -377,6 +681,25 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": true,
+    "inputs": [
+      {
+        "name": "",
+        "type": "address"
+      }
+    ],
+    "name": "whitelistOffsetByAddress",
+    "outputs": [
+      {
+        "name": "",
+        "type": "int256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -462,6 +785,20 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "Complaint",
+        "type": "bytes"
+      }
+    ],
+    "name": "addDKGComplaint",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -528,6 +865,50 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "Round",
+        "type": "uint256"
+      },
+      {
+        "name": "SignedCRS",
+        "type": "bytes"
+      }
+    ],
+    "name": "proposeCRS",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "Name",
+        "type": "string"
+      },
+      {
+        "name": "Email",
+        "type": "string"
+      },
+      {
+        "name": "Location",
+        "type": "string"
+      },
+      {
+        "name": "Url",
+        "type": "string"
+      }
+    ],
+    "name": "updateNodeInfo",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -547,6 +928,36 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "PublicKey",
+        "type": "bytes"
+      },
+      {
+        "name": "Name",
+        "type": "string"
+      },
+      {
+        "name": "Email",
+        "type": "string"
+      },
+      {
+        "name": "Location",
+        "type": "string"
+      },
+      {
+        "name": "Url",
+        "type": "string"
+      }
+    ],
+    "name": "register",
+    "outputs": [],
+    "payable": true,
+    "stateMutability": "payable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -594,6 +1005,25 @@ const GovernanceABIJSON = `
     "stateMutability": "view",
     "type": "function"
   },
+  {
+    "constant": false,
+    "inputs": [
+      {
+        "name": "addr",
+        "type": "address"
+      }
+    ],
+    "name": "addToWhitelist",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
   {
     "constant": true,
     "inputs": [
@@ -666,30 +1096,72 @@ const GovernanceABIJSON = `
     "type": "function"
   },
   {
-    "anonymous": false,
-    "inputs": [],
-    "name": "ConfigurationChanged",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
+    "constant": false,
     "inputs": [
       {
-        "indexed": true,
-        "name": "Round",
-        "type": "uint256"
-      },
-      {
-        "indexed": false,
-        "name": "CRS",
-        "type": "bytes32"
+        "name": "NewOwner",
+        "type": "address"
       }
     ],
-    "name": "CRSProposed",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
+    "name": "transferOwnership",
+    "outputs": [],
+    "payable": false,
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "nodesLength",
+    "outputs": [
+      {
+        "name": "",
+        "type": "uint256"
+      }
+    ],
+    "payable": false,
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "constant": true,
+    "inputs": [],
+    "name": "isConsortium",
+    "outputs": [
+      {
+        "name": "",
+        "type": "bool"
+      }
+    ],
+    "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"
+  },
+  {
+    "anonymous": false,
     "inputs": [
       {
         "indexed": true,
@@ -874,374 +1346,6 @@ const GovernanceABIJSON = `
     ],
     "name": "DKGReset",
     "type": "event"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "NewOwner",
-        "type": "address"
-      }
-    ],
-    "name": "transferOwnership",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "MinStake",
-        "type": "uint256"
-      },
-      {
-        "name": "LockupPeriod",
-        "type": "uint256"
-      },
-      {
-        "name": "MinGasPrice",
-        "type": "uint256"
-      },
-      {
-        "name": "BlockGasLimit",
-        "type": "uint256"
-      },
-      {
-        "name": "LambdaBA",
-        "type": "uint256"
-      },
-      {
-        "name": "LambdaDKG",
-        "type": "uint256"
-      },
-      {
-        "name": "NotaryParamAlpha",
-        "type": "uint256"
-      },
-      {
-        "name": "NotaryParamBeta",
-        "type": "uint256"
-      },
-      {
-        "name": "RoundLength",
-        "type": "uint256"
-      },
-      {
-        "name": "MinBlockInterval",
-        "type": "uint256"
-      },
-      {
-        "name": "FineValues",
-        "type": "uint256[]"
-      }
-    ],
-    "name": "updateConfiguration",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Complaint",
-        "type": "bytes"
-      }
-    ],
-    "name": "addDKGComplaint",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "PublicKey",
-        "type": "bytes"
-      }
-    ],
-    "name": "addDKGMasterPublicKey",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "MPKReady",
-        "type": "bytes"
-      }
-    ],
-    "name": "addDKGMPKReady",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Finalize",
-        "type": "bytes"
-      }
-    ],
-    "name": "addDKGFinalize",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Success",
-        "type": "bytes"
-      }
-    ],
-    "name": "addDKGSuccess",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "NodeAddress",
-        "type": "address"
-      }
-    ],
-    "name": "payFine",
-    "outputs": [],
-    "payable": true,
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "constant": true,
-    "inputs": [],
-    "name": "nodesLength",
-    "outputs": [
-      {
-        "name": "",
-        "type": "uint256"
-      }
-    ],
-    "payable": false,
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Round",
-        "type": "uint256"
-      },
-      {
-        "name": "SignedCRS",
-        "type": "bytes"
-      }
-    ],
-    "name": "proposeCRS",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Type",
-        "type": "uint256"
-      },
-      {
-        "name": "Arg1",
-        "type": "bytes"
-      },
-      {
-        "name": "Arg2",
-        "type": "bytes"
-      }
-    ],
-    "name": "report",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "NewSignedCRS",
-        "type": "bytes"
-      }
-    ],
-    "name": "resetDKG",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "PublicKey",
-        "type": "bytes"
-      },
-      {
-        "name": "Name",
-        "type": "string"
-      },
-      {
-        "name": "Email",
-        "type": "string"
-      },
-      {
-        "name": "Location",
-        "type": "string"
-      },
-      {
-        "name": "Url",
-        "type": "string"
-      }
-    ],
-    "name": "register",
-    "outputs": [],
-    "payable": true,
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "NewPublicKey",
-        "type": "bytes"
-      }
-    ],
-    "name": "replaceNodePublicKey",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [],
-    "name": "stake",
-    "outputs": [],
-    "payable": true,
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "NewOwner",
-        "type": "address"
-      }
-    ],
-    "name": "transferNodeOwnership",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "OldOwner",
-        "type": "address"
-      },
-      {
-        "name": "NewOwner",
-        "type": "address"
-      }
-    ],
-    "name": "transferNodeOwnershipByFoundation",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Amount",
-        "type": "uint256"
-      }
-    ],
-    "name": "unstake",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [
-      {
-        "name": "Name",
-        "type": "string"
-      },
-      {
-        "name": "Email",
-        "type": "string"
-      },
-      {
-        "name": "Location",
-        "type": "string"
-      },
-      {
-        "name": "Url",
-        "type": "string"
-      }
-    ],
-    "name": "updateNodeInfo",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": false,
-    "inputs": [],
-    "name": "withdraw",
-    "outputs": [],
-    "payable": false,
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "constant": true,
-    "inputs": [],
-    "name": "withdrawable",
-    "outputs": [
-      {
-        "name": "",
-        "type": "bool"
-      }
-    ],
-    "payable": false,
-    "stateMutability": "view",
-    "type": "function"
   }
 ]
 `
diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go
index ddcd6d3f6..130d5bf24 100644
--- a/core/vm/oracle_contracts.go
+++ b/core/vm/oracle_contracts.go
@@ -99,6 +99,9 @@ const (
 	minBlockIntervalLoc
 	fineValuesLoc
 	finedRecordsLoc
+	isConsortiumLoc
+	addressWhitelistLoc
+	whitelistOffsetByAddressLoc
 )
 
 func publicKeyToNodeKeyAddress(pkBytes []byte) (common.Address, error) {
@@ -124,7 +127,7 @@ func IdToAddress(id coreTypes.NodeID) common.Address {
 	return common.BytesToAddress(id.Hash[12:])
 }
 
-// State manipulation helper fro the governance contract.
+// State manipulation helper for the governance contract.
 type GovernanceState struct {
 	StateDB StateDB
 }
@@ -984,6 +987,80 @@ func (s *GovernanceState) SetFineRecords(recordHash Bytes32, status bool) {
 	s.setStateBigInt(loc, big.NewInt(value))
 }
 
+// bool public isConsortium;
+func (s *GovernanceState) IsConsortium() bool {
+	return s.getStateBigInt(big.NewInt(isConsortiumLoc)).Cmp(big.NewInt(0)) > 0
+}
+func (s *GovernanceState) EnableConsortium() {
+	s.setStateBigInt(big.NewInt(isConsortiumLoc), big.NewInt(1))
+}
+
+// address[] public addressWhitelist;
+func (s *GovernanceState) LenWhitelist() *big.Int {
+	return s.getStateBigInt(big.NewInt(addressWhitelistLoc))
+}
+func (s *GovernanceState) AddressWhitelist(index *big.Int) common.Address {
+	arrayBaseLoc := s.getSlotLoc(big.NewInt(addressWhitelistLoc))
+	return common.BigToAddress(s.getStateBigInt(new(big.Int).Add(arrayBaseLoc, index)))
+}
+func (s *GovernanceState) AddressWhitelists() []common.Address {
+	len := s.LenWhitelist()
+	result := make([]common.Address, len.Uint64())
+	for i := 0; i < int(len.Uint64()); i++ {
+		result[i] = s.AddressWhitelist(big.NewInt(int64(i)))
+	}
+	return result
+}
+func (s *GovernanceState) putAddressWhitelist(addr common.Address, offset *big.Int) {
+	arrayBaseLoc := s.getSlotLoc(big.NewInt(addressWhitelistLoc))
+	loc := new(big.Int).Add(arrayBaseLoc, offset)
+	s.setState(common.BigToHash(loc), addr.Hash())
+	s.putWhitelistOffsetByAddress(addr, offset)
+}
+func (s *GovernanceState) AddToWhitelist(addr common.Address) *big.Int {
+	offset := s.WhitelistOffsetByAddress(addr)
+	if offset.Cmp(bigZero) >= 0 {
+		return offset
+	}
+	len := s.getStateBigInt(big.NewInt(addressWhitelistLoc))
+	s.putAddressWhitelist(addr, len)
+	len = new(big.Int).Add(len, big.NewInt(1))
+	s.setStateBigInt(big.NewInt(addressWhitelistLoc), len)
+	return len
+}
+func (s *GovernanceState) DeleteAddressWhitelist(addr common.Address) *big.Int {
+	offset := s.WhitelistOffsetByAddress(addr)
+	if offset.Cmp(bigZero) < 0 {
+		return offset
+	}
+	s.DeleteWhitelistOffsetByAddress(addr)
+	len := s.getStateBigInt(big.NewInt(addressWhitelistLoc))
+	newLen := new(big.Int).Sub(len, big.NewInt(1))
+	if len.Cmp(big.NewInt(1)) > 0 && offset.Cmp(newLen) != 0 {
+		lastAddr := s.AddressWhitelist(newLen)
+		s.putAddressWhitelist(lastAddr, offset)
+	}
+	s.setStateBigInt(
+		big.NewInt(addressWhitelistLoc),
+		newLen,
+	)
+	return len
+}
+
+// mapping(address => int256) whitelistOffsetByAddress
+func (s *GovernanceState) WhitelistOffsetByAddress(addr common.Address) *big.Int {
+	loc := s.getMapLoc(big.NewInt(whitelistOffsetByAddressLoc), addr.Bytes())
+	return new(big.Int).Sub(s.getStateBigInt(loc), big.NewInt(1))
+}
+func (s *GovernanceState) putWhitelistOffsetByAddress(addr common.Address, offset *big.Int) {
+	loc := s.getMapLoc(big.NewInt(whitelistOffsetByAddressLoc), addr.Bytes())
+	s.setStateBigInt(loc, new(big.Int).Add(offset, big.NewInt(1)))
+}
+func (s *GovernanceState) DeleteWhitelistOffsetByAddress(addr common.Address) {
+	loc := s.getMapLoc(big.NewInt(whitelistOffsetByAddressLoc), addr.Bytes())
+	s.setStateBigInt(loc, big.NewInt(0))
+}
+
 // Initialize initializes governance contract state.
 func (s *GovernanceState) Initialize(config *params.DexconConfig, totalSupply *big.Int) {
 	if config.NextHalvingSupply.Cmp(totalSupply) <= 0 {
@@ -1009,6 +1086,22 @@ func (s *GovernanceState) Initialize(config *params.DexconConfig, totalSupply *b
 
 	// Set DKGRound.
 	s.SetDKGRound(big.NewInt(int64(dexCore.DKGDelayRound)))
+
+	// Set Whitelist
+	s.setWhitelist(config.IsConsortium)
+}
+
+func (s *GovernanceState) setWhitelist(isConsortium bool) {
+	if isConsortium {
+		s.EnableConsortium()
+		for _, node := range s.Nodes() {
+			address, err := publicKeyToNodeKeyAddress(node.PublicKey)
+			if err != nil {
+				panic(err)
+			}
+			s.AddToWhitelist(address)
+		}
+	}
 }
 
 // Register is a helper function for creating genesis state.
@@ -1079,6 +1172,8 @@ func (s *GovernanceState) Configuration() *params.DexconConfig {
 		RoundLength:       s.getStateBigInt(big.NewInt(roundLengthLoc)).Uint64(),
 		MinBlockInterval:  s.getStateBigInt(big.NewInt(minBlockIntervalLoc)).Uint64(),
 		FineValues:        s.FineValues(),
+		AddressWhitelist:  s.AddressWhitelists(),
+		IsConsortium:      s.getStateBigInt(big.NewInt(isConsortiumLoc)).Uint64() != 0,
 	}
 }
 
@@ -1098,7 +1193,11 @@ func (s *GovernanceState) UpdateConfiguration(cfg *params.DexconConfig) {
 	s.setStateBigInt(big.NewInt(roundLengthLoc), big.NewInt(int64(cfg.RoundLength)))
 	s.setStateBigInt(big.NewInt(minBlockIntervalLoc), big.NewInt(int64(cfg.MinBlockInterval)))
 	s.SetFineValues(cfg.FineValues)
-
+	if cfg.IsConsortium {
+		for _, addr := range cfg.AddressWhitelist {
+			s.AddToWhitelist(addr)
+		}
+	}
 	// Calculate set size.
 	s.CalNotarySetSize()
 }
@@ -2274,6 +2373,20 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return g.addDKGSuccess(Success)
+	case "addToWhitelist":
+		var address common.Address
+		if err := method.Inputs.Unpack(&address, arguments); err != nil {
+			return nil, errExecutionReverted
+		}
+		offset, err := g.addToWhitelist(address)
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		res, err := method.Outputs.Pack(offset)
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	case "nodesLength":
 		res, err := method.Outputs.Pack(g.state.LenNodes())
 		if err != nil {
@@ -2295,6 +2408,20 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return g.proposeCRS(args.Round, args.SignedCRS)
+	case "removeFromWhitelist":
+		var address common.Address
+		if err := method.Inputs.Unpack(&address, arguments); err != nil {
+			return nil, errExecutionReverted
+		}
+		offset, err := g.removeFromWhitelist(address)
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		res, err := method.Outputs.Pack(offset)
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	case "report":
 		args := struct {
 			Type *big.Int
@@ -2371,6 +2498,12 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return g.updateNodeInfo(args.Name, args.Email, args.Location, args.Url)
+	case "whitelistLength":
+		res, err := method.Outputs.Pack(g.state.LenWhitelist())
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	case "withdraw":
 		return g.withdraw()
 	case "withdrawable":
@@ -2379,11 +2512,21 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return res, nil
-
 	// --------------------------------
 	// Solidity auto generated methods.
 	// --------------------------------
 
+	case "addressWhitelist":
+		offset := new(big.Int)
+		if err := method.Inputs.Unpack(&offset, arguments); err != nil {
+			return nil, errExecutionReverted
+		}
+		address := g.state.AddressWhitelist(offset)
+		res, err := method.Outputs.Pack(address)
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	case "blockGasLimit":
 		res, err := method.Outputs.Pack(g.state.BlockGasLimit())
 		if err != nil {
@@ -2538,6 +2681,12 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return res, nil
+	case "isConsortium":
+		res, err := method.Outputs.Pack(g.state.IsConsortium())
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	case "lambdaBA":
 		res, err := method.Outputs.Pack(g.state.LambdaBA())
 		if err != nil {
@@ -2694,7 +2843,18 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re
 			return nil, errExecutionReverted
 		}
 		return res, nil
+	case "whitelistOffsetByAddress":
+		address := common.Address{}
+		if err := method.Inputs.Unpack(&address, arguments); err != nil {
+			return nil, errExecutionReverted
+		}
+		res, err := method.Outputs.Pack(g.state.WhitelistOffsetByAddress(address))
+		if err != nil {
+			return nil, errExecutionReverted
+		}
+		return res, nil
 	}
+
 	return nil, errExecutionReverted
 }
 
@@ -2801,6 +2961,22 @@ func (g *GovernanceContract) replaceNodePublicKey(newPublicKey []byte) ([]byte,
 	return nil, nil
 }
 
+func (g *GovernanceContract) addToWhitelist(addr common.Address) (*big.Int, error) {
+	// Only owner can update whitelist.
+	if g.contract.Caller() != g.state.Owner() {
+		return nil, errExecutionReverted
+	}
+	return g.state.AddToWhitelist(addr), nil
+}
+
+func (g *GovernanceContract) removeFromWhitelist(addr common.Address) (*big.Int, error) {
+	// Only owner can update whitelist.
+	if g.contract.Caller() != g.state.Owner() {
+		return nil, errExecutionReverted
+	}
+	return g.state.DeleteAddressWhitelist(addr), nil
+}
+
 func PackProposeCRS(round uint64, signedCRS []byte) ([]byte, error) {
 	method := GovernanceABI.Name2Method["proposeCRS"]
 	res, err := method.Inputs.Pack(big.NewInt(int64(round)), signedCRS)
diff --git a/core/vm/oracle_contracts_test.go b/core/vm/oracle_contracts_test.go
index 192628b22..bf2f7c71a 100644
--- a/core/vm/oracle_contracts_test.go
+++ b/core/vm/oracle_contracts_test.go
@@ -1022,6 +1022,137 @@ func (g *GovernanceContractTestSuite) TestMiscVariableReading() {
 	g.Require().NoError(err)
 }
 
+func (g *GovernanceContractTestSuite) TestIsConsortium() {
+	var isConsortium bool
+
+	_, addr1 := newPrefundAccount(g.stateDB)
+
+	input, err := GovernanceABI.ABI.Pack("isConsortium")
+	g.Require().NoError(err)
+	res, err := g.call(GovernanceContractAddress, addr1, input, big.NewInt(0))
+	g.Require().NoError(err)
+	err = GovernanceABI.ABI.Unpack(&isConsortium, "isConsortium", res)
+	g.Require().NoError(err)
+	g.Require().Equal(false, isConsortium)
+
+	g.s.EnableConsortium()
+	res, err = g.call(GovernanceContractAddress, addr1, input, big.NewInt(0))
+	g.Require().NoError(err)
+	err = GovernanceABI.ABI.Unpack(&isConsortium, "isConsortium", res)
+	g.Require().NoError(err)
+	g.Require().Equal(true, isConsortium)
+}
+
+func (g *GovernanceContractTestSuite) TestUpdateWhitelist() {
+	checkLength := func(expectLen *big.Int) {
+		var length *big.Int
+
+		lenInput, err := GovernanceABI.ABI.Pack("whitelistLength")
+		g.Require().NoError(err)
+		res, err := g.call(GovernanceContractAddress, g.config.Owner, lenInput, big.NewInt(0))
+		err = GovernanceABI.ABI.Unpack(&length, "whitelistLength", res)
+		g.Require().NoError(err)
+		g.Require().Equal(expectLen.Int64(), length.Int64())
+	}
+	check := func(addr common.Address, offset *big.Int) {
+		var resOffset *big.Int
+		var resAddr common.Address
+
+		value := big.NewInt(0)
+		// check offset
+		input, err := GovernanceABI.ABI.Pack("whitelistOffsetByAddress", addr)
+		g.Require().NoError(err)
+		res, err := g.call(GovernanceContractAddress, addr, input, value)
+		g.Require().NoError(err)
+		err = GovernanceABI.ABI.Unpack(&resOffset, "whitelistOffsetByAddress", res)
+		g.Require().NoError(err)
+		g.Require().Equal(offset.Int64(), resOffset.Int64())
+
+		// check address
+		if offset.Cmp(big.NewInt(0)) >= 0 {
+			input, err = GovernanceABI.ABI.Pack("addressWhitelist", offset)
+			res, err = g.call(GovernanceContractAddress, addr, input, value)
+			g.Require().NoError(err)
+			err = GovernanceABI.ABI.Unpack(&resAddr, "addressWhitelist", res)
+			g.Require().NoError(err)
+			g.Require().Equal(addr, resAddr)
+		}
+	}
+	_, addr1 := newPrefundAccount(g.stateDB)
+	_, addr2 := newPrefundAccount(g.stateDB)
+	_, addr3 := newPrefundAccount(g.stateDB)
+
+	input, err := GovernanceABI.ABI.Pack("addToWhitelist", addr1)
+	g.Require().NoError(err)
+
+	// Call with non-owner.
+	_, err = g.call(GovernanceContractAddress, addr1, input, big.NewInt(0))
+	g.Require().NotNil(err)
+	// Call with owner.
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	checkLength(big.NewInt(1))
+	// duplicated call should not affect
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	checkLength(big.NewInt(1))
+	// append addr2
+	input, err = GovernanceABI.ABI.Pack("addToWhitelist", addr2)
+	g.Require().NoError(err)
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+
+	checkLength(big.NewInt(2))
+	check(addr1, big.NewInt(0))
+	check(addr2, big.NewInt(1))
+
+	// delete addr2
+	input, err = GovernanceABI.ABI.Pack("removeFromWhitelist", addr2)
+	g.Require().NoError(err)
+	// Call with non-owner.
+	_, err = g.call(GovernanceContractAddress, addr1, input, big.NewInt(0))
+	g.Require().NotNil(err)
+	// Call with owner
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	check(addr2, big.NewInt(-1))
+	checkLength(big.NewInt(1))
+
+	// append addr2 back
+	input, err = GovernanceABI.ABI.Pack("addToWhitelist", addr2)
+	g.Require().NoError(err)
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	checkLength(big.NewInt(2))
+	check(addr1, big.NewInt(0))
+	check(addr2, big.NewInt(1))
+
+	// delete addr1
+	input, err = GovernanceABI.ABI.Pack("removeFromWhitelist", addr1)
+	g.Require().NoError(err)
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	check(addr1, big.NewInt(-1))
+	check(addr2, big.NewInt(0))
+	checkLength(big.NewInt(1))
+
+	// delete addr2
+	input, err = GovernanceABI.ABI.Pack("removeFromWhitelist", addr2)
+	g.Require().NoError(err)
+	_, err = g.call(GovernanceContractAddress, g.config.Owner, input, big.NewInt(0))
+	g.Require().NoError(err)
+	check(addr1, big.NewInt(-1))
+	check(addr2, big.NewInt(-1))
+	checkLength(big.NewInt(0))
+
+	// duplicated delete addr2
+	input, err = GovernanceABI.ABI.Pack("removeFromWhitelist", addr2)
+	g.Require().NoError(err)
+	// delete not in whitelist address
+	input, err = GovernanceABI.ABI.Pack("removeFromWhitelist", addr3)
+	g.Require().NoError(err)
+}
+
 func (g *GovernanceContractTestSuite) TestHalvingCondition() {
 	// TotalSupply 2.5B reached
 	g.s.MiningHalved()
@@ -1244,6 +1375,66 @@ func (g *GovernanceContractTestSuite) TestResetDKG() {
 	}
 }
 
+func (g *GovernanceContractTestSuite) TestConsortium() {
+	g.Require().False(g.s.IsConsortium())
+	g.s.EnableConsortium()
+	g.Require().True(g.s.IsConsortium())
+}
+
+func (g *GovernanceContractTestSuite) TestAddWhitelist() {
+	_, addr1 := newPrefundAccount(g.stateDB)
+	_, addr2 := newPrefundAccount(g.stateDB)
+	_, addr3 := newPrefundAccount(g.stateDB)
+	// no address in list
+	expectAddress := g.s.AddressWhitelist(big.NewInt(0))
+	g.Require().Equal(common.Address{}, expectAddress)
+	expectAddrList := g.s.AddressWhitelists()
+	g.Require().Equal([]common.Address{}, expectAddrList)
+
+	// insert addr
+	g.s.AddToWhitelist(addr1)
+	g.s.AddToWhitelist(addr1) // duplicated insertion should be ignored
+	expectAddress = g.s.AddressWhitelist(big.NewInt(0))
+	g.Require().Equal(addr1, expectAddress)
+	expectAddrList = g.s.AddressWhitelists()
+	g.Require().Equal([]common.Address{addr1}, expectAddrList)
+
+	g.s.AddToWhitelist(addr2)
+	expectAddress = g.s.AddressWhitelist(big.NewInt(1))
+	g.Require().Equal(addr2, expectAddress)
+	expectAddrList = g.s.AddressWhitelists()
+	g.Require().Equal([]common.Address{addr1, addr2}, expectAddrList)
+
+	offset := g.s.WhitelistOffsetByAddress(addr1)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(0)) == 0)
+	offset = g.s.WhitelistOffsetByAddress(addr2)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(1)) == 0)
+	offset = g.s.WhitelistOffsetByAddress(addr3)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(-1)) == 0)
+
+	// remove addr
+	g.s.DeleteAddressWhitelist(addr1)
+	g.s.DeleteAddressWhitelist(addr1) // duplicated remove should be ignored
+
+	offset = g.s.WhitelistOffsetByAddress(addr1)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(-1)) == 0)
+	offset = g.s.WhitelistOffsetByAddress(addr2)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(0)) == 0)
+
+	expectAddress = g.s.AddressWhitelist(big.NewInt(0))
+	g.Require().Equal(addr2, expectAddress)
+	expectAddrList = g.s.AddressWhitelists()
+	g.Require().Equal([]common.Address{addr2}, expectAddrList)
+
+	g.s.DeleteAddressWhitelist(addr2)
+
+	offset = g.s.WhitelistOffsetByAddress(addr2)
+	g.Require().Equal(true, offset.Cmp(big.NewInt(-1)) == 0)
+
+	expectAddrList = g.s.AddressWhitelists()
+	g.Require().Equal([]common.Address{}, expectAddrList)
+}
+
 func TestGovernanceContract(t *testing.T) {
 	suite.Run(t, new(GovernanceContractTestSuite))
 }
diff --git a/dex/handler.go b/dex/handler.go
index ca26bc6e8..4c1ed9a36 100644
--- a/dex/handler.go
+++ b/dex/handler.go
@@ -41,6 +41,7 @@ import (
 	"errors"
 	"fmt"
 	"math"
+	"math/big"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -55,6 +56,7 @@ import (
 	"github.com/tangerine-network/go-tangerine/consensus"
 	"github.com/tangerine-network/go-tangerine/core"
 	"github.com/tangerine-network/go-tangerine/core/types"
+	"github.com/tangerine-network/go-tangerine/core/vm"
 	"github.com/tangerine-network/go-tangerine/crypto"
 	dexDB "github.com/tangerine-network/go-tangerine/dex/db"
 	"github.com/tangerine-network/go-tangerine/dex/downloader"
@@ -89,6 +91,7 @@ const (
 
 	maxAgreementResultBroadcast = 3
 	maxFinalizedBlockBroadcast  = 3
+	checkPeerDuration           = 10 * time.Minute
 )
 
 // errIncompatibleConfig is returned if the requested protocols and configs are
@@ -349,6 +352,7 @@ func (pm *ProtocolManager) ReportBadPeerChan() chan<- interface{} {
 }
 
 func (pm *ProtocolManager) badPeerWatchLoop() {
+	go pm.checkPeerInWhitelist(pm.reportBadPeerChan)
 	for {
 		select {
 		case id := <-pm.reportBadPeerChan:
@@ -360,13 +364,42 @@ func (pm *ProtocolManager) badPeerWatchLoop() {
 	}
 }
 
+func (pm *ProtocolManager) checkPeerInWhitelist(reportBadPeerChan chan<- interface{}) {
+	for {
+		for id, p := range pm.peers.peers {
+			if !pm.inWhitelist(p) {
+				reportBadPeerChan <- id
+			}
+		}
+		time.Sleep(checkPeerDuration)
+	}
+}
+
 func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
 	return newPeer(pv, p, newMeteredMsgWriter(rw))
 }
 
+func (pm *ProtocolManager) inWhitelist(p *peer) bool {
+	state, err := pm.blockchain.State()
+	if err != nil {
+		p.Log().Debug("get state fail in checking whitelist", "err", err)
+		return false
+	}
+	govState := vm.GovernanceState{StateDB: state}
+	if !govState.IsConsortium() {
+		return true
+	}
+	address := crypto.PubkeyToAddress(*p.Node().Pubkey())
+	return govState.WhitelistOffsetByAddress(address).Cmp(big.NewInt(0)) >= 0
+}
+
 // handle is the callback invoked to manage the life cycle of an eth peer. When
 // this function terminates, the peer is disconnected.
 func (pm *ProtocolManager) handle(p *peer) error {
+	if !pm.inWhitelist(p) {
+		p.Log().Debug("Peer disconnect: permission denied", "name", p.Name())
+		return p2p.DiscPermissionDenied
+	}
 	// Ignore maxPeers if this is a trusted peer
 	if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted {
 		return p2p.DiscTooManyPeers
diff --git a/dex/handler_test.go b/dex/handler_test.go
index 80f36ae9e..f276e1e4a 100644
--- a/dex/handler_test.go
+++ b/dex/handler_test.go
@@ -17,9 +17,12 @@
 package dex
 
 import (
+	"fmt"
+	"io/ioutil"
 	"math"
 	"math/big"
 	"math/rand"
+	"net"
 	"testing"
 
 	"github.com/tangerine-network/go-tangerine/common"
@@ -30,6 +33,7 @@ import (
 	"github.com/tangerine-network/go-tangerine/dex/downloader"
 	"github.com/tangerine-network/go-tangerine/ethdb"
 	"github.com/tangerine-network/go-tangerine/p2p"
+	"github.com/tangerine-network/go-tangerine/p2p/enode"
 	"github.com/tangerine-network/go-tangerine/params"
 )
 
@@ -52,7 +56,7 @@ func TestProtocolCompatibility(t *testing.T) {
 	for i, tt := range tests {
 		ProtocolVersions = []uint{tt.version}
 
-		pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil)
+		pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil, params.TestChainConfig)
 		if pm != nil {
 			defer pm.Stop()
 		}
@@ -444,3 +448,74 @@ func testGetReceipt(t *testing.T, protocol int) {
 		t.Errorf("receipts mismatch: %v", err)
 	}
 }
+
+func TestHandlerWithConsoritum62(t *testing.T) { testHandlerWithConsoritum(t, 62) }
+func TestHandlerWithConsoritum63(t *testing.T) { testHandlerWithConsoritum(t, 63) }
+
+func testHandlerWithConsoritum(t *testing.T, version int) {
+	key, err := crypto.GenerateKey()
+	if err != nil {
+		t.Errorf("gen key fail %v", err)
+	}
+
+	// set config and create protocol manager
+	config := params.NewTestChainConig()
+	config.ChainID = big.NewInt(int64(DefaultConfig.NetworkId))
+	config.Dexcon = params.NewTestDexonConfig()
+	config.Dexcon.IsConsortium = true
+	pm, _, err := newTestProtocolManager(downloader.FullSync, 0, nil, nil, config)
+	defer pm.Stop()
+	if err != nil {
+		t.Fatalf("Failed to create protocol manager: %v", err)
+	}
+
+	// create peer
+	pipenet1, pipenet2 := p2p.MsgPipe()
+	defer func() {
+		pipenet1.Close()
+		pipenet2.Close()
+	}()
+	node := enode.NewV4(&key.PublicKey, net.IP{}, 0, 0)
+	peer := pm.newPeer(version, p2p.NewPeerWithEnode(node, "handlerTest", nil), pipenet1)
+
+	// try to call handle, and should get permission denied error
+	if err := pm.handle(peer); err != p2p.DiscPermissionDenied {
+		t.Errorf("Expect get DiscPermissionDenied, but get %v", err)
+	}
+
+	// add address to whitelist, and create new pm
+	address := crypto.PubkeyToAddress(key.PublicKey)
+	config.Dexcon.AddressWhitelist = []common.Address{address}
+	pm2, _, err := newTestProtocolManager(downloader.FullSync, 0, nil, nil, config)
+	defer pm2.Stop()
+
+	if err != nil {
+		t.Fatalf("Failed to create protocol manager: %v", err)
+	}
+	handleErr := make(chan error)
+	go func() {
+		handleErr <- pm2.handle(peer)
+	}()
+	// do the handshake
+	msg, err := pipenet2.ReadMsg()
+	ioutil.ReadAll(msg.Payload)
+	p2p.Send(pipenet2, 0,
+		statusData{
+			ProtocolVersion: uint32(version),
+			NetworkId:       config.ChainID.Uint64(),
+			Number:          0,
+			CurrentBlock:    common.Hash{},
+			GenesisBlock:    pm2.blockchain.Genesis().Hash(),
+		},
+	)
+	// send status code to terminate the handleMsg loop
+	p2p.Send(pipenet2, StatusMsg, struct{}{})
+	err = <-handleErr
+	expectError := fmt.Errorf("%v - %v",
+		errorToString[ErrExtraStatusMsg],
+		"uncontrolled status message",
+	)
+	if err.Error() != expectError.Error() {
+		t.Errorf("err not match, expect: %s, but got: %s", expectError, err)
+	}
+}
diff --git a/dex/helper_test.go b/dex/helper_test.go
index 6e2cd3d77..5e171fef0 100644
--- a/dex/helper_test.go
+++ b/dex/helper_test.go
@@ -110,13 +110,18 @@ func (a *testApp) SubscribeNewFinalizedBlockEvent(
 // newTestProtocolManager creates a new protocol manager for testing purposes,
 // with the given number of blocks already known, and potential notification
 // channels for different events.
-func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase, error) {
+func newTestProtocolManager(mode downloader.SyncMode,
+	blocks int,
+	generator func(int, *core.BlockGen),
+	newtx chan<- []*types.Transaction,
+	chainConfig *params.ChainConfig,
+) (*ProtocolManager, *ethdb.MemDatabase, error) {
 	var (
 		evmux  = new(event.TypeMux)
 		engine = ethash.NewFaker()
 		db     = ethdb.NewMemDatabase()
 		gspec  = &core.Genesis{
-			Config: params.TestChainConfig,
+			Config: chainConfig,
 			Alloc:  core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000), Staked: big.NewInt(0)}},
 		}
 		genesis       = gspec.MustCommit(db)
@@ -150,7 +155,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
 // channels for different events. In case of an error, the constructor force-
 // fails the test.
 func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) {
-	pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx)
+	pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx, params.TestChainConfig)
 	if err != nil {
 		t.Fatalf("Failed to create protocol manager: %v", err)
 	}
diff --git a/p2p/peer_error.go b/p2p/peer_error.go
index ab61bfef0..f410b76a8 100644
--- a/p2p/peer_error.go
+++ b/p2p/peer_error.go
@@ -69,6 +69,7 @@ const (
 	DiscUnexpectedIdentity
 	DiscSelf
 	DiscReadTimeout
+	DiscPermissionDenied
 	DiscSubprotocolError = 0x10
 )
 
diff --git a/params/config.go b/params/config.go
index c884b8cdc..b00fc6ee9 100644
--- a/params/config.go
+++ b/params/config.go
@@ -267,22 +267,24 @@ func (c *CliqueConfig) String() string {
 
 // DexconConfig is the consensus engine configs for DEXON consensus.
 type DexconConfig struct {
-	GenesisCRSText    string         `json:"genesisCRSText"`
-	Owner             common.Address `json:"owner"`
-	MinStake          *big.Int       `json:"minStake"`
-	LockupPeriod      uint64         `json:"lockupPeriod"`
-	MiningVelocity    float32        `json:"miningVelocity"`
-	NextHalvingSupply *big.Int       `json:"nextHalvingSupply"`
-	LastHalvedAmount  *big.Int       `json:"lastHalvedAmount"`
-	MinGasPrice       *big.Int       `json:"minGasPrice"`
-	BlockGasLimit     uint64         `json:"blockGasLimit"`
-	LambdaBA          uint64         `json:"lambdaBA"`
-	LambdaDKG         uint64         `json:"lambdaDKG"`
-	NotaryParamAlpha  float32        `json:"notaryParamAlpha"`
-	NotaryParamBeta   float32        `json:"notaryParamBeta"`
-	RoundLength       uint64         `json:"roundLength"`
-	MinBlockInterval  uint64         `json:"minBlockInterval"`
-	FineValues        []*big.Int     `json:"fineValues"`
+	GenesisCRSText    string           `json:"genesisCRSText"`
+	Owner             common.Address   `json:"owner"`
+	MinStake          *big.Int         `json:"minStake"`
+	LockupPeriod      uint64           `json:"lockupPeriod"`
+	MiningVelocity    float32          `json:"miningVelocity"`
+	NextHalvingSupply *big.Int         `json:"nextHalvingSupply"`
+	LastHalvedAmount  *big.Int         `json:"lastHalvedAmount"`
+	MinGasPrice       *big.Int         `json:"minGasPrice"`
+	BlockGasLimit     uint64           `json:"blockGasLimit"`
+	LambdaBA          uint64           `json:"lambdaBA"`
+	LambdaDKG         uint64           `json:"lambdaDKG"`
+	NotaryParamAlpha  float32          `json:"notaryParamAlpha"`
+	NotaryParamBeta   float32          `json:"notaryParamBeta"`
+	RoundLength       uint64           `json:"roundLength"`
+	MinBlockInterval  uint64           `json:"minBlockInterval"`
+	FineValues        []*big.Int       `json:"fineValues"`
+	IsConsortium      bool             `json:"isConsortium"`
+	AddressWhitelist  []common.Address `json:"addressWhitelist"`
 }
 
 type dexconConfigSpecMarshaling struct {
@@ -295,7 +297,7 @@ type dexconConfigSpecMarshaling struct {
 
 // String implements the stringer interface, returning the consensus engine details.
 func (d *DexconConfig) String() string {
-	return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v MiningVelocity: %v NextHalvingSupply: %v LastHalvedAmount: %v MinGasPrice: %v BlockGasLimit: %v LambdaBA: %v LambdaDKG: %v NotaryParamAlpha: %v NotaryParamBeta: %v RoundLength: %v MinBlockInterval: %v FineValues: %v}",
+	return fmt.Sprintf("{GenesisCRSText: %v Owner: %v MinStake: %v LockupPeriod: %v MiningVelocity: %v NextHalvingSupply: %v LastHalvedAmount: %v MinGasPrice: %v BlockGasLimit: %v LambdaBA: %v LambdaDKG: %v NotaryParamAlpha: %v NotaryParamBeta: %v RoundLength: %v MinBlockInterval: %v FineValues: %v IsConsortium: %v AddressWhitelist: %v}",
 		d.GenesisCRSText,
 		d.Owner,
 		d.MinStake,
@@ -312,6 +314,8 @@ func (d *DexconConfig) String() string {
 		d.RoundLength,
 		d.MinBlockInterval,
 		d.FineValues,
+		d.IsConsortium,
+		d.AddressWhitelist,
 	)
 }
 
@@ -552,3 +556,35 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
 		IsPetersburg:     c.IsPetersburg(num),
 	}
 }
+
+// NewTestChainConfig is the ChainConfig constructor for test
+func NewTestChainConig() *ChainConfig {
+	return &ChainConfig{big.NewInt(1), 0, big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil, nil}
+}
+
+func NewTestDexonConfig() *DexconConfig {
+	return &DexconConfig{
+		GenesisCRSText:    "Tangerine Testnet",
+		Owner:             common.HexToAddress("0x0D54AF942d6bF13870F5CA65D470954f21D3cBE5"),
+		MinStake:          new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)),
+		LockupPeriod:      3600,
+		MiningVelocity:    0.18,
+		NextHalvingSupply: new(big.Int).Mul(big.NewInt(1e18), big.NewInt(125e6)),
+		LastHalvedAmount:  new(big.Int).Mul(big.NewInt(1e18), big.NewInt(75e6)),
+		MinGasPrice:       new(big.Int).Mul(big.NewInt(1e9), big.NewInt(1)),
+		BlockGasLimit:     210000000,
+		LambdaBA:          250,
+		LambdaDKG:         20000,
+		NotaryParamAlpha:  70.5,
+		NotaryParamBeta:   264,
+		RoundLength:       3600,
+		MinBlockInterval:  1000,
+		FineValues: []*big.Int{
+			new(big.Int).Mul(big.NewInt(1e18), big.NewInt(100)),
+			new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1)),
+			new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)),
+			new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)),
+			new(big.Int).Mul(big.NewInt(1e18), big.NewInt(1e6)),
+		},
+	}
+}
diff --git a/params/gen_dexcon_config.go b/params/gen_dexcon_config.go
index 28e2775ff..2a8d84546 100644
--- a/params/gen_dexcon_config.go
+++ b/params/gen_dexcon_config.go
@@ -31,6 +31,8 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
 		RoundLength       uint64                  `json:"roundLength"`
 		MinBlockInterval  uint64                  `json:"minBlockInterval"`
 		FineValues        []*math.HexOrDecimal256 `json:"fineValues"`
+		IsConsortium      bool                    `json:"isConsortium"`
+		AddressWhitelist  []common.Address        `json:"addressWhitelist"`
 	}
 	var enc DexconConfig
 	enc.GenesisCRSText = d.GenesisCRSText
@@ -54,6 +56,8 @@ func (d DexconConfig) MarshalJSON() ([]byte, error) {
 			enc.FineValues[k] = (*math.HexOrDecimal256)(v)
 		}
 	}
+	enc.IsConsortium = d.IsConsortium
+	enc.AddressWhitelist = d.AddressWhitelist
 	return json.Marshal(&enc)
 }
 
@@ -76,6 +80,8 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
 		RoundLength       *uint64                 `json:"roundLength"`
 		MinBlockInterval  *uint64                 `json:"minBlockInterval"`
 		FineValues        []*math.HexOrDecimal256 `json:"fineValues"`
+		IsConsortium      *bool                   `json:"isConsortium"`
+		AddressWhitelist  []common.Address        `json:"addressWhitelist"`
 	}
 	var dec DexconConfig
 	if err := json.Unmarshal(input, &dec); err != nil {
@@ -132,5 +138,11 @@ func (d *DexconConfig) UnmarshalJSON(input []byte) error {
 			d.FineValues[k] = (*big.Int)(v)
 		}
 	}
+	if dec.IsConsortium != nil {
+		d.IsConsortium = *dec.IsConsortium
+	}
+	if dec.AddressWhitelist != nil {
+		d.AddressWhitelist = dec.AddressWhitelist
+	}
 	return nil
 }
-- 
cgit v1.2.3