Solana Track Upload

This exercise aims at uploading a track to IPFS and then persisting the track's CID along with some track metadata to the Solana blockchain. Users can later access their track using the public key of the track.

Installation

Assumptions

  • You have the dependencies to run an anchor project.
  • You need to have - Rust, Solana, Yarn and Anchor installed.
  • You can also find instructions to get set up.

Running the project

  • All commands need to br run from the project root.
  • Build the project using anchor build
  • Start a localnet using anchor localnet. We will be using the localnet for our purposes for now.
  • Tests - anchor test
  • Deploy - anchor deploy
  • Install CLI dependencies by running yarn
  • Set Enviromnent Variables
  • Ensure you have a solana key genereated. One can be using solana-keygen new
  • Check your keypair and its location using solana config get keypair
  • export ANCHOR_PROVIDER_URL="http://localhost:8899"
  • export ANCHOR_WALLET=~/.config/solana/id.json Once you have the project deployed and the environment variables set you can interact with it using the CLI.

CLI

I have added the CLI scripts to the scripts section in package.json making it more convenient to run.

Upload Track

Uploads a given CID to Solana. Additionaly we can have the utility upload a track to IPFS, and persist the CID to solana. If --cid argument is passed then the CID is used. We can instead pass a --path argument to upload to IPFS and persist the path. Do make a note of the track key since we will be using this to get the track.

➜  track_upload git:(master) ✗ yarn upload_track --help

Options:
      --help     Show help                                             [boolean]
      --version  Show version number                                   [boolean]
  -c, --cid                                             [string] [default: null]
  -p, --path                                            [string] [default: null]
  -a, --artist                                            [string] [default: ""]
  -t, --title                                             [string] [default: ""]

E.g.

yarn upload_track --path track.mp3 --title "My Sample" --artist "TrackArtist"

Get Track

We can get a track metadata (Artist, Title, CID) from Solana and also get the track from IPFS. This needs the public key from the previous step.

➜  track_upload git:(master) ✗ yarn get_track --help
Options:
      --help      Show help                                            [boolean]
      --version   Show version number                                  [boolean]
  -k, --key                                                  [string] [required]
  -d, --download                                       [boolean] [default: true]

E.g.

yarn get_track -k D8sQycBUZkvpLM12bFpZ4GShb3tbMvPyXLe5J5boo85W

Update Track

Update a track be passing it the existing public key and new values for CID, Title, Artist.

➜  track_upload git:(master) ✗ yarn update_track 

Options:
      --help     Show help                                             [boolean]
      --version  Show version number                                   [boolean]
  -k, --key                                                  [string] [required]
  -c, --cid                                             [string] [default: null]
  -p, --path                                            [string] [default: null]
  -a, --artist                                            [string] [default: ""]
  -t, --title                                             [string] [default: ""]

E.g.

yarn update_track -k D8sQycBUZkvpLM12bFpZ4GShb3tbMvPyXLe5J5boo85W -a "New Artist"

My Tracks

Gets tracks uploaded by you

yarn my_tracks

Returns

Artist: 6TrackArtist, Title: 6 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
https://ipfs.infura.io/ipfs//QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
Artist: 11TrackArtist, Title: 11 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
https://ipfs.infura.io/ipfs//QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
Artist: 10TrackArtist, Title: 10 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
https://ipfs.infura.io/ipfs//QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
Artist: 15TrackArtist, Title: 15 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
https://ipfs.infura.io/ipfs//QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
Artist: 14TrackArtist, Title: 14 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
https://ipfs.infura.io/ipfs//QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi
Artist: 8TrackArtist, Title: 8 My Sample ARTIST, cid: QmTdvKsDU76eB1BaL1s6Nh6uZRp3zDhxgJdWk7ZSdZSPzi

Soteria security analysis

I ran soteria to ensure there we no vulnerabilities detected in my contract

The project creates multiple executables. Please select one from the list below
to detect races. 

In the future, you can specify the executable using the option "-e" if you know 
which ones you want to analyze. 

    coderrect -e executable_name1,executable_name2 your_build_command_line

..... (omitted output)

Analyzing /home/anant/auduis/track_upload/.coderrect/build/bpfel-unknown-unknown/release/deps/track_upload.ll ...
 - ✔ [00m:00s] Loading IR From File                    
 - ▖ [00m:00s] Running Compiler Optimization Passes          
EntryPoints:
entrypoint
 - ✔ [00m:00s] Running Compiler Optimization Passes                                    
 - ✔ [00m:00s] Running Pointer Analysis                        
 - ✔ [00m:00s] Building Static Happens-Before Graph                                    
 - ✔ [00m:00s] Detecting Vunerabilities                        
detected 0 untrustful accounts in total.
detected 0 unsafe math operations in total.

--------The summary of potential vulnerabilities in track_upload.ll--------

	No vulnerabilities detected


anant@desktop:~/auduis/track_upload$ 

Tests

The tests are written using mocha which is typical for anchor. These tests directly test uploading a track and updating a track. To update the track we first need to get the uploaded track so it implicitly tests that as well. We also test updating a track with a different signer and it fails. To run tests run: anchor test

Anchor

This was built using the Anchor toolkit. It gives a opinionated structure to the solana program. It providesus several utilities such as anchor test, anchor deploy, anchor localnet. Anchor also provides us utilities in rust to simplify our code by handling lot of the Serialization, deserialization of data, instructions etc. On the web3 side it provides convenient utilities to interact with solana programs without having to interact directly with the idl. It also provides utilities around keypair generation, getting accounts, programs etc. Making it a lot easier to build.

Contract Logic

The contract takes an account for the track that contains the CID, Artist, Track Title, and the signer.

Initialization

The initilization logic accepts a track (Pub key for track account). We also pass in the Title, Artist and CID to be stored. These are presisted to the account.

Update

Update accepts modified CID, Artist, Title and modifies the track account. These are accepted as "Optional" By checking if the vecror is of size 0. Idealy this would be done using Option but that was giving some issues with serialization.

The update logic

During update we check to make sure if the call has been signed by the original signer. This is done to ensure others can not modify tracks.

Missing Stuff

  • String types need better length control for any production use as this will cause the account to run out of space.

Structure

  • Anchor.toml defines the anchor project.
  • package.json defines the node project.
  • tsconfig.json sets up typescript.
  • The program directory contains the Rust code for the solana program
  • When we run anchor build - output goes to the target directory
  • Tests are in the tests directory
  • The app directory typically is for UI code but I am using it for the CLI code.