Where is the message handler in go-ethereum? - go

I'm currently trying to figure out the Ethereum code and have learned how to send transactions to the blockchain using the client module.Here is an example of a contract call function:
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
var hex hexutil.Bytes
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
if err != nil {
return nil, err
}
return hex, nil
}
, where CallContext defined as:`
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr {
return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result)
}
msg, err := c.newMessage(method, args...)
if err != nil {
return err
}
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
if c.isHTTP {
err = c.sendHTTP(ctx, op, msg)
} else {
err = c.send(ctx, op, msg)
}
if err != nil {
return err
}
// dispatch has accepted the request and will close the channel when it quits.
switch resp, err := op.wait(ctx, c); {
case err != nil:
return err
case resp.Error != nil:
return resp.Error
case len(resp.Result) == 0:
return ErrNoResult
default:
return json.Unmarshal(resp.Result, &result)
}`
And my question is: Where is the handler for these messages implemented in go - ethereum?
For example:
switch msg.name:
case "eth_call": ...
case "eth_sendTx": ...
...

Related

How to show progress during upload asynchronously with WASM

I am currently using Go WASM to upload a file to a server. During the upload it shall emit a call to update the upload progress in the UI.
I am currently using the following struct to have an indication of the progress:
type progressReporter struct {
r io.Reader
fileSizeEncrypted int64
sent int64
file js.Value
}
func (pr *progressReporter) Read(p []byte) (int, error) {
n, err := pr.r.Read(p)
pr.sent = pr.sent + int64(n)
pr.report()
return n, err
}
func (pr *progressReporter) report() {
go js.Global().Get("dropzoneObject").Call("emit", "uploadprogress", pr.file, pr.sent*100/pr.fileSizeEncrypted, pr.sent)
}
The upload happens in a promise:
func UploadChunk(this js.Value, args []js.Value) interface{} {
[...]
handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
resolve := args[0]
reject := args[1]
go func() {
[...]
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", "encrypted.file")
if err != nil {
return err
}
_, err = part.Write(*data)
if err != nil {
return err
}
err = writer.Close()
if err != nil {
return err
}
pReporter := progressReporter{
r: body,
fileSizeEncrypted: fileSize,
sent: offset,
file: jsFile,
}
r, err := http.NewRequest("POST", "./uploadChunk", &pReporter)
if err != nil {
return err
}
r.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(r)
if err != nil {
return err
}
[...]
}
}
Although the code works fine, all emit calls to update the UI are sent after the POST request is finished. Is there any way to have this call asynchronously?
The full source code can be found here

Testing Nats subscription in Go

I have a function designed to listen to a Nats subject and route the messages as it receives them:
func (conn *JetStreamConnection) SubscribeMultiple(ctx context.Context, subject string,
subscribers ...*SubscriptionCallback) error {
callbacks := make(map[string]func(*pnats.NatsMessage) (func(context.Context), error))
for _, subscriber := range subscribers {
callbacks[subscriber.Category] = subscriber.Callback
}
fullSubject := fmt.Sprintf("%s.*", subject)
sub, err := conn.context.SubscribeSync(fullSubject, nats.Context(ctx))
if err != nil {
return err
}
loop:
for {
select {
case <-ctx.Done():
break loop
default:
}
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
return err
}
msg.InProgress()
var message pnats.NatsMessage
if err := conn.unmarshaller(msg.Data, &message); err != nil {
msg.Term()
return err
}
actualSubject := fmt.Sprintf("%s.%s", subject, message.Context.Category)
subscriber, ok := callbacks[message.Context.Category]
if !ok {
msg.Nak()
continue
}
callback, err := subscriber(&message)
if err == nil {
msg.Ack()
} else {
msg.Nak()
return err
}
callback(ctx)
}
if err := sub.Unsubscribe(); err != nil {
return err
}
return nil
}
My problem is that, since the SubscribeSync function produces a *nats.Subscription object, I have no way to mock out the test. How can I test around this object?
You can put your loop in a separate function. This func can accept an interface that describes nats Subscription instead of *nats.Subscription. This way you will be able to create Subscription mocks with gomock or other tools. After that you can test the inside func separately
Something like this:
func (conn *JetStreamConnection) SubscribeMultiple(ctx context.Context, subject string,
subscribers ...*SubscriptionCallback) error {
callbacks := make(map[string]func(*pnats.NatsMessage) (func(context.Context), error))
for _, subscriber := range subscribers {
callbacks[subscriber.Category] = subscriber.Callback
}
fullSubject := fmt.Sprintf("%s.*", subject)
sub, err := conn.context.SubscribeSync(fullSubject, nats.Context(ctx))
if err != nil {
return err
}
return run(ctx, sub)
}
//go:generate mockgen -source conn.go -destination ../mocks/conn.go -package mocks
type ISubscription interface{
NextMsgWithContext(ctx context.Context) (*nats.Msg, error)
Unsubscribe() error
}
func (conn *JetStreamConnection) run(ctx context.Context, sub ISubscription) error {
loop:
for {
select {
case <-ctx.Done():
break loop
default:
}
msg, err := sub.NextMsgWithContext(ctx)
if err != nil {
return err
}
msg.InProgress()
var message pnats.NatsMessage
if err := conn.unmarshaller(msg.Data, &message); err != nil {
msg.Term()
return err
}
actualSubject := fmt.Sprintf("%s.%s", subject, message.Context.Category)
subscriber, ok := callbacks[message.Context.Category]
if !ok {
msg.Nak()
continue
}
callback, err := subscriber(&message)
if err == nil {
msg.Ack()
} else {
msg.Nak()
return err
}
callback(ctx)
}
if err := sub.Unsubscribe(); err != nil {
return err
}
}
upd: if you still want to test SubscribeMultiple, you can create a Runner that will have only one func Run and take it as dependency for JetStreamConnection. Again, you can create a mock for Runner and test with it

Passing an CSVExport function to a handler Gin

I have created a CSV export function for my to-do list application. The function is working, the handler is returning a written file but I get a strange panic in the console from the Gin framework:
http: wrote more than the declared Content-Length
Is that something crucial and how can I fix this panic.
This is my function:
func (r *Repository) CSVExport() (*os.File, error) {
tasks, err := r.getAllTasks()
if err != nil {
return nil, err
}
file, err := os.Create("tasks.csv")
if err != nil {
return nil, err
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
var taskNameList []string
for _, task := range tasks {
taskNameList = append(taskNameList, task.Text)
}
err = writer.Write(taskNameList)
if err != nil {
return nil, err
}
return file, nil
}
And this is the handler:
func CSVExport(data model.ListOperations) gin.HandlerFunc {
return func(c *gin.Context) {
tasks, err := data.CSVExport()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Task"})
}
c.FileAttachment("./tasks.csv", "tasks.csv")
c.Writer.Header().Set("attachment", "filename=tasks.csv")
c.JSON(200, tasks)
}
}
Your code has some error:
You need to return on error
You can't return JSON after returning your file with fileAttachment (it already does this stuff)
func CSVExport(data model.ListOperations) gin.HandlerFunc {
return func(c *gin.Context) {
tasks, err := data.CSVExport()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Task"})
return //stop it on error
}
c.FileAttachment("./tasks.csv", "tasks.csv")
c.Writer.Header().Set("attachment", "filename=tasks.csv")
//c.JSON(200, tasks) not need the fileAttachement func do it
}
}

Golang unexpected EOF

Here's my code, I'm new to Go.
I tried googling the issue, but I can't quite put my finger on it.
I think it has something to do with the Read() method.
package main
import (
...
)
type compressor struct {
content []byte
}
func (r *compressor) compress() []byte {
...
}
func (r *compressor) decompress() []byte {
var buffer bytes.Buffer
dc := flate.NewReader(&buffer)
_, err := dc.Read(r.content)
if err != nil {
if err != io.EOF {
log.Fatal(err)
}
}
return buffer.Bytes()
}
func main() {
fileName := os.Args[1]
fmt.Println(os.Args)
contents, err := ioutil.ReadFile(fileName)
if err != nil {
log.Fatal(err)
}
fmt.Print("Uncompressed data: ")
fmt.Println(len(contents))
comp := compressor{contents}
buffer := comp.decompress()
fmt.Print("Uncompressed data: ")
fmt.Println(len(comp.decompress()))
err = ioutil.WriteFile(fileName+".decjc", buffer, 0644)
if err != nil {
log.Fatal(err)
}
}
Here's the output
dylan#skynet:~/Documents/EXP/jc$ ./jc data.txt.jc
[./jc data.txt.jc]
Uncompressed data: 2364480
2018/06/29 21:41:35 unexpected EOF
After doing a trace on the particular code in question I have come to the following answer.
/src/bytes/reader.go 70
func (r *Reader) ReadByte() (byte, error) {
...
if r.i >= int64(len(r.s)) {
return 0, io.EOF
}
....
}
There are four functions in bytes/reader that can return io.EOF, and zero functions that can return io.ErrUnexpectedEOF. The four functions that can return io.EOF are:
Read(b []byte)
ReadAt(b []byte, off int64)
ReadByte()
ReadRune()
/src/compress/flate/inflate.go 698
func (f *decompressor) moreBits() error {
c, err := f.r.ReadByte()
if err != nil {
return noEOF(err)
}
...
}
Of the four functions that can return io.EOF, only one function in flate/inflate.go calls any of them: moreBits() calls ReadByte()
/src/compress/flate/inflate.go 690
func noEOF(e error) error {
if e == io.EOF {
return io.ErrUnexpectedEOF
}
...
}
When moreBits() receives an error it calls noEOF(), which checks if it had received an io.EOF. If this was the case then io.ErrUnexpectedEOF is returned backed. Everything seems to be working as intended, and it appears that it is the user's responsibility to be on the look out for this particular case. A suggested edit to the code above to handle what appears to be defined behavior is:
func (r *compressor) decompress() []byte {
dc := flate.NewReader(bytes.NewReader(r.content))
defer dc.Close()
rb, err := ioutil.ReadAll(dc)
if err != nil {
if err != io.EOF && err != io.ErrUnexpectedEOF {
log.Fatalf("Err %v\n read %v", err, rb)
}
}
return rb
}
This was checked under go1.12.9
You got the in and outputs mixed up.
flate.NewReader takes the compressed input as an io.Reader and it returns a io.ReadCloser that can be used to get the uncompressed output:
func (r *compressor) decompress() []byte {
dc := flate.NewReader(bytes.NewReader(r.content))
defer dc.Close()
rb, err := ioutil.ReadAll(dc)
if err != nil {
if err != io.EOF {
log.Fatalf("Err %v\n read %v", err, rb)
}
}
return rb
}

How do I make this Go code more DRY?

I'm implementing a Go wrapper for a REST API. It basically parses JSON and should return the appropriate struct type. I find myself doing a lot of this:
// GetBlueprintDetails returns details about a blueprint
func (c *Client) GetBlueprintDetails(projectID string, blueprintID string) (*BlueprintDetails, *APIError) {
path := fmt.Sprintf("projects/%s/blueprints/%s", projectID, blueprintID)
res, err := c.Request("GET", path, nil, nil)
if err != nil {
return nil, err
}
var ret BlueprintDetails
e := json.Unmarshal(res.Body, &ret)
if e != nil {
return nil, &APIError{Error: &e}
}
return &ret, nil
}
// GetProjects returns a list of projects for the user
func (c *Client) GetProjects() (*[]Project, *APIError) {
res, err := c.Request("GET", "projects", nil, nil)
if err != nil {
return nil, err
}
var ret []Project
e := json.Unmarshal(res.Body, &ret)
if e != nil {
return nil, &APIError{Error: &e}
}
return &ret, nil
}
The only difference between the two functions is the type of the unmarshaled struct basically. I know there are no generic in Go, but there has to be a pattern to make this more DRY.
Any ideas?
You may create a MakeRequest function that does the http request part and unmarshal the json to struct
Here is how you may do it, have a look at the MakeRequest function
// GetBlueprintDetails returns details about a blueprint
func (c *Client) GetBlueprintDetails(projectID string, blueprintID string) (*BlueprintDetails, *APIError) {
path := fmt.Sprintf("projects/%s/blueprints/%s", projectID, blueprintID)
bluePrintDetails = new(BlueprintDetails)
err := c.MakeRequest("GET", path, bluePrintDetails)
return bluePrintDetails, err
}
// GetProjects returns a list of projects for the user
func (c *Client) GetProjects() (*[]Project, *APIError) {
projects = make([]Project, 0)
err := c.MakeRequest("GET", "project", &projects)
return &projects, err
}
func (c *Client) MakeRequest(method string, path string, response interface{}) *APIError {
res, err := c.Request(method, path, nil, nil)
if err != nil {
return nil, err
}
e := json.Unmarshal(res.Body, response)
if e != nil {
return &APIError{Error: &e}
}
return nil
}

Resources