Chaincode function to display struct values - go

I'm trying to write a simple chaincode that uses a structure to store customer details. I have one setDetails func that works fine. I wish to write another getDetails func that takes UID as arguement and prints the details of the customer with that UID. Need Help!
package main
import (
"errors"
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
type Customer struct {
UID string
Name string
Address struct {
StreetNo string
Country string
}
}
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
fmt.Printf("initialization done!!!")
fmt.Printf("initialization done!!!")
return nil, nil
}
func (t *SimpleChaincode) setDetails(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
if len(args) < 3 {
return nil, errors.New("insert Into Table failed. Must include 3 column values")
}
customer := &Customer{}
customer.UID = args[0]
customer.Name = args[1]
customer.Address.Country = args[2]
return nil, nil
}
func (t *SimpleChaincode) getDetails(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
//wish to print all details of an particular customer corresponding to the UID
return nil, nil
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
function, args := stub.GetFunctionAndParameters()
fmt.Printf("Inside Invoke %s", function)
if function == "setDetails" {
return t.setDetails(stub, args)
} else if function == "getDetails" {
return t.getDetails(stub, args)
}
return nil, errors.New("Invalid invoke function name. Expecting \"query\"")
}
func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}

I didn't know about Hyperledger until now, but after a look at the github documentation, I get you would use stub.PutState to store your customer information, then later use stub.GetState to get it back.
As both methods request a byte slice, my guess would be something along these lines:
func (t *SimpleChaincode) setDetails(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
if len(args) < 3 {
return nil, errors.New("insert Into Table failed. Must include 3 column values")
}
customer := &Customer{}
customer.UID = args[0]
customer.Name = args[1]
customer.Address.Country = args[2]
raw, err := json.Marshal(customer)
if err != nil {
return nil, err
}
err := stub.PuState(customer.UID, raw)
if err != nil {
return nil, err
}
return nil, nil
}
func (t *SimpleChaincode) getDetails(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
if len(args) != 1 {
return nil, errors.New("Incorrect number of arguments. Expecting name of the key to query")
}
return stub.GetState(args[0])
}

Related

Default HTTP file server - modifications of the content

How can I compose the default Go HTTP file server (serve if exists, show file listing otherwise) with additional HTML?
Sample http.go with default file server:
package main
import "net/http"
func main() {
http.Handle("/", http.FileServer(http.Dir(".")))
http.ListenAndServe(":8090", nil)
}
Loading the default page (http://localhost:8090) gives something like:
<pre>LICENSE
README.md
studio.jpg
</pre>
I found it is declared at fs.go.
I want to keep that section, but with my own header and footer (preferably without copying the dirList function and making small changes):
<title>My files</title>
<pre>LICENSE
README.md
studio.jpg
</pre>
<p>And that's all, folks!</p>
Based on this answer, you can implement own FileSystem for a FileServer
This implementation is very buggy at best, and you should probably never ever use it, but it should show you how the FileSystem interface can be implemented for arbitrary 'files'.
type InMemoryFS map[string]http.File
type InMemoryFile struct {
at int64
Name string
data []byte
fs InMemoryFS
}
func NewFile(name string, data []byte) *InMemoryFile {
return &InMemoryFile{at: 0,
Name: name,
data: data,
fs: make(InMemoryFS)}
}
// Implements the http.File interface
func (f *InMemoryFile) Close() error {
return nil
}
func (f *InMemoryFile) Stat() (os.FileInfo, error) {
return &InMemoryFileInfo{f}, nil
}
func (f *InMemoryFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
func (f *InMemoryFile) Read(b []byte) (int, error) {
i := 0
for f.at < int64(len(f.data)) && i < len(b) {
b[i] = f.data[f.at]
i++
f.at++
}
return i, nil
}
func (f *InMemoryFile) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0:
f.at = offset
case 1:
f.at += offset
case 2:
f.at = int64(len(f.data)) + offset
}
return f.at, nil
}
type InMemoryFileInfo struct {
file *InMemoryFile
}
// Implements os.FileInfo
func (s *InMemoryFileInfo) Name() string { return s.file.Name }
func (s *InMemoryFileInfo) Size() int64 { return int64(len(s.file.data)) }
func (s *InMemoryFileInfo) Mode() os.FileMode { return os.ModeTemporary }
func (s *InMemoryFileInfo) ModTime() time.Time { return time.Time{} }
func (s *InMemoryFileInfo) IsDir() bool { return false }
func (s *InMemoryFileInfo) Sys() interface{} { return nil }
// CustomFsDecorator: is `http.FileSystem` decorator
type CustomFsDecorator struct {
http.FileSystem
}
func (fs CustomFsDecorator) Open(name string) (http.File, error) {
file, err := fs.FileSystem.Open(name)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
return nil, err
}
if info.IsDir() {
return file, nil
}
b, err := io.ReadAll(file)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
// add header's lines
_, err = buf.Write([]byte("<title>My files</title>\n"))
if err != nil {
return nil, err
}
_, err = buf.Write(b)
if err != nil {
return nil, err
}
// add footer's lines
_, err = buf.Write([]byte("\n<p>And that's all, folks!</p>"))
if err != nil {
return nil, err
}
return NewFile(info.Name(), buf.Bytes()), nil
}
func Test(t *testing.T) {
cfsys := CustomFsDecorator{FileSystem: http.Dir("./static")}
fsys := http.FileServer(cfsys)
req := httptest.NewRequest(http.MethodGet, "/some.html", nil)
w := httptest.NewRecorder()
fsys.ServeHTTP(w, req)
res := w.Result()
defer func() {
_ = res.Body.Close()
}()
data, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
fmt.Println(string(data))
}
👇🏻
<title>My files</title>
<pre>LICENSE
README.md
studio.jpg
</pre>
<p>And that's all, folks!</p>
PLAYGROUND

Difference in func with receiver and param golang

I try to create marshal func for struct Item.
So the question is, why the first example gives stackoverflow for goroutine and second works correctly?
Call method with receiver
type Item struct{
a int
}
func some(items []Item){
for _,i:=range items{
buf,err:=i.MarhsalBinary()
fmt.Println(buf.Bytes(), err)
}
func (i Item) MarshalBinary() ([]byte, error) {
var (
buf bytes.Buffer
err error
)
if err = gob.NewEncoder(&buf).Encode(i); err != nil { // endless loop here
return nil, err
}
return buf.Bytes(), err
}
This is pass by value.
type Item struct{
a int
}
func some(items []Item){
for _,i:=range items{
buf,err:=MarhsalBinary(i)
fmt.Println(buf.Bytes(), err)
}
func MarshalBinary(i Item) ([]byte, error) {
var (
buf bytes.Buffer
err error
)
if err = gob.NewEncoder(&buf).Encode(i); err != nil { // works correctly
return nil, err
}
return buf.Bytes(), err
}

Mocking open in go…

I have been following this answer trying to mock open. I have got exactly no where.
This is the test code I have:
func (m mockedFS) Open(name string) (file, error) {
if m.reportErrOpen {
return nil, errors.New("Fake failure")
}
mockedFile := mockIORead{}
mockedFile.On("ReadAll", mock.AnythingOfType("[]uint8")).Return(0, fmt.Errorf("error reading"))
mockedFile.On("Read", mock.AnythingOfType("[]byte")).Return(0, errors.New("NON"))
return mockedFile, nil
}
type mockIORead struct {
mock.Mock
reportErr bool // Tells if this mocked FS should return error in our tests
reportSize int64 // Tells what size should Stat() report in our test
}
func (m mockIORead) Read(b []byte) (n int, err error) {
if m.reportErr {
return 0, errors.New("A fake failure")
}
s := "Fear the old blood"
copy(b[:], s)
return 0, nil
}
func (m mockIORead) Close() error {
return nil
}
func (m mockIORead) ReadAt([]byte, int64) (int, error) {
return 0, nil
}
func (m mockIORead) Seek(int64, int) (int64, error) {
return 0, nil
}
func (m mockIORead) Stat() (os.FileInfo, error) {
if m.reportErr {
return nil, os.ErrNotExist
}
return mockedFileInfo{size: m.reportSize}, nil
}
func TestOok(t *testing.T) {
oldFs := fs
// Create and "install" mocked fs:
mfs := &mockedFS{}
fs = mfs
// Make sure fs is restored after this test:
defer func() {
fs = oldFs
}()
mfs.reportErr = false
mfs.reportErrOpen = false
token, err := Ook("fake")
assert.NotNil(t, err)
assert.Equal(t, "Fear the old blood", token)
}
And this is the code under test:
func Ook(name string) (string, error) {
_, err := fs.Stat(name)
if err != nil {
return "", nil
}
file, err := fs.Open(name)
if err != nil {
return "", errors.Wrap(err, "Cannot open token file")
}
defer file.Close()
_, err = ioutil.ReadAll(file)
fmt.Print("PING\n")
if err != nil {
return "", errors.Wrap(err, "Could not read token")
}
return "Fear the old blood", nil
//return string(token), nil
}
What the hell am I doing wrong?
The first error is that your mockIORead.Read() returns wrong values. It must return the number of read bytes (bytes written to the slice argument) (e.g. what copy() would return).
Next, mockIORead.Read() must be stateful! Reader.Read() might be called several times, there is no guarantee the passed slice can accommodate all the data you want to return (via the passed b slice).
So mockIORead must store the data you want to return, and it must remember how much of them has been delivered so far, so the next Read() call can continue from there.
An easy implementation of this is to utilize bytes.Buffer:
type mockIORead struct {
mock.Mock
reportErr bool // Tells if this mocked FS should return error in our tests
reportSize int64 // Tells what size should Stat() report in our test
content *bytes.Buffer
}
When returning such a mockIORead, initialize content with the content you wish to return:
func (m mockedFS) Open(name string) (file, error) {
if m.reportErrOpen {
return nil, errors.New("Fake failure")
}
mockedFile := mockIORead{
content: bytes.NewBufferString("Fear the old blood"),
}
return mockedFile, nil
}
And thanks to the available bytes.Buffer.Read() method, the mockIORead.Read() implementation can be as simple as this:
func (m mockIORead) Read(b []byte) (n int, err error) {
if m.reportErr {
return 0, errors.New("A fake failure")
}
return m.content.Read(b)
}
The Ook() function itself should not try to "stat" as you haven't mocked it (and so calling the original os.Stat() will likely yield an error for the "fake" file name used in the test):
func Ook(name string) (string, error) {
file, err := fs.Open(name)
if err != nil {
return "", errors.Wrap(err, "Cannot open token file")
}
defer file.Close()
token, err := ioutil.ReadAll(file)
fmt.Print("PING\n")
if err != nil {
return "", errors.Wrap(err, "Could not read token")
}
return string(token), nil
}
And the testing code:
func TestOok(t *testing.T) {
oldFs := fs
// Create and "install" mocked fs:
mfs := &mockedFS{}
fs = mfs
// Make sure fs is restored after this test:
defer func() {
fs = oldFs
}()
mfs.reportErr = false
mfs.reportErrOpen = false
token, err := Ook("fake")
assert.Nil(t, err)
assert.Equal(t, "Fear the old blood", token)
}
Which yields a successful ("OK") test.

Chaincode in go - Hyperledger v 1.0 - too many arguments to return

I am getting below error stack by running one of my Chaincode program in Hyperledger. I am trying to build a small application that will insert a keyvalue pair of username and a status and using this I can insert or read values from the ledger:
go build
# _/home/ubuntu/go/src/Chaincodeexample
./Samplesupplychain.go:28:9: too many arguments to return
have (nil, error)
want (peer.Response)
.... This continues for all other functions in my code and final ones are below :
./Samplesupplychain.go:80:33: too many arguments in call to Checkuploadstatus
have (shim.ChaincodeStubInterface, []string)
want (shim.ChaincodeStubInterface)
./Samplesupplychain.go:90:26: too many arguments in call to CreateUser
have (shim.ChaincodeStubInterface, []string)
want (shim.ChaincodeStubInterface)
./Samplesupplychain.go:92:36: undefined: username
./Samplesupplychain.go:92:36: too many errors
My code is below :
package main
import(
"errors"
"fmt"
pb "github.com/hyperledger/fabric/protos/peer"
//"encoding/json"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
type SampleChaincode struct {
}
var logger = shim.NewLogger("my logger")
//Create a struct for these 2 values
type testuser struct{
Username string `json:"username"`
Fileuploaded string `json:"fileuploaded"`
}
//A function to create user on the ledger
func CreateUser(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if len(args) < 2 {
logger.Error("Invalid number of args")
return nil, errors.New("Expected atleast 1 argument for user creation")
}
var Username = args[0]
var UsernameInput = args[1]
//trying to understand
err := stub.PutState(Username, []byte(UsernameInput))
if err != nil {
logger.Error("Could not save new User to ledger", err)
return nil, err
}
var customEvent = "{eventType: 'UserCreation', description:" + Username + "' Successfully created'}"
err = stub.SetEvent("evtSender", []byte(customEvent))
if err != nil {
return nil, err
}
logger.Info("Successfully saved a supply chain user")
return nil, nil
}
func Checkuploadstatus(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
logger.Debug("Entering supplychain application")
if len(args) < 1 {
logger.Error("Invalid number of arguments")
return nil, errors.New("Missing details")
}
var Fileuploadedstatusforuser = args[0]
bytes, err := stub.GetState(Fileuploadedstatusforuser)
if err != nil {
logger.Error("Could not fetch Fileuploadedstatus with "+ Fileuploadedstatusforuser +" from ledger", err)
return nil, err
}
return bytes, nil
}
func (t *SampleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
return shim.Success(nil)
}
func (t *SampleChaincode) Query(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if function == "Checkuploadstatus" {
return Checkuploadstatus(stub, args)
}
return shim.Success(nil)
}
func (t *SampleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if function == "CreateUser" {
return CreateUser(stub, args)
} else {
return nil, errors.New(username + " does not have access to create a User")
}
return shim.Success(nil)
}
func main() {
lld, _ := shim.LogLevel("DEBUG")
fmt.Println(lld)
logger.SetLevel(lld)
fmt.Println(logger.IsEnabledFor(lld))
err := shim.Start(new(SampleChaincode))
if err != nil {
logger.Error("Could not start SampleChaincode")
} else {
logger.Info("SampleChaincode successfully started")
}
}
Any help will be appreciated as I am currently using Hyperledger v 1.0.0
The answer here is pretty simple. I explain it for the first error. The rest is similar.
In your func you are defining one return type: pb.Response
But in your return statement you are returning 2 values.
func Checkuploadstatus(stub shim.ChaincodeStubInterface) pb.Response {
...
return nil, errors.New("Expected atleast 1 argument for user creation")
To fix this you can change the signature of your func or the return statement
Solution change the signature:
func Checkuploadstatus(stub shim.ChaincodeStubInterface) (pb.Response, error) {
...
return nil, errors.New("Expected atleast 1 argument for user creation")
Solution change the return (then no error handling):
func Checkuploadstatus(stub shim.ChaincodeStubInterface) pb.Response {
...
return nil
I updated the signature and return statements of my functions and it worked perfectly fine.
func CreateUser(stub shim.ChaincodeStubInterface, args []string) pb.Response {
if len(args) < 2 {
logger.Error("Invalid number of args")
return shim.Error("Expected atleast 1 argument for user creation")
}
var Username = args[0]
var UsernameInput = args[1]
//trying to understand
err := stub.PutState(Username, []byte(UsernameInput))
if err != nil {
logger.Error("Could not save new User to ledger", err)
return shim.Error(err.Error())
}
var customEvent = "{eventType: 'UserCreation', description:" + Username + "' Successfully created'}"
err = stub.SetEvent("evtSender", []byte(customEvent))
if err != nil {
return shim.Error(err.Error())
}
logger.Info("Successfully saved a supply chain user")
return shim.Success(nil)
}

Chaincode not building - Go program error - Cannot refer to unexported name

I am getting an error while building my go code on an Ubuntu machine. I have check GOPATH variables and everything seems fine. Below is stack trace of error I am getting:
/go/src/Chaincodeexample$ go build
# _/home/ubuntu/go/src/Chaincodeexample
./Samplesupplychain.go:13:14: cannot refer to unexported name shim.logger
./Samplesupplychain.go:91:5: syntax error: non-declaration statement outside function body
Below is my code :
import(
"errors"
"fmt"
"encoding/json"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
var logger = shim.logger("my logger")
//Create a struct for these 2 values
type testuser struct{
Username string `json:"username"`
Fileuploaded string `json:"fileuploaded"`
}
//A function to create a user on the ledger
func CreateUser(stub shim.ChaincodeStubInterface, args []string) ([]byte, error){
if len(args) < 2 {
logger.Error("Invalid number of args")
return nil, errors.New("Expected atleast 1 argument for user creation")
}
var Username = args[0]
var UsernameInput = args[1]
//trying to understand
err := stub.PutState(Username, []byte(UsernameInput))
if err != nil {
logger.Error("Could not save new User to ledger", err)
return nil, err
}
var customEvent = "{eventType: 'UserCreation', description:" + Username + "' Successfully created'}"
err = stub.SetEvent("evtSender", []byte(customEvent))
if err != nil {
return nil, err
}
logger.Info("Successfully saved a supply chain user")
return nil, nil
}
func Checkuploadstatus(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
logger.Debug("Entering supply-chain application")
if len(args) < 1 {
logger.Error("Invalid number of arguments")
return nil, errors.New("Missing details")
}
var Fileuploadedstatus = args[0]
bytes, err := stub.GetState(Fileuploadedstatus)
if err != nil {
logger.Error("Could not fetch Fileuploadedstatus with "+ Fileuploadedstatus +" from ledger", err)
return nil, err
}
return bytes, nil
}
func (t *testuser) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
return nil, nil
}
func (t *testuser) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
if function == "Checkuploadstatus" {
return Checkuploadstatus(stub, args)
}
return nil, nil
}
func (t *testuser) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
if function == "CreateUser" {
return CreateUser(stub, args)
} else {
return nil, errors.New(username + " does not have access to create a User")
}
}
return nil, nil
}
Give me idea to resolve this.
In Go only you can only access names in other packages if they start with an upper-case letter. This is kind of like using public and private in Java or C++.
Call your type Logger instead of logger and it will work.

Resources