diff --git a/.github/workflows/test-high-throughput.yaml b/.github/workflows/test-high-throughput.yaml index 0241cc38..b249f8f1 100644 --- a/.github/workflows/test-high-throughput.yaml +++ b/.github/workflows/test-high-throughput.yaml @@ -35,9 +35,14 @@ jobs: go run . manyUpdates testvar1 100 + go run . prune 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 . delete testvar2 + go run . getstandard testvar2 + go run . updatestandard testvar2 100 + + go run . getstandard testvar2 + go run . delstandard testvar2 - name: Stop Fabric working-directory: high-throughput diff --git a/high-throughput/README.md b/high-throughput/README.md index ec7d329b..35ca8019 100644 --- a/high-throughput/README.md +++ b/high-throughput/README.md @@ -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 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 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 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 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`: -``` -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 + ``` -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: diff --git a/high-throughput/application-go/app.go b/high-throughput/application-go/app.go index a4a3a347..c0e1c5dc 100644 --- a/high-throughput/application-go/app.go +++ b/high-throughput/application-go/app.go @@ -9,11 +9,14 @@ package main import ( "log" "os" + "sync" + "sync/atomic" "time" "github.com/hyperledger/fabric-gateway/pkg/client" "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() { @@ -22,7 +25,7 @@ func main() { if len(os.Args) <= 2 { 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 { log.Fatalf("error: provide value and operation") } else if len(os.Args) == 3 { @@ -56,38 +59,117 @@ func main() { contract := gateway.GetNetwork("mychannel").GetContract("bigdatacc") // Handle different functions - if function == "update" { - result, err := f.Update(contract, function, variableName, change, sign) - if err != nil { - log.Fatalf("error: %v", err) - } - log.Println("Value of variable", string(variableName), ": ", string(result)) - - } else if function == "delete" || function == "prune" || function == "delstandard" { - result, err := f.DeletePrune(contract, function, variableName) - if err != nil { - log.Fatalf("error: %v", err) - } - log.Println(string(result)) - } else if function == "get" || function == "getstandard" { - result, err := f.Query(contract, function, variableName) - if err != nil { - log.Fatalf("error: %v", err) - } - log.Println("Value of variable", string(variableName), ": ", string(result)) - } else if function == "manyUpdates" { - log.Println("submitting 1000 concurrent updates...") - result, err := f.ManyUpdates(contract, "update", variableName, change, sign) - if err != nil { - 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)) + switch function { + case "update": + update(contract, variableName, change, sign) + case "updatestandard": + updateStandard(contract, variableName, change, sign) + case "delete": + delete(contract, variableName) + case "prune": + prune(contract, variableName) + case "delstandard": + delStandard(contract, variableName) + case "get": + get(contract, variableName) + case "getstandard": + getStandard(contract, variableName) + case "manyUpdates": + manyUpdates(contract, "Update", variableName, change, sign) + get(contract, variableName) + case "manyUpdatesTraditional": + manyUpdates(contract, "UpdateStandard", variableName, change, sign) + getStandard(contract, variableName) + default: + log.Fatalln("Unkown function:", function) } } + +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) +} diff --git a/high-throughput/application-go/functions/deletePrune.go b/high-throughput/application-go/functions/deletePrune.go deleted file mode 100644 index 065b332e..00000000 --- a/high-throughput/application-go/functions/deletePrune.go +++ /dev/null @@ -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 -} diff --git a/high-throughput/application-go/functions/manyUpdates.go b/high-throughput/application-go/functions/manyUpdates.go deleted file mode 100644 index 8f3983a5..00000000 --- a/high-throughput/application-go/functions/manyUpdates.go +++ /dev/null @@ -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 -} diff --git a/high-throughput/application-go/functions/query.go b/high-throughput/application-go/functions/query.go deleted file mode 100644 index 00c8d449..00000000 --- a/high-throughput/application-go/functions/query.go +++ /dev/null @@ -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 -} diff --git a/high-throughput/application-go/functions/update.go b/high-throughput/application-go/functions/update.go deleted file mode 100644 index f1635a00..00000000 --- a/high-throughput/application-go/functions/update.go +++ /dev/null @@ -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 -} diff --git a/high-throughput/application-go/go.mod b/high-throughput/application-go/go.mod index b82059aa..491adda8 100644 --- a/high-throughput/application-go/go.mod +++ b/high-throughput/application-go/go.mod @@ -4,11 +4,11 @@ go 1.23.0 require ( 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 ) require ( - github.com/hyperledger/fabric-protos-go-apiv2 v0.3.7 // indirect github.com/miekg/pkcs11 v1.1.1 // indirect golang.org/x/crypto v0.40.0 // indirect golang.org/x/net v0.42.0 // indirect diff --git a/high-throughput/chaincode-go/go.mod b/high-throughput/chaincode-go/go.mod index 56dd5106..6174251e 100644 --- a/high-throughput/chaincode-go/go.mod +++ b/high-throughput/chaincode-go/go.mod @@ -2,17 +2,25 @@ module github.com/hyperledger/fabric-samples/high-throughput/chaincode go 1.23.0 -require ( - github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a - github.com/hyperledger/fabric-protos-go v0.3.0 -) +require github.com/hyperledger/fabric-contract-api-go/v2 v2.2.0 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/sys v0.34.0 // indirect golang.org/x/text v0.27.0 // 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 + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/high-throughput/chaincode-go/go.sum b/high-throughput/chaincode-go/go.sum index 3a65d90e..2752f8ee 100644 --- a/high-throughput/chaincode-go/go.sum +++ b/high-throughput/chaincode-go/go.sum @@ -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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a h1:HwSCxEeiBthwcazcAykGATQ36oG9M+HEQvGLvB7aLvA= -github.com/hyperledger/fabric-chaincode-go v0.0.0-20230228194215-b84622ba6a7a/go.mod h1:TDSu9gxURldEnaGSFbH1eMlfSQBWQcMQfnDBcpQv5lU= -github.com/hyperledger/fabric-protos-go v0.3.0 h1:MXxy44WTMENOh5TI8+PCK2x6pMj47Go2vFRKDHB2PZs= -github.com/hyperledger/fabric-protos-go v0.3.0/go.mod h1:WWnyWP40P2roPmmvxsUXSvVI/CF6vwY1K1UFidnKBys= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +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/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= 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/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/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -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/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/high-throughput/chaincode-go/high-throughput.go b/high-throughput/chaincode-go/high-throughput.go index 3651d716..95988e48 100644 --- a/high-throughput/chaincode-go/high-throughput.go +++ b/high-throughput/chaincode-go/high-throughput.go @@ -27,55 +27,26 @@ package main */ import ( "fmt" + "log" "strconv" - "github.com/hyperledger/fabric-chaincode-go/shim" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-contract-api-go/v2/contractapi" ) -// SmartContract is the data structure which represents this contract and on which various contract lifecycle functions are attached -type SmartContract struct { -} - -// 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) +func main() { + chaincode, err := contractapi.NewChaincode(&SmartContract{}) + if err != nil { + log.Panicf("Error creating chaincode: %s", err) } - 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[2] -> operation (currently supported are addition "+" and subtraction "-") * - * @param APIstub The chaincode shim - * @param args The arguments array for the update invocation - * - * @return A response structure indicating success or failure with a message + * Returns a response indicating success or failure with a message. */ -func (s *SmartContract) update(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { - // Check we have a valid number of args - 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) +func (s *SmartContract) Update(ctx contractapi.TransactionContextInterface, name string, delta string, op string) (string, error) { + _, err := strconv.ParseFloat(delta, 64) 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 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 - txid := APIstub.GetTxID() + txid := ctx.GetStub().GetTxID() compositeIndexName := "varName~op~value~txID" // 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 { - 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 - compositePutErr := APIstub.PutState(compositeKey, []byte{0x00}) + compositePutErr := ctx.GetStub().PutState(compositeKey, []byte{0x00}) 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: * - args[0] -> The name of the variable to get the value of * - * @param APIstub The chaincode shim - * @param args The arguments array for the get invocation - * - * @return A response structure indicating success or failure with a message + * Returns a response indicating success or failure with a message */ -func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { - // Check we have a valid number of args - if len(args) != 1 { - return shim.Error("Incorrect number of arguments, expecting 1") - } - - name := args[0] +func (s *SmartContract) Get(ctx contractapi.TransactionContextInterface, name string) (string, error) { // 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 { - 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() // Check the variable existed 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 var finalVal float64 - var i int - for i = 0; deltaResultsIterator.HasNext(); i++ { + for deltaResultsIterator.HasNext() { // Get the next row responseRange, nextErr := deltaResultsIterator.Next() if nextErr != nil { - return shim.Error(nextErr.Error()) + return "", nextErr } // Split the composite key into its component parts - _, keyParts, splitKeyErr := APIstub.SplitCompositeKey(responseRange.Key) + _, keyParts, splitKeyErr := ctx.GetStub().SplitCompositeKey(responseRange.Key) if splitKeyErr != nil { - return shim.Error(splitKeyErr.Error()) + return "", splitKeyErr } // 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 value, convErr := strconv.ParseFloat(valueStr, 64) if convErr != nil { - return shim.Error(convErr.Error()) + return "", convErr } switch operation { @@ -191,11 +141,11 @@ func (s *SmartContract) get(APIstub shim.ChaincodeStubInterface, args []string) case "-": finalVal -= value 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: * - args[0] -> The name of the variable to prune * - * @param APIstub The chaincode shim - * @param args The args array for the prune invocation - * - * @return A response structure indicating success or failure with a message + * Returns a response indicating success or failure with a message */ -func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { - // 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] - +func (s *SmartContract) Prune(ctx contractapi.TransactionContextInterface, name string) (string, error) { // 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 { - 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() // Check the variable existed 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 var finalVal float64 var i int - for i = 0; deltaResultsIterator.HasNext(); i++ { + for ; deltaResultsIterator.HasNext(); i++ { // Get the next row responseRange, nextErr := deltaResultsIterator.Next() if nextErr != nil { - return shim.Error(nextErr.Error()) + return "", nextErr } // Split the key into its composite parts - _, keyParts, splitKeyErr := APIstub.SplitCompositeKey(responseRange.Key) + _, keyParts, splitKeyErr := ctx.GetStub().SplitCompositeKey(responseRange.Key) if splitKeyErr != nil { - return shim.Error(splitKeyErr.Error()) + return "", splitKeyErr } // Retrieve the operation and value @@ -253,13 +192,13 @@ func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string // Convert the value to a float value, convErr := strconv.ParseFloat(valueStr, 64) if convErr != nil { - return shim.Error(convErr.Error()) + return "", convErr } // Delete the row from the ledger - deltaRowDelErr := APIstub.DelState(responseRange.Key) + deltaRowDelErr := ctx.GetStub().DelState(responseRange.Key) 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 @@ -269,17 +208,16 @@ func (s *SmartContract) prune(APIstub shim.ChaincodeStubInterface, args []string case "-": finalVal -= value default: - return shim.Error(fmt.Sprintf("Unrecognized operation %s", operation)) + return "", fmt.Errorf("unrecognized operation %s", operation) } } // Update the ledger with the final value - updateResp := s.update(APIstub, []string{name, strconv.FormatFloat(finalVal, 'f', -1, 64), "+"}) - if updateResp.Status == ERROR { - return shim.Error(fmt.Sprintf("Could not update the final value of the variable after pruning: %s", updateResp.Message)) + if updateMessage, err := s.Update(ctx, name, strconv.FormatFloat(finalVal, 'f', -1, 64), "+"); err != nil { + return "", fmt.Errorf("could not update the final value of the variable after pruning: %s", updateMessage) } - 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: * - args[0] -> The name of the variable to delete * - * @param APIstub The chaincode shim - * @param args The arguments array for the delete invocation - * - * @return A response structure indicating success or failure with a message + * Returns a response indicating success or failure with a message */ -func (s *SmartContract) delete(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { - // 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] - +func (s *SmartContract) Delete(ctx contractapi.TransactionContextInterface, name string) (string, error) { // 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 { - 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() // Ensure the variable exists 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 var i int - for i = 0; deltaResultsIterator.HasNext(); i++ { + for ; deltaResultsIterator.HasNext(); i++ { responseRange, nextErr := deltaResultsIterator.Next() 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 { - 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))) -} - -/** - * 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) - } + return fmt.Sprintf("Deleted %s, %d rows removed", name, i), nil } /** * All functions below this are for testing traditional editing of a single row */ -func (s *SmartContract) putStandard(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { - name := args[0] - valStr := args[1] - - _, getErr := APIstub.GetState(name) - if getErr != nil { - return shim.Error(fmt.Sprintf("Failed to retrieve the state of %s: %s", name, getErr.Error())) +func (s *SmartContract) UpdateStandard(ctx contractapi.TransactionContextInterface, name string, delta string, operation string) (float64, error) { + deltaValue, err := strconv.ParseFloat(delta, 64) + if err != nil { + return 0, err } - putErr := APIstub.PutState(name, []byte(valStr)) - if putErr != nil { - return shim.Error(fmt.Sprintf("Failed to put state: %s", putErr.Error())) + var currentValue float64 + if valueBytes, err := ctx.GetStub().GetState(name); err == nil && len(valueBytes) > 0 { + 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 { - name := args[0] - - val, getErr := APIstub.GetState(name) - if getErr != nil { - return shim.Error(fmt.Sprintf("Failed to get state: %s", getErr.Error())) +func (s *SmartContract) GetStandard(ctx contractapi.TransactionContextInterface, name string) (string, error) { + valueBytes, err := ctx.GetStub().GetState(name) + if err != nil { + return "", fmt.Errorf("failed to get state: %w", err) } - return shim.Success(val) -} - -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())) + value, err := strconv.ParseFloat(string(valueBytes), 64) + if err != nil { + return "", err } - 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 } diff --git a/high-throughput/networkDown.sh b/high-throughput/networkDown.sh index 20ea8007..453e4d38 100755 --- a/high-throughput/networkDown.sh +++ b/high-throughput/networkDown.sh @@ -11,7 +11,3 @@ set -ex pushd ../test-network ./network.sh down popd - - -rm -rf application-go/wallet/ -rm -rf application-go/keystore/ \ No newline at end of file diff --git a/high-throughput/startFabric.sh b/high-throughput/startFabric.sh index 06892e70..a32c9cdc 100755 --- a/high-throughput/startFabric.sh +++ b/high-throughput/startFabric.sh @@ -19,7 +19,7 @@ pushd ../test-network echo "Bring up test network" ./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 cat <