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 }