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