Merge of 1812 with permissions changes and fix of conflicts

This commit is contained in:
vsmk98 2018-10-12 04:24:36 +00:00
commit d6352b30c3
24 changed files with 5988 additions and 17 deletions

View File

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"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
@ -183,6 +184,7 @@ func (c *BoundContract) Transfer(opts *TransactOpts) (*types.Transaction, error)
// transact executes an actual transaction invocation, first deriving any missing
// authorization fields, and then scheduling the transaction for execution.
func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
log.Info("Inside transact")
var err error
// 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 {
return nil, err
}
log.Info("calling SendTransaction in side transact")
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
return nil, err
}

View File

@ -117,6 +117,8 @@ func main() {
}
}
var knownNodes []*discover.Node
if *runv5 {
if _, err := discv5.ListenUDP(nodeKey, conn, realaddr, "", restrictList); err != nil {
utils.Fatalf("%v", err)
@ -127,7 +129,7 @@ func main() {
AnnounceAddr: realaddr,
NetRestrict: restrictList,
}
if _, err := discover.ListenUDP(conn, cfg); err != nil {
if _, err := discover.ListenUDP(conn, cfg, knownNodes); err != nil {
utils.Fatalf("%v", err)
}
}

View File

@ -39,6 +39,8 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"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"
)
@ -289,6 +291,18 @@ func startNode(ctx *cli.Context, stack *node.Node) {
events := make(chan accounts.WalletEvent, 16)
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() {
// Create a chain state reader for self-derivation
rpcClient, err := stack.Attach()

21
controls/client.go Normal file
View File

@ -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
}

View File

@ -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]);
}
}
}
}

View File

@ -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"}]

130
controls/cluster/cluster.go Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -82,6 +82,9 @@ var (
// ErrEtherValueUnsupported is returned if a transaction specifies an Ether Value
// for a private Quorum transaction.
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 (
@ -261,6 +264,17 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
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
// outside blockchain events as well as for various reporting and transaction
// eviction events.
@ -607,6 +621,14 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
if tx.Gas() < intrGas {
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
}
@ -1256,3 +1278,33 @@ func (t *txLookup) Remove(hash common.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
}

View File

@ -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
}

View File

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/log"
)
// 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 {
log.Info("inside SendTx")
return b.eth.txPool.AddLocal(signedTx)
}

View File

@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/log"
)
// 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
// contract address after the transaction has been mined.
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
log.Info("Inside SendTransaction")
data, err := rlp.EncodeToBytes(tx)
if err != nil {
return err

View File

@ -389,11 +389,14 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
isPrivate := args.PrivateFor != nil
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)
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)
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 {
return common.Hash{}, err
}
@ -418,6 +421,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
if err != nil {
return common.Hash{}, err
}
log.Info("Calling submitTransaction 1")
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 {
log.Info("inside toTransaction")
var input []byte
if args.Data != nil {
input = *args.Data
@ -1213,13 +1218,16 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
input = *args.Input
}
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)
}
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)
}
// 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) {
log.Info("inside submitTransaction")
if isPrivate {
tx.SetPrivate()
}
@ -1271,14 +1279,17 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
log.Info("args.data is nil")
}
// 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
if len(data) > 0 {
//Send private transaction to local Constellation node
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", 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)
if err != nil {
return common.Hash{}, err
}
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", 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 {
return common.Hash{}, err
}
}
// zekun: HACK
d := hexutil.Bytes(data)
@ -1302,6 +1313,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
if err != nil {
return common.Hash{}, err
}
log.Info("Calling submitTransaction 2")
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 {
return common.Hash{}, err
}
log.Info("Calling submitTransaction 3")
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
}
//End-Quorum
func resolvePrivateFor(privateFor []string) []string{
var newPrivateFor []string
for _, value := range privateFor {
keys := types.ResolvePrivateForKeys(value)
newPrivateFor = append(newPrivateFor, keys...)
}
return newPrivateFor
}

View File

@ -227,8 +227,9 @@ type Config struct {
}
// ListenUDP returns a new table that listens for UDP packets on laddr.
func ListenUDP(c conn, cfg Config) (*Table, error) {
tab, _, err := newUDP(c, cfg)
func ListenUDP(c conn, cfg Config, knownNodes []*Node) (*Table, error) {
tab, _, err := newUDP(c, cfg, knownNodes)
if err != nil {
return nil, err
}
@ -236,7 +237,8 @@ func ListenUDP(c conn, cfg Config) (*Table, error) {
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{
conn: c,
priv: cfg.PrivateKey,
@ -251,10 +253,18 @@ func newUDP(c conn, cfg Config) (*Table, *udp, error) {
}
// TODO: separate TCP port
udp.ourEndpoint = makeEndpoint(realaddr, uint16(realaddr.Port))
tab, err := newTable(udp, PubkeyID(&cfg.PrivateKey.PublicKey), realaddr, cfg.NodeDBPath, cfg.Bootnodes)
if err != nil {
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
go udp.loop()

View File

@ -60,6 +60,7 @@ type udpTest struct {
sent [][]byte
localkey, remotekey *ecdsa.PrivateKey
remoteaddr *net.UDPAddr
knownNodes []*Node
}
func newUDPTest(t *testing.T) *udpTest {
@ -70,7 +71,7 @@ func newUDPTest(t *testing.T) *udpTest {
remotekey: newkey(),
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.
<-test.table.initDone
return test

View File

@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
@ -13,13 +14,14 @@ import (
const (
NODE_NAME_LENGTH = 32
PERMISSIONED_CONFIG = "permissioned-nodes.json"
BLACKLIST_CONFIG = "disallowed-nodes.json"
)
// check if a given node is permissioned to connect to the change
func isNodePermissioned(nodename string, currentNode string, datadir string, direction string) bool {
var permissionedList []string
nodes := parsePermissionedNodes(datadir)
nodes := ParsePermissionedNodes(datadir)
for _, v := range nodes {
permissionedList = append(permissionedList, v.ID.String())
}
@ -28,6 +30,10 @@ func isNodePermissioned(nodename string, currentNode string, datadir string, dir
for _, v := range permissionedList {
if v == nodename {
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
}
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
//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)
@ -76,3 +82,33 @@ func parsePermissionedNodes(DataDir string) []*discover.Node {
}
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
}

View File

@ -101,6 +101,10 @@ type Config struct {
// allowed to connect, even above the peer limit.
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.
// 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.
@ -449,6 +453,13 @@ func (srv *Server) Start() (err error) {
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
if !srv.NoDiscovery {
cfg := discover.Config{
@ -459,7 +470,9 @@ func (srv *Server) Start() (err error) {
Bootnodes: srv.BootstrapNodes,
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 {
return err
}

8
params/quorum.go Normal file
View File

@ -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}
)

View File

@ -42,3 +42,7 @@ func (s *PublicRaftAPI) Cluster() []*Address {
nodeInfo := s.raftService.raftProtocolManager.NodeInfo()
return append(nodeInfo.PeerAddresses, nodeInfo.Address)
}
func (s *PublicRaftAPI) GetRaftId (enodeId string) (uint16, error) {
return s.raftService.raftProtocolManager.FetchRaftId(enodeId)
}

View File

@ -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.
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)
}