mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
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:
parent
ad1950554d
commit
6130e1b9e8
13 changed files with 297 additions and 379 deletions
9
.github/workflows/test-high-throughput.yaml
vendored
9
.github/workflows/test-high-throughput.yaml
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Println("Value of variable", string(variableName), ": ", string(result))
|
|
||||||
|
|
||||||
} else if function == "delete" || function == "prune" || function == "delstandard" {
|
func update(contract *client.Contract, variableName, change, sign string) {
|
||||||
result, err := f.DeletePrune(contract, function, variableName)
|
result, err := contract.SubmitTransaction("Update", variableName, change, sign)
|
||||||
if err != nil {
|
failOnError(err)
|
||||||
log.Fatalf("error: %v", err)
|
|
||||||
}
|
|
||||||
log.Println(string(result))
|
log.Println(string(result))
|
||||||
} else if function == "get" || function == "getstandard" {
|
get(contract, variableName)
|
||||||
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" {
|
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...")
|
log.Println("submitting 1000 concurrent updates...")
|
||||||
result, err := f.ManyUpdates(contract, "update", variableName, change, sign)
|
|
||||||
|
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 {
|
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)
|
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
chaincode, err := contractapi.NewChaincode(&SmartContract{})
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("Error creating chaincode: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
type SmartContract struct {
|
||||||
}
|
contractapi.Contract
|
||||||
|
|
||||||
// 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.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SmartContract) getStandard(APIstub shim.ChaincodeStubInterface, args []string) pb.Response {
|
valueStr := strconv.FormatFloat(currentValue, 'f', -1, 64)
|
||||||
name := args[0]
|
|
||||||
|
|
||||||
val, getErr := APIstub.GetState(name)
|
if err := ctx.GetStub().PutState(name, []byte(valueStr)); err != nil {
|
||||||
if getErr != nil {
|
return 0, fmt.Errorf("failed to put state: %w", err)
|
||||||
return shim.Error(fmt.Sprintf("Failed to get state: %s", getErr.Error()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shim.Success(val)
|
return currentValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SmartContract) delStandard(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 {
|
||||||
getErr := APIstub.DelState(name)
|
return "", fmt.Errorf("failed to get state: %w", err)
|
||||||
if getErr != nil {
|
|
||||||
return shim.Error(fmt.Sprintf("Failed to delete state: %s", getErr.Error()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shim.Success(nil)
|
value, err := strconv.ParseFloat(string(valueBytes), 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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/
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue