|
|
|
@ -3,11 +3,6 @@ package quorum
|
|
|
|
|
import (
|
|
|
|
|
"crypto/ecdsa"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
|
|
|
|
"math/big"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/accounts"
|
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
@ -19,6 +14,7 @@ import (
|
|
|
|
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
|
"github.com/ethereum/go-ethereum/params"
|
|
|
|
|
"math/big"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//default gas limit to use if not passed in sendTxArgs
|
|
|
|
@ -31,18 +27,17 @@ var defaultGasPrice = big.NewInt(0)
|
|
|
|
|
type PermAction int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
ProposeNode PermAction = iota
|
|
|
|
|
ApproveNode
|
|
|
|
|
ProposeNodeDeactivation
|
|
|
|
|
ApproveNodeDeactivation
|
|
|
|
|
ProposeNodeActivation
|
|
|
|
|
ApproveNodeActivation
|
|
|
|
|
ProposeNodeBlacklisting
|
|
|
|
|
ApproveNodeBlacklisting
|
|
|
|
|
AddVoter
|
|
|
|
|
RemoveVoter
|
|
|
|
|
SetAccountAccess
|
|
|
|
|
CancelPendingOperation
|
|
|
|
|
AddOrg PermAction = iota
|
|
|
|
|
ApproveOrg
|
|
|
|
|
UpdateOrgStatus
|
|
|
|
|
ApproveOrgStatus
|
|
|
|
|
AddNode
|
|
|
|
|
UpdateNodeStatus
|
|
|
|
|
AssignOrgAdminAccount
|
|
|
|
|
ApproveOrgAdminAccount
|
|
|
|
|
AddNewRole
|
|
|
|
|
RemoveRole
|
|
|
|
|
AssignAccountRole
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// OrgKeyAction represents an action in cluster contract
|
|
|
|
@ -63,9 +58,6 @@ type NodeCheckRetVal int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
Success NodeCheckRetVal = iota
|
|
|
|
|
DetailsMismatch
|
|
|
|
|
NothingToApprove
|
|
|
|
|
NothingToCancel
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Voter access type
|
|
|
|
@ -76,6 +68,10 @@ const (
|
|
|
|
|
Inactive
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type PermissionContracts struct {
|
|
|
|
|
PermInterf *pbind.PermInterface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// QuorumControlsAPI provides an API to access Quorum's node permission and org key management related services
|
|
|
|
|
type QuorumControlsAPI struct {
|
|
|
|
|
txPool *core.TxPool
|
|
|
|
@ -88,18 +84,22 @@ type QuorumControlsAPI struct {
|
|
|
|
|
permEnabled bool
|
|
|
|
|
orgEnabled bool
|
|
|
|
|
permConfig *types.PermissionConfig
|
|
|
|
|
permInterf *pbind.PermInterface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// txArgs holds arguments required for execute functions
|
|
|
|
|
type txArgs struct {
|
|
|
|
|
voter common.Address
|
|
|
|
|
nodeId string
|
|
|
|
|
orgId string
|
|
|
|
|
url string
|
|
|
|
|
roleId string
|
|
|
|
|
isVoter bool
|
|
|
|
|
acctId common.Address
|
|
|
|
|
accessType uint8
|
|
|
|
|
status uint8
|
|
|
|
|
voter common.Address
|
|
|
|
|
morgId string
|
|
|
|
|
tmKey string
|
|
|
|
|
txa ethapi.SendTxArgs
|
|
|
|
|
acctId common.Address
|
|
|
|
|
accessType uint8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type nodeStatus struct {
|
|
|
|
@ -187,7 +187,7 @@ var (
|
|
|
|
|
|
|
|
|
|
// NewQuorumControlsAPI creates a new QuorumControlsAPI to access quorum services
|
|
|
|
|
func NewQuorumControlsAPI(tp *core.TxPool, am *accounts.Manager) *QuorumControlsAPI {
|
|
|
|
|
return &QuorumControlsAPI{tp, nil, am, nil, nil, nil, nil, false, false, nil}
|
|
|
|
|
return &QuorumControlsAPI{tp, nil, am, nil, nil, nil, nil, false, false, nil, nil}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// helper function decodes the node status to string
|
|
|
|
@ -207,9 +207,9 @@ func decodePendingOp(pendingOp uint8) string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Init initializes QuorumControlsAPI with eth client, permission contract and org key management control
|
|
|
|
|
func (p *QuorumControlsAPI) Init(ethClnt *ethclient.Client, key *ecdsa.PrivateKey, apiName string, pconfig *types.PermissionConfig) error {
|
|
|
|
|
func (p *QuorumControlsAPI) Init(ethClnt *ethclient.Client, key *ecdsa.PrivateKey, apiName string, pconfig *types.PermissionConfig, pc *pbind.PermInterface) error {
|
|
|
|
|
p.ethClnt = ethClnt
|
|
|
|
|
if apiName == "quorumNodeMgmt" || apiName == "quorumAcctMgmt" {
|
|
|
|
|
if apiName == "quorumPermission" {
|
|
|
|
|
var contractAddress common.Address
|
|
|
|
|
//TODO: to be updated with new contract API
|
|
|
|
|
//if pconfig.IsEmpty() {
|
|
|
|
@ -233,43 +233,10 @@ func (p *QuorumControlsAPI) Init(ethClnt *ethclient.Client, key *ecdsa.PrivateKe
|
|
|
|
|
p.orgEnabled = true
|
|
|
|
|
}
|
|
|
|
|
p.key = key
|
|
|
|
|
p.permInterf = pc
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the list of Nodes and status of each
|
|
|
|
|
func (s *QuorumControlsAPI) PermissionNodeList() []nodeStatus {
|
|
|
|
|
if !s.permEnabled {
|
|
|
|
|
nodeStatArr := make([]nodeStatus, 1)
|
|
|
|
|
nodeStatArr[0].EnodeId = "Permisssions control not enabled for network"
|
|
|
|
|
return nodeStatArr
|
|
|
|
|
}
|
|
|
|
|
ps := s.newPermSessionWithNodeKeySigner()
|
|
|
|
|
// get the total number of nodes on the contract
|
|
|
|
|
nodeCnt, err := ps.GetNumberOfNodes()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
nodeCntI := nodeCnt.Int64()
|
|
|
|
|
nodeStatArr := make([]nodeStatus, nodeCntI)
|
|
|
|
|
// loop for each index and get the node details from the contract
|
|
|
|
|
i := int64(0)
|
|
|
|
|
for i < nodeCntI {
|
|
|
|
|
nodeDtls, err := ps.GetNodeDetailsFromIndex(big.NewInt(i))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("error getting node details", "err", err)
|
|
|
|
|
} else {
|
|
|
|
|
nodeStatArr[i].EnodeId = "enode://" + nodeDtls.EnodeId + "@" + nodeDtls.IpAddrPort
|
|
|
|
|
nodeStatArr[i].EnodeId += "?discport=" + nodeDtls.DiscPort
|
|
|
|
|
if len(nodeDtls.RaftPort) > 0 {
|
|
|
|
|
nodeStatArr[i].EnodeId += "&raftport=" + nodeDtls.RaftPort
|
|
|
|
|
}
|
|
|
|
|
nodeStatArr[i].Status = decodeNodeStatus(nodeDtls.NodeStatus)
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return nodeStatArr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *QuorumControlsAPI) OrgList() []types.OrgInfo {
|
|
|
|
|
return types.OrgInfoMap.GetOrgList()
|
|
|
|
|
}
|
|
|
|
@ -286,86 +253,6 @@ func (s *QuorumControlsAPI) AcctList() []types.AccountInfo {
|
|
|
|
|
return types.AcctInfoMap.GetAcctList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the list of permissioned accounts and access type of each
|
|
|
|
|
func (s *QuorumControlsAPI) PermissionAccountList() []accountInfo {
|
|
|
|
|
if !s.permEnabled {
|
|
|
|
|
acctInfoArr := make([]accountInfo, 1)
|
|
|
|
|
acctInfoArr[0].Address = "Account access control not enable for the network"
|
|
|
|
|
return acctInfoArr
|
|
|
|
|
}
|
|
|
|
|
ps := s.newPermSessionWithNodeKeySigner()
|
|
|
|
|
// get the total number of accounts with permissions
|
|
|
|
|
acctCnt, err := ps.GetNumberOfAccounts()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
acctCntI := acctCnt.Int64()
|
|
|
|
|
log.Debug("total permission accounts", "count", acctCntI)
|
|
|
|
|
acctInfoArr := make([]accountInfo, acctCntI)
|
|
|
|
|
// loop for each index and get the node details from the contract
|
|
|
|
|
i := int64(0)
|
|
|
|
|
for i < acctCntI {
|
|
|
|
|
a, err := ps.GetAccountDetails(big.NewInt(i))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("error getting account info", "err", err)
|
|
|
|
|
} else {
|
|
|
|
|
acctInfoArr[i].Address = a.Acct.String()
|
|
|
|
|
acctInfoArr[i].Access = decodeAccountPermission(a.AcctAccess)
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return acctInfoArr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the list of voters for node management
|
|
|
|
|
func (s *QuorumControlsAPI) VoterList() []string {
|
|
|
|
|
if !s.permEnabled {
|
|
|
|
|
voterArr := make([]string, 1)
|
|
|
|
|
voterArr[0] = "Permissions control not enabled for the network"
|
|
|
|
|
return voterArr
|
|
|
|
|
}
|
|
|
|
|
ps := s.newPermSessionWithNodeKeySigner()
|
|
|
|
|
// get the total number of accounts with permissions
|
|
|
|
|
voterCnt, err := ps.GetNumberOfVoters()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
voterCntI := voterCnt.Int64()
|
|
|
|
|
log.Debug("total voters", "count", voterCntI)
|
|
|
|
|
var voterArr []string
|
|
|
|
|
// loop for each index and get the node details from the contract
|
|
|
|
|
i := int64(0)
|
|
|
|
|
for i < voterCntI {
|
|
|
|
|
voter, err := ps.GetVoter(big.NewInt(i))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("error getting voter info", "err", err)
|
|
|
|
|
} else {
|
|
|
|
|
if voter.VoterStatus == uint8(Active) {
|
|
|
|
|
voterArr = append(voterArr, voter.Addr.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return voterArr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *QuorumControlsAPI) newPermSessionWithNodeKeySigner() *pbind.PermissionsSession {
|
|
|
|
|
auth := bind.NewKeyedTransactor(s.key)
|
|
|
|
|
ps := &pbind.PermissionsSession{
|
|
|
|
|
Contract: s.permContr,
|
|
|
|
|
CallOpts: bind.CallOpts{
|
|
|
|
|
Pending: true,
|
|
|
|
|
},
|
|
|
|
|
TransactOpts: bind.TransactOpts{
|
|
|
|
|
From: auth.From,
|
|
|
|
|
Signer: auth.Signer,
|
|
|
|
|
GasLimit: 4700000,
|
|
|
|
|
GasPrice: big.NewInt(0),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
return ps
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *QuorumControlsAPI) newOrgKeySessionWithNodeKeySigner() *obind.ClusterSession {
|
|
|
|
|
auth := bind.NewKeyedTransactor(s.key)
|
|
|
|
|
cs := &obind.ClusterSession{
|
|
|
|
@ -382,66 +269,50 @@ func (s *QuorumControlsAPI) newOrgKeySessionWithNodeKeySigner() *obind.ClusterSe
|
|
|
|
|
}
|
|
|
|
|
return cs
|
|
|
|
|
}
|
|
|
|
|
func decodeAccountPermission(access uint8) string {
|
|
|
|
|
if status, ok := accountPermMap[access]; ok {
|
|
|
|
|
return status
|
|
|
|
|
}
|
|
|
|
|
return "Unknown"
|
|
|
|
|
|
|
|
|
|
func (s *QuorumControlsAPI) AddOrg(orgId string, url string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AddOrg, txArgs{orgId: orgId, url: url, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddVoter adds an account to the list of accounts that can approve nodes proposed or deactivated
|
|
|
|
|
func (s *QuorumControlsAPI) AddVoter(vaddr common.Address, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AddVoter, txArgs{voter: vaddr, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveOrg(orgId string, url string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveOrg, txArgs{orgId: orgId, url: url, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveVoter removes an account from the list of accounts that can approve nodes proposed or deactivated
|
|
|
|
|
func (s *QuorumControlsAPI) RemoveVoter(vaddr common.Address, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(RemoveVoter, txArgs{voter: vaddr, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) UpdateOrgStatus(orgId string, status uint8, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
log.Info("AJ-update org status", "org", orgId, "status", status)
|
|
|
|
|
return s.executePermAction(UpdateOrgStatus, txArgs{orgId: orgId, status: status, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ProposeNode proposes a node to join the network
|
|
|
|
|
func (s *QuorumControlsAPI) ProposeNode(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ProposeNode, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) AddNode(orgId string, url string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AddNode, txArgs{orgId: orgId, url: url, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApproveNode approves a proposed node to join the network
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveNode(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveNode, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) UpdateNodeStatus(orgId string, url string, status uint8, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(UpdateNodeStatus, txArgs{orgId: orgId, url: url, status: status, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeactivateNode requests a node to get deactivated
|
|
|
|
|
func (s *QuorumControlsAPI) ProposeNodeDeactivation(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ProposeNodeDeactivation, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveOrgStatus(orgId string, status uint8, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveOrgStatus, txArgs{orgId: orgId, status: status, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApproveDeactivateNode approves a node to get deactivated
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveNodeDeactivation(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveNodeDeactivation, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) AssignOrgAdminAccount(orgId string, acct common.Address, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AssignOrgAdminAccount, txArgs{orgId: orgId, acctId: acct, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ActivateNode requests a deactivated node to get activated
|
|
|
|
|
func (s *QuorumControlsAPI) ProposeNodeActivation(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ProposeNodeActivation, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveOrgAdminAccount(acct common.Address, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveOrgAdminAccount, txArgs{acctId: acct, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ApproveNodeActivation approves a node to get activated back
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveNodeActivation(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveNodeActivation, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) AddNewRole(roleId string, orgId string, access uint8, isVoter bool, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AddNewRole, txArgs{orgId: orgId, roleId: roleId, accessType: access, isVoter: isVoter, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Request a node to be blacklisted
|
|
|
|
|
func (s *QuorumControlsAPI) ProposeNodeBlacklisting(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ProposeNodeBlacklisting, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) RemoveRole(roleId string, orgId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(RemoveRole, txArgs{orgId: orgId, roleId: roleId, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Approves blacklisting of a node proposed for blacklisting
|
|
|
|
|
func (s *QuorumControlsAPI) ApproveNodeBlacklisting(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(ApproveNodeBlacklisting, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CancelPendingOperation cancels a pending operation
|
|
|
|
|
func (s *QuorumControlsAPI) CancelPendingOperation(nodeId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(CancelPendingOperation, txArgs{nodeId: nodeId, txa: txa})
|
|
|
|
|
func (s *QuorumControlsAPI) AssignAccountRole(acct common.Address, roleId string, orgId string, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(AssignAccountRole, txArgs{orgId: orgId, roleId: roleId, acctId: acct, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddMasterOrg adds an new master organization to the contract
|
|
|
|
@ -479,129 +350,13 @@ func (s *QuorumControlsAPI) ApprovePendingOp(orgId string, txa ethapi.SendTxArgs
|
|
|
|
|
return s.executeOrgKeyAction(ApprovePendingOp, txArgs{txa: txa, orgId: orgId})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetAccountAccess sets the account access to the given type
|
|
|
|
|
func (s *QuorumControlsAPI) SetAccountAccess(acct common.Address, access uint8, txa ethapi.SendTxArgs) ExecStatus {
|
|
|
|
|
return s.executePermAction(SetAccountAccess, txArgs{acctId: acct, accessType: access, txa: txa})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns node details given the enode id
|
|
|
|
|
func getNodeDetailsFromEnode(nodeId string) (string, string, string, string, error) {
|
|
|
|
|
node, err := enode.ParseV4(nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return "", "", "", "", err
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
ipAddr := node.IP().String()
|
|
|
|
|
port := fmt.Sprintf("%v", node.TCP)
|
|
|
|
|
discPort := fmt.Sprintf("%v", node.UDP)
|
|
|
|
|
raftPort := fmt.Sprintf("%v", node.RaftPort)
|
|
|
|
|
ipAddrPort := ipAddr + ":" + port
|
|
|
|
|
|
|
|
|
|
return enodeID, discPort, raftPort, ipAddrPort, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks if the input node details for approval is matching with details stored in contract
|
|
|
|
|
func checkNodeDetails(ps *pbind.PermissionsSession, nodeId string, action PermAction) (error, ExecStatus) {
|
|
|
|
|
enodeID, discPort, raftPort, ipAddrPort, err := getNodeDetailsFromEnode(nodeId)
|
|
|
|
|
|
|
|
|
|
cnode, err := ps.GetNodeDetails(enodeID)
|
|
|
|
|
log.Info("SMK-checkNodeDetails @481", "err", err, "nodeStatus", cnode.NodeStatus)
|
|
|
|
|
if err == nil {
|
|
|
|
|
log.Info("SMK-checkNodeDetails @483")
|
|
|
|
|
nodeStatus := decodeNodeStatus(cnode.NodeStatus)
|
|
|
|
|
// if node status is Blacklisted no activities are allowed on the same.
|
|
|
|
|
if nodeStatus == "Blacklisted" {
|
|
|
|
|
return errors.New("Cannot propose blacklisted node"), ErrBlacklistedNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nodeStatus == "NotInNetwork" && action != ProposeNode && action != ProposeNodeBlacklisting {
|
|
|
|
|
return errors.New("operation cannot be performed"), ErrOpNotAllowed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newNode := false
|
|
|
|
|
if nodeStatus == "NotInNetwork" && len(cnode.IpAddrPort) == 0 {
|
|
|
|
|
newNode = true
|
|
|
|
|
}
|
|
|
|
|
detailsMatch := false
|
|
|
|
|
if strings.Compare(ipAddrPort, cnode.IpAddrPort) == 0 && strings.Compare(discPort, cnode.DiscPort) == 0 && strings.Compare(raftPort, cnode.RaftPort) == 0 {
|
|
|
|
|
detailsMatch = true
|
|
|
|
|
}
|
|
|
|
|
// if the node is not in network and is being proposed for blacklisting or as a new node
|
|
|
|
|
// allow the operation. For anyother operation, the node will be in the network and all details
|
|
|
|
|
// should match
|
|
|
|
|
if action == ProposeNode || action == ProposeNodeBlacklisting {
|
|
|
|
|
if !newNode && !detailsMatch {
|
|
|
|
|
return errors.New("Details Mismtach"), ErrNodeDetailsMismatch
|
|
|
|
|
}
|
|
|
|
|
} else if !detailsMatch {
|
|
|
|
|
return errors.New("Details Mismtach"), ErrNodeDetailsMismatch
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if propose action, check if node status allows the operation
|
|
|
|
|
if (action == ProposeNode && nodeStatus != "NotInNetwork") ||
|
|
|
|
|
(action == ProposeNodeDeactivation && nodeStatus != "Approved") ||
|
|
|
|
|
(action == ProposeNodeActivation && nodeStatus != "Deactivated") {
|
|
|
|
|
return errors.New("operation cannot be performed"), ErrOpNotAllowed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if approval action, check if anything pendinga approval
|
|
|
|
|
if (action == ApproveNode && nodeStatus != "PendingApproval") ||
|
|
|
|
|
(action == ApproveNodeDeactivation && nodeStatus != "PendingDeactivation") ||
|
|
|
|
|
(action == ApproveNodeActivation && nodeStatus != "PendingActivation") ||
|
|
|
|
|
(action == ApproveNodeBlacklisting && nodeStatus != "PendingBlacklisting") {
|
|
|
|
|
return errors.New("Nothing to approve"), ErrNothingToApprove
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if action == CancelPendingOperation && nodeStatus != "PendingApproval" &&
|
|
|
|
|
nodeStatus != "PendingDeactivation" && nodeStatus != "PendingActivation" &&
|
|
|
|
|
nodeStatus != "PendingBlacklisting" {
|
|
|
|
|
return errors.New("Nothing to cancel"), ErrNothingToCancel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (action == ProposeNode && nodeStatus == "PendingApproval") ||
|
|
|
|
|
(action == ProposeNodeDeactivation && nodeStatus == "PendingDeactivation") ||
|
|
|
|
|
(action == ProposeNodeActivation && nodeStatus == "PendingActivation") ||
|
|
|
|
|
(action == ProposeNodeBlacklisting && nodeStatus == "PendingBlacklisting") {
|
|
|
|
|
return errors.New("Node already proposed"), ErrNodeProposed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, ExecSuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// performs all necessary validation before the request can be processed
|
|
|
|
|
func (s *QuorumControlsAPI) validateOpDetails(ps *pbind.PermissionsSession, enodeID string, from common.Address, action PermAction) (error, ExecStatus) {
|
|
|
|
|
|
|
|
|
|
// check if the input node is fine
|
|
|
|
|
err, execStatus := checkNodeDetails(ps, enodeID, action)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return errors.New("Node details mismatch"), execStatus
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if action is propose type then check if voter nodes are there in the network
|
|
|
|
|
if action == ProposeNode || action == ProposeNodeDeactivation || action == ProposeNodeActivation || action == ProposeNodeBlacklisting {
|
|
|
|
|
if !checkVoterExists(ps) {
|
|
|
|
|
return errors.New("No voter account"), ErrNoVoterAccount
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if approval process, check if the account is a voter account
|
|
|
|
|
if action == ApproveNode || action == ApproveNodeDeactivation || action == ApproveNodeActivation || action == ApproveNodeBlacklisting || action == CancelPendingOperation {
|
|
|
|
|
if !checkIsVoter(ps, from) {
|
|
|
|
|
return errors.New("Not a voter account"), ErrAccountNotAVoter
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, ExecSuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// executePermAction helps to execute an action in permission contract
|
|
|
|
|
func (s *QuorumControlsAPI) executePermAction(action PermAction, args txArgs) ExecStatus {
|
|
|
|
|
|
|
|
|
|
if !s.permEnabled {
|
|
|
|
|
return ErrPermissionDisabled
|
|
|
|
|
}
|
|
|
|
|
log.Info("AJ-exec perm action", "action", action, "txargs", args)
|
|
|
|
|
var err error
|
|
|
|
|
var w accounts.Wallet
|
|
|
|
|
|
|
|
|
@ -609,133 +364,44 @@ func (s *QuorumControlsAPI) executePermAction(action PermAction, args txArgs) Ex
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ErrInvalidAccount
|
|
|
|
|
}
|
|
|
|
|
ps := s.newPermSession(w, args.txa)
|
|
|
|
|
var tx *types.Transaction
|
|
|
|
|
var node *enode.Node
|
|
|
|
|
var execStatus ExecStatus
|
|
|
|
|
|
|
|
|
|
if action != SetAccountAccess && action != AddVoter && action != RemoveVoter {
|
|
|
|
|
err, execStatus = s.validateOpDetails(ps, args.nodeId, args.txa.From, action)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return execStatus
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pinterf := s.newPermInterfaceSession(w, args.txa)
|
|
|
|
|
var tx *types.Transaction
|
|
|
|
|
|
|
|
|
|
switch action {
|
|
|
|
|
case AddVoter:
|
|
|
|
|
if locErr, execStatus := valAccountAccessVoter(args.txa.From, args.voter); locErr != nil {
|
|
|
|
|
return execStatus
|
|
|
|
|
}
|
|
|
|
|
if checkIsVoter(ps, args.voter) {
|
|
|
|
|
return ErrVoterExists
|
|
|
|
|
}
|
|
|
|
|
tx, err = ps.AddVoter(args.voter)
|
|
|
|
|
|
|
|
|
|
case RemoveVoter:
|
|
|
|
|
if locErr, execStatus := valAccountAccessVoter(args.txa.From, common.Address{}); locErr != nil {
|
|
|
|
|
return execStatus
|
|
|
|
|
}
|
|
|
|
|
if !checkIsVoter(ps, args.voter) {
|
|
|
|
|
return ErrAccountIsNotVoter
|
|
|
|
|
}
|
|
|
|
|
tx, err = ps.RemoveVoter(args.voter)
|
|
|
|
|
case AddOrg:
|
|
|
|
|
tx, err = pinterf.AddOrg(args.orgId, args.url)
|
|
|
|
|
|
|
|
|
|
case ProposeNode:
|
|
|
|
|
enodeID, discPort, raftPort, ipAddrPort, locerr := getNodeDetailsFromEnode(args.nodeId)
|
|
|
|
|
if locerr != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
case ApproveOrg:
|
|
|
|
|
tx, err = pinterf.ApproveOrg(args.orgId, args.url)
|
|
|
|
|
|
|
|
|
|
tx, err = ps.ProposeNode(enodeID, ipAddrPort, discPort, raftPort)
|
|
|
|
|
case UpdateOrgStatus:
|
|
|
|
|
tx, err = pinterf.UpdateOrgStatus(args.orgId, big.NewInt(int64(args.status)))
|
|
|
|
|
|
|
|
|
|
case ApproveNode:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
case ApproveOrgStatus:
|
|
|
|
|
tx, err = pinterf.ApproveOrgStatus(args.orgId, big.NewInt(int64(args.status)))
|
|
|
|
|
|
|
|
|
|
tx, err = ps.ApproveNode(enodeID)
|
|
|
|
|
case AddNode:
|
|
|
|
|
tx, err = pinterf.AddNode(args.orgId, args.url)
|
|
|
|
|
|
|
|
|
|
case ProposeNodeDeactivation:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
tx, err = ps.ProposeDeactivation(enodeID)
|
|
|
|
|
case UpdateNodeStatus:
|
|
|
|
|
tx, err = pinterf.UpdateNodeStatus(args.orgId, args.url, big.NewInt(int64(args.status)))
|
|
|
|
|
|
|
|
|
|
case ApproveNodeDeactivation:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
case AssignOrgAdminAccount:
|
|
|
|
|
tx, err = pinterf.AssignOrgAdminAccount(args.orgId, args.acctId)
|
|
|
|
|
|
|
|
|
|
tx, err = ps.DeactivateNode(enodeID)
|
|
|
|
|
case ApproveOrgAdminAccount:
|
|
|
|
|
tx, err = pinterf.ApproveOrgAdminAccount(args.acctId)
|
|
|
|
|
|
|
|
|
|
case ProposeNodeActivation:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
tx, err = ps.ProposeNodeActivation(enodeID)
|
|
|
|
|
case AddNewRole:
|
|
|
|
|
tx, err = pinterf.AddNewRole(args.roleId, args.orgId, big.NewInt(int64(args.accessType)), args.isVoter)
|
|
|
|
|
|
|
|
|
|
case ApproveNodeActivation:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
|
|
|
|
|
tx, err = ps.ActivateNode(enodeID)
|
|
|
|
|
|
|
|
|
|
case ProposeNodeBlacklisting:
|
|
|
|
|
enodeID, discPort, raftPort, ipAddrPort, locerr := getNodeDetailsFromEnode(args.nodeId)
|
|
|
|
|
if locerr != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
tx, err = ps.ProposeNodeBlacklisting(enodeID, ipAddrPort, discPort, raftPort)
|
|
|
|
|
case ApproveNodeBlacklisting:
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
|
|
|
|
|
tx, err = ps.BlacklistNode(enodeID)
|
|
|
|
|
|
|
|
|
|
case SetAccountAccess:
|
|
|
|
|
if args.accessType > 3 {
|
|
|
|
|
return ErrInvalidAccountAccess
|
|
|
|
|
}
|
|
|
|
|
if locErr, execStatus := validateAccoutOp(ps, args.txa.From, args.acctId, args.accessType); locErr != nil {
|
|
|
|
|
return execStatus
|
|
|
|
|
}
|
|
|
|
|
tx, err = ps.UpdateAccountAccess(args.acctId, args.accessType)
|
|
|
|
|
|
|
|
|
|
case CancelPendingOperation:
|
|
|
|
|
if !checkIsVoter(ps, args.txa.From) {
|
|
|
|
|
return ErrAccountNotAVoter
|
|
|
|
|
}
|
|
|
|
|
node, err = enode.ParseV4(args.nodeId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Error("invalid node id: %v", err)
|
|
|
|
|
return ErrInvalidNode
|
|
|
|
|
}
|
|
|
|
|
enodeID := node.EnodeID()
|
|
|
|
|
|
|
|
|
|
tx, err = ps.CancelPendingOperation(enodeID)
|
|
|
|
|
case RemoveRole:
|
|
|
|
|
tx, err = pinterf.RemoveRole(args.roleId, args.orgId)
|
|
|
|
|
|
|
|
|
|
case AssignAccountRole:
|
|
|
|
|
tx, err = pinterf.AssignAccountRole(args.acctId, args.orgId, args.roleId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
@ -746,34 +412,6 @@ func (s *QuorumControlsAPI) executePermAction(action PermAction, args txArgs) Ex
|
|
|
|
|
return ExecSuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns the voter list for a given organization
|
|
|
|
|
func (s *QuorumControlsAPI) GetOrgVoterList(morgId string) []string {
|
|
|
|
|
if !s.orgEnabled {
|
|
|
|
|
voterArr := make([]string, 1)
|
|
|
|
|
voterArr[0] = "Org management control not enabled for the network"
|
|
|
|
|
return voterArr
|
|
|
|
|
}
|
|
|
|
|
ps := s.newOrgKeySessionWithNodeKeySigner()
|
|
|
|
|
// get the total number of accounts with permissions
|
|
|
|
|
voterCnt, err := ps.GetNumberOfVoters(morgId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
voterCntI := voterCnt.Int64()
|
|
|
|
|
log.Debug("total voters", "count", voterCntI)
|
|
|
|
|
var voterArr []string
|
|
|
|
|
// loop for each index and get the node details from the contract
|
|
|
|
|
i := int64(0)
|
|
|
|
|
for i < voterCntI {
|
|
|
|
|
voter, err := ps.GetVoter(morgId, big.NewInt(i))
|
|
|
|
|
if err == nil && voter.Active {
|
|
|
|
|
voterArr = append(voterArr, voter.Addr.String())
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return voterArr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns the master org, org and linked key details
|
|
|
|
|
func (s *QuorumControlsAPI) OrgKeyInfo() []orgInfo {
|
|
|
|
|
if !s.orgEnabled {
|
|
|
|
@ -823,27 +461,6 @@ func (s *QuorumControlsAPI) OrgKeyInfo() []orgInfo {
|
|
|
|
|
return orgArr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this function returns the approval pending action at sub org level
|
|
|
|
|
func (s *QuorumControlsAPI) GetPendingOpDetails(orgId string) PendingOpInfo {
|
|
|
|
|
if !s.orgEnabled {
|
|
|
|
|
return PendingOpInfo{"Org key management not enabled for the network", "None"}
|
|
|
|
|
}
|
|
|
|
|
ps := s.newOrgKeySessionWithNodeKeySigner()
|
|
|
|
|
ret, _ := ps.CheckOrgExists(orgId)
|
|
|
|
|
if ret {
|
|
|
|
|
// get the total number of accounts with permissions
|
|
|
|
|
pendingKey, pendingOp, err := ps.GetOrgPendingOp(orgId)
|
|
|
|
|
if err == nil {
|
|
|
|
|
pendOpInfo := PendingOpInfo{pendingKey, decodePendingOp(pendingOp)}
|
|
|
|
|
return pendOpInfo
|
|
|
|
|
} else {
|
|
|
|
|
return PendingOpInfo{"Info not found", "None"}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return PendingOpInfo{"Org not found", "None"}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// executeOrgKeyAction helps to execute an action in cluster contract
|
|
|
|
|
func (s *QuorumControlsAPI) executeOrgKeyAction(action OrgKeyAction, args txArgs) ExecStatus {
|
|
|
|
|
if !s.orgEnabled {
|
|
|
|
@ -981,25 +598,10 @@ func (s *QuorumControlsAPI) validateAccount(from common.Address) (accounts.Walle
|
|
|
|
|
return w, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checkVoterExists checks if any vote accounts are there. If yes returns true, else false
|
|
|
|
|
func checkVoterExists(ps *pbind.PermissionsSession) bool {
|
|
|
|
|
tx, err := ps.GetNumberOfValidVoters()
|
|
|
|
|
log.Debug("number of voters", "count", tx)
|
|
|
|
|
|
|
|
|
|
return (err == nil && tx.Cmp(big.NewInt(0)) > 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks if any accounts is a valid voter to approve the action
|
|
|
|
|
func checkIsVoter(ps *pbind.PermissionsSession, acctId common.Address) bool {
|
|
|
|
|
tx, err := ps.IsVoter(acctId)
|
|
|
|
|
return (err == nil && tx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// newPermSession creates a new permission contract session
|
|
|
|
|
func (s *QuorumControlsAPI) newPermSession(w accounts.Wallet, txa ethapi.SendTxArgs) *pbind.PermissionsSession {
|
|
|
|
|
func (s *QuorumControlsAPI) newPermInterfaceSession(w accounts.Wallet, txa ethapi.SendTxArgs) *pbind.PermInterfaceSession {
|
|
|
|
|
frmAcct, transactOpts, gasLimit, gasPrice, nonce := s.getTxParams(txa, w)
|
|
|
|
|
ps := &pbind.PermissionsSession{
|
|
|
|
|
Contract: s.permContr,
|
|
|
|
|
ps := &pbind.PermInterfaceSession{
|
|
|
|
|
Contract: s.permInterf,
|
|
|
|
|
CallOpts: bind.CallOpts{
|
|
|
|
|
Pending: true,
|
|
|
|
|
},
|
|
|
|
@ -1054,43 +656,6 @@ func (s *QuorumControlsAPI) getTxParams(txa ethapi.SendTxArgs, w accounts.Wallet
|
|
|
|
|
return frmAcct, transactOpts, gasLimit, gasPrice, nonce
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks if the account performing the operation has sufficient access privileges
|
|
|
|
|
// and if at least on full access account will be left after the operation
|
|
|
|
|
func validateAccoutOp(ps *pbind.PermissionsSession, from, targetAcct common.Address, accessType uint8) (error, ExecStatus) {
|
|
|
|
|
fromAcctAccess := types.GetAcctAccess(from)
|
|
|
|
|
targetAcctAccess := types.GetAcctAccess(targetAcct)
|
|
|
|
|
|
|
|
|
|
retVal := false
|
|
|
|
|
|
|
|
|
|
// Check if the from account is sufficient access rights to set the access
|
|
|
|
|
if fromAcctAccess == types.FullAccess {
|
|
|
|
|
retVal = true
|
|
|
|
|
} else if fromAcctAccess == types.ContractDeploy && accessType != uint8(types.FullAccess) {
|
|
|
|
|
retVal = true
|
|
|
|
|
} else if fromAcctAccess == types.Transact && (accessType == uint8(types.Transact) || accessType == uint8(types.ReadOnly)) {
|
|
|
|
|
retVal = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if retVal && fromAcctAccess != types.FullAccess {
|
|
|
|
|
if (fromAcctAccess == types.ContractDeploy && targetAcctAccess == types.FullAccess) ||
|
|
|
|
|
(fromAcctAccess == types.Transact &&
|
|
|
|
|
(targetAcctAccess == types.ContractDeploy || targetAcctAccess == types.FullAccess)) {
|
|
|
|
|
retVal = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if !retVal {
|
|
|
|
|
return errors.New("Account does not have sufficient access"), ErrAccountAccess
|
|
|
|
|
}
|
|
|
|
|
if targetAcctAccess == types.FullAccess && accessType != uint8(types.FullAccess) {
|
|
|
|
|
numFullAccessAcct, err := ps.GetFullAccessAccountCount()
|
|
|
|
|
if err == nil && numFullAccessAcct.Cmp(big.NewInt(1)) == 0 {
|
|
|
|
|
return errors.New("Last account with full access. Operation not allowed"), ErrLastFullAccessAcct
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil, ExecSuccess
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// checks if the account performing the operation has sufficient access privileges
|
|
|
|
|
func valAccountAccessVoter(fromAcct, targetAcct common.Address) (error, ExecStatus) {
|
|
|
|
|
acctAccess := types.GetAcctAccess(fromAcct)
|
|
|
|
|