Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚧 WIP: Add off-chain-data go client application 🚧 #1269

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

twoGiants
Copy link
Contributor

@twoGiants twoGiants commented Nov 11, 2024

Don't close the PR. I am working on it. New commits are coming CW 52.

Description to be added...

@twoGiants twoGiants force-pushed the off-chain-data-go branch 3 times, most recently from 45a2aac to 09402bc Compare November 15, 2024 18:02
@twoGiants twoGiants force-pushed the off-chain-data-go branch 3 times, most recently from f7ede20 to 4e58c31 Compare November 25, 2024 14:56
Created project structure, fixed typos. Implemented connect.go and
getAllAssets.go. The latter uses an assetTransferBasic struct which
provides a simple API for basic asset operations like create, transfer,
etc.

Added transact.go with some util functions. Using google uuid package to
generate random UUIDs for the transactions.

Implemented pretty printing of JSON results.

Implemented app.go entry point with error handling. The existing
commands are getAllAssets, transact and listen. They can be called from
the command line via: "go run . <command> <command> ...". They will be
executed in order and if a command is not known an the application
panics and aborts before executing any of the commands.

Implementing listen.go. Added checkpointer, context setups, call to
BlockEvents and all the interfaces needed for parsing. Started
implementing the interfaces needed to represent a block bottom up in
structs. Finished NamespaceReadWriteSet, ReadWriteSet and
EndorserTransaction.

Signed-off-by: Stanislav Jakuschevskij <[email protected]>
For the GetCreator() method return the identity.Identity interface was
also implemented.
Comment on lines 15 to 31
type Block interface {
GetNumber() uint64
GetTransactions() []Transaction
ToProto() *common.Block
}

type ParsedBlock struct {
block *common.Block
validationCodes []byte
transactions []Transaction
}

func NewParsedBlock(block *common.Block) Block {
validationCodes := getTransactionValidationCodes(block)

return &ParsedBlock{block, validationCodes, nil}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more typical pattern in Go is to return concrete structs, and use interfaces for parameter types received by functions. Interfaces are typically used for return types only where different implementation structs might be returned.

For this case you might be better not to have an interface at all.

Suggested change
type Block interface {
GetNumber() uint64
GetTransactions() []Transaction
ToProto() *common.Block
}
type ParsedBlock struct {
block *common.Block
validationCodes []byte
transactions []Transaction
}
func NewParsedBlock(block *common.Block) Block {
validationCodes := getTransactionValidationCodes(block)
return &ParsedBlock{block, validationCodes, nil}
}
type Block struct {
block *common.Block
validationCodes []byte
transactions []Transaction
}
func ParseBlock(block *common.Block) Block {
validationCodes := getTransactionValidationCodes(block)
return &Block{block, validationCodes, nil}
}

To make it clearer you might consider moving the parsing code (and associated structs) into a different package. Then each Block variable in the application code will be referred to as either common.Block or parser.Block (for example).

Copy link
Contributor Author

@twoGiants twoGiants Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more typical pattern in Go is to return concrete structs, and use interfaces for parameter types received by functions.

Thx, didn't knew. Will do that.

Interfaces are typically used for return types only where different implementation structs might be returned.

Of course, that is right and not only in go. Will keep it in mind.

To make it clearer you might consider moving the parsing code (and associated structs) into a different package. Then each Block variable in the application code will be referred to as either common.Block or parser.Block (for example).

Yes, that is exactly my plan once I'll make it work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this case you might be better not to have an interface at all.

The TypeScript implementation is using interfaces. I thought I was supposed to follow it closely?

I will implement a version without interfaces and see how it is.

}

// TODO: needs cache, getPayloads, parsePayload
func (pb *ParsedBlock) GetTransactions() []Transaction {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go typically doesn't use Get for getters (although it can make sense in some cases). A more typical naming would just be Transactions. See Effective Go.

Suggested change
func (pb *ParsedBlock) GetTransactions() []Transaction {
func (pb *ParsedBlock) Transactions() []Transaction {

Copy link
Contributor Author

@twoGiants twoGiants Dec 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes yes, this was also bothering me. I remember that one well.

On the side: getters and setters aren't a good pattern anyway imho. I agree mostly with Allen Holub on this one => good read Why getter and setter methods are evil. But I would not go as far as not using them at all. It depends on the project and style used.

}

// Implements identity.Identity Interface
type IdentityImpl struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's really an implementation class only created by implementation of this package then I would make it private (first character lower case). Often an interface is not used and the concrete class is returned, but reusing the fabric-gateway Identity interface might make sense here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will review and update the encapsulation once I refactored the code into the parser package.

Comment on lines 3 to 5
go 1.22.7

toolchain go1.23.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would recommend setting the minimum required Go version to 1.22.0 and removing the toolchain so it builds with the locally installed version of Go, provided it is at least the minimum required version. You can do that by running:

go get [email protected] toolchain@none

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx. Will do.

Created parser, contract and utils packages and extracted each piece of
functionality into its own files. Removed "Get" prefix from methods and
changed return values from interfaces to structs.
Removed Block and Transaction interfaces and unused statusCode function.
Using the struct instead of the interfaces now.
Added block processor struct and the process method.
Implemented getting valid transactions from the last processed index.
Added data structures needed for the store.

Decomposed the parser.Block.Transactions() method into readable chunks.

Added transaction processor struct and process method. Unwrapping
read write set data from the transaction, mapping to a new "write"
data structure and passing down to the store.

Store is an empty function and will be implemented next.

Signed-off-by: Stanislav Jakuschevskij <[email protected]>
Persisting ledger writes to the file system into the store.log file in
the application-go directory. The write values are converted from bytes
to a string when the read write sets are unwrapped in the transaction
processor.

Signed-off-by: Stanislav Jakuschevskij <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants