Fix high-throughput sample (#1351)

Build failures because the startFabric.sh script specified that the
chaincode required initialization, which is both legacy behavior and is
unnecessary.

The chaincode is updated to use the Contract API instead of the legacy /
low-level chaincode API. The client application is also simplified.

Signed-off-by: Mark S. Lewis <Mark.S.Lewis@outlook.com>
This commit is contained in:
Mark S. Lewis 2025-10-06 13:36:09 +01:00 committed by GitHub
parent ad1950554d
commit 6130e1b9e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 297 additions and 379 deletions

View file

@ -35,9 +35,14 @@ jobs:
go run . manyUpdates testvar1 100 + go run . manyUpdates testvar1 100 +
go run . prune testvar1 go run . prune testvar1
go run . get testvar1 go run . get testvar1
go run . update testvar2 100 + go run . update testvar1 100 +
go run . get testvar1
go run . delete testvar1
go run . manyUpdatesTraditional testvar2 100 + go run . manyUpdatesTraditional testvar2 100 +
go run . delete testvar2 go run . getstandard testvar2
go run . updatestandard testvar2 100 +
go run . getstandard testvar2
go run . delstandard testvar2
- name: Stop Fabric - name: Stop Fabric
working-directory: high-throughput working-directory: high-throughput

View file

@ -35,7 +35,7 @@ Hyperledger Fabric, however, careful considerations need to be given to the data
Let's look at some concrete use cases and how an organization might implement high-throughput storage. These cases will try and explore some of the Let's look at some concrete use cases and how an organization might implement high-throughput storage. These cases will try and explore some of the
advantages and disadvantages of such a system, and how to overcome them. advantages and disadvantages of such a system, and how to overcome them.
#### Example 1 (IOT): Boxer Construction Analysts ### Example 1 (IOT): Boxer Construction Analysts
Boxer Construction Analysts is an IOT company focused on enabling real-time monitoring of large, expensive assets (machinery) on commercial Boxer Construction Analysts is an IOT company focused on enabling real-time monitoring of large, expensive assets (machinery) on commercial
construction projects. They've partnered with the only construction vehicle company in New York, Condor Machines Inc., to provide a reliable, construction projects. They've partnered with the only construction vehicle company in New York, Condor Machines Inc., to provide a reliable,
@ -59,7 +59,7 @@ eventually be committed to the ledger in the order they were received. Additiona
by a dashboard to provide live monitoring data. The only difference the engineers have to pay attention to in this case is to make sure the sensors can by a dashboard to provide live monitoring data. The only difference the engineers have to pay attention to in this case is to make sure the sensors can
send deltas from the previous reading, rather than fixed readings. send deltas from the previous reading, rather than fixed readings.
#### Example 2 (Balance Transfer): Robinson Credit Co. ### Example 2 (Balance Transfer): Robinson Credit Co.
Robinson Credit Co. provides credit and financial services to large businesses. As such, their accounts are large, complex, and accessed by many Robinson Credit Co. provides credit and financial services to large businesses. As such, their accounts are large, complex, and accessed by many
people at once at any time of the day. They want to switch to blockchain, but are having trouble keeping up with the number of deposits and people at once at any time of the day. They want to switch to blockchain, but are having trouble keeping up with the number of deposits and
@ -167,29 +167,19 @@ The application will query the variable after submitting the transaction. The re
We will now see what happens when you try to run 1000 concurrent updates using a traditional transaction. Run the following command to create a variable named `testvar2`: We will now see what happens when you try to run 1000 concurrent updates using a traditional transaction. Run the following command to create a variable named `testvar2`:
```
go run . update testvar2 100 +
```
The variable will have a value of 100:
```
2020/10/27 18:01:45 Value of variable testvar2 : 100
```
Now lets try to update `testvar2` 1000 times in parallel:
``` ```
go run . manyUpdatesTraditional testvar2 100 + go run . manyUpdatesTraditional testvar2 100 +
``` ```
When the program ends, you may see that none of the updates succeeded. When the program ends, you should see that many of the updates failed and that the value is much lower than expected.
``` ```
2020/10/27 18:03:15 Final value of variable testvar2 : 100 2025/09/12 10:59:30 submitting 1000 concurrent updates...
2025/09/12 10:59:31 998 submit failures.
2025/09/12 10:59:31 Value of variable testvar2: 200
``` ```
The transactions failed because multiple transactions in each block updated the same key. Because of these transactions generated read/write conflicts, the transactions included in each block were rejected in the validation stage. The transactions failed because multiple transactions in each block updated the same key concurrently. Because of this, these transactions generated read/write conflicts and the transactions included in each block were rejected in the validation stage.
You can can examine the peer logs to view the messages generated by the rejected blocks: You can can examine the peer logs to view the messages generated by the rejected blocks:

View file

@ -9,11 +9,14 @@ package main
import ( import (
"log" "log"
"os" "os"
"sync"
"sync/atomic"
"time" "time"
"github.com/hyperledger/fabric-gateway/pkg/client" "github.com/hyperledger/fabric-gateway/pkg/client"
"github.com/hyperledger/fabric-gateway/pkg/hash" "github.com/hyperledger/fabric-gateway/pkg/hash"
f "github.com/hyperledger/fabric-samples/high-throughput/application-go/functions" gatewaypb "github.com/hyperledger/fabric-protos-go-apiv2/gateway"
"google.golang.org/grpc/status"
) )
func main() { func main() {
@ -22,7 +25,7 @@ func main() {
if len(os.Args) <= 2 { if len(os.Args) <= 2 {
log.Println("Usage: function variableName") log.Println("Usage: function variableName")
log.Fatalf("functions: update manyUpdates manyUpdatesTraditional get prune delete") log.Fatalf("functions: update delete prune get manyUpdates updatestandard delstandard getstandard manyUpdatesTraditional")
} else if (os.Args[1] == "update" || os.Args[1] == "manyUpdates" || os.Args[1] == "manyUpdatesTraditional") && len(os.Args) < 5 { } else if (os.Args[1] == "update" || os.Args[1] == "manyUpdates" || os.Args[1] == "manyUpdatesTraditional") && len(os.Args) < 5 {
log.Fatalf("error: provide value and operation") log.Fatalf("error: provide value and operation")
} else if len(os.Args) == 3 { } else if len(os.Args) == 3 {
@ -56,38 +59,117 @@ func main() {
contract := gateway.GetNetwork("mychannel").GetContract("bigdatacc") contract := gateway.GetNetwork("mychannel").GetContract("bigdatacc")
// Handle different functions // Handle different functions
if function == "update" { switch function {
result, err := f.Update(contract, function, variableName, change, sign) case "update":
if err != nil { update(contract, variableName, change, sign)
log.Fatalf("error: %v", err) case "updatestandard":
} updateStandard(contract, variableName, change, sign)
log.Println("Value of variable", string(variableName), ": ", string(result)) case "delete":
delete(contract, variableName)
} else if function == "delete" || function == "prune" || function == "delstandard" { case "prune":
result, err := f.DeletePrune(contract, function, variableName) prune(contract, variableName)
if err != nil { case "delstandard":
log.Fatalf("error: %v", err) delStandard(contract, variableName)
} case "get":
log.Println(string(result)) get(contract, variableName)
} else if function == "get" || function == "getstandard" { case "getstandard":
result, err := f.Query(contract, function, variableName) getStandard(contract, variableName)
if err != nil { case "manyUpdates":
log.Fatalf("error: %v", err) manyUpdates(contract, "Update", variableName, change, sign)
} get(contract, variableName)
log.Println("Value of variable", string(variableName), ": ", string(result)) case "manyUpdatesTraditional":
} else if function == "manyUpdates" { manyUpdates(contract, "UpdateStandard", variableName, change, sign)
log.Println("submitting 1000 concurrent updates...") getStandard(contract, variableName)
result, err := f.ManyUpdates(contract, "update", variableName, change, sign) default:
if err != nil { log.Fatalln("Unkown function:", function)
log.Fatalf("error: %v", err)
}
log.Println("Final value of variable", string(variableName), ": ", string(result))
} else if function == "manyUpdatesTraditional" {
log.Println("submitting 1000 concurrent updates...")
result, err := f.ManyUpdates(contract, "putstandard", variableName, change, sign)
if err != nil {
log.Fatalf("error: %v", err)
}
log.Println("Final value of variable", string(variableName), ": ", string(result))
} }
} }
func update(contract *client.Contract, variableName, change, sign string) {
result, err := contract.SubmitTransaction("Update", variableName, change, sign)
failOnError(err)
log.Println(string(result))
get(contract, variableName)
}
func updateStandard(contract *client.Contract, variableName, change, sign string) {
result, err := contract.SubmitTransaction("UpdateStandard", variableName, change, sign)
failOnError(err)
log.Printf("Value of variable %s: %s\n", variableName, result)
}
func delete(contract *client.Contract, variableName string) {
result, err := contract.SubmitTransaction("Delete", variableName)
failOnError(err)
log.Println(string(result))
}
func prune(contract *client.Contract, variableName string) {
result, err := contract.SubmitTransaction("Prune", variableName)
failOnError(err)
log.Println(string(result))
}
func delStandard(contract *client.Contract, variableName string) {
_, err := contract.SubmitTransaction("DelStandard", variableName)
failOnError(err)
log.Printf("Deleted %s.\n", variableName)
}
func get(contract *client.Contract, variableName string) {
result, err := contract.EvaluateTransaction("Get", variableName)
failOnError(err)
log.Printf("Value of variable %s: %s\n", variableName, result)
}
func getStandard(contract *client.Contract, variableName string) {
result, err := contract.EvaluateTransaction("GetStandard", variableName)
failOnError(err)
log.Printf("Value of variable %s: %s\n", variableName, result)
}
func manyUpdates(contract *client.Contract, function, variableName, change, sign string) {
log.Println("submitting 1000 concurrent updates...")
var failCount atomic.Uint32
var wg sync.WaitGroup
for range 1000 {
wg.Add(1)
go func() {
defer wg.Done()
_, err := contract.SubmitTransaction(function, variableName, change, sign)
if err != nil {
failCount.Add(1)
}
}()
}
wg.Wait()
n := failCount.Load()
if n > 0 {
log.Printf("%v submit failures.", n)
}
}
func failOnError(err error) {
if err == nil {
return
}
details := status.Convert(err).Details()
if len(details) > 0 {
log.Println("Error Details:")
for _, detail := range details {
switch detail := detail.(type) {
case *gatewaypb.ErrorDetail:
log.Printf("- address: %s\n mspId: %s\n message: %s\n", detail.Address, detail.MspId, detail.Message)
default:
log.Printf("- %s", detail)
}
}
}
log.Fatalf("error: %v", err)
}

View file

@ -1,22 +0,0 @@
/*
Copyright 2020 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package functions
import (
"fmt"
"github.com/hyperledger/fabric-gateway/pkg/client"
)
// DeletePrune deletes or prunes a variable
func DeletePrune(contract *client.Contract, function, variableName string) ([]byte, error) {
result, err := contract.SubmitTransaction(function, variableName)
if err != nil {
return result, fmt.Errorf("failed to Submit transaction: %v", err)
}
return result, err
}

View file

@ -1,39 +0,0 @@
/*
Copyright 2020 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package functions
import (
"fmt"
"sync"
"github.com/hyperledger/fabric-gateway/pkg/client"
)
// ManyUpdates allows you to push many cuncurrent updates to a variable
func ManyUpdates(contract *client.Contract, function, variableName, change, sign string) ([]byte, error) {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() ([]byte, error) {
defer wg.Done()
result, err := contract.SubmitTransaction(function, variableName, change, sign)
if err != nil {
return result, fmt.Errorf("failed to evaluate transaction: %v", err)
}
return result, nil
}()
}
wg.Wait()
result, err := contract.EvaluateTransaction("get", variableName)
if err != nil {
return nil, fmt.Errorf("failed to evaluate transaction: %v", err)
}
return result, err
}

View file

@ -1,22 +0,0 @@
/*
Copyright 2020 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package functions
import (
"fmt"
"github.com/hyperledger/fabric-gateway/pkg/client"
)
// Query can be used to read the latest value of a variable
func Query(contract *client.Contract, function, variableName string) ([]byte, error) {
result, err := contract.EvaluateTransaction(function, variableName)
if err != nil {
return nil, fmt.Errorf("failed to evaluate transaction: %v", err)
}
return result, err
}

View file

@ -1,27 +0,0 @@
/*
Copyright 2020 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package functions
import (
"fmt"
"github.com/hyperledger/fabric-gateway/pkg/client"
)
// Update can be used to update or prune the variable
func Update(contract *client.Contract, function, variableName, change, sign string) ([]byte, error) {
result, err := contract.SubmitTransaction(function, variableName, change, sign)
if err != nil {
return result, fmt.Errorf("failed to Submit transaction: %v", err)
}
result, err = contract.EvaluateTransaction("get", variableName)
if err != nil {
return nil, fmt.Errorf("failed to evaluate transaction: %v", err)
}
return result, err
}

View file

@ -4,11 +4,11 @@ go 1.23.0
require ( require (
github.com/hyperledger/fabric-gateway v1.8.0 github.com/hyperledger/fabric-gateway v1.8.0
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7
google.golang.org/grpc v1.73.0 google.golang.org/grpc v1.73.0
) )
require ( require (
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect
golang.org/x/crypto v0.40.0 // indirect golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect golang.org/x/net v0.42.0 // indirect

View file

@ -2,17 +2,25 @@ module github.com/hyperledger/fabric-samples/high-throughput/chaincode
go 1.23.0 go 1.23.0
require ( require github.com/hyperledger/fabric-contract-api-go/v2 v2.2.0
github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a
github.com/hyperledger/fabric-protos-go v0.3.0
)
require ( require (
github.com/golang/protobuf v1.5.3 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0 // indirect
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/net v0.42.0 // indirect golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect google.golang.org/grpc v1.67.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View file

@ -1,33 +1,61 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a h1:HwSCxEeiBthwcazcAykGATQ36oG9M+HEQvGLvB7aLvA= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a/go.mod h1:TDSu9gxURldEnaGSFbH1eMlfSQBWQcMQfnDBcpQv5lU= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/hyperledger/fabric-protos-go v0.3.0 h1:MXxy44WTMENOh5TI8+PCK2x6pMj47Go2vFRKDHB2PZs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/hyperledger/fabric-protos-go v0.3.0/go.mod h1:WWnyWP40P2roPmmvxsUXSvVI/CF6vwY1K1UFidnKBys= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0 h1:IhkHfrl5X/fVnmB6pWeCYCdIJRi9bxj+WTnVN8DtW3c=
github.com/hyperledger/fabric-chaincode-go/v2 v2.0.0/go.mod h1:PHHaFffjw7p7n9bmCfcm7RqDqYdivNEsJdiNIKZo5Lk=
github.com/hyperledger/fabric-contract-api-go/v2 v2.2.0 h1:rmUoBmciB0GL/miqcbJmJbgp5QTWoJUrZo+CNxrNLF4=
github.com/hyperledger/fabric-contract-api-go/v2 v2.2.0/go.mod h1:FeWeO/jwGjiME7ak3GufqKIcwkejtzrDG4QxbfKydWs=
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 h1:YJrd+gMaeY0/vsN0aS0QkEKTivGoUnSRIXxGJ7KI+Pc=
github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4/go.mod h1:bau/6AJhvEcu9GKKYHlDXAxXKzYNfhP6xu2GXuxEcFk=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -27,55 +27,26 @@ package main
*/ */
import ( import (
"fmt" "fmt"
"log"
"strconv" "strconv"
"github.com/hyperledger/fabric-chaincode-go/shim" "github.com/hyperledger/fabric-contract-api-go/v2/contractapi"
pb "github.com/hyperledger/fabric-protos-go/peer"
) )
// SmartContract is the data structure which represents this contract and on which various contract lifecycle functions are attached func main() {
type SmartContract struct { chaincode, err := contractapi.NewChaincode(&SmartContract{})
} if err != nil {
log.Panicf("Error creating chaincode: %s", err)
// Define Status codes for the response
const (
OK = 200
ERROR = 500
)
// Init is called when the smart contract is instantiated
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}
// Invoke routes invocations to the appropriate function in chaincode
// Current supported invocations are:
// - update, adds a delta to an aggregate variable in the ledger, all variables are assumed to start at 0
// - get, retrieves the aggregate value of a variable in the ledger
// - prune, deletes all rows associated with the variable and replaces them with a single row containing the aggregate value
// - delete, removes all rows associated with the variable
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) pb.Response {
// Retrieve the requested Smart Contract function and arguments
function, args := APIstub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == "update" {
return s.update(APIstub, args)
} else if function == "get" {
return s.get(APIstub, args)
} else if function == "prune" {
return s.prune(APIstub, args)
} else if function == "delete" {
return s.delete(APIstub, args)
} else if function == "putstandard" {
return s.putStandard(APIstub, args)
} else if function == "getstandard" {
return s.getStandard(APIstub, args)
} else if function == "delstandard" {
return s.delStandard(APIstub, args)
} }
return shim.Error("Invalid Smart Contract function name.") if err := chaincode.Start(); err != nil {
log.Panicf("Error starting chaincode: %s", err)
}
}
// SmartContract is the data structure which represents this contract and its functions
type SmartContract struct {
contractapi.Contract
} }
/** /**
@ -86,47 +57,36 @@ func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) pb.Response
* - args[1] -> new delta (float) * - args[1] -> new delta (float)
* - args[2] -> operation (currently supported are addition "+" and subtraction "-") * - args[2] -> operation (currently supported are addition "+" and subtraction "-")
* *
* @param APIstub The chaincode shim * Returns a response indicating success or failure with a message.
* @param args The arguments array for the update invocation
*
* @return A response structure indicating success or failure with a message
*/ */
func (s *SmartContract) update(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) Update(ctx contractapi.TransactionContextInterface, name string, delta string, op string) (string, error) {
// Check we have a valid number of args _, err := strconv.ParseFloat(delta, 64)
if len(args) != 3 {
return shim.Error("Incorrect number of arguments, expecting 3")
}
// Extract the args
name := args[0]
op := args[2]
_, err := strconv.ParseFloat(args[1], 64)
if err != nil { if err != nil {
return shim.Error("Provided value was not a number") return "", fmt.Errorf("provided value was not a number: %s", delta)
} }
// Make sure a valid operator is provided // Make sure a valid operator is provided
if op != "+" && op != "-" { if op != "+" && op != "-" {
return shim.Error(fmt.Sprintf("Operator %s is unrecognized", op)) return "", fmt.Errorf("operator %s is unrecognized", op)
} }
// Retrieve info needed for the update procedure // Retrieve info needed for the update procedure
txid := APIstub.GetTxID() txid := ctx.GetStub().GetTxID()
compositeIndexName := "varName~op~value~txID" compositeIndexName := "varName~op~value~txID"
// Create the composite key that will allow us to query for all deltas on a particular variable // Create the composite key that will allow us to query for all deltas on a particular variable
compositeKey, compositeErr := APIstub.CreateCompositeKey(compositeIndexName, []string{name, op, args[1], txid}) compositeKey, compositeErr := ctx.GetStub().CreateCompositeKey(compositeIndexName, []string{name, op, delta, txid})
if compositeErr != nil { if compositeErr != nil {
return shim.Error(fmt.Sprintf("Could not create a composite key for %s: %s", name, compositeErr.Error())) return "", fmt.Errorf("could not create a composite key for %s: %w", name, compositeErr)
} }
// Save the composite key index // Save the composite key index
compositePutErr := APIstub.PutState(compositeKey, []byte{0x00}) compositePutErr := ctx.GetStub().PutState(compositeKey, []byte{0x00})
if compositePutErr != nil { if compositePutErr != nil {
return shim.Error(fmt.Sprintf("Could not put operation for %s in the ledger: %s", name, compositePutErr.Error())) return "", fmt.Errorf("could not put operation for %s in the ledger: %w", name, compositePutErr)
} }
return shim.Success([]byte(fmt.Sprintf("Successfully added %s%s to %s", op, args[1], name))) return fmt.Sprintf("Successfully added %s%s to %s", op, delta, name), nil
} }
/** /**
@ -135,44 +95,34 @@ func (s *SmartContract) update(APIstub shim.ChaincodeStubInterface, args []strin
* following argument: * following argument:
* - args[0] -> The name of the variable to get the value of * - args[0] -> The name of the variable to get the value of
* *
* @param APIstub The chaincode shim * Returns a response indicating success or failure with a message
* @param args The arguments array for the get invocation
*
* @return A response structure indicating success or failure with a message
*/ */
func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) Get(ctx contractapi.TransactionContextInterface, name string) (string, error) {
// Check we have a valid number of args
if len(args) != 1 {
return shim.Error("Incorrect number of arguments, expecting 1")
}
name := args[0]
// Get all deltas for the variable // Get all deltas for the variable
deltaResultsIterator, deltaErr := APIstub.GetStateByPartialCompositeKey("varName~op~value~txID", []string{name}) deltaResultsIterator, deltaErr := ctx.GetStub().GetStateByPartialCompositeKey("varName~op~value~txID", []string{name})
if deltaErr != nil { if deltaErr != nil {
return shim.Error(fmt.Sprintf("Could not retrieve value for %s: %s", name, deltaErr.Error())) return "", fmt.Errorf("could not retrieve value for %s: %w", name, deltaErr)
} }
defer deltaResultsIterator.Close() defer deltaResultsIterator.Close()
// Check the variable existed // Check the variable existed
if !deltaResultsIterator.HasNext() { if !deltaResultsIterator.HasNext() {
return shim.Error(fmt.Sprintf("No variable by the name %s exists", name)) return "", fmt.Errorf("no variable by the name %s exists", name)
} }
// Iterate through result set and compute final value // Iterate through result set and compute final value
var finalVal float64 var finalVal float64
var i int for deltaResultsIterator.HasNext() {
for i = 0; deltaResultsIterator.HasNext(); i++ {
// Get the next row // Get the next row
responseRange, nextErr := deltaResultsIterator.Next() responseRange, nextErr := deltaResultsIterator.Next()
if nextErr != nil { if nextErr != nil {
return shim.Error(nextErr.Error()) return "", nextErr
} }
// Split the composite key into its component parts // Split the composite key into its component parts
_, keyParts, splitKeyErr := APIstub.SplitCompositeKey(responseRange.Key) _, keyParts, splitKeyErr := ctx.GetStub().SplitCompositeKey(responseRange.Key)
if splitKeyErr != nil { if splitKeyErr != nil {
return shim.Error(splitKeyErr.Error()) return "", splitKeyErr
} }
// Retrieve the delta value and operation // Retrieve the delta value and operation
@ -182,7 +132,7 @@ func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string)
// Convert the value string and perform the operation // Convert the value string and perform the operation
value, convErr := strconv.ParseFloat(valueStr, 64) value, convErr := strconv.ParseFloat(valueStr, 64)
if convErr != nil { if convErr != nil {
return shim.Error(convErr.Error()) return "", convErr
} }
switch operation { switch operation {
@ -191,11 +141,11 @@ func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string)
case "-": case "-":
finalVal -= value finalVal -= value
default: default:
return shim.Error(fmt.Sprintf("Unrecognized operation %s", operation)) return "", fmt.Errorf("unrecognized operation %s", operation)
} }
} }
return shim.Success([]byte(strconv.FormatFloat(finalVal, 'f', -1, 64))) return strconv.FormatFloat(finalVal, 'f', -1, 64), nil
} }
/** /**
@ -204,46 +154,35 @@ func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string)
* computed value of the variable. The args array contains the following argument: * computed value of the variable. The args array contains the following argument:
* - args[0] -> The name of the variable to prune * - args[0] -> The name of the variable to prune
* *
* @param APIstub The chaincode shim * Returns a response indicating success or failure with a message
* @param args The args array for the prune invocation
*
* @return A response structure indicating success or failure with a message
*/ */
func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) Prune(ctx contractapi.TransactionContextInterface, name string) (string, error) {
// Check we have a valid number of ars
if len(args) != 1 {
return shim.Error("Incorrect number of arguments, expecting 1")
}
// Retrieve the name of the variable to prune
name := args[0]
// Get all delta rows for the variable // Get all delta rows for the variable
deltaResultsIterator, deltaErr := APIstub.GetStateByPartialCompositeKey("varName~op~value~txID", []string{name}) deltaResultsIterator, deltaErr := ctx.GetStub().GetStateByPartialCompositeKey("varName~op~value~txID", []string{name})
if deltaErr != nil { if deltaErr != nil {
return shim.Error(fmt.Sprintf("Could not retrieve value for %s: %s", name, deltaErr.Error())) return "", fmt.Errorf("could not retrieve value for %s: %w", name, deltaErr)
} }
defer deltaResultsIterator.Close() defer deltaResultsIterator.Close()
// Check the variable existed // Check the variable existed
if !deltaResultsIterator.HasNext() { if !deltaResultsIterator.HasNext() {
return shim.Error(fmt.Sprintf("No variable by the name %s exists", name)) return "", fmt.Errorf("no variable by the name %s exists", name)
} }
// Iterate through result set computing final value while iterating and deleting each key // Iterate through result set computing final value while iterating and deleting each key
var finalVal float64 var finalVal float64
var i int var i int
for i = 0; deltaResultsIterator.HasNext(); i++ { for ; deltaResultsIterator.HasNext(); i++ {
// Get the next row // Get the next row
responseRange, nextErr := deltaResultsIterator.Next() responseRange, nextErr := deltaResultsIterator.Next()
if nextErr != nil { if nextErr != nil {
return shim.Error(nextErr.Error()) return "", nextErr
} }
// Split the key into its composite parts // Split the key into its composite parts
_, keyParts, splitKeyErr := APIstub.SplitCompositeKey(responseRange.Key) _, keyParts, splitKeyErr := ctx.GetStub().SplitCompositeKey(responseRange.Key)
if splitKeyErr != nil { if splitKeyErr != nil {
return shim.Error(splitKeyErr.Error()) return "", splitKeyErr
} }
// Retrieve the operation and value // Retrieve the operation and value
@ -253,13 +192,13 @@ func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string
// Convert the value to a float // Convert the value to a float
value, convErr := strconv.ParseFloat(valueStr, 64) value, convErr := strconv.ParseFloat(valueStr, 64)
if convErr != nil { if convErr != nil {
return shim.Error(convErr.Error()) return "", convErr
} }
// Delete the row from the ledger // Delete the row from the ledger
deltaRowDelErr := APIstub.DelState(responseRange.Key) deltaRowDelErr := ctx.GetStub().DelState(responseRange.Key)
if deltaRowDelErr != nil { if deltaRowDelErr != nil {
return shim.Error(fmt.Sprintf("Could not delete delta row: %s", deltaRowDelErr.Error())) return "", fmt.Errorf("could not delete delta row: %w", deltaRowDelErr)
} }
// Add the value of the deleted row to the final aggregate // Add the value of the deleted row to the final aggregate
@ -269,17 +208,16 @@ func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string
case "-": case "-":
finalVal -= value finalVal -= value
default: default:
return shim.Error(fmt.Sprintf("Unrecognized operation %s", operation)) return "", fmt.Errorf("unrecognized operation %s", operation)
} }
} }
// Update the ledger with the final value // Update the ledger with the final value
updateResp := s.update(APIstub, []string{name, strconv.FormatFloat(finalVal, 'f', -1, 64), "+"}) if updateMessage, err := s.Update(ctx, name, strconv.FormatFloat(finalVal, 'f', -1, 64), "+"); err != nil {
if updateResp.Status == ERROR { return "", fmt.Errorf("could not update the final value of the variable after pruning: %s", updateMessage)
return shim.Error(fmt.Sprintf("Could not update the final value of the variable after pruning: %s", updateResp.Message))
} }
return shim.Success([]byte(fmt.Sprintf("Successfully pruned variable %s, final value is %f, %d rows pruned", args[0], finalVal, i))) return fmt.Sprintf("Successfully pruned variable %s, final value is %f, %d rows pruned", name, finalVal, i), nil
} }
/** /**
@ -287,110 +225,91 @@ func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string
* contains the following argument: * contains the following argument:
* - args[0] -> The name of the variable to delete * - args[0] -> The name of the variable to delete
* *
* @param APIstub The chaincode shim * Returns a response indicating success or failure with a message
* @param args The arguments array for the delete invocation
*
* @return A response structure indicating success or failure with a message
*/ */
func (s *SmartContract) delete(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) Delete(ctx contractapi.TransactionContextInterface, name string) (string, error) {
// Check there are a correct number of arguments
if len(args) != 1 {
return shim.Error("Incorrect number of arguments, expecting 1")
}
// Retrieve the variable name
name := args[0]
// Delete all delta rows // Delete all delta rows
deltaResultsIterator, deltaErr := APIstub.GetStateByPartialCompositeKey("varName~op~value~txID", []string{name}) deltaResultsIterator, deltaErr := ctx.GetStub().GetStateByPartialCompositeKey("varName~op~value~txID", []string{name})
if deltaErr != nil { if deltaErr != nil {
return shim.Error(fmt.Sprintf("Could not retrieve delta rows for %s: %s", name, deltaErr.Error())) return "", fmt.Errorf("could not retrieve delta rows for %s: %w", name, deltaErr)
} }
defer deltaResultsIterator.Close() defer deltaResultsIterator.Close()
// Ensure the variable exists // Ensure the variable exists
if !deltaResultsIterator.HasNext() { if !deltaResultsIterator.HasNext() {
return shim.Error(fmt.Sprintf("No variable by the name %s exists", name)) return "", fmt.Errorf("no variable by the name %s exists", name)
} }
// Iterate through result set and delete all indices // Iterate through result set and delete all indices
var i int var i int
for i = 0; deltaResultsIterator.HasNext(); i++ { for ; deltaResultsIterator.HasNext(); i++ {
responseRange, nextErr := deltaResultsIterator.Next() responseRange, nextErr := deltaResultsIterator.Next()
if nextErr != nil { if nextErr != nil {
return shim.Error(fmt.Sprintf("Could not retrieve next delta row: %s", nextErr.Error())) return "", fmt.Errorf("could not retrieve next delta row: %w", nextErr)
} }
deltaRowDelErr := APIstub.DelState(responseRange.Key) deltaRowDelErr := ctx.GetStub().DelState(responseRange.Key)
if deltaRowDelErr != nil { if deltaRowDelErr != nil {
return shim.Error(fmt.Sprintf("Could not delete delta row: %s", deltaRowDelErr.Error())) return "", fmt.Errorf("could not delete delta row: %w", deltaRowDelErr)
} }
} }
return shim.Success([]byte(fmt.Sprintf("Deleted %s, %d rows removed", name, i))) return fmt.Sprintf("Deleted %s, %d rows removed", name, i), nil
}
/**
* Converts a float64 to a byte array
*
* @param f The float64 to convert
*
* @return The byte array representation
*/
func f2barr(f float64) []byte {
str := strconv.FormatFloat(f, 'f', -1, 64)
return []byte(str)
}
// The main function is only relevant in unit test mode. Only included here for completeness.
func main() {
// Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
} }
/** /**
* All functions below this are for testing traditional editing of a single row * All functions below this are for testing traditional editing of a single row
*/ */
func (s *SmartContract) putStandard(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) UpdateStandard(ctx contractapi.TransactionContextInterface, name string, delta string, operation string) (float64, error) {
name := args[0] deltaValue, err := strconv.ParseFloat(delta, 64)
valStr := args[1] if err != nil {
return 0, err
_, getErr := APIstub.GetState(name)
if getErr != nil {
return shim.Error(fmt.Sprintf("Failed to retrieve the state of %s: %s", name, getErr.Error()))
} }
putErr := APIstub.PutState(name, []byte(valStr)) var currentValue float64
if putErr != nil { if valueBytes, err := ctx.GetStub().GetState(name); err == nil && len(valueBytes) > 0 {
return shim.Error(fmt.Sprintf("Failed to put state: %s", putErr.Error())) currentValue, err = strconv.ParseFloat(string(valueBytes), 64)
if err != nil {
return 0, err
}
} }
return shim.Success(nil) switch operation {
case "+":
currentValue += deltaValue
case "-":
currentValue -= deltaValue
default:
return 0, fmt.Errorf("unrecognized operation %s", operation)
}
valueStr := strconv.FormatFloat(currentValue, 'f', -1, 64)
if err := ctx.GetStub().PutState(name, []byte(valueStr)); err != nil {
return 0, fmt.Errorf("failed to put state: %w", err)
}
return currentValue, nil
} }
func (s *SmartContract) getStandard(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { func (s *SmartContract) GetStandard(ctx contractapi.TransactionContextInterface, name string) (string, error) {
name := args[0] valueBytes, err := ctx.GetStub().GetState(name)
if err != nil {
val, getErr := APIstub.GetState(name) return "", fmt.Errorf("failed to get state: %w", err)
if getErr != nil {
return shim.Error(fmt.Sprintf("Failed to get state: %s", getErr.Error()))
} }
return shim.Success(val) value, err := strconv.ParseFloat(string(valueBytes), 64)
} if err != nil {
return "", err
func (s *SmartContract) delStandard(APIstub shim.ChaincodeStubInterface, args []string) pb.Response {
name := args[0]
getErr := APIstub.DelState(name)
if getErr != nil {
return shim.Error(fmt.Sprintf("Failed to delete state: %s", getErr.Error()))
} }
return shim.Success(nil) return strconv.FormatFloat(value, 'f', -1, 64), nil
}
func (s *SmartContract) DelStandard(ctx contractapi.TransactionContextInterface, name string) error {
if err := ctx.GetStub().DelState(name); err != nil {
return fmt.Errorf("failed to delete state: %w", err)
}
return nil
} }

View file

@ -11,7 +11,3 @@ set -ex
pushd ../test-network pushd ../test-network
./network.sh down ./network.sh down
popd popd
rm -rf application-go/wallet/
rm -rf application-go/keystore/

View file

@ -19,7 +19,7 @@ pushd ../test-network
echo "Bring up test network" echo "Bring up test network"
./network.sh up createChannel -ca ./network.sh up createChannel -ca
./network.sh deployCC -ccn bigdatacc -ccp ../high-throughput/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')" -cci Init ./network.sh deployCC -ccn bigdatacc -ccp ../high-throughput/chaincode-go/ -ccl go -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
popd popd
cat <<EOF cat <<EOF