Major code refactoring
- geth package implemented - jail package implemented (and tested) - cgo and xgo (android, ios) builds w/o any issues - better conditional compilation of signals code
This commit is contained in:
parent
5fb4aef1cc
commit
edd8763c3c
|
@ -13,8 +13,6 @@
|
|||
.ethtest
|
||||
*/**/*tx_database*
|
||||
*/**/*dapps*
|
||||
Godeps/_workspace/pkg
|
||||
Godeps/_workspace/bin
|
||||
|
||||
#*
|
||||
.#*
|
||||
|
@ -35,8 +33,9 @@ profile.cov
|
|||
.vagrant
|
||||
|
||||
# tests
|
||||
src/.ethereumtest/
|
||||
.ethereumtest/
|
||||
#
|
||||
# golang
|
||||
cover.out
|
||||
cover.html
|
||||
coverage.out
|
||||
coverage-all.out
|
||||
coverage.html
|
||||
|
|
39
Makefile
39
Makefile
|
@ -5,7 +5,7 @@ GOBIN = build/bin
|
|||
GO ?= latest
|
||||
|
||||
statusgo:
|
||||
build/env.sh go build -i -o $(GOBIN)/statusgo -v $(shell build/flags.sh) ./src
|
||||
build/env.sh go build -i -o $(GOBIN)/statusgo -v $(shell build/flags.sh) ./cmd/status
|
||||
@echo "status go compilation done."
|
||||
@echo "Run \"build/bin/statusgo\" to view available commands"
|
||||
|
||||
|
@ -14,25 +14,48 @@ statusgo-cross: statusgo-android statusgo-ios
|
|||
@ls -ld $(GOBIN)/statusgo-*
|
||||
|
||||
statusgo-android: xgo
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/flags.sh) ./src
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/flags.sh) ./cmd/status
|
||||
@echo "Android cross compilation done:"
|
||||
|
||||
statusgo-ios: xgo
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./src
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./cmd/status
|
||||
@echo "iOS framework cross compilation done:"
|
||||
|
||||
statusgo-ios-simulator: xgo
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./src
|
||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/flags.sh) ./cmd/status
|
||||
@echo "iOS framework cross compilation done:"
|
||||
|
||||
xgo:
|
||||
build/env.sh go get github.com/karalabe/xgo
|
||||
|
||||
test:
|
||||
build/env.sh go test -v -coverprofile=cover.out ./src
|
||||
test-all:
|
||||
@build/env.sh echo "mode: set" > coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./geth
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./jail
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
build/env.sh go test -coverprofile=coverage.out -covermode=set ./extkeys
|
||||
@build/env.sh tail -n +2 coverage.out >> coverage-all.out
|
||||
@build/env.sh go tool cover -html=coverage-all.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage-all.out
|
||||
|
||||
test-cover: test
|
||||
build/env.sh go tool cover -html=cover.out -o cover.html
|
||||
test: test-all
|
||||
|
||||
test-geth:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./geth
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-jail:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./jail
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
test-extkeys:
|
||||
build/env.sh go test -v -coverprofile=coverage.out ./extkeys
|
||||
@build/env.sh go tool cover -html=coverage.out -o coverage.html
|
||||
@build/env.sh go tool cover -func=coverage.out
|
||||
|
||||
clean:
|
||||
rm -fr build/bin/*
|
||||
rm coverage.out coverage-all.out coverage.html
|
||||
|
|
|
@ -16,7 +16,7 @@ WS2="$ROOT/build/_workspace/project"
|
|||
if [ ! -d "$WS1/src" ]; then
|
||||
mkdir -p "$WS1"
|
||||
cd "$WS1"
|
||||
ln -s "$ROOT/src/vendor" src
|
||||
ln -s "$ROOT/vendor" src
|
||||
cd "$ROOT"
|
||||
fi
|
||||
|
||||
|
|
|
@ -1,29 +1,39 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool StatusServiceSignalEvent(const char *jsonEvent);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"github.com/status-im/status-go/geth"
|
||||
"github.com/status-im/status-go/jail"
|
||||
)
|
||||
|
||||
var emptyError = ""
|
||||
// export TriggerTestSignal
|
||||
func TriggerTestSignal() {
|
||||
C.StatusServiceSignalEvent(C.CString(`{"answer": 42}`))
|
||||
}
|
||||
|
||||
//export CreateAccount
|
||||
func CreateAccount(password *C.char) *C.char {
|
||||
|
||||
// This is equivalent to creating an account from the command line,
|
||||
// just modified to handle the function arg passing
|
||||
address, pubKey, mnemonic, err := createAccount(C.GoString(password))
|
||||
address, pubKey, mnemonic, err := geth.CreateAccount(C.GoString(password))
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AccountInfo{
|
||||
out := geth.AccountInfo{
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Mnemonic: mnemonic,
|
||||
|
@ -37,15 +47,15 @@ func CreateAccount(password *C.char) *C.char {
|
|||
//export CreateChildAccount
|
||||
func CreateChildAccount(parentAddress, password *C.char) *C.char {
|
||||
|
||||
address, pubKey, err := createChildAccount(C.GoString(parentAddress), C.GoString(password))
|
||||
address, pubKey, err := geth.CreateChildAccount(C.GoString(parentAddress), C.GoString(password))
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AccountInfo{
|
||||
out := geth.AccountInfo{
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Error: errString,
|
||||
|
@ -58,15 +68,15 @@ func CreateChildAccount(parentAddress, password *C.char) *C.char {
|
|||
//export RecoverAccount
|
||||
func RecoverAccount(password, mnemonic *C.char) *C.char {
|
||||
|
||||
address, pubKey, err := recoverAccount(C.GoString(password), C.GoString(mnemonic))
|
||||
address, pubKey, err := geth.RecoverAccount(C.GoString(password), C.GoString(mnemonic))
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AccountInfo{
|
||||
out := geth.AccountInfo{
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Mnemonic: C.GoString(mnemonic),
|
||||
|
@ -81,15 +91,15 @@ func RecoverAccount(password, mnemonic *C.char) *C.char {
|
|||
func Login(address, password *C.char) *C.char {
|
||||
// loads a key file (for a given address), tries to decrypt it using the password, to verify ownership
|
||||
// if verified, purges all the previous identities from Whisper, and injects verified key as shh identity
|
||||
err := selectAccount(C.GoString(address), C.GoString(password))
|
||||
err := geth.SelectAccount(C.GoString(address), C.GoString(password))
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := JSONError{
|
||||
out := geth.JSONError{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
@ -101,15 +111,15 @@ func Login(address, password *C.char) *C.char {
|
|||
func Logout() *C.char {
|
||||
|
||||
// This is equivalent to clearing whisper identities
|
||||
err := logout()
|
||||
err := geth.Logout()
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := JSONError{
|
||||
out := geth.JSONError{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
@ -123,15 +133,15 @@ func UnlockAccount(address, password *C.char, seconds int) *C.char {
|
|||
// This is equivalent to unlocking an account from the command line,
|
||||
// just modified to unlock the account for the currently running geth node
|
||||
// based on the provided arguments
|
||||
err := unlockAccount(C.GoString(address), C.GoString(password), seconds)
|
||||
err := geth.UnlockAccount(C.GoString(address), C.GoString(password), seconds)
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := JSONError{
|
||||
out := geth.JSONError{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
@ -141,15 +151,15 @@ func UnlockAccount(address, password *C.char, seconds int) *C.char {
|
|||
|
||||
//export CompleteTransaction
|
||||
func CompleteTransaction(id, password *C.char) *C.char {
|
||||
txHash, err := completeTransaction(C.GoString(id), C.GoString(password))
|
||||
txHash, err := geth.CompleteTransaction(C.GoString(id), C.GoString(password))
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := CompleteTransactionResult{
|
||||
out := geth.CompleteTransactionResult{
|
||||
Hash: txHash.Hex(),
|
||||
Error: errString,
|
||||
}
|
||||
|
@ -160,17 +170,16 @@ func CompleteTransaction(id, password *C.char) *C.char {
|
|||
|
||||
//export StartNode
|
||||
func StartNode(datadir *C.char) *C.char {
|
||||
|
||||
// This starts a geth node with the given datadir
|
||||
err := createAndStartNode(C.GoString(datadir))
|
||||
err := geth.CreateAndRunNode(C.GoString(datadir), geth.RPCPort)
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := JSONError{
|
||||
out := geth.JSONError{
|
||||
Error: errString,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&out)
|
||||
|
@ -178,33 +187,33 @@ func StartNode(datadir *C.char) *C.char {
|
|||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//export parse
|
||||
func parse(chatId *C.char, js *C.char) *C.char {
|
||||
res := Parse(C.GoString(chatId), C.GoString(js))
|
||||
//export InitJail
|
||||
func InitJail(js *C.char) {
|
||||
jail.Init(C.GoString(js))
|
||||
}
|
||||
|
||||
//export Parse
|
||||
func Parse(chatId *C.char, js *C.char) *C.char {
|
||||
res := jail.GetInstance().Parse(C.GoString(chatId), C.GoString(js))
|
||||
return C.CString(res)
|
||||
}
|
||||
|
||||
//export call
|
||||
func call(chatId *C.char, path *C.char, params *C.char) *C.char {
|
||||
res := Call(C.GoString(chatId), C.GoString(path), C.GoString(params))
|
||||
//export Call
|
||||
func Call(chatId *C.char, path *C.char, params *C.char) *C.char {
|
||||
res := jail.GetInstance().Call(C.GoString(chatId), C.GoString(path), C.GoString(params))
|
||||
return C.CString(res)
|
||||
}
|
||||
|
||||
//export initJail
|
||||
func initJail(js *C.char) {
|
||||
Init(C.GoString(js))
|
||||
}
|
||||
|
||||
//export addPeer
|
||||
func addPeer(url *C.char) *C.char {
|
||||
success, err := doAddPeer(C.GoString(url))
|
||||
errString := emptyError
|
||||
//export AddPeer
|
||||
func AddPeer(url *C.char) *C.char {
|
||||
success, err := geth.GetNodeManager().AddPeer(C.GoString(url))
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AddPeerResult{
|
||||
out := geth.AddPeerResult{
|
||||
Success: success,
|
||||
Error: errString,
|
||||
}
|
||||
|
@ -213,24 +222,24 @@ func addPeer(url *C.char) *C.char {
|
|||
return C.CString(string(outBytes))
|
||||
}
|
||||
|
||||
//export addWhisperFilter
|
||||
func addWhisperFilter(filterJson *C.char) *C.char {
|
||||
//export AddWhisperFilter
|
||||
func AddWhisperFilter(filterJson *C.char) *C.char {
|
||||
|
||||
var id int
|
||||
var filter whisper.NewFilterArgs
|
||||
|
||||
err := json.Unmarshal([]byte(C.GoString(filterJson)), &filter)
|
||||
if err == nil {
|
||||
id = doAddWhisperFilter(filter)
|
||||
id = geth.AddWhisperFilter(filter)
|
||||
}
|
||||
|
||||
errString := emptyError
|
||||
errString := ""
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
errString = err.Error()
|
||||
}
|
||||
|
||||
out := AddWhisperFilterResult{
|
||||
out := geth.AddWhisperFilterResult{
|
||||
Id: id,
|
||||
Error: errString,
|
||||
}
|
||||
|
@ -240,14 +249,12 @@ func addWhisperFilter(filterJson *C.char) *C.char {
|
|||
|
||||
}
|
||||
|
||||
//export removeWhisperFilter
|
||||
func removeWhisperFilter(idFilter int) {
|
||||
|
||||
doRemoveWhisperFilter(idFilter)
|
||||
//export RemoveWhisperFilter
|
||||
func RemoveWhisperFilter(idFilter int) {
|
||||
geth.RemoveWhisperFilter(idFilter)
|
||||
}
|
||||
|
||||
//export clearWhisperFilters
|
||||
func clearWhisperFilters() {
|
||||
|
||||
doClearWhisperFilters()
|
||||
//export ClearWhisperFilters
|
||||
func ClearWhisperFilters() {
|
||||
geth.ClearWhisperFilters()
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func copyFile(dst, src string) error {
|
||||
s, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
d, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
if _, err := io.Copy(d, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,30 +1,16 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool GethServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
||||
package geth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
errextra "github.com/pkg/errors"
|
||||
"github.com/status-im/status-go/src/extkeys"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidGethNode = errors.New("no running node detected for account unlock")
|
||||
ErrInvalidWhisperService = errors.New("whisper service is unavailable")
|
||||
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
||||
ErrAddressToAccountMappingFailure = errors.New("cannot retreive a valid account for a given address")
|
||||
ErrAccountToKeyMappingFailure = errors.New("cannot retreive a valid key for a given account")
|
||||
ErrUnlockCalled = errors.New("no need to unlock accounts, login instead")
|
||||
|
@ -32,32 +18,25 @@ var (
|
|||
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
|
||||
ErrWhisperNoIdentityFound = errors.New("failed to locate identity previously injected into Whisper")
|
||||
ErrNoAccountSelected = errors.New("no account has been selected, please login")
|
||||
ErrInvalidMasterKeyCreated = errors.New("can not create master extended key")
|
||||
)
|
||||
|
||||
// createAccount creates an internal geth account
|
||||
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||
// sub-account derivations)
|
||||
func createAccount(password string) (address, pubKey, mnemonic string, err error) {
|
||||
if currentNode == nil {
|
||||
return "", "", "", ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if accountManager == nil {
|
||||
return "", "", "", ErrInvalidAccountManager
|
||||
}
|
||||
|
||||
func CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
|
||||
// generate mnemonic phrase
|
||||
m := extkeys.NewMnemonic()
|
||||
m := extkeys.NewMnemonic(extkeys.Salt)
|
||||
mnemonic, err = m.MnemonicPhrase(128, extkeys.EnglishLanguage)
|
||||
if err != nil {
|
||||
return "", "", "", errextra.Wrap(err, "Can not create mnemonic seed")
|
||||
return "", "", "", fmt.Errorf("can not create mnemonic seed: %v", err)
|
||||
}
|
||||
|
||||
// generate extended master key (see BIP32)
|
||||
extKey, err := extkeys.NewMaster(m.MnemonicSeed(mnemonic, password), []byte(extkeys.Salt))
|
||||
if err != nil {
|
||||
return "", "", "", errextra.Wrap(err, "Can not create master extended key")
|
||||
return "", "", "", fmt.Errorf("can not create master extended key: %v", err)
|
||||
}
|
||||
|
||||
// import created key into account keystore
|
||||
|
@ -72,17 +51,15 @@ func createAccount(password string) (address, pubKey, mnemonic string, err error
|
|||
// createChildAccount creates sub-account for an account identified by parent address.
|
||||
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
||||
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
||||
func createChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||
if currentNode == nil {
|
||||
return "", "", ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if accountManager == nil {
|
||||
return "", "", ErrInvalidAccountManager
|
||||
func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
|
||||
nodeManager := GetNodeManager()
|
||||
accountManager, err := nodeManager.AccountManager()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if parentAddress == "" { // by default derive from currently selected account
|
||||
parentAddress = selectedAddress
|
||||
parentAddress = nodeManager.SelectedAddress
|
||||
}
|
||||
|
||||
if parentAddress == "" {
|
||||
|
@ -123,20 +100,12 @@ func createChildAccount(parentAddress, password string) (address, pubKey string,
|
|||
|
||||
// recoverAccount re-creates master key using given details.
|
||||
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
||||
func recoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
||||
if currentNode == nil {
|
||||
return "", "", ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if accountManager == nil {
|
||||
return "", "", ErrInvalidAccountManager
|
||||
}
|
||||
|
||||
func RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
|
||||
// re-create extended key (see BIP32)
|
||||
m := extkeys.NewMnemonic()
|
||||
m := extkeys.NewMnemonic(extkeys.Salt)
|
||||
extKey, err := extkeys.NewMaster(m.MnemonicSeed(mnemonic, password), []byte(extkeys.Salt))
|
||||
if err != nil {
|
||||
return "", "", errextra.Wrap(err, "Can not create master extended key")
|
||||
return "", "", ErrInvalidMasterKeyCreated
|
||||
}
|
||||
|
||||
// import re-created key into account keystore
|
||||
|
@ -151,14 +120,13 @@ func recoverAccount(password, mnemonic string) (address, pubKey string, err erro
|
|||
// selectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
||||
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
||||
// all previous identities are removed).
|
||||
func selectAccount(address, password string) error {
|
||||
if currentNode == nil {
|
||||
return ErrInvalidGethNode
|
||||
func SelectAccount(address, password string) error {
|
||||
nodeManager := GetNodeManager()
|
||||
accountManager, err := nodeManager.AccountManager()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if accountManager == nil {
|
||||
return ErrInvalidAccountManager
|
||||
}
|
||||
account, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
return ErrAddressToAccountMappingFailure
|
||||
|
@ -169,34 +137,35 @@ func selectAccount(address, password string) error {
|
|||
return fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
|
||||
}
|
||||
|
||||
if whisperService == nil {
|
||||
return ErrInvalidWhisperService
|
||||
whisperService, err := nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := whisperService.InjectIdentity(accountKey.PrivateKey); err != nil {
|
||||
return ErrWhisperIdentityInjectionFailure
|
||||
}
|
||||
|
||||
// persist address for easier recovery of currently selected key (from Whisper)
|
||||
selectedAddress = address
|
||||
nodeManager.SelectedAddress = address
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logout clears whisper identities
|
||||
func logout() error {
|
||||
if currentNode == nil {
|
||||
return ErrInvalidGethNode
|
||||
}
|
||||
if whisperService == nil {
|
||||
return ErrInvalidWhisperService
|
||||
func Logout() error {
|
||||
nodeManager := GetNodeManager()
|
||||
whisperService, err := nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := whisperService.ClearIdentities()
|
||||
err = whisperService.ClearIdentities()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
|
||||
}
|
||||
|
||||
selectedAddress = ""
|
||||
nodeManager.SelectedAddress = ""
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -204,85 +173,31 @@ func logout() error {
|
|||
// unlockAccount unlocks an existing account for a certain duration and
|
||||
// inject the account as a whisper identity if the account was created as
|
||||
// a whisper enabled account
|
||||
func unlockAccount(address, password string, seconds int) error {
|
||||
if currentNode == nil {
|
||||
return ErrInvalidGethNode
|
||||
}
|
||||
|
||||
func UnlockAccount(address, password string, seconds int) error {
|
||||
return ErrUnlockCalled
|
||||
}
|
||||
|
||||
// importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key.
|
||||
// Once account key is formed, that key is put (if not already) into keystore i.e. key is *encoded* into key file.
|
||||
func importExtendedKey(extKey *extkeys.ExtendedKey, password string) (address, pubKey string, err error) {
|
||||
accountManager, err := GetNodeManager().AccountManager()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// imports extended key, create key file (if necessary)
|
||||
account, err := accountManager.ImportExtendedKey(extKey, password)
|
||||
if err != nil {
|
||||
return "", "", errextra.Wrap(err, "Account manager could not create the account")
|
||||
return "", "", err
|
||||
}
|
||||
address = fmt.Sprintf("%x", account.Address)
|
||||
|
||||
// obtain public key to return
|
||||
account, key, err := accountManager.AccountDecryptedKey(account, password)
|
||||
if err != nil {
|
||||
return address, "", errextra.Wrap(err, "Could not recover the key")
|
||||
return address, "", err
|
||||
}
|
||||
pubKey = common.ToHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// createAndStartNode creates a node entity and starts the
|
||||
// node running locally
|
||||
func createAndStartNode(inputDir string) error {
|
||||
|
||||
currentNode = MakeNode(inputDir)
|
||||
if currentNode != nil {
|
||||
RunNode(currentNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("Could not create the in-memory node object")
|
||||
|
||||
}
|
||||
|
||||
func doAddPeer(url string) (bool, error) {
|
||||
server := currentNode.Server()
|
||||
if server == nil {
|
||||
return false, errors.New("node not started")
|
||||
}
|
||||
// Try to add the url as a static peer and return
|
||||
node, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
server.AddPeer(node)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func onSendTransactionRequest(queuedTx status.QueuedTx) {
|
||||
event := GethEvent{
|
||||
Type: "sendTransactionQueued",
|
||||
Event: SendTransactionEvent{
|
||||
Id: string(queuedTx.Id),
|
||||
Args: queuedTx.Args,
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(&event)
|
||||
C.GethServiceSignalEvent(C.CString(string(body)))
|
||||
}
|
||||
|
||||
func completeTransaction(id, password string) (common.Hash, error) {
|
||||
if currentNode != nil {
|
||||
if lightEthereum != nil {
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password)
|
||||
}
|
||||
|
||||
return common.Hash{}, errors.New("can not retrieve LES service")
|
||||
}
|
||||
|
||||
return common.Hash{}, errors.New("can not complete transaction: no running node detected")
|
||||
}
|
|
@ -0,0 +1,308 @@
|
|||
package geth_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/status-im/status-go/geth"
|
||||
)
|
||||
|
||||
func TestCreateChildAccount(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
account, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("can not get account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present
|
||||
account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if key.ExtendedKey == nil {
|
||||
t.Error("CKD#2 has not been generated for new account")
|
||||
return
|
||||
}
|
||||
|
||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||
_, _, err = geth.CreateChildAccount("", newAccountPassword)
|
||||
if !reflect.DeepEqual(err, geth.ErrNoAccountSelected) {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account w/o login): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = geth.SelectAccount(address, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// try to create sub-account with wrong password
|
||||
_, _, err = geth.CreateChildAccount("", "wrong password")
|
||||
if !reflect.DeepEqual(err, errors.New("cannot retreive a valid key for a given account: could not decrypt key with given passphrase")) {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account with wrong password): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// create sub-account (from implicit parent)
|
||||
subAccount1, subPubKey1, err := geth.CreateChildAccount("", newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure that sub-account index automatically progresses
|
||||
subAccount2, subPubKey2, err := geth.CreateChildAccount("", newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
}
|
||||
if subAccount1 == subAccount2 || subPubKey1 == subPubKey2 {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return
|
||||
}
|
||||
|
||||
// create sub-account (from explicit parent)
|
||||
subAccount3, subPubKey3, err := geth.CreateChildAccount(subAccount2, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
}
|
||||
if subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3 {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoverAccount(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
accountManager, _ := geth.GetNodeManager().AccountManager()
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
// try recovering using password + mnemonic
|
||||
addressCheck, pubKeyCheck, err := geth.RecoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed: %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details")
|
||||
}
|
||||
|
||||
// now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device
|
||||
account, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("can not get account from address: %v", err)
|
||||
}
|
||||
|
||||
account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
extChild2String := key.ExtendedKey.String()
|
||||
|
||||
if err := accountManager.DeleteAccount(account, newAccountPassword); err != nil {
|
||||
t.Errorf("cannot remove account: %v", err)
|
||||
}
|
||||
|
||||
addressCheck, pubKeyCheck, err = geth.RecoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed (for non-cached account): %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details (for non-cached account)")
|
||||
}
|
||||
|
||||
// make sure that extended key exists and is imported ok too
|
||||
account, key, err = accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
if extChild2String != key.ExtendedKey.String() {
|
||||
t.Errorf("CKD#2 key mismatch, expected: %s, got: %s", extChild2String, key.ExtendedKey.String())
|
||||
}
|
||||
|
||||
// make sure that calling import several times, just returns from cache (no error is expected)
|
||||
addressCheck, pubKeyCheck, err = geth.RecoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed (for non-cached account): %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details (for non-cached account)")
|
||||
}
|
||||
|
||||
// time to login with recovered data
|
||||
whisperService, err := geth.GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
err = geth.SelectAccount(addressCheck, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountSelect(t *testing.T) {
|
||||
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// test to see if the account was injected in whisper
|
||||
whisperService, err := geth.GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// create an account
|
||||
address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
|
||||
// try selecting with wrong password
|
||||
err = geth.SelectAccount(address1, "wrongPassword")
|
||||
if err == nil {
|
||||
t.Error("select account is expected to throw error: wrong password used")
|
||||
return
|
||||
}
|
||||
err = geth.SelectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
err = geth.SelectAccount(address2, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Error("identity should be removed, but it is still present in whisper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountLogout(t *testing.T) {
|
||||
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
whisperService, err := geth.GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, pubKey, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure that identity doesn't exist (yet) in Whisper
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
|
||||
// select/login
|
||||
err = geth.SelectAccount(address, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity not injected into whisper")
|
||||
}
|
||||
|
||||
err = geth.Logout()
|
||||
if err != nil {
|
||||
t.Errorf("cannot logout: %v", err)
|
||||
}
|
||||
|
||||
// now, logout and check if identity is removed indeed
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity not cleared from whisper")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package geth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/release"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
clientIdentifier = "Geth" // Client identifier to advertise over the network
|
||||
versionMajor = 1 // Major version component of the current release
|
||||
versionMinor = 5 // Minor version component of the current release
|
||||
versionPatch = 0 // Patch version component of the current release
|
||||
versionMeta = "unstable" // Version metadata to append to the version string
|
||||
|
||||
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle
|
||||
|
||||
RPCPort = 8545 // RPC port (replaced in unit tests)
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDataDirPreprocessingFailed = errors.New("failed to pre-process data directory")
|
||||
ErrInvalidGethNode = errors.New("no running geth node detected")
|
||||
ErrInvalidAccountManager = errors.New("could not retrieve account manager")
|
||||
ErrInvalidWhisperService = errors.New("whisper service is unavailable")
|
||||
ErrInvalidLightEthereumService = errors.New("can not retrieve LES service")
|
||||
ErrInvalidClient = errors.New("RPC client is not properly initialized")
|
||||
ErrNodeStartFailure = errors.New("could not create the in-memory node object")
|
||||
)
|
||||
|
||||
type NodeNotificationHandler func(jsonEvent string)
|
||||
|
||||
type NodeManager struct {
|
||||
currentNode *node.Node // currently running geth node
|
||||
ctx *cli.Context // the CLI context used to start the geth node
|
||||
lightEthereum *les.LightEthereum // LES service
|
||||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||
SelectedAddress string // address of the account that was processed during the last call to SelectAccount()
|
||||
whisperService *whisper.Whisper // Whisper service
|
||||
client *rpc.ClientRestartWrapper // RPC client
|
||||
notificationHandler NodeNotificationHandler // internal signal handler (used in tests)
|
||||
}
|
||||
|
||||
var (
|
||||
nodeManagerInstance *NodeManager
|
||||
createOnce sync.Once
|
||||
)
|
||||
|
||||
func NewNodeManager(datadir string, rpcport int) *NodeManager {
|
||||
createOnce.Do(func() {
|
||||
nodeManagerInstance = &NodeManager{}
|
||||
nodeManagerInstance.MakeNode(datadir, rpcport)
|
||||
nodeManagerInstance.SetNotificationHandler(func(jsonEvent string) {
|
||||
glog.V(logger.Info).Infof("internal notification received: %s\n", jsonEvent)
|
||||
})
|
||||
})
|
||||
|
||||
return nodeManagerInstance
|
||||
}
|
||||
|
||||
func GetNodeManager() *NodeManager {
|
||||
return nodeManagerInstance
|
||||
}
|
||||
|
||||
// createAndStartNode creates a node entity and starts the
|
||||
// node running locally
|
||||
func CreateAndRunNode(datadir string, rpcport int) error {
|
||||
nodeManager := NewNodeManager(datadir, rpcport)
|
||||
|
||||
if nodeManager.HasNode() {
|
||||
nodeManager.RunNode()
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrNodeStartFailure
|
||||
}
|
||||
|
||||
// MakeNode create a geth node entity
|
||||
func (m *NodeManager) MakeNode(datadir string, rpcport int) *node.Node {
|
||||
// TODO remove admin rpcapi flag
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("lightkdf", true, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength")
|
||||
set.Bool("shh", true, "whisper")
|
||||
set.Bool("light", true, "disable eth")
|
||||
set.Bool("testnet", true, "light test network")
|
||||
set.Bool("rpc", true, "enable rpc")
|
||||
set.String("rpcaddr", "localhost", "host for RPC")
|
||||
set.Int("rpcport", rpcport, "rpc port")
|
||||
set.String("rpccorsdomain", "*", "allow all domains")
|
||||
set.String("verbosity", "3", "verbosity level")
|
||||
set.String("rpcapi", "db,eth,net,web3,shh,personal,admin", "rpc api(s)")
|
||||
set.String("datadir", datadir, "data directory for geth")
|
||||
set.String("logdir", datadir, "log dir for glog")
|
||||
m.ctx = cli.NewContext(nil, set, nil)
|
||||
|
||||
// Construct the textual version string from the individual components
|
||||
vString := fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch)
|
||||
|
||||
// Construct the version release oracle configuration
|
||||
var rConfig release.Config
|
||||
rConfig.Oracle = common.HexToAddress(versionOracle)
|
||||
|
||||
rConfig.Major = uint32(versionMajor)
|
||||
rConfig.Minor = uint32(versionMinor)
|
||||
rConfig.Patch = uint32(versionPatch)
|
||||
|
||||
utils.DebugSetup(m.ctx)
|
||||
|
||||
// create node and start requested protocols
|
||||
m.currentNode = utils.MakeNode(m.ctx, clientIdentifier, vString)
|
||||
utils.RegisterEthService(m.ctx, m.currentNode, rConfig, makeDefaultExtra())
|
||||
|
||||
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
||||
shhEnabled := m.ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
||||
shhAutoEnabled := !m.ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && m.ctx.GlobalIsSet(utils.DevModeFlag.Name)
|
||||
if shhEnabled || shhAutoEnabled {
|
||||
utils.RegisterShhService(m.currentNode)
|
||||
}
|
||||
|
||||
m.accountManager = m.currentNode.AccountManager()
|
||||
|
||||
return m.currentNode
|
||||
}
|
||||
|
||||
// StartNode starts a geth node entity
|
||||
func (m *NodeManager) RunNode() {
|
||||
utils.StartNode(m.currentNode)
|
||||
|
||||
if m.currentNode.AccountManager() == nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get account manager")
|
||||
}
|
||||
if err := m.currentNode.Service(&m.whisperService); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get whisper service:", err)
|
||||
}
|
||||
if err := m.currentNode.Service(&m.lightEthereum); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
||||
}
|
||||
m.lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||
|
||||
m.client = rpc.NewClientRestartWrapper(func() *rpc.Client {
|
||||
client, err := m.currentNode.Attach()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return client
|
||||
})
|
||||
m.currentNode.Wait()
|
||||
}
|
||||
|
||||
func (m *NodeManager) AddPeer(url string) (bool, error) {
|
||||
if m == nil || !m.HasNode() {
|
||||
return false, ErrInvalidGethNode
|
||||
}
|
||||
|
||||
server := m.currentNode.Server()
|
||||
if server == nil {
|
||||
return false, errors.New("node not started")
|
||||
}
|
||||
// Try to add the url as a static peer and return
|
||||
parsedNode, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid enode: %v", err)
|
||||
}
|
||||
server.AddPeer(parsedNode)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) HasNode() bool {
|
||||
return m != nil && m.currentNode != nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) HasAccountManager() bool {
|
||||
return m.accountManager != nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) AccountManager() (*accounts.Manager, error) {
|
||||
if m == nil || !m.HasNode() {
|
||||
return nil, ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if !m.HasAccountManager() {
|
||||
return nil, ErrInvalidAccountManager
|
||||
}
|
||||
|
||||
return m.accountManager, nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) HasWhisperService() bool {
|
||||
return m.whisperService != nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) WhisperService() (*whisper.Whisper, error) {
|
||||
if m == nil || !m.HasNode() {
|
||||
return nil, ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if !m.HasWhisperService() {
|
||||
return nil, ErrInvalidWhisperService
|
||||
}
|
||||
|
||||
return m.whisperService, nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) HasLightEthereumService() bool {
|
||||
return m.lightEthereum != nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) LightEthereumService() (*les.LightEthereum, error) {
|
||||
if m == nil || !m.HasNode() {
|
||||
return nil, ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if !m.HasLightEthereumService() {
|
||||
return nil, ErrInvalidLightEthereumService
|
||||
}
|
||||
|
||||
return m.lightEthereum, nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) HasClientRestartWrapper() bool {
|
||||
return m.client != nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) {
|
||||
if m == nil || !m.HasNode() {
|
||||
return nil, ErrInvalidGethNode
|
||||
}
|
||||
|
||||
if !m.HasClientRestartWrapper() {
|
||||
return nil, ErrInvalidClient
|
||||
}
|
||||
|
||||
return m.client, nil
|
||||
}
|
||||
|
||||
func (m *NodeManager) SetNotificationHandler(fn NodeNotificationHandler) {
|
||||
m.notificationHandler = fn
|
||||
}
|
||||
|
||||
func (m *NodeManager) NotificationHandler() NodeNotificationHandler {
|
||||
return m.notificationHandler
|
||||
}
|
||||
|
||||
func makeDefaultExtra() []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
|
||||
return extra
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package geth_test
|
||||
|
||||
import (
|
||||
"github.com/status-im/status-go/geth"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
||||
testAddressPassword = "asdf"
|
||||
newAccountPassword = "badpassword"
|
||||
|
||||
whisperMessage1 = "test message 1 (K1 -> K1)"
|
||||
whisperMessage2 = "test message 2 (K1 -> '')"
|
||||
whisperMessage3 = "test message 3 ('' -> '')"
|
||||
whisperMessage4 = "test message 4 ('' -> K1)"
|
||||
whisperMessage5 = "test message 5 (K2 -> K1)"
|
||||
)
|
||||
|
||||
func TestNodeSetup(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
#if defined(IOS_DEPLOYMENT)
|
||||
// ======================================================================================
|
||||
// iOS framework compilation using xgo
|
||||
// ======================================================================================
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool StatusServiceSignalEvent(const char *jsonEvent) {
|
||||
// code for sending JSON notification up to iOS app
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(ANDROID_DEPLOYMENT)
|
||||
// ======================================================================================
|
||||
// Android archive compilation using xgo
|
||||
// ======================================================================================
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <jni.h>
|
||||
|
||||
bool StatusServiceSignalEvent(const char *jsonEvent);
|
||||
|
||||
static JavaVM *gJavaVM = NULL;
|
||||
static jclass JavaClassPtr_StatusService = NULL;
|
||||
static jmethodID JavaMethodPtr_signalEvent = NULL;
|
||||
|
||||
static bool JniLibraryInit(JNIEnv *env);
|
||||
|
||||
/*!
|
||||
* @brief Get interface to JNI.
|
||||
*
|
||||
* @return true if thread should be detached from JNI.
|
||||
*/
|
||||
static bool JniAttach(JNIEnv **env) {
|
||||
jint status;
|
||||
|
||||
if (gJavaVM == NULL) {
|
||||
env = NULL;
|
||||
}
|
||||
|
||||
status = (*gJavaVM)->GetEnv(gJavaVM, (void **)env, JNI_VERSION_1_6);
|
||||
if (status == JNI_EDETACHED) {
|
||||
// attach thread to JNI
|
||||
//(*gJavaVM)->AttachCurrentThread( gJavaVM, (void **)env, NULL ); // Oracle JNI API
|
||||
(*gJavaVM)->AttachCurrentThread(gJavaVM, env, NULL); // Android JNI API
|
||||
return true;
|
||||
} else if (status != JNI_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief The VM calls JNI_OnLoad when the native library is loaded.
|
||||
*/
|
||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
bool detach;
|
||||
JNIEnv *env;
|
||||
int result = JNI_VERSION_1_6;
|
||||
|
||||
gJavaVM = vm;
|
||||
|
||||
// attach thread to JNI
|
||||
detach = JniAttach(&env);
|
||||
if (env == NULL) {
|
||||
// failed
|
||||
gJavaVM = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!JniLibraryInit(env)) {
|
||||
// fail loading of JNI library
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
// detach thread from JNI
|
||||
(*gJavaVM)->DetachCurrentThread(gJavaVM);
|
||||
}
|
||||
|
||||
if (result != JNI_VERSION_1_6) {
|
||||
gJavaVM = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Initialize library.
|
||||
*/
|
||||
bool JniLibraryInit(JNIEnv *env) {
|
||||
int i;
|
||||
|
||||
JavaClassPtr_StatusService = (*env)->FindClass(env, "com/statusim/module/StatusService");
|
||||
if (JavaClassPtr_StatusService == NULL) return false;
|
||||
|
||||
JavaClassPtr_StatusService = (jclass)(*env)->NewGlobalRef(env, JavaClassPtr_StatusService);
|
||||
if (JavaClassPtr_StatusService == NULL) return false;
|
||||
|
||||
struct {
|
||||
bool bStatic;
|
||||
jclass classPtr;
|
||||
jmethodID *methodPtr;
|
||||
const char *methodId;
|
||||
const char *params;
|
||||
} javaMethodDescriptors[] = {
|
||||
{
|
||||
true,
|
||||
JavaClassPtr_StatusService,
|
||||
&JavaMethodPtr_signalEvent, // &JavaMethodPtr_someNonStaticMethod
|
||||
"signalEvent", // someNonStaticMethod
|
||||
"(Ljava/lang/String;)V"
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(javaMethodDescriptors) / sizeof(javaMethodDescriptors[0]); i++) {
|
||||
if (javaMethodDescriptors[i].bStatic) {
|
||||
*(javaMethodDescriptors[i].methodPtr) = (*env)->GetStaticMethodID(
|
||||
env, javaMethodDescriptors[i].classPtr, javaMethodDescriptors[i].methodId, javaMethodDescriptors[i].params);
|
||||
} else {
|
||||
*(javaMethodDescriptors[i].methodPtr) = (*env)->GetMethodID(
|
||||
env, javaMethodDescriptors[i].classPtr, javaMethodDescriptors[i].methodId, javaMethodDescriptors[i].params);
|
||||
}
|
||||
|
||||
if (*(javaMethodDescriptors[i].methodPtr) == NULL) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Calls static method signalEvent of class com.statusim.module.StatusService.
|
||||
*
|
||||
* @param jsonEvent - UTF8 string
|
||||
*/
|
||||
bool StatusServiceSignalEvent(const char *jsonEvent) {
|
||||
bool detach;
|
||||
JNIEnv *env;
|
||||
|
||||
// attach thread to JNI
|
||||
detach = JniAttach( &env );
|
||||
if (env == NULL) { // failed
|
||||
return false;
|
||||
}
|
||||
|
||||
jstring javaJsonEvent = NULL;
|
||||
if (jsonEvent != NULL) {
|
||||
javaJsonEvent = (*env)->NewStringUTF(env, jsonEvent);
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod(env, JavaClassPtr_StatusService, JavaMethodPtr_signalEvent, javaJsonEvent);
|
||||
|
||||
if (javaJsonEvent != NULL) (*env)->DeleteLocalRef(env, javaJsonEvent);
|
||||
|
||||
if (detach) { // detach thread from JNI
|
||||
(*gJavaVM)->DetachCurrentThread(gJavaVM);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
// ======================================================================================
|
||||
// cgo compilation (for local tests)
|
||||
// ======================================================================================
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
bool StatusServiceSignalEvent(const char *jsonEvent) {
|
||||
NotifyNode((char *)jsonEvent); // re-send notification back to status node
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
package geth
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool StatusServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
)
|
||||
|
||||
const (
|
||||
EventTransactionQueued = "transaction.queued"
|
||||
)
|
||||
|
||||
func onSendTransactionRequest(queuedTx status.QueuedTx) {
|
||||
event := GethEvent{
|
||||
Type: EventTransactionQueued,
|
||||
Event: SendTransactionEvent{
|
||||
Id: string(queuedTx.Id),
|
||||
Args: queuedTx.Args,
|
||||
},
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(&event)
|
||||
C.StatusServiceSignalEvent(C.CString(string(body)))
|
||||
}
|
||||
|
||||
func CompleteTransaction(id, password string) (common.Hash, error) {
|
||||
lightEthereum, err := GetNodeManager().LightEthereumService()
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
return backend.CompleteQueuedTransaction(status.QueuedTxId(id), password)
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package geth_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/status-im/status-go/geth"
|
||||
)
|
||||
|
||||
func TestQueuedTransactions(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
accountManager, err := geth.GetNodeManager().AccountManager()
|
||||
if err != nil {
|
||||
t.Errorf(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, _, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// test transaction queueing
|
||||
lightEthereum, err := geth.GetNodeManager().LightEthereumService()
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||
return
|
||||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// make sure you panic if transaction complete doesn't return
|
||||
completeQueuedTransaction := make(chan bool, 1)
|
||||
geth.PanicAfter(20*time.Second, completeQueuedTransaction)
|
||||
|
||||
// replace transaction notification handler
|
||||
var txHash = common.Hash{}
|
||||
geth.GetNodeManager().SetNotificationHandler(func(jsonEvent string) {
|
||||
var envelope geth.GethEvent
|
||||
if err := json.Unmarshal([]byte(jsonEvent), &envelope); err != nil {
|
||||
t.Errorf("cannot unmarshal event's JSON: %s", jsonEvent)
|
||||
return
|
||||
}
|
||||
if envelope.Type == geth.EventTransactionQueued {
|
||||
event := envelope.Event.(map[string]interface{})
|
||||
glog.V(logger.Info).Infof("Transaction queued (will be completed in 5 secs): {id: %s}\n", event["id"].(string))
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if txHash, err = geth.CompleteTransaction(event["id"].(string), testAddressPassword); err != nil {
|
||||
t.Errorf("cannot complete queued transation[%v]: %v", event["id"], err)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(logger.Info).Infof("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex())
|
||||
completeQueuedTransaction <- true // so that timeout is aborted
|
||||
}
|
||||
})
|
||||
|
||||
// try completing non-existing transaction
|
||||
if _, err := geth.CompleteTransaction("some-bad-transaction-id", testAddressPassword); err == nil {
|
||||
t.Error("error expected and not recieved")
|
||||
return
|
||||
}
|
||||
|
||||
// send normal transaction
|
||||
from, err := utils.MakeAddress(accountManager, testAddress)
|
||||
if err != nil {
|
||||
t.Errorf("could not retrieve account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
to, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("could not retrieve account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// this call blocks, up until Complete Transaction is called
|
||||
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||
From: from.Address,
|
||||
To: &to.Address,
|
||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: cannot send transaction: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(txHash, txHashCheck) {
|
||||
t.Error("Transaction hash returned from SendTransaction is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
if reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||
t.Error("Test failed: transaction was never queued or completed")
|
||||
return
|
||||
}
|
||||
|
||||
// now test eviction queue
|
||||
txQueue := backend.TransactionQueue()
|
||||
var i = 0
|
||||
backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) {
|
||||
//glog.V(logger.Info).Infof("%d. Transaction queued (queue size: %d): {id: %v}\n", i, txQueue.Count(), queuedTx.Id)
|
||||
i++
|
||||
})
|
||||
|
||||
if txQueue.Count() != 0 {
|
||||
t.Errorf("transaction count should be zero: %d", txQueue.Count())
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go backend.SendTransaction(nil, status.SendTxArgs{})
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
t.Logf("Number of transactions queued: %d. Queue size (shouldn't be more than %d): %d", i, status.DefaultTxQueueCap, txQueue.Count())
|
||||
|
||||
if txQueue.Count() != 10 {
|
||||
t.Errorf("transaction count should be 10: got %d", txQueue.Count())
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < status.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines
|
||||
go backend.SendTransaction(nil, status.SendTxArgs{})
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if txQueue.Count() != status.DefaultTxQueueCap && txQueue.Count() != (status.DefaultTxQueueCap-1) {
|
||||
t.Errorf("transaction count should be %d (or %d): got %d", status.DefaultTxQueueCap, status.DefaultTxQueueCap-1, txQueue.Count())
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package common
|
||||
package geth
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
|
@ -35,7 +35,7 @@ type WhisperMessageEvent struct {
|
|||
}
|
||||
|
||||
type SendTransactionEvent struct {
|
||||
Id string `json:"hash"`
|
||||
Id string `json:"id"`
|
||||
Args status.SendTxArgs `json:"args"`
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
package geth
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
var muPrepareTestNode sync.Mutex
|
||||
|
||||
const (
|
||||
testDataDir = "../.ethereumtest"
|
||||
testNodeSyncSeconds = 300
|
||||
)
|
||||
|
||||
//export NotifyNode
|
||||
func NotifyNode(jsonEvent *C.char) {
|
||||
GetNodeManager().NotificationHandler()(C.GoString(jsonEvent))
|
||||
}
|
||||
|
||||
func CopyFile(dst, src string) error {
|
||||
s, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
d, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
if _, err := io.Copy(d, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadFromFile is usefull for loading test data, from testdata/filename into a variable
|
||||
func LoadFromFile(filename string) string {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
io.Copy(buf, f)
|
||||
f.Close()
|
||||
|
||||
return string(buf.Bytes())
|
||||
}
|
||||
|
||||
func PrepareTestNode() (err error) {
|
||||
muPrepareTestNode.Lock()
|
||||
defer muPrepareTestNode.Unlock()
|
||||
|
||||
manager := GetNodeManager()
|
||||
if manager.HasNode() {
|
||||
return nil
|
||||
}
|
||||
|
||||
syncRequired := false
|
||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
||||
syncRequired = true
|
||||
}
|
||||
|
||||
// prepare node directory
|
||||
dataDir, err := PreprocessDataDir(testDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("make node failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// import test account (with test ether on it)
|
||||
dst := filepath.Join(testDataDir, "testnet", "keystore", "test-account.pk")
|
||||
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
||||
err = CopyFile(dst, filepath.Join("../data", "test-account.pk"))
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("cannot copy test account PK: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// start geth node and wait for it to initialize
|
||||
// internally once.Do() is used, so call below is thread-safe
|
||||
go CreateAndRunNode(dataDir, 8546) // to avoid conflicts with running react-native app, run on different port
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
manager = GetNodeManager()
|
||||
if !manager.HasNode() {
|
||||
panic("could not obtain geth node")
|
||||
}
|
||||
|
||||
manager.AddPeer("enode://409772c7dea96fa59a912186ad5bcdb5e51b80556b3fe447d940f99d9eaadb51d4f0ffedb68efad232b52475dd7bd59b51cee99968b3cc79e2d5684b33c4090c@139.162.166.59:30303")
|
||||
|
||||
if syncRequired {
|
||||
glog.V(logger.Warn).Infof("Sync is required, it will take %d seconds", testNodeSyncSeconds)
|
||||
time.Sleep(testNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done
|
||||
} else {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RemoveTestNode() {
|
||||
err := os.RemoveAll(testDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("could not clean up temporary datadir")
|
||||
}
|
||||
}
|
||||
|
||||
func PreprocessDataDir(dataDir string) (string, error) {
|
||||
testDataDir := path.Join(dataDir, "testnet", "keystore")
|
||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(testDataDir, 0755); err != nil {
|
||||
return dataDir, ErrDataDirPreprocessingFailed
|
||||
}
|
||||
}
|
||||
|
||||
// copy over static peer nodes list (LES auto-discovery is not stable yet)
|
||||
dst := filepath.Join(dataDir, "testnet", "static-nodes.json")
|
||||
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
||||
src := filepath.Join("../data", "static-nodes.json")
|
||||
if err := CopyFile(dst, src); err != nil {
|
||||
return dataDir, err
|
||||
}
|
||||
}
|
||||
|
||||
return dataDir, nil
|
||||
}
|
||||
|
||||
// PanicAfter throws panic() after waitSeconds, unless abort channel receives notification
|
||||
func PanicAfter(waitSeconds time.Duration, abort chan bool) {
|
||||
// panic if function takes too long
|
||||
timeout := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
time.Sleep(waitSeconds)
|
||||
timeout <- true
|
||||
}()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-abort:
|
||||
return
|
||||
case <-timeout:
|
||||
panic("function takes to long, which generally means we are stuck")
|
||||
}
|
||||
}()
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package geth
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool StatusServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
var (
|
||||
whisperFilters []int
|
||||
)
|
||||
|
||||
func onWhisperMessage(message *whisper.Message) {
|
||||
event := GethEvent{
|
||||
Type: "whisper",
|
||||
Event: WhisperMessageEvent{
|
||||
Payload: string(message.Payload),
|
||||
From: common.ToHex(crypto.FromECDSAPub(message.Recover())),
|
||||
To: common.ToHex(crypto.FromECDSAPub(message.To)),
|
||||
Sent: message.Sent.Unix(),
|
||||
TTL: int64(message.TTL / time.Second),
|
||||
Hash: common.ToHex(message.Hash.Bytes()),
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(&event)
|
||||
C.StatusServiceSignalEvent(C.CString(string(body)))
|
||||
}
|
||||
|
||||
func AddWhisperFilter(args whisper.NewFilterArgs) int {
|
||||
whisperService, err := GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
filter := whisper.Filter{
|
||||
To: crypto.ToECDSAPub(common.FromHex(args.To)),
|
||||
From: crypto.ToECDSAPub(common.FromHex(args.From)),
|
||||
Topics: whisper.NewFilterTopics(args.Topics...),
|
||||
Fn: onWhisperMessage,
|
||||
}
|
||||
|
||||
id := whisperService.Watch(filter)
|
||||
whisperFilters = append(whisperFilters, id)
|
||||
return id
|
||||
}
|
||||
|
||||
func RemoveWhisperFilter(idFilter int) {
|
||||
whisperService, err := GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
whisperService.Unwatch(idFilter)
|
||||
}
|
||||
|
||||
func ClearWhisperFilters() {
|
||||
for _, idFilter := range whisperFilters {
|
||||
RemoveWhisperFilter(idFilter)
|
||||
}
|
||||
whisperFilters = nil
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
package geth_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"github.com/status-im/status-go/geth"
|
||||
)
|
||||
|
||||
func TestWhisperMessaging(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
whisperService, err := geth.GetNodeManager().WhisperService()
|
||||
if err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperService)
|
||||
|
||||
// prepare message
|
||||
postArgs := whisper.PostArgs{
|
||||
From: "",
|
||||
To: "",
|
||||
TTL: 10,
|
||||
Topics: [][]byte{[]byte("test topic")},
|
||||
Payload: "test message",
|
||||
}
|
||||
|
||||
// create an accounts
|
||||
address1, pubKey1, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := geth.CreateAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// start watchers
|
||||
var receivedMessages = map[string]bool{
|
||||
whisperMessage1: false,
|
||||
whisperMessage2: false,
|
||||
whisperMessage3: false,
|
||||
whisperMessage4: false,
|
||||
whisperMessage5: false,
|
||||
}
|
||||
whisperService.Watch(whisper.Filter{
|
||||
//From: crypto.ToECDSAPub(common.FromHex(pubKey1)),
|
||||
//To: crypto.ToECDSAPub(common.FromHex(pubKey2)),
|
||||
Fn: func(msg *whisper.Message) {
|
||||
//glog.V(logger.Info).Infof("Whisper message received: %s", msg.Payload)
|
||||
receivedMessages[string(msg.Payload)] = true
|
||||
},
|
||||
})
|
||||
|
||||
// inject key of newly created account into Whisper, as identity
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
err = geth.SelectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
identitySucceess := whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1)))
|
||||
if !identitySucceess || err != nil {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
if whisperService.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) { // ensure that second id is not injected
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
|
||||
// double selecting (shouldn't be a problem)
|
||||
err = geth.SelectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TEST 0: From != nil && To != nil: encrypted signed message (but we cannot decrypt it - so watchers will not report this)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = pubKey2 // owner of that public key will be able to decrypt it
|
||||
postSuccess, err := whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 1: From != nil && To != nil: encrypted signed message (to self)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage1
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// send from account that is not in Whisper identity list
|
||||
postArgs.From = pubKey2
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if err == nil || err.Error() != fmt.Sprintf("unknown identity to send from: %s", pubKey2) {
|
||||
t.Error("expected error not voiced: we are sending from non-injected whisper identity")
|
||||
}
|
||||
|
||||
// TEST 2: From != nil && To == nil: signed broadcast (known sender)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = ""
|
||||
postArgs.Payload = whisperMessage2
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 3: From == nil && To == nil: anonymous broadcast
|
||||
postArgs.From = ""
|
||||
postArgs.To = ""
|
||||
postArgs.Payload = whisperMessage3
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 4: From == nil && To != nil: encrypted anonymous message
|
||||
postArgs.From = ""
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage4
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 5: From != nil && To != nil: encrypted and signed response
|
||||
postArgs.From = ""
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage5
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // allow whisper to poll
|
||||
for message, status := range receivedMessages {
|
||||
if !status {
|
||||
t.Errorf("Expected message not received: %s", message)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,259 @@
|
|||
package jail
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/robertkrimen/otto"
|
||||
"github.com/status-im/status-go/geth"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidJail = errors.New("jail environment is not properly initialized")
|
||||
)
|
||||
|
||||
type Jail struct {
|
||||
client *rpc.ClientRestartWrapper // lazy inited on the first call to jail.ClientRestartWrapper()
|
||||
VMs map[string]*otto.Otto
|
||||
statusJS string
|
||||
}
|
||||
|
||||
var jailInstance *Jail
|
||||
var once sync.Once
|
||||
|
||||
func New() *Jail {
|
||||
once.Do(func() {
|
||||
jailInstance = &Jail{
|
||||
VMs: make(map[string]*otto.Otto),
|
||||
}
|
||||
})
|
||||
|
||||
return jailInstance
|
||||
}
|
||||
|
||||
func Init(js string) *Jail {
|
||||
jailInstance = New() // singleton, we will always get the same reference
|
||||
jailInstance.statusJS = js
|
||||
|
||||
return jailInstance
|
||||
}
|
||||
|
||||
func GetInstance() *Jail {
|
||||
return New() // singleton, we will always get the same reference
|
||||
}
|
||||
|
||||
func (jail *Jail) Parse(chatId string, js string) string {
|
||||
if jail == nil {
|
||||
return printError(ErrInvalidJail.Error())
|
||||
}
|
||||
|
||||
vm := otto.New()
|
||||
initJjs := jail.statusJS + ";"
|
||||
jail.VMs[chatId] = vm
|
||||
_, err := vm.Run(initJjs)
|
||||
vm.Set("jeth", struct{}{})
|
||||
|
||||
jethObj, _ := vm.Get("jeth")
|
||||
jethObj.Object().Set("send", jail.Send)
|
||||
jethObj.Object().Set("sendAsync", jail.Send)
|
||||
|
||||
jjs := Web3_JS + `
|
||||
var Web3 = require('web3');
|
||||
var web3 = new Web3(jeth);
|
||||
var Bignumber = require("bignumber.js");
|
||||
function bn(val){
|
||||
return new Bignumber(val);
|
||||
}
|
||||
` + js + "; var catalog = JSON.stringify(_status_catalog);"
|
||||
vm.Run(jjs)
|
||||
|
||||
res, _ := vm.Get("catalog")
|
||||
|
||||
return printResult(res.String(), err)
|
||||
}
|
||||
|
||||
func (jail *Jail) Call(chatId string, path string, args string) string {
|
||||
_, err := jail.ClientRestartWrapper()
|
||||
if err != nil {
|
||||
return printError(err.Error())
|
||||
}
|
||||
|
||||
vm, ok := jail.VMs[chatId]
|
||||
if !ok {
|
||||
return printError(fmt.Sprintf("VM[%s] doesn't exist.", chatId))
|
||||
}
|
||||
|
||||
res, err := vm.Call("call", nil, path, args)
|
||||
|
||||
return printResult(res.String(), err)
|
||||
}
|
||||
|
||||
func (jail *Jail) GetVM(chatId string) (*otto.Otto, error) {
|
||||
if jail == nil {
|
||||
return nil, ErrInvalidJail
|
||||
}
|
||||
|
||||
vm, ok := jail.VMs[chatId]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("VM[%s] doesn't exist.", chatId)
|
||||
}
|
||||
|
||||
return vm, nil
|
||||
}
|
||||
|
||||
type jsonrpcCall struct {
|
||||
Id int64
|
||||
Method string
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
// Send will serialize the first argument, send it to the node and returns the response.
|
||||
func (jail *Jail) Send(call otto.FunctionCall) (response otto.Value) {
|
||||
clientFactory, err := jail.ClientRestartWrapper()
|
||||
if err != nil {
|
||||
return newErrorResponse(call, -32603, err.Error(), nil)
|
||||
}
|
||||
|
||||
// Remarshal the request into a Go value.
|
||||
JSON, _ := call.Otto.Object("JSON")
|
||||
reqVal, err := JSON.Call("stringify", call.Argument(0))
|
||||
if err != nil {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
var (
|
||||
rawReq = []byte(reqVal.String())
|
||||
reqs []jsonrpcCall
|
||||
batch bool
|
||||
)
|
||||
if rawReq[0] == '[' {
|
||||
batch = true
|
||||
json.Unmarshal(rawReq, &reqs)
|
||||
} else {
|
||||
batch = false
|
||||
reqs = make([]jsonrpcCall, 1)
|
||||
json.Unmarshal(rawReq, &reqs[0])
|
||||
}
|
||||
|
||||
// Execute the requests.
|
||||
resps, _ := call.Otto.Object("new Array()")
|
||||
for _, req := range reqs {
|
||||
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
|
||||
resp.Set("id", req.Id)
|
||||
var result json.RawMessage
|
||||
|
||||
client := clientFactory.Client()
|
||||
errc := make(chan error, 1)
|
||||
errc2 := make(chan error)
|
||||
go func() {
|
||||
errc2 <- <-errc
|
||||
}()
|
||||
errc <- client.Call(&result, req.Method, req.Params...)
|
||||
err = <-errc2
|
||||
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
if result == nil {
|
||||
// Special case null because it is decoded as an empty
|
||||
// raw message for some reason.
|
||||
resp.Set("result", otto.NullValue())
|
||||
} else {
|
||||
resultVal, err := JSON.Call("parse", string(result))
|
||||
if err != nil {
|
||||
resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object()
|
||||
} else {
|
||||
resp.Set("result", resultVal)
|
||||
}
|
||||
}
|
||||
case rpc.Error:
|
||||
resp.Set("error", map[string]interface{}{
|
||||
"code": err.ErrorCode(),
|
||||
"message": err.Error(),
|
||||
})
|
||||
default:
|
||||
resp = newErrorResponse(call, -32603, err.Error(), &req.Id).Object()
|
||||
}
|
||||
resps.Call("push", resp)
|
||||
}
|
||||
|
||||
// Return the responses either to the callback (if supplied)
|
||||
// or directly as the return value.
|
||||
if batch {
|
||||
response = resps.Value()
|
||||
} else {
|
||||
response, _ = resps.Get("0")
|
||||
}
|
||||
if fn := call.Argument(1); fn.Class() == "Function" {
|
||||
fn.Call(otto.NullValue(), otto.NullValue(), response)
|
||||
return otto.UndefinedValue()
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (jail *Jail) ClientRestartWrapper() (*rpc.ClientRestartWrapper, error) {
|
||||
if jail == nil {
|
||||
return nil, ErrInvalidJail
|
||||
}
|
||||
|
||||
if jail.client != nil {
|
||||
return jail.client, nil
|
||||
}
|
||||
|
||||
nodeManager := geth.GetNodeManager()
|
||||
if !nodeManager.HasNode() {
|
||||
return nil, geth.ErrInvalidGethNode
|
||||
}
|
||||
|
||||
// obtain RPC client from running node
|
||||
client, err := nodeManager.ClientRestartWrapper()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jail.client = client
|
||||
|
||||
return jail.client, nil
|
||||
}
|
||||
|
||||
func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) otto.Value {
|
||||
// Bundle the error into a JSON RPC call response
|
||||
m := map[string]interface{}{"version": "2.0", "id": id, "error": map[string]interface{}{"code": code, msg: msg}}
|
||||
res, _ := json.Marshal(m)
|
||||
val, _ := call.Otto.Run("(" + string(res) + ")")
|
||||
return val
|
||||
}
|
||||
|
||||
// throwJSException panics on an otto.Value. The Otto VM will recover from the
|
||||
// Go panic and throw msg as a JavaScript error.
|
||||
func throwJSException(msg interface{}) otto.Value {
|
||||
val, err := otto.ToValue(msg)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err)
|
||||
}
|
||||
panic(val)
|
||||
}
|
||||
|
||||
func printError(error string) string {
|
||||
str := geth.JSONError{
|
||||
Error: error,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&str)
|
||||
return string(outBytes)
|
||||
}
|
||||
|
||||
func printResult(res string, err error) string {
|
||||
var out string
|
||||
if err != nil {
|
||||
out = printError(err.Error())
|
||||
} else {
|
||||
if "undefined" == res {
|
||||
res = "null"
|
||||
}
|
||||
out = fmt.Sprintf(`{"result": %s}`, res)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
package jail_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/geth"
|
||||
"github.com/status-im/status-go/jail"
|
||||
)
|
||||
|
||||
const (
|
||||
TEST_ADDRESS = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
||||
CHAT_ID_INIT = "CHAT_ID_INIT_TEST"
|
||||
CHAT_ID_CALL = "CHAT_ID_CALL_TEST"
|
||||
CHAT_ID_NON_EXISTENT = "CHAT_IDNON_EXISTENT"
|
||||
|
||||
TESTDATA_STATUS_JS = "testdata/status.js"
|
||||
)
|
||||
|
||||
func TestJailUnInited(t *testing.T) {
|
||||
errorWrapper := func(err error) string {
|
||||
return `{"error":"` + err.Error() + `"}`
|
||||
}
|
||||
|
||||
expectedError := errorWrapper(jail.ErrInvalidJail)
|
||||
|
||||
var jailInstance *jail.Jail
|
||||
response := jailInstance.Parse(CHAT_ID_CALL, ``)
|
||||
if response != expectedError {
|
||||
t.Errorf("error expected, but got: %v", response)
|
||||
}
|
||||
|
||||
response = jailInstance.Call(CHAT_ID_CALL, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
if response != expectedError {
|
||||
t.Errorf("error expected, but got: %v", response)
|
||||
}
|
||||
|
||||
_, err := jailInstance.GetVM(CHAT_ID_CALL)
|
||||
if err != jail.ErrInvalidJail {
|
||||
t.Errorf("error expected, but got: %v", err)
|
||||
}
|
||||
|
||||
_, err = jailInstance.ClientRestartWrapper()
|
||||
if err != jail.ErrInvalidJail {
|
||||
t.Errorf("error expected, but got: %v", err)
|
||||
}
|
||||
|
||||
// now make sure that if Init is called, then Parse doesn't produce any error
|
||||
jailInstance = jail.Init(``)
|
||||
if jailInstance == nil {
|
||||
t.Error("jail instance shouldn't be nil at this point")
|
||||
return
|
||||
}
|
||||
statusJS := geth.LoadFromFile(TESTDATA_STATUS_JS) + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
response = jailInstance.Parse(CHAT_ID_CALL, statusJS)
|
||||
expectedResponse := `{"result": {"commands":{},"responses":{}}}`
|
||||
if response != expectedResponse {
|
||||
t.Errorf("unexpected response received: %v", response)
|
||||
}
|
||||
|
||||
// however, we still expect issue voiced if somebody tries to execute code with Call
|
||||
response = jailInstance.Call(CHAT_ID_CALL, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
if response != errorWrapper(geth.ErrInvalidGethNode) {
|
||||
t.Errorf("error expected, but got: %v", response)
|
||||
}
|
||||
|
||||
// make sure that Call() succeeds when node is started
|
||||
err = geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
response = jailInstance.Call(CHAT_ID_CALL, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse = `{"result": 144}`
|
||||
if response != expectedResponse {
|
||||
t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestJailInit(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
initCode := `
|
||||
var _status_catalog = {
|
||||
foo: 'bar'
|
||||
};
|
||||
`
|
||||
jailInstance := jail.Init(initCode)
|
||||
|
||||
extraCode := `
|
||||
var extraFunc = function (x) {
|
||||
return x * x;
|
||||
};
|
||||
`
|
||||
response := jailInstance.Parse(CHAT_ID_INIT, extraCode)
|
||||
|
||||
expectedResponse := `{"result": {"foo":"bar"}}`
|
||||
|
||||
if !reflect.DeepEqual(expectedResponse, response) {
|
||||
t.Error("Expected output not returned from jail.Parse()")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestJailFunctionCall(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
jailInstance := jail.Init("")
|
||||
|
||||
// load Status JS and add test command to it
|
||||
statusJS := geth.LoadFromFile(TESTDATA_STATUS_JS) + `;
|
||||
_status_catalog.commands["testCommand"] = function (params) {
|
||||
return params.val * params.val;
|
||||
};`
|
||||
jailInstance.Parse(CHAT_ID_CALL, statusJS)
|
||||
|
||||
// call with wrong chat id
|
||||
response := jailInstance.Call(CHAT_ID_NON_EXISTENT, "", "")
|
||||
expectedError := `{"error":"VM[CHAT_IDNON_EXISTENT] doesn't exist."}`
|
||||
if response != expectedError {
|
||||
t.Errorf("expected error is not returned: expected %s, got %s", expectedError, response)
|
||||
return
|
||||
}
|
||||
|
||||
// call extraFunc()
|
||||
response = jailInstance.Call(CHAT_ID_CALL, `["commands", "testCommand"]`, `{"val": 12}`)
|
||||
expectedResponse := `{"result": 144}`
|
||||
if response != expectedResponse {
|
||||
t.Errorf("expected response is not returned: expected %s, got %s", expectedResponse, response)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestJailRPCSend(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
jailInstance := jail.Init("")
|
||||
|
||||
// load Status JS and add test command to it
|
||||
statusJS := geth.LoadFromFile(TESTDATA_STATUS_JS)
|
||||
jailInstance.Parse(CHAT_ID_CALL, statusJS)
|
||||
|
||||
// obtain VM for a given chat (to send custom JS to jailed version of Send())
|
||||
vm, err := jailInstance.GetVM(CHAT_ID_CALL)
|
||||
if err != nil {
|
||||
t.Errorf("cannot get VM: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = vm.Run(`
|
||||
var data = {"jsonrpc":"2.0","method":"eth_getBalance","params":["` + TEST_ADDRESS + `", "latest"],"id":1};
|
||||
var sendResult = web3.currentProvider.send(data)
|
||||
console.log(JSON.stringify(sendResult))
|
||||
var sendResult = web3.fromWei(sendResult.result, "ether")
|
||||
`)
|
||||
if err != nil {
|
||||
t.Errorf("cannot run custom code on VM: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := vm.Get("sendResult")
|
||||
if err != nil {
|
||||
t.Errorf("cannot obtain result of balance check operation: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
balance, err := value.ToFloat()
|
||||
if err != nil {
|
||||
t.Errorf("cannot obtain result of balance check operation: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if balance < 90 || balance > 100 {
|
||||
t.Error("wrong balance (there should be lots of test Ether on that account)")
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("Balance of %.2f ETH found on '%s' account", balance, TEST_ADDRESS)
|
||||
}
|
||||
|
||||
func TestJailMultipleInitSingletonJail(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
jailInstance1 := jail.Init("")
|
||||
jailInstance2 := jail.Init("")
|
||||
jailInstance3 := jail.New()
|
||||
jailInstance4 := jail.GetInstance()
|
||||
|
||||
if !reflect.DeepEqual(jailInstance1, jailInstance2) {
|
||||
t.Error("singleton property of jail instance is violated")
|
||||
}
|
||||
if !reflect.DeepEqual(jailInstance2, jailInstance3) {
|
||||
t.Error("singleton property of jail instance is violated")
|
||||
}
|
||||
if !reflect.DeepEqual(jailInstance3, jailInstance4) {
|
||||
t.Error("singleton property of jail instance is violated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJailGetVM(t *testing.T) {
|
||||
err := geth.PrepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
jailInstance := jail.Init("")
|
||||
|
||||
expectedError := `VM[` + CHAT_ID_NON_EXISTENT + `] doesn't exist.`
|
||||
_, err = jailInstance.GetVM(CHAT_ID_NON_EXISTENT)
|
||||
if err == nil || err.Error() != expectedError {
|
||||
t.Error("expected error, but call succeeded")
|
||||
}
|
||||
|
||||
// now let's create VM..
|
||||
jailInstance.Parse(CHAT_ID_CALL, ``)
|
||||
// ..and see if VM becomes available
|
||||
_, err = jailInstance.GetVM(CHAT_ID_CALL)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
status.command({
|
||||
name: "location",
|
||||
description: "Send location",
|
||||
color: "#9a5dcf",
|
||||
preview: function (params) {
|
||||
var text = status.components.text(
|
||||
{
|
||||
style: {
|
||||
marginTop: 5,
|
||||
marginHorizontal: 0,
|
||||
fontSize: 14,
|
||||
fontFamily: "font",
|
||||
color: "black"
|
||||
}
|
||||
}, params.value);
|
||||
var uri = "https://maps.googleapis.com/maps/api/staticmap?center="
|
||||
+ params.value
|
||||
+ "&size=100x100&maptype=roadmap&key=AIzaSyBNsj1qoQEYPb3IllmWMAscuXW0eeuYqAA&language=en"
|
||||
+ "&markers=size:mid%7Ccolor:0xff0000%7Clabel:%7C"
|
||||
+ params.value;
|
||||
|
||||
var image = status.components.image(
|
||||
{
|
||||
source: {uri: uri},
|
||||
style: {
|
||||
width: 100,
|
||||
height: 100
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return status.components.view({}, [text, image]);
|
||||
}
|
||||
}).param({
|
||||
name: "address",
|
||||
type: status.types.TEXT,
|
||||
placeholder: "Address"
|
||||
});
|
||||
|
||||
var phones = [
|
||||
{
|
||||
number: "89171111111",
|
||||
description: "Number format 1"
|
||||
},
|
||||
{
|
||||
number: "89371111111",
|
||||
description: "Number format 1"
|
||||
},
|
||||
{
|
||||
number: "+79171111111",
|
||||
description: "Number format 2"
|
||||
},
|
||||
{
|
||||
number: "9171111111",
|
||||
description: "Number format 3"
|
||||
}
|
||||
];
|
||||
|
||||
function suggestionsContainerStyle(suggestionsCount) {
|
||||
return {
|
||||
marginVertical: 1,
|
||||
marginHorizontal: 0,
|
||||
height: Math.min(150, (56 * suggestionsCount)),
|
||||
backgroundColor: "white",
|
||||
borderRadius: 5
|
||||
};
|
||||
}
|
||||
|
||||
var suggestionContainerStyle = {
|
||||
paddingLeft: 16,
|
||||
backgroundColor: "white"
|
||||
};
|
||||
|
||||
var suggestionSubContainerStyle = {
|
||||
height: 56,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#0000001f"
|
||||
};
|
||||
|
||||
var valueStyle = {
|
||||
marginTop: 9,
|
||||
fontSize: 14,
|
||||
fontFamily: "font",
|
||||
color: "#000000de"
|
||||
};
|
||||
|
||||
var descriptionStyle = {
|
||||
marginTop: 1.5,
|
||||
fontSize: 14,
|
||||
fontFamily: "font",
|
||||
color: "#838c93de"
|
||||
};
|
||||
|
||||
function startsWith(str1, str2) {
|
||||
// String.startsWith(...) doesn't work in otto
|
||||
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
|
||||
}
|
||||
|
||||
function phoneSuggestions(params) {
|
||||
var ph, suggestions;
|
||||
if (!params.value || params.value == "") {
|
||||
ph = phones;
|
||||
} else {
|
||||
ph = phones.filter(function (phone) {
|
||||
return startsWith(phone.number, params.value);
|
||||
});
|
||||
}
|
||||
|
||||
if (ph.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
suggestions = ph.map(function (phone) {
|
||||
return status.components.touchable(
|
||||
{onPress: [status.events.SET_VALUE, phone.number]},
|
||||
status.components.view(suggestionContainerStyle,
|
||||
[status.components.view(suggestionSubContainerStyle,
|
||||
[
|
||||
status.components.text(
|
||||
{style: valueStyle},
|
||||
phone.number
|
||||
),
|
||||
status.components.text(
|
||||
{style: descriptionStyle},
|
||||
phone.description
|
||||
)
|
||||
])])
|
||||
);
|
||||
});
|
||||
|
||||
var view = status.components.scrollView(
|
||||
suggestionsContainerStyle(ph.length),
|
||||
suggestions
|
||||
);
|
||||
|
||||
return {markup: view};
|
||||
}
|
||||
|
||||
var phoneConfig = {
|
||||
name: "phone",
|
||||
description: "Send phone number",
|
||||
color: "#5fc48d",
|
||||
validator: function (params) {
|
||||
return {
|
||||
validationHandler: "phone",
|
||||
parameters: [params.value]
|
||||
};
|
||||
},
|
||||
params: [{
|
||||
name: "phone",
|
||||
type: status.types.PHONE,
|
||||
suggestions: phoneSuggestions,
|
||||
placeholder: "Phone number"
|
||||
}],
|
||||
handler: function (params) {
|
||||
return {
|
||||
event: "sign-up",
|
||||
params: [params.value]
|
||||
};
|
||||
}
|
||||
};
|
||||
status.response(phoneConfig);
|
||||
status.command(phoneConfig);
|
||||
|
||||
status.command({
|
||||
name: "help",
|
||||
description: "Help",
|
||||
color: "#7099e6",
|
||||
/* Validator example
|
||||
validator: function (params) {
|
||||
if (params.value != "3") {
|
||||
var error = status.components.view(
|
||||
{backgroundColor: "red"},
|
||||
[status.components.text({}, "ooops :(")]
|
||||
);
|
||||
return {errors: [error]}
|
||||
}
|
||||
},*/
|
||||
params: [{
|
||||
name: "query",
|
||||
type: status.types.TEXT
|
||||
}]
|
||||
});
|
||||
|
||||
status.response({
|
||||
name: "confirmation-code",
|
||||
color: "#7099e6",
|
||||
description: "Confirmation code",
|
||||
params: [{
|
||||
name: "code",
|
||||
type: status.types.NUMBER
|
||||
}],
|
||||
handler: function (params) {
|
||||
return {
|
||||
event: "confirm-sign-up",
|
||||
params: [params.value]
|
||||
};
|
||||
},
|
||||
validator: function(params){
|
||||
if(!/^[\d]{4}$/.test(params.value)){
|
||||
var error = status.components.validationMessage(
|
||||
"Confirmation code",
|
||||
"Wrong format"
|
||||
);
|
||||
|
||||
return {errors: [error]}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
status.response({
|
||||
name: "keypair",
|
||||
color: "#7099e6",
|
||||
description: "Keypair password",
|
||||
icon: "icon_lock_white",
|
||||
params: [{
|
||||
name: "password",
|
||||
type: status.types.PASSWORD
|
||||
}],
|
||||
handler: function (params) {
|
||||
return {
|
||||
event: "save-password",
|
||||
params: [params.value]
|
||||
};
|
||||
},
|
||||
preview: function (params) {
|
||||
return status.components.text(
|
||||
{
|
||||
style: {
|
||||
marginTop: 5,
|
||||
marginHorizontal: 0,
|
||||
fontSize: 14,
|
||||
fontFamily: "font",
|
||||
color: "black"
|
||||
}
|
||||
}, "*****");
|
||||
}
|
||||
});
|
||||
|
||||
function walletView(params) {
|
||||
if (params.value != "") {
|
||||
var url = params.value;
|
||||
if (!/^[a-zA-Z-_]+:/.test(url)) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
return {webViewUrl: url};
|
||||
}
|
||||
}
|
||||
|
||||
status.command({
|
||||
name: "browse",
|
||||
description: "browser",
|
||||
color: "#ffa500",
|
||||
fullscreen: true,
|
||||
suggestionsTrigger: 'on-send',
|
||||
params: [{
|
||||
name: "webpage",
|
||||
suggestions: walletView,
|
||||
type: status.types.TEXT
|
||||
}]
|
||||
});
|
||||
|
||||
function validateBalance(params) {
|
||||
try {
|
||||
var val = web3.toWei(params.value, "ether");
|
||||
} catch (err) {
|
||||
return {
|
||||
errors: [
|
||||
status.components.validationMessage(
|
||||
"Amount",
|
||||
"Amount is not valid number"//err.message
|
||||
)
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
var balance = web3.eth.getBalance(params.command.address);
|
||||
if (bn(val).greaterThan(bn(balance))) {
|
||||
return {
|
||||
errors: [
|
||||
status.components.validationMessage(
|
||||
"Amount",
|
||||
"Not enough ETH on balance ("
|
||||
+ web3.fromWei(balance, "ether")
|
||||
+ " ETH)"
|
||||
)
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function sendTransaction(params) {
|
||||
var data = {
|
||||
from: params.command.from,
|
||||
to: params.command.to,
|
||||
value: web3.toWei(params.value, "ether")
|
||||
};
|
||||
var hash = web3.eth.sendTransaction(data);
|
||||
|
||||
return {"transaction-hash": hash};
|
||||
}
|
||||
|
||||
status.command({
|
||||
name: "send",
|
||||
color: "#5fc48d",
|
||||
description: "Send transaction",
|
||||
params: [{
|
||||
name: "amount",
|
||||
type: status.types.NUMBER
|
||||
}],
|
||||
preview: function (params) {
|
||||
return status.components.text(
|
||||
{},
|
||||
params.value + " ETH"
|
||||
);
|
||||
},
|
||||
handler: sendTransaction,
|
||||
validator: validateBalance
|
||||
});
|
|
@ -0,0 +1,155 @@
|
|||
var _status_catalog = {
|
||||
commands: {},
|
||||
responses: {}
|
||||
};
|
||||
|
||||
function Command() {
|
||||
}
|
||||
function Response() {
|
||||
}
|
||||
|
||||
Command.prototype.addToCatalog = function () {
|
||||
_status_catalog.commands[this.name] = this;
|
||||
};
|
||||
|
||||
Command.prototype.param = function (parameter) {
|
||||
this.params.push(parameter);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Command.prototype.create = function (com) {
|
||||
this.name = com.name;
|
||||
this.description = com.description;
|
||||
this.handler = com.handler;
|
||||
this["has-handler"] = com.handler != null;
|
||||
this.validator = com.validator;
|
||||
this.color = com.color;
|
||||
this.icon = com.icon;
|
||||
this.params = com.params || [];
|
||||
this.preview = com.preview;
|
||||
this["suggestions-trigger"] = com.suggestionsTrigger || "on-change";
|
||||
this.fullscreen = com.fullscreen;
|
||||
this.addToCatalog();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
Response.prototype = Object.create(Command.prototype);
|
||||
Response.prototype.addToCatalog = function () {
|
||||
_status_catalog.responses[this.name] = this;
|
||||
};
|
||||
Response.prototype.onReceiveResponse = function (handler) {
|
||||
this.onReceive = handler;
|
||||
};
|
||||
|
||||
function call(pathStr, paramsStr) {
|
||||
var params = JSON.parse(paramsStr),
|
||||
path = JSON.parse(pathStr),
|
||||
fn, res;
|
||||
|
||||
fn = path.reduce(function (catalog, name) {
|
||||
if (catalog && catalog[name]) {
|
||||
return catalog[name];
|
||||
}
|
||||
},
|
||||
_status_catalog
|
||||
);
|
||||
|
||||
if (!fn) {
|
||||
return null;
|
||||
}
|
||||
|
||||
res = fn(params);
|
||||
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
|
||||
function text(options, s) {
|
||||
return ['text', options, s];
|
||||
}
|
||||
|
||||
function view(options, elements) {
|
||||
return ['view', options].concat(elements);
|
||||
}
|
||||
|
||||
function image(options) {
|
||||
return ['image', options];
|
||||
}
|
||||
|
||||
function touchable(options, element) {
|
||||
return ['touchable', options, element];
|
||||
}
|
||||
|
||||
function scrollView(options, elements) {
|
||||
return ['scroll-view', options].concat(elements);
|
||||
}
|
||||
|
||||
function webView(url) {
|
||||
return ['web-view', {
|
||||
source: {
|
||||
uri: url
|
||||
},
|
||||
javaScriptEnabled: true
|
||||
}];
|
||||
}
|
||||
|
||||
function validationMessage(titleText, descriptionText) {
|
||||
var titleStyle = {
|
||||
color: "white",
|
||||
fontSize: 12,
|
||||
fontFamily: "sans-serif"
|
||||
};
|
||||
var title = status.components.text(titleStyle, titleText);
|
||||
|
||||
var descriptionStyle = {
|
||||
color: "white",
|
||||
fontSize: 12,
|
||||
fontFamily: "sans-serif",
|
||||
opacity: 0.9
|
||||
};
|
||||
var description = status.components.text(descriptionStyle, descriptionText);
|
||||
|
||||
return status.components.view(
|
||||
{
|
||||
backgroundColor: "red",
|
||||
height: 61,
|
||||
paddingLeft: 16,
|
||||
paddingTop: 14,
|
||||
},
|
||||
[title, description]
|
||||
);
|
||||
}
|
||||
|
||||
var status = {
|
||||
command: function (h) {
|
||||
var command = new Command();
|
||||
return command.create(h);
|
||||
},
|
||||
response: function (h) {
|
||||
var response = new Response();
|
||||
return response.create(h);
|
||||
},
|
||||
autorun: function (commandName) {
|
||||
_status_catalog.autorun = commandName;
|
||||
},
|
||||
types: {
|
||||
TEXT: 'text',
|
||||
NUMBER: 'number',
|
||||
PHONE: 'phone',
|
||||
PASSWORD: 'password'
|
||||
},
|
||||
events: {
|
||||
SET_VALUE: 'set-value'
|
||||
},
|
||||
components: {
|
||||
view: view,
|
||||
text: text,
|
||||
image: image,
|
||||
touchable: touchable,
|
||||
scrollView: scrollView,
|
||||
webView: webView,
|
||||
validationMessage: validationMessage
|
||||
}
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package jail
|
||||
|
||||
const Web3_JS = `
|
||||
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
@ -1,500 +0,0 @@
|
|||
{
|
||||
"ImportPath": "github.com/status-im/statusgo/src",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v74",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/Gustav-Simonsson/go-opencl/cl",
|
||||
"Rev": "593e01cfc4f3353585015321e01951d4a907d3ef"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/aristanetworks/goarista/atime",
|
||||
"Rev": "41405b70e69314415c378d9456fd01075f2ad2f2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/ethash",
|
||||
"Comment": "v23.1-247-g2e80de5",
|
||||
"Rev": "2e80de5022370cfe632195b1720db52d07ff8a77"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/accounts",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/accounts/abi",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/accounts/abi/bind",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/cmd/utils",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common/compiler",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common/httpclient",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common/mclock",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common/registrar",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/common/registrar/ethreg",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/core",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/core/state",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/core/types",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/core/vm",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/crypto",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/crypto/ecies",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/crypto/randentropy",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/crypto/secp256k1",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/crypto/sha3",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/eth",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/eth/downloader",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/eth/fetcher",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/eth/filters",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/eth/gasprice",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/ethdb",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/event",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/event/filter",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/internal/debug",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/internal/ethapi",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/les",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/les/flowcontrol",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/light",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/logger",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/logger/glog",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/metrics",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/miner",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/node",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/p2p",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/p2p/discover",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/p2p/nat",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/params",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/pow",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/release",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/rlp",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/rpc",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/trie",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ethereum/go-ethereum/whisper",
|
||||
"Comment": "v1.0.1-1001-g66560ee",
|
||||
"Rev": "66560ee28d836e91ac50bf2d6236b7ee4c9ecadf"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/snappy",
|
||||
"Rev": "d9eb7a3d35ec988b8585d4a0068e462c27d28380"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/golang-lru",
|
||||
"Rev": "a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/golang-lru/simplelru",
|
||||
"Rev": "a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/dcps/internetgateway1",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/dcps/internetgateway2",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/httpu",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/scpd",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/soap",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/huin/goupnp/ssdp",
|
||||
"Rev": "46bde78b11f3f021f2a511df138be9e2fc7506e8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/jackpal/go-nat-pmp",
|
||||
"Comment": "v1.0.1-4-g1fa385a",
|
||||
"Rev": "1fa385a6f45828c83361136b45b1a21a12139493"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/microsoft/go-winio",
|
||||
"Comment": "v0.3.5",
|
||||
"Rev": "4f1a71750d95a5a8a46c40a67ffbed8129c2f138"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pborman/uuid",
|
||||
"Comment": "v1.0-11-gc55201b",
|
||||
"Rev": "c55201b036063326c5b1b89ccfe45a184973d073"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pkg/errors",
|
||||
"Comment": "v0.4.0",
|
||||
"Rev": "d814416a46cbb066b728cfff58d30a986bc9ddbe"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rcrowley/go-metrics",
|
||||
"Rev": "eeba7bd0dd01ace6e690fa833b3f22aaec29af43"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rjeczalik/notify",
|
||||
"Rev": "5dd6205716539662f8f14ab513552b41eab69d5d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rs/cors",
|
||||
"Rev": "3ca2b550f6a4333b63c845850f760a7d00412cd6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/rs/xhandler",
|
||||
"Rev": "d9d9599b6aaf6a058cb7b1f48291ded2cbd13390"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/cache",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/comparer",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/errors",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/filter",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/iterator",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/journal",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/memdb",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/opt",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/storage",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/table",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/syndtr/goleveldb/leveldb/util",
|
||||
"Rev": "cfa635847112c5dc4782e128fa7e0d05fdbfb394"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/pbkdf2",
|
||||
"Rev": "1777f3ba8c1fed80fcaec3317e3aaa4f627764d2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/ripemd160",
|
||||
"Rev": "1777f3ba8c1fed80fcaec3317e3aaa4f627764d2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/scrypt",
|
||||
"Rev": "1777f3ba8c1fed80fcaec3317e3aaa4f627764d2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/context",
|
||||
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html",
|
||||
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/atom",
|
||||
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/html/charset",
|
||||
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/websocket",
|
||||
"Rev": "fb93926129b8ec0056f2f458b1f519654814edf0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/sys/windows",
|
||||
"Rev": "f64b50fbea64174967a8882830d621a18ee1548e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/charmap",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/htmlindex",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/internal",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/internal/identifier",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/japanese",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/korean",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/simplifiedchinese",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/traditionalchinese",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/encoding/unicode",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/internal/tag",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/internal/utf8internal",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/language",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/runes",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/text/transform",
|
||||
"Rev": "a71fd10341b064c10f4a81ceac72bcf70f26ea34"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/tools/go/ast/astutil",
|
||||
"Rev": "5e468032ea9e193c60de97cfcd040ffa7a9b774e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/tools/imports",
|
||||
"Rev": "5e468032ea9e193c60de97cfcd040ffa7a9b774e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/fatih/set.v0",
|
||||
"Comment": "v0.1.0-3-g27c4092",
|
||||
"Rev": "27c40922c40b43fe04554d8223a402af3ea333f3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/karalabe/cookiejar.v2/collections/prque",
|
||||
"Rev": "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/urfave/cli.v1",
|
||||
"Comment": "v1.18.0",
|
||||
"Rev": "1efa31f08b9333f1bd4882d61f9d668a70cd902e"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
|
@ -1,631 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/les/status"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
testDataDir = ".ethereumtest"
|
||||
testAddress = "0x89b50b2b26947ccad43accaef76c21d175ad85f4"
|
||||
testAddressPassword = "asdf"
|
||||
testNodeSyncSeconds = 180
|
||||
newAccountPassword = "badpassword"
|
||||
|
||||
whisperMessage1 = "test message 1 (K1 -> K1)"
|
||||
whisperMessage2 = "test message 2 (K1 -> '')"
|
||||
whisperMessage3 = "test message 3 ('' -> '')"
|
||||
whisperMessage4 = "test message 4 ('' -> K1)"
|
||||
whisperMessage5 = "test message 5 (K2 -> K1)"
|
||||
)
|
||||
|
||||
func TestCreateChildAccount(t *testing.T) {
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
account, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("can not get account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// obtain decrypted key, and make sure that extended key (which will be used as root for sub-accounts) is present
|
||||
account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if key.ExtendedKey == nil {
|
||||
t.Error("CKD#2 has not been generated for new account")
|
||||
return
|
||||
}
|
||||
|
||||
// try creating sub-account, w/o selecting main account i.e. w/o login to main account
|
||||
_, _, err = createChildAccount("", newAccountPassword)
|
||||
if !reflect.DeepEqual(err, ErrNoAccountSelected) {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account w/o login): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = selectAccount(address, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// try to create sub-account with wrong password
|
||||
_, _, err = createChildAccount("", "wrong password")
|
||||
if !reflect.DeepEqual(err, errors.New("cannot retreive a valid key for a given account: could not decrypt key with given passphrase")) {
|
||||
t.Errorf("expected error is not returned (tried to create sub-account with wrong password): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// create sub-account (from implicit parent)
|
||||
subAccount1, subPubKey1, err := createChildAccount("", newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure that sub-account index automatically progresses
|
||||
subAccount2, subPubKey2, err := createChildAccount("", newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
}
|
||||
if subAccount1 == subAccount2 || subPubKey1 == subPubKey2 {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return
|
||||
}
|
||||
|
||||
// create sub-account (from explicit parent)
|
||||
subAccount3, subPubKey3, err := createChildAccount(subAccount2, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("cannot create sub-account: %v", err)
|
||||
}
|
||||
if subAccount1 == subAccount3 || subPubKey1 == subPubKey3 || subAccount2 == subAccount3 || subPubKey2 == subPubKey3 {
|
||||
t.Error("sub-account index auto-increament failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoverAccount(t *testing.T) {
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, pubKey, mnemonic, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s, mnemonic:%s}", address, pubKey, mnemonic)
|
||||
|
||||
// try recovering using password + mnemonic
|
||||
addressCheck, pubKeyCheck, err := recoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed: %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details")
|
||||
}
|
||||
|
||||
// now test recovering, but make sure that account/key file is removed i.e. simulate recovering on a new device
|
||||
account, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("can not get account from address: %v", err)
|
||||
}
|
||||
|
||||
account, key, err := accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
extChild2String := key.ExtendedKey.String()
|
||||
|
||||
if err := accountManager.DeleteAccount(account, newAccountPassword); err != nil {
|
||||
t.Errorf("cannot remove account: %v", err)
|
||||
}
|
||||
|
||||
addressCheck, pubKeyCheck, err = recoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed (for non-cached account): %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details (for non-cached account)")
|
||||
}
|
||||
|
||||
// make sure that extended key exists and is imported ok too
|
||||
account, key, err = accountManager.AccountDecryptedKey(account, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("can not obtain decrypted account key: %v", err)
|
||||
return
|
||||
}
|
||||
if extChild2String != key.ExtendedKey.String() {
|
||||
t.Errorf("CKD#2 key mismatch, expected: %s, got: %s", extChild2String, key.ExtendedKey.String())
|
||||
}
|
||||
|
||||
// make sure that calling import several times, just returns from cache (no error is expected)
|
||||
addressCheck, pubKeyCheck, err = recoverAccount(newAccountPassword, mnemonic)
|
||||
if err != nil {
|
||||
t.Errorf("recover account failed (for non-cached account): %v", err)
|
||||
return
|
||||
}
|
||||
if address != addressCheck || pubKey != pubKeyCheck {
|
||||
t.Error("recover account details failed to pull the correct details (for non-cached account)")
|
||||
}
|
||||
|
||||
// time to login with recovered data
|
||||
var whisperInstance *whisper.Whisper
|
||||
if err := currentNode.Service(&whisperInstance); err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) {
|
||||
t.Errorf("identity already present in whisper")
|
||||
}
|
||||
err = selectAccount(addressCheck, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKeyCheck))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountSelect(t *testing.T) {
|
||||
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// test to see if the account was injected in whisper
|
||||
var whisperInstance *whisper.Whisper
|
||||
if err := currentNode.Service(&whisperInstance); err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// create an account
|
||||
address1, pubKey1, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// make sure that identity is not (yet injected)
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Errorf("identity already present in whisper")
|
||||
}
|
||||
|
||||
// try selecting with wrong password
|
||||
err = selectAccount(address1, "wrongPassword")
|
||||
if err == nil {
|
||||
t.Errorf("select account is expected to throw error: wrong password used")
|
||||
return
|
||||
}
|
||||
err = selectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
|
||||
// select another account, make sure that previous account is wiped out from Whisper cache
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||
t.Errorf("identity already present in whisper")
|
||||
}
|
||||
err = selectAccount(address2, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Errorf("identity should be removed, but it is still present in whisper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountLogout(t *testing.T) {
|
||||
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
var whisperInstance *whisper.Whisper
|
||||
if err := currentNode.Service(&whisperInstance); err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, pubKey, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// make sure that identity doesn't exist (yet) in Whisper
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity already present in whisper")
|
||||
}
|
||||
|
||||
// select/login
|
||||
err = selectAccount(address, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
if !whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity not injected into whisper")
|
||||
}
|
||||
|
||||
err = logout()
|
||||
if err != nil {
|
||||
t.Errorf("cannot logout: %v", err)
|
||||
}
|
||||
|
||||
// now, logout and check if identity is removed indeed
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey))) {
|
||||
t.Error("identity not cleared from whisper")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhisperMessaging(t *testing.T) {
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// test to see if the account was injected in whisper
|
||||
var whisperInstance *whisper.Whisper
|
||||
if err := currentNode.Service(&whisperInstance); err != nil {
|
||||
t.Errorf("whisper service not running: %v", err)
|
||||
}
|
||||
whisperAPI := whisper.NewPublicWhisperAPI(whisperInstance)
|
||||
|
||||
// prepare message
|
||||
postArgs := whisper.PostArgs{
|
||||
From: "",
|
||||
To: "",
|
||||
TTL: 10,
|
||||
Topics: [][]byte{[]byte("test topic")},
|
||||
Payload: "test message",
|
||||
}
|
||||
|
||||
// create an accounts
|
||||
address1, pubKey1, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address1, pubKey1)
|
||||
|
||||
address2, pubKey2, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
t.Error("Test failed: could not create account")
|
||||
return
|
||||
}
|
||||
glog.V(logger.Info).Infof("Account created: {address: %s, key: %s}", address2, pubKey2)
|
||||
|
||||
// start watchers
|
||||
var receivedMessages = map[string]bool{
|
||||
whisperMessage1: false,
|
||||
whisperMessage2: false,
|
||||
whisperMessage3: false,
|
||||
whisperMessage4: false,
|
||||
whisperMessage5: false,
|
||||
}
|
||||
whisperService.Watch(whisper.Filter{
|
||||
//From: crypto.ToECDSAPub(common.FromHex(pubKey1)),
|
||||
//To: crypto.ToECDSAPub(common.FromHex(pubKey2)),
|
||||
Fn: func(msg *whisper.Message) {
|
||||
glog.V(logger.Info).Infof("Whisper message received: %s", msg.Payload)
|
||||
receivedMessages[string(msg.Payload)] = true
|
||||
},
|
||||
})
|
||||
|
||||
// inject key of newly created account into Whisper, as identity
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1))) {
|
||||
t.Errorf("identity already present in whisper")
|
||||
}
|
||||
err = selectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
identitySucceess := whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey1)))
|
||||
if !identitySucceess || err != nil {
|
||||
t.Errorf("identity not injected into whisper: %v", err)
|
||||
}
|
||||
if whisperInstance.HasIdentity(crypto.ToECDSAPub(common.FromHex(pubKey2))) { // ensure that second id is not injected
|
||||
t.Errorf("identity already present in whisper")
|
||||
}
|
||||
|
||||
// double selecting (shouldn't be a problem)
|
||||
err = selectAccount(address1, newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: could not select account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TEST 0: From != nil && To != nil: encrypted signed message (but we cannot decrypt it - so watchers will not report this)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = pubKey2 // owner of that public key will be able to decrypt it
|
||||
postSuccess, err := whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 1: From != nil && To != nil: encrypted signed message (to self)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage1
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// send from account that is not in Whisper identity list
|
||||
postArgs.From = pubKey2
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if err == nil || err.Error() != fmt.Sprintf("unknown identity to send from: %s", pubKey2) {
|
||||
t.Errorf("expected error not voiced: we are sending from non-injected whisper identity")
|
||||
}
|
||||
|
||||
// TEST 2: From != nil && To == nil: signed broadcast (known sender)
|
||||
postArgs.From = pubKey1
|
||||
postArgs.To = ""
|
||||
postArgs.Payload = whisperMessage2
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 3: From == nil && To == nil: anonymous broadcast
|
||||
postArgs.From = ""
|
||||
postArgs.To = ""
|
||||
postArgs.Payload = whisperMessage3
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 4: From == nil && To != nil: encrypted anonymous message
|
||||
postArgs.From = ""
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage4
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
// TEST 5: From != nil && To != nil: encrypted and signed response
|
||||
postArgs.From = ""
|
||||
postArgs.To = pubKey1
|
||||
postArgs.Payload = whisperMessage5
|
||||
postSuccess, err = whisperAPI.Post(postArgs)
|
||||
if !postSuccess || err != nil {
|
||||
t.Errorf("could not post to whisper: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second) // allow whisper to poll
|
||||
for message, status := range receivedMessages {
|
||||
if !status {
|
||||
t.Errorf("Expected message not received: %s", message)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestQueuedTransactions(t *testing.T) {
|
||||
err := prepareTestNode()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// create an account
|
||||
address, _, _, err := createAccount(newAccountPassword)
|
||||
if err != nil {
|
||||
t.Errorf("could not create account: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// test transaction queueing
|
||||
var lightEthereum *les.LightEthereum
|
||||
if err := currentNode.Service(&lightEthereum); err != nil {
|
||||
t.Errorf("Test failed: LES service is not running: %v", err)
|
||||
}
|
||||
backend := lightEthereum.StatusBackend
|
||||
|
||||
// replace transaction notification handler
|
||||
var txHash = common.Hash{}
|
||||
backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) {
|
||||
glog.V(logger.Info).Infof("Transaction queued (will be completed in 5 secs): {id: %v, hash: %v}\n", queuedTx.Id, queuedTx.Hash.Hex())
|
||||
time.Sleep(5 * time.Second)
|
||||
if txHash, err = completeTransaction(string(queuedTx.Id), testAddressPassword); err != nil {
|
||||
t.Errorf("cannot complete queued transation[%v]: %v", queuedTx.Id, err)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(logger.Info).Infof("Transaction complete: https://testnet.etherscan.io/tx/%s", txHash.Hex())
|
||||
})
|
||||
|
||||
// try completing non-existing transaction
|
||||
if _, err := completeTransaction("some-bad-transaction-id", testAddressPassword); err == nil {
|
||||
t.Errorf("Test failed: error expected and not recieved")
|
||||
return
|
||||
}
|
||||
|
||||
// send normal transaction
|
||||
from, err := utils.MakeAddress(accountManager, testAddress)
|
||||
if err != nil {
|
||||
t.Errorf("could not retrieve account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
to, err := utils.MakeAddress(accountManager, address)
|
||||
if err != nil {
|
||||
t.Errorf("could not retrieve account from address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// this call blocks, up until Complete Transaction is called
|
||||
txHashCheck, err := backend.SendTransaction(nil, status.SendTxArgs{
|
||||
From: from.Address,
|
||||
To: &to.Address,
|
||||
Value: rpc.NewHexNumber(big.NewInt(1000000000000)),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("Test failed: cannot send transaction: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(txHash, txHashCheck) {
|
||||
t.Errorf("Transaction hash returned from SendTransaction is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
if reflect.DeepEqual(txHashCheck, common.Hash{}) {
|
||||
t.Error("Test failed: transaction was never queued or completed")
|
||||
return
|
||||
}
|
||||
|
||||
// now test eviction queue
|
||||
txQueue := backend.TransactionQueue()
|
||||
var i = 0
|
||||
backend.SetTransactionQueueHandler(func(queuedTx status.QueuedTx) {
|
||||
//glog.V(logger.Info).Infof("%d. Transaction queued (queue size: %d): {id: %v}\n", i, txQueue.Count(), queuedTx.Id)
|
||||
i++
|
||||
})
|
||||
|
||||
if txQueue.Count() != 0 {
|
||||
t.Errorf("transaction count should be zero: %d", txQueue.Count())
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go backend.SendTransaction(nil, status.SendTxArgs{})
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
t.Logf("Number of transactions queued: %d. Queue size (shouldn't be more than %d): %d", i, status.DefaultTxQueueCap, txQueue.Count())
|
||||
|
||||
if txQueue.Count() != 10 {
|
||||
t.Errorf("transaction count should be 10: got %d", txQueue.Count())
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < status.DefaultTxQueueCap+5; i++ { // stress test by hitting with lots of goroutines
|
||||
go backend.SendTransaction(nil, status.SendTxArgs{})
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
if txQueue.Count() != status.DefaultTxQueueCap && txQueue.Count() != (status.DefaultTxQueueCap-1) {
|
||||
t.Errorf("transaction count should be %d (or %d): got %d", status.DefaultTxQueueCap, status.DefaultTxQueueCap-1, txQueue.Count())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func prepareTestNode() error {
|
||||
if currentNode != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rpcport = 8546 // in order to avoid conflicts with running react-native app
|
||||
|
||||
syncRequired := false
|
||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
||||
syncRequired = true
|
||||
}
|
||||
|
||||
dataDir, err := preprocessDataDir(testDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("make node failed:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// import test account (with test ether on it)
|
||||
err = copyFile(filepath.Join(testDataDir, "testnet", "keystore", "test-account.pk"), filepath.Join("data", "test-account.pk"))
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("Test failed: cannot copy test account PK: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// start geth node and wait for it to initialize
|
||||
go createAndStartNode(dataDir)
|
||||
time.Sleep(5 * time.Second)
|
||||
if currentNode == nil {
|
||||
return errors.New("Test failed: could not start geth node")
|
||||
}
|
||||
|
||||
if syncRequired {
|
||||
glog.V(logger.Warn).Infof("Sync is required, it will take %d seconds", testNodeSyncSeconds)
|
||||
time.Sleep(testNodeSyncSeconds * time.Second) // LES syncs headers, so that we are up do date when it is done
|
||||
} else {
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
err := os.RemoveAll(testDataDir)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("Test failed: could not clean up temporary datadir")
|
||||
}
|
||||
}
|
207
src/jail.go
207
src/jail.go
|
@ -1,207 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/robertkrimen/otto"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var statusJs string
|
||||
var vms = make(map[string]*otto.Otto)
|
||||
|
||||
func Init(js string) {
|
||||
statusJs = js
|
||||
}
|
||||
|
||||
func printError(error string) string {
|
||||
str := JSONError{
|
||||
Error: error,
|
||||
}
|
||||
outBytes, _ := json.Marshal(&str)
|
||||
return string(outBytes)
|
||||
}
|
||||
|
||||
func printResult(res string, err error) string {
|
||||
var out string
|
||||
if err != nil {
|
||||
out = printError(err.Error())
|
||||
} else {
|
||||
if "undefined" == res {
|
||||
res = "null";
|
||||
}
|
||||
out = fmt.Sprintf(`{"result": %s}`, res)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func Parse(chatId string, js string) string {
|
||||
vm := otto.New()
|
||||
initJjs := statusJs + ";"
|
||||
vms[chatId] = vm
|
||||
_, err := vm.Run(initJjs)
|
||||
vm.Set("jeth", struct{}{})
|
||||
|
||||
jethObj, _ := vm.Get("jeth")
|
||||
jethObj.Object().Set("send", Send)
|
||||
jethObj.Object().Set("sendAsync", Send)
|
||||
|
||||
jjs := Web3_JS + `
|
||||
var Web3 = require('web3');
|
||||
var web3 = new Web3(jeth);
|
||||
var Bignumber = require("bignumber.js");
|
||||
function bn(val){
|
||||
return new Bignumber(val);
|
||||
}
|
||||
` + js + "; var catalog = JSON.stringify(_status_catalog);"
|
||||
vm.Run(jjs)
|
||||
|
||||
res, _ := vm.Get("catalog")
|
||||
|
||||
return printResult(res.String(), err)
|
||||
}
|
||||
|
||||
func Call(chatId string, path string, args string) string {
|
||||
vm, ok := vms[chatId]
|
||||
if !ok {
|
||||
return printError(fmt.Sprintf("Vm[%s] doesn't exist.", chatId))
|
||||
}
|
||||
|
||||
res, err := vm.Call("call", nil, path, args)
|
||||
|
||||
return printResult(res.String(), err);
|
||||
}
|
||||
|
||||
// Send will serialize the first argument, send it to the node and returns the response.
|
||||
func Send(call otto.FunctionCall) (response otto.Value) {
|
||||
// Ensure that we've got a batch request (array) or a single request (object)
|
||||
arg := call.Argument(0).Object()
|
||||
if arg == nil || (arg.Class() != "Array" && arg.Class() != "Object") {
|
||||
throwJSException("request must be an object or array")
|
||||
}
|
||||
// Convert the otto VM arguments to Go values
|
||||
data, err := call.Otto.Call("JSON.stringify", nil, arg)
|
||||
if err != nil {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
reqjson, err := data.ToString()
|
||||
|
||||
if err != nil {
|
||||
throwJSException(err.Error())
|
||||
}
|
||||
|
||||
var (
|
||||
reqs []rpc.JSONRequest
|
||||
batch = true
|
||||
)
|
||||
if err = json.Unmarshal([]byte(reqjson), &reqs); err != nil {
|
||||
// single request?
|
||||
reqs = make([]rpc.JSONRequest, 1)
|
||||
if err = json.Unmarshal([]byte(reqjson), &reqs[0]); err != nil {
|
||||
throwJSException("invalid request")
|
||||
}
|
||||
batch = false
|
||||
}
|
||||
// Iteratively execute the requests
|
||||
call.Otto.Set("response_len", len(reqs))
|
||||
call.Otto.Run("var ret_response = new Array(response_len);")
|
||||
|
||||
for i, req := range reqs {
|
||||
// Execute the RPC request and parse the reply
|
||||
if err = client.Send(&req); err != nil {
|
||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
result := make(map[string]interface{})
|
||||
if err = client.Recv(&result); err != nil {
|
||||
return newErrorResponse(call, -32603, err.Error(), req.Id)
|
||||
}
|
||||
// Feed the reply back into the JavaScript runtime environment
|
||||
id, _ := result["id"]
|
||||
jsonver, _ := result["jsonrpc"]
|
||||
|
||||
call.Otto.Set("ret_id", id)
|
||||
call.Otto.Set("ret_jsonrpc", jsonver)
|
||||
call.Otto.Set("response_idx", i)
|
||||
|
||||
if res, ok := result["result"]; ok {
|
||||
payload, _ := json.Marshal(res)
|
||||
call.Otto.Set("ret_result", string(payload))
|
||||
response, err = call.Otto.Run(`
|
||||
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, result: JSON.parse(ret_result) };
|
||||
`)
|
||||
continue
|
||||
}
|
||||
if res, ok := result["error"]; ok {
|
||||
payload, _ := json.Marshal(res)
|
||||
call.Otto.Set("ret_result", string(payload))
|
||||
response, err = call.Otto.Run(`
|
||||
ret_response[response_idx] = { jsonrpc: ret_jsonrpc, id: ret_id, error: JSON.parse(ret_result) };
|
||||
`)
|
||||
continue
|
||||
}
|
||||
return newErrorResponse(call, -32603, fmt.Sprintf("Invalid response"), new(int64))
|
||||
}
|
||||
// Convert single requests back from batch ones
|
||||
if !batch {
|
||||
call.Otto.Run("ret_response = ret_response[0];")
|
||||
}
|
||||
// Execute any registered callbacks
|
||||
if call.Argument(1).IsObject() {
|
||||
call.Otto.Set("callback", call.Argument(1))
|
||||
call.Otto.Run(`
|
||||
if (Object.prototype.toString.call(callback) == '[object Function]') {
|
||||
callback(null, ret_response);
|
||||
}
|
||||
`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// newErrorResponse creates a JSON RPC error response for a specific request id,
|
||||
// containing the specified error code and error message. Beside returning the
|
||||
// error to the caller, it also sets the ret_error and ret_response JavaScript
|
||||
// variables.
|
||||
func newErrorResponse(call otto.FunctionCall, code int, msg string, id interface{}) (response otto.Value) {
|
||||
// Bundle the error into a JSON RPC call response
|
||||
res := rpc.JSONErrResponse{
|
||||
Version: "2.0",
|
||||
Id: id,
|
||||
Error: rpc.JSONError{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
},
|
||||
}
|
||||
// Serialize the error response into JavaScript variables
|
||||
errObj, err := json.Marshal(res.Error)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error: %v", err)
|
||||
}
|
||||
resObj, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JSON RPC error response: %v", err)
|
||||
}
|
||||
|
||||
if _, err = call.Otto.Run("ret_error = " + string(errObj)); err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to set `ret_error` to the occurred error: %v", err)
|
||||
}
|
||||
resVal, err := call.Otto.Run("ret_response = " + string(resObj))
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to set `ret_response` to the JSON RPC response: %v", err)
|
||||
}
|
||||
return resVal
|
||||
}
|
||||
|
||||
|
||||
// throwJSException panics on an otto.Value. The Otto VM will recover from the
|
||||
// Go panic and throw msg as a JavaScript error.
|
||||
func throwJSException(msg interface{}) otto.Value {
|
||||
val, err := otto.ToValue(msg)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err)
|
||||
}
|
||||
panic(val)
|
||||
}
|
170
src/library.c
170
src/library.c
|
@ -1,170 +0,0 @@
|
|||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef IOS_DEPLOYMENT
|
||||
#else
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
bool GethServiceSignalEvent( const char *jsonEvent );
|
||||
|
||||
#ifdef IOS_DEPLOYMENT
|
||||
|
||||
bool GethServiceSignalEvent( const char *jsonEvent )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static JavaVM *gJavaVM = NULL;
|
||||
static jclass JavaClassPtr_GethService = NULL;
|
||||
static jmethodID JavaMethodPtr_signalEvent = NULL;
|
||||
|
||||
static bool JniLibraryInit( JNIEnv *env );
|
||||
|
||||
/*!
|
||||
* @brief Get interface to JNI.
|
||||
*
|
||||
* @return true if thread should be detached from JNI.
|
||||
*/
|
||||
static bool JniAttach( JNIEnv **env )
|
||||
{
|
||||
jint status;
|
||||
|
||||
if (gJavaVM == NULL)
|
||||
{
|
||||
env = NULL;
|
||||
}
|
||||
|
||||
status = (*gJavaVM)->GetEnv( gJavaVM, (void **)env, JNI_VERSION_1_6);
|
||||
if (status == JNI_EDETACHED)
|
||||
{
|
||||
// attach thread to JNI
|
||||
//(*gJavaVM)->AttachCurrentThread( gJavaVM, (void **)env, NULL ); // Oracle JNI API
|
||||
(*gJavaVM)->AttachCurrentThread( gJavaVM, env, NULL ); // Android JNI API
|
||||
return true;
|
||||
}
|
||||
else if (status != JNI_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief The VM calls JNI_OnLoad when the native library is loaded.
|
||||
*/
|
||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
{
|
||||
bool detach;
|
||||
JNIEnv *env;
|
||||
int result = JNI_VERSION_1_6;
|
||||
|
||||
gJavaVM = vm;
|
||||
|
||||
// attach thread to JNI
|
||||
detach = JniAttach( &env );
|
||||
if (env == NULL)
|
||||
{
|
||||
// failed
|
||||
gJavaVM = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!JniLibraryInit( env ))
|
||||
{
|
||||
// fail loading of JNI library
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if (detach)
|
||||
{
|
||||
// detach thread from JNI
|
||||
(*gJavaVM)->DetachCurrentThread( gJavaVM );
|
||||
}
|
||||
|
||||
if (result != JNI_VERSION_1_6)
|
||||
{
|
||||
gJavaVM = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Initialize library.
|
||||
*/
|
||||
bool JniLibraryInit( JNIEnv *env )
|
||||
{
|
||||
int i;
|
||||
|
||||
JavaClassPtr_GethService = (*env)->FindClass( env, "com/statusim/geth/service/GethService" );
|
||||
if (JavaClassPtr_GethService == NULL) return false;
|
||||
JavaClassPtr_GethService = (jclass)(*env)->NewGlobalRef( env, JavaClassPtr_GethService );
|
||||
if (JavaClassPtr_GethService == NULL) return false;
|
||||
|
||||
struct { bool bStatic; jclass classPtr; jmethodID *methodPtr; const char *methodId; const char *params; } javaMethodDescriptors[] =
|
||||
{
|
||||
{ true, JavaClassPtr_GethService, &JavaMethodPtr_signalEvent, "signalEvent", "(Ljava/lang/String;)V" },
|
||||
|
||||
// { false, JavaClassPtr_GethService, &JavaMethodPtr_someNonStaticMethod, "someNonStaticMethod", "(Ljava/lang/String;)V" },
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(javaMethodDescriptors) / sizeof(javaMethodDescriptors[0]); i++)
|
||||
{
|
||||
if (javaMethodDescriptors[i].bStatic)
|
||||
{
|
||||
*(javaMethodDescriptors[i].methodPtr) = (*env)->GetStaticMethodID( env, javaMethodDescriptors[i].classPtr, javaMethodDescriptors[i].methodId, javaMethodDescriptors[i].params );
|
||||
}
|
||||
else
|
||||
{
|
||||
*(javaMethodDescriptors[i].methodPtr) = (*env)->GetMethodID( env, javaMethodDescriptors[i].classPtr, javaMethodDescriptors[i].methodId, javaMethodDescriptors[i].params );
|
||||
}
|
||||
|
||||
if (*(javaMethodDescriptors[i].methodPtr) == NULL) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Calls static method signalEvent of class com.statusim.GethService.
|
||||
*
|
||||
* @param jsonEvent - UTF8 string
|
||||
*/
|
||||
bool GethServiceSignalEvent( const char *jsonEvent )
|
||||
{
|
||||
bool detach;
|
||||
JNIEnv *env;
|
||||
|
||||
// attach thread to JNI
|
||||
detach = JniAttach( &env );
|
||||
if (env == NULL)
|
||||
{
|
||||
// failed
|
||||
return false;
|
||||
}
|
||||
|
||||
jstring javaJsonEvent = NULL;
|
||||
if (jsonEvent != NULL)
|
||||
{
|
||||
javaJsonEvent = (*env)->NewStringUTF( env, jsonEvent );
|
||||
}
|
||||
|
||||
(*env)->CallStaticVoidMethod( env, JavaClassPtr_GethService, JavaMethodPtr_signalEvent, javaJsonEvent );
|
||||
|
||||
if (javaJsonEvent != NULL) (*env)->DeleteLocalRef( env, javaJsonEvent );
|
||||
|
||||
if (detach)
|
||||
{
|
||||
// detach thread from JNI
|
||||
(*gJavaVM)->DetachCurrentThread( gJavaVM );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
162
src/node.go
162
src/node.go
|
@ -1,162 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/release"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
clientIdentifier = "Geth" // Client identifier to advertise over the network
|
||||
versionMajor = 1 // Major version component of the current release
|
||||
versionMinor = 5 // Minor version component of the current release
|
||||
versionPatch = 0 // Patch version component of the current release
|
||||
versionMeta = "unstable" // Version metadata to append to the version string
|
||||
|
||||
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle
|
||||
)
|
||||
|
||||
var (
|
||||
vString string // Combined textual representation of the version
|
||||
rConfig release.Config // Structured version information and release oracle config
|
||||
currentNode *node.Node // currently running geth node
|
||||
c *cli.Context // the CLI context used to start the geth node
|
||||
lightEthereum *les.LightEthereum // LES service
|
||||
accountManager *accounts.Manager // the account manager attached to the currentNode
|
||||
selectedAddress string // address of the account that was processed during the last call to SelectAccount()
|
||||
whisperService *whisper.Whisper // whisper service
|
||||
datadir string // data directory for geth
|
||||
rpcport int = 8545 // RPC port (replaced in unit tests)
|
||||
client *rpc.Client
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDataDirPreprocessingFailed = errors.New("Failed to pre-process data directory")
|
||||
)
|
||||
|
||||
// MakeNode create a geth node entity
|
||||
func MakeNode(inputDir string) *node.Node {
|
||||
|
||||
datadir := inputDir
|
||||
|
||||
// TODO remove admin rpcapi flag
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
set.Bool("lightkdf", true, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength")
|
||||
set.Bool("shh", true, "whisper")
|
||||
set.Bool("light", true, "disable eth")
|
||||
set.Bool("testnet", true, "light test network")
|
||||
set.Bool("rpc", true, "enable rpc")
|
||||
set.String("rpcaddr", "localhost", "host for RPC")
|
||||
set.Int("rpcport", rpcport, "rpc port")
|
||||
set.String("rpccorsdomain", "*", "allow all domains")
|
||||
set.String("verbosity", "3", "verbosity level")
|
||||
set.String("rpcapi", "db,eth,net,web3,shh,personal,admin", "rpc api(s)")
|
||||
set.String("datadir", datadir, "data directory for geth")
|
||||
set.String("logdir", datadir, "log dir for glog")
|
||||
c = cli.NewContext(nil, set, nil)
|
||||
|
||||
// Construct the textual version string from the individual components
|
||||
vString = fmt.Sprintf("%d.%d.%d", versionMajor, versionMinor, versionPatch)
|
||||
|
||||
// Construct the version release oracle configuration
|
||||
rConfig.Oracle = common.HexToAddress(versionOracle)
|
||||
|
||||
rConfig.Major = uint32(versionMajor)
|
||||
rConfig.Minor = uint32(versionMinor)
|
||||
rConfig.Patch = uint32(versionPatch)
|
||||
|
||||
utils.DebugSetup(c)
|
||||
currentNode = makeNode(c, clientIdentifier, vString)
|
||||
|
||||
return currentNode
|
||||
|
||||
}
|
||||
|
||||
func makeNode(ctx *cli.Context, name, version string) *node.Node {
|
||||
nodeIn := utils.MakeNode(ctx, name, version)
|
||||
utils.RegisterEthService(ctx, nodeIn, rConfig, makeDefaultExtra())
|
||||
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
||||
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
||||
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
|
||||
if shhEnabled || shhAutoEnabled {
|
||||
utils.RegisterShhService(nodeIn)
|
||||
}
|
||||
return nodeIn
|
||||
}
|
||||
|
||||
// StartNode starts a geth node entity
|
||||
func RunNode(nodeIn *node.Node) {
|
||||
utils.StartNode(nodeIn)
|
||||
|
||||
if err := nodeIn.Service(&accountManager); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get account manager:", err)
|
||||
}
|
||||
if err := nodeIn.Service(&whisperService); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get whisper service:", err)
|
||||
}
|
||||
if err := nodeIn.Service(&lightEthereum); err != nil {
|
||||
glog.V(logger.Warn).Infoln("cannot get light ethereum service:", err)
|
||||
}
|
||||
lightEthereum.StatusBackend.SetTransactionQueueHandler(onSendTransactionRequest)
|
||||
|
||||
client, _ = nodeIn.Attach()
|
||||
nodeIn.Wait()
|
||||
}
|
||||
|
||||
func makeDefaultExtra() []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(versionMajor<<16 | versionMinor<<8 | versionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
return extra
|
||||
}
|
||||
|
||||
func preprocessDataDir(dataDir string) (string, error) {
|
||||
testDataDir := path.Join(dataDir, "testnet", "keystore")
|
||||
if _, err := os.Stat(testDataDir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(testDataDir, 0755); err != nil {
|
||||
return dataDir, ErrDataDirPreprocessingFailed
|
||||
}
|
||||
}
|
||||
|
||||
// copy over static peer nodes list (LES auto-discovery is not stable yet)
|
||||
dst := filepath.Join(dataDir, "testnet", "static-nodes.json")
|
||||
if _, err := os.Stat(dst); os.IsNotExist(err) {
|
||||
src := filepath.Join("data", "static-nodes.json")
|
||||
if err := copyFile(dst, src); err != nil {
|
||||
return dataDir, err
|
||||
}
|
||||
}
|
||||
|
||||
return dataDir, nil
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
extern bool GethServiceSignalEvent( const char *jsonEvent );
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/whisper"
|
||||
)
|
||||
|
||||
var(
|
||||
whisperFilters []int
|
||||
)
|
||||
|
||||
|
||||
func onWhisperMessage(message *whisper.Message) {
|
||||
event := GethEvent{
|
||||
Type: "whisper",
|
||||
Event: WhisperMessageEvent{
|
||||
Payload: string(message.Payload),
|
||||
From: common.ToHex(crypto.FromECDSAPub(message.Recover())),
|
||||
To: common.ToHex(crypto.FromECDSAPub(message.To)),
|
||||
Sent: message.Sent.Unix(),
|
||||
TTL: int64(message.TTL / time.Second),
|
||||
Hash: common.ToHex(message.Hash.Bytes()),
|
||||
},
|
||||
}
|
||||
body, _ := json.Marshal(&event)
|
||||
C.GethServiceSignalEvent(C.CString(string(body)))
|
||||
}
|
||||
|
||||
func doAddWhisperFilter(args whisper.NewFilterArgs) int {
|
||||
var id int
|
||||
filter := whisper.Filter{
|
||||
To: crypto.ToECDSAPub(common.FromHex(args.To)),
|
||||
From: crypto.ToECDSAPub(common.FromHex(args.From)),
|
||||
Topics: whisper.NewFilterTopics(args.Topics...),
|
||||
Fn: onWhisperMessage,
|
||||
}
|
||||
|
||||
id = whisperService.Watch(filter)
|
||||
whisperFilters = append(whisperFilters, id)
|
||||
return id
|
||||
}
|
||||
|
||||
func doRemoveWhisperFilter(idFilter int) {
|
||||
whisperService.Unwatch(idFilter)
|
||||
}
|
||||
|
||||
func doClearWhisperFilters() {
|
||||
for _, idFilter := range whisperFilters {
|
||||
doRemoveWhisperFilter(idFilter)
|
||||
}
|
||||
whisperFilters = nil
|
||||
}
|
|
@ -34,7 +34,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/src/extkeys"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -33,7 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/src/extkeys"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -39,7 +39,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/status-im/status-go/src/extkeys"
|
||||
"github.com/status-im/status-go/extkeys"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM karalabe/xgo-latest
|
||||
|
||||
# Inject the container entry point, the build script (patched for Status bindings conditional builds of library.c)
|
||||
# Inject the container entry point, the build script (patched for Status bindings conditional builds of C code)
|
||||
ADD build.sh /build.sh
|
||||
ENV BUILD /build.sh
|
||||
RUN chmod +x $BUILD
|
||||
|
|
|
@ -159,6 +159,7 @@ for TARGET in $TARGETS; do
|
|||
if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "android" ]; then
|
||||
PLATFORM=16 # Jelly Bean 4.0.0
|
||||
fi
|
||||
CGO_STATUS_IM="-D ANDROID_DEPLOYMENT"
|
||||
if [ "$PLATFORM" -ge 16 ]; then
|
||||
CGO_CCPIE="-fPIE"
|
||||
CGO_LDPIE="-fPIE"
|
||||
|
@ -190,11 +191,11 @@ for TARGET in $TARGETS; do
|
|||
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "arm" ]; then
|
||||
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $EXT_LDAMD $LD" $BM -o "/build/$NAME-android-$PLATFORM-arm`extension android`" ./$PACK
|
||||
CGO_CFLAGS="-D ANDROID_DEPLOYMENT" CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE $CGO_STATUS_IM" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $EXT_LDAMD $LD" $BM -o "/build/$NAME-android-$PLATFORM-arm`extension android`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then
|
||||
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 go build $V $X "${T[@]}" --ldflags="$V $EXT_LDAMD $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-arm.so" ./$PACK
|
||||
CC=arm-linux-androideabi-gcc CXX=arm-linux-androideabi-g++ GOOS=android GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDAMD $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-arm.so" ./$PACK
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -214,11 +215,11 @@ for TARGET in $TARGETS; do
|
|||
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "386" ]; then
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $LD" $BM -o "/build/$NAME-android-$PLATFORM-386`extension android`" ./$PACK
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE $CGO_STATUS_IM" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $LD" $BM -o "/build/$NAME-android-$PLATFORM-386`extension android`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 go build $V $X "${T[@]}" --ldflags="$V $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-386.so" ./$PACK
|
||||
CC=i686-linux-android-gcc CXX=i686-linux-android-g++ GOOS=android GOARCH=386 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${T[@]}" --ldflags="$V $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-386.so" ./$PACK
|
||||
fi
|
||||
fi
|
||||
if [ "$PLATFORM" -ge 21 ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ] || [ $XGOARCH == "aar" ]); then
|
||||
|
@ -234,14 +235,15 @@ for TARGET in $TARGETS; do
|
|||
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]; then
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $LD" $BM -o "/build/$NAME-android-$PLATFORM-arm64`extension android`" ./$PACK
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_CCPIE $CGO_STATUS_IM" CGO_CXXFLAGS="$CGO_CCPIE" CGO_LDFLAGS="$CGO_LDPIE" go build $V $X "${T[@]}" --ldflags="$V $EXT_LDPIE $LD" $BM -o "/build/$NAME-android-$PLATFORM-arm64`extension android`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 go get $V $X "${T[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 go build $V $X "${T[@]}" --ldflags="$V $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-arm64.so" ./$PACK
|
||||
CC=aarch64-linux-android-gcc CXX=aarch64-linux-android-g++ GOOS=android GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${T[@]}" --ldflags="$V $LD" --buildmode=c-shared -o "/build-android-aar/$NAME-android-$PLATFORM-arm64.so" ./$PACK
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
unset CGO_STATUS_IM # good to let that extra var go away
|
||||
# Assemble the Android Archive from the built shared libraries
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then
|
||||
title=${NAME^}
|
||||
|
@ -491,7 +493,7 @@ for TARGET in $TARGETS; do
|
|||
if [ "$GO_VERSION" -lt 160 ]; then
|
||||
LDSTRIP="-s"
|
||||
fi
|
||||
STATUS_IM_CFLAGS="-D IOS_DEPLOYMENT"
|
||||
CGO_STATUS_IM="-D IOS_DEPLOYMENT"
|
||||
# Cross compile to all available iOS and simulator platforms
|
||||
if [ -d "$IOS_NDK_ARM_7" ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm-7" ] || [ $XGOARCH == "framework" ]); then
|
||||
echo "Bootstrapping ios-$PLATFORM/arm-7..."
|
||||
|
@ -505,7 +507,7 @@ for TARGET in $TARGETS; do
|
|||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm GOARM=7 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-ios-$PLATFORM-armv7`extension darwin`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then
|
||||
CGO_CFLAGS="-D IOS_DEPLOYMENT" CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm GOARM=7 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-armv7.a" ./$PACK
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm GOARM=7 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-armv7.a" ./$PACK
|
||||
fi
|
||||
echo "Cleaning up Go runtime for ios-$PLATFORM/arm-7..."
|
||||
rm -rf /usr/local/go/pkg/darwin_arm
|
||||
|
@ -519,10 +521,10 @@ for TARGET in $TARGETS; do
|
|||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ HOST=arm-apple-darwin11 PREFIX=/usr/local $BUILD_DEPS /deps ${DEPS_ARGS[@]}
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go get $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]; then
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-ios-$PLATFORM-arm64`extension darwin`" ./$PACK
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${IOSTAGS[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-ios-$PLATFORM-arm64`extension darwin`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then
|
||||
CGO_CFLAGS="-D IOS_DEPLOYMENT" CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-arm64.a" ./$PACK
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-arm64.a" ./$PACK
|
||||
fi
|
||||
echo "Cleaning up Go runtime for ios-$PLATFORM/arm64..."
|
||||
rm -rf /usr/local/go/pkg/darwin_arm64
|
||||
|
@ -537,15 +539,16 @@ for TARGET in $TARGETS; do
|
|||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ HOST=arm-apple-darwin11 PREFIX=/usr/local $BUILD_DEPS /deps ${DEPS_ARGS[@]}
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go get $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" -d ./$PACK
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "amd64" ]; then
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-ios-$PLATFORM-x86_64`extension darwin`" ./$PACK
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${IOSTAGS[@]}" --ldflags="$LDSTRIP $V $LD" $BM -o "/build/$NAME-ios-$PLATFORM-x86_64`extension darwin`" ./$PACK
|
||||
fi
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then
|
||||
CGO_CFLAGS="-D IOS_DEPLOYMENT" CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-x86_64.a" ./$PACK
|
||||
CC=arm-apple-darwin11-clang CXX=arm-apple-darwin11-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="$CGO_STATUS_IM" go build $V $X "${IOSTAGS[@]}" --ldflags="$V $LD" --buildmode=c-archive -o "/build-ios-fw/$NAME-ios-$PLATFORM-x86_64.a" ./$PACK
|
||||
fi
|
||||
echo "Cleaning up Go runtime for ios-$PLATFORM/amd64..."
|
||||
rm -rf /usr/local/go/pkg/darwin_amd64
|
||||
mv /usr/local/go/pkg/darwin_amd64_bak /usr/local/go/pkg/darwin_amd64
|
||||
fi
|
||||
unset CGO_STATUS_IM
|
||||
# Assemble the iOS framework from the built binaries
|
||||
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then
|
||||
title=${NAME^}
|
||||
|
|
Loading…
Reference in New Issue