diff --git a/controls/backend/api.go b/controls/backend/api.go index 05af5e77b..31b697822 100644 --- a/controls/backend/api.go +++ b/controls/backend/api.go @@ -3,34 +3,122 @@ package backend import ( "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/log" - ) + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "strings" + "math/big" + "github.com/ethereum/go-ethereum/p2p/discover" + "fmt" + "io/ioutil" + "path/filepath" + "github.com/ethereum/go-ethereum/controls/permbind" +) -type PermissionAPI struct { -} -func NewPermissionAPI() *PermissionAPI { - return &PermissionAPI{} -} - -func APIs() []rpc.API { +func APIs(ec *ethclient.Client, e *eth.Ethereum, datadir string) []rpc.API { return []rpc.API{ { Namespace: "permnode", Version: "1.0", - Service: NewPermissionAPI(), + Service: NewPermissionAPI(ec, e, datadir), Public: true, }, } } +type PermissionAPI struct { + ethClient *ethclient.Client + eth *eth.Ethereum + permissionsContr *permbind.Permissions + transOpts *bind.TransactOpts + +} + + + + +func getKeyFromKeyStore(datadir string) string { + + files, err := ioutil.ReadDir(filepath.Join(datadir, "keystore")) + if err != nil { + utils.Fatalf("Failed to read keystore directory: %v", err) + } + + // HACK: here we always use the first key as transactor + var keyPath string + for _, f := range files { + keyPath = filepath.Join(datadir, "keystore", f.Name()) + break + } + keyBlob, err := ioutil.ReadFile(keyPath) + if err != nil { + utils.Fatalf("Failed to read key file: %v", err) + } + // n := bytes.IndexByte(keyBlob, 0) + n := len(keyBlob) + + return string(keyBlob[:n]) +} + +func NewPermissionAPI(ec *ethclient.Client, e *eth.Ethereum, datadir string) *PermissionAPI { + permissionsContract, err := permbind.NewPermissions(params.QuorumPermissionsContract, ec) + if err != nil { + utils.Fatalf("Failed to instantiate a Permissions contract: %v", err) + } + //TODO check if reading from keystore is correct approach + key := getKeyFromKeyStore(datadir) + auth, err := bind.NewTransactor(strings.NewReader(key), "") + if err != nil { + utils.Fatalf("Failed to create authorized transactor: %v", err) + } + return &PermissionAPI{ec, e, permissionsContract, auth} +} + func (s *PermissionAPI) AddVoter(addr string) string { log.Info("AJ-called1") return "added voter " + addr } func (s *PermissionAPI) ProposeNode(enodeId string) string { - log.Info("AJ-called2") - return "proposed node " + enodeId + node, err := discover.ParseNode(enodeId) + if err != nil { + return fmt.Sprintf("invalid node id: %v", err) + } + enodeID := node.ID.String() + 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 + + log.Trace("AJ-Adding node to permissions contract", "enodeID", enodeID) + + nonce := s.eth.TxPool().Nonce(s.transOpts.From) + s.transOpts.Nonce = new(big.Int).SetUint64(nonce) + + permissionsSession := &permbind.PermissionsSession{ + Contract: s.permissionsContr, + CallOpts: bind.CallOpts{ + Pending: true, + }, + TransactOpts: bind.TransactOpts{ + From: s.transOpts.From, + Signer: s.transOpts.Signer, + GasLimit: 4700000, + GasPrice: big.NewInt(0), + }, + } + + tx, err := permissionsSession.ProposeNode(enodeID, ipAddrPort, discPort, raftPort) + if err != nil { + log.Warn("AJ-Failed to propose node", "err", err) + } + statusMsg := fmt.Sprintf("Transaction pending tx hash %s", tx.Hash()) + log.Debug(statusMsg) + return statusMsg } func (s *PermissionAPI) BlacklistNode(enodeId string) string { diff --git a/controls/permissions/permissions_binding.go b/controls/permbind/permissions_binding.go similarity index 99% rename from controls/permissions/permissions_binding.go rename to controls/permbind/permissions_binding.go index 7347ccd21..c96c93078 100644 --- a/controls/permissions/permissions_binding.go +++ b/controls/permbind/permissions_binding.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package permissions +package permbind import ( "math/big" diff --git a/controls/permissions/permissions.go b/controls/permissions/permissions.go index 7dd579c74..2861c953a 100644 --- a/controls/permissions/permissions.go +++ b/controls/permissions/permissions.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/raft" "gopkg.in/urfave/cli.v1" + "github.com/ethereum/go-ethereum/controls/permbind" ) const ( PERMISSIONED_CONFIG = "permissioned-nodes.json" @@ -50,7 +51,7 @@ func QuorumPermissioning(ctx *cli.Context, stack *node.Node ) error { } // check if permissioning contract is there at address. If not return from here - if _ , err = NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader); err != nil { + if _ , err = permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader); err != nil { log.Error ("Permissions not enabled for the network : ", "err" , err) return nil } @@ -110,17 +111,17 @@ func manageNodePermissions(ctx *cli.Context, stack *node.Node, e *eth.Ethereum, // 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, isRaft bool) { - permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("failed to monitor new node add : ", "err" , err) } - ch := make(chan *PermissionsNodeApproved, 1) + ch := make(chan *permbind.PermissionsNodeApproved, 1) opts := &bind.WatchOpts{} var blockNumber uint64 = 1 opts.Start = &blockNumber - var nodeAddEvent *PermissionsNodeApproved + var nodeAddEvent *permbind.PermissionsNodeApproved _, err = permissions.WatchNodeApproved(opts, ch) if err != nil { @@ -138,17 +139,17 @@ func monitorNewNodeAdd(stack *node.Node, stateReader *ethclient.Client, isRaft b // 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, isRaft bool) { - permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("Failed to monitor node delete: ", "err" , err) } - ch := make(chan *PermissionsNodeDeactivated) + ch := make(chan *permbind.PermissionsNodeDeactivated) opts := &bind.WatchOpts{} var blockNumber uint64 = 1 opts.Start = &blockNumber - var newNodeDeleteEvent *PermissionsNodeDeactivated + var newNodeDeleteEvent *permbind.PermissionsNodeDeactivated _, err = permissions.WatchNodeDeactivated(opts, ch) if err != nil { @@ -167,16 +168,16 @@ func monitorNodeDeactivation(stack *node.Node, stateReader *ethclient.Client, is // 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, isRaft bool) { - permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("failed to monitor new node add : ", "err" , err) } - ch := make(chan *PermissionsNodeBlacklisted, 1) + ch := make(chan *permbind.PermissionsNodeBlacklisted, 1) opts := &bind.WatchOpts{} var blockNumber uint64 = 1 opts.Start = &blockNumber - var nodeBlacklistEvent *PermissionsNodeBlacklisted + var nodeBlacklistEvent *permbind.PermissionsNodeBlacklisted _, err = permissions.WatchNodeBlacklisted(opts, ch) if err != nil { @@ -220,7 +221,7 @@ func updatePermissionedNodes(stack *node.Node, enodeId , ipAddrPort, discPort, r } //this function populates the new node information into the permissioned-nodes.json file -func updateDisallowedNodes(nodeBlacklistEvent *PermissionsNodeBlacklisted, stack *node.Node, isRaft bool){ +func updateDisallowedNodes(nodeBlacklistEvent *permbind.PermissionsNodeBlacklisted, stack *node.Node, isRaft bool){ dataDir := stack.DataDir() log.Debug("updateDisallowedNodes", "DataDir", dataDir, "file", BLACKLIST_CONFIG) @@ -275,7 +276,7 @@ func manageAccountPermissions(stack *node.Node, stateReader *ethclient.Client) e // populates the nodes list from permissioned-nodes.json into the permissions // smart contract func populatePermissionedNodes (stack *node.Node, stateReader *ethclient.Client, isRaft bool) error{ - permissions, err := NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("Failed to monitor node delete: ", "err" , err) return err @@ -308,7 +309,7 @@ func populatePermissionedNodes (stack *node.Node, stateReader *ethclient.Client, // 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) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("Failed to monitor node delete: ", "err" , err) return err @@ -331,16 +332,16 @@ func populateAcctPermissions(stack *node.Node, stateReader *ethclient.Client) er // 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) + permissions, err := permbind.NewPermissionsFilterer(params.QuorumPermissionsContract, stateReader) if err != nil { log.Error ("Failed to monitor Account permissions : ", "err" , err) } - ch := make(chan *PermissionsAccountAccessModified) + ch := make(chan *permbind.PermissionsAccountAccessModified) opts := &bind.WatchOpts{} var blockNumber uint64 = 1 opts.Start = &blockNumber - var newEvent *PermissionsAccountAccessModified + var newEvent *permbind.PermissionsAccountAccessModified _, err = permissions.WatchAccountAccessModified(opts, ch) if err != nil { @@ -392,11 +393,10 @@ func formatEnodeId( enodeId , ipAddrPort, discPort, raftPort string, isRaft bool //populates the nodes list from permissioned-nodes.json into the permissions //smart contract func populateStaticNodesToContract(ctx *cli.Context, stack *node.Node, e *eth.Ethereum, stateReader *ethclient.Client){ - //Read the key file from key store. SHOULD WE MAKE IT CONFIG value key := getKeyFromKeyStore(ctx) - permissionsContract, err := NewPermissions(params.QuorumPermissionsContract, stateReader) + permissionsContract, err := permbind.NewPermissions(params.QuorumPermissionsContract, stateReader) if err != nil { utils.Fatalf("Failed to instantiate a Permissions contract: %v", err) @@ -406,7 +406,7 @@ func populateStaticNodesToContract(ctx *cli.Context, stack *node.Node, e *eth.Et utils.Fatalf("Failed to create authorized transactor: %v", err) } - permissionsSession := &PermissionsSession{ + permissionsSession := &permbind.PermissionsSession{ Contract: permissionsContract, CallOpts: bind.CallOpts{ Pending: true, diff --git a/node/node.go b/node/node.go index 2b67c1c32..bed42d33e 100644 --- a/node/node.go +++ b/node/node.go @@ -35,6 +35,12 @@ import ( "github.com/ethereum/go-ethereum/rpc" "github.com/prometheus/prometheus/util/flock" "github.com/ethereum/go-ethereum/controls/backend" +/* "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/cmd/utils"*/ + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/eth" ) // Node is a container on which services can be registered. @@ -249,6 +255,18 @@ func (n *Node) openDataDir() error { return nil } +func createEthClient(stack *Node) (*ethclient.Client, *eth.Ethereum, error){ + var e *eth.Ethereum + if err := stack.Service(&e); err != nil { + return nil, nil, err + } + rpcClient, err := stack.Attach() + if err != nil { + return nil, nil, err + } + return ethclient.NewClient(rpcClient), e, nil +} + // startRPC is a helper method to start all the various RPC endpoint during node // startup. It's not meant to be called at any time afterwards as it makes certain // assumptions about the state of the node. @@ -256,11 +274,18 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { // Gather all the possible APIs to surface apis := n.apis() for _, service := range services { + //TODO get a ethereum service and pass the config from it to add perm service apis = append(apis, service.APIs()...) } - apis = append(apis, backend.APIs()...) - log.Info("AJ-permissions api added") + if n.config.EnableNodePermission { + ec, e, err := createEthClient(n) + if err != nil { + utils.Fatalf("Error creating eth client: %v", err) + } + apis = append(apis, backend.APIs(ec, e, n.InstanceDir())...) + log.Info("AJ-permissions api added") + } // Start the various API endpoints, terminating all in case of errors if err := n.startInProc(apis); err != nil {