mirror of https://github.com/poanetwork/quorum.git
Merge of 1812 with permissions changes and fix of conflicts
This commit is contained in:
commit
d6352b30c3
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignerFn is a signer function callback when a contract requires a method to
|
// SignerFn is a signer function callback when a contract requires a method to
|
||||||
|
@ -183,6 +184,7 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
|
||||||
// transact executes an actual transaction invocation, first deriving any missing
|
// transact executes an actual transaction invocation, first deriving any missing
|
||||||
// authorization fields, and then scheduling the transaction for execution.
|
// authorization fields, and then scheduling the transaction for execution.
|
||||||
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
|
||||||
|
log.Info("Inside transact")
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Ensure a valid value field and resolve the account nonce
|
// Ensure a valid value field and resolve the account nonce
|
||||||
|
@ -238,6 +240,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
log.Info("calling SendTransaction in side transact")
|
||||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,8 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var knownNodes []*discover.Node
|
||||||
|
|
||||||
if *runv5 {
|
if *runv5 {
|
||||||
if _, err := discv5.ListenUDP(nodeKey, conn, realaddr, "", restrictList); err != nil {
|
if _, err := discv5.ListenUDP(nodeKey, conn, realaddr, "", restrictList); err != nil {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
|
@ -127,7 +129,7 @@ func main() {
|
||||||
AnnounceAddr: realaddr,
|
AnnounceAddr: realaddr,
|
||||||
NetRestrict: restrictList,
|
NetRestrict: restrictList,
|
||||||
}
|
}
|
||||||
if _, err := discover.ListenUDP(conn, cfg); err != nil {
|
if _, err := discover.ListenUDP(conn, cfg, knownNodes); err != nil {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/controls/permissions"
|
||||||
|
"github.com/ethereum/go-ethereum/controls/cluster"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -289,6 +291,18 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
events := make(chan accounts.WalletEvent, 16)
|
events := make(chan accounts.WalletEvent, 16)
|
||||||
stack.AccountManager().Subscribe(events)
|
stack.AccountManager().Subscribe(events)
|
||||||
|
|
||||||
|
//START - QUORUM Permissioning
|
||||||
|
if permissioned := ctx.GlobalBool(utils.EnableNodePermissionFlag.Name); permissioned {
|
||||||
|
if err := permissions.QuorumPermissioning(ctx, stack); err != nil {
|
||||||
|
utils.Fatalf("Failed to start Quorum Permissioning: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Changes for managing org level cluster keys for privateFor txns
|
||||||
|
if err := cluster.ManageOrgKeys(ctx, stack); err != nil {
|
||||||
|
log.Warn("Org key management failed", "err", err)
|
||||||
|
}
|
||||||
|
//END - QUORUM Permissioning
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
// Create a chain state reader for self-derivation
|
// Create a chain state reader for self-derivation
|
||||||
rpcClient, err := stack.Attach()
|
rpcClient, err := stack.Attach()
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package controls
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
)
|
||||||
|
// Create an RPC client for the contract interface
|
||||||
|
func CreateEthClient(stack *node.Node ) (*ethclient.Client, error){
|
||||||
|
var e *eth.Ethereum
|
||||||
|
if err := stack.Service(&e); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, err := stack.Attach()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ethclient.NewClient(rpcClient), nil
|
||||||
|
}
|
|
@ -0,0 +1,338 @@
|
||||||
|
pragma solidity ^0.4.23;
|
||||||
|
|
||||||
|
contract Clusterkeys {
|
||||||
|
|
||||||
|
// Struct for managing the org details
|
||||||
|
enum Operation {None, Add, Delete}
|
||||||
|
struct OrgDetails {
|
||||||
|
string orgId;
|
||||||
|
string [] privateKey;
|
||||||
|
string pendingKey;
|
||||||
|
Operation pendingOp;
|
||||||
|
}
|
||||||
|
OrgDetails [] private orgList;
|
||||||
|
mapping(bytes32 => uint) private OrgIndex;
|
||||||
|
|
||||||
|
// Struct for managing the voter accounst for the org
|
||||||
|
struct OrgVoterDetails {
|
||||||
|
string orgId;
|
||||||
|
address [] orgVoterAccount;
|
||||||
|
}
|
||||||
|
OrgVoterDetails [] private voterList;
|
||||||
|
mapping(bytes32 => uint) private VoterOrgIndex;
|
||||||
|
|
||||||
|
// mapping to monitor the voting status for each acount and
|
||||||
|
// overall voting count
|
||||||
|
mapping (uint => mapping (address => bool)) private voteStatus;
|
||||||
|
mapping (uint => uint) private voteCount;
|
||||||
|
|
||||||
|
uint private numberOfOrgs = 0;
|
||||||
|
uint private orgVoterNum = 0;
|
||||||
|
|
||||||
|
// events related to Org level key management
|
||||||
|
event OrgKeyAdded(string _orgId, string _privateKey);
|
||||||
|
event OrgKeyDeleted(string _orgId, string _privateKey);
|
||||||
|
event KeyNotFound(string _privateKey);
|
||||||
|
event KeyExists(string _orgId, string _privateKey);
|
||||||
|
event OrgNotFound(string _orgId);
|
||||||
|
|
||||||
|
// events related to org level approval process
|
||||||
|
event PendingApproval(string _orgId);
|
||||||
|
event ItemForApproval(string _orgId, Operation _pendingOp, string _privateKey);
|
||||||
|
event NothingToApprove(string _orgId);
|
||||||
|
|
||||||
|
// events related to managing voting accounts for the org
|
||||||
|
event NoVotingAccount(string _orgId);
|
||||||
|
event VoterAdded(string _orgId, address _address);
|
||||||
|
event VoterNotFound(string _orgId, address _address);
|
||||||
|
event VoterAccountDeleted(string _orgId, address _address);
|
||||||
|
event VoterExists(string _orgId, address _address);
|
||||||
|
|
||||||
|
// events related to helper functions to print all org keys and voter keys
|
||||||
|
event PrintAll(string _orgId, string _privateKey);
|
||||||
|
event PrintVoter(string _orgId, address _voterAccount);
|
||||||
|
|
||||||
|
// returns the org index for the org list
|
||||||
|
function getOrgIndex(string _orgId) internal view returns (uint)
|
||||||
|
{
|
||||||
|
return OrgIndex[keccak256(abi.encodePacked(_orgId))] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the voter index for the org from voter list
|
||||||
|
function getOrgIndexVoter(string _orgId) internal view returns (uint)
|
||||||
|
{
|
||||||
|
return VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if the sender is one of the registered voter account for the org
|
||||||
|
modifier canVote(string _orgId){
|
||||||
|
bool flag = false;
|
||||||
|
uint orgIndex = getOrgIndexVoter(_orgId);
|
||||||
|
for (uint i = 0; i < voterList[orgIndex].orgVoterAccount.length; i++){
|
||||||
|
if ( voterList[orgIndex].orgVoterAccount[i] == msg.sender){
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(flag, "Account cannot vote");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// checks if the org has any voter accounts set up or not
|
||||||
|
function checkIfVoterExists(string _orgId, address _address) internal view returns (bool, uint){
|
||||||
|
bool keyExists = false;
|
||||||
|
uint voterIndex = getOrgIndexVoter(_orgId);
|
||||||
|
for (uint i = 0; i < voterList[voterIndex].orgVoterAccount.length; i++){
|
||||||
|
if(keccak256(abi.encodePacked(voterList[voterIndex].orgVoterAccount[i])) == keccak256(abi.encodePacked(_address))){
|
||||||
|
keyExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (keyExists, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if the voter account is already in the voter accounts list for the org
|
||||||
|
function checkVotingAccountExists(string _orgId) internal returns (bool)
|
||||||
|
{
|
||||||
|
if (VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] == 0){
|
||||||
|
emit NoVotingAccount(_orgId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint orgIndex = getOrgIndexVoter(_orgId);
|
||||||
|
if (voterList[orgIndex].orgVoterAccount.length == 0) {
|
||||||
|
emit NoVotingAccount(_orgId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if there are any pending unapproved actions for the org
|
||||||
|
function checkingPendingOp(string _orgId) internal view returns (bool)
|
||||||
|
{
|
||||||
|
if (OrgIndex[keccak256(abi.encodePacked(_orgId))] == 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint orgIndex = getOrgIndex(_orgId);
|
||||||
|
if (orgList[orgIndex].pendingOp != Operation.None) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if there the key is already in the list of private keys for the org
|
||||||
|
function checkIfKeyExists(string _orgId, string _privateKey) internal view returns (bool, uint){
|
||||||
|
bool keyExists = false;
|
||||||
|
uint orgIndex = getOrgIndex(_orgId);
|
||||||
|
for (uint i = 0; i < orgList[orgIndex].privateKey.length; i++){
|
||||||
|
if(keccak256(abi.encodePacked(orgList[orgIndex].privateKey[i])) == keccak256(abi.encodePacked(_privateKey))){
|
||||||
|
keyExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (keyExists, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function for adding a voter account to a org
|
||||||
|
function addVoter(string _orgId, address _address) external
|
||||||
|
{
|
||||||
|
if (VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
|
||||||
|
orgVoterNum++;
|
||||||
|
VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] = orgVoterNum;
|
||||||
|
voterList.push( OrgVoterDetails(_orgId, new address[](0)));
|
||||||
|
voterList[orgVoterNum - 1].orgVoterAccount.push(_address);
|
||||||
|
emit VoterAdded(_orgId, _address);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool voterExists = false;
|
||||||
|
uint i = 0;
|
||||||
|
(voterExists, i) = checkIfVoterExists(_orgId, _address);
|
||||||
|
if (voterExists) {
|
||||||
|
emit VoterExists(_orgId, _address);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint voterIndex = getOrgIndexVoter(_orgId);
|
||||||
|
voterList[voterIndex].orgVoterAccount.push(_address);
|
||||||
|
emit VoterAdded(_orgId, _address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function for deleting a voter account to a org
|
||||||
|
function deleteVoter(string _orgId, address _address) external
|
||||||
|
{
|
||||||
|
if (VoterOrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
|
||||||
|
emit OrgNotFound(_orgId);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint voterIndex = getOrgIndexVoter(_orgId);
|
||||||
|
(bool voterExists, uint i) = checkIfVoterExists(_orgId, _address);
|
||||||
|
|
||||||
|
if (voterExists == true) {
|
||||||
|
for (uint j = i; j < voterList[voterIndex].orgVoterAccount.length -1; j++){
|
||||||
|
voterList[voterIndex].orgVoterAccount[j] = voterList[voterIndex].orgVoterAccount[j+1];
|
||||||
|
}
|
||||||
|
delete voterList[voterIndex].orgVoterAccount[voterList[voterIndex].orgVoterAccount.length -1];
|
||||||
|
voterList[voterIndex].orgVoterAccount.length --;
|
||||||
|
emit VoterAccountDeleted(_orgId, _address);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit VoterNotFound(_orgId, _address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function for adding a private key for the org. Thsi will be added once
|
||||||
|
// approval process is complete
|
||||||
|
function addOrgKey(string _orgId, string _privateKey) external
|
||||||
|
{
|
||||||
|
if (checkVotingAccountExists(_orgId)){
|
||||||
|
if (OrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
|
||||||
|
numberOfOrgs++;
|
||||||
|
OrgIndex[keccak256(abi.encodePacked(_orgId))] = numberOfOrgs;
|
||||||
|
orgList.push( OrgDetails(_orgId, new string[](0), _privateKey, Operation.Add));
|
||||||
|
voterInit(_orgId);
|
||||||
|
emit ItemForApproval(_orgId, Operation.Add, _privateKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (checkingPendingOp(_orgId)){
|
||||||
|
emit PendingApproval(_orgId);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool keyExists = false;
|
||||||
|
uint i = 0;
|
||||||
|
(keyExists, i) = checkIfKeyExists(_orgId, _privateKey);
|
||||||
|
if (keyExists) {
|
||||||
|
emit KeyExists(_orgId, _privateKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint orgIndex;
|
||||||
|
orgIndex = getOrgIndex(_orgId);
|
||||||
|
orgList[orgIndex].pendingKey = _privateKey;
|
||||||
|
orgList[orgIndex].pendingOp = Operation.Add;
|
||||||
|
voterInit(_orgId);
|
||||||
|
emit ItemForApproval(_orgId,Operation.Add, _privateKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function for deleting a private key for the org. Thsi will be deleted once
|
||||||
|
// approval process is complete
|
||||||
|
function deleteOrgKey(string _orgId, string _privateKey) external
|
||||||
|
{
|
||||||
|
if (checkVotingAccountExists(_orgId)){
|
||||||
|
if (OrgIndex[keccak256(abi.encodePacked(_orgId))] == 0) {
|
||||||
|
emit OrgNotFound(_orgId);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (checkingPendingOp(_orgId)){
|
||||||
|
emit PendingApproval(_orgId);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint orgIndex = getOrgIndex(_orgId);
|
||||||
|
uint i = 0;
|
||||||
|
bool keyExists = false;
|
||||||
|
|
||||||
|
(keyExists, i) = checkIfKeyExists (_orgId, _privateKey);
|
||||||
|
if (keyExists == true) {
|
||||||
|
orgList[orgIndex].pendingKey = _privateKey;
|
||||||
|
orgList[orgIndex].pendingOp = Operation.Delete;
|
||||||
|
voterInit(_orgId);
|
||||||
|
emit ItemForApproval(_orgId, Operation.Delete, _privateKey);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit KeyNotFound(_privateKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function for approving key add or delete operations
|
||||||
|
function approvePendingOp(string _orgId) external canVote(_orgId)
|
||||||
|
{
|
||||||
|
if (checkingPendingOp(_orgId)){
|
||||||
|
uint orgIndex = getOrgIndex(_orgId);
|
||||||
|
processVote(_orgId);
|
||||||
|
processApproval(orgIndex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
emit NothingToApprove(_orgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the voter account votes to false. This will be called when a
|
||||||
|
// new item is initiated for approval
|
||||||
|
function voterInit(string _orgId) internal {
|
||||||
|
uint orgIndex = getOrgIndexVoter(_orgId);
|
||||||
|
for (uint i = 0; i < voterList[orgIndex].orgVoterAccount.length; i++){
|
||||||
|
voteStatus[orgIndex][voterList[orgIndex].orgVoterAccount[i]] = false;
|
||||||
|
}
|
||||||
|
voteCount[orgIndex] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// processes the vote from the voter account.
|
||||||
|
function processVote (string _orgId) internal {
|
||||||
|
uint orgIndex = getOrgIndexVoter(_orgId);
|
||||||
|
if (voteStatus[orgIndex][msg.sender] == false ){
|
||||||
|
voteStatus[orgIndex][msg.sender] = true;
|
||||||
|
voteCount[orgIndex]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if enough votes have been cast for the pending operation. If yes
|
||||||
|
// returns true
|
||||||
|
function checkEnoughVotes (string _orgId) internal view returns (bool) {
|
||||||
|
uint orgIndex = getOrgIndexVoter(_orgId);
|
||||||
|
if (voteCount[orgIndex] > voterList[orgIndex].orgVoterAccount.length / 2 ){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to process the approavl for add or delete
|
||||||
|
function processApproval(uint _orgIndex) internal {
|
||||||
|
if(checkEnoughVotes(orgList[_orgIndex].orgId)){
|
||||||
|
string storage locKey = orgList[_orgIndex].pendingKey;
|
||||||
|
if (orgList[_orgIndex].pendingOp == Operation.Add){
|
||||||
|
orgList[_orgIndex].privateKey.push(orgList[_orgIndex].pendingKey);
|
||||||
|
emit OrgKeyAdded(orgList[_orgIndex].orgId, locKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool keyExists = false;
|
||||||
|
uint i = 0;
|
||||||
|
(keyExists, i) = checkIfKeyExists (orgList[_orgIndex].orgId, locKey);
|
||||||
|
for (uint j = i; j < orgList[_orgIndex].privateKey.length -1; j++){
|
||||||
|
orgList[_orgIndex].privateKey[j] = orgList[_orgIndex].privateKey[j+1];
|
||||||
|
}
|
||||||
|
delete orgList[_orgIndex].privateKey[orgList[_orgIndex].privateKey.length -1];
|
||||||
|
orgList[_orgIndex].privateKey.length --;
|
||||||
|
emit OrgKeyDeleted(orgList[_orgIndex].orgId, locKey);
|
||||||
|
}
|
||||||
|
orgList[_orgIndex].pendingOp = Operation.None;
|
||||||
|
orgList[_orgIndex].pendingKey = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to print all privates keys for an org
|
||||||
|
function printAllOrg () public {
|
||||||
|
for (uint i = 0; i < orgList.length; i++){
|
||||||
|
for (uint j = 0; j < orgList[i].privateKey.length ; j++){
|
||||||
|
emit PrintAll(orgList[i].orgId, orgList[i].privateKey[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to print all voters accounts for an org
|
||||||
|
function printAllVoter () public {
|
||||||
|
for (uint i = 0; i < voterList.length; i++){
|
||||||
|
for (uint j = 0; j < voterList[i].orgVoterAccount.length ; j++){
|
||||||
|
emit PrintVoter(voterList[i].orgId, voterList[i].orgVoterAccount[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
[{"constant":false,"inputs":[],"name":"printAllOrg","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_orgId","type":"string"}],"name":"approvePendingOp","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_orgId","type":"string"},{"name":"_privateKey","type":"string"}],"name":"deleteOrgKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_orgId","type":"string"},{"name":"_address","type":"address"}],"name":"addVoter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_orgId","type":"string"},{"name":"_address","type":"address"}],"name":"deleteVoter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"printAllVoter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_orgId","type":"string"},{"name":"_privateKey","type":"string"}],"name":"addOrgKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_privateKey","type":"string"}],"name":"OrgKeyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_privateKey","type":"string"}],"name":"OrgKeyDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_voterAccount","type":"string"}],"name":"orgVoterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_privateKey","type":"string"}],"name":"KeyNotFound","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"}],"name":"OrgNotFound","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_privateKey","type":"string"}],"name":"KeyExists","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_address","type":"address"}],"name":"VoterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_address","type":"address"}],"name":"VoterExists","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_address","type":"address"}],"name":"VoterNotFound","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_address","type":"address"}],"name":"VoterAccountDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"}],"name":"NoVotingAccount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"}],"name":"PendingApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_pendingOp","type":"uint8"},{"indexed":false,"name":"_privateKey","type":"string"}],"name":"ItemForApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"}],"name":"NothingToApprove","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_privateKey","type":"string"}],"name":"PrintAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_voterAccount","type":"address"}],"name":"PrintVoter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_orgId","type":"string"},{"indexed":false,"name":"_pendingOp","type":"uint8"},{"indexed":false,"name":"_pendingKey","type":"string"}],"name":"PrintKey","type":"event"}]
|
|
@ -0,0 +1,130 @@
|
||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/controls"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
// This function first adds the node list from permissioned-nodes.json to
|
||||||
|
// the permissiones contract deployed as a precompile via genesis.json
|
||||||
|
func ManageOrgKeys(ctx *cli.Context, stack *node.Node ) error {
|
||||||
|
// Create a new ethclient to for interfacing with the contract
|
||||||
|
stateReader, err := controls.CreateEthClient(stack)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Unable to create ethereum client for cluster check : ", "err" , err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if permissioning contract is there at address. If not return from here
|
||||||
|
if _ , err = NewClusterFilterer(params.PrivateKeyManagementContract, stateReader); err != nil {
|
||||||
|
log.Error ("Cluster not enabled for the network : ", "err" , err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
manageClusterKeys(stack, stateReader);
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func manageClusterKeys (stack *node.Node, stateReader *ethclient.Client ) error {
|
||||||
|
//call populate nodes to populate the nodes into contract
|
||||||
|
if err := populatePrivateKeys (stack, stateReader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//monitor for nodes deletiin via smart contract
|
||||||
|
monitorKeyChanges(stack, stateReader)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func populatePrivateKeys(stack *node.Node, stateReader *ethclient.Client) error{
|
||||||
|
cluster, err := NewClusterFilterer(params.PrivateKeyManagementContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor node delete: ", "err" , err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &bind.FilterOpts{}
|
||||||
|
pastAddEvents, err := cluster.FilterOrgKeyAdded(opts)
|
||||||
|
|
||||||
|
recExists := true
|
||||||
|
for recExists {
|
||||||
|
recExists = pastAddEvents.Next()
|
||||||
|
if recExists {
|
||||||
|
types.AddOrgKey(pastAddEvents.Event.OrgId, pastAddEvents.Event.PrivateKey )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = &bind.FilterOpts{}
|
||||||
|
pastDeleteEvents, err := cluster.FilterOrgKeyDeleted(opts)
|
||||||
|
|
||||||
|
recExists = true
|
||||||
|
for recExists {
|
||||||
|
recExists = pastDeleteEvents.Next()
|
||||||
|
if recExists {
|
||||||
|
types.DeleteOrgKey(pastDeleteEvents.Event.OrgId, pastDeleteEvents.Event.PrivateKey )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func monitorKeyChanges(stack *node.Node, stateReader *ethclient.Client) {
|
||||||
|
go monitorKeyAdd(stack, stateReader)
|
||||||
|
|
||||||
|
go monitorKeyDelete(stack, stateReader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func monitorKeyAdd(stack *node.Node, stateReader *ethclient.Client){
|
||||||
|
cluster, err := NewClusterFilterer(params.PrivateKeyManagementContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor Account cluster : ", "err" , err)
|
||||||
|
}
|
||||||
|
ch := make(chan *ClusterOrgKeyAdded)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var newEvent *ClusterOrgKeyAdded
|
||||||
|
|
||||||
|
_, err = cluster.WatchOrgKeyAdded(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed WatchOrgKeyDeleted: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case newEvent = <-ch:
|
||||||
|
types.AddOrgKey(newEvent.OrgId, newEvent.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func monitorKeyDelete(stack *node.Node, stateReader *ethclient.Client){
|
||||||
|
cluster, err := NewClusterFilterer(params.PrivateKeyManagementContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor Account cluster : ", "err" , err)
|
||||||
|
}
|
||||||
|
ch := make(chan *ClusterOrgKeyDeleted)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var newEvent *ClusterOrgKeyDeleted
|
||||||
|
|
||||||
|
_, err = cluster.WatchOrgKeyDeleted(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed WatchOrgKeyDeleted: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case newEvent = <-ch:
|
||||||
|
types.DeleteOrgKey(newEvent.OrgId, newEvent.PrivateKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,318 @@
|
||||||
|
pragma solidity ^0.4.23;
|
||||||
|
|
||||||
|
contract Permissions {
|
||||||
|
|
||||||
|
// enum and struct declaration
|
||||||
|
enum NodeStatus { NotInList, PendingApproval, Approved, PendingDeactivation, Deactivated, PendingBlacklisting, Blacklisted }
|
||||||
|
enum AccountAccess { FullAccess, ReadOnly, Transact, ContractDeploy }
|
||||||
|
struct NodeDetails {
|
||||||
|
string enodeId; //e.g. 127.0.0.1:20005
|
||||||
|
string ipAddrPort;
|
||||||
|
string discPort;
|
||||||
|
string raftPort;
|
||||||
|
NodeStatus status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use an array to store node details
|
||||||
|
// if we want to list all node one day, mapping is not capable
|
||||||
|
NodeDetails[] private nodeList;
|
||||||
|
// use a mapping of enodeid to array index to track node
|
||||||
|
mapping (bytes32 => uint) private nodeIdToIndex;
|
||||||
|
// keep track of node number
|
||||||
|
uint private numberOfNodes;
|
||||||
|
|
||||||
|
// use an array to store account details
|
||||||
|
// if we want to list all account one day, mapping is not capable
|
||||||
|
address[] private accountList;
|
||||||
|
|
||||||
|
// store node approval, deactivation and blacklisting vote status (prevent double vote)
|
||||||
|
mapping (uint => mapping (address => bool)) private voteStatus;
|
||||||
|
// valid vote count
|
||||||
|
mapping (uint => uint) private voteCount;
|
||||||
|
|
||||||
|
// node permission events for new node propose
|
||||||
|
event NodeProposed(string _enodeId);
|
||||||
|
event NodeApproved(string _enodeId, string _ipAddrPort, string _discPort, string _raftPort);
|
||||||
|
event VoteNodeApproval(string _enodeId, address _accountAddress);
|
||||||
|
|
||||||
|
// node permission events for node decativation
|
||||||
|
event NodePendingDeactivation (string _enodeId);
|
||||||
|
event NodeDeactivated(string _enodeId, string _ipAddrPort, string _discPort, string _raftPort);
|
||||||
|
event VoteNodeDeactivation(string _enodeId, address _accountAddress);
|
||||||
|
|
||||||
|
// node permission events for node blacklist
|
||||||
|
event NodePendingBlacklist(string _enodeId);
|
||||||
|
event NodeBlacklisted(string _enodeId, string _ipAddrPort, string _discPort, string _raftPort);
|
||||||
|
event VoteNodeBlacklist(string _enodeId, address _accountAddress);
|
||||||
|
|
||||||
|
// account permission events
|
||||||
|
event AccountAccessModified(address _address, AccountAccess _access);
|
||||||
|
|
||||||
|
// events related to voting accounts for majority voting
|
||||||
|
event NoVotingAccount();
|
||||||
|
event VoterAdded(address _address);
|
||||||
|
event VoterRemoved(address _address);
|
||||||
|
|
||||||
|
// Checks if the given enode exists
|
||||||
|
modifier enodeInList(string _enodeId)
|
||||||
|
{
|
||||||
|
require(nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] != 0, "Enode is not in the list");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the given enode does not exists
|
||||||
|
modifier enodeNotInList(string _enodeId)
|
||||||
|
{
|
||||||
|
require(nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] == 0, "Enode is in the list");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the account can vote
|
||||||
|
modifier canVote()
|
||||||
|
{
|
||||||
|
bool flag = false;
|
||||||
|
for (uint i=0; i<accountList.length; i++){
|
||||||
|
if (accountList[i] == msg.sender){
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(flag, "Account can not vote");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public and external functions */
|
||||||
|
// view functions
|
||||||
|
|
||||||
|
// Get number of nodes
|
||||||
|
function getNumberOfNodes() public view returns (uint)
|
||||||
|
{
|
||||||
|
return numberOfNodes;
|
||||||
|
}
|
||||||
|
// Get number of accounts and voting accounts
|
||||||
|
function getNumberOfAccounts() public view returns (uint)
|
||||||
|
{
|
||||||
|
return accountList.length;
|
||||||
|
}
|
||||||
|
// Get node status by enode id
|
||||||
|
function getNodeStatus(string _enodeId) public view enodeInList(_enodeId) returns (NodeStatus)
|
||||||
|
{
|
||||||
|
return nodeList[getNodeIndex(_enodeId)].status;
|
||||||
|
}
|
||||||
|
// Get vote count by enode id
|
||||||
|
function getVoteCount(string _enodeId) public view enodeInList(_enodeId) returns (uint)
|
||||||
|
{
|
||||||
|
return voteCount[getNodeIndex(_enodeId)];
|
||||||
|
}
|
||||||
|
// Get vote status by enode id and voter address
|
||||||
|
function getVoteStatus(string _enodeId, address _voter) public view enodeInList(_enodeId) returns (bool)
|
||||||
|
{
|
||||||
|
return voteStatus[getNodeIndex(_enodeId)][_voter];
|
||||||
|
}
|
||||||
|
// for potential external use
|
||||||
|
// Get enode id by index
|
||||||
|
function getEnodeId(uint _index) external view returns (string)
|
||||||
|
{
|
||||||
|
if (_index <= numberOfNodes){
|
||||||
|
return nodeList[_index].enodeId;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get account address by index
|
||||||
|
function getAccountAddress(uint _index) external view returns (address)
|
||||||
|
{
|
||||||
|
if (_index <= accountList.length){
|
||||||
|
return accountList[_index];
|
||||||
|
} else {
|
||||||
|
return address(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// state change functions
|
||||||
|
|
||||||
|
// propose a new node to the network
|
||||||
|
function proposeNode(string _enodeId, string _ipAddrPort, string _discPort, string _raftPort) external enodeNotInList(_enodeId)
|
||||||
|
{
|
||||||
|
if (checkVotingAccountExist()){
|
||||||
|
// increment node number, add node to the list
|
||||||
|
numberOfNodes++;
|
||||||
|
nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] = numberOfNodes;
|
||||||
|
nodeList.push(NodeDetails(_enodeId, _ipAddrPort,_discPort, _raftPort, NodeStatus.PendingApproval));
|
||||||
|
// add voting status, numberOfNodes is the index of current proposed node
|
||||||
|
for (uint i = 0; i < accountList.length; i++){
|
||||||
|
voteStatus[numberOfNodes][accountList[i]] = false;
|
||||||
|
}
|
||||||
|
voteCount[numberOfNodes] = 0;
|
||||||
|
// emit event
|
||||||
|
emit NodeProposed(_enodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a node to the nodeList mapping and emits node added event if successfully and node exists event of node is already present
|
||||||
|
function approveNode(string _enodeId) external canVote
|
||||||
|
{
|
||||||
|
require(getNodeStatus(_enodeId) == NodeStatus.PendingApproval, "Node need to be in PendingApproval status");
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
require(voteStatus[nodeIndex][msg.sender] == false, "Node can not double vote");
|
||||||
|
// vote node
|
||||||
|
voteStatus[nodeIndex][msg.sender] = true;
|
||||||
|
voteCount[nodeIndex]++;
|
||||||
|
// emit event
|
||||||
|
emit VoteNodeApproval(_enodeId, msg.sender);
|
||||||
|
// check if node vote reach majority
|
||||||
|
checkNodeApproval(_enodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propose a node for deactivation from network
|
||||||
|
function proposeDeactivation(string _enodeId) external enodeInList(_enodeId)
|
||||||
|
{
|
||||||
|
if (checkVotingAccountExist()){
|
||||||
|
require(getNodeStatus(_enodeId) == NodeStatus.Approved, "Node need to be in Approved status");
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
nodeList[nodeIndex].status = NodeStatus.PendingDeactivation;
|
||||||
|
// add voting status, numberOfNodes is the index of current proposed node
|
||||||
|
for (uint i = 0; i < accountList.length; i++){
|
||||||
|
voteStatus[nodeIndex][accountList[i]] = false;
|
||||||
|
}
|
||||||
|
voteCount[nodeIndex] = 0;
|
||||||
|
// emit event
|
||||||
|
emit NodePendingDeactivation(_enodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//deactivates a given Enode and emits the decativation event
|
||||||
|
function deactivateNode(string _enodeId) external canVote
|
||||||
|
{
|
||||||
|
require(getNodeStatus(_enodeId) == NodeStatus.PendingDeactivation, "Node need to be in PendingDeactivation status");
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
require(voteStatus[nodeIndex][msg.sender] == false, "Node can not double vote");
|
||||||
|
// vote node
|
||||||
|
voteStatus[nodeIndex][msg.sender] = true;
|
||||||
|
voteCount[nodeIndex]++;
|
||||||
|
// emit event
|
||||||
|
emit VoteNodeDeactivation(_enodeId, msg.sender);
|
||||||
|
// check if node vote reach majority
|
||||||
|
checkNodeDeactivation(_enodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propose node for blacklisting
|
||||||
|
function proposeNodeBlacklisting(string _enodeId, string _ipAddrPort, string _discPort, string _raftPort) external
|
||||||
|
{
|
||||||
|
if (checkVotingAccountExist()){
|
||||||
|
uint nodeIndex;
|
||||||
|
// check if node is in the nodeList
|
||||||
|
if (nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] != 0){
|
||||||
|
// no matter what status the node is in, vote will reset and node status change to PendingBlacklisting
|
||||||
|
nodeList[nodeIndex].status = NodeStatus.PendingBlacklisting;
|
||||||
|
nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
} else {
|
||||||
|
// increment node number, add node to the list
|
||||||
|
numberOfNodes++;
|
||||||
|
nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] = numberOfNodes;
|
||||||
|
nodeList.push(NodeDetails(_enodeId, _ipAddrPort,_discPort, _raftPort, NodeStatus.PendingBlacklisting));
|
||||||
|
nodeIndex = numberOfNodes;
|
||||||
|
}
|
||||||
|
// add voting status, numberOfNodes is the index of current proposed node
|
||||||
|
for (uint i = 0; i < accountList.length; i++){
|
||||||
|
voteStatus[nodeIndex][accountList[i]] = false;
|
||||||
|
}
|
||||||
|
voteCount[nodeIndex] = 0;
|
||||||
|
// emit event
|
||||||
|
emit NodePendingBlacklist(_enodeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Approve node blacklisting
|
||||||
|
function blacklistNode(string _enodeId) external canVote
|
||||||
|
{
|
||||||
|
require(getNodeStatus(_enodeId) == NodeStatus.PendingBlacklisting, "Node need to be in PendingBlacklisting status");
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
require(voteStatus[nodeIndex][msg.sender] == false, "Node can not double vote");
|
||||||
|
// vote node
|
||||||
|
voteStatus[nodeIndex][msg.sender] = true;
|
||||||
|
voteCount[nodeIndex]++;
|
||||||
|
// emit event
|
||||||
|
emit VoteNodeBlacklist(_enodeId, msg.sender);
|
||||||
|
// check if node vote reach majority
|
||||||
|
checkNodeBlacklisting(_enodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the Node is already added. If yes then returns true
|
||||||
|
function updateAccountAccess(address _address, AccountAccess _accountAccess) external
|
||||||
|
{
|
||||||
|
emit AccountAccessModified(_address, _accountAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add voting account
|
||||||
|
function addVoter(address _address) external
|
||||||
|
{
|
||||||
|
// Check if account already exists
|
||||||
|
for (uint i=0; i<accountList.length; i++){
|
||||||
|
if (accountList[i] == _address){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accountList.push(_address);
|
||||||
|
emit VoterAdded(_address);
|
||||||
|
}
|
||||||
|
// Remove voting account
|
||||||
|
function removeVoter(address _address) external
|
||||||
|
{
|
||||||
|
// Check if account already exists
|
||||||
|
for (uint i=0; i<accountList.length; i++){
|
||||||
|
if (accountList[i] == _address){
|
||||||
|
for (uint j=i+1; j<accountList.length; j++){
|
||||||
|
accountList[j-1] = accountList[j];
|
||||||
|
}
|
||||||
|
delete accountList[accountList.length];
|
||||||
|
emit VoterRemoved(_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* private functions */
|
||||||
|
|
||||||
|
function getNodeIndex(string _enodeId) internal view returns (uint)
|
||||||
|
{
|
||||||
|
return nodeIdToIndex[keccak256(abi.encodePacked(_enodeId))] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkVotingAccountExist() internal returns (bool)
|
||||||
|
{
|
||||||
|
if (accountList.length == 0){
|
||||||
|
emit NoVotingAccount();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNodeApproval(string _enodeId) internal
|
||||||
|
{
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
if (voteCount[nodeIndex] > accountList.length / 2){
|
||||||
|
nodeList[nodeIndex].status = NodeStatus.Approved;
|
||||||
|
emit NodeApproved(nodeList[nodeIndex].enodeId, nodeList[nodeIndex].ipAddrPort, nodeList[nodeIndex].discPort, nodeList[nodeIndex].raftPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNodeDeactivation(string _enodeId) internal
|
||||||
|
{
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
if (voteCount[nodeIndex] > accountList.length / 2){
|
||||||
|
nodeList[nodeIndex].status = NodeStatus.Deactivated;
|
||||||
|
emit NodeDeactivated(nodeList[nodeIndex].enodeId, nodeList[nodeIndex].ipAddrPort, nodeList[nodeIndex].discPort, nodeList[nodeIndex].raftPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNodeBlacklisting(string _enodeId) internal
|
||||||
|
{
|
||||||
|
uint nodeIndex = getNodeIndex(_enodeId);
|
||||||
|
if (voteCount[nodeIndex] > accountList.length / 2){
|
||||||
|
nodeList[nodeIndex].status = NodeStatus.Blacklisted;
|
||||||
|
emit NodeBlacklisted(nodeList[nodeIndex].enodeId, nodeList[nodeIndex].ipAddrPort, nodeList[nodeIndex].discPort, nodeList[nodeIndex].raftPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,348 @@
|
||||||
|
package permissions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/controls"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/raft"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
PERMISSIONED_CONFIG = "permissioned-nodes.json"
|
||||||
|
BLACKLIST_CONFIG = "disallowed-nodes.json"
|
||||||
|
RAFT = "raft"
|
||||||
|
ISTANBUL = "istanbul"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NodeOperation uint8
|
||||||
|
const (
|
||||||
|
NodeAdd NodeOperation = iota
|
||||||
|
NodeDelete
|
||||||
|
)
|
||||||
|
|
||||||
|
// This function first adds the node list from permissioned-nodes.json to
|
||||||
|
// the permissiones contract deployed as a precompile via genesis.json
|
||||||
|
func QuorumPermissioning(ctx *cli.Context, stack *node.Node ) error {
|
||||||
|
// Create a new ethclient to for interfacing with the contract
|
||||||
|
stateReader, err := controls.CreateEthClient(stack)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Unable to create ethereum client for permissions check : ", "err" , err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if permissioning contract is there at address. If not return from here
|
||||||
|
if _ , err = NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader); err != nil {
|
||||||
|
log.Error ("Permissions not enabled for the network : ", "err" , err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
consensusEngine := ISTANBUL
|
||||||
|
if ctx.GlobalBool(utils.RaftModeFlag.Name) {
|
||||||
|
consensusEngine = RAFT
|
||||||
|
}
|
||||||
|
// Monitors node addition and decativation from network
|
||||||
|
manageNodePermissions(stack, stateReader, consensusEngine);
|
||||||
|
|
||||||
|
// Monitors account level persmissions update from smart contarct
|
||||||
|
manageAccountPermissions(stack, stateReader);
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Manages node addition and decavtivation from network
|
||||||
|
func manageNodePermissions(stack *node.Node, stateReader *ethclient.Client, consensusEngine string) {
|
||||||
|
//monitor for new nodes addition via smart contract
|
||||||
|
go monitorNewNodeAdd(stack, stateReader, consensusEngine)
|
||||||
|
|
||||||
|
//monitor for nodes deletiin via smart contract
|
||||||
|
go monitorNodeDeactivation(stack, stateReader, consensusEngine)
|
||||||
|
|
||||||
|
//monitor for nodes blacklisting via smart contract
|
||||||
|
go monitorNodeBlacklisting(stack, stateReader, consensusEngine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This functions listens on the channel for new node approval via smart contract and
|
||||||
|
// adds the same into permissioned-nodes.json
|
||||||
|
func monitorNewNodeAdd(stack *node.Node, stateReader *ethclient.Client, consensusEngine string) {
|
||||||
|
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("failed to monitor new node add : ", "err" , err)
|
||||||
|
}
|
||||||
|
datadir := stack.DataDir()
|
||||||
|
|
||||||
|
ch := make(chan *PermissionsNodeApproved, 1)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var nodeAddEvent *PermissionsNodeApproved
|
||||||
|
|
||||||
|
_, err = permissions.WatchNodeApproved(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed WatchNodeApproved: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case nodeAddEvent = <-ch:
|
||||||
|
updatePermissionedNodes(nodeAddEvent.EnodeId, nodeAddEvent.IpAddrPort, nodeAddEvent.DiscPort, nodeAddEvent.RaftPort, datadir, consensusEngine, NodeAdd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This functions listens on the channel for new node approval via smart contract and
|
||||||
|
// adds the same into permissioned-nodes.json
|
||||||
|
func monitorNodeDeactivation(stack *node.Node, stateReader *ethclient.Client, consensusEngine string) {
|
||||||
|
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor node delete: ", "err" , err)
|
||||||
|
}
|
||||||
|
datadir := stack.DataDir()
|
||||||
|
|
||||||
|
ch := make(chan *PermissionsNodeDeactivated)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var newNodeDeleteEvent *PermissionsNodeDeactivated
|
||||||
|
|
||||||
|
_, err = permissions.WatchNodeDeactivated(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed NodeDeactivated: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case newNodeDeleteEvent = <-ch:
|
||||||
|
updatePermissionedNodes(newNodeDeleteEvent.EnodeId, newNodeDeleteEvent.IpAddrPort, newNodeDeleteEvent.DiscPort, newNodeDeleteEvent.RaftPort, datadir, consensusEngine, NodeDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function listnes on the channel for any node blacklisting event via smart contract
|
||||||
|
// and adds the same disallowed-nodes.json
|
||||||
|
func monitorNodeBlacklisting(stack *node.Node, stateReader *ethclient.Client, consensusEngine string) {
|
||||||
|
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("failed to monitor new node add : ", "err" , err)
|
||||||
|
}
|
||||||
|
ch := make(chan *PermissionsNodeBlacklisted, 1)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var nodeBlacklistEvent *PermissionsNodeBlacklisted
|
||||||
|
|
||||||
|
_, err = permissions.WatchNodeBlacklisted(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed WatchNodeBlacklisted: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case nodeBlacklistEvent = <-ch:
|
||||||
|
updateDisallowedNodes(nodeBlacklistEvent, stack, consensusEngine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//this function populates the new node information into the permissioned-nodes.json file
|
||||||
|
func updatePermissionedNodes(enodeId , ipAddrPort, discPort, raftPort, dataDir, consensusEngine string, operation NodeOperation){
|
||||||
|
log.Debug("updatePermissionedNodes", "DataDir", dataDir, "file", PERMISSIONED_CONFIG)
|
||||||
|
|
||||||
|
path := filepath.Join(dataDir, PERMISSIONED_CONFIG)
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
log.Error("Read Error for permissioned-nodes.json file. This is because 'permissioned' flag is specified but no permissioned-nodes.json file is present.", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Load the nodes from the config file
|
||||||
|
blob, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("updatePermissionedNodes: Failed to access permissioned-nodes.json", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nodelist := []string{}
|
||||||
|
if err := json.Unmarshal(blob, &nodelist); err != nil {
|
||||||
|
log.Error("updatePermissionedNodes: Failed to load nodes list", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newEnodeId := formatEnodeId(enodeId, ipAddrPort, discPort, raftPort, consensusEngine)
|
||||||
|
|
||||||
|
if (operation == NodeAdd){
|
||||||
|
nodelist = append(nodelist, newEnodeId)
|
||||||
|
} else {
|
||||||
|
index := 0
|
||||||
|
for i, enodeId := range nodelist {
|
||||||
|
if (enodeId == newEnodeId){
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodelist = append(nodelist[:index], nodelist[index+1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
mu := sync.RWMutex{}
|
||||||
|
blob, _ = json.Marshal(nodelist)
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
if err:= ioutil.WriteFile(path, blob, 0644); err!= nil{
|
||||||
|
log.Error("updatePermissionedNodes: Error writing new node info to file", "err", err)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
//this function populates the new node information into the permissioned-nodes.json file
|
||||||
|
func updateDisallowedNodes(nodeBlacklistEvent *PermissionsNodeBlacklisted, stack *node.Node, consensusEngine string){
|
||||||
|
dataDir := stack.DataDir()
|
||||||
|
log.Debug("updateDisallowedNodes", "DataDir", dataDir, "file", BLACKLIST_CONFIG)
|
||||||
|
|
||||||
|
fileExisted := true
|
||||||
|
path := filepath.Join(dataDir, BLACKLIST_CONFIG)
|
||||||
|
// Check if the file is existing. If the file is not existing create the file
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
log.Error("Read Error for disallowed-nodes.json file." , "err", err)
|
||||||
|
if _, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0644); err != nil {
|
||||||
|
log.Error("Failed to create disallowed-nodes.json file ", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileExisted = false
|
||||||
|
}
|
||||||
|
|
||||||
|
nodelist := []string{}
|
||||||
|
// Load the nodes from the config file
|
||||||
|
if fileExisted == true {
|
||||||
|
blob, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("updateDisallowedNodes Failed to access disallowed-nodes.json", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (blob != nil) {
|
||||||
|
if err := json.Unmarshal(blob, &nodelist); err != nil {
|
||||||
|
log.Error("updateDisallowedNodes: Failed to load nodes list", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newEnodeId := formatEnodeId (nodeBlacklistEvent.EnodeId, nodeBlacklistEvent.IpAddrPort, nodeBlacklistEvent.DiscPort, nodeBlacklistEvent.RaftPort, consensusEngine )
|
||||||
|
nodelist = append(nodelist, newEnodeId)
|
||||||
|
mu := sync.RWMutex{}
|
||||||
|
blob, _ := json.Marshal(nodelist)
|
||||||
|
mu.Lock()
|
||||||
|
if err:= ioutil.WriteFile(path, blob, 0644); err!= nil{
|
||||||
|
log.Error("updateDisallowedNodes: Error writing new node info to file", "err", err)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// Disconnect the peer if it is already connected
|
||||||
|
disconnectNode(stack, newEnodeId, consensusEngine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manages account level permissions update
|
||||||
|
func manageAccountPermissions(stack *node.Node, stateReader *ethclient.Client) error {
|
||||||
|
//call populate nodes to populate the nodes into contract
|
||||||
|
if err := populateAcctPermissions (stack, stateReader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//monitor for nodes deletiin via smart contract
|
||||||
|
go monitorAccountPermissions(stack, stateReader)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// populates the nodes list from permissioned-nodes.json into the permissions
|
||||||
|
// smart contract
|
||||||
|
func populateAcctPermissions(stack *node.Node, stateReader *ethclient.Client) error{
|
||||||
|
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor node delete: ", "err" , err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &bind.FilterOpts{}
|
||||||
|
pastEvents, err := permissions.FilterAccountAccessModified(opts)
|
||||||
|
|
||||||
|
recExists := true
|
||||||
|
for recExists {
|
||||||
|
recExists = pastEvents.Next()
|
||||||
|
if recExists {
|
||||||
|
types.AddAccountAccess(pastEvents.Event.Address, pastEvents.Event.Access)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Monitors permissions changes at acount level and uodate the global permissions
|
||||||
|
// map with the same
|
||||||
|
func monitorAccountPermissions(stack *node.Node, stateReader *ethclient.Client) {
|
||||||
|
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
|
||||||
|
if err != nil {
|
||||||
|
log.Error ("Failed to monitor Account permissions : ", "err" , err)
|
||||||
|
}
|
||||||
|
ch := make(chan *PermissionsAccountAccessModified)
|
||||||
|
|
||||||
|
opts := &bind.WatchOpts{}
|
||||||
|
var blockNumber uint64 = 1
|
||||||
|
opts.Start = &blockNumber
|
||||||
|
var newEvent *PermissionsAccountAccessModified
|
||||||
|
|
||||||
|
_, err = permissions.WatchAccountAccessModified(opts, ch)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed NewNodeProposed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case newEvent = <-ch:
|
||||||
|
types.AddAccountAccess(newEvent.Address, newEvent.Access)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect the node from the network
|
||||||
|
func disconnectNode (stack *node.Node, enodeId, consensusEngine string){
|
||||||
|
if consensusEngine == RAFT {
|
||||||
|
var raftService *raft.RaftService
|
||||||
|
if err := stack.Service(&raftService); err == nil {
|
||||||
|
raftApi := raft.NewPublicRaftAPI(raftService)
|
||||||
|
|
||||||
|
//get the raftId for the given enodeId
|
||||||
|
raftId, err := raftApi.GetRaftId(enodeId)
|
||||||
|
if err == nil {
|
||||||
|
raftApi.RemovePeer(raftId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Istanbul - disconnect the peer
|
||||||
|
server := stack.Server()
|
||||||
|
if server != nil {
|
||||||
|
node, err := discover.ParseNode(enodeId)
|
||||||
|
if err == nil {
|
||||||
|
server.RemovePeer(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to format EnodeId
|
||||||
|
func formatEnodeId( enodeId , ipAddrPort, discPort, raftPort, consensusEngine string) string {
|
||||||
|
newEnodeId := "enode://" + enodeId + "@" + ipAddrPort + "?discPort=" + discPort
|
||||||
|
if consensusEngine == RAFT {
|
||||||
|
newEnodeId = enodeId + "&raftport=" + raftPort
|
||||||
|
}
|
||||||
|
return newEnodeId
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -82,6 +82,9 @@ var (
|
||||||
// ErrEtherValueUnsupported is returned if a transaction specifies an Ether Value
|
// ErrEtherValueUnsupported is returned if a transaction specifies an Ether Value
|
||||||
// for a private Quorum transaction.
|
// for a private Quorum transaction.
|
||||||
ErrEtherValueUnsupported = errors.New("ether value is not supported for private transactions")
|
ErrEtherValueUnsupported = errors.New("ether value is not supported for private transactions")
|
||||||
|
// ErrUnahorizedAccount is returned if the sender account is not authorized by the
|
||||||
|
// permissions module
|
||||||
|
ErrUnAuthorizedAccount = errors.New("Account not authorized for this operation")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -261,6 +264,17 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nonce returns the nonce for the given addr from the pending state.
|
||||||
|
// Can only be used for local transactions.
|
||||||
|
func (pool *TxPool) Nonce(addr common.Address) uint64 {
|
||||||
|
pool.mu.Lock()
|
||||||
|
defer pool.mu.Unlock()
|
||||||
|
if pool.pendingState == nil {
|
||||||
|
pool.lockedReset(nil, nil)
|
||||||
|
}
|
||||||
|
return pool.pendingState.GetNonce(addr)
|
||||||
|
}
|
||||||
|
|
||||||
// loop is the transaction pool's main event loop, waiting for and reacting to
|
// loop is the transaction pool's main event loop, waiting for and reacting to
|
||||||
// outside blockchain events as well as for various reporting and transaction
|
// outside blockchain events as well as for various reporting and transaction
|
||||||
// eviction events.
|
// eviction events.
|
||||||
|
@ -607,6 +621,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
if tx.Gas() < intrGas {
|
if tx.Gas() < intrGas {
|
||||||
return ErrIntrinsicGas
|
return ErrIntrinsicGas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the sender account is authorized to perform the transaction
|
||||||
|
if isQuorum {
|
||||||
|
if err := checkAccount(from, tx.To()); err != nil {
|
||||||
|
return ErrUnAuthorizedAccount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,3 +1278,33 @@ func (t *txLookup) Remove(hash common.Hash) {
|
||||||
|
|
||||||
delete(t.all, hash)
|
delete(t.all, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks if the account is permissioned for transaction
|
||||||
|
func checkAccount(fromAcct common.Address, toAcct *common.Address) error {
|
||||||
|
access := types.GetAcctAccess(fromAcct)
|
||||||
|
|
||||||
|
switch access {
|
||||||
|
case types.FullAccess:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case types.ReadOnly:
|
||||||
|
err := errors.New("Account Does not have transaction permissions")
|
||||||
|
return err
|
||||||
|
|
||||||
|
case types.Transact:
|
||||||
|
if toAcct == nil {
|
||||||
|
err := errors.New("Account Does not have contract create permissions")
|
||||||
|
return err
|
||||||
|
}else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case types.ContractDeploy:
|
||||||
|
if toAcct != nil {
|
||||||
|
err := errors.New("Account Does not have transacte permissions")
|
||||||
|
return err
|
||||||
|
}else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
package types
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccessType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
FullAccess AccessType = iota
|
||||||
|
ReadOnly
|
||||||
|
Transact
|
||||||
|
ContractDeploy
|
||||||
|
)
|
||||||
|
|
||||||
|
type PermStruct struct {
|
||||||
|
AcctId common.Address
|
||||||
|
AcctAccess AccessType
|
||||||
|
}
|
||||||
|
type OrgStruct struct {
|
||||||
|
OrgId string
|
||||||
|
Keys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PermAccountsMap map[common.Address][] *PermStruct
|
||||||
|
|
||||||
|
type PermOrgKeyMap map[string][] *OrgStruct
|
||||||
|
|
||||||
|
var AcctMap = make(map[common.Address] *PermStruct)
|
||||||
|
|
||||||
|
var OrgKeyMap = make(map[string] *OrgStruct)
|
||||||
|
|
||||||
|
func AddAccountAccess(acctId common.Address, access uint8) {
|
||||||
|
mu := sync.RWMutex{}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
AcctMap[acctId] = &PermStruct {AcctId : acctId, AcctAccess : AccessType(access)}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAcctAccess(acctId common.Address) AccessType {
|
||||||
|
mu := sync.RWMutex{}
|
||||||
|
if len(AcctMap) != 0 {
|
||||||
|
if _, ok := AcctMap[acctId]; ok {
|
||||||
|
mu.RLock()
|
||||||
|
acctAccess := AcctMap[acctId].AcctAccess
|
||||||
|
mu.RUnlock()
|
||||||
|
return acctAccess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FullAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddOrgKey(orgId string, keys string){
|
||||||
|
|
||||||
|
if len(OrgKeyMap) != 0 {
|
||||||
|
if _, ok := OrgKeyMap[orgId]; ok {
|
||||||
|
// Org record exists. Append the key only
|
||||||
|
OrgKeyMap[orgId].Keys = append (OrgKeyMap[orgId].Keys, keys)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// first record into the map or firts record for the org
|
||||||
|
var locKeys []string
|
||||||
|
locKeys = append(locKeys, keys);
|
||||||
|
OrgKeyMap[orgId] = &OrgStruct {OrgId : orgId, Keys : locKeys}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteOrgKey(orgId string, keys string){
|
||||||
|
|
||||||
|
if len(OrgKeyMap) != 0 {
|
||||||
|
if _, ok := OrgKeyMap[orgId]; ok {
|
||||||
|
for i, keyVal := range OrgKeyMap[orgId].Keys{
|
||||||
|
if keyVal == keys {
|
||||||
|
OrgKeyMap[orgId].Keys = append(OrgKeyMap[orgId].Keys[:i], OrgKeyMap[orgId].Keys[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResolvePrivateForKeys(orgId string ) []string {
|
||||||
|
var keys []string
|
||||||
|
mu := sync.RWMutex{}
|
||||||
|
|
||||||
|
if len(OrgKeyMap) != 0 {
|
||||||
|
if _, ok := OrgKeyMap[orgId]; ok {
|
||||||
|
if len(OrgKeyMap[orgId].Keys) > 0{
|
||||||
|
mu.RLock()
|
||||||
|
keys = OrgKeyMap[orgId].Keys
|
||||||
|
mu.RUnlock()
|
||||||
|
} else {
|
||||||
|
keys = append(keys, orgId)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys = append(keys, orgId)
|
||||||
|
return keys
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EthAPIBackend implements ethapi.Backend for full nodes
|
// EthAPIBackend implements ethapi.Backend for full nodes
|
||||||
|
@ -173,6 +174,7 @@ func (b *EthAPIBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
|
log.Info("inside SendTx")
|
||||||
return b.eth.txPool.AddLocal(signedTx)
|
return b.eth.txPool.AddLocal(signedTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client defines typed wrappers for the Ethereum RPC API.
|
// Client defines typed wrappers for the Ethereum RPC API.
|
||||||
|
@ -483,6 +484,7 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
|
||||||
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||||
// contract address after the transaction has been mined.
|
// contract address after the transaction has been mined.
|
||||||
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
|
log.Info("Inside SendTransaction")
|
||||||
data, err := rlp.EncodeToBytes(tx)
|
data, err := rlp.EncodeToBytes(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -389,11 +389,14 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
||||||
isPrivate := args.PrivateFor != nil
|
isPrivate := args.PrivateFor != nil
|
||||||
|
|
||||||
if isPrivate {
|
if isPrivate {
|
||||||
|
// Resolve the PrivateFrom - if its a org which is linked multiple constellation keys,
|
||||||
|
// this will fetch the linked constellation ids
|
||||||
|
privateFor := resolvePrivateFor(args.PrivateFor)
|
||||||
data := []byte(*args.Data)
|
data := []byte(*args.Data)
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", privateFor)
|
||||||
data, err := private.P.Send(data, args.PrivateFrom, args.PrivateFor)
|
data, err := private.P.Send(data, args.PrivateFrom, args.PrivateFor)
|
||||||
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", privateFor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
@ -418,6 +421,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
log.Info("Calling submitTransaction 1")
|
||||||
return submitTransaction(ctx, s.b, signed, isPrivate)
|
return submitTransaction(ctx, s.b, signed, isPrivate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,6 +1210,7 @@ func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (args *SendTxArgs) toTransaction() *types.Transaction {
|
func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||||
|
log.Info("inside toTransaction")
|
||||||
var input []byte
|
var input []byte
|
||||||
if args.Data != nil {
|
if args.Data != nil {
|
||||||
input = *args.Data
|
input = *args.Data
|
||||||
|
@ -1213,13 +1218,16 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||||
input = *args.Input
|
input = *args.Input
|
||||||
}
|
}
|
||||||
if args.To == nil {
|
if args.To == nil {
|
||||||
|
log.Info("Contract creation call")
|
||||||
return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||||
}
|
}
|
||||||
|
log.Info("New transaction callcreation call")
|
||||||
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// submitTransaction is a helper function that submits tx to txPool and logs a message.
|
// submitTransaction is a helper function that submits tx to txPool and logs a message.
|
||||||
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, isPrivate bool) (common.Hash, error) {
|
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, isPrivate bool) (common.Hash, error) {
|
||||||
|
log.Info("inside submitTransaction")
|
||||||
if isPrivate {
|
if isPrivate {
|
||||||
tx.SetPrivate()
|
tx.SetPrivate()
|
||||||
}
|
}
|
||||||
|
@ -1271,11 +1279,14 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
||||||
log.Info("args.data is nil")
|
log.Info("args.data is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) > 0 {
|
// Resolve the PrivateFrom - if its a org which is linked multiple constellation keys,
|
||||||
|
// this will fetch the linked constellation ids
|
||||||
|
privateFor := resolvePrivateFor(args.PrivateFor)
|
||||||
//Send private transaction to local Constellation node
|
//Send private transaction to local Constellation node
|
||||||
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
if len(data) > 0 {
|
||||||
data, err = private.P.Send(data, args.PrivateFrom, args.PrivateFor)
|
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", privateFor)
|
||||||
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
|
data, err = private.P.Send(data, args.PrivateFrom, privateFor)
|
||||||
|
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", privateFor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1313,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
log.Info("Calling submitTransaction 2")
|
||||||
return submitTransaction(ctx, s.b, signed, isPrivate)
|
return submitTransaction(ctx, s.b, signed, isPrivate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1312,6 +1324,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
|
||||||
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
log.Info("Calling submitTransaction 3")
|
||||||
return submitTransaction(ctx, s.b, tx, tx.IsPrivate())
|
return submitTransaction(ctx, s.b, tx, tx.IsPrivate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1689,3 +1702,16 @@ func (s *PublicBlockChainAPI) GetQuorumPayload(digestHex string) (string, error)
|
||||||
return fmt.Sprintf("0x%x", data), nil
|
return fmt.Sprintf("0x%x", data), nil
|
||||||
}
|
}
|
||||||
//End-Quorum
|
//End-Quorum
|
||||||
|
|
||||||
|
func resolvePrivateFor(privateFor []string) []string{
|
||||||
|
|
||||||
|
var newPrivateFor []string
|
||||||
|
|
||||||
|
for _, value := range privateFor {
|
||||||
|
keys := types.ResolvePrivateForKeys(value)
|
||||||
|
newPrivateFor = append(newPrivateFor, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPrivateFor
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -227,8 +227,9 @@ type Config struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||||
func ListenUDP(c conn, cfg Config) (*Table, error) {
|
func ListenUDP(c conn, cfg Config, knownNodes []*Node) (*Table, error) {
|
||||||
tab, _, err := newUDP(c, cfg)
|
|
||||||
|
tab, _, err := newUDP(c, cfg, knownNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -236,7 +237,8 @@ func ListenUDP(c conn, cfg Config) (*Table, error) {
|
||||||
return tab, nil
|
return tab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUDP(c conn, cfg Config) (*Table, *udp, error) {
|
func newUDP(c conn, cfg Config, knownNodes []*Node) (*Table, *udp, error) {
|
||||||
|
|
||||||
udp := &udp{
|
udp := &udp{
|
||||||
conn: c,
|
conn: c,
|
||||||
priv: cfg.PrivateKey,
|
priv: cfg.PrivateKey,
|
||||||
|
@ -251,10 +253,18 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
|
||||||
}
|
}
|
||||||
// TODO: separate TCP port
|
// TODO: separate TCP port
|
||||||
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
|
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
|
||||||
|
|
||||||
tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
|
tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepopulate nodes database with the known nodes
|
||||||
|
if nodesLen := len(knownNodes); nodesLen > 0 {
|
||||||
|
log.Info("Adding predefined nodes to node database", "count", nodesLen)
|
||||||
|
tab.stuff(knownNodes)
|
||||||
|
}
|
||||||
|
|
||||||
udp.Table = tab
|
udp.Table = tab
|
||||||
|
|
||||||
go udp.loop()
|
go udp.loop()
|
||||||
|
|
|
@ -60,6 +60,7 @@ type udpTest struct {
|
||||||
sent [][]byte
|
sent [][]byte
|
||||||
localkey, remotekey *ecdsa.PrivateKey
|
localkey, remotekey *ecdsa.PrivateKey
|
||||||
remoteaddr *net.UDPAddr
|
remoteaddr *net.UDPAddr
|
||||||
|
knownNodes []*Node
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUDPTest(t *testing.T) *udpTest {
|
func newUDPTest(t *testing.T) *udpTest {
|
||||||
|
@ -70,7 +71,7 @@ func newUDPTest(t *testing.T) *udpTest {
|
||||||
remotekey: newkey(),
|
remotekey: newkey(),
|
||||||
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
|
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
|
||||||
}
|
}
|
||||||
test.table, test.udp, _ = newUDP(test.pipe, Config{PrivateKey: test.localkey})
|
test.table, test.udp, _ = newUDP(test.pipe, Config{PrivateKey: test.localkey}, test.knownNodes)
|
||||||
// Wait for initial refresh so the table doesn't send unexpected findnode.
|
// Wait for initial refresh so the table doesn't send unexpected findnode.
|
||||||
<-test.table.initDone
|
<-test.table.initDone
|
||||||
return test
|
return test
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
@ -13,13 +14,14 @@ import (
|
||||||
const (
|
const (
|
||||||
NODE_NAME_LENGTH = 32
|
NODE_NAME_LENGTH = 32
|
||||||
PERMISSIONED_CONFIG = "permissioned-nodes.json"
|
PERMISSIONED_CONFIG = "permissioned-nodes.json"
|
||||||
|
BLACKLIST_CONFIG = "disallowed-nodes.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
// check if a given node is permissioned to connect to the change
|
// check if a given node is permissioned to connect to the change
|
||||||
func isNodePermissioned(nodename string, currentNode string, datadir string, direction string) bool {
|
func isNodePermissioned(nodename string, currentNode string, datadir string, direction string) bool {
|
||||||
|
|
||||||
var permissionedList []string
|
var permissionedList []string
|
||||||
nodes := parsePermissionedNodes(datadir)
|
nodes := ParsePermissionedNodes(datadir)
|
||||||
for _, v := range nodes {
|
for _, v := range nodes {
|
||||||
permissionedList = append(permissionedList, v.ID.String())
|
permissionedList = append(permissionedList, v.ID.String())
|
||||||
}
|
}
|
||||||
|
@ -28,6 +30,10 @@ func isNodePermissioned(nodename string, currentNode string, datadir string, dir
|
||||||
for _, v := range permissionedList {
|
for _, v := range permissionedList {
|
||||||
if v == nodename {
|
if v == nodename {
|
||||||
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "ALLOWED-BY", currentNode[:NODE_NAME_LENGTH])
|
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "ALLOWED-BY", currentNode[:NODE_NAME_LENGTH])
|
||||||
|
// check if the node is blacklisted
|
||||||
|
if isNodeBlackListed(nodename, datadir){
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH])
|
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH])
|
||||||
|
@ -39,7 +45,7 @@ func isNodePermissioned(nodename string, currentNode string, datadir string, dir
|
||||||
//this is a shameless copy from the config.go. It is a duplication of the code
|
//this is a shameless copy from the config.go. It is a duplication of the code
|
||||||
//for the timebeing to allow reload of the permissioned nodes while the server is running
|
//for the timebeing to allow reload of the permissioned nodes while the server is running
|
||||||
|
|
||||||
func parsePermissionedNodes(DataDir string) []*discover.Node {
|
func ParsePermissionedNodes(DataDir string) []*discover.Node {
|
||||||
|
|
||||||
log.Debug("parsePermissionedNodes", "DataDir", DataDir, "file", PERMISSIONED_CONFIG)
|
log.Debug("parsePermissionedNodes", "DataDir", DataDir, "file", PERMISSIONED_CONFIG)
|
||||||
|
|
||||||
|
@ -76,3 +82,33 @@ func parsePermissionedNodes(DataDir string) []*discover.Node {
|
||||||
}
|
}
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function checks if the node is black-listed
|
||||||
|
func isNodeBlackListed (nodeName, dataDir string ) bool {
|
||||||
|
log.Debug("isNodeBlackListed", "DataDir", dataDir, "file", BLACKLIST_CONFIG)
|
||||||
|
|
||||||
|
path := filepath.Join(dataDir, BLACKLIST_CONFIG)
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
log.Debug("Read Error for disallowed-nodes.json file. disallowed-nodes.json file is not present.", "err", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Load the nodes from the config file
|
||||||
|
blob, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("isNodeBlackListed: Failed to access nodes", "err", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
nodelist := []string{}
|
||||||
|
if err := json.Unmarshal(blob, &nodelist); err != nil {
|
||||||
|
log.Debug("parsePermissionedNodes: Failed to load nodes", "err", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range nodelist {
|
||||||
|
if strings.Contains(v, nodeName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -101,6 +101,10 @@ type Config struct {
|
||||||
// allowed to connect, even above the peer limit.
|
// allowed to connect, even above the peer limit.
|
||||||
TrustedNodes []*discover.Node
|
TrustedNodes []*discover.Node
|
||||||
|
|
||||||
|
// KnownNodes contains a list of nodes that are used to pre-populate the
|
||||||
|
// node database.
|
||||||
|
KnownNodes []*discover.Node
|
||||||
|
|
||||||
// Connectivity can be restricted to certain IP networks.
|
// Connectivity can be restricted to certain IP networks.
|
||||||
// If this option is set to a non-nil value, only hosts which match one of the
|
// If this option is set to a non-nil value, only hosts which match one of the
|
||||||
// IP networks contained in the list are considered.
|
// IP networks contained in the list are considered.
|
||||||
|
@ -449,6 +453,13 @@ func (srv *Server) Start() (err error) {
|
||||||
sconn = &sharedUDPConn{conn, unhandled}
|
sconn = &sharedUDPConn{conn, unhandled}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
knownNodes := append([]*discover.Node(nil), srv.KnownNodes...)
|
||||||
|
if srv.EnableNodePermission {
|
||||||
|
knownNodes = append(knownNodes, ParsePermissionedNodes(srv.DataDir)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv.KnownNodes = knownNodes
|
||||||
|
|
||||||
// node table
|
// node table
|
||||||
if !srv.NoDiscovery {
|
if !srv.NoDiscovery {
|
||||||
cfg := discover.Config{
|
cfg := discover.Config{
|
||||||
|
@ -459,7 +470,9 @@ func (srv *Server) Start() (err error) {
|
||||||
Bootnodes: srv.BootstrapNodes,
|
Bootnodes: srv.BootstrapNodes,
|
||||||
Unhandled: unhandled,
|
Unhandled: unhandled,
|
||||||
}
|
}
|
||||||
ntab, err := discover.ListenUDP(conn, cfg)
|
log.Info("calling ListenUDP")
|
||||||
|
ntab, err := discover.ListenUDP(conn, cfg, knownNodes)
|
||||||
|
log.Info("after ListenUDP")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package params
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
|
var (
|
||||||
|
QuorumPermissionsContract = common.Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32}
|
||||||
|
PrivateKeyManagementContract = common.Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34}
|
||||||
|
)
|
|
@ -42,3 +42,7 @@ func (s *PublicRaftAPI) Cluster() []*Address {
|
||||||
nodeInfo := s.raftService.raftProtocolManager.NodeInfo()
|
nodeInfo := s.raftService.raftProtocolManager.NodeInfo()
|
||||||
return append(nodeInfo.PeerAddresses, nodeInfo.Address)
|
return append(nodeInfo.PeerAddresses, nodeInfo.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PublicRaftAPI) GetRaftId (enodeId string) (uint16, error) {
|
||||||
|
return s.raftService.raftProtocolManager.FetchRaftId(enodeId)
|
||||||
|
}
|
||||||
|
|
|
@ -920,3 +920,17 @@ func (pm *ProtocolManager) LeaderAddress() (*Address, error) {
|
||||||
// We expect to reach this if pm.leader is 0, which is how etcd denotes the lack of a leader.
|
// We expect to reach this if pm.leader is 0, which is how etcd denotes the lack of a leader.
|
||||||
return nil, errors.New("no leader is currently elected")
|
return nil, errors.New("no leader is currently elected")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the raft id for a given enodeId
|
||||||
|
func (pm *ProtocolManager) FetchRaftId (enodeId string) (uint16, error) {
|
||||||
|
node, err := discover.ParseNode(enodeId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for raftId, peer := range pm.peers {
|
||||||
|
if peer.p2pNode.ID == node.ID {
|
||||||
|
return raftId, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("node not found in the cluster: %v", enodeId)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue