github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/permission/permission.go (about)

     1  package permission
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/kisexp/xdchain/common"
    10  	"github.com/kisexp/xdchain/core"
    11  	"github.com/kisexp/xdchain/eth"
    12  	"github.com/kisexp/xdchain/ethclient"
    13  	"github.com/kisexp/xdchain/log"
    14  	"github.com/kisexp/xdchain/p2p/enode"
    15  	"github.com/kisexp/xdchain/params"
    16  	pcore "github.com/kisexp/xdchain/permission/core"
    17  	ptype "github.com/kisexp/xdchain/permission/core/types"
    18  )
    19  
    20  // This is to make sure all contract instances are ready and initialized
    21  //
    22  // Required to be call after standard service start lifecycle
    23  func (p *PermissionCtrl) AfterStart() error {
    24  	log.Debug("permission service: binding contracts")
    25  	err := <-p.errorChan // capture any error happened during asyncStart. Also wait here if asyncStart is not yet finish
    26  	if err != nil {
    27  		return err
    28  	}
    29  	if err = p.contract.BindContracts(); err != nil {
    30  		return fmt.Errorf("populateInitPermissions failed to bind contracts: %v", err)
    31  	}
    32  
    33  	// populate the initial list of permissioned nodes and account accesses
    34  	if err := p.populateInitPermissions(params.DEFAULT_ORGCACHE_SIZE, params.DEFAULT_ROLECACHE_SIZE,
    35  		params.DEFAULT_NODECACHE_SIZE, params.DEFAULT_ACCOUNTCACHE_SIZE); err != nil {
    36  		return fmt.Errorf("populateInitPermissions failed: %v", err)
    37  	}
    38  
    39  	// set the function point for transaction allowed check
    40  	pcore.PermissionTransactionAllowedFunc = p.IsTransactionAllowed
    41  	setPermissionService(p)
    42  
    43  	// set the default access to ReadOnly
    44  	pcore.SetDefaults(p.permConfig.NwAdminRole, p.permConfig.OrgAdminRole, p.IsV2Permission())
    45  	for _, f := range []func() error{
    46  		p.monitorQIP714Block,               // monitor block number to activate new permissions controls
    47  		p.backend.ManageOrgPermissions,     // monitor org management related events
    48  		p.backend.ManageNodePermissions,    // monitor org  level Node management events
    49  		p.backend.ManageRolePermissions,    // monitor org level role management events
    50  		p.backend.ManageAccountPermissions, // monitor org level account management events
    51  	} {
    52  		if err := f(); err != nil {
    53  			return err
    54  		}
    55  	}
    56  
    57  	log.Info("permission service: is now ready")
    58  
    59  	return nil
    60  }
    61  
    62  // start service asynchronously due to dependencies
    63  func (p *PermissionCtrl) asyncStart() {
    64  	var ethereum *eth.Ethereum
    65  	// will be blocked here until Node is up
    66  	if err := p.node.Lifecycle(&ethereum); err != nil {
    67  		p.errorChan <- fmt.Errorf("dependent ethereum service not started")
    68  		return
    69  	}
    70  	defer func() {
    71  		p.errorChan <- nil
    72  	}()
    73  	// for cases where the node is joining an existing network, permission service
    74  	// can be brought up only after block syncing is complete. This function
    75  	// waits for block syncing before the starting permissions
    76  	p.startWaitGroup.Add(1)
    77  	go func(_wg *sync.WaitGroup) {
    78  		log.Debug("permission service: waiting for downloader")
    79  		stopChan, stopSubscription := ptype.SubscribeStopEvent()
    80  		pollingTicker := time.NewTicker(10 * time.Millisecond)
    81  		defer func(start time.Time) {
    82  			log.Debug("permission service: downloader completed", "took", time.Since(start))
    83  			stopSubscription.Unsubscribe()
    84  			pollingTicker.Stop()
    85  			_wg.Done()
    86  		}(time.Now())
    87  		for {
    88  			select {
    89  			case <-pollingTicker.C:
    90  				if pcore.GetSyncStatus() && !ethereum.Downloader().Synchronising() {
    91  					return
    92  				}
    93  			case <-stopChan:
    94  				return
    95  			}
    96  		}
    97  	}(p.startWaitGroup) // wait for downloader to sync if any
    98  
    99  	log.Debug("permission service: waiting for all dependencies to be ready")
   100  	p.startWaitGroup.Wait()
   101  	client, err := p.node.Attach()
   102  	if err != nil {
   103  		p.errorChan <- fmt.Errorf("unable to create rpc client: %v", err)
   104  		return
   105  	}
   106  	p.ethClnt = ethclient.NewClient(client)
   107  	p.eth = ethereum
   108  	p.isRaft = p.eth.BlockChain().Config().Istanbul == nil && p.eth.BlockChain().Config().Clique == nil
   109  	p.updateBackEnd()
   110  }
   111  
   112  // monitors QIP714Block and set default access
   113  func (p *PermissionCtrl) monitorQIP714Block() error {
   114  	// if QIP714block is not given, set the default access
   115  	// to readonly
   116  	if p.eth.BlockChain().Config().QIP714Block == nil || p.eth.BlockChain().Config().IsQIP714(p.eth.BlockChain().CurrentBlock().Number()) {
   117  		pcore.SetQIP714BlockReached()
   118  		return nil
   119  	}
   120  	//QIP714block is given, monitor block count
   121  	go func() {
   122  		chainHeadCh := make(chan core.ChainHeadEvent, 1)
   123  		headSub := p.eth.BlockChain().SubscribeChainHeadEvent(chainHeadCh)
   124  		defer headSub.Unsubscribe()
   125  		stopChan, stopSubscription := ptype.SubscribeStopEvent()
   126  		defer stopSubscription.Unsubscribe()
   127  		for {
   128  			select {
   129  			case head := <-chainHeadCh:
   130  				if p.eth.BlockChain().Config().IsQIP714(head.Block.Number()) {
   131  					pcore.SetQIP714BlockReached()
   132  					return
   133  				}
   134  			case <-stopChan:
   135  				return
   136  			}
   137  		}
   138  	}()
   139  	return nil
   140  }
   141  
   142  func (p *PermissionCtrl) instantiateCache(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize int) {
   143  	// instantiate the cache objects for permissions
   144  	pcore.OrgInfoMap = pcore.NewOrgCache(orgCacheSize)
   145  	pcore.OrgInfoMap.PopulateCacheFunc(p.populateOrgToCache)
   146  
   147  	pcore.RoleInfoMap = pcore.NewRoleCache(roleCacheSize)
   148  	pcore.RoleInfoMap.PopulateCacheFunc(p.populateRoleToCache)
   149  
   150  	pcore.NodeInfoMap = pcore.NewNodeCache(nodeCacheSize)
   151  	pcore.NodeInfoMap.PopulateCacheFunc(p.populateNodeCache)
   152  	pcore.NodeInfoMap.PopulateValidateFunc(p.populateNodeCacheAndValidate)
   153  
   154  	pcore.AcctInfoMap = pcore.NewAcctCache(accountCacheSize)
   155  	pcore.AcctInfoMap.PopulateCacheFunc(p.populateAccountToCache)
   156  }
   157  
   158  // Thus function checks if the initial network boot up status and if no
   159  // populates permissions model with details from permission-config.json
   160  func (p *PermissionCtrl) populateInitPermissions(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize int) error {
   161  	p.instantiateCache(orgCacheSize, roleCacheSize, nodeCacheSize, accountCacheSize)
   162  	networkInitialized, err := p.contract.GetNetworkBootStatus()
   163  	if err != nil {
   164  		// handle the scenario of no contract code.
   165  		log.Warn("Failed to retrieve network boot status ", "err", err)
   166  		return err
   167  	}
   168  
   169  	if !networkInitialized {
   170  		p.backend.MonitorNetworkBootUp()
   171  		if err := p.bootupNetwork(); err != nil {
   172  			return err
   173  		}
   174  	} else {
   175  		//populate orgs, nodes, roles and accounts from contract
   176  		for _, f := range []func() error{
   177  			p.populateOrgsFromContract,
   178  			p.populateNodesFromContract,
   179  			p.populateRolesFromContract,
   180  			p.populateAccountsFromContract,
   181  		} {
   182  			if err := f(); err != nil {
   183  				return err
   184  			}
   185  		}
   186  		pcore.SetNetworkBootUpCompleted()
   187  	}
   188  	return nil
   189  }
   190  
   191  // initialize the permissions model and populate initial values
   192  func (p *PermissionCtrl) bootupNetwork() error {
   193  	if _, err := p.contract.SetPolicy(p.permConfig.NwAdminOrg, p.permConfig.NwAdminRole, p.permConfig.OrgAdminRole); err != nil {
   194  		log.Error("bootupNetwork SetPolicy failed", "err", err)
   195  		return err
   196  	}
   197  	if _, err := p.contract.Init(p.permConfig.SubOrgBreadth, p.permConfig.SubOrgDepth); err != nil {
   198  		log.Error("bootupNetwork init failed", "err", err)
   199  		return err
   200  	}
   201  
   202  	pcore.OrgInfoMap.UpsertOrg(p.permConfig.NwAdminOrg, "", p.permConfig.NwAdminOrg, big.NewInt(1), pcore.OrgApproved)
   203  	pcore.RoleInfoMap.UpsertRole(p.permConfig.NwAdminOrg, p.permConfig.NwAdminRole, true, true, pcore.FullAccess, true)
   204  	// populate the initial Node list from static-nodes.json
   205  	if err := p.populateStaticNodesToContract(); err != nil {
   206  		return err
   207  	}
   208  	// populate initial account access to full access
   209  	if err := p.populateInitAccountAccess(); err != nil {
   210  		return err
   211  	}
   212  
   213  	// update network status to boot completed
   214  	if err := p.updateNetworkStatus(); err != nil {
   215  		log.Error("failed to updated network boot status", "error", err)
   216  		return err
   217  	}
   218  	return nil
   219  }
   220  
   221  // populates the account access details from contract into cache
   222  func (p *PermissionCtrl) populateAccountsFromContract() error {
   223  	if numberOfRoles, err := p.contract.GetNumberOfAccounts(); err == nil {
   224  		iOrgNum := numberOfRoles.Uint64()
   225  		for k := uint64(0); k < iOrgNum; k++ {
   226  			if addr, org, role, status, orgAdmin, err := p.contract.GetAccountDetailsFromIndex(big.NewInt(int64(k))); err == nil {
   227  				pcore.AcctInfoMap.UpsertAccount(org, role, addr, orgAdmin, pcore.AcctStatus(int(status.Int64())))
   228  			}
   229  		}
   230  	} else {
   231  		return err
   232  	}
   233  	return nil
   234  }
   235  
   236  // populates the role details from contract into cache
   237  func (p *PermissionCtrl) populateRolesFromContract() error {
   238  	if numberOfRoles, err := p.contract.GetNumberOfRoles(); err == nil {
   239  		iOrgNum := numberOfRoles.Uint64()
   240  		for k := uint64(0); k < iOrgNum; k++ {
   241  			if roleStruct, err := p.contract.GetRoleDetailsFromIndex(big.NewInt(int64(k))); err == nil {
   242  				pcore.RoleInfoMap.UpsertRole(roleStruct.OrgId, roleStruct.RoleId, roleStruct.Voter, roleStruct.Admin, pcore.AccessType(int(roleStruct.AccessType.Int64())), roleStruct.Active)
   243  			}
   244  		}
   245  
   246  	} else {
   247  		return err
   248  	}
   249  	return nil
   250  }
   251  
   252  // populates the Node details from contract into cache
   253  func (p *PermissionCtrl) populateNodesFromContract() error {
   254  	if numberOfNodes, err := p.contract.GetNumberOfNodes(); err == nil {
   255  		iOrgNum := numberOfNodes.Uint64()
   256  		for k := uint64(0); k < iOrgNum; k++ {
   257  			if orgId, url, status, err := p.contract.GetNodeDetailsFromIndex(big.NewInt(int64(k))); err == nil {
   258  				pcore.NodeInfoMap.UpsertNode(orgId, url, pcore.NodeStatus(int(status.Int64())))
   259  			}
   260  		}
   261  	} else {
   262  		return err
   263  	}
   264  	return nil
   265  }
   266  
   267  // populates the org details from contract into cache
   268  func (p *PermissionCtrl) populateOrgsFromContract() error {
   269  
   270  	if numberOfOrgs, err := p.contract.GetNumberOfOrgs(); err == nil {
   271  		iOrgNum := numberOfOrgs.Uint64()
   272  		for k := uint64(0); k < iOrgNum; k++ {
   273  			if orgId, porgId, ultParent, level, status, err := p.contract.GetOrgInfo(big.NewInt(int64(k))); err == nil {
   274  				pcore.OrgInfoMap.UpsertOrg(orgId, porgId, ultParent, level, pcore.OrgStatus(int(status.Int64())))
   275  			}
   276  		}
   277  	} else {
   278  		return err
   279  	}
   280  	return nil
   281  }
   282  
   283  // Reads the node list from static-nodes.json and populates into the contract
   284  func (p *PermissionCtrl) populateStaticNodesToContract() error {
   285  	nodes := p.node.Server().Config.StaticNodes
   286  	for _, node := range nodes {
   287  		url := pcore.GetNodeUrl(node.EnodeID(), node.IP().String(), uint16(node.TCP()), uint16(node.RaftPort()), p.isRaft)
   288  		_, err := p.contract.AddAdminNode(url)
   289  		if err != nil {
   290  			log.Warn("Failed to propose node", "err", err, "enode", node.EnodeID())
   291  			return err
   292  		}
   293  		pcore.NodeInfoMap.UpsertNode(p.permConfig.NwAdminOrg, url, 2)
   294  	}
   295  	return nil
   296  }
   297  
   298  // Invokes the initAccounts function of smart contract to set the initial
   299  // set of accounts access to full access
   300  func (p *PermissionCtrl) populateInitAccountAccess() error {
   301  	for _, a := range p.permConfig.Accounts {
   302  		_, er := p.contract.AddAdminAccount(a)
   303  		if er != nil {
   304  			log.Warn("Error adding permission initial account list", "err", er, "account", a)
   305  			return er
   306  		}
   307  		pcore.AcctInfoMap.UpsertAccount(p.permConfig.NwAdminOrg, p.permConfig.NwAdminRole, a, true, 2)
   308  	}
   309  	return nil
   310  }
   311  
   312  // updates network boot status to true
   313  func (p *PermissionCtrl) updateNetworkStatus() error {
   314  	_, err := p.contract.UpdateNetworkBootStatus()
   315  	if err != nil {
   316  		log.Warn("Failed to udpate network boot status ", "err", err)
   317  		return err
   318  	}
   319  	return nil
   320  }
   321  
   322  // getter to get an account record from the contract
   323  func (p *PermissionCtrl) populateAccountToCache(acctId common.Address) (*pcore.AccountInfo, error) {
   324  	account, orgId, roleId, status, isAdmin, err := p.contract.GetAccountDetails(acctId)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	if status.Int64() == 0 {
   330  		return nil, ptype.ErrAccountNotThere
   331  	}
   332  	return &pcore.AccountInfo{AcctId: account, OrgId: orgId, RoleId: roleId, Status: pcore.AcctStatus(status.Int64()), IsOrgAdmin: isAdmin}, nil
   333  }
   334  
   335  // getter to get a org record from the contract
   336  func (p *PermissionCtrl) populateOrgToCache(orgId string) (*pcore.OrgInfo, error) {
   337  	org, parentOrgId, ultimateParentId, orgLevel, orgStatus, err := p.contract.GetOrgDetails(orgId)
   338  	if err != nil {
   339  		return nil, err
   340  	}
   341  	if orgStatus.Int64() == 0 {
   342  		return nil, ptype.ErrOrgDoesNotExists
   343  	}
   344  	orgInfo := pcore.OrgInfo{OrgId: org, ParentOrgId: parentOrgId, UltimateParent: ultimateParentId, Status: pcore.OrgStatus(orgStatus.Int64()), Level: orgLevel}
   345  	// now need to build the list of sub orgs for this org
   346  	subOrgIndexes, err := p.contract.GetSubOrgIndexes(orgId)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	if len(subOrgIndexes) == 0 {
   352  		return &orgInfo, nil
   353  	}
   354  
   355  	// range through the sub org indexes and get the org ids to populate the suborg list
   356  	for _, s := range subOrgIndexes {
   357  		subOrgId, _, _, _, _, err := p.contract.GetOrgInfo(s)
   358  
   359  		if err != nil {
   360  			return nil, err
   361  		}
   362  		orgInfo.SubOrgList = append(orgInfo.SubOrgList, orgId+"."+subOrgId)
   363  
   364  	}
   365  	return &orgInfo, nil
   366  }
   367  
   368  // getter to get a role record from the contract
   369  func (p *PermissionCtrl) populateRoleToCache(roleKey *pcore.RoleKey) (*pcore.RoleInfo, error) {
   370  	roleDetails, err := p.contract.GetRoleDetails(roleKey.RoleId, roleKey.OrgId)
   371  
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	if roleDetails.OrgId == "" {
   377  		return nil, ptype.ErrInvalidRole
   378  	}
   379  	return &pcore.RoleInfo{OrgId: roleDetails.OrgId, RoleId: roleDetails.RoleId, IsVoter: roleDetails.Voter, IsAdmin: roleDetails.Admin, Access: pcore.AccessType(roleDetails.AccessType.Int64()), Active: roleDetails.Active}, nil
   380  }
   381  
   382  // getter to get a role record from the contract
   383  func (p *PermissionCtrl) populateNodeCache(url string) (*pcore.NodeInfo, error) {
   384  	orgId, url, status, err := p.contract.GetNodeDetails(url)
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  
   389  	if status.Int64() == 0 {
   390  		return nil, ptype.ErrNodeDoesNotExists
   391  	}
   392  	return &pcore.NodeInfo{OrgId: orgId, Url: url, Status: pcore.NodeStatus(status.Int64())}, nil
   393  }
   394  
   395  // getter to get a Node record from the contract
   396  func (p *PermissionCtrl) populateNodeCacheAndValidate(hexNodeId, ultimateParentId string) bool {
   397  	txnAllowed := false
   398  	passedEnode, _ := enode.ParseV4(hexNodeId)
   399  	if numberOfNodes, err := p.contract.GetNumberOfNodes(); err == nil {
   400  		numNodes := numberOfNodes.Uint64()
   401  		for k := uint64(0); k < numNodes; k++ {
   402  			if orgId, url, status, err := p.contract.GetNodeDetailsFromIndex(big.NewInt(int64(k))); err == nil {
   403  				if orgRec, err := pcore.OrgInfoMap.GetOrg(orgId); err != nil {
   404  					if orgRec.UltimateParent == ultimateParentId {
   405  						recEnode, _ := enode.ParseV4(url)
   406  						if recEnode.ID() == passedEnode.ID() {
   407  							txnAllowed = true
   408  							pcore.NodeInfoMap.UpsertNode(orgId, url, pcore.NodeStatus(int(status.Int64())))
   409  						}
   410  					}
   411  				}
   412  			}
   413  		}
   414  	}
   415  	return txnAllowed
   416  }