api.go 13 KB

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