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:
Victor Farazdagi 2016-09-11 14:44:14 +03:00
parent 5fb4aef1cc
commit edd8763c3c
37 changed files with 2522 additions and 1979 deletions

9
.gitignore vendored
View File

@ -13,8 +13,6 @@
.ethtest .ethtest
*/**/*tx_database* */**/*tx_database*
*/**/*dapps* */**/*dapps*
Godeps/_workspace/pkg
Godeps/_workspace/bin
#* #*
.#* .#*
@ -35,8 +33,9 @@ profile.cov
.vagrant .vagrant
# tests # tests
src/.ethereumtest/ .ethereumtest/
# #
# golang # golang
cover.out coverage.out
cover.html coverage-all.out
coverage.html

View File

@ -5,7 +5,7 @@ GOBIN = build/bin
GO ?= latest GO ?= latest
statusgo: 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 "status go compilation done."
@echo "Run \"build/bin/statusgo\" to view available commands" @echo "Run \"build/bin/statusgo\" to view available commands"
@ -14,25 +14,48 @@ statusgo-cross: statusgo-android statusgo-ios
@ls -ld $(GOBIN)/statusgo-* @ls -ld $(GOBIN)/statusgo-*
statusgo-android: xgo 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:" @echo "Android cross compilation done:"
statusgo-ios: xgo 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:" @echo "iOS framework cross compilation done:"
statusgo-ios-simulator: xgo 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:" @echo "iOS framework cross compilation done:"
xgo: xgo:
build/env.sh go get github.com/karalabe/xgo build/env.sh go get github.com/karalabe/xgo
test: test-all:
build/env.sh go test -v -coverprofile=cover.out ./src @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 test: test-all
build/env.sh go tool cover -html=cover.out -o cover.html
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: clean:
rm -fr build/bin/* rm -fr build/bin/*
rm coverage.out coverage-all.out coverage.html

View File

@ -16,7 +16,7 @@ WS2="$ROOT/build/_workspace/project"
if [ ! -d "$WS1/src" ]; then if [ ! -d "$WS1/src" ]; then
mkdir -p "$WS1" mkdir -p "$WS1"
cd "$WS1" cd "$WS1"
ln -s "$ROOT/src/vendor" src ln -s "$ROOT/vendor" src
cd "$ROOT" cd "$ROOT"
fi fi

View File

@ -1,29 +1,39 @@
package main package main
/*
#include <stddef.h>
#include <stdbool.h>
extern bool StatusServiceSignalEvent(const char *jsonEvent);
*/
import "C" import "C"
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/whisper"
"os" "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 //export CreateAccount
func CreateAccount(password *C.char) *C.char { func CreateAccount(password *C.char) *C.char {
// This is equivalent to creating an account from the command line, // This is equivalent to creating an account from the command line,
// just modified to handle the function arg passing // 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := AccountInfo{ out := geth.AccountInfo{
Address: address, Address: address,
PubKey: pubKey, PubKey: pubKey,
Mnemonic: mnemonic, Mnemonic: mnemonic,
@ -37,15 +47,15 @@ func CreateAccount(password *C.char) *C.char {
//export CreateChildAccount //export CreateChildAccount
func CreateChildAccount(parentAddress, password *C.char) *C.char { 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := AccountInfo{ out := geth.AccountInfo{
Address: address, Address: address,
PubKey: pubKey, PubKey: pubKey,
Error: errString, Error: errString,
@ -58,15 +68,15 @@ func CreateChildAccount(parentAddress, password *C.char) *C.char {
//export RecoverAccount //export RecoverAccount
func RecoverAccount(password, mnemonic *C.char) *C.char { 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := AccountInfo{ out := geth.AccountInfo{
Address: address, Address: address,
PubKey: pubKey, PubKey: pubKey,
Mnemonic: C.GoString(mnemonic), Mnemonic: C.GoString(mnemonic),
@ -81,15 +91,15 @@ func RecoverAccount(password, mnemonic *C.char) *C.char {
func Login(address, password *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 // 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 // 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := JSONError{ out := geth.JSONError{
Error: errString, Error: errString,
} }
outBytes, _ := json.Marshal(&out) outBytes, _ := json.Marshal(&out)
@ -101,15 +111,15 @@ func Login(address, password *C.char) *C.char {
func Logout() *C.char { func Logout() *C.char {
// This is equivalent to clearing whisper identities // This is equivalent to clearing whisper identities
err := logout() err := geth.Logout()
errString := emptyError errString := ""
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := JSONError{ out := geth.JSONError{
Error: errString, Error: errString,
} }
outBytes, _ := json.Marshal(&out) 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, // This is equivalent to unlocking an account from the command line,
// just modified to unlock the account for the currently running geth node // just modified to unlock the account for the currently running geth node
// based on the provided arguments // 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := JSONError{ out := geth.JSONError{
Error: errString, Error: errString,
} }
outBytes, _ := json.Marshal(&out) outBytes, _ := json.Marshal(&out)
@ -141,15 +151,15 @@ func UnlockAccount(address, password *C.char, seconds int) *C.char {
//export CompleteTransaction //export CompleteTransaction
func CompleteTransaction(id, password *C.char) *C.char { 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := CompleteTransactionResult{ out := geth.CompleteTransactionResult{
Hash: txHash.Hex(), Hash: txHash.Hex(),
Error: errString, Error: errString,
} }
@ -160,17 +170,16 @@ func CompleteTransaction(id, password *C.char) *C.char {
//export StartNode //export StartNode
func StartNode(datadir *C.char) *C.char { func StartNode(datadir *C.char) *C.char {
// This starts a geth node with the given datadir // 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 { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := JSONError{ out := geth.JSONError{
Error: errString, Error: errString,
} }
outBytes, _ := json.Marshal(&out) outBytes, _ := json.Marshal(&out)
@ -178,33 +187,33 @@ func StartNode(datadir *C.char) *C.char {
return C.CString(string(outBytes)) return C.CString(string(outBytes))
} }
//export parse //export InitJail
func parse(chatId *C.char, js *C.char) *C.char { func InitJail(js *C.char) {
res := Parse(C.GoString(chatId), C.GoString(js)) 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) return C.CString(res)
} }
//export call //export Call
func call(chatId *C.char, path *C.char, params *C.char) *C.char { func Call(chatId *C.char, path *C.char, params *C.char) *C.char {
res := Call(C.GoString(chatId), C.GoString(path), C.GoString(params)) res := jail.GetInstance().Call(C.GoString(chatId), C.GoString(path), C.GoString(params))
return C.CString(res) return C.CString(res)
} }
//export initJail //export AddPeer
func initJail(js *C.char) { func AddPeer(url *C.char) *C.char {
Init(C.GoString(js)) success, err := geth.GetNodeManager().AddPeer(C.GoString(url))
} errString := ""
//export addPeer
func addPeer(url *C.char) *C.char {
success, err := doAddPeer(C.GoString(url))
errString := emptyError
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := AddPeerResult{ out := geth.AddPeerResult{
Success: success, Success: success,
Error: errString, Error: errString,
} }
@ -213,24 +222,24 @@ func addPeer(url *C.char) *C.char {
return C.CString(string(outBytes)) return C.CString(string(outBytes))
} }
//export addWhisperFilter //export AddWhisperFilter
func addWhisperFilter(filterJson *C.char) *C.char { func AddWhisperFilter(filterJson *C.char) *C.char {
var id int var id int
var filter whisper.NewFilterArgs var filter whisper.NewFilterArgs
err := json.Unmarshal([]byte(C.GoString(filterJson)), &filter) err := json.Unmarshal([]byte(C.GoString(filterJson)), &filter)
if err == nil { if err == nil {
id = doAddWhisperFilter(filter) id = geth.AddWhisperFilter(filter)
} }
errString := emptyError errString := ""
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
errString = err.Error() errString = err.Error()
} }
out := AddWhisperFilterResult{ out := geth.AddWhisperFilterResult{
Id: id, Id: id,
Error: errString, Error: errString,
} }
@ -240,14 +249,12 @@ func addWhisperFilter(filterJson *C.char) *C.char {
} }
//export removeWhisperFilter //export RemoveWhisperFilter
func removeWhisperFilter(idFilter int) { func RemoveWhisperFilter(idFilter int) {
geth.RemoveWhisperFilter(idFilter)
doRemoveWhisperFilter(idFilter)
} }
//export clearWhisperFilters //export ClearWhisperFilters
func clearWhisperFilters() { func ClearWhisperFilters() {
geth.ClearWhisperFilters()
doClearWhisperFilters()
} }

View File

@ -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
}

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"testing" "testing"
"github.com/status-im/status-go/extkeys" "github.com/status-im/status-go/extkeys"
) )

View File

@ -1,30 +1,16 @@
package main package geth
/*
#include <stddef.h>
#include <stdbool.h>
extern bool GethServiceSignalEvent( const char *jsonEvent );
*/
import "C"
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/les/status" "github.com/status-im/status-go/extkeys"
"github.com/ethereum/go-ethereum/p2p/discover"
errextra "github.com/pkg/errors"
"github.com/status-im/status-go/src/extkeys"
) )
var ( 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") ErrAddressToAccountMappingFailure = errors.New("cannot retreive a valid account for a given address")
ErrAccountToKeyMappingFailure = errors.New("cannot retreive a valid key for a given account") ErrAccountToKeyMappingFailure = errors.New("cannot retreive a valid key for a given account")
ErrUnlockCalled = errors.New("no need to unlock accounts, login instead") ErrUnlockCalled = errors.New("no need to unlock accounts, login instead")
@ -32,32 +18,25 @@ var (
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities") ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
ErrWhisperNoIdentityFound = errors.New("failed to locate identity previously injected into Whisper") ErrWhisperNoIdentityFound = errors.New("failed to locate identity previously injected into Whisper")
ErrNoAccountSelected = errors.New("no account has been selected, please login") 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 // 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 // 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 // Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
// sub-account derivations) // sub-account derivations)
func createAccount(password string) (address, pubKey, mnemonic string, err error) { func CreateAccount(password string) (address, pubKey, mnemonic string, err error) {
if currentNode == nil {
return "", "", "", ErrInvalidGethNode
}
if accountManager == nil {
return "", "", "", ErrInvalidAccountManager
}
// generate mnemonic phrase // generate mnemonic phrase
m := extkeys.NewMnemonic() m := extkeys.NewMnemonic(extkeys.Salt)
mnemonic, err = m.MnemonicPhrase(128, extkeys.EnglishLanguage) mnemonic, err = m.MnemonicPhrase(128, extkeys.EnglishLanguage)
if err != nil { 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) // generate extended master key (see BIP32)
extKey, err := extkeys.NewMaster(m.MnemonicSeed(mnemonic, password), []byte(extkeys.Salt)) extKey, err := extkeys.NewMaster(m.MnemonicSeed(mnemonic, password), []byte(extkeys.Salt))
if err != nil { 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 // 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. // createChildAccount creates sub-account for an account identified by parent address.
// CKD#2 is used as root for master accounts (when parentAddress is ""). // CKD#2 is used as root for master accounts (when parentAddress is "").
// Otherwise (when parentAddress != ""), child is derived directly from parent. // Otherwise (when parentAddress != ""), child is derived directly from parent.
func createChildAccount(parentAddress, password string) (address, pubKey string, err error) { func CreateChildAccount(parentAddress, password string) (address, pubKey string, err error) {
if currentNode == nil { nodeManager := GetNodeManager()
return "", "", ErrInvalidGethNode accountManager, err := nodeManager.AccountManager()
} if err != nil {
return "", "", err
if accountManager == nil {
return "", "", ErrInvalidAccountManager
} }
if parentAddress == "" { // by default derive from currently selected account if parentAddress == "" { // by default derive from currently selected account
parentAddress = selectedAddress parentAddress = nodeManager.SelectedAddress
} }
if parentAddress == "" { if parentAddress == "" {
@ -123,20 +100,12 @@ func createChildAccount(parentAddress, password string) (address, pubKey string,
// recoverAccount re-creates master key using given details. // recoverAccount re-creates master key using given details.
// Once master key is re-generated, it is inserted into keystore (if not already there). // 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) { func RecoverAccount(password, mnemonic string) (address, pubKey string, err error) {
if currentNode == nil {
return "", "", ErrInvalidGethNode
}
if accountManager == nil {
return "", "", ErrInvalidAccountManager
}
// re-create extended key (see BIP32) // 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)) extKey, err := extkeys.NewMaster(m.MnemonicSeed(mnemonic, password), []byte(extkeys.Salt))
if err != nil { if err != nil {
return "", "", errextra.Wrap(err, "Can not create master extended key") return "", "", ErrInvalidMasterKeyCreated
} }
// import re-created key into account keystore // 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 // 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, // using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
// all previous identities are removed). // all previous identities are removed).
func selectAccount(address, password string) error { func SelectAccount(address, password string) error {
if currentNode == nil { nodeManager := GetNodeManager()
return ErrInvalidGethNode accountManager, err := nodeManager.AccountManager()
if err != nil {
return err
} }
if accountManager == nil {
return ErrInvalidAccountManager
}
account, err := utils.MakeAddress(accountManager, address) account, err := utils.MakeAddress(accountManager, address)
if err != nil { if err != nil {
return ErrAddressToAccountMappingFailure return ErrAddressToAccountMappingFailure
@ -169,34 +137,35 @@ func selectAccount(address, password string) error {
return fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err) return fmt.Errorf("%s: %v", ErrAccountToKeyMappingFailure.Error(), err)
} }
if whisperService == nil { whisperService, err := nodeManager.WhisperService()
return ErrInvalidWhisperService if err != nil {
return err
} }
if err := whisperService.InjectIdentity(accountKey.PrivateKey); err != nil { if err := whisperService.InjectIdentity(accountKey.PrivateKey); err != nil {
return ErrWhisperIdentityInjectionFailure return ErrWhisperIdentityInjectionFailure
} }
// persist address for easier recovery of currently selected key (from Whisper) // persist address for easier recovery of currently selected key (from Whisper)
selectedAddress = address nodeManager.SelectedAddress = address
return nil return nil
} }
// logout clears whisper identities // logout clears whisper identities
func logout() error { func Logout() error {
if currentNode == nil { nodeManager := GetNodeManager()
return ErrInvalidGethNode whisperService, err := nodeManager.WhisperService()
} if err != nil {
if whisperService == nil { return err
return ErrInvalidWhisperService
} }
err := whisperService.ClearIdentities() err = whisperService.ClearIdentities()
if err != nil { if err != nil {
return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err) return fmt.Errorf("%s: %v", ErrWhisperClearIdentitiesFailure, err)
} }
selectedAddress = "" nodeManager.SelectedAddress = ""
return nil return nil
} }
@ -204,85 +173,31 @@ func logout() error {
// unlockAccount unlocks an existing account for a certain duration and // unlockAccount unlocks an existing account for a certain duration and
// inject the account as a whisper identity if the account was created as // inject the account as a whisper identity if the account was created as
// a whisper enabled account // a whisper enabled account
func unlockAccount(address, password string, seconds int) error { func UnlockAccount(address, password string, seconds int) error {
if currentNode == nil {
return ErrInvalidGethNode
}
return ErrUnlockCalled return ErrUnlockCalled
} }
// importExtendedKey processes incoming extended key, extracts required info and creates corresponding account key. // 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. // 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) { 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) // imports extended key, create key file (if necessary)
account, err := accountManager.ImportExtendedKey(extKey, password) account, err := accountManager.ImportExtendedKey(extKey, password)
if err != nil { if err != nil {
return "", "", errextra.Wrap(err, "Account manager could not create the account") return "", "", err
} }
address = fmt.Sprintf("%x", account.Address) address = fmt.Sprintf("%x", account.Address)
// obtain public key to return // obtain public key to return
account, key, err := accountManager.AccountDecryptedKey(account, password) account, key, err := accountManager.AccountDecryptedKey(account, password)
if err != nil { if err != nil {
return address, "", errextra.Wrap(err, "Could not recover the key") return address, "", err
} }
pubKey = common.ToHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)) pubKey = common.ToHex(crypto.FromECDSAPub(&key.PrivateKey.PublicKey))
return 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")
}

308
geth/accounts_test.go Normal file
View File

@ -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")
}
}

281
geth/node.go Normal file
View File

@ -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
}

26
geth/node_test.go Normal file
View File

@ -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
}
}

181
geth/signals.c Normal file
View File

@ -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

43
geth/txqueue.go Normal file
View File

@ -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)
}

149
geth/txqueue_test.go Normal file
View File

@ -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
}
}

View File

@ -1,4 +1,4 @@
package common package geth
import ( import (
"github.com/ethereum/go-ethereum/les/status" "github.com/ethereum/go-ethereum/les/status"
@ -35,7 +35,7 @@ type WhisperMessageEvent struct {
} }
type SendTransactionEvent struct { type SendTransactionEvent struct {
Id string `json:"hash"` Id string `json:"id"`
Args status.SendTxArgs `json:"args"` Args status.SendTxArgs `json:"args"`
} }

161
geth/utils.go Normal file
View File

@ -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")
}
}()
}

69
geth/whisper.go Normal file
View File

@ -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
}

164
geth/whisper_test.go Normal file
View File

@ -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)
}
}
}

259
jail/jail.go Normal file
View File

@ -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
}

242
jail/jail_test.go Normal file
View File

@ -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)
}
}

320
jail/testdata/commands.js vendored Normal file
View File

@ -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
});

155
jail/testdata/status.js vendored Normal file
View File

@ -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
}
};

View File

@ -1,4 +1,4 @@
package main package jail
const Web3_JS = ` 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){ 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){

500
src/Godeps/Godeps.json generated
View File

@ -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"
}
]
}

5
src/Godeps/Readme generated
View File

@ -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.

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -34,7 +34,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/src/extkeys" "github.com/status-im/status-go/extkeys"
) )
var ( var (

View File

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"github.com/status-im/status-go/src/extkeys" "github.com/status-im/status-go/extkeys"
) )
const ( const (

View File

@ -39,7 +39,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/randentropy" "github.com/ethereum/go-ethereum/crypto/randentropy"
"github.com/pborman/uuid" "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/pbkdf2"
"golang.org/x/crypto/scrypt" "golang.org/x/crypto/scrypt"
) )

View File

@ -1,6 +1,6 @@
FROM karalabe/xgo-latest 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 ADD build.sh /build.sh
ENV BUILD /build.sh ENV BUILD /build.sh
RUN chmod +x $BUILD RUN chmod +x $BUILD

View File

@ -159,6 +159,7 @@ for TARGET in $TARGETS; do
if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "android" ]; then if [ "$PLATFORM" == "" ] || [ "$PLATFORM" == "." ] || [ "$PLATFORM" == "android" ]; then
PLATFORM=16 # Jelly Bean 4.0.0 PLATFORM=16 # Jelly Bean 4.0.0
fi fi
CGO_STATUS_IM="-D ANDROID_DEPLOYMENT"
if [ "$PLATFORM" -ge 16 ]; then if [ "$PLATFORM" -ge 16 ]; then
CGO_CCPIE="-fPIE" CGO_CCPIE="-fPIE"
CGO_LDPIE="-fPIE" CGO_LDPIE="-fPIE"
@ -190,11 +191,11 @@ for TARGET in $TARGETS; do
if [ $XGOARCH == "." ] || [ $XGOARCH == "arm" ]; then 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then 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 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 fi
fi fi
@ -214,11 +215,11 @@ for TARGET in $TARGETS; do
if [ $XGOARCH == "." ] || [ $XGOARCH == "386" ]; then 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then 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 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
fi fi
if [ "$PLATFORM" -ge 21 ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ] || [ $XGOARCH == "aar" ]); then if [ "$PLATFORM" -ge 21 ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ] || [ $XGOARCH == "aar" ]); then
@ -234,14 +235,15 @@ for TARGET in $TARGETS; do
if [ $XGOARCH == "." ] || [ $XGOARCH == "arm64" ]; then 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then 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 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 fi
fi fi
unset CGO_STATUS_IM # good to let that extra var go away
# Assemble the Android Archive from the built shared libraries # Assemble the Android Archive from the built shared libraries
if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then if [ $XGOARCH == "." ] || [ $XGOARCH == "aar" ]; then
title=${NAME^} title=${NAME^}
@ -491,7 +493,7 @@ for TARGET in $TARGETS; do
if [ "$GO_VERSION" -lt 160 ]; then if [ "$GO_VERSION" -lt 160 ]; then
LDSTRIP="-s" LDSTRIP="-s"
fi fi
STATUS_IM_CFLAGS="-D IOS_DEPLOYMENT" CGO_STATUS_IM="-D IOS_DEPLOYMENT"
# Cross compile to all available iOS and simulator platforms # Cross compile to all available iOS and simulator platforms
if [ -d "$IOS_NDK_ARM_7" ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm-7" ] || [ $XGOARCH == "framework" ]); then if [ -d "$IOS_NDK_ARM_7" ] && ([ $XGOARCH == "." ] || [ $XGOARCH == "arm-7" ] || [ $XGOARCH == "framework" ]); then
echo "Bootstrapping ios-$PLATFORM/arm-7..." 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then 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 fi
echo "Cleaning up Go runtime for ios-$PLATFORM/arm-7..." echo "Cleaning up Go runtime for ios-$PLATFORM/arm-7..."
rm -rf /usr/local/go/pkg/darwin_arm 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++ 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 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then 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 fi
echo "Cleaning up Go runtime for ios-$PLATFORM/arm64..." echo "Cleaning up Go runtime for ios-$PLATFORM/arm64..."
rm -rf /usr/local/go/pkg/darwin_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++ 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 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 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 fi
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then 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 fi
echo "Cleaning up Go runtime for ios-$PLATFORM/amd64..." echo "Cleaning up Go runtime for ios-$PLATFORM/amd64..."
rm -rf /usr/local/go/pkg/darwin_amd64 rm -rf /usr/local/go/pkg/darwin_amd64
mv /usr/local/go/pkg/darwin_amd64_bak /usr/local/go/pkg/darwin_amd64 mv /usr/local/go/pkg/darwin_amd64_bak /usr/local/go/pkg/darwin_amd64
fi fi
unset CGO_STATUS_IM
# Assemble the iOS framework from the built binaries # Assemble the iOS framework from the built binaries
if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then if [ $XGOARCH == "." ] || [ $XGOARCH == "framework" ]; then
title=${NAME^} title=${NAME^}