Merge pull request 'add versionCallback and use on reboot tor; change start preference for bundled tor; new go 1.17 build directives' (#31) from versionCallback into master
Reviewed-on: #31
This commit is contained in:
commit
882849b66a
2
acn.go
2
acn.go
|
@ -38,6 +38,8 @@ type ACN interface {
|
||||||
WaitTillBootstrapped() error
|
WaitTillBootstrapped() error
|
||||||
// Sets the callback function to be called when ACN status changes
|
// Sets the callback function to be called when ACN status changes
|
||||||
SetStatusCallback(callback func(int, string))
|
SetStatusCallback(callback func(int, string))
|
||||||
|
// Sets the callback function to be called when ACN reboots to emit the version
|
||||||
|
SetVersionCallback(callback func(string))
|
||||||
|
|
||||||
// Restarts the underlying connection
|
// Restarts the underlying connection
|
||||||
Restart()
|
Restart()
|
||||||
|
|
|
@ -12,6 +12,7 @@ const acnError = "error initializing anonymous communication network"
|
||||||
// and you need a safe substitute that can later be replaced with a working ACN without impacting calling clients.
|
// and you need a safe substitute that can later be replaced with a working ACN without impacting calling clients.
|
||||||
type ErrorACN struct {
|
type ErrorACN struct {
|
||||||
statusCallbackCache func(int, string)
|
statusCallbackCache func(int, string)
|
||||||
|
versionCallbackCache func(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Callback() func(int, string) {
|
func (e ErrorACN) Callback() func(int, string) {
|
||||||
|
@ -34,6 +35,10 @@ func (e *ErrorACN) SetStatusCallback(callback func(int, string)) {
|
||||||
e.statusCallbackCache = callback
|
e.statusCallbackCache = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ErrorACN) SetVersionCallback(callback func(string)) {
|
||||||
|
e.versionCallbackCache = callback
|
||||||
|
}
|
||||||
|
|
||||||
func (e ErrorACN) Restart() {
|
func (e ErrorACN) Restart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.13
|
||||||
require (
|
require (
|
||||||
filippo.io/edwards25519 v1.0.0-rc.1
|
filippo.io/edwards25519 v1.0.0-rc.1
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.4
|
git.openprivacy.ca/openprivacy/bine v0.0.4
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.2
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -4,6 +4,8 @@ git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGy
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
|
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
|
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|
|
@ -51,6 +51,10 @@ func (lp *localProvider) SetStatusCallback(callback func(int, string)) {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lp *localProvider) SetVersionCallback(callback func(string)) {
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
|
||||||
func (lp *localProvider) GetPID() (int, error) {
|
func (lp *localProvider) GetPID() (int, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@ func (p *ProxyACN) SetStatusCallback(callback func(int, string)) {
|
||||||
p.acn.SetStatusCallback(callback)
|
p.acn.SetStatusCallback(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ProxyACN) SetVersionCallback(callback func(string)) {
|
||||||
|
p.acn.SetVersionCallback(callback)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ProxyACN) Restart() {
|
func (p *ProxyACN) Restart() {
|
||||||
p.acn.Restart()
|
p.acn.Restart()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package tor
|
package tor
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package tor
|
package tor
|
||||||
|
|
|
@ -70,10 +70,12 @@ type torProvider struct {
|
||||||
breakChan chan bool
|
breakChan chan bool
|
||||||
childListeners map[string]*onionListenService
|
childListeners map[string]*onionListenService
|
||||||
statusCallback func(int, string)
|
statusCallback func(int, string)
|
||||||
|
versionCallback func(string)
|
||||||
lastRestartTime time.Time
|
lastRestartTime time.Time
|
||||||
authenticator tor.Authenticator
|
authenticator tor.Authenticator
|
||||||
isClosed bool
|
isClosed bool
|
||||||
dataDir string
|
dataDir string
|
||||||
|
version string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ols *onionListenService) AddressFull() string {
|
func (ols *onionListenService) AddressFull() string {
|
||||||
|
@ -209,17 +211,7 @@ func (tp *torProvider) GetBootstrapStatus() (int, string) {
|
||||||
func (tp *torProvider) GetVersion() string {
|
func (tp *torProvider) GetVersion() string {
|
||||||
tp.lock.Lock()
|
tp.lock.Lock()
|
||||||
defer tp.lock.Unlock()
|
defer tp.lock.Unlock()
|
||||||
|
return tp.version
|
||||||
if tp.t == nil {
|
|
||||||
return "No Tor"
|
|
||||||
}
|
|
||||||
|
|
||||||
pinfo, err := tp.t.Control.ProtocolInfo()
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
return pinfo.TorVersion
|
|
||||||
}
|
|
||||||
return "No Tor"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *torProvider) closed() bool {
|
func (tp *torProvider) closed() bool {
|
||||||
|
@ -324,6 +316,7 @@ func (tp *torProvider) restart() {
|
||||||
|
|
||||||
// preserve status callback after shutdown
|
// preserve status callback after shutdown
|
||||||
statusCallback := tp.statusCallback
|
statusCallback := tp.statusCallback
|
||||||
|
versionCallback := tp.versionCallback
|
||||||
|
|
||||||
tp.t = nil
|
tp.t = nil
|
||||||
log.Debugf("Restarting Tor Process")
|
log.Debugf("Restarting Tor Process")
|
||||||
|
@ -334,6 +327,10 @@ func (tp *torProvider) restart() {
|
||||||
tp.t = newTp.t
|
tp.t = newTp.t
|
||||||
tp.dialer = newTp.dialer
|
tp.dialer = newTp.dialer
|
||||||
tp.statusCallback = statusCallback
|
tp.statusCallback = statusCallback
|
||||||
|
tp.versionCallback = versionCallback
|
||||||
|
if tp.versionCallback != nil {
|
||||||
|
tp.versionCallback(tp.version)
|
||||||
|
}
|
||||||
tp.lastRestartTime = time.Now()
|
tp.lastRestartTime = time.Now()
|
||||||
tp.isClosed = false
|
tp.isClosed = false
|
||||||
go tp.monitorRestart()
|
go tp.monitorRestart()
|
||||||
|
@ -391,6 +388,12 @@ func (tp *torProvider) SetStatusCallback(callback func(int, string)) {
|
||||||
tp.statusCallback = callback
|
tp.statusCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tp *torProvider) SetVersionCallback(callback func(string)) {
|
||||||
|
tp.lock.Lock()
|
||||||
|
defer tp.lock.Unlock()
|
||||||
|
tp.versionCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
func (tp *torProvider) Callback() func(int, string) {
|
func (tp *torProvider) Callback() func(int, string) {
|
||||||
tp.lock.Lock()
|
tp.lock.Lock()
|
||||||
defer tp.lock.Unlock()
|
defer tp.lock.Unlock()
|
||||||
|
@ -399,10 +402,10 @@ func (tp *torProvider) Callback() func(int, string) {
|
||||||
|
|
||||||
func (tp *torProvider) callStatusCallback(prog int, status string) {
|
func (tp *torProvider) callStatusCallback(prog int, status string) {
|
||||||
tp.lock.Lock()
|
tp.lock.Lock()
|
||||||
|
defer tp.lock.Unlock()
|
||||||
if tp.statusCallback != nil {
|
if tp.statusCallback != nil {
|
||||||
tp.statusCallback(prog, status)
|
tp.statusCallback(prog, status)
|
||||||
}
|
}
|
||||||
tp.lock.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTorACNWithAuth creates/starts a Tor ACN and returns a usable ACN object
|
// NewTorACNWithAuth creates/starts a Tor ACN and returns a usable ACN object
|
||||||
|
@ -429,7 +432,7 @@ func newHideCmd(exePath string) process.Creator {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *torProvider) checkVersion() error {
|
func (tp *torProvider) checkVersion() (string, error) {
|
||||||
// attempt connect to system tor
|
// attempt connect to system tor
|
||||||
log.Debugf("dialing tor control port")
|
log.Debugf("dialing tor control port")
|
||||||
controlport, err := dialControlPort(tp.controlPort)
|
controlport, err := dialControlPort(tp.controlPort)
|
||||||
|
@ -442,13 +445,13 @@ func (tp *torProvider) checkVersion() error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if minTorVersionReqs(pinfo.TorVersion) {
|
if minTorVersionReqs(pinfo.TorVersion) {
|
||||||
log.Debugln("OK version " + pinfo.TorVersion)
|
log.Debugln("OK version " + pinfo.TorVersion)
|
||||||
return nil
|
return pinfo.TorVersion, nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("tor version not supported: %v", pinfo.TorVersion)
|
return pinfo.TorVersion, fmt.Errorf("tor version not supported: %v", pinfo.TorVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTor(appDirectory string, bundledTorPath string, dataDir string, controlPort int, authenticator tor.Authenticator) (*torProvider, error) {
|
func startTor(appDirectory string, bundledTorPath string, dataDir string, controlPort int, authenticator tor.Authenticator) (*torProvider, error) {
|
||||||
|
@ -456,10 +459,11 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
|
||||||
|
|
||||||
os.MkdirAll(torDir, 0700)
|
os.MkdirAll(torDir, 0700)
|
||||||
|
|
||||||
tp := &torProvider{authenticator: authenticator, controlPort: controlPort, appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool, 1), statusCallback: nil, lastRestartTime: time.Now().Add(-restartCooldown)}
|
tp := &torProvider{authenticator: authenticator, controlPort: controlPort, appDirectory: appDirectory, bundeledTorPath: bundledTorPath, childListeners: make(map[string]*onionListenService), breakChan: make(chan bool, 1), statusCallback: nil, versionCallback: nil, lastRestartTime: time.Now().Add(-restartCooldown)}
|
||||||
|
|
||||||
log.Debugf("checking if there is a running system tor")
|
log.Debugf("checking if there is a running system tor")
|
||||||
if err := tp.checkVersion(); err == nil {
|
if version, err := tp.checkVersion(); err == nil {
|
||||||
|
tp.version = version
|
||||||
controlport, err := dialControlPort(tp.controlPort)
|
controlport, err := dialControlPort(tp.controlPort)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Debugf("creating tor handler from system tor")
|
log.Debugf("creating tor handler from system tor")
|
||||||
|
@ -476,16 +480,9 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not, try running system tor
|
// if not, try bundled tor, then running system tor
|
||||||
log.Debugln("checking if we can run system installed tor or bundled tor")
|
log.Debugln("checking if we can run bundled tor or system installed tor")
|
||||||
if checkCmdlineTorVersion("tor") {
|
if version, pass := checkCmdlineTorVersion(bundledTorPath); pass {
|
||||||
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("Error connecting to self-run system tor: %v\n", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tp.t = t
|
|
||||||
} else if bundledTorPath != "" && checkCmdlineTorVersion(bundledTorPath) {
|
|
||||||
log.Debugln("bundled tor appears viable, attempting to use '" + bundledTorPath + "'")
|
log.Debugln("bundled tor appears viable, attempting to use '" + bundledTorPath + "'")
|
||||||
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
|
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), ExePath: bundledTorPath, DebugWriter: nil, ProcessCreator: newHideCmd(bundledTorPath)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -493,15 +490,25 @@ func startTor(appDirectory string, bundledTorPath string, dataDir string, contro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tp.t = t
|
tp.t = t
|
||||||
|
tp.version = version
|
||||||
|
} else if version, pass := checkCmdlineTorVersion("tor"); pass {
|
||||||
|
t, err := tor.Start(context.TODO(), &tor.StartConf{ControlPort: tp.controlPort, DisableCookieAuth: true, UseEmbeddedControlConn: false, DisableEagerAuth: true, EnableNetwork: true, DataDir: dataDir, TorrcFile: path.Join(torDir, "torrc"), DebugWriter: nil, ProcessCreator: newHideCmd("tor")})
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error connecting to self-run system tor: %v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp.t = t
|
||||||
|
tp.version = version
|
||||||
} else {
|
} else {
|
||||||
log.Debugln("Could not find a viable tor running or to run")
|
log.Debugln("Could not find a viable tor running or to run")
|
||||||
return nil, fmt.Errorf("could not connect to or start Tor that met requirements (min Tor version 0.3.5.x)")
|
return nil, fmt.Errorf("could not connect to or start Tor that met requirements (min Tor version 0.3.5.x)")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := tp.checkVersion()
|
version, err := tp.checkVersion()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tp.t.DeleteDataDirOnClose = false // caller is responsible for dealing with cached information...
|
tp.t.DeleteDataDirOnClose = false // caller is responsible for dealing with cached information...
|
||||||
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
|
tp.dialer, err = tp.t.Dialer(context.TODO(), &tor.DialConf{Authenticator: tp.authenticator})
|
||||||
|
tp.version = version
|
||||||
return tp, err
|
return tp, err
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("could not connect to running tor: %v", err)
|
return nil, fmt.Errorf("could not connect to running tor: %v", err)
|
||||||
|
@ -572,7 +579,10 @@ func createFromExisting(controlport *control.Conn, datadir string) *tor.Tor {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCmdlineTorVersion(torCmd string) bool {
|
func checkCmdlineTorVersion(torCmd string) (string, bool) {
|
||||||
|
if torCmd == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
// ideally we would use CommandContext with Timeout here
|
// ideally we would use CommandContext with Timeout here
|
||||||
// but it doesn't work with programs that may launch other processes via scripts e.g. exec
|
// but it doesn't work with programs that may launch other processes via scripts e.g. exec
|
||||||
// and the workout is more complex than just implementing the logic ourselves...
|
// and the workout is more complex than just implementing the logic ourselves...
|
||||||
|
@ -599,13 +609,13 @@ func checkCmdlineTorVersion(torCmd string) bool {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("tor process timed out")
|
log.Debugf("tor process timed out")
|
||||||
return false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
re := regexp.MustCompile(`[0-1]\.[0-9]\.[0-9]\.[0-9]`)
|
re := regexp.MustCompile(`[0-1]\.[0-9]\.[0-9]\.[0-9]`)
|
||||||
sysTorVersion := re.Find(outb.Bytes())
|
sysTorVersion := re.Find(outb.Bytes())
|
||||||
log.Infof("tor version: %v", string(sysTorVersion))
|
log.Infof("tor version: %v", string(sysTorVersion))
|
||||||
return minTorVersionReqs(string(sysTorVersion))
|
return string(sysTorVersion), minTorVersionReqs(string(sysTorVersion))
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if supplied version meets our min requirments
|
// returns true if supplied version meets our min requirments
|
||||||
|
|
|
@ -19,11 +19,19 @@ func getStatusCallback(progChan chan int) func(int, string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getVersionCallback(verChan chan string) func(string) {
|
||||||
|
return func(version string) {
|
||||||
|
fmt.Printf("version: %v\n", version)
|
||||||
|
verChan <- version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTorProvider(t *testing.T) {
|
func TestTorProvider(t *testing.T) {
|
||||||
|
|
||||||
goRoutineStart := runtime.NumGoroutine()
|
goRoutineStart := runtime.NumGoroutine()
|
||||||
|
|
||||||
progChan := make(chan int, 10)
|
progChan := make(chan int, 10)
|
||||||
|
verChan := make(chan string, 10)
|
||||||
log.SetLevel(log.LevelDebug)
|
log.SetLevel(log.LevelDebug)
|
||||||
torpath := path.Join("..", "tmp/tor")
|
torpath := path.Join("..", "tmp/tor")
|
||||||
|
|
||||||
|
@ -43,6 +51,7 @@ func TestTorProvider(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
acn.SetStatusCallback(getStatusCallback(progChan))
|
acn.SetStatusCallback(getStatusCallback(progChan))
|
||||||
|
acn.SetVersionCallback(getVersionCallback(verChan))
|
||||||
|
|
||||||
progress := 0
|
progress := 0
|
||||||
for progress < 100 {
|
for progress < 100 {
|
||||||
|
@ -57,6 +66,13 @@ func TestTorProvider(t *testing.T) {
|
||||||
progress = <-progChan
|
progress = <-progChan
|
||||||
t.Logf("progress: %v", progress)
|
t.Logf("progress: %v", progress)
|
||||||
}
|
}
|
||||||
|
log.Debugf("Pulling tor version from version callback chan...\n")
|
||||||
|
version := <-verChan
|
||||||
|
if version == "" {
|
||||||
|
t.Errorf("failed to get tor version, got empty string\n")
|
||||||
|
} else {
|
||||||
|
log.Debugf("Tor version: %v\n", version)
|
||||||
|
}
|
||||||
|
|
||||||
// Test opening the OP Server
|
// Test opening the OP Server
|
||||||
_, _, err = acn.Open("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd")
|
_, _, err = acn.Open("isbr2t6bflul2zyi6hjtnuezb2xvfr42svzjg2q3gyqfgg3wmnrbkkqd")
|
||||||
|
|
Loading…
Reference in New Issue