Invoke Atomic Transaction

post

/restproxy/api/v2/atomicTransactions

Invoke an atomic transaction. This allows you to have multiple transactions spanning multiple channels which are committed atomically - either all of them are committed successfully, or none of them are commited.

Request

Supported Media Types
Body ()
Request to invoke a transaction
Root Schema : schema
Type: object
Show Source
Nested Schema : atomicTransactionLRCReq
Type: object
Request body for transaction to be executed as the Last Resource Commit (LRC) transaction
Show Source
Nested Schema : transactions
Type: array
Array of individual transactions to process
Show Source
Nested Schema : atomicTransactionEthRequest
Type: object
Request body for Ethereum transaction to be executed
Show Source
Nested Schema : finalityParams
Type: object
Show Source
Nested Schema : signedReq
Type: object
Show Source
Nested Schema : unsignedReq
Type: object
Show Source
Nested Schema : paramKeys
Type: array
List of keys that are to be extracted from the results of the final OBP chaincode transaction and sent to the smart contract via parameters
Show Source
Nested Schema : params
Type: object
Additional Properties Allowed: true
Parameters that are required by the method to be called in the smart contract. Required if isContractCall is set to true
Nested Schema : atomicTransactionInvokeReq
Type: object
Show Source
Nested Schema : args
Type: array
Arguments for the chaincode
Show Source
Nested Schema : endorsers
Type: array
Endorsers for the chaincode
Show Source
Nested Schema : transientMap
Type: object
Additional Properties Allowed
Show Source
TransientMap for the chaincode
Back to Top

Response

Supported Media Types

201 Response

Successful operation for sync transaction
Body ()
Root Schema : schema
Type: object
Show Source
Nested Schema : atomicTransactionResults
Type: object
Show Source
Nested Schema : atomicTransactionLRCResults
Type: object
Show Source
Nested Schema : transactions
Type: array
Details of individual transactions processed
Show Source
Nested Schema : atomicTransactionEthResponse
Type: object
Show Source
Nested Schema : atomicTransactionIndividualResults
Type: object
Show Source
Nested Schema : commit
Type: object
Details on the commit phase of the transaction
Show Source
Nested Schema : prepare
Type: object
Details on the prepare phase of the transaction
Show Source
Nested Schema : rollback
Type: object
Details on the rollback phase of the transaction
Show Source

202 Response

Successful operation for async transaction
Body ()
Root Schema : schema
Type: object
Show Source
Nested Schema : result
Type: object
Show Source

400 Response

Bad Request

401 Response

Not authorized

403 Response

Forbidden

404 Response

Invalid parameters

500 Response

Service unavailable
Back to Top

Examples

Example of an Atomic Transaction with Two Chaincodes

The following example shows how to invoke an atomic transaction that comprises two chaincode transactions by submitting a POST request on the REST resource using cURL.

curl -v -u <username>:<password> -X POST \
  "https://<rest proxy of your blockchain instance>/api/v2/atomicTransactions" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  --data @<JSON file with the request parameters>

For example,

curl -v -u <username>:<password> -X POST \
  "https://myvm.oracle.com:10001/restproxy/api/v2/atomicTransactions" \
  -H "accept: application/json" \
  -H "Content-Type: application/json" 
  --data @file.json
The contents of file.json are:
{
 "transactions": [
   {"chaincode":"obcs-marbles","args":["transferMarble", "marble1", "tom"],"timeout":0, "channel":"goods"},
   {"chaincode":"obcs-example02","args":["invoke", "a", "b", "50"],"timeout":0, "channel":"wallet"}
 ],
 "isolationLevel": "serializable",
 "prepareTimeout": 10000,
 "sync": true
}
Where:
  • transactions is an array that contains two chaincode transactions. The first transaction transfers a marble by using the Marbles sample chaincode. The second transaction pays for the marble by using the Balance Transfer sample chaincode.
  • args specify the chaincode function to be invoked along with its parameters.
  • isolationLevel with the serializable value indicates that all keys in the read and write set of the transactions are locked between the prepare phase and the commit phase. The readCommitted value for the isolation indicates that keys in the write set of transactions are locked between the prepare phase and the commit phase.
  • timeout specifies the maximum number of milliseconds for the transaction to timeout during the prepare phase.
  • sync with a true value indicates that this is a synchronous transaction.

The following example shows the contents of the response body in JSON format for a successful atomic transaction:

{
  "returnCode": "Success",
  "error": "",
  "result": {
    "transactions": [
      {
        "channel": "goods",
        "chaincode": "obcs-marbles",
        "txstatus": "Committed",
        "prepare": {
          "txid": "bb248ef3f948cb107417c3f66ea144910644dee8086a370d44687fd9fe262233"
        },
        "commit": {
          "txid": "21068846ef85942f9df1ccfd27dcb95d509146c5a0d10c6366642215badcb3b4"
        },
        "rollback": {}
      },
      {
        "channel": "wallet",
        "chaincode": "obcs-example02",
        "txstatus": "Committed",
        "prepare": {
          "txid": "08d03d7170e7f696c79da0aa60142bbfdc08b3d92cee0d9423983dd19caf809c"
        },
        "commit": {
          "txid": "ef9d8a824631f9e671162b81c7d1617080746ef3897b029cfbef1cfd1594d0ab"
        },
        "rollback": {}
      }
    ],
    "globalStatus": "Success",
    "globalTxid": "21438a99-9fd7-4f96-b0bf-259910a26006",
    "txStartTime": "2022-08-15T16:16:19.2180799642"
  }
}

In this case, the value returned for globalStatus is Success, which indicates that all of the transactions that the atomic transaction comprises were committed successfully.

The following example shows the response for an unsuccessful atomic transaction, where one of the operations encountered an error during the prepare phase. Here, a non-integer value was supplied for a Balance Transfer parameter that requires an integer value. In this case, the transaction that succeeded during the prepare phase is rolled back.

{
  "returnCode": "Failure",
  "error": "blockchain transaction error",
  "result": {
    "transactions": [
      {
        "channel": "goods",
        "chaincode": "obcs-marbles",
        "txstatus": "Rolledback",
        "prepare": {
          "txid": "bb248ef3f948cb107417c3f66ea144910644dee8086a370d44687fd9fe262233"
        },
        "commit": {},
        "rollback": {
          "txid": "21068846ef85942f9df1ccfd27dcb95d509146c5a0d10c6366642215badcb3b4"
        },
      },
      {
        "channel": "wallet",
        "chaincode": "obcs-example02",
        "txstatus": "FailedPrepare",
        "prepare": {
          "error": "failed to invoke chaincode: Transaction processing for endorser [myvm.oracle.com:20010]: Chaincode status Code: (500) UNKNOWN.
            Description: Invalid transaction amount, expecting a integer value"
        },
        "commit": {},
        "rollback": {
          "error":"skipping rollback since prepare stage has failed or was skipped"
        }
      }
    ],
    "globalStatus": "Failure",
    "globalTxid": "21438a99-9fd7-4f96-b0bf-259910a26006",
    "txStartTime": "2022-08-15T16:16:19.2180799642"
  }
}

Example of Ethereum Transactions

Ethereum transactions are supported in atomic workflows by using a last resource commit (LRC) optimization, because they cannot be broken down into prepare and commit phases. After all other transactions are in the prepared state, the Ethereum transaction is started. If the Ethereum transaction succeeds, then the other transactions are committed. If the Ethereum transaction fails, then the other transactions are rolled back.

The following JSON file shows how to include an unsigned Ethereum transaction, described in the lrc section, with an atomic transaction that invokes two other chaincode transactions. The lrc section includes an ethReq section, which describes the transaction, and a finalityParams section, which determines how long to wait for transaction finality.
{
 "transactions": [
   {"chaincode":"bt1","args":["invoke", "a", "b", "7"],"timeout":0, "channel":"ch1"},
   {"chaincode":"bt2","args":["invoke", "a", "b", "5"],"timeout":0, "channel":"ch2"}
 ],
 "lrc": {
   "ethReq": {
     "url": "http://<IP address of Ethereum server>:<port>",
     "chainId": 1337,
     "unSignedReq": {
        "privateKey": "de1fab4e05b476f81d11901a69e58a3000859395eb6bdb222fcb583b8d599b00",
        "ethValue": "1000000000000000000",
        "gasLimit": 21000,
        "toAddress": "0x7336d04f97fdccd0a93462947474c8879a5c6597"
     },
     "finalityParams": {
       "checkFinality": true,
       "blocksToWait": 2,
       "secondsToWait": 30
     }
   }
 },
 "isolationLevel": "serializable",
 "prepareTimeout": 10000,
 "sync": true
}
Where, in addition to the parameters described previously:
  • url is the URL of the Ethereum endpoint, including the protocol and port number.
  • chainId is the network ID of the Ethereum network.
  • unsignedReq is the unsigned request body, which includes the following parameters:
    • privateKey: the private key in hexadecimal string format.Foot 1
    • ethValue: the amount of Ether to transfer, measured in Wei.
    • gasLimit: the maximum amount of gas that the transaction is allowed to consume.
    • toAddress: the receiving address for the Ether transfer.
  • checkFinality is a Boolean value that specifies whether to check for transaction finality.
  • blocksToWait is the number of blocks to wait before a transaction is considered final.
  • secondsToWait is the number of seconds to wait for finality. The maximum wait time is 120 seconds.
The following example shows a successful response body for the previous transaction. You can use the txHex value to look up information about the block in a block explorer tool such as Etherscan.
{
    "returnCode": "Success",
    "error": "",
    "result": {
        "transactions": [
            {
                "channel": "ch1",
                "chaincode": "bt1",
                "txstatus": "Committed",
                "prepare": {
                    "txid": "f9c65ac93568b3cb878f3f98a5021c443cf138a6411da22d2154ec6f52299de3"
                },
                "commit": {
                    "txid": "b7539349027a0e26eb0522f3066aea721a6bb3bf29d8e821736f73b52357a637"
                },
                "rollback": {}
            },
            {
                "channel": "ch2",
                "chaincode": "bt2",
                "txstatus": "Committed",
                "prepare": {
                    "txid": "b96d046ab11437b7dc6d5d584ef5f33968c11f893e3587ebf07aec0484be38e2"
                },
                "commit": {
                    "txid": "8f3f1fccfead63a2ce6a76be7c2678791ffc8093997a36c94100faa312f95f17"
                },
                "rollback": {}
            }
        ],
        "lrc": {
            "ethResp": {
                "block": 19,
                "txHex": "0xc5741e0da0e16d9b594cac44d5c3cd70d552d013552265f2d06789a01d3fa81d"
            },
            "txstatus": "Committed"
        },
        "globalStatus": "Success",
        "globalTxid": "4b9099f4-9528-82cc-357a-7ed5c703d772",
        "txStartTime": "2022-09-14T10:56:40.018420392Z"
    }
}
The following JSON file shows how to include a signed Ethereum transaction with an atomic transaction that invokes two other chaincode transactions. Instead of an unSignedReq section, the request includes a signedReq section.
{
    "transactions": [
        {
            "chaincode": "obcs-example02","args": ["invoke","a","b","1"], 
            "timeout": 0,
            "channel": "default",
            "endorsers":["http://<IP address of endorser>:<port>"]
        }
    ],
    "lrc": {
        "ethReq": {
            "url": "http://<IP address of Ethereum server>:<port>",
            "chainId": 5,
            "signedReq": { 
                "signedTxHex" : "02f8950506841ad2748084476807808401406f409442efb56de8e6a516fff899c9263ef3d21ffd9c298502540be400a4e3456fa90000000000000000000000000000000000000000000000000000000000000001c080a074c985cce323b924e5503592a1301e301c4a3fa4c55234cf60b2782dce81e825a0605b9f61113057e02f614b4d2a52fc5953719aec30743ff4d8b7e2bbf3a206b5"
            },
            "pendingTimeout": 420,
            "finalityParams": {
                "checkFinality": true,
                "blocksToWait": 10,
                "secondsToWait": 20
            }
        }
    },
    "isolationLevel": "serializable",
    "prepareTimeout": 10000,
    "sync": false
}

Where, in addition to the parameters described previously, unsignedReq is the signed request body, which includes the signedTxHex parameter, which is the signed request string in hexadecimal format.

The following example shows a successful response body for the previous transaction:
{
    "returnCode": "Success",
    "error": "",
    "result": {
        "transactions": [
            {
                "channel": "default",
                "chaincode": "obcs-example02",
                "txstatus": "Committed",
                "prepare": {
                    "txid": "cb2bb0474bbd1dcb53e8dea68be26f9f3480e60aeeae12977c34fe27a7583f9b"
                },
                "commit": {
                    "txid": "915d61e3c1b9b2c9e1a55aa13b3670cb8c2b1b14ea6426b3cad1ff0bb519532f"
                },
                "rollback": {}
            }
        ],
        "lrc": {
            "ethResp": {
                "block": 8167525,
                "txHex": "0x53254e565aedfdac6d63e40d64c967347661dcc64045a673f948b52c6e48ff4c"
            },
            "txstatus": "Committed"
        },
        "globalStatus": "Success",
        "globalTxid": "3ab9421d-9dfa-40c0-a463-2f9fcb936c42",
        "txStartTime": "2022-12-20T06:09:47.040072324Z"
    }
    }

NFT Transfer From OBP to Ethereum

You can send an NFT from a chaincode deployed in Oracle Blockchain Platform to a smart contract deployed on Ethereum or Polygon blockchain networks. This is done using an extension of an atomic transaction's last resource commit (LRC) transaction request.

Blockchain Platform burns the required NFT from Oracle Blockchain Platform chaincode by calling the burnNFT method present in ERC1155 and ERC721 Go and TypeScript App Builder-generated chaincodes. This method burns the NFT from the OBP chaincode and returns the required information from the NFT such as tokenId, price, and tokenHistory. Oracle Blockchain Platform then:
  • collects the information returned by the burnNFT method
  • extracts the required parameters from it
  • creates a transaction (contract call) with the parameters required by the method
  • mints the NFT on the smart contract deployed over an Ethereum or Polygon blockchain network by invoking a custom mint method present in the smart contract
  • sets all the custom properties required by the NFT such as tokenPrice and tokenHistory on Oracle Blockchain Platform.

Example of a request body for an NFT transfer:

{
   "transactions": [
       {
           "chaincode": "<chaincodeName>",
           "args": [
              "burnNFT",
              "Org1MSP",
              "user1",
               "1"
           ],
           "timeout": 0,
           "channel": "<channelName>"
       }
   ],
   "lrc": {
       "ethReq": {
           "url": "https://eth-sepolia.g.alchemy.com/v2/6Ho4Tv8uQ1hKCoIjfI_2v_xN6gZJWqLP",
           "chainId": 11155111,
           "unsignedReq": {
               "toAddress": "<contractAddress>",
               "ethValue": "0",
               "methodName": "mintNft",
               "isContractCall": true,
               "abi":"[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"tokenHistory\",\"type\":\"string\"}],\"name\":\"mintNft\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
               "privateKey": "<privateKey>",
               "paramKeys": ["tokenId", "tokenUri", "price", "tokenHistory"],
               "params": {
                   "to": "<toAddress>",
                   "id": "${tokenId}",
                   "tokenUri": "${tokenUri}",
                   "price": "${price}",
                   "tokenHistory": "${tokenHistory}"
               },
               "gasLimit": 2589190,
               "gasTipCap": "250000000",
               "gasFeeCap": "250000000"
           },
           "pendingTimeout": 500,
           "finalityParams": {
               "checkFinality": false,
               "blocksToWait": 3,
               "secondsToWait": 30
           }
       }
   },
   "isolationLevel": "serializable",
   "prepareTimeout": 60000,
   "sync": false
}
Where, in addition to the parameters described previously:
  • url is the URL of the Ethereum endpoint, including the protocol and port number.
  • chainId is the network ID of the Ethereum network.
  • unsignedReq is the unsigned request body, which includes the following parameters:
    • toAddress: the receiving address for the Ether transfer.
    • ethValue: the amount of Ether to transfer, measured in Wei.
    • methodName: method name to be called in the smart contract. This example uses a custom mint method present in the smart contract (mintNft) which mints the NFT on the smart contract deployed over an Ethereum blockchain network.
    • isContractCall: specifies that the given request is a smart contract call.
    • abi: an Application Binary Interface allows your smart contract to interact with other applications and smart contracts.
    • privateKey: the private key in hexadecimal string format.
    • paramKeys: the keys to be extracted from the results of the final Oracle Blockchain Platform chaincode transaction and sent to the smart contract via parameters.
    • params: parameters that are required by the method to be called in the smart contract.
    • gasLimit: the maximum amount of gas that the transaction is allowed to consume.
    • gasTipCap: the maximum fees to be included as a tip to the validator.
    • gasFeeCap: the maximum fees you are willing to pay for the transaction.
  • pendingTimeout the max time, in seconds, to wait in case the transaction remains in pending state.
  • finalityParams is the number of blocks to wait before a transaction is considered final.
    • checkFinality: specifies that transaction finality check is not required for the transaction.
    • blocksToWait: specifies that it will wait for 3 blocks before a transaction is considered final.
    • secondsToWait: specifies that it will wait 30 seconds before a transaction is considered final.

Example of the response body for an NFT transfer:

{
"returnCode": "Success",
"error": "",
"result": {
     "transactions": [
         {
             "channel": "default",
             "chaincode": "obcs-example02",
             "txstatus": "Committed",
             "prepare": {
                 "txid": "b8dda6fe3bf4013c587c96aa1028e57ce3ad71ea59d5d4612d2795ab88046896"
             },
             "commit": {
                 "txid": "45f7da042ebedac4faebd458692752e1b934f5f3561fc36255fec6d6ee1d7976"
             },
             "rollback": {}
         }
     ],
     "lrc": {
         "ethResp": {
             "block": 8167617,
             "txHex": "0x99c2da536f751491311a6f671a74e14b3614897b78b0e30d24ba65ff3a370a38"
         },
         "txstatus": "Committed"
     },
     "globalStatus": "Success",
     "globalTxid": "734e4f32-d2b1-4111-9168-f89f739a16e4",
     "txStartTime": "2022-12-20T06:33:24.577427607Z"
}
}


Footnote Legend

Footnote 1: When using the atomicTransaction API to burn Oracle Blockchain Platform tokens and re-mint them on Ethereum or other EVM networks with Oracle Blockchain Platform token history included in their metadata, you should use the unsigned Ethereum request in order to include the token history and other relevant data returned from Oracle Blockchain Platform burn method into Ethereum mint method via specified parameters. In order to use the unsigned Ethereum request, you need to include a private key for your account in the atomicTransaction API payload so that the REST proxy can finalize the request with the parameters specified and sign it before invoking Ethereum RPC API. Your key is protected in transit using TLS (HTTP/S), but exercise caution if your calls are redirected through other external systems (such as proxies) before reaching the Oracle Blockchain Platform REST Proxy endpoint. Once the call reaches the Oracle Blockchain Platform REST Proxy, the private key is used in memory for the sole purpose of signing the Ethereum call request and is discarded after the operation. It is not logged or stored anywhere within the Oracle Blockchain Platform service or Oracle Cloud Infrastructure.
Back to Top