What are tags?

Version Control Systems (VCS) provide the functionality to tag specific points in a repository’s history as milestones which can be used for a marked version release (i.e. v1.0.1). A tag can be considered as a branch which should not change after being created and mapped to a commit.

tag

Tags point to commits

Why make tags immutable?

tag

Original comic: https://xkcd.com/2347/

The above comic accurately depicts our modern digital infrastructure where dependencies are stacked upon each other like these blocks, each block supporting the weight of all the blocks above it.

Tags allow software developers to determine which versions of code they can reliably use as dependencies in their projects. Many automation systems rely on tags to build and install dependencies.

Therefore, mutating these tags by changing the commits that they point to can cause:

in the project and other projects that depend on it.

tag

Mutating a tag can cause issues

Our solution

Our solution uses the tamper-evident and tamper-resistant nature of blockchain to guarantee tag immutability and provide assurance to users that their products will continue functioning with the same level of reliability.

info icon
If a repository has a tag v1.0.0 which points to commit b06cdb0, then it should not be possible to delete or modify it by pointing it to a different commit 49a68cd. This way, users who were depending on the changes that were made until commit b06cdb0 will not be pointed to the potentially buggy and incompatible commit 49a68cd.

Our solution leverages blockchain and smart contracts to:

We have exposed RESTful APIs so that third-party applications can easily use our solution to provide tag immutability.

Since most VCS support the concept of tags, they can make use of our tool-agnostic solution to support tag immutability in their products. Source-code repository hosting services such as GitHub , GitLab , Bitbucket , and SourceForge can also provide their users with tag immutability and since these host most of the world’s open-source projects, the benefit can be extended to millions of developers and end-users worldwide.

tag

Our solution

Scope of this project

While our solution is conceptually VCS-agnostic, for the purposes of this project, we have created an immutable tagging system compatible with GitHub, since it is the most popular code hosting platform with easily accessible APIs.

tag

Application Architecture

Our application is made up of three fundamental components:

  1. Ethereum Smart Contracts written in Solidity
  2. A Node.js Middleware
  3. Front-end Web Application written using ReactJS

In the following sections, we will go through each of these components in detail.

Smart Contract

We have used Ethereum smart contracts because of their ease of use and widely available support. Using Solidity , a programming language used to code smart contracts in Etheruem, we have created a struct to store all details about tags:

contract ImmutableTag {

    struct Tag {
        string repoURL;
        string tagID;
        string commitHash;
    }

    ...
}

We then have a mapping (equivalent to a dictionary or hashtable in other programming languages) of string to Tag objects inside the smart contract:

mapping(string => Tag) private tags;

By using a mapping instead of a array, we can look up tags in O(1) time. The key for this mapping is a concatenation of the repository URL and the tag.

For example, if we want to create a tag v1.0.0 in the repository https://github.com/Immutable-Tag/SmartContacts, then the key in the mapping will be "https://github.com/Immutable-Tag/SmartContacts_v1.0.0" and the value will be the following Tag object:

{
    repoURL: "https://github.com/Immutable-Tag/SmartContacts"
    tagID: "v1.0.0"
    commitHash: "66190b9fc987cb12c3a302c84123122e68ef6450"
}

We have added three functions to support our product functionality:

  1. createTag - This function creates tags to be stored in the Ethereum Blockchain by adding it to the mapping tags. The function accepts the repository URL, tag, and the commit to be associated with the tag as parameters.
function createTag(string memory _repoURL, string memory _tagID, string memory _commitHash) public {
    string memory key;
    key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
    tags[key] = Tag(_repoURL, _tagID, _commitHash);
}
  1. getTag - This function takes a repository URL and tag ID and looks up in the mapping to retrieve the tag’s details.
function getTag(string memory _repoURL, string memory _tagID) public view returns (Tag memory) {
    string memory key;
    key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
    return tags[key];
}
  1. checkTag - This function checks if a tag already exsits on the blockchain or not. If the tag exists in the mapping then this function returns true, otherwise it returns false.
function checkTag(string memory _repoURL, string memory _tagID) public view returns (bool) {
    string memory key;
    key = string(abi.encodePacked(string(abi.encodePacked(_repoURL, "_")), _tagID));
    return bytes(tags[key].commitHash).length != 0;
}

We are using the Truffle Suite for developing our solution as it provides a development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine (EVM). It also provides features like built-in Smart Contract compilation, linking, deployment and binary management and automated contract testing. We used Ganache to simulate an Ethereum blockchain and deployed our smart contract to it.

After writing our smart contract, we deploy it to Ganache, our local blockchain, by running the command truffle migrate. This compiles our Solidity smart contract code to JavaScript and deploys it to Ganache. The output of this deployment looks like this:

...
2_tag_contracts.js
==================

   Replacing 'ImmutableTag'
   ------------------------
   > transaction hash:    0x0e1ef624894d2a75d2356deac0daf3a077195eff60d1e96452662ce0804c3c5e
   > Blocks: 0            Seconds: 0
   > contract address:    0x5A58B7cAf4609a1c9f7934A0bDBcbeF3B4d27bb8
   > block number:        3
   > block timestamp:     1637725439
   > account:             0x851F4BE5447fbC3e40b50A650584551434D9579F
   > balance:             9999.97973206
   > gas used:            779116 (0xbe36c)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.01558232 ETH


   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.01558232 ETH


Summary
=======
> Total deployments:   2
> Final cost:          0.01942118 ETH

We use the contract address of the ImmutableTag smart contract (0x5A58B7cAf4609a1c9f7934A0bDBcbeF3B4d27bb8 in this case) to invoke it from the middleware. We provide more details about this in the next section.

You can find the entire code and documentation for our smart contract in this GitHub repository .

Middleware

Our middleware layer is a RESTful API server built using Node.js. The APIs provided by the server are consumed by our React front-end.

The server also calls GitHub APIs to verify the existence of a commit to which a tag will point, and optionally, to create tags in the GitHub repository using GitHub personal access tokens.

Connecting Node.js server to the blockchain

In the previous section, we described how we deploy our smart contract to Ganache and get a contract address once the deployment succeeds. We use this address to connect the middleware to the smart contract using web3.js and invoke smart contract functions for tag creation and tag retrieval.

Creating a Tag

This API creates a new tag on the blockchain. It first checks if the tag already exists in the blockchain by invoking the checkTag smart contract function. If the tag does not exist, it makes a request to GitHub to verify the existence of the commit that is being mapped to the tag. If the commit is valid, it proceeds to create the tag in the blockchain by invoking the createTag smart contract function. (Please see the previous section on Smart contracts to understand more.)

If a GitHub personal access token is passed to it, then the API also creates the tag in the GitHub repository.

API endpoint

POST /v1/tags

Parameters

Name Type In Description
Content-Type string header application/json
repo_url string body repository URL
tag_id string body tag name
commit_id string body commit hash

Example

curl -i -X POST "http://localhost:5000/v1/tags" \
    -H 'Content-Type: application/json' \
    -d '{"repo_url": "https://github.com/Immutable-Tag/SmartContacts",\
        "tag_id": "v1.0.0",\
        "commit_id": "66190b9fc987cb12c3a302c84123122e68ef6450"}'

Response:

HTTP/1.1 201 Created
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 39
ETag: W/"27-p5b2vwAQyjd8Enu5ruBWmlgkkzw"
Date: Wed, 24 Nov 2021 04:58:54 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"message":"Successfully created tag."}

If we try to map an existing tag to a different commit, the response looks like:

HTTP/1.1 400 Bad Request
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-KM94dQmY+pgv0Ecd8L77N5qIs0c"
Date: Wed, 24 Nov 2021 04:58:27 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"error":"Tag already exists"}

If the commit doesn’t exist, the response looks like:

HTTP/1.1 400 Bad Request
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-KM94dQmY+pgv0Ecd8L77N5qIs0c"
Date: Wed, 24 Nov 2021 04:58:27 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"error":"Commit does not exist"}

Retrieving a Tag

This API retrieves a tag and its related information from the blockchain. For this, we invoke the getTag smart contract function which returns the Tag object if it exists in the mapping. (Please see the previous section on Smart contracts to understand more.)

API endpoint

POST /v1/tags/{tag_id}

Parameters

Name Type In Description
Content-Type string header application/json
tag_id string path tag name
repo_url string body repository URL

Example

curl -i -X POST "http://localhost:5000/v1/tags/v1.0.0" \
    -H 'Content-Type: application/json' \
    -d '{"repo_url": "https://github.com/Immutable-Tag/SmartContacts"}'

Response:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 134
ETag: W/"86-aGtC1lmpzo6bWnvAnDjnyX41gHg"
Date: Wed, 24 Nov 2021 04:57:49 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"repo_url":"https://github.com/Immutable-Tag/SmartContacts","tag_id":"v1.0.0","commit_id":"66190b9fc987cb12c3a302c84123122e68ef6450"}

If we try to get a non-existent tag, the response looks like:

HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 30
ETag: W/"1e-Tox4M2y0N7WkRGDAFErMm9hsPbA"
Date: Wed, 24 Nov 2021 04:57:36 GMT
Connection: keep-alive
Keep-Alive: timeout=5

{"error":"Tag does not exist"}

You can find the entire code and documentation for our middleware in this GitHub repository .

The React App

We have created a frontend using ReactJS to showcase how to:

  1. Create a tag immutably
  2. Retrieve created tag

tag

Home Page

Creating a Tag

Upon navigating to the create tag page, we can see a form that requires the following fields:

  1. The Repository URL for which we wish to create a tag
  2. The tag name
  3. The commit which we wish to map the tag to

Optionally, the form also accepts a GitHub token which includes permissions for creating tags in the GitHub repository as well as the blockchain.

create tag

Creating a Tag

A tag is not created in the following scenarios:

  1. Tag already exists - To guarantee immutability, a tag once created cannot be modified. Therefore, trying to map a pre-exisiting tag to a different commit gives an error message Tag already exists.

Tag already exists

Tag already exists

  1. Commit does not exist - Trying to create a tag with an invalid commit, i.e. a commit which does not exist in the provided Repository URL gives an error message Commit does not exist

Commit does not exist

Commit does not exist

Retrieving a tag

Once a tag is created in the blockchain, it can be retrieved by providing:

  1. The Repository URL for which we created the tag
  2. The tag name

Retrieving a tag

Retrieving a tag

Tag retrieval will be unsuccessful if we pass a tag name that does not exist in the corresponding repository URL.

Failed to retrieve tag

Failed to retrieve tag

You can find the entire code and documentation for our React application in this GitHub repository .

Application Workflow

The sequence diagrams below show the application workflow for creating and retrieving tags:

Creating a tag

Retrieving a tag

Demo

Running the application locally

Please refer to this link for steps on how to run the application locally. In case you encounter any issues, please create a GitHub issue here .

Using the application

Once you the application running, the following video shows how you can use the application to create tags immutably and retrieve them.

Demo video

Future Work

Our team decided to test the idea of tag immutability using Ethereum, which allowed us to quickly build a Proof-of-Concept using smart contracts written in Solidity.

Compatibility with other blockchains

Our next step is to allow our solution to easily plug in any blockchain that supports smart contracts. We would also like to use permissioned blockchains such as ResilientDB in order to achieve a higher throughput.

GitHub OAuth App

Currently our application optionally reads a GitHub access token from the UI and creates tags in the GitHub repositories. We would like to instead create a GitHub OAuth App that users can give permissions for creating tags in the repositories they have access to.

Compatibility with other VCS

Our current scope is limited to working with git tags for repositories hosted on GitHub . In the future, we would like our APIs to be easily consumed by not only other git hosting providers such as GitLab and Bitbucket , but also hosting providers for Subversion and Mercurial VCS.

Automation with existing CI/CD workflows

While our project has a web front-end which makes it easy for our users to use our application, we can also provide Command Line Interface (CLI) tools for interacting with our API. The primary benefit of this would be easy integration into automated CI/CD workflows of engineering teams.

API Authentication

In the future we will add include authentication for our API using API Keys and also add rate limiting to prevent security attacks.

Application outside VCS

The concept of tag immutability can be applied to not only VCS, but also other areas where tagging is used. A popular example is that of Docker tags, where tags point to specific image digests/hashes. As cloud computing continues to be more popular, this will be extremely helpful for Docker Hub and cloud providers that have a container registry for storing Docker images. This includes Amazon Web Services , Google Cloud Platform , Microsoft Azure , IBM Cloud and Oracle Cloud .

About the team

This product was conceived and developed as part of our coursework for the Fall 2021 class ECS 265: Distributed Database Systems taught by Prof. Mohammad Sadoghi at the University of California, Davis . Our team consists of following members:

  1. Vasudha Jha ( vajha@ucdavis.edu )
  2. Parichaya Chatterji ( parichay@ucdavis.edu )
  3. Anusha Kulkarni ( ahkulkarni@ucdavis.edu )
  4. Preethi Muruganandam ( pmuruganandam@ucdavis.edu )
  5. Ashwitha Kassetty ( avkassetty@ucdavis.edu )