Skip to content

Contract Versioning

Introduction

Contracts on a blockchain are immutable, but sometimes an ongoing business exchange may require new or altered terms of a smart contract. This would normally require a separate contract to be written to reflect the changes, since a contract cannot be modified. Rather than forcing users to create new contracts, requiring them to remember the most up-to-date name, STRATO allows users to have multiple versions of the same contract. Contract versioning allows contracts to have different variables and functions in newer versions while still maintaining the definitions and records of the previous versions.

Usage

Contract Versioning is a feature that is always working in the background. Therefore users do not need to worry about enabling or disabling it for their contracts. All versions of a contract will be available to a user through all API methods that use a contract's name, such as searching on Cirrus.

Example

To demonstrate the feature in action, we present a demo scenario where STRATO's contract versioning will be effective.

Create Initial Contract Definition

The initial terms for a smart contract are created. In this scenario, we use the Simple Storage contract. Right now, Simple Storage only contains 1 variable, and 2 functions.

contract SimpleStorage {
  uint storedData;
  constructor() {
    storedData = 1;
  }
  function set(uint x) {
    storedData = x;
  }
  function get() constant returns (uint) {
    return storedData;
  }
}

Upload Initial Contract

The contract is posted to the blockchain:

curl -X POST \
  -H "Accept: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "txs": [
        {
          "payload": {
            "contract": "SimpleStorage",
            "src": <contract-src>,
            "args": {},
            "metadata": {
              "VM": "SolidVM"
            }
          },
          "type": "CONTRACT"
        }
        ],
        "txParams": {
          "gasLimit": 10000000000,
          "gasPrice": 1
        }
      }' \
  "https://<strato_address>/strato/v2.3/transaction?resolve=true"

Redefine Contract Terms

The Simple Storage contract definition must be updated to add a second variable (and corresponding functions), so the contract is rewritten:

contract SimpleStorage {
  uint storedDataAlt;
  uint storedData;
  constructor() {
    storedData = 5;
    storedDataAlt = 2;
  }
  function setStoredData(uint y) {
    storedData = y;
  }
  function setStoredDataAlt(uint z) {
    storedDataAlt = z;
  }
  function getStoredData() constant returns (uint) {
    return storedData;
  }
  function getStoredDataAlt() constant returns (uint) {
    return storedDataAlt;
  }
}
Upload New Contract

The new version of the Simple Storage contract is posted to the blockchain:

curl -X POST \
  -H "Accept: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "txs": [
        {
          "payload": {
            "contract": "SimpleStorage",
            "src": <modified-contract-src>,
            "args": {},
            "metadata": {
              "VM": "SolidVM"
            }
          },
          "type": "CONTRACT"
        }
        ],
        "txParams": {
          "gasLimit": 10000000000,
          "gasPrice": 1
        }
      }' \
  "https://<strato_address>/strato/v2.3/transaction?resolve=true"

Access Both Versions

Both contracts have been stored properly in the database, rather than the different versions causing errors. They are both accessible by searching Cirrus for the Simple Storage contract:

curl -X GET \
  -H "Accept: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  "https://<strato_address>/cirrus/search/SimpleStorage

Response

When a contract's definition has been expanded to include more state variables, the columns of contracts of previous versions will be null.

[
  {
    "address": "caa0038caffbc7dbaaf0bc72d8252ae56f2eb10f",
    "chainId": "",
    "block_hash": "4dd4bcd3832cff9c28393c38230e38d963df3a4133416b8d17f9dafdbbac09c8",
    "block_timestamp": "2021-11-15 20:19:53 UTC",
    "block_number": "3",
    "transaction_hash": "6ea4e9974610cb3526e4b0411df98748454c1575fee53d570f208ca62757a102",
    "transaction_sender": "ddc9d9249278fd2290d2762c369bd12ec47fff0a",
    "transaction_function_name": "",
    "storedData": "0000000000000000000000000000000000000000",
    "storedDataAlt": null
  },
  {
    "address": "e88a63072027f92f08f97deac2d13767c22468c8",
    "chainId": "",
    "block_hash": "770d4603ed920f119553932651d67f7ce337dddf5c842106088b5644d4eed40a",
    "block_timestamp": "2021-11-15 20:20:03 UTC",
    "block_number": "4",
    "transaction_hash": "0d8e0b2e08615580e24d1ce8eec6befd9bdd1da6e64b2c5e2ccd5bd943dcce43",
    "transaction_sender": "ddc9d9249278fd2290d2762c369bd12ec47fff0a",
    "transaction_function_name": "",
    "storedData": "0000000000000000000000000000000000000000",
    "storedDataAlt": "0000000000000000000000000000000000000000"
  }
]

Known Limitations

Data types of each named variable in the smart contract cannot change between versions. Versioning will not work if the contract variable is instantiated as one data type by one contract and then declared (or instantiated) as a different data type in a future contract. In this case, the database will store the first instance of the contract but will not store future instances of the contract of the same name. A common workaround for this limitation would be to store the second contract with a slightly different name.