Merge branch '1812-permission-rpc-api' of https://github.com/vsmk98/quorum into 1812-permission-rpc-api

Merging with Amal's API changes
This commit is contained in:
vsmk98 2018-11-01 10:56:06 +08:00
commit 78c79e59a2
3 changed files with 196 additions and 234 deletions

View File

@ -33,15 +33,15 @@ import (
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/controls/cluster"
"github.com/ethereum/go-ethereum/controls/permission"
"github.com/ethereum/go-ethereum/core/quorum"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/controls/permission"
"github.com/ethereum/go-ethereum/controls/cluster"
"github.com/ethereum/go-ethereum/core/quorum"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -390,6 +390,6 @@ func startQuorumPermissionService(ctx *cli.Context, stack *node.Node) {
utils.Fatalf("Failed to attach to self: %v", err) utils.Fatalf("Failed to attach to self: %v", err)
} }
stateReader := ethclient.NewClient(rpcClient) stateReader := ethclient.NewClient(rpcClient)
qapi.Init(stateReader, stack.InstanceDir()) qapi.Init(stateReader)
log.Info("Permission API initialized") log.Info("Permission API initialized")
} }

View File

@ -1,46 +1,64 @@
package quorum package quorum
import ( import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/ethclient"
"io/ioutil"
"path/filepath"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/p2p/discover"
"fmt" "fmt"
"math/big"
"github.com/ethereum/go-ethereum/log"
pbind "github.com/ethereum/go-ethereum/controls/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"strings" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
pbind "github.com/ethereum/go-ethereum/controls/bind"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
"math/big"
) )
var defaultGasLimit = uint64(4700000) var defaultGasLimit = uint64(470000000)
var defaultGasPrice = big.NewInt(0) var defaultGasPrice = big.NewInt(0)
type PermAction int
const (
ProposeNode PermAction = iota
ApproveNode
DeactivateNode
ApproveDeactivateNode
AddVoter
RemoveVoter
)
type OrgKeyAction int
const (
AddOrgKey OrgKeyAction = iota
RemoveOrgKey
)
type PermissionAPI struct { type PermissionAPI struct {
txPool *core.TxPool txPool *core.TxPool
ethClnt *ethclient.Client ethClnt *ethclient.Client
am *accounts.Manager acntMgr *accounts.Manager
trnOpt *bind.TransactOpts txOpt *bind.TransactOpts
permContr *pbind.Permissions permContr *pbind.Permissions
clustContr *pbind.Cluster clustContr *pbind.Cluster
} }
func NewPermissionAPI(tp *core.TxPool, am *accounts.Manager) *PermissionAPI { type txArgs struct {
pa := &PermissionAPI{tp, nil, am, nil, nil, nil} from common.Address
return pa voter common.Address
nodeId string
orgId string
keyId string
} }
func (p *PermissionAPI) Init(ethClnt *ethclient.Client, datadir string) error { func NewPermissionAPI(tp *core.TxPool, am *accounts.Manager) *PermissionAPI {
return &PermissionAPI{tp, nil, am, nil, nil, nil}
}
func (p *PermissionAPI) Init(ethClnt *ethclient.Client) error {
p.ethClnt = ethClnt p.ethClnt = ethClnt
key, kerr := getKeyFromKeyStore(datadir)
if kerr != nil {
log.Error("error reading key file", "err", kerr)
return kerr
}
permContr, err := pbind.NewPermissions(params.QuorumPermissionsContract, p.ethClnt) permContr, err := pbind.NewPermissions(params.QuorumPermissionsContract, p.ethClnt)
if err != nil { if err != nil {
return err return err
@ -51,226 +69,165 @@ func (p *PermissionAPI) Init(ethClnt *ethclient.Client, datadir string) error {
return err return err
} }
p.clustContr = clustContr p.clustContr = clustContr
auth, err := bind.NewTransactor(strings.NewReader(key), "")
if err != nil {
return err
}
p.trnOpt = auth
return nil return nil
} }
func (s *PermissionAPI) AddVoter(addr common.Address) bool { func (s *PermissionAPI) AddVoter(from common.Address, vaddr common.Address) bool {
acct := accounts.Account{Address: addr} return s.executePermAction(AddVoter, txArgs{voter: vaddr, from: from})
w, err := s.am.Find(acct)
if err != nil {
return false
}
ps := s.newPermSession1(w, acct)
nonce := s.txPool.Nonce(acct.Address)
ps.TransactOpts.Nonce = new(big.Int).SetUint64(nonce)
tx, err := ps.AddVoter(addr)
if err != nil {
log.Warn("Failed to add voter", "err", err)
return false
}
txHash := tx.Hash()
log.Info("Transaction pending", "tx hash", string(txHash[:]))
return true
} }
func (s *PermissionAPI) RemoveVoter(addr common.Address) bool { func (s *PermissionAPI) RemoveVoter(from common.Address, vaddr common.Address) bool {
ps := s.newPermSession() return s.executePermAction(RemoveVoter, txArgs{voter: vaddr, from: from})
tx, err := ps.RemoveVoter(addr)
if err != nil {
log.Warn("Failed to remove voter", "err", err)
return false
}
txHash := tx.Hash()
log.Info("Transaction pending", "tx hash", string(txHash[:]))
return true
} }
func (s *PermissionAPI) ProposeNode(from common.Address, nodeId string) bool { func (s *PermissionAPI) ProposeNode(from common.Address, nodeId string) bool {
acct := accounts.Account{Address: from} return s.executePermAction(ProposeNode, txArgs{nodeId: nodeId, from: from})
w, err := s.am.Find(acct) }
func (s *PermissionAPI) ApproveNode(from common.Address, nodeId string) bool {
return s.executePermAction(ApproveNode, txArgs{nodeId: nodeId, from: from})
}
func (s *PermissionAPI) DeactivateNode(from common.Address, nodeId string) bool {
return s.executePermAction(DeactivateNode, txArgs{nodeId: nodeId, from: from})
}
func (s *PermissionAPI) ApproveDeactivateNode(from common.Address, nodeId string) bool {
return s.executePermAction(ApproveDeactivateNode, txArgs{nodeId: nodeId, from: from})
}
func (s *PermissionAPI) RemoveOrgKey(from common.Address, orgId string, pvtKey string) bool {
return s.executeOrgKeyAction(RemoveOrgKey, txArgs{from: from, orgId: orgId, keyId: pvtKey})
}
func (s *PermissionAPI) AddOrgKey(from common.Address, orgId string, pvtKey string) bool {
return s.executeOrgKeyAction(AddOrgKey, txArgs{from: from, orgId: orgId, keyId: pvtKey})
}
func (s *PermissionAPI) executePermAction(action PermAction, args txArgs) bool {
fromAcct, w, err := s.validateAccount(args.from)
if err != nil { if err != nil {
return false return false
} }
ps := s.newPermSession(w, fromAcct)
var tx *types.Transaction
ps := s.newPermSession1(w, acct) switch action {
case AddVoter:
tx, err = ps.AddVoter(args.voter)
case RemoveVoter:
tx, err = ps.RemoveVoter(args.voter)
case ProposeNode:
node, err := discover.ParseNode(args.nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
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
tx, err = ps.ProposeNode(enodeID, ipAddrPort, discPort, raftPort)
case ApproveNode:
node, err := discover.ParseNode(args.nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
tx, err = ps.ApproveNode(enodeID)
case DeactivateNode:
node, err := discover.ParseNode(args.nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
tx, err = ps.DeactivateNode(enodeID)
case ApproveDeactivateNode:
node, err := discover.ParseNode(args.nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
//TODO change to approve deactivate node
tx, err = ps.DeactivateNode(enodeID)
}
if err != nil {
log.Error("Failed to execute permission action", "action", action, "err", err)
return false
}
log.Debug("executed permission action", "action", action, "tx", tx)
return true
}
func (s *PermissionAPI) executeOrgKeyAction(action OrgKeyAction, args txArgs) bool {
fromAcct, w, err := s.validateAccount(args.from)
if err != nil {
return false
}
ps := s.newClusterSession(w, fromAcct)
var tx *types.Transaction
switch action {
case AddOrgKey:
tx, err = ps.AddOrgKey(args.orgId, args.keyId)
case RemoveOrgKey:
tx, err = ps.DeleteOrgKey(args.orgId, args.keyId)
}
if err != nil {
log.Error("Failed to execute orgKey action", "action", action, "err", err)
return false
}
log.Debug("executed orgKey action", "action", action, "tx", tx)
return true
}
func (s *PermissionAPI) validateAccount(from common.Address) (accounts.Account, accounts.Wallet, error) {
acct := accounts.Account{Address: from}
w, err := s.acntMgr.Find(acct)
if err != nil {
return acct, nil, err
}
return acct, w, nil
}
func (s *PermissionAPI) newPermSession(w accounts.Wallet, acct accounts.Account) *pbind.PermissionsSession {
transactOpts := bind.NewWalletTransactor(w, acct)
ps := &pbind.PermissionsSession{
Contract: s.permContr,
CallOpts: bind.CallOpts{
Pending: true,
},
TransactOpts: bind.TransactOpts{
From: acct.Address,
GasLimit: defaultGasLimit,
GasPrice: defaultGasPrice,
Signer: transactOpts.Signer,
},
}
nonce := s.txPool.Nonce(acct.Address) nonce := s.txPool.Nonce(acct.Address)
ps.TransactOpts.Nonce = new(big.Int).SetUint64(nonce) ps.TransactOpts.Nonce = new(big.Int).SetUint64(nonce)
return ps
node, err := discover.ParseNode(nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
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
tx, err := ps.ProposeNode(enodeID, ipAddrPort, discPort, raftPort)
if err != nil {
log.Warn("Failed to propose node", "err", err)
log.Error("Failed to propose node: %v", err)
return false
}
txHash := tx.Hash()
statusMsg := fmt.Sprintf("Transaction pending tx hash %s", string(txHash[:]))
log.Debug(statusMsg)
return true
} }
func (s *PermissionAPI) ApproveNode(nodeId string) bool { func (s *PermissionAPI) newClusterSession(w accounts.Wallet, acct accounts.Account) *pbind.ClusterSession {
node, err := discover.ParseNode(nodeId) transactOpts := bind.NewWalletTransactor(w, acct)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
ps := s.newPermSession()
tx, err := ps.ApproveNode(enodeID)
if err != nil {
log.Warn("Failed to propose node", "err", err)
return false
}
txHash := tx.Hash()
log.Debug("Transaction pending", "tx hash", string(txHash[:]))
return true
}
func (s *PermissionAPI) DeactivateNode(nodeId string) bool {
node, err := discover.ParseNode(nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
ps := s.newPermSession()
tx, err := ps.DeactivateNode(enodeID)
if err != nil {
log.Warn("Failed to propose node", "err", err)
return false
}
txHash := tx.Hash()
log.Debug("Transaction pending", "tx hash", string(txHash[:]))
return true
}
func (s *PermissionAPI) ApproveDeactivateNode(nodeId string) bool {
node, err := discover.ParseNode(nodeId)
if err != nil {
log.Error("invalid node id: %v", err)
return false
}
enodeID := node.ID.String()
ps := s.newPermSession()
//TODO change it to approveDeactivate node once contract is updated
tx, err := ps.DeactivateNode(enodeID)
if err != nil {
log.Warn("Failed to propose node", "err", err)
return false
}
txHash := tx.Hash()
log.Debug("Transaction pending", "tx hash", string(txHash[:]))
return true
}
func (s *PermissionAPI) newPermSession() *pbind.PermissionsSession {
return &pbind.PermissionsSession{
Contract: s.permContr,
CallOpts: bind.CallOpts{
Pending: true,
},
TransactOpts: bind.TransactOpts{
From: s.trnOpt.From,
Signer: s.trnOpt.Signer,
GasLimit: defaultGasLimit,
GasPrice: defaultGasPrice,
},
}
}
func (s *PermissionAPI) newPermSession1(w accounts.Wallet, acct accounts.Account) *pbind.PermissionsSession {
auth := bind.NewWalletTransactor(w, acct)
return &pbind.PermissionsSession{
Contract: s.permContr,
CallOpts: bind.CallOpts{
Pending: true,
},
TransactOpts: bind.TransactOpts{
From: acct.Address,
GasLimit: defaultGasLimit,
GasPrice: defaultGasPrice,
Signer: auth.Signer,
},
}
}
func (s *PermissionAPI) newClusterSession() *pbind.ClusterSession {
return &pbind.ClusterSession{ return &pbind.ClusterSession{
Contract: s.clustContr, Contract: s.clustContr,
CallOpts: bind.CallOpts{ CallOpts: bind.CallOpts{
Pending: true, Pending: true,
}, },
TransactOpts: bind.TransactOpts{ TransactOpts: bind.TransactOpts{
From: acct.Address,
GasLimit: defaultGasLimit, GasLimit: defaultGasLimit,
GasPrice: defaultGasPrice, GasPrice: defaultGasPrice,
Signer: transactOpts.Signer,
}, },
} }
} }
func (s *PermissionAPI) AddOrgKey(orgId string, pvtKey string) bool {
cs := s.newClusterSession()
tx, err := cs.AddOrgKey(orgId, pvtKey)
if err != nil {
log.Warn("Failed to add org key", "err", err)
return false
}
txHash := tx.Hash()
log.Info("Transaction pending", "tx hash", string(txHash[:]))
return true
}
func (s *PermissionAPI) RemoveOrgKey(orgId string, pvtKey string) bool {
cs := s.newClusterSession()
tx, err := cs.DeleteOrgKey(orgId, pvtKey)
if err != nil {
log.Warn("Failed to remove org key", "err", err)
return false
}
txHash := tx.Hash()
log.Info("Transaction pending", "tx hash", string(txHash[:]))
return true
}
func getKeyFromKeyStore(datadir string) (string, error) {
files, err := ioutil.ReadDir(filepath.Join(datadir, "keystore"))
if err != nil {
return "", 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 {
return "", err
}
n := len(keyBlob)
return string(keyBlob[:n]), nil
}

View File

@ -686,14 +686,14 @@ web3._extend({
new web3._extend.Method({ new web3._extend.Method({
name: 'addVoter', name: 'addVoter',
call: 'quorum_addVoter', call: 'quorum_addVoter',
params: 1, params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter] inputFormatter: [web3._extend.formatters.inputAddressFormatter,web3._extend.formatters.inputAddressFormatter]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'removeVoter', name: 'removeVoter',
call: 'quorum_removeVoter', call: 'quorum_removeVoter',
params: 1, params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter] inputFormatter: [web3._extend.formatters.inputAddressFormatter,web3._extend.formatters.inputAddressFormatter]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'proposeNode', name: 'proposeNode',
@ -704,27 +704,32 @@ web3._extend({
new web3._extend.Method({ new web3._extend.Method({
name: 'approveNode', name: 'approveNode',
call: 'quorum_approveNode', call: 'quorum_approveNode',
params: 1 params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter,null]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'deactivateNode', name: 'deactivateNode',
call: 'quorum_deactivateNode', call: 'quorum_deactivateNode',
params: 1 params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter,null]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'approveDeactivateNode', name: 'approveDeactivateNode',
call: 'quorum_approveDeactivateNode', call: 'quorum_approveDeactivateNode',
params: 1 params: 2,
inputFormatter: [web3._extend.formatters.inputAddressFormatter,null]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'addOrgKey', name: 'addOrgKey',
call: 'quorum_addOrgKey', call: 'quorum_addOrgKey',
params: 2 params: 3,
inputFormatter: [web3._extend.formatters.inputAddressFormatter,null,null]
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'removeOrgKey', name: 'removeOrgKey',
call: 'quorum_removeOrgKey', call: 'quorum_removeOrgKey',
params: 2 params: 3,
inputFormatter: [web3._extend.formatters.inputAddressFormatter,null,null]
}), }),
] ]
}) })