mirror of
https://github.com/status-im/status-go.git
synced 2025-01-12 23:55:03 +00:00
653da5bcd0
Currently it is quite easy to introduce concurrency issues while working with transaction object. For example, race issue will exist every time while transaction is processed in a separate goroutine and caller will try to check for an error before event to Done channel is sent. This change removes all the data that is updated on transaction and leaves it with ID, Args and Context (which is not used at the moment). Signed-off-by: Dmitry Shulyak <yashulyak@gmail.com>
165 lines
4.3 KiB
Go
165 lines
4.3 KiB
Go
package common
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/pborman/uuid"
|
|
"github.com/status-im/status-go/geth/log"
|
|
"github.com/status-im/status-go/static"
|
|
)
|
|
|
|
const (
|
|
// MessageIDKey is a key for message ID
|
|
// This ID is required to track from which chat a given send transaction request is coming.
|
|
MessageIDKey = contextKey("message_id")
|
|
)
|
|
|
|
type contextKey string // in order to make sure that our context key does not collide with keys from other packages
|
|
|
|
// errors
|
|
var (
|
|
ErrInvalidAccountAddressOrKey = errors.New("cannot parse address or key to valid account address")
|
|
)
|
|
|
|
// ParseAccountString parses hex encoded string and returns is as accounts.Account.
|
|
func ParseAccountString(account string) (accounts.Account, error) {
|
|
// valid address, convert to account
|
|
if common.IsHexAddress(account) {
|
|
return accounts.Account{Address: common.HexToAddress(account)}, nil
|
|
}
|
|
|
|
return accounts.Account{}, ErrInvalidAccountAddressOrKey
|
|
}
|
|
|
|
// FromAddress converts account address from string to common.Address.
|
|
// The function is useful to format "From" field of send transaction struct.
|
|
func FromAddress(accountAddress string) common.Address {
|
|
from, err := ParseAccountString(accountAddress)
|
|
if err != nil {
|
|
return common.Address{}
|
|
}
|
|
|
|
return from.Address
|
|
}
|
|
|
|
// ToAddress converts account address from string to *common.Address.
|
|
// The function is useful to format "To" field of send transaction struct.
|
|
func ToAddress(accountAddress string) *common.Address {
|
|
to, err := ParseAccountString(accountAddress)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return &to.Address
|
|
}
|
|
|
|
// ImportTestAccount imports keystore from static resources, see "static/keys" folder
|
|
func ImportTestAccount(keystoreDir, accountFile string) error {
|
|
// make sure that keystore folder exists
|
|
if _, err := os.Stat(keystoreDir); os.IsNotExist(err) {
|
|
os.MkdirAll(keystoreDir, os.ModePerm) // nolint: errcheck, gas
|
|
}
|
|
|
|
dst := filepath.Join(keystoreDir, accountFile)
|
|
err := ioutil.WriteFile(dst, static.MustAsset("keys/"+accountFile), 0644)
|
|
if err != nil {
|
|
log.Warn("cannot copy test account PK", "error", err)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// PanicAfter throws panic() after waitSeconds, unless abort channel receives notification
|
|
func PanicAfter(waitSeconds time.Duration, abort chan struct{}, desc string) {
|
|
go func() {
|
|
select {
|
|
case <-abort:
|
|
return
|
|
case <-time.After(waitSeconds):
|
|
panic("whatever you were doing takes toooo long: " + desc)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// NameOf returns name of caller, at runtime
|
|
func NameOf(f interface{}) string {
|
|
v := reflect.ValueOf(f)
|
|
if v.Kind() == reflect.Func {
|
|
if rf := runtime.FuncForPC(v.Pointer()); rf != nil {
|
|
return rf.Name()
|
|
}
|
|
}
|
|
return v.String()
|
|
}
|
|
|
|
// MessageIDFromContext returns message id from context (if exists)
|
|
func MessageIDFromContext(ctx context.Context) string {
|
|
if ctx == nil {
|
|
return ""
|
|
}
|
|
if messageID, ok := ctx.Value(MessageIDKey).(string); ok {
|
|
return messageID
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// ParseJSONArray parses JSON array into Go array of string
|
|
func ParseJSONArray(items string) ([]string, error) {
|
|
var parsedItems []string
|
|
err := json.Unmarshal([]byte(items), &parsedItems)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return parsedItems, nil
|
|
}
|
|
|
|
// Fatalf is used to halt the execution.
|
|
// When called the function prints stack end exits.
|
|
// Failure is logged into both StdErr and StdOut.
|
|
func Fatalf(reason interface{}, args ...interface{}) {
|
|
// decide on output stream
|
|
w := io.MultiWriter(os.Stdout, os.Stderr)
|
|
outf, _ := os.Stdout.Stat() // nolint: gas
|
|
errf, _ := os.Stderr.Stat() // nolint: gas
|
|
if outf != nil && errf != nil && os.SameFile(outf, errf) {
|
|
w = os.Stderr
|
|
}
|
|
|
|
// find out whether error or string has been passed as a reason
|
|
r := reflect.ValueOf(reason)
|
|
if r.Kind() == reflect.String {
|
|
fmt.Fprintf(w, "Fatal Failure: "+reason.(string)+"\n", args) //nolint: gas
|
|
} else {
|
|
fmt.Fprintf(w, "Fatal Failure: %v\n", reason.(error)) //nolint: gas
|
|
}
|
|
|
|
debug.PrintStack()
|
|
|
|
os.Exit(1)
|
|
}
|
|
|
|
// CreateTransaction returns a transaction object.
|
|
func CreateTransaction(ctx context.Context, args SendTxArgs) *QueuedTx {
|
|
return &QueuedTx{
|
|
ID: QueuedTxID(uuid.New()),
|
|
Context: ctx,
|
|
Args: args,
|
|
Result: make(chan TransactionResult, 1),
|
|
}
|
|
}
|