github.com/RobustRoundRobin/quorum@v20.10.0+incompatible/extension/api.go (about)

     1  package extension
     2  
     3  import (
     4  	"encoding/base64"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/core/types"
    11  	"github.com/ethereum/go-ethereum/internal/ethapi"
    12  )
    13  
    14  var (
    15  	errNotAcceptor = errors.New("account is not acceptor of this extension request")
    16  	errNotCreator  = errors.New("account is not the creator of this extension request")
    17  )
    18  
    19  const extensionCompleted = "DONE"
    20  const extensionInProgress = "ACTIVE"
    21  
    22  type PrivateExtensionAPI struct {
    23  	privacyService *PrivacyService
    24  }
    25  
    26  func NewPrivateExtensionAPI(privacyService *PrivacyService) *PrivateExtensionAPI {
    27  	return &PrivateExtensionAPI{
    28  		privacyService: privacyService,
    29  	}
    30  }
    31  
    32  // ActiveExtensionContracts returns the list of all currently outstanding extension contracts
    33  func (api *PrivateExtensionAPI) ActiveExtensionContracts() []ExtensionContract {
    34  	api.privacyService.mu.Lock()
    35  	defer api.privacyService.mu.Unlock()
    36  
    37  	extracted := make([]ExtensionContract, 0, len(api.privacyService.currentContracts))
    38  	for _, contract := range api.privacyService.currentContracts {
    39  		extracted = append(extracted, *contract)
    40  	}
    41  	return extracted
    42  }
    43  
    44  // checks of the passed contract address is under extension process
    45  func (api *PrivateExtensionAPI) checkIfContractUnderExtension(toExtend common.Address) bool {
    46  	for _, v := range api.ActiveExtensionContracts() {
    47  		if v.ContractExtended == toExtend {
    48  			return true
    49  		}
    50  	}
    51  	return false
    52  }
    53  
    54  // checks if the voter has already voted on the contract.
    55  func (api *PrivateExtensionAPI) checkAlreadyVoted(addressToVoteOn, from common.Address) bool {
    56  	caller, _ := api.privacyService.managementContractFacade.Caller(addressToVoteOn)
    57  	opts := bind.CallOpts{Pending: true, From: from}
    58  
    59  	voted, _ := caller.CheckIfVoted(&opts)
    60  	return voted
    61  }
    62  
    63  // checks if the voter has already voted on the contract.
    64  func (api *PrivateExtensionAPI) checkIfExtensionComplete(addressToVoteOn, from common.Address) (bool, error) {
    65  	caller, _ := api.privacyService.managementContractFacade.Caller(addressToVoteOn)
    66  	opts := bind.CallOpts{Pending: true, From: from}
    67  
    68  	status, err := caller.CheckIfExtensionFinished(&opts)
    69  	if err != nil {
    70  		return true, err
    71  	}
    72  	return status, nil
    73  }
    74  
    75  // returns the contract being extended for the given management contract
    76  func (api *PrivateExtensionAPI) getContractExtended(addressToVoteOn, from common.Address) (common.Address, error) {
    77  	caller, _ := api.privacyService.managementContractFacade.Caller(addressToVoteOn)
    78  	opts := bind.CallOpts{Pending: true, From: from}
    79  
    80  	return caller.ContractToExtend(&opts)
    81  }
    82  
    83  // checks if the contract being extended is a public contract
    84  func (api *PrivateExtensionAPI) checkIfPublicContract(toExtend common.Address) bool {
    85  	// check if the passed contract is public contract
    86  	publicStateDb, _, _ := api.privacyService.stateFetcher.chainAccessor.State()
    87  	if publicStateDb != nil && publicStateDb.Exist(toExtend) {
    88  		return true
    89  	}
    90  	return false
    91  }
    92  
    93  // checks if the contract being extended is available on the node
    94  func (api *PrivateExtensionAPI) checkIfPrivateStateExists(toExtend common.Address) bool {
    95  	// check if the private contract exists on the node extending the contract
    96  	_, privateStateDb, _ := api.privacyService.stateFetcher.chainAccessor.State()
    97  	if privateStateDb != nil {
    98  		if privateStateDb.GetCode(toExtend) != nil {
    99  			return true
   100  		}
   101  	}
   102  	return false
   103  }
   104  
   105  // ApproveContractExtension submits the vote to the specified extension management contract. The vote indicates whether to extend
   106  // a given contract to a new participant or not
   107  func (api *PrivateExtensionAPI) ApproveExtension(addressToVoteOn common.Address, vote bool, txa ethapi.SendTxArgs) (string, error) {
   108  	// check if the extension has been completed. if yes
   109  	// no acceptance required
   110  	status, err := api.checkIfExtensionComplete(addressToVoteOn, txa.From)
   111  	if err != nil {
   112  		return "", err
   113  	}
   114  
   115  	if status {
   116  		return "", errors.New("contract extension process complete. nothing to accept")
   117  	}
   118  
   119  	if !types.CheckIfAdminAccount(txa.From) {
   120  		return "", errors.New("account cannot accept extension")
   121  	}
   122  
   123  	toExtend, err := api.getContractExtended(addressToVoteOn, txa.From)
   124  	if err != nil {
   125  		return "", err
   126  	}
   127  
   128  	// get all participants for the contract being extended
   129  	participants, err := api.privacyService.GetAllParticipants(api.privacyService.stateFetcher.getCurrentBlockHash(), toExtend)
   130  	if err == nil {
   131  		txa.PrivateFor = append(txa.PrivateFor, participants...)
   132  	}
   133  
   134  	txArgs, err := api.privacyService.GenerateTransactOptions(txa)
   135  	if err != nil {
   136  		return "", err
   137  	}
   138  
   139  	voterList, err := api.privacyService.managementContractFacade.GetAllVoters(addressToVoteOn)
   140  	if err != nil {
   141  		return "", err
   142  	}
   143  	if isVoter := checkAddressInList(txArgs.From, voterList); !isVoter {
   144  		return "", errNotAcceptor
   145  	}
   146  
   147  	if api.checkAlreadyVoted(addressToVoteOn, txArgs.From) {
   148  		return "", errors.New("already voted")
   149  	}
   150  	uuid, err := generateUuid(addressToVoteOn, txArgs.PrivateFrom, txArgs.PrivateFor, api.privacyService.ptm)
   151  	if err != nil {
   152  		return "", err
   153  	}
   154  
   155  	//Find the extension contract in order to interact with it
   156  	extender, err := api.privacyService.managementContractFacade.Transactor(addressToVoteOn)
   157  	if err != nil {
   158  		return "", err
   159  	}
   160  
   161  	//Perform the vote transaction.
   162  	tx, err := extender.DoVote(txArgs, vote, uuid)
   163  	if err != nil {
   164  		return "", err
   165  	}
   166  	msg := fmt.Sprintf("0x%x", tx.Hash())
   167  	return msg, nil
   168  }
   169  
   170  // ExtendContract deploys a new extension management contract to the blockchain to start the process of extending
   171  // a contract to a new participant
   172  //Create a new extension contract that signifies that we want to add a new participant to an existing contract
   173  //This should contain:
   174  // - arguments for sending a new transaction (the same as sendTransaction)
   175  // - the contract address we want to extend
   176  // - the new PTM public key
   177  // - the Ethereum addresses of who can vote to extend the contract
   178  func (api *PrivateExtensionAPI) ExtendContract(toExtend common.Address, newRecipientPtmPublicKey string, recipientAddr common.Address, txa ethapi.SendTxArgs) (string, error) {
   179  
   180  	// check if the contract to be extended is already under extension
   181  	// if yes throw an error
   182  	if api.checkIfContractUnderExtension(toExtend) {
   183  		return "", errors.New("contract extension in progress for the given contract address")
   184  	}
   185  
   186  	// check if a public contract is being extended
   187  	if api.checkIfPublicContract(toExtend) {
   188  		return "", errors.New("extending a public contract!!! not allowed")
   189  	}
   190  
   191  	// check if a public contract is being extended
   192  	if !api.checkIfPrivateStateExists(toExtend) {
   193  		return "", errors.New("extending a non-existent private contract!!! not allowed")
   194  	}
   195  
   196  	// check if recipient address is 0x0
   197  	if recipientAddr == (common.Address{0}) {
   198  		return "", errors.New("invalid recipient address")
   199  	}
   200  
   201  	// check if contract creator
   202  	if !api.privacyService.CheckIfContractCreator(api.privacyService.stateFetcher.getCurrentBlockHash(), toExtend) {
   203  		return "", errors.New("operation not allowed")
   204  	}
   205  
   206  	// if running in permissioned mode with new permissions model
   207  	// ensure that the account extending the contract is an admin
   208  	// account and recipient account is an admin account as well
   209  	if txa.From == recipientAddr {
   210  		return "", errors.New("account accepting the extension cannot be the account initiating extension")
   211  	}
   212  	if !types.CheckIfAdminAccount(txa.From) {
   213  		return "", errors.New("account not an org admin account, cannot initiate extension")
   214  	}
   215  	if !types.CheckIfAdminAccount(recipientAddr) {
   216  		return "", errors.New("recipient account address is not an org admin account. cannot accept extension")
   217  	}
   218  
   219  	// check the new key is valid
   220  	if _, err := base64.StdEncoding.DecodeString(newRecipientPtmPublicKey); err != nil {
   221  		return "", errors.New("invalid new recipient transaction manager key provided")
   222  	}
   223  
   224  	// check the the intended new recipient will actually receive the extension request
   225  	switch len(txa.PrivateFor) {
   226  	case 0:
   227  		txa.PrivateFor = append(txa.PrivateFor, newRecipientPtmPublicKey)
   228  	case 1:
   229  		if txa.PrivateFor[0] != newRecipientPtmPublicKey {
   230  			return "", errors.New("mismatch between recipient transaction manager key and privateFor argument")
   231  		}
   232  	default:
   233  		return "", errors.New("invalid transaction manager keys given in privateFor argument")
   234  	}
   235  
   236  	// get all participants for the contract being extended
   237  	participants, err := api.privacyService.GetAllParticipants(api.privacyService.stateFetcher.getCurrentBlockHash(), toExtend)
   238  	if err == nil {
   239  		txa.PrivateFor = append(txa.PrivateFor, participants...)
   240  	}
   241  
   242  	//generate some valid transaction options for sending in the transaction
   243  	txArgs, err := api.privacyService.GenerateTransactOptions(txa)
   244  	if err != nil {
   245  		return "", err
   246  	}
   247  
   248  	//Deploy the contract
   249  	tx, err := api.privacyService.managementContractFacade.Deploy(txArgs, toExtend, recipientAddr, newRecipientPtmPublicKey)
   250  	if err != nil {
   251  		return "", err
   252  	}
   253  
   254  	//Return the transaction hash for later lookup
   255  	msg := fmt.Sprintf("0x%x", tx.Hash())
   256  	return msg, nil
   257  }
   258  
   259  // CancelExtension allows the creator to cancel the given extension contract, ensuring
   260  // that no more calls for votes or accepting can be made
   261  func (api *PrivateExtensionAPI) CancelExtension(extensionContract common.Address, txa ethapi.SendTxArgs) (string, error) {
   262  	status, err := api.checkIfExtensionComplete(extensionContract, txa.From)
   263  	if err != nil {
   264  		return "", err
   265  	}
   266  	if status {
   267  		return "", errors.New("contract extension process complete. nothing to cancel")
   268  	}
   269  
   270  	toExtend, err := api.getContractExtended(extensionContract, txa.From)
   271  	if err != nil {
   272  		return "", err
   273  	}
   274  
   275  	// get all participants for the contract being extended
   276  	participants, err := api.privacyService.GetAllParticipants(api.privacyService.stateFetcher.getCurrentBlockHash(), toExtend)
   277  	if err == nil {
   278  		txa.PrivateFor = append(txa.PrivateFor, participants...)
   279  	}
   280  
   281  	txArgs, err := api.privacyService.GenerateTransactOptions(txa)
   282  	if err != nil {
   283  		return "", err
   284  	}
   285  
   286  	caller, err := api.privacyService.managementContractFacade.Caller(extensionContract)
   287  	if err != nil {
   288  		return "", err
   289  	}
   290  	creatorAddress, err := caller.Creator(nil)
   291  	if err != nil {
   292  		return "", err
   293  	}
   294  	if isCreator := checkAddressInList(txArgs.From, []common.Address{creatorAddress}); !isCreator {
   295  		return "", errNotCreator
   296  	}
   297  
   298  	extender, err := api.privacyService.managementContractFacade.Transactor(extensionContract)
   299  	if err != nil {
   300  		return "", err
   301  	}
   302  
   303  	tx, err := extender.Finish(txArgs)
   304  	if err != nil {
   305  		return "", err
   306  	}
   307  	msg := fmt.Sprintf("0x%x", tx.Hash())
   308  	return msg, nil
   309  }
   310  
   311  // Returns the extension status from management contract
   312  func (api *PrivateExtensionAPI) GetExtensionStatus(extensionContract common.Address) (string, error) {
   313  
   314  	status, err := api.checkIfExtensionComplete(extensionContract, common.Address{})
   315  	if err != nil {
   316  		return "", err
   317  	}
   318  
   319  	if status {
   320  		return extensionCompleted, nil
   321  	}
   322  
   323  	return extensionInProgress, nil
   324  }