quorum/permissions/node_permissions.go

282 lines
8.5 KiB
Go
Raw Normal View History

2018-07-12 03:00:19 -07:00
package permissions
import (
"encoding/json"
2018-07-12 03:00:19 -07:00
"io/ioutil"
"path/filepath"
"os"
"sync"
2018-07-12 03:00:19 -07:00
// "github.com/ethereum/go-ethereum/common"
2018-08-05 22:26:29 -07:00
"github.com/ethereum/go-ethereum/core/types"
2018-07-12 03:00:19 -07:00
"github.com/ethereum/go-ethereum/accounts/abi/bind"
2018-07-13 18:22:43 -07:00
"github.com/ethereum/go-ethereum/params"
2018-07-12 03:00:19 -07:00
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"gopkg.in/urfave/cli.v1"
)
const (
PERMISSIONED_CONFIG = "permissioned-nodes.json"
)
2018-07-13 18:22:43 -07:00
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
2018-08-05 22:26:29 -07:00
func QuorumPermissioning(ctx *cli.Context, stack *node.Node ) error {
2018-08-05 22:26:29 -07:00
// Create a new ethclient to for interfacing with the contract
stateReader, err := createEthClient(stack)
if err != nil {
log.Error ("Unable to create ethereum client for permissions check : ", "err" , err)
2018-08-05 22:26:29 -07:00
return err
}
2018-07-13 18:22:43 -07:00
// 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
}
// Monitors node addition and decativation from network
manageNodePermissions(stack, stateReader);
// Monitors account level persmissions update from smart contarct
manageAccountPermissions(stack, stateReader);
return nil
}
2018-07-13 18:22:43 -07:00
// 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
}
// Manages node addition and decavtivation from network
func manageNodePermissions(stack *node.Node, stateReader *ethclient.Client) {
2018-07-13 18:22:43 -07:00
//monitor for new nodes addition via smart contract
go monitorNewNodeAdd(stack)
//monitor for nodes deletiin via smart contract
go monitorNodeDelete(stack, stateReader)
2018-07-13 18:22:43 -07:00
}
// This functions listens on the channel for new node approval via smart contract and
2018-07-13 18:22:43 -07:00
// adds the same into permissioned-nodes.json
func monitorNewNodeAdd(stack *node.Node) {
stateReader, err := createEthClient(stack)
2018-07-13 18:22:43 -07:00
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
if err != nil {
log.Error ("failed to monitor new node add : ", "err" , err)
2018-07-13 18:22:43 -07:00
}
2018-07-16 19:13:33 -07:00
datadir := stack.DataDir()
2018-07-13 18:22:43 -07:00
ch := make(chan *PermissionsNodeApproved, 1)
2018-07-13 18:22:43 -07:00
opts := &bind.WatchOpts{}
var blockNumber uint64 = 1
opts.Start = &blockNumber
var nodeAddEvent *PermissionsNodeApproved
2018-07-13 18:22:43 -07:00
for {
_, err = permissions.WatchNodeApproved(opts, ch)
2018-07-13 18:22:43 -07:00
if err != nil {
log.Info("Failed NewNodeProposed: %v", err)
}
select {
case nodeAddEvent = <-ch:
log.Info("calling update permissions, length is", "event", nodeAddEvent)
updatePermissionedNodes(nodeAddEvent.EnodeId, nodeAddEvent.IpAddrPort, nodeAddEvent.DiscPort, nodeAddEvent.RaftPort, datadir, NodeAdd)
}
2018-07-13 18:22:43 -07:00
}
}
// This functions listens on the channel for new node approval via smart contract and
// adds the same into permissioned-nodes.json
2018-08-05 22:26:29 -07:00
func monitorNodeDelete(stack *node.Node, stateReader *ethclient.Client) {
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
if err != nil {
2018-08-05 22:26:29 -07:00
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
for {
_, err = permissions.WatchNodeDeactivated(opts, ch)
if err != nil {
log.Info("Failed NodeDeactivated: %v", err)
}
var newNodeDeleteEvent *PermissionsNodeDeactivated = <-ch
updatePermissionedNodes(newNodeDeleteEvent.EnodeId, newNodeDeleteEvent.IpAddrPort, newNodeDeleteEvent.DiscPort, newNodeDeleteEvent.RaftPort, datadir, NodeDelete)
}
}
2018-08-05 22:26:29 -07:00
//this function populates the new node information into the permissioned-nodes.json file
func updatePermissionedNodes(enodeId , ipAddrPort, discPort, raftPort, dataDir 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("parsePermissionedNodes: Failed to load nodes list", "err", err)
return
}
2018-07-16 19:13:33 -07:00
// HACK: currently the ip, discpot and raft port are hard coded. Need to enhance the
//contract to pass these variables as part of the event and change this
// newEnodeId := "enode://" + enodeId + "@127.0.0.1:21005?discport=0&raftport=50406"
newEnodeId := "enode://" + enodeId + "@" + ipAddrPort + "?discPort=" + discPort + "&raftport=" + raftPort
log.Info("Enode id is : " , "newEnodeId", newEnodeId)
index := 0
// HACK: currently seeing the event multiple times on the channel and hence the write to file is
// happening multiple times. To avoid this checking to see if the record exists before write
// Need to be removed once the proper issue is identified
enodeExists := false
for i, enodeId := range nodelist {
if (enodeId == newEnodeId){
index = i
enodeExists = true
break
}
}
if (operation == NodeAdd){
if enodeExists == false {
nodelist = append(nodelist, newEnodeId)
}
} else {
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()
}
// 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.FilterAcctAccessModified(opts)
recExists := true
for recExists {
recExists = pastEvents.Next()
if recExists {
types.AddAccountAccess(pastEvents.Event.AcctId, 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) {
log.Info("Inside monotorAccountPermissions")
permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader)
if err != nil {
log.Error ("Failed to monitor Account permissions : ", "err" , err)
}
ch := make(chan *PermissionsAcctAccessModified)
opts := &bind.WatchOpts{}
var blockNumber uint64 = 1
opts.Start = &blockNumber
// const addr1 = "ed9d02e382b34818e88b88a309c7fe71e65f419d"
// var acctAddr1 = common.HexToAddress(addr1)
// types.AddAccountAccess(acctAddr1, 0)
// const addr2 = "ca843569e3427144cead5e4d5999a3d0ccf92b8e"
// var acctAddr2 = common.HexToAddress(addr2)
// types.AddAccountAccess(acctAddr2, 1)
// const addr3 = "0fbdc686b912d7722dc86510934589e0aaf3b55a"
// var acctAddr3= common.HexToAddress(addr3)
// types.AddAccountAccess(acctAddr3, 2)
// const addr4 = "9186eb3d20cbd1f5f992a950d808c4495153abd5"
// var acctAddr4= common.HexToAddress(addr4)
// types.AddAccountAccess(acctAddr4, 3)
for {
_, err = permissions.WatchAcctAccessModified(opts, ch)
if err != nil {
log.Info("Failed NewNodeProposed: %v", err)
}
var newEvent *PermissionsAcctAccessModified = <-ch
log.Info("caught the event and calling PutAcctMap")
types.AddAccountAccess(newEvent.AcctId, newEvent.Access)
}
}