mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 07:25:10 +00:00
Implement caching
Added caching util function with tests and applied in: - parser.Block.Transactions(), - parser.Payload.ChannelHeader(), - parser.Payload.SignatureHeader(), - parser.NamespaceReadWriteSet.ReadWriteSet(), - parser.EndorserTransaction.ReadWriteSets(), methods, as it was in the typescript sample. Corrected Println usage and added comments to util functions. Signed-off-by: Stanislav Jakuschevskij <stas@two-giants.com>
This commit is contained in:
parent
095bf304b4
commit
c4db079e0c
12 changed files with 158 additions and 73 deletions
|
|
@ -33,7 +33,7 @@ func main() {
|
|||
printUsage()
|
||||
panic(fmt.Errorf("unknown command: %s", name))
|
||||
}
|
||||
fmt.Printf("command: %s\n", name)
|
||||
fmt.Println("command:", name)
|
||||
}
|
||||
|
||||
client := newGrpcConnection()
|
||||
|
|
@ -47,7 +47,7 @@ func main() {
|
|||
|
||||
func printUsage() {
|
||||
fmt.Println("Arguments: <command1> [<command2> ...]")
|
||||
fmt.Printf("Available commands: %v\n", availableCommands())
|
||||
fmt.Println("Available commands:", availableCommands())
|
||||
}
|
||||
|
||||
func availableCommands() string {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ func getAllAssets(clientConnection *grpc.ClientConn) {
|
|||
smartContract := atb.NewAssetTransferBasic(contract)
|
||||
assets := smartContract.GetAllAssets()
|
||||
|
||||
fmt.Printf("%s\n", formatJSON(assets))
|
||||
fmt.Println(formatJSON(assets))
|
||||
}
|
||||
|
||||
func formatJSON(data []byte) string {
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ func listen(clientConnection *grpc.ClientConn) {
|
|||
}
|
||||
defer checkpointer.Close()
|
||||
|
||||
fmt.Printf("Start event listening from block %d\n", checkpointer.BlockNumber())
|
||||
fmt.Printf("Last processed transaction ID within block: %s\n", checkpointer.TransactionID())
|
||||
fmt.Println("Start event listening from block", checkpointer.BlockNumber())
|
||||
fmt.Println("Last processed transaction ID within block:", checkpointer.TransactionID())
|
||||
if simulatedFailureCount > 0 {
|
||||
fmt.Printf("Simulating a write failure every %d transactions\n", simulatedFailureCount)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,23 +22,38 @@ func (b *Block) Number() uint64 {
|
|||
return header.GetNumber()
|
||||
}
|
||||
|
||||
// TODO: needs cache
|
||||
func (b *Block) Transactions() []*Transaction {
|
||||
envelopes := b.unmarshalEnvelopesFromBlockData()
|
||||
return utils.Cache(func() []*Transaction {
|
||||
envelopes := b.unmarshalEnvelopesFromBlockData()
|
||||
|
||||
commonPayloads := b.unmarshalPayloadsFrom(envelopes)
|
||||
commonPayloads := b.unmarshalPayloadsFrom(envelopes)
|
||||
|
||||
payloads := b.parse(commonPayloads)
|
||||
payloads := b.parse(commonPayloads)
|
||||
|
||||
result := b.createTransactionsFrom(payloads)
|
||||
return b.createTransactionsFrom(payloads)
|
||||
})()
|
||||
}
|
||||
|
||||
func (b *Block) unmarshalEnvelopesFromBlockData() []*common.Envelope {
|
||||
result := []*common.Envelope{}
|
||||
for _, blockData := range b.block.GetData().GetData() {
|
||||
envelope := &common.Envelope{}
|
||||
if err := proto.Unmarshal(blockData, envelope); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result = append(result, envelope)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (*Block) createTransactionsFrom(payloads []*PayloadImpl) []*Transaction {
|
||||
result := []*Transaction{}
|
||||
for _, payload := range payloads {
|
||||
result = append(result, NewTransaction(payload))
|
||||
func (*Block) unmarshalPayloadsFrom(envelopes []*common.Envelope) []*common.Payload {
|
||||
result := []*common.Payload{}
|
||||
for _, envelope := range envelopes {
|
||||
commonPayload := &common.Payload{}
|
||||
if err := proto.Unmarshal(envelope.GetPayload(), commonPayload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result = append(result, commonPayload)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -62,30 +77,6 @@ func (b *Block) parse(commonPayloads []*common.Payload) []*PayloadImpl {
|
|||
return result
|
||||
}
|
||||
|
||||
func (*Block) unmarshalPayloadsFrom(envelopes []*common.Envelope) []*common.Payload {
|
||||
result := []*common.Payload{}
|
||||
for _, envelope := range envelopes {
|
||||
commonPayload := &common.Payload{}
|
||||
if err := proto.Unmarshal(envelope.GetPayload(), commonPayload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result = append(result, commonPayload)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *Block) unmarshalEnvelopesFromBlockData() []*common.Envelope {
|
||||
result := []*common.Envelope{}
|
||||
for _, blockData := range b.block.GetData().GetData() {
|
||||
envelope := &common.Envelope{}
|
||||
if err := proto.Unmarshal(blockData, envelope); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result = append(result, envelope)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *Block) extractTransactionValidationCodes() []byte {
|
||||
metadata := utils.AssertDefined(
|
||||
b.block.GetMetadata(),
|
||||
|
|
@ -98,6 +89,14 @@ func (b *Block) extractTransactionValidationCodes() []byte {
|
|||
)
|
||||
}
|
||||
|
||||
func (*Block) createTransactionsFrom(payloads []*PayloadImpl) []*Transaction {
|
||||
result := []*Transaction{}
|
||||
for _, payload := range payloads {
|
||||
result = append(result, NewTransaction(payload))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO remove unused?
|
||||
func (b *Block) ToProto() *common.Block {
|
||||
return b.block
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import (
|
|||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
func TestGetReadWriteSetsFromEndorserTransaction(t *testing.T) {
|
||||
func Test_GetReadWriteSetsFromEndorserTransaction(t *testing.T) {
|
||||
nsReadWriteSetFake, expectedNamespace, expectedAsset := nsReadWriteSetFake()
|
||||
|
||||
transaction := &peer.Transaction{
|
||||
|
|
@ -73,7 +73,7 @@ func assertReadWriteSet(
|
|||
}
|
||||
}
|
||||
|
||||
func TestReadWriteSetWrapping(t *testing.T) {
|
||||
func Test_ReadWriteSetWrapping(t *testing.T) {
|
||||
nsReadWriteSetFake, _, _ := nsReadWriteSetFake()
|
||||
|
||||
txReadWriteSetFake := &rwset.TxReadWriteSet{
|
||||
|
|
@ -86,7 +86,7 @@ func TestReadWriteSetWrapping(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNamespaceReadWriteSetParsing(t *testing.T) {
|
||||
func Test_NamespaceReadWriteSetParsing(t *testing.T) {
|
||||
nsReadWriteSetFake, expectedNamespace, expectedAsset := nsReadWriteSetFake()
|
||||
|
||||
parsedNsRwSet := parser.ParseNamespaceReadWriteSet(nsReadWriteSetFake)
|
||||
|
|
|
|||
|
|
@ -30,15 +30,16 @@ func ParsePayload(payload *common.Payload, statusCode int32) *PayloadImpl {
|
|||
}
|
||||
|
||||
func (p *PayloadImpl) ChannelHeader() *common.ChannelHeader {
|
||||
header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header")
|
||||
return utils.Cache(func() *common.ChannelHeader {
|
||||
header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header")
|
||||
|
||||
// TODO add cache, return cachedChannelHeader like in blockParser.ts:77
|
||||
result := &common.ChannelHeader{}
|
||||
if err := proto.Unmarshal(header.GetChannelHeader(), result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result := &common.ChannelHeader{}
|
||||
if err := proto.Unmarshal(header.GetChannelHeader(), result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return result
|
||||
return result
|
||||
})()
|
||||
}
|
||||
|
||||
func (p *PayloadImpl) EndorserTransaction() EndorserTransaction {
|
||||
|
|
@ -55,15 +56,16 @@ func (p *PayloadImpl) EndorserTransaction() EndorserTransaction {
|
|||
}
|
||||
|
||||
func (p *PayloadImpl) SignatureHeader() *common.SignatureHeader {
|
||||
header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header")
|
||||
return utils.Cache(func() *common.SignatureHeader {
|
||||
header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header")
|
||||
|
||||
// TODO add cache, return cachedSignatureHeader like in blockParser.ts:77
|
||||
result := &common.SignatureHeader{}
|
||||
if err := proto.Unmarshal(header.GetSignatureHeader(), result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result := &common.SignatureHeader{}
|
||||
if err := proto.Unmarshal(header.GetSignatureHeader(), result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return result
|
||||
return result
|
||||
})()
|
||||
}
|
||||
|
||||
func (p *PayloadImpl) TransactionValidationCode() int32 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"offChainData/utils"
|
||||
|
||||
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset"
|
||||
"github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
|
@ -53,14 +55,15 @@ func (p *NamespaceReadWriteSetImpl) Namespace() string {
|
|||
return p.nsReadWriteSet.GetNamespace()
|
||||
}
|
||||
|
||||
// TODO add cache
|
||||
func (p *NamespaceReadWriteSetImpl) ReadWriteSet() *kvrwset.KVRWSet {
|
||||
result := kvrwset.KVRWSet{}
|
||||
if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return utils.Cache(func() *kvrwset.KVRWSet {
|
||||
result := kvrwset.KVRWSet{}
|
||||
if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), &result); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &result
|
||||
return &result
|
||||
})()
|
||||
}
|
||||
|
||||
// TODO remove unused
|
||||
|
|
|
|||
|
|
@ -71,21 +71,20 @@ func ParseEndorserTransaction(transaction *peer.Transaction) *EndorserTransactio
|
|||
return &EndorserTransactionImpl{transaction}
|
||||
}
|
||||
|
||||
// TODO add cache
|
||||
func (p *EndorserTransactionImpl) ReadWriteSets() []ReadWriteSet {
|
||||
chaincodeActionPayloads := p.unmarshalChaincodeActionPayloads()
|
||||
return utils.Cache(func() []ReadWriteSet {
|
||||
chaincodeActionPayloads := p.unmarshalChaincodeActionPayloads()
|
||||
|
||||
chaincodeEndorsedActions := p.extractChaincodeEndorsedActionsFrom(chaincodeActionPayloads)
|
||||
chaincodeEndorsedActions := p.extractChaincodeEndorsedActionsFrom(chaincodeActionPayloads)
|
||||
|
||||
proposalResponsePayloads := p.unmarshalProposalResponsePayloadsFrom(chaincodeEndorsedActions)
|
||||
proposalResponsePayloads := p.unmarshalProposalResponsePayloadsFrom(chaincodeEndorsedActions)
|
||||
|
||||
chaincodeActions := p.unmarshalChaincodeActionsFrom(proposalResponsePayloads)
|
||||
chaincodeActions := p.unmarshalChaincodeActionsFrom(proposalResponsePayloads)
|
||||
|
||||
txReadWriteSets := p.unmarshalTxReadWriteSetsFrom(chaincodeActions)
|
||||
txReadWriteSets := p.unmarshalTxReadWriteSetsFrom(chaincodeActions)
|
||||
|
||||
parsedReadWriteSets := p.parseReadWriteSets(txReadWriteSets)
|
||||
|
||||
return parsedReadWriteSets
|
||||
return p.parseReadWriteSets(txReadWriteSets)
|
||||
})()
|
||||
}
|
||||
|
||||
func (p *EndorserTransactionImpl) unmarshalChaincodeActionPayloads() []*peer.ChaincodeActionPayload {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func (t *transactApp) transact() {
|
|||
anAsset := atb.NewAsset()
|
||||
|
||||
t.smartContract.CreateAsset(anAsset)
|
||||
fmt.Printf("\nCreated asset %s\n", anAsset.ID)
|
||||
fmt.Println("Created asset", anAsset.ID)
|
||||
|
||||
// Transfer randomly 1 in 2 assets to a new owner.
|
||||
if utils.RandomInt(2) == 0 {
|
||||
|
|
@ -56,6 +56,6 @@ func (t *transactApp) transact() {
|
|||
// Delete randomly 1 in 4 created assets.
|
||||
if utils.RandomInt(4) == 0 {
|
||||
t.smartContract.DeleteAsset(anAsset.ID)
|
||||
fmt.Printf("Deleted asset %s\n", anAsset.ID)
|
||||
fmt.Println("Deleted asset", anAsset.ID)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,13 @@ import (
|
|||
"math/big"
|
||||
)
|
||||
|
||||
// Pick a random element from an array.
|
||||
func RandomElement(values []string) string {
|
||||
result := values[RandomInt(len(values))]
|
||||
return result
|
||||
}
|
||||
|
||||
// Generate a random integer in the range 0 to max - 1.
|
||||
func RandomInt(max int) int {
|
||||
result, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
if err != nil {
|
||||
|
|
@ -20,6 +22,7 @@ func RandomInt(max int) int {
|
|||
return int(result.Int64())
|
||||
}
|
||||
|
||||
// Pick a random element from an array, excluding the current value.
|
||||
func DifferentElement(values []string, currentValue string) string {
|
||||
candidateValues := []string{}
|
||||
for _, v := range values {
|
||||
|
|
@ -30,6 +33,7 @@ func DifferentElement(values []string, currentValue string) string {
|
|||
return RandomElement(candidateValues)
|
||||
}
|
||||
|
||||
// Return the value if it is defined; otherwise panics with an error message.
|
||||
func AssertDefined[T any](value T, message string) T {
|
||||
if any(value) == any(nil) {
|
||||
panic(errors.New(message))
|
||||
|
|
@ -37,3 +41,17 @@ func AssertDefined[T any](value T, message string) T {
|
|||
|
||||
return value
|
||||
}
|
||||
|
||||
// Wrap a function call with a cache. On first call the wrapped function is invoked to
|
||||
// obtain a result. Subsequent calls return the cached result.
|
||||
func Cache[T any](f func() T) func() T {
|
||||
value := any(nil)
|
||||
|
||||
return func() T {
|
||||
if value == nil {
|
||||
value = f()
|
||||
}
|
||||
|
||||
return value.(T)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
64
off_chain_data/application-go/utils/utils_test.go
Normal file
64
off_chain_data/application-go/utils/utils_test.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package utils_test
|
||||
|
||||
import (
|
||||
"offChainData/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_cachePrimitiveFunctionResult(t *testing.T) {
|
||||
counter := 0
|
||||
f := func() int {
|
||||
counter++
|
||||
return 5
|
||||
}
|
||||
|
||||
cachedFunc := utils.Cache(f)
|
||||
result1 := cachedFunc()
|
||||
result2 := cachedFunc()
|
||||
|
||||
if counter != 1 {
|
||||
t.Error("expected counter to be 1, but got", counter)
|
||||
}
|
||||
|
||||
if result1 != 5 || result2 != 5 {
|
||||
t.Fatal("expected results to be 5, but got", result1, result2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cacheWrappedPrimitiveFunctionResult(t *testing.T) {
|
||||
controlValue := 5
|
||||
multiplyControlValueBy := func(n int) int { controlValue *= n; return controlValue }
|
||||
|
||||
cachedFunc := utils.Cache(func() int { return multiplyControlValueBy(5) })
|
||||
result1 := cachedFunc()
|
||||
result2 := cachedFunc()
|
||||
|
||||
if controlValue != 25 {
|
||||
t.Error("expected control value to be 25, but got", controlValue)
|
||||
}
|
||||
|
||||
if result1 != 25 || result2 != 25 {
|
||||
t.Fatal("expected cached results to be 25, but got", result1, result2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cacheWrappedDataStructureResult(t *testing.T) {
|
||||
type GreetMe struct {
|
||||
helloTo string
|
||||
}
|
||||
|
||||
controlStruct := &GreetMe{helloTo: "Hello "}
|
||||
greet := func(name string) *GreetMe { controlStruct.helloTo += name; return controlStruct }
|
||||
|
||||
cachedFunc := utils.Cache(func() *GreetMe { return greet("John Doe") })
|
||||
result1 := cachedFunc().helloTo
|
||||
result2 := cachedFunc().helloTo
|
||||
|
||||
if controlStruct.helloTo != "Hello John Doe" {
|
||||
t.Error("expected control value to be 'Hello John Doe', but got", controlStruct)
|
||||
}
|
||||
|
||||
if result1 != "Hello John Doe" || result2 != "Hello John Doe" {
|
||||
t.Fatal("expected cached results to be 'Hello John Doe', but got", result1, result2)
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ export function assertDefined<T>(value: T | null | undefined, message: string):
|
|||
*/
|
||||
export function cache<T>(f: () => T): () => T {
|
||||
let value: T | undefined;
|
||||
return () => {
|
||||
return (): T => {
|
||||
if (value === undefined) {
|
||||
value = f();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue