2018-07-12 03:00:19 -07:00
package permissions
import (
2018-07-16 03:34:20 -07:00
"encoding/json"
2018-07-12 03:00:19 -07:00
"io/ioutil"
"path/filepath"
2018-07-16 03:34:20 -07:00
"os"
2018-08-12 23:55:58 -07:00
"sync"
2018-07-12 03:00:19 -07:00
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/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
2018-09-19 18:51:36 -07:00
"github.com/ethereum/go-ethereum/p2p/discover"
2018-09-28 01:15:59 -07:00
"github.com/ethereum/go-ethereum/controls"
2018-09-18 23:55:41 -07:00
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/raft"
2018-07-12 03:00:19 -07:00
"gopkg.in/urfave/cli.v1"
)
2018-07-16 03:34:20 -07:00
const (
PERMISSIONED_CONFIG = "permissioned-nodes.json"
2018-08-31 04:35:35 -07:00
BLACKLIST_CONFIG = "disallowed-nodes.json"
2018-09-18 23:55:41 -07:00
RAFT = "raft"
ISTANBUL = "istanbul"
2018-07-16 03:34:20 -07:00
)
2018-07-13 18:22:43 -07:00
2018-08-12 19:00:35 -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 {
// Create a new ethclient to for interfacing with the contract
2018-09-28 01:15:59 -07:00
stateReader , err := controls . CreateEthClient ( stack )
2018-08-05 22:26:29 -07:00
if err != nil {
2018-08-12 19:00:35 -07:00
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
2018-08-12 19:00:35 -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
}
2018-09-18 23:55:41 -07:00
consensusEngine := ISTANBUL
if ctx . GlobalBool ( utils . RaftModeFlag . Name ) {
consensusEngine = RAFT
}
2018-08-12 19:00:35 -07:00
// Monitors node addition and decativation from network
2018-09-18 23:55:41 -07:00
manageNodePermissions ( stack , stateReader , consensusEngine ) ;
2018-08-12 19:00:35 -07:00
// Monitors account level persmissions update from smart contarct
manageAccountPermissions ( stack , stateReader ) ;
return nil
}
2018-07-13 18:22:43 -07:00
2018-08-12 19:00:35 -07:00
// Manages node addition and decavtivation from network
2018-09-18 23:55:41 -07:00
func manageNodePermissions ( stack * node . Node , stateReader * ethclient . Client , consensusEngine string ) {
2018-07-13 18:22:43 -07:00
//monitor for new nodes addition via smart contract
2018-09-19 22:55:57 -07:00
go monitorNewNodeAdd ( stack , stateReader , consensusEngine )
2018-07-26 00:46:53 -07:00
//monitor for nodes deletiin via smart contract
2018-09-19 22:55:57 -07:00
go monitorNodeDeactivation ( stack , stateReader , consensusEngine )
2018-08-31 04:35:35 -07:00
//monitor for nodes blacklisting via smart contract
2018-09-18 23:55:41 -07:00
go monitorNodeBlacklisting ( stack , stateReader , consensusEngine )
2018-07-13 18:22:43 -07:00
}
2018-08-12 19:00:35 -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
2018-09-19 22:55:57 -07:00
func monitorNewNodeAdd ( stack * node . Node , stateReader * ethclient . Client , consensusEngine string ) {
2018-07-13 18:22:43 -07:00
permissions , err := NewPermissionsFilterer ( params . QuorumPermissionsContract , stateReader )
if err != nil {
2018-08-12 19:00:35 -07:00
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
2018-08-12 23:55:58 -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
2018-08-12 23:55:58 -07:00
var nodeAddEvent * PermissionsNodeApproved
2018-07-13 18:22:43 -07:00
2018-08-16 20:20:33 -07:00
_ , err = permissions . WatchNodeApproved ( opts , ch )
if err != nil {
log . Info ( "Failed WatchNodeApproved: %v" , err )
}
2018-08-12 19:00:35 -07:00
2018-08-16 20:20:33 -07:00
for {
2018-08-12 23:55:58 -07:00
select {
case nodeAddEvent = <- ch :
2018-09-19 22:55:57 -07:00
updatePermissionedNodes ( nodeAddEvent . EnodeId , nodeAddEvent . IpAddrPort , nodeAddEvent . DiscPort , nodeAddEvent . RaftPort , datadir , consensusEngine , NodeAdd )
2018-08-12 23:55:58 -07:00
}
2018-07-13 18:22:43 -07:00
}
}
2018-08-12 19:00:35 -07:00
// This functions listens on the channel for new node approval via smart contract and
2018-07-26 00:46:53 -07:00
// adds the same into permissioned-nodes.json
2018-09-19 22:55:57 -07:00
func monitorNodeDeactivation ( stack * node . Node , stateReader * ethclient . Client , consensusEngine string ) {
2018-07-26 00:46:53 -07:00
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 )
2018-07-26 00:46:53 -07:00
}
datadir := stack . DataDir ( )
ch := make ( chan * PermissionsNodeDeactivated )
opts := & bind . WatchOpts { }
var blockNumber uint64 = 1
opts . Start = & blockNumber
2018-08-16 20:20:33 -07:00
var newNodeDeleteEvent * PermissionsNodeDeactivated
2018-07-26 00:46:53 -07:00
2018-08-16 20:20:33 -07:00
_ , err = permissions . WatchNodeDeactivated ( opts , ch )
if err != nil {
log . Info ( "Failed NodeDeactivated: %v" , err )
}
2018-08-12 23:55:58 -07:00
2018-08-16 20:20:33 -07:00
for {
select {
case newNodeDeleteEvent = <- ch :
2018-09-19 22:55:57 -07:00
updatePermissionedNodes ( newNodeDeleteEvent . EnodeId , newNodeDeleteEvent . IpAddrPort , newNodeDeleteEvent . DiscPort , newNodeDeleteEvent . RaftPort , datadir , consensusEngine , NodeDelete )
2018-08-16 20:20:33 -07:00
}
2018-07-26 00:46:53 -07:00
2018-08-16 20:20:33 -07:00
}
2018-07-26 00:46:53 -07:00
}
2018-08-05 22:26:29 -07:00
2018-08-31 04:35:35 -07:00
// This function listnes on the channel for any node blacklisting event via smart contract
// and adds the same disallowed-nodes.json
2018-09-18 23:55:41 -07:00
func monitorNodeBlacklisting ( stack * node . Node , stateReader * ethclient . Client , consensusEngine string ) {
2018-08-31 04:35:35 -07:00
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 :
2018-09-18 23:55:41 -07:00
updateDisallowedNodes ( nodeBlacklistEvent , stack , consensusEngine )
2018-08-31 04:35:35 -07:00
}
}
}
2018-07-16 03:34:20 -07:00
//this function populates the new node information into the permissioned-nodes.json file
2018-09-19 22:55:57 -07:00
func updatePermissionedNodes ( enodeId , ipAddrPort , discPort , raftPort , dataDir , consensusEngine string , operation NodeOperation ) {
2018-07-26 00:46:53 -07:00
log . Debug ( "updatePermissionedNodes" , "DataDir" , dataDir , "file" , PERMISSIONED_CONFIG )
2018-07-16 03:34:20 -07:00
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 {
2018-07-26 00:46:53 -07:00
log . Error ( "updatePermissionedNodes: Failed to access permissioned-nodes.json" , "err" , err )
2018-07-16 03:34:20 -07:00
return
}
nodelist := [ ] string { }
if err := json . Unmarshal ( blob , & nodelist ) ; err != nil {
2018-08-31 04:35:35 -07:00
log . Error ( "updatePermissionedNodes: Failed to load nodes list" , "err" , err )
2018-07-16 03:34:20 -07:00
return
}
2018-09-19 22:55:57 -07:00
newEnodeId := formatEnodeId ( enodeId , ipAddrPort , discPort , raftPort , consensusEngine )
2018-08-12 23:55:58 -07:00
2018-08-12 19:00:35 -07:00
if ( operation == NodeAdd ) {
2018-08-16 20:20:33 -07:00
nodelist = append ( nodelist , newEnodeId )
2018-08-12 23:55:58 -07:00
} else {
2018-08-16 20:20:33 -07:00
index := 0
for i , enodeId := range nodelist {
if ( enodeId == newEnodeId ) {
index = i
break
}
}
2018-07-26 00:46:53 -07:00
nodelist = append ( nodelist [ : index ] , nodelist [ index + 1 : ] ... )
}
2018-07-16 03:34:20 -07:00
2018-08-12 23:55:58 -07:00
mu := sync . RWMutex { }
2018-07-16 03:34:20 -07:00
blob , _ = json . Marshal ( nodelist )
2018-08-12 19:00:35 -07:00
2018-08-12 23:55:58 -07:00
mu . Lock ( )
2018-07-16 03:34:20 -07:00
if err := ioutil . WriteFile ( path , blob , 0644 ) ; err != nil {
2018-07-26 00:46:53 -07:00
log . Error ( "updatePermissionedNodes: Error writing new node info to file" , "err" , err )
2018-07-16 03:34:20 -07:00
}
2018-08-12 23:55:58 -07:00
mu . Unlock ( )
2018-08-12 19:00:35 -07:00
}
2018-08-31 04:35:35 -07:00
//this function populates the new node information into the permissioned-nodes.json file
2018-09-18 23:55:41 -07:00
func updateDisallowedNodes ( nodeBlacklistEvent * PermissionsNodeBlacklisted , stack * node . Node , consensusEngine string ) {
2018-09-17 22:52:46 -07:00
dataDir := stack . DataDir ( )
2018-08-31 04:35:35 -07:00
log . Debug ( "updateDisallowedNodes" , "DataDir" , dataDir , "file" , BLACKLIST_CONFIG )
2018-09-02 23:59:45 -07:00
fileExisted := true
2018-08-31 04:35:35 -07:00
path := filepath . Join ( dataDir , BLACKLIST_CONFIG )
2018-09-02 23:59:45 -07:00
// Check if the file is existing. If the file is not existing create the file
2018-08-31 04:35:35 -07:00
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
}
2018-09-02 23:59:45 -07:00
fileExisted = false
2018-08-31 04:35:35 -07:00
}
nodelist := [ ] string { }
2018-09-02 23:59:45 -07:00
// 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
}
2018-08-31 04:35:35 -07:00
}
}
2018-09-19 22:55:57 -07:00
newEnodeId := formatEnodeId ( nodeBlacklistEvent . EnodeId , nodeBlacklistEvent . IpAddrPort , nodeBlacklistEvent . DiscPort , nodeBlacklistEvent . RaftPort , consensusEngine )
2018-08-31 04:35:35 -07:00
nodelist = append ( nodelist , newEnodeId )
mu := sync . RWMutex { }
2018-09-02 23:59:45 -07:00
blob , _ := json . Marshal ( nodelist )
2018-08-31 04:35:35 -07:00
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 ( )
2018-09-17 22:52:46 -07:00
// Disconnect the peer if it is already connected
2018-09-19 18:51:36 -07:00
disconnectNode ( stack , newEnodeId , consensusEngine )
2018-08-31 04:35:35 -07:00
}
2018-08-12 19:00:35 -07:00
// 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 {
2018-09-28 01:15:59 -07:00
return err
2018-08-12 19:00:35 -07:00
}
//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 { }
2018-09-05 19:40:21 -07:00
pastEvents , err := permissions . FilterAccountAccessModified ( opts )
2018-08-12 19:00:35 -07:00
recExists := true
for recExists {
recExists = pastEvents . Next ( )
if recExists {
2018-09-05 19:40:21 -07:00
types . AddAccountAccess ( pastEvents . Event . Address , pastEvents . Event . Access )
2018-08-12 19:00:35 -07:00
}
}
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 )
}
2018-09-05 19:40:21 -07:00
ch := make ( chan * PermissionsAccountAccessModified )
2018-08-12 19:00:35 -07:00
opts := & bind . WatchOpts { }
var blockNumber uint64 = 1
opts . Start = & blockNumber
2018-09-05 19:40:21 -07:00
var newEvent * PermissionsAccountAccessModified
2018-08-12 19:00:35 -07:00
2018-09-05 19:40:21 -07:00
_ , err = permissions . WatchAccountAccessModified ( opts , ch )
2018-08-16 20:20:33 -07:00
if err != nil {
log . Info ( "Failed NewNodeProposed: %v" , err )
}
2018-08-12 19:00:35 -07:00
for {
2018-08-16 20:20:33 -07:00
select {
case newEvent = <- ch :
2018-09-05 19:40:21 -07:00
types . AddAccountAccess ( newEvent . Address , newEvent . Access )
2018-08-12 19:00:35 -07:00
}
}
2018-07-16 03:34:20 -07:00
}
2018-09-19 18:51:36 -07:00
// 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 )
}
}
}
}
2018-09-19 22:55:57 -07:00
// helper function to format EnodeId
func formatEnodeId ( enodeId , ipAddrPort , discPort , raftPort , consensusEngine string ) string {
newEnodeId := "enode://" + enodeId + "@" + ipAddrPort + "?discPort=" + discPort
if consensusEngine == RAFT {
2018-10-23 07:58:39 -07:00
newEnodeId += "&raftport=" + raftPort
2018-09-19 22:55:57 -07:00
}
return newEnodeId
}