github.com/s7techlab/cckit@v0.10.5/examples/cpaper/chaincode.go (about) 1 package cpaper 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/s7techlab/cckit/router" 8 "github.com/s7techlab/cckit/router/param" 9 ) 10 11 type TaskState string 12 13 const ( 14 CommercialPaperTypeName = "CommercialPaper" 15 16 CommercialPaperIssued TaskState = "issued" 17 CommercialPaperTrading TaskState = "trading" 18 CommercialPaperRedeemed TaskState = "redeemed" 19 ) 20 21 type CommercialPaper struct { 22 Issuer string `json:"issuer,omitempty"` 23 PaperNumber string `json:"paper_number,omitempty"` 24 Owner string `json:"owner,omitempty"` 25 IssueDate time.Time `json:"issue_date,omitempty"` 26 MaturityDate time.Time `json:"maturity_date,omitempty"` 27 FaceValue int32 `json:"face_value,omitempty"` 28 State TaskState `json:"state,omitempty"` 29 } 30 31 // Key commercial paper has a composite key < CommercialPaper, Issuer, PaperNumber > 32 func (c CommercialPaper) Key() ([]string, error) { 33 return []string{CommercialPaperTypeName, c.Issuer, c.PaperNumber}, nil 34 } 35 36 type IssueCommercialPaper struct { 37 Issuer string `json:"issuer,omitempty"` 38 PaperNumber string `json:"paper_number,omitempty"` 39 IssueDate time.Time `json:"issue_date,omitempty"` 40 MaturityDate time.Time `json:"maturity_date,omitempty"` 41 FaceValue int32 `json:"face_value,omitempty"` 42 } 43 44 type BuyCommercialPaper struct { 45 Issuer string `json:"issuer,omitempty"` 46 PaperNumber string `json:"paper_number,omitempty"` 47 CurrentOwner string `json:"current_owner,omitempty"` 48 NewOwner string `json:"new_owner,omitempty"` 49 Price int32 `json:"price,omitempty"` 50 PurchaseDate time.Time `json:"purchase_date,omitempty"` 51 } 52 53 type RedeemCommercialPaper struct { 54 Issuer string `json:"issuer,omitempty"` 55 PaperNumber string `json:"paper_number,omitempty"` 56 RedeemingOwner string `json:"redeeming_owner,omitempty"` 57 RedeemDate time.Time `json:"redeem_date,omitempty"` 58 } 59 60 func NewCC() *router.Chaincode { 61 r := router.New(`commercial_paper`) 62 63 r.Init(func(context router.Context) (i interface{}, e error) { 64 // No implementation required with this example 65 // It could be where data migration is performed, if necessary 66 return nil, nil 67 }) 68 69 r. 70 // Read methods 71 Query(`list`, list). 72 // Get method has 2 params - commercial paper primary key components 73 Query(`get`, get, param.String("issuer"), param.String("paper_number")). 74 75 // Transaction methods 76 Invoke(`issue`, issue, param.Struct("issueData", &IssueCommercialPaper{})). 77 Invoke(`buy`, buy, param.Struct("buyData", &BuyCommercialPaper{})). 78 Invoke(`redeem`, redeem, param.Struct("redeemData", &RedeemCommercialPaper{})) 79 80 return router.NewChaincode(r) 81 } 82 83 func get(c router.Context) (interface{}, error) { 84 var ( 85 issuer = c.ParamString("issuer") 86 paperNumber = c.ParamString("paper_number") 87 ) 88 89 return c.State().Get(&CommercialPaper{ 90 Issuer: issuer, 91 PaperNumber: paperNumber, 92 }) 93 } 94 95 func list(c router.Context) (interface{}, error) { 96 // List method retrieves all entries from the ledger using GetStateByPartialCompositeKey method and passing it the 97 // namespace of our contract type, in this example that's `CommercialPaper`, then it unmarshalls received bytes via 98 // json.Unmarshal method and creates a JSON array of CommercialPaper entities 99 return c.State().List(CommercialPaperTypeName, &CommercialPaper{}) 100 } 101 102 func issue(c router.Context) (interface{}, error) { 103 var ( 104 issueData = c.Param("issueData").(IssueCommercialPaper) // Assert the chaincode parameter 105 commercialPaper = &CommercialPaper{ 106 Issuer: issueData.Issuer, 107 PaperNumber: issueData.PaperNumber, 108 Owner: issueData.Issuer, 109 IssueDate: issueData.IssueDate, 110 MaturityDate: issueData.MaturityDate, 111 FaceValue: issueData.FaceValue, 112 State: CommercialPaperIssued, // Initial state 113 } 114 ) 115 116 return commercialPaper, c.State().Insert(commercialPaper) 117 } 118 119 func buy(c router.Context) (interface{}, error) { 120 var ( 121 commercialPaper CommercialPaper 122 // Buy transaction payload 123 buyData = c.Param("buyData").(BuyCommercialPaper) 124 125 // Get the current commercial paper state 126 cp, err = c.State().Get(&CommercialPaper{ 127 Issuer: buyData.Issuer, 128 PaperNumber: buyData.PaperNumber, 129 }, &CommercialPaper{}) 130 ) 131 132 if err != nil { 133 return nil, fmt.Errorf("paper not found: %w", err) 134 } 135 136 commercialPaper = cp.(CommercialPaper) 137 138 // Validate current owner 139 if commercialPaper.Owner != buyData.CurrentOwner { 140 return nil, fmt.Errorf( 141 "paper %s %s is not owned by %s", 142 commercialPaper.Issuer, commercialPaper.PaperNumber, buyData.CurrentOwner) 143 } 144 145 // First buyData moves state from ISSUED to TRADING 146 if commercialPaper.State == CommercialPaperIssued { 147 commercialPaper.State = CommercialPaperTrading 148 } 149 150 // Check paper is not already REDEEMED 151 if commercialPaper.State == CommercialPaperTrading { 152 commercialPaper.Owner = buyData.NewOwner 153 } else { 154 return nil, fmt.Errorf( 155 "paper %s %s is not trading.current state = %s", 156 commercialPaper.Issuer, commercialPaper.PaperNumber, commercialPaper.State) 157 } 158 159 return commercialPaper, c.State().Put(commercialPaper) 160 } 161 162 func redeem(c router.Context) (interface{}, error) { 163 var ( 164 commercialPaper CommercialPaper 165 166 // Buy transaction payload 167 redeemData = c.Param("redeemData").(RedeemCommercialPaper) 168 169 // Get the current commercial paper state 170 cp, err = c.State().Get(&CommercialPaper{ 171 Issuer: redeemData.Issuer, 172 PaperNumber: redeemData.PaperNumber, 173 }, &CommercialPaper{}) 174 ) 175 176 if err != nil { 177 return nil, fmt.Errorf("paper not found: %w", err) 178 } 179 180 commercialPaper = cp.(CommercialPaper) 181 182 // Check paper is not REDEEMED 183 if commercialPaper.State == CommercialPaperRedeemed { 184 return nil, fmt.Errorf( 185 "paper %s %s is already redeemed", 186 commercialPaper.Issuer, commercialPaper.PaperNumber) 187 } 188 189 // Verify that the redeemer owns the commercial paper before redeeming it 190 if commercialPaper.Owner == redeemData.RedeemingOwner { 191 commercialPaper.Owner = redeemData.Issuer 192 commercialPaper.State = CommercialPaperRedeemed 193 } else { 194 return nil, fmt.Errorf( 195 "redeeming owner does not own paper %s %s", 196 commercialPaper.Issuer, commercialPaper.PaperNumber) 197 } 198 199 return commercialPaper, c.State().Put(commercialPaper) 200 }