Create a chain code

Till now ..

We have created a fabric network with 2 organisations and also a channels where these 2 orgs participate for the voting process. Also majority ( majority of 2 org is 2) endorsement is required to for any transaction result to be committed to the result

Next

We will write a chaincode for casting a vote for a candidate

  • We create a voteContract.go file

  • In fabric a smart contract has to implement contractapi.ContractInterface . The easiest way to do so is by embedding contractapi.Contract struct in our custom contract.

  • The cast api takes in TransactionContextInterface from fabric-contract-api-go/contractapi which gives us apis to modify the ledger and other relevant fields for a vote. Note in fabric a chaincode method we can only pass primitive types (bool, float32,int etc) or a struct which has primitive fields

  • 1 - first we create a composite key (here its with voterid field but we can use multiple fields also)- Think of it as primary key for of the vote collections. all crud operations on "vote" will be by using this key.

  • 2 - we check if there is a vote with this key in the world state. We return an error if the vote exists for this key

  • 3 - we create this vote in the world state

There is another chaincode function - GetVoteCountForCand . which can be used to get the vote against a candidate . Its not an efficient way to iterate over the world state , but the important point is to see how we can query world state db. fabric apis do not expose all (aggregation/map reduce) apis from underlying couch db , perhaps a downstream db would be preferred in real world .Also please notice - ../chaincode/META-INF/statedb/couchdb/indexes/indexCandidate.json. Since we are querying using a non key field (candidateid) in this function we have to create a couch db index.

voteContract.go

type VoteContract struct {
	contractapi.Contract
}

func (v *VoteContract) Cast(ctx contractapi.TransactionContextInterface,
   voterid string, issueDateTime string, candidateid string, voteid string) error {
   vote := Vote{
      Voteid:        voteid,
      Voterid:       voterid,
      IssueDateTime: issueDateTime,
      Candidateid:   candidateid,
   }
   key, err := ctx.GetStub().CreateCompositeKey("vote", []string{voterid}) ---> 1  
   if err != nil {
      return xerrors.Errorf("unable to create key %w", err)
   }

   state, err := ctx.GetStub().GetState(key) ----> 2
   if err != nil {
      return xerrors.Errorf("unable to get state for key %w", err)
   }
   if state != nil {
      return fmt.Errorf("voter already voted")
   }

   bytes, err := json.Marshal(vote)
   if err != nil {
      return xerrors.Errorf("unable to marshal vote %w", err)
   }

   err = ctx.GetStub().PutState(key, bytes) .----> 3 
   if err != nil {
      return xerrors.Errorf("unable to update state %w", err)
   }

   return nil
}

The tests

We have used mockery to generate mocks for the interfaces we are going to use

First lets us write test to verify our expectation

Test - 1 is a simple test to assert no error are returned if fabric apis do not error

Test-2 - is to verify that the same user identified by user-id field cannot vote again

Last updated

Was this helpful?